Vendor github.com/docker/docker/pkg/term
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
478f50fb2e
commit
2a20ea7daf
16 changed files with 1274 additions and 0 deletions
66
vendor/github.com/docker/docker/pkg/term/ascii.go
generated
vendored
Normal file
66
vendor/github.com/docker/docker/pkg/term/ascii.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ASCII list the possible supported ASCII key sequence
|
||||||
|
var ASCII = []string{
|
||||||
|
"ctrl-@",
|
||||||
|
"ctrl-a",
|
||||||
|
"ctrl-b",
|
||||||
|
"ctrl-c",
|
||||||
|
"ctrl-d",
|
||||||
|
"ctrl-e",
|
||||||
|
"ctrl-f",
|
||||||
|
"ctrl-g",
|
||||||
|
"ctrl-h",
|
||||||
|
"ctrl-i",
|
||||||
|
"ctrl-j",
|
||||||
|
"ctrl-k",
|
||||||
|
"ctrl-l",
|
||||||
|
"ctrl-m",
|
||||||
|
"ctrl-n",
|
||||||
|
"ctrl-o",
|
||||||
|
"ctrl-p",
|
||||||
|
"ctrl-q",
|
||||||
|
"ctrl-r",
|
||||||
|
"ctrl-s",
|
||||||
|
"ctrl-t",
|
||||||
|
"ctrl-u",
|
||||||
|
"ctrl-v",
|
||||||
|
"ctrl-w",
|
||||||
|
"ctrl-x",
|
||||||
|
"ctrl-y",
|
||||||
|
"ctrl-z",
|
||||||
|
"ctrl-[",
|
||||||
|
"ctrl-\\",
|
||||||
|
"ctrl-]",
|
||||||
|
"ctrl-^",
|
||||||
|
"ctrl-_",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
|
||||||
|
func ToBytes(keys string) ([]byte, error) {
|
||||||
|
codes := []byte{}
|
||||||
|
next:
|
||||||
|
for _, key := range strings.Split(keys, ",") {
|
||||||
|
if len(key) != 1 {
|
||||||
|
for code, ctrl := range ASCII {
|
||||||
|
if ctrl == key {
|
||||||
|
codes = append(codes, byte(code))
|
||||||
|
continue next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key == "DEL" {
|
||||||
|
codes = append(codes, 127)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Unknown character: '%s'", key)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
codes = append(codes, byte(key[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codes, nil
|
||||||
|
}
|
50
vendor/github.com/docker/docker/pkg/term/tc_linux_cgo.go
generated
vendored
Normal file
50
vendor/github.com/docker/docker/pkg/term/tc_linux_cgo.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// +build linux,cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #include <termios.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Termios is the Unix API for terminal I/O.
|
||||||
|
// It is passthrough for syscall.Termios in order to make it portable with
|
||||||
|
// other platforms where it is not available or handled differently.
|
||||||
|
type Termios syscall.Termios
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if err := tcget(fd, &oldState.termios); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
|
||||||
|
C.cfmakeraw((*C.struct_termios)(unsafe.Pointer(&newState)))
|
||||||
|
if err := tcset(fd, &newState); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcget(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
ret, err := C.tcgetattr(C.int(fd), (*C.struct_termios)(unsafe.Pointer(p)))
|
||||||
|
if ret != 0 {
|
||||||
|
return err.(syscall.Errno)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcset(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
ret, err := C.tcsetattr(C.int(fd), C.TCSANOW, (*C.struct_termios)(unsafe.Pointer(p)))
|
||||||
|
if ret != 0 {
|
||||||
|
return err.(syscall.Errno)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
20
vendor/github.com/docker/docker/pkg/term/tc_other.go
generated
vendored
Normal file
20
vendor/github.com/docker/docker/pkg/term/tc_other.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// +build !windows
|
||||||
|
// +build !linux !cgo
|
||||||
|
// +build !solaris !cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tcget(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcset(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p)))
|
||||||
|
return err
|
||||||
|
}
|
63
vendor/github.com/docker/docker/pkg/term/tc_solaris_cgo.go
generated
vendored
Normal file
63
vendor/github.com/docker/docker/pkg/term/tc_solaris_cgo.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// +build solaris,cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #include <termios.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Termios is the Unix API for terminal I/O.
|
||||||
|
// It is passthrough for syscall.Termios in order to make it portable with
|
||||||
|
// other platforms where it is not available or handled differently.
|
||||||
|
type Termios syscall.Termios
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if err := tcget(fd, &oldState.termios); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
|
||||||
|
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON | syscall.IXANY)
|
||||||
|
newState.Oflag &^= syscall.OPOST
|
||||||
|
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
||||||
|
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
||||||
|
newState.Cflag |= syscall.CS8
|
||||||
|
|
||||||
|
/*
|
||||||
|
VMIN is the minimum number of characters that needs to be read in non-canonical mode for it to be returned
|
||||||
|
Since VMIN is overloaded with another element in canonical mode when we switch modes it defaults to 4. It
|
||||||
|
needs to be explicitly set to 1.
|
||||||
|
*/
|
||||||
|
newState.Cc[C.VMIN] = 1
|
||||||
|
newState.Cc[C.VTIME] = 0
|
||||||
|
|
||||||
|
if err := tcset(fd, &newState); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcget(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
ret, err := C.tcgetattr(C.int(fd), (*C.struct_termios)(unsafe.Pointer(p)))
|
||||||
|
if ret != 0 {
|
||||||
|
return err.(syscall.Errno)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcset(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
ret, err := C.tcsetattr(C.int(fd), C.TCSANOW, (*C.struct_termios)(unsafe.Pointer(p)))
|
||||||
|
if ret != 0 {
|
||||||
|
return err.(syscall.Errno)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
123
vendor/github.com/docker/docker/pkg/term/term.go
generated
vendored
Normal file
123
vendor/github.com/docker/docker/pkg/term/term.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
// Package term provides structures and helper functions to work with
|
||||||
|
// terminal (state, sizes).
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidState is returned if the state of the terminal is invalid.
|
||||||
|
ErrInvalidState = errors.New("Invalid terminal state")
|
||||||
|
)
|
||||||
|
|
||||||
|
// State represents the state of the terminal.
|
||||||
|
type State struct {
|
||||||
|
termios Termios
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winsize represents the size of the terminal window.
|
||||||
|
type Winsize struct {
|
||||||
|
Height uint16
|
||||||
|
Width uint16
|
||||||
|
x uint16
|
||||||
|
y uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdStreams returns the standard streams (stdin, stdout, stedrr).
|
||||||
|
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
||||||
|
return os.Stdin, os.Stdout, os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
|
||||||
|
func GetFdInfo(in interface{}) (uintptr, bool) {
|
||||||
|
var inFd uintptr
|
||||||
|
var isTerminalIn bool
|
||||||
|
if file, ok := in.(*os.File); ok {
|
||||||
|
inFd = file.Fd()
|
||||||
|
isTerminalIn = IsTerminal(inFd)
|
||||||
|
}
|
||||||
|
return inFd, isTerminalIn
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios Termios
|
||||||
|
return tcget(fd, &termios) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreTerminal restores the terminal connected to the given file descriptor
|
||||||
|
// to a previous state.
|
||||||
|
func RestoreTerminal(fd uintptr, state *State) error {
|
||||||
|
if state == nil {
|
||||||
|
return ErrInvalidState
|
||||||
|
}
|
||||||
|
if err := tcset(fd, &state.termios); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveState saves the state of the terminal connected to the given file descriptor.
|
||||||
|
func SaveState(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if err := tcget(fd, &oldState.termios); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableEcho applies the specified state to the terminal connected to the file
|
||||||
|
// descriptor, with echo disabled.
|
||||||
|
func DisableEcho(fd uintptr, state *State) error {
|
||||||
|
newState := state.termios
|
||||||
|
newState.Lflag &^= syscall.ECHO
|
||||||
|
|
||||||
|
if err := tcset(fd, &newState); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
handleInterrupt(fd, state)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
||||||
|
// raw mode and returns the previous state. On UNIX, this puts both the input
|
||||||
|
// and output into raw mode. On Windows, it only puts the input into raw mode.
|
||||||
|
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
|
oldState, err := MakeRaw(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
handleInterrupt(fd, oldState)
|
||||||
|
return oldState, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawTerminalOutput puts the output of terminal connected to the given file
|
||||||
|
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
|
||||||
|
// state. On Windows, it disables LF -> CRLF translation.
|
||||||
|
func SetRawTerminalOutput(fd uintptr) (*State, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleInterrupt(fd uintptr, state *State) {
|
||||||
|
sigchan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigchan, os.Interrupt)
|
||||||
|
go func() {
|
||||||
|
for range sigchan {
|
||||||
|
// quit cleanly and the new terminal item is on a new line
|
||||||
|
fmt.Println()
|
||||||
|
signal.Stop(sigchan)
|
||||||
|
close(sigchan)
|
||||||
|
RestoreTerminal(fd, state)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
41
vendor/github.com/docker/docker/pkg/term/term_solaris.go
generated
vendored
Normal file
41
vendor/github.com/docker/docker/pkg/term/term_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stropts.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
// Small wrapper to get rid of variadic args of ioctl()
|
||||||
|
int my_ioctl(int fd, int cmd, struct winsize *ws) {
|
||||||
|
return ioctl(fd, cmd, ws);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// GetWinsize returns the window size based on the specified file descriptor.
|
||||||
|
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||||
|
ws := &Winsize{}
|
||||||
|
ret, err := C.my_ioctl(C.int(fd), C.int(syscall.TIOCGWINSZ), (*C.struct_winsize)(unsafe.Pointer(ws)))
|
||||||
|
// Skip retval = 0
|
||||||
|
if ret == 0 {
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
return ws, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWinsize tries to set the specified window size for the specified file descriptor.
|
||||||
|
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||||
|
ret, err := C.my_ioctl(C.int(fd), C.int(syscall.TIOCSWINSZ), (*C.struct_winsize)(unsafe.Pointer(ws)))
|
||||||
|
// Skip retval = 0
|
||||||
|
if ret == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
29
vendor/github.com/docker/docker/pkg/term/term_unix.go
generated
vendored
Normal file
29
vendor/github.com/docker/docker/pkg/term/term_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// +build !solaris,!windows
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetWinsize returns the window size based on the specified file descriptor.
|
||||||
|
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||||
|
ws := &Winsize{}
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||||
|
// Skipp errno = 0
|
||||||
|
if err == 0 {
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
return ws, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWinsize tries to set the specified window size for the specified file descriptor.
|
||||||
|
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||||
|
// Skipp errno = 0
|
||||||
|
if err == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
233
vendor/github.com/docker/docker/pkg/term/term_windows.go
generated
vendored
Normal file
233
vendor/github.com/docker/docker/pkg/term/term_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Azure/go-ansiterm/winterm"
|
||||||
|
"github.com/docker/docker/pkg/term/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State holds the console mode for the terminal.
|
||||||
|
type State struct {
|
||||||
|
mode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winsize is used for window size.
|
||||||
|
type Winsize struct {
|
||||||
|
Height uint16
|
||||||
|
Width uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
|
||||||
|
enableVirtualTerminalInput = 0x0200
|
||||||
|
enableVirtualTerminalProcessing = 0x0004
|
||||||
|
disableNewlineAutoReturn = 0x0008
|
||||||
|
)
|
||||||
|
|
||||||
|
// vtInputSupported is true if enableVirtualTerminalInput is supported by the console
|
||||||
|
var vtInputSupported bool
|
||||||
|
|
||||||
|
// StdStreams returns the standard streams (stdin, stdout, stedrr).
|
||||||
|
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
||||||
|
// Turn on VT handling on all std handles, if possible. This might
|
||||||
|
// fail, in which case we will fall back to terminal emulation.
|
||||||
|
var emulateStdin, emulateStdout, emulateStderr bool
|
||||||
|
fd := os.Stdin.Fd()
|
||||||
|
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
||||||
|
// Validate that enableVirtualTerminalInput is supported, but do not set it.
|
||||||
|
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalInput); err != nil {
|
||||||
|
emulateStdin = true
|
||||||
|
} else {
|
||||||
|
vtInputSupported = true
|
||||||
|
}
|
||||||
|
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
||||||
|
// remembers invalid bits on input handles.
|
||||||
|
winterm.SetConsoleMode(fd, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = os.Stdout.Fd()
|
||||||
|
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
||||||
|
// Validate disableNewlineAutoReturn is supported, but do not set it.
|
||||||
|
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil {
|
||||||
|
emulateStdout = true
|
||||||
|
} else {
|
||||||
|
winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = os.Stderr.Fd()
|
||||||
|
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
||||||
|
// Validate disableNewlineAutoReturn is supported, but do not set it.
|
||||||
|
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil {
|
||||||
|
emulateStderr = true
|
||||||
|
} else {
|
||||||
|
winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("ConEmuANSI") == "ON" || os.Getenv("ConsoleZVersion") != "" {
|
||||||
|
// The ConEmu and ConsoleZ terminals emulate ANSI on output streams well.
|
||||||
|
emulateStdin = true
|
||||||
|
emulateStdout = false
|
||||||
|
emulateStderr = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if emulateStdin {
|
||||||
|
stdIn = windows.NewAnsiReader(syscall.STD_INPUT_HANDLE)
|
||||||
|
} else {
|
||||||
|
stdIn = os.Stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
if emulateStdout {
|
||||||
|
stdOut = windows.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
|
||||||
|
} else {
|
||||||
|
stdOut = os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
if emulateStderr {
|
||||||
|
stdErr = windows.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
|
||||||
|
} else {
|
||||||
|
stdErr = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
|
||||||
|
func GetFdInfo(in interface{}) (uintptr, bool) {
|
||||||
|
return windows.GetHandleInfo(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWinsize returns the window size based on the specified file descriptor.
|
||||||
|
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||||
|
info, err := winterm.GetConsoleScreenBufferInfo(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
winsize := &Winsize{
|
||||||
|
Width: uint16(info.Window.Right - info.Window.Left + 1),
|
||||||
|
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
return winsize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
return windows.IsConsole(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreTerminal restores the terminal connected to the given file descriptor
|
||||||
|
// to a previous state.
|
||||||
|
func RestoreTerminal(fd uintptr, state *State) error {
|
||||||
|
return winterm.SetConsoleMode(fd, state.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveState saves the state of the terminal connected to the given file descriptor.
|
||||||
|
func SaveState(fd uintptr) (*State, error) {
|
||||||
|
mode, e := winterm.GetConsoleMode(fd)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
return &State{mode: mode}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableEcho disables echo for the terminal connected to the given file descriptor.
|
||||||
|
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
||||||
|
func DisableEcho(fd uintptr, state *State) error {
|
||||||
|
mode := state.mode
|
||||||
|
mode &^= winterm.ENABLE_ECHO_INPUT
|
||||||
|
mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
|
||||||
|
err := winterm.SetConsoleMode(fd, mode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an interrupt handler to catch and restore prior state
|
||||||
|
restoreAtInterrupt(fd, state)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
||||||
|
// raw mode and returns the previous state. On UNIX, this puts both the input
|
||||||
|
// and output into raw mode. On Windows, it only puts the input into raw mode.
|
||||||
|
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
|
state, err := MakeRaw(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an interrupt handler to catch and restore prior state
|
||||||
|
restoreAtInterrupt(fd, state)
|
||||||
|
return state, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawTerminalOutput puts the output of terminal connected to the given file
|
||||||
|
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
|
||||||
|
// state. On Windows, it disables LF -> CRLF translation.
|
||||||
|
func SetRawTerminalOutput(fd uintptr) (*State, error) {
|
||||||
|
state, err := SaveState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore failures, since disableNewlineAutoReturn might not be supported on this
|
||||||
|
// version of Windows.
|
||||||
|
winterm.SetConsoleMode(fd, state.mode|disableNewlineAutoReturn)
|
||||||
|
return state, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
state, err := SaveState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := state.mode
|
||||||
|
|
||||||
|
// See
|
||||||
|
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
|
||||||
|
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
||||||
|
|
||||||
|
// Disable these modes
|
||||||
|
mode &^= winterm.ENABLE_ECHO_INPUT
|
||||||
|
mode &^= winterm.ENABLE_LINE_INPUT
|
||||||
|
mode &^= winterm.ENABLE_MOUSE_INPUT
|
||||||
|
mode &^= winterm.ENABLE_WINDOW_INPUT
|
||||||
|
mode &^= winterm.ENABLE_PROCESSED_INPUT
|
||||||
|
|
||||||
|
// Enable these modes
|
||||||
|
mode |= winterm.ENABLE_EXTENDED_FLAGS
|
||||||
|
mode |= winterm.ENABLE_INSERT_MODE
|
||||||
|
mode |= winterm.ENABLE_QUICK_EDIT_MODE
|
||||||
|
if vtInputSupported {
|
||||||
|
mode |= enableVirtualTerminalInput
|
||||||
|
}
|
||||||
|
|
||||||
|
err = winterm.SetConsoleMode(fd, mode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreAtInterrupt(fd uintptr, state *State) {
|
||||||
|
sigchan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigchan, os.Interrupt)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_ = <-sigchan
|
||||||
|
RestoreTerminal(fd, state)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
}
|
69
vendor/github.com/docker/docker/pkg/term/termios_darwin.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/pkg/term/termios_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TIOCGETA
|
||||||
|
setTermios = syscall.TIOCSETA
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios magic numbers, passthrough to the ones defined in syscall.
|
||||||
|
const (
|
||||||
|
IGNBRK = syscall.IGNBRK
|
||||||
|
PARMRK = syscall.PARMRK
|
||||||
|
INLCR = syscall.INLCR
|
||||||
|
IGNCR = syscall.IGNCR
|
||||||
|
ECHONL = syscall.ECHONL
|
||||||
|
CSIZE = syscall.CSIZE
|
||||||
|
ICRNL = syscall.ICRNL
|
||||||
|
ISTRIP = syscall.ISTRIP
|
||||||
|
PARENB = syscall.PARENB
|
||||||
|
ECHO = syscall.ECHO
|
||||||
|
ICANON = syscall.ICANON
|
||||||
|
ISIG = syscall.ISIG
|
||||||
|
IXON = syscall.IXON
|
||||||
|
BRKINT = syscall.BRKINT
|
||||||
|
INPCK = syscall.INPCK
|
||||||
|
OPOST = syscall.OPOST
|
||||||
|
CS8 = syscall.CS8
|
||||||
|
IEXTEN = syscall.IEXTEN
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios is the Unix API for terminal I/O.
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint64
|
||||||
|
Oflag uint64
|
||||||
|
Cflag uint64
|
||||||
|
Lflag uint64
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint64
|
||||||
|
Ospeed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||||
|
newState.Oflag &^= OPOST
|
||||||
|
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||||
|
newState.Cflag &^= (CSIZE | PARENB)
|
||||||
|
newState.Cflag |= CS8
|
||||||
|
newState.Cc[syscall.VMIN] = 1
|
||||||
|
newState.Cc[syscall.VTIME] = 0
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
69
vendor/github.com/docker/docker/pkg/term/termios_freebsd.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/pkg/term/termios_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TIOCGETA
|
||||||
|
setTermios = syscall.TIOCSETA
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios magic numbers, passthrough to the ones defined in syscall.
|
||||||
|
const (
|
||||||
|
IGNBRK = syscall.IGNBRK
|
||||||
|
PARMRK = syscall.PARMRK
|
||||||
|
INLCR = syscall.INLCR
|
||||||
|
IGNCR = syscall.IGNCR
|
||||||
|
ECHONL = syscall.ECHONL
|
||||||
|
CSIZE = syscall.CSIZE
|
||||||
|
ICRNL = syscall.ICRNL
|
||||||
|
ISTRIP = syscall.ISTRIP
|
||||||
|
PARENB = syscall.PARENB
|
||||||
|
ECHO = syscall.ECHO
|
||||||
|
ICANON = syscall.ICANON
|
||||||
|
ISIG = syscall.ISIG
|
||||||
|
IXON = syscall.IXON
|
||||||
|
BRKINT = syscall.BRKINT
|
||||||
|
INPCK = syscall.INPCK
|
||||||
|
OPOST = syscall.OPOST
|
||||||
|
CS8 = syscall.CS8
|
||||||
|
IEXTEN = syscall.IEXTEN
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios is the Unix API for terminal I/O.
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint32
|
||||||
|
Oflag uint32
|
||||||
|
Cflag uint32
|
||||||
|
Lflag uint32
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint32
|
||||||
|
Ospeed uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||||
|
newState.Oflag &^= OPOST
|
||||||
|
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||||
|
newState.Cflag &^= (CSIZE | PARENB)
|
||||||
|
newState.Cflag |= CS8
|
||||||
|
newState.Cc[syscall.VMIN] = 1
|
||||||
|
newState.Cc[syscall.VTIME] = 0
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
47
vendor/github.com/docker/docker/pkg/term/termios_linux.go
generated
vendored
Normal file
47
vendor/github.com/docker/docker/pkg/term/termios_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// +build !cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TCGETS
|
||||||
|
setTermios = syscall.TCSETS
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios is the Unix API for terminal I/O.
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint32
|
||||||
|
Oflag uint32
|
||||||
|
Cflag uint32
|
||||||
|
Lflag uint32
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint32
|
||||||
|
Ospeed uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
|
||||||
|
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
|
||||||
|
newState.Oflag &^= syscall.OPOST
|
||||||
|
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
||||||
|
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
||||||
|
newState.Cflag |= syscall.CS8
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
69
vendor/github.com/docker/docker/pkg/term/termios_openbsd.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/pkg/term/termios_openbsd.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TIOCGETA
|
||||||
|
setTermios = syscall.TIOCSETA
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios magic numbers, passthrough to the ones defined in syscall.
|
||||||
|
const (
|
||||||
|
IGNBRK = syscall.IGNBRK
|
||||||
|
PARMRK = syscall.PARMRK
|
||||||
|
INLCR = syscall.INLCR
|
||||||
|
IGNCR = syscall.IGNCR
|
||||||
|
ECHONL = syscall.ECHONL
|
||||||
|
CSIZE = syscall.CSIZE
|
||||||
|
ICRNL = syscall.ICRNL
|
||||||
|
ISTRIP = syscall.ISTRIP
|
||||||
|
PARENB = syscall.PARENB
|
||||||
|
ECHO = syscall.ECHO
|
||||||
|
ICANON = syscall.ICANON
|
||||||
|
ISIG = syscall.ISIG
|
||||||
|
IXON = syscall.IXON
|
||||||
|
BRKINT = syscall.BRKINT
|
||||||
|
INPCK = syscall.INPCK
|
||||||
|
OPOST = syscall.OPOST
|
||||||
|
CS8 = syscall.CS8
|
||||||
|
IEXTEN = syscall.IEXTEN
|
||||||
|
)
|
||||||
|
|
||||||
|
// Termios is the Unix API for terminal I/O.
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint32
|
||||||
|
Oflag uint32
|
||||||
|
Cflag uint32
|
||||||
|
Lflag uint32
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint32
|
||||||
|
Ospeed uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||||
|
newState.Oflag &^= OPOST
|
||||||
|
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||||
|
newState.Cflag &^= (CSIZE | PARENB)
|
||||||
|
newState.Cflag |= CS8
|
||||||
|
newState.Cc[syscall.VMIN] = 1
|
||||||
|
newState.Cc[syscall.VTIME] = 0
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
263
vendor/github.com/docker/docker/pkg/term/windows/ansi_reader.go
generated
vendored
Normal file
263
vendor/github.com/docker/docker/pkg/term/windows/ansi_reader.go
generated
vendored
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
ansiterm "github.com/Azure/go-ansiterm"
|
||||||
|
"github.com/Azure/go-ansiterm/winterm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
escapeSequence = ansiterm.KEY_ESC_CSI
|
||||||
|
)
|
||||||
|
|
||||||
|
// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
|
||||||
|
type ansiReader struct {
|
||||||
|
file *os.File
|
||||||
|
fd uintptr
|
||||||
|
buffer []byte
|
||||||
|
cbBuffer int
|
||||||
|
command []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
|
||||||
|
// Windows console input handle.
|
||||||
|
func NewAnsiReader(nFile int) io.ReadCloser {
|
||||||
|
initLogger()
|
||||||
|
file, fd := winterm.GetStdFile(nFile)
|
||||||
|
return &ansiReader{
|
||||||
|
file: file,
|
||||||
|
fd: fd,
|
||||||
|
command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
|
||||||
|
buffer: make([]byte, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the wrapped file.
|
||||||
|
func (ar *ansiReader) Close() (err error) {
|
||||||
|
return ar.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fd returns the file descriptor of the wrapped file.
|
||||||
|
func (ar *ansiReader) Fd() uintptr {
|
||||||
|
return ar.fd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads up to len(p) bytes of translated input events into p.
|
||||||
|
func (ar *ansiReader) Read(p []byte) (int, error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previously read bytes exist, read as much as we can and return
|
||||||
|
if len(ar.buffer) > 0 {
|
||||||
|
logger.Debugf("Reading previously cached bytes")
|
||||||
|
|
||||||
|
originalLength := len(ar.buffer)
|
||||||
|
copiedLength := copy(p, ar.buffer)
|
||||||
|
|
||||||
|
if copiedLength == originalLength {
|
||||||
|
ar.buffer = make([]byte, 0, len(p))
|
||||||
|
} else {
|
||||||
|
ar.buffer = ar.buffer[copiedLength:]
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf("Read from cache p[%d]: % x", copiedLength, p)
|
||||||
|
return copiedLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and translate key events
|
||||||
|
events, err := readInputEvents(ar.fd, len(p))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if len(events) == 0 {
|
||||||
|
logger.Debug("No input events detected")
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keyBytes := translateKeyEvents(events, []byte(escapeSequence))
|
||||||
|
|
||||||
|
// Save excess bytes and right-size keyBytes
|
||||||
|
if len(keyBytes) > len(p) {
|
||||||
|
logger.Debugf("Received %d keyBytes, only room for %d bytes", len(keyBytes), len(p))
|
||||||
|
ar.buffer = keyBytes[len(p):]
|
||||||
|
keyBytes = keyBytes[:len(p)]
|
||||||
|
} else if len(keyBytes) == 0 {
|
||||||
|
logger.Debug("No key bytes returned from the translator")
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
copiedLength := copy(p, keyBytes)
|
||||||
|
if copiedLength != len(keyBytes) {
|
||||||
|
return 0, errors.New("unexpected copy length encountered")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf("Read p[%d]: % x", copiedLength, p)
|
||||||
|
logger.Debugf("Read keyBytes[%d]: % x", copiedLength, keyBytes)
|
||||||
|
return copiedLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readInputEvents polls until at least one event is available.
|
||||||
|
func readInputEvents(fd uintptr, maxBytes int) ([]winterm.INPUT_RECORD, error) {
|
||||||
|
// Determine the maximum number of records to retrieve
|
||||||
|
// -- Cast around the type system to obtain the size of a single INPUT_RECORD.
|
||||||
|
// unsafe.Sizeof requires an expression vs. a type-reference; the casting
|
||||||
|
// tricks the type system into believing it has such an expression.
|
||||||
|
recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
|
||||||
|
countRecords := maxBytes / recordSize
|
||||||
|
if countRecords > ansiterm.MAX_INPUT_EVENTS {
|
||||||
|
countRecords = ansiterm.MAX_INPUT_EVENTS
|
||||||
|
} else if countRecords == 0 {
|
||||||
|
countRecords = 1
|
||||||
|
}
|
||||||
|
logger.Debugf("[windows] readInputEvents: Reading %v records (buffer size %v, record size %v)", countRecords, maxBytes, recordSize)
|
||||||
|
|
||||||
|
// Wait for and read input events
|
||||||
|
events := make([]winterm.INPUT_RECORD, countRecords)
|
||||||
|
nEvents := uint32(0)
|
||||||
|
eventsExist, err := winterm.WaitForSingleObject(fd, winterm.WAIT_INFINITE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if eventsExist {
|
||||||
|
err = winterm.ReadConsoleInput(fd, events, &nEvents)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a slice restricted to the number of returned records
|
||||||
|
logger.Debugf("[windows] readInputEvents: Read %v events", nEvents)
|
||||||
|
return events[:nEvents], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyEvent Translation Helpers
|
||||||
|
|
||||||
|
var arrowKeyMapPrefix = map[uint16]string{
|
||||||
|
winterm.VK_UP: "%s%sA",
|
||||||
|
winterm.VK_DOWN: "%s%sB",
|
||||||
|
winterm.VK_RIGHT: "%s%sC",
|
||||||
|
winterm.VK_LEFT: "%s%sD",
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyMapPrefix = map[uint16]string{
|
||||||
|
winterm.VK_UP: "\x1B[%sA",
|
||||||
|
winterm.VK_DOWN: "\x1B[%sB",
|
||||||
|
winterm.VK_RIGHT: "\x1B[%sC",
|
||||||
|
winterm.VK_LEFT: "\x1B[%sD",
|
||||||
|
winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1
|
||||||
|
winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4
|
||||||
|
winterm.VK_INSERT: "\x1B[2%s~",
|
||||||
|
winterm.VK_DELETE: "\x1B[3%s~",
|
||||||
|
winterm.VK_PRIOR: "\x1B[5%s~",
|
||||||
|
winterm.VK_NEXT: "\x1B[6%s~",
|
||||||
|
winterm.VK_F1: "",
|
||||||
|
winterm.VK_F2: "",
|
||||||
|
winterm.VK_F3: "\x1B[13%s~",
|
||||||
|
winterm.VK_F4: "\x1B[14%s~",
|
||||||
|
winterm.VK_F5: "\x1B[15%s~",
|
||||||
|
winterm.VK_F6: "\x1B[17%s~",
|
||||||
|
winterm.VK_F7: "\x1B[18%s~",
|
||||||
|
winterm.VK_F8: "\x1B[19%s~",
|
||||||
|
winterm.VK_F9: "\x1B[20%s~",
|
||||||
|
winterm.VK_F10: "\x1B[21%s~",
|
||||||
|
winterm.VK_F11: "\x1B[23%s~",
|
||||||
|
winterm.VK_F12: "\x1B[24%s~",
|
||||||
|
}
|
||||||
|
|
||||||
|
// translateKeyEvents converts the input events into the appropriate ANSI string.
|
||||||
|
func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for _, event := range events {
|
||||||
|
if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
|
||||||
|
buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyToString maps the given input event record to the corresponding string.
|
||||||
|
func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
|
||||||
|
if keyEvent.UnicodeChar == 0 {
|
||||||
|
return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, alt, control := getControlKeys(keyEvent.ControlKeyState)
|
||||||
|
if control {
|
||||||
|
// TODO(azlinux): Implement following control sequences
|
||||||
|
// <Ctrl>-D Signals the end of input from the keyboard; also exits current shell.
|
||||||
|
// <Ctrl>-H Deletes the first character to the left of the cursor. Also called the ERASE key.
|
||||||
|
// <Ctrl>-Q Restarts printing after it has been stopped with <Ctrl>-s.
|
||||||
|
// <Ctrl>-S Suspends printing on the screen (does not stop the program).
|
||||||
|
// <Ctrl>-U Deletes all characters on the current line. Also called the KILL key.
|
||||||
|
// <Ctrl>-E Quits current command and creates a core
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Alt>+Key generates ESC N Key
|
||||||
|
if !control && alt {
|
||||||
|
return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(keyEvent.UnicodeChar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
|
||||||
|
func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
|
||||||
|
shift, alt, control := getControlKeys(controlState)
|
||||||
|
modifier := getControlKeysModifier(shift, alt, control)
|
||||||
|
|
||||||
|
if format, ok := arrowKeyMapPrefix[key]; ok {
|
||||||
|
return fmt.Sprintf(format, escapeSequence, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
if format, ok := keyMapPrefix[key]; ok {
|
||||||
|
return fmt.Sprintf(format, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// getControlKeys extracts the shift, alt, and ctrl key states.
|
||||||
|
func getControlKeys(controlState uint32) (shift, alt, control bool) {
|
||||||
|
shift = 0 != (controlState & winterm.SHIFT_PRESSED)
|
||||||
|
alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
|
||||||
|
control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
|
||||||
|
return shift, alt, control
|
||||||
|
}
|
||||||
|
|
||||||
|
// getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
|
||||||
|
func getControlKeysModifier(shift, alt, control bool) string {
|
||||||
|
if shift && alt && control {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_8
|
||||||
|
}
|
||||||
|
if alt && control {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_7
|
||||||
|
}
|
||||||
|
if shift && control {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_6
|
||||||
|
}
|
||||||
|
if control {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_5
|
||||||
|
}
|
||||||
|
if shift && alt {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_4
|
||||||
|
}
|
||||||
|
if alt {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_3
|
||||||
|
}
|
||||||
|
if shift {
|
||||||
|
return ansiterm.KEY_CONTROL_PARAM_2
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
64
vendor/github.com/docker/docker/pkg/term/windows/ansi_writer.go
generated
vendored
Normal file
64
vendor/github.com/docker/docker/pkg/term/windows/ansi_writer.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
ansiterm "github.com/Azure/go-ansiterm"
|
||||||
|
"github.com/Azure/go-ansiterm/winterm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation.
|
||||||
|
type ansiWriter struct {
|
||||||
|
file *os.File
|
||||||
|
fd uintptr
|
||||||
|
infoReset *winterm.CONSOLE_SCREEN_BUFFER_INFO
|
||||||
|
command []byte
|
||||||
|
escapeSequence []byte
|
||||||
|
inAnsiSequence bool
|
||||||
|
parser *ansiterm.AnsiParser
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
|
||||||
|
// Windows console output handle.
|
||||||
|
func NewAnsiWriter(nFile int) io.Writer {
|
||||||
|
initLogger()
|
||||||
|
file, fd := winterm.GetStdFile(nFile)
|
||||||
|
info, err := winterm.GetConsoleScreenBufferInfo(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file))
|
||||||
|
logger.Infof("newAnsiWriter: parser %p", parser)
|
||||||
|
|
||||||
|
aw := &ansiWriter{
|
||||||
|
file: file,
|
||||||
|
fd: fd,
|
||||||
|
infoReset: info,
|
||||||
|
command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
|
||||||
|
escapeSequence: []byte(ansiterm.KEY_ESC_CSI),
|
||||||
|
parser: parser,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("newAnsiWriter: aw.parser %p", aw.parser)
|
||||||
|
logger.Infof("newAnsiWriter: %v", aw)
|
||||||
|
return aw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *ansiWriter) Fd() uintptr {
|
||||||
|
return aw.fd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes len(p) bytes from p to the underlying data stream.
|
||||||
|
func (aw *ansiWriter) Write(p []byte) (total int, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("Write: % x", p)
|
||||||
|
logger.Infof("Write: %s", string(p))
|
||||||
|
return aw.parser.Parse(p)
|
||||||
|
}
|
35
vendor/github.com/docker/docker/pkg/term/windows/console.go
generated
vendored
Normal file
35
vendor/github.com/docker/docker/pkg/term/windows/console.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Azure/go-ansiterm/winterm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
|
||||||
|
func GetHandleInfo(in interface{}) (uintptr, bool) {
|
||||||
|
switch t := in.(type) {
|
||||||
|
case *ansiReader:
|
||||||
|
return t.Fd(), true
|
||||||
|
case *ansiWriter:
|
||||||
|
return t.Fd(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
var inFd uintptr
|
||||||
|
var isTerminal bool
|
||||||
|
|
||||||
|
if file, ok := in.(*os.File); ok {
|
||||||
|
inFd = file.Fd()
|
||||||
|
isTerminal = IsConsole(inFd)
|
||||||
|
}
|
||||||
|
return inFd, isTerminal
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsConsole returns true if the given file descriptor is a Windows Console.
|
||||||
|
// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
|
||||||
|
func IsConsole(fd uintptr) bool {
|
||||||
|
_, e := winterm.GetConsoleMode(fd)
|
||||||
|
return e == nil
|
||||||
|
}
|
33
vendor/github.com/docker/docker/pkg/term/windows/windows.go
generated
vendored
Normal file
33
vendor/github.com/docker/docker/pkg/term/windows/windows.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
|
||||||
|
// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
|
||||||
|
// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
ansiterm "github.com/Azure/go-ansiterm"
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger *logrus.Logger
|
||||||
|
var initOnce sync.Once
|
||||||
|
|
||||||
|
func initLogger() {
|
||||||
|
initOnce.Do(func() {
|
||||||
|
logFile := ioutil.Discard
|
||||||
|
|
||||||
|
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
|
||||||
|
logFile, _ = os.Create("ansiReaderWriter.log")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = &logrus.Logger{
|
||||||
|
Out: logFile,
|
||||||
|
Formatter: new(logrus.TextFormatter),
|
||||||
|
Level: logrus.DebugLevel,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue