36
vendor/github.com/moby/term/windows/BUILD
generated
vendored
Normal file
36
vendor/github.com/moby/term/windows/BUILD
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"ansi_reader.go",
|
||||
"ansi_writer.go",
|
||||
"console.go",
|
||||
"windows.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/moby/term/windows",
|
||||
importpath = "github.com/moby/term/windows",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//vendor/github.com/Azure/go-ansiterm:go_default_library",
|
||||
"//vendor/github.com/Azure/go-ansiterm/winterm:go_default_library",
|
||||
"//vendor/github.com/sirupsen/logrus:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
263
vendor/github.com/moby/term/windows/ansi_reader.go
generated
vendored
Normal file
263
vendor/github.com/moby/term/windows/ansi_reader.go
generated
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
// +build windows
|
||||
|
||||
package windowsconsole // import "github.com/moby/term/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/moby/term/windows/ansi_writer.go
generated
vendored
Normal file
64
vendor/github.com/moby/term/windows/ansi_writer.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// +build windows
|
||||
|
||||
package windowsconsole // import "github.com/moby/term/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/moby/term/windows/console.go
generated
vendored
Normal file
35
vendor/github.com/moby/term/windows/console.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// +build windows
|
||||
|
||||
package windowsconsole // import "github.com/moby/term/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
|
||||
}
|
34
vendor/github.com/moby/term/windows/windows.go
generated
vendored
Normal file
34
vendor/github.com/moby/term/windows/windows.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// +build windows
|
||||
// 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 windowsconsole // import "github.com/moby/term/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,
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user