helper/wrappedreadline: helper for dealing with wrapped standard streams

This commit is contained in:
Mitchell Hashimoto 2016-11-13 12:10:42 -08:00
parent 6272b969d4
commit 73a1564dac
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 133 additions and 0 deletions

View File

@ -0,0 +1,84 @@
// wrappedreadline is a package that has helpers for interacting with
// readline from a panicwrap executable.
//
// panicwrap overrides the standard file descriptors so that the child process
// no longer looks like a TTY. The helpers here access the extra file descriptors
// passed by panicwrap to fix that.
package wrappedreadline
import (
"os"
"runtime"
"github.com/chzyer/readline"
)
// These are the file descriptor numbers for the original stdin, stdout, stderr
// streams from the parent process.
const (
StdinFd = 3
StdoutFd = 4
StderrFd = 5
)
// These are the *os.File values for the standard streams.
var (
Stdin = os.NewFile(uintptr(StdinFd), "stdin")
Stdout = os.NewFile(uintptr(StdoutFd), "stdout")
Stderr = os.NewFile(uintptr(StderrFd), "stderr")
)
// Override overrides the values in readline.Config that need to be
// set with wrapped values.
func Override(cfg *readline.Config) *readline.Config {
cfg.Stdin = Stdin
cfg.Stdout = Stdout
cfg.Stderr = Stderr
cfg.FuncGetWidth = TerminalWidth
cfg.FuncIsTerminal = IsTerminal
var rm RawMode
cfg.FuncMakeRaw = rm.Enter
cfg.FuncExitRaw = rm.Exit
return cfg
}
// IsTerminal determines if this process is attached to a TTY.
func IsTerminal() bool {
// Windows is always a terminal
if runtime.GOOS == "windows" {
return true
}
// Same implementation as readline but with our custom fds
return readline.IsTerminal(StdinFd) && (readline.IsTerminal(StdoutFd) || readline.IsTerminal(StderrFd))
}
// TerminalWidth gets the terminal width in characters.
func TerminalWidth() int {
if runtime.GOOS == "windows" {
return readline.GetScreenWidth()
}
return getWidth()
}
// RawMode is a helper for entering and exiting raw mode.
type RawMode struct {
state *readline.State
}
func (r *RawMode) Enter() (err error) {
r.state, err = readline.MakeRaw(StdinFd)
return err
}
func (r *RawMode) Exit() error {
if r.state == nil {
return nil
}
return readline.Restore(StdinFd, r.state)
}

View File

@ -0,0 +1,41 @@
// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
package wrappedreadline
import (
"syscall"
"unsafe"
)
// getWidth impl for Unix
func getWidth() int {
w := getWidthFd(StdoutFd)
if w < 0 {
w = getWidthFd(StderrFd)
}
return w
}
type winsize struct {
Row uint16
Col uint16
Xpixel uint16
Ypixel uint16
}
// get width of the terminal
func getWidthFd(stdoutFd int) int {
ws := &winsize{}
retCode, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
uintptr(stdoutFd),
uintptr(syscall.TIOCGWINSZ),
uintptr(unsafe.Pointer(ws)))
if int(retCode) == -1 {
_ = errno
return -1
}
return int(ws.Col)
}

View File

@ -0,0 +1,8 @@
// +build windows
package wrappedreadline
// getWidth impl for other
func getWidth() int {
return 0
}