1// wrappedreadline is a package that has helpers for interacting with 2// readline from a panicwrap executable. 3// 4// panicwrap overrides the standard file descriptors so that the child process 5// no longer looks like a TTY. The helpers here access the extra file descriptors 6// passed by panicwrap to fix that. 7// 8// panicwrap should be checked for with panicwrap.Wrapped before using this 9// librar, since this library won't adapt if the binary is not wrapped. 10package wrappedreadline 11 12import ( 13 "runtime" 14 15 "github.com/chzyer/readline" 16 17 "github.com/hashicorp/terraform/internal/helper/wrappedstreams" 18) 19 20// Override overrides the values in readline.Config that need to be 21// set with wrapped values. 22func Override(cfg *readline.Config) *readline.Config { 23 cfg.Stdin = wrappedstreams.Stdin() 24 cfg.Stdout = wrappedstreams.Stdout() 25 cfg.Stderr = wrappedstreams.Stderr() 26 27 cfg.FuncGetWidth = TerminalWidth 28 cfg.FuncIsTerminal = IsTerminal 29 30 rm := RawMode{StdinFd: int(wrappedstreams.Stdin().Fd())} 31 cfg.FuncMakeRaw = rm.Enter 32 cfg.FuncExitRaw = rm.Exit 33 34 return cfg 35} 36 37// IsTerminal determines if this process is attached to a TTY. 38func IsTerminal() bool { 39 // Windows is always a terminal 40 if runtime.GOOS == "windows" { 41 return true 42 } 43 44 // Same implementation as readline but with our custom fds 45 return readline.IsTerminal(int(wrappedstreams.Stdin().Fd())) && 46 (readline.IsTerminal(int(wrappedstreams.Stdout().Fd())) || 47 readline.IsTerminal(int(wrappedstreams.Stderr().Fd()))) 48} 49 50// TerminalWidth gets the terminal width in characters. 51func TerminalWidth() int { 52 if runtime.GOOS == "windows" { 53 return readline.GetScreenWidth() 54 } 55 56 return getWidth() 57} 58 59// RawMode is a helper for entering and exiting raw mode. 60type RawMode struct { 61 StdinFd int 62 63 state *readline.State 64} 65 66func (r *RawMode) Enter() (err error) { 67 r.state, err = readline.MakeRaw(r.StdinFd) 68 return err 69} 70 71func (r *RawMode) Exit() error { 72 if r.state == nil { 73 return nil 74 } 75 76 return readline.Restore(r.StdinFd, r.state) 77} 78