https://github.com/droundy/iolaus/blob/master/src/util/cook.go
It's a bit ugly, because if you don't set the terminal back to cooked,
the shell will be left in a weird state. It's designed to be used
like:
defer cook.Undo(cook.SetRaw())
so you can be sure that the tty is set back to cooked. If I were to
rewrite this, I'd do it differently, as I wrote this particular code a
long while back (maybe a year?).
David
--
David Roundy
This is a neat trick. I'm guessing the arguments to cook.Undo() are
evaluated when the function is executed, not when the defer is
executed.
Dave
I like that trick. If you make Undo a method of the secret type, you
can express it a different way:
package main
import "fmt"
type T int
func Start() T {
fmt.Println("Start")
return T(0)
}
func (t T) End() {
fmt.Println("End")
}
func main() {
defer Start().End()
fmt.Println("During")
}
Andrew
That's definitely nicer. If I wrote it again, I'd be tempted to just
return a function so you'd end up with
defer Start()()
but that seems a bit uglier than your suggestion, as the second
parentheses look weird, and you could easily omit them and not notice
that you aren't ending at all.
--
David Roundy
Yes, that is the behavior of defer, which does make this sort of
trickery possible. :)
--
David Roundy
Elegant IMHO, but it deserves a comment:
defer Start()() // Keep your paws off my code unless you grok this
--
.O. | Sterling (Chip) Camden | http://camdensoftware.com
..O | ster...@camdensoftware.com | http://chipsquips.com
OOO | 2048R/D6DBAF91 | http://chipstips.com
Yeah, that's heading into dangerous territory. It's a neat trick, but
I wouldn't use it in code I expected others to use. It's much more
readable (and idiomatic) split over two lines:
t := Start()
defer t.End()
Andrew
I cobbled together a (unix-specific, syscall-using) solution based on the stack overflow answer to the same question in python, and a previous post on this list. References inline.Note: this seems to work, but I haven't taken the time to really understand what I'm doing, and I welcome any suggestions or fixes.func getTermios() (result syscall.Termios, err error) {r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin), syscall.TCGETS, uintptr(unsafe.Pointer(&result)))if errno != 0 {return result, os.NewSyscallError("SYS_IOCTL", errno)}if r1 != 0 {return result, fmt.Errorf("Error: expected first syscall result to be 0, got %d", r1)}return result, nil}func setTermios(t syscall.Termios) error {r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin), syscall.TCSETS, uintptr(unsafe.Pointer(&t)))if errno != 0 {return os.NewSyscallError("SYS_IOCTL", errno)}if r1 != 0 {return fmt.Errorf("Error: expected first syscall result to be 0, got %d", r1)}return nil}func getFileStatusFlags() (int32, error) {r1, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(syscall.Stdin), syscall.F_GETFL, 0)if errno != 0 {return 0, os.NewSyscallError("SYS_FCNTL", errno)}r := int32(r1)if r < 0 {return 0, fmt.Errorf("Error: expected first syscall result to be >= 0, got %d", r)}return r, nil}func setFileStatusFlags(f int32) error {r1, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(syscall.Stdin), syscall.F_SETFL, uintptr(f))if errno != 0 {return os.NewSyscallError("SYS_FCNTL", errno)}if r1 != 0 {return fmt.Errorf("Error: expected first syscall result to be 0, got %d", r1)}return nil}func readSingleKeypress() (byte, error) {oldFl, err := getFileStatusFlags()if err != nil {return 0, err}oldTermios, err := getTermios()if err != nil {return 0, err}defer setFileStatusFlags(oldFl)defer setTermios(oldTermios)newFl, newTermios := oldFl, oldTermiosnewTermios.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP)newTermios.Iflag &^= (syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)newTermios.Oflag &^= syscall.OPOSTnewTermios.Cflag &^= (syscall.CSIZE | syscall.PARENB)newTermios.Cflag |= syscall.CS8newTermios.Lflag &^= (syscall.ECHONL | syscall.ECHO | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)newTermios.Cc[syscall.VMIN] = 1newTermios.Cc[syscall.VTIME] = 0if err = setTermios(newTermios); err != nil {return 0, err}newFl &^= syscall.O_NONBLOCKif err = setFileStatusFlags(newFl); err != nil {return 0, err}keys := []byte{0}n, err := syscall.Read(syscall.Stdin, keys)if err != nil {return 0, err}if n != 1 {return 0, fmt.Errorf("Expected to read 1 byte, got %d", n)}return keys[0], nil}
On Tuesday, December 18, 2012 10:21:55 AM UTC-8, alber...@gmail.com wrote:Hi everybody!
I'm also trying to do this but the code in cook.go seems outdated.
Does anyone have any suggestion?
Thank you,
Alberto
On Wed, Apr 24, 2013 at 11:12 AM, Archos <raul...@sent.com> wrote:
> What I'm doing on the next goroutine is to trap both signals of CTRL-Z and
> CTRL-C; the rest of the process does not needs know about it.
Doesn't need to know about it? What if the process already installed a
signal trap? Then the signal handler in your library cripples the
previously existing one. You're carelessly mutating global state.
> Once the
> function ends, that goroutine also is finished.
>
> sig := make(chan os.Signal, 1)
> signal.Notify(sig, syscall.SIGINT, syscall.SIGTSTP)
> go func() {
> for {
> select {
> case <-sig: // ignore
> }
> }
> }()
What makes you think so (wrt "... is finished.")? The signal silencing
goroutine never returns and thus leaks on every call to ReadPassword
at least one stack segment.
On Wed, Apr 24, 2013 at 11:12 AM, Archos <raul...@sent.com> wrote:
> What I'm doing on the next goroutine is to trap both signals of CTRL-Z and
> CTRL-C; the rest of the process does not needs know about it.
Doesn't need to know about it? What if the process already installed a
signal trap? Then the signal handler in your library cripples the
previously existing one. You're carelessly mutating global state.
> Once the function ends, that goroutine also is finished.
What makes you think so (wrt "... is finished.")? The signal silencing
goroutine never returns and thus leaks on every call to ReadPassword
at least one stack segment.