Browse code

pkg/term: vendor moby/term and make pkg/term an alias

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2020/04/16 18:02:09
Showing 34 changed files
1 1
deleted file mode 100644
... ...
@@ -1,67 +0,0 @@
1
-package term // import "github.com/docker/docker/pkg/term"
2
-
3
-import (
4
-	"fmt"
5
-	"strings"
6
-)
7
-
8
-// ASCII list the possible supported ASCII key sequence
9
-var ASCII = []string{
10
-	"ctrl-@",
11
-	"ctrl-a",
12
-	"ctrl-b",
13
-	"ctrl-c",
14
-	"ctrl-d",
15
-	"ctrl-e",
16
-	"ctrl-f",
17
-	"ctrl-g",
18
-	"ctrl-h",
19
-	"ctrl-i",
20
-	"ctrl-j",
21
-	"ctrl-k",
22
-	"ctrl-l",
23
-	"ctrl-m",
24
-	"ctrl-n",
25
-	"ctrl-o",
26
-	"ctrl-p",
27
-	"ctrl-q",
28
-	"ctrl-r",
29
-	"ctrl-s",
30
-	"ctrl-t",
31
-	"ctrl-u",
32
-	"ctrl-v",
33
-	"ctrl-w",
34
-	"ctrl-x",
35
-	"ctrl-y",
36
-	"ctrl-z",
37
-	"ctrl-[",
38
-	"ctrl-\\",
39
-	"ctrl-]",
40
-	"ctrl-^",
41
-	"ctrl-_",
42
-}
43
-
44
-// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
45
-// Deprecated: use github.com/moby/term.ToBytes
46
-func ToBytes(keys string) ([]byte, error) {
47
-	codes := []byte{}
48
-next:
49
-	for _, key := range strings.Split(keys, ",") {
50
-		if len(key) != 1 {
51
-			for code, ctrl := range ASCII {
52
-				if ctrl == key {
53
-					codes = append(codes, byte(code))
54
-					continue next
55
-				}
56
-			}
57
-			if key == "DEL" {
58
-				codes = append(codes, 127)
59
-			} else {
60
-				return nil, fmt.Errorf("Unknown character: '%s'", key)
61
-			}
62
-		} else {
63
-			codes = append(codes, key[0])
64
-		}
65
-	}
66
-	return codes, nil
67
-}
68 1
deleted file mode 100644
... ...
@@ -1,25 +0,0 @@
1
-package term // import "github.com/docker/docker/pkg/term"
2
-
3
-import (
4
-	"testing"
5
-
6
-	"gotest.tools/v3/assert"
7
-	is "gotest.tools/v3/assert/cmp"
8
-)
9
-
10
-func TestToBytes(t *testing.T) {
11
-	codes, err := ToBytes("ctrl-a,a")
12
-	assert.NilError(t, err)
13
-	assert.Check(t, is.DeepEqual([]byte{1, 97}, codes))
14
-
15
-	_, err = ToBytes("shift-z")
16
-	assert.Check(t, is.ErrorContains(err, ""))
17
-
18
-	codes, err = ToBytes("ctrl-@,ctrl-[,~,ctrl-o")
19
-	assert.NilError(t, err)
20
-	assert.Check(t, is.DeepEqual([]byte{0, 27, 126, 15}, codes))
21
-
22
-	codes, err = ToBytes("DEL,+")
23
-	assert.NilError(t, err)
24
-	assert.Check(t, is.DeepEqual([]byte{127, 43}, codes))
25
-}
26 1
new file mode 100644
... ...
@@ -0,0 +1,84 @@
0
+// Package term provides structures and helper functions to work with
1
+// terminal (state, sizes).
2
+//
3
+// Deprecated: use github.com/moby/term instead
4
+package term // import "github.com/docker/docker/pkg/term"
5
+
6
+import (
7
+	"github.com/moby/term"
8
+)
9
+
10
+// EscapeError is special error which returned by a TTY proxy reader's Read()
11
+// method in case its detach escape sequence is read.
12
+// Deprecated: use github.com/moby/term.EscapeError
13
+type EscapeError = term.EscapeError
14
+
15
+// State represents the state of the terminal.
16
+// Deprecated: use github.com/moby/term.State
17
+type State = term.State
18
+
19
+// Winsize represents the size of the terminal window.
20
+// Deprecated: use github.com/moby/term.Winsize
21
+type Winsize = term.Winsize
22
+
23
+var (
24
+	// ASCII list the possible supported ASCII key sequence
25
+	ASCII = term.ASCII
26
+
27
+	// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
28
+	// Deprecated: use github.com/moby/term.ToBytes
29
+	ToBytes = term.ToBytes
30
+
31
+	// StdStreams returns the standard streams (stdin, stdout, stderr).
32
+	// Deprecated: use github.com/moby/term.StdStreams
33
+	StdStreams = term.StdStreams
34
+
35
+	// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
36
+	// Deprecated: use github.com/moby/term.GetFdInfo
37
+	GetFdInfo = term.GetFdInfo
38
+
39
+	// GetWinsize returns the window size based on the specified file descriptor.
40
+	// Deprecated: use github.com/moby/term.GetWinsize
41
+	GetWinsize = term.GetWinsize
42
+
43
+	// IsTerminal returns true if the given file descriptor is a terminal.
44
+	// Deprecated: use github.com/moby/term.IsTerminal
45
+	IsTerminal = term.IsTerminal
46
+
47
+	// RestoreTerminal restores the terminal connected to the given file descriptor
48
+	// to a previous state.
49
+	// Deprecated: use github.com/moby/term.RestoreTerminal
50
+	RestoreTerminal = term.RestoreTerminal
51
+
52
+	// SaveState saves the state of the terminal connected to the given file descriptor.
53
+	// Deprecated: use github.com/moby/term.SaveState
54
+	SaveState = term.SaveState
55
+
56
+	// DisableEcho applies the specified state to the terminal connected to the file
57
+	// descriptor, with echo disabled.
58
+	// Deprecated: use github.com/moby/term.DisableEcho
59
+	DisableEcho = term.DisableEcho
60
+
61
+	// SetRawTerminal puts the terminal connected to the given file descriptor into
62
+	// raw mode and returns the previous state. On UNIX, this puts both the input
63
+	// and output into raw mode. On Windows, it only puts the input into raw mode.
64
+	// Deprecated: use github.com/moby/term.SetRawTerminal
65
+	SetRawTerminal = term.SetRawTerminal
66
+
67
+	// SetRawTerminalOutput puts the output of terminal connected to the given file
68
+	// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
69
+	// state. On Windows, it disables LF -> CRLF translation.
70
+	// Deprecated: use github.com/moby/term.SetRawTerminalOutput
71
+	SetRawTerminalOutput = term.SetRawTerminalOutput
72
+
73
+	// MakeRaw puts the terminal connected to the given file descriptor into raw
74
+	// mode and returns the previous state of the terminal so that it can be restored.
75
+	// Deprecated: use github.com/moby/term.MakeRaw
76
+	MakeRaw = term.MakeRaw
77
+
78
+	// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
79
+	// and detects when the specified escape keys are read, in which case the Read
80
+	// method will return an error of type EscapeError.
81
+	// Deprecated: use github.com/moby/term.NewEscapeProxy
82
+	NewEscapeProxy = term.NewEscapeProxy
83
+)
0 84
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// +build !windows
1
+
2
+package term // import "github.com/docker/docker/pkg/term"
3
+
4
+import (
5
+	"github.com/moby/term"
6
+)
7
+
8
+// Termios is the Unix API for terminal I/O.
9
+// Deprecated: use github.com/moby/term.Termios
10
+type Termios = term.Termios
11
+
12
+var (
13
+	// ErrInvalidState is returned if the state of the terminal is invalid.
14
+	ErrInvalidState = term.ErrInvalidState
15
+
16
+	// SetWinsize tries to set the specified window size for the specified file descriptor.
17
+	// Deprecated: use github.com/moby/term.GetWinsize
18
+	SetWinsize = term.SetWinsize
19
+)
0 20
deleted file mode 100644
... ...
@@ -1,90 +0,0 @@
1
-package term // import "github.com/docker/docker/pkg/term"
2
-
3
-import (
4
-	"io"
5
-)
6
-
7
-// EscapeError is special error which returned by a TTY proxy reader's Read()
8
-// method in case its detach escape sequence is read.
9
-// Deprecated: use github.com/moby/term.EscapeError
10
-type EscapeError struct{}
11
-
12
-func (EscapeError) Error() string {
13
-	return "read escape sequence"
14
-}
15
-
16
-// escapeProxy is used only for attaches with a TTY. It is used to proxy
17
-// stdin keypresses from the underlying reader and look for the passed in
18
-// escape key sequence to signal a detach.
19
-type escapeProxy struct {
20
-	escapeKeys   []byte
21
-	escapeKeyPos int
22
-	r            io.Reader
23
-	buf          []byte
24
-}
25
-
26
-// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
27
-// and detects when the specified escape keys are read, in which case the Read
28
-// method will return an error of type EscapeError.
29
-// Deprecated: use github.com/moby/term.NewEscapeProxy
30
-func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
31
-	return &escapeProxy{
32
-		escapeKeys: escapeKeys,
33
-		r:          r,
34
-	}
35
-}
36
-
37
-func (r *escapeProxy) Read(buf []byte) (n int, err error) {
38
-	if len(r.escapeKeys) > 0 && r.escapeKeyPos == len(r.escapeKeys) {
39
-		return 0, EscapeError{}
40
-	}
41
-
42
-	if len(r.buf) > 0 {
43
-		n = copy(buf, r.buf)
44
-		r.buf = r.buf[n:]
45
-	}
46
-
47
-	nr, err := r.r.Read(buf[n:])
48
-	n += nr
49
-	if len(r.escapeKeys) == 0 {
50
-		return n, err
51
-	}
52
-
53
-	for i := 0; i < n; i++ {
54
-		if buf[i] == r.escapeKeys[r.escapeKeyPos] {
55
-			r.escapeKeyPos++
56
-
57
-			// Check if the full escape sequence is matched.
58
-			if r.escapeKeyPos == len(r.escapeKeys) {
59
-				n = i + 1 - r.escapeKeyPos
60
-				if n < 0 {
61
-					n = 0
62
-				}
63
-				return n, EscapeError{}
64
-			}
65
-			continue
66
-		}
67
-
68
-		// If we need to prepend a partial escape sequence from the previous
69
-		// read, make sure the new buffer size doesn't exceed len(buf).
70
-		// Otherwise, preserve any extra data in a buffer for the next read.
71
-		if i < r.escapeKeyPos {
72
-			preserve := make([]byte, 0, r.escapeKeyPos+n)
73
-			preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
74
-			preserve = append(preserve, buf[:n]...)
75
-			n = copy(buf, preserve)
76
-			i += r.escapeKeyPos
77
-			r.buf = append(r.buf, preserve[n:]...)
78
-		}
79
-		r.escapeKeyPos = 0
80
-	}
81
-
82
-	// If we're in the middle of reading an escape sequence, make sure we don't
83
-	// let the caller read it. If later on we find that this is not the escape
84
-	// sequence, we'll prepend it back to buf.
85
-	n -= r.escapeKeyPos
86
-	if n < 0 {
87
-		n = 0
88
-	}
89
-	return n, err
90
-}
91 1
deleted file mode 100644
... ...
@@ -1,208 +0,0 @@
1
-package term // import "github.com/docker/docker/pkg/term"
2
-
3
-import (
4
-	"bytes"
5
-	"testing"
6
-
7
-	"gotest.tools/v3/assert"
8
-	is "gotest.tools/v3/assert/cmp"
9
-)
10
-
11
-func TestEscapeProxyRead(t *testing.T) {
12
-	t.Run("no escape keys, keys [a]", func(t *testing.T) {
13
-		escapeKeys, _ := ToBytes("")
14
-		keys, _ := ToBytes("a")
15
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
16
-
17
-		buf := make([]byte, len(keys))
18
-		nr, err := reader.Read(buf)
19
-		assert.NilError(t, err)
20
-		assert.Equal(t, nr, len(keys))
21
-		assert.DeepEqual(t, keys, buf)
22
-	})
23
-
24
-	t.Run("no escape keys, keys [a,b,c]", func(t *testing.T) {
25
-		escapeKeys, _ := ToBytes("")
26
-		keys, _ := ToBytes("a,b,c")
27
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
28
-
29
-		buf := make([]byte, len(keys))
30
-		nr, err := reader.Read(buf)
31
-		assert.NilError(t, err)
32
-		assert.Equal(t, nr, len(keys))
33
-		assert.DeepEqual(t, keys, buf)
34
-	})
35
-
36
-	t.Run("no escape keys, no keys", func(t *testing.T) {
37
-		escapeKeys, _ := ToBytes("")
38
-		keys, _ := ToBytes("")
39
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
40
-
41
-		buf := make([]byte, len(keys))
42
-		nr, err := reader.Read(buf)
43
-		assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read")
44
-		assert.Equal(t, nr, 0)
45
-		assert.Check(t, is.Len(keys, 0))
46
-		assert.Check(t, is.Len(buf, 0))
47
-	})
48
-
49
-	t.Run("DEL escape key, keys [a,b,c,+]", func(t *testing.T) {
50
-		escapeKeys, _ := ToBytes("DEL")
51
-		keys, _ := ToBytes("a,b,c,+")
52
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
53
-
54
-		buf := make([]byte, len(keys))
55
-		nr, err := reader.Read(buf)
56
-		assert.NilError(t, err)
57
-		assert.Equal(t, nr, len(keys))
58
-		assert.DeepEqual(t, keys, buf)
59
-	})
60
-
61
-	t.Run("DEL escape key, no keys", func(t *testing.T) {
62
-		escapeKeys, _ := ToBytes("DEL")
63
-		keys, _ := ToBytes("")
64
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
65
-
66
-		buf := make([]byte, len(keys))
67
-		nr, err := reader.Read(buf)
68
-		assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read")
69
-		assert.Equal(t, nr, 0)
70
-		assert.Check(t, is.Len(keys, 0))
71
-		assert.Check(t, is.Len(buf, 0))
72
-	})
73
-
74
-	t.Run("ctrl-x,ctrl-@ escape key, keys [DEL]", func(t *testing.T) {
75
-		escapeKeys, _ := ToBytes("ctrl-x,ctrl-@")
76
-		keys, _ := ToBytes("DEL")
77
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
78
-
79
-		buf := make([]byte, len(keys))
80
-		nr, err := reader.Read(buf)
81
-		assert.NilError(t, err)
82
-		assert.Equal(t, nr, 1)
83
-		assert.DeepEqual(t, keys, buf)
84
-	})
85
-
86
-	t.Run("ctrl-c escape key, keys [ctrl-c]", func(t *testing.T) {
87
-		escapeKeys, _ := ToBytes("ctrl-c")
88
-		keys, _ := ToBytes("ctrl-c")
89
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
90
-
91
-		buf := make([]byte, len(keys))
92
-		nr, err := reader.Read(buf)
93
-		assert.Error(t, err, "read escape sequence")
94
-		assert.Equal(t, nr, 0)
95
-		assert.DeepEqual(t, keys, buf)
96
-	})
97
-
98
-	t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c],[ctrl-z]", func(t *testing.T) {
99
-		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
100
-		keys, _ := ToBytes("ctrl-c,ctrl-z")
101
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
102
-
103
-		buf := make([]byte, 1)
104
-		nr, err := reader.Read(buf)
105
-		assert.NilError(t, err)
106
-		assert.Equal(t, nr, 0)
107
-		assert.DeepEqual(t, keys[0:1], buf)
108
-
109
-		nr, err = reader.Read(buf)
110
-		assert.Error(t, err, "read escape sequence")
111
-		assert.Equal(t, nr, 0)
112
-		assert.DeepEqual(t, keys[1:], buf)
113
-	})
114
-
115
-	t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c,ctrl-z]", func(t *testing.T) {
116
-		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
117
-		keys, _ := ToBytes("ctrl-c,ctrl-z")
118
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
119
-
120
-		buf := make([]byte, 2)
121
-		nr, err := reader.Read(buf)
122
-		assert.Error(t, err, "read escape sequence")
123
-		assert.Equal(t, nr, 0, "nr should be equal to 0")
124
-		assert.DeepEqual(t, keys, buf)
125
-	})
126
-
127
-	t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c],[DEL,+]", func(t *testing.T) {
128
-		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
129
-		keys, _ := ToBytes("ctrl-c,DEL,+")
130
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
131
-
132
-		buf := make([]byte, 1)
133
-		nr, err := reader.Read(buf)
134
-		assert.NilError(t, err)
135
-		assert.Equal(t, nr, 0)
136
-		assert.DeepEqual(t, keys[0:1], buf)
137
-
138
-		buf = make([]byte, len(keys))
139
-		nr, err = reader.Read(buf)
140
-		assert.NilError(t, err)
141
-		assert.Equal(t, nr, len(keys))
142
-		assert.DeepEqual(t, keys, buf)
143
-	})
144
-
145
-	t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c],[DEL]", func(t *testing.T) {
146
-		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
147
-		keys, _ := ToBytes("ctrl-c,DEL")
148
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
149
-
150
-		buf := make([]byte, 1)
151
-		nr, err := reader.Read(buf)
152
-		assert.NilError(t, err)
153
-		assert.Equal(t, nr, 0)
154
-		assert.DeepEqual(t, keys[0:1], buf)
155
-
156
-		buf = make([]byte, len(keys))
157
-		nr, err = reader.Read(buf)
158
-		assert.NilError(t, err)
159
-		assert.Equal(t, nr, len(keys))
160
-		assert.DeepEqual(t, keys, buf)
161
-	})
162
-
163
-	t.Run("a,b,c,d escape key, keys [a,b],[c,d]", func(t *testing.T) {
164
-		escapeKeys, _ := ToBytes("a,b,c,d")
165
-		keys, _ := ToBytes("a,b,c,d")
166
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
167
-
168
-		buf := make([]byte, 2)
169
-		nr, err := reader.Read(buf)
170
-		assert.NilError(t, err)
171
-		assert.Equal(t, 0, nr)
172
-		assert.DeepEqual(t, keys[0:2], buf)
173
-
174
-		buf = make([]byte, 2)
175
-		nr, err = reader.Read(buf)
176
-		assert.Error(t, err, "read escape sequence")
177
-		assert.Equal(t, 0, nr)
178
-		assert.DeepEqual(t, keys[2:4], buf)
179
-	})
180
-
181
-	t.Run("ctrl-p,ctrl-q escape key, keys [ctrl-p],[a],[ctrl-p,ctrl-q]", func(t *testing.T) {
182
-		escapeKeys, _ := ToBytes("ctrl-p,ctrl-q")
183
-		keys, _ := ToBytes("ctrl-p,a,ctrl-p,ctrl-q")
184
-		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
185
-
186
-		buf := make([]byte, 1)
187
-		nr, err := reader.Read(buf)
188
-		assert.NilError(t, err)
189
-		assert.Equal(t, 0, nr)
190
-
191
-		buf = make([]byte, 1)
192
-		nr, err = reader.Read(buf)
193
-		assert.NilError(t, err)
194
-		assert.Equal(t, 1, nr)
195
-		assert.DeepEqual(t, keys[:1], buf)
196
-
197
-		buf = make([]byte, 2)
198
-		nr, err = reader.Read(buf)
199
-		assert.NilError(t, err)
200
-		assert.Equal(t, 1, nr)
201
-		assert.DeepEqual(t, keys[1:3], buf)
202
-
203
-		buf = make([]byte, 2)
204
-		nr, err = reader.Read(buf)
205
-		assert.Error(t, err, "read escape sequence")
206
-		assert.Equal(t, 0, nr)
207
-	})
208
-}
209 1
deleted file mode 100644
... ...
@@ -1,20 +0,0 @@
1
-// +build !windows
2
-
3
-package term // import "github.com/docker/docker/pkg/term"
4
-
5
-import (
6
-	"syscall"
7
-	"unsafe"
8
-
9
-	"golang.org/x/sys/unix"
10
-)
11
-
12
-func tcget(fd uintptr, p *Termios) syscall.Errno {
13
-	_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p)))
14
-	return err
15
-}
16
-
17
-func tcset(fd uintptr, p *Termios) syscall.Errno {
18
-	_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p)))
19
-	return err
20
-}
21 1
deleted file mode 100644
... ...
@@ -1,136 +0,0 @@
1
-// +build !windows
2
-
3
-// Package term provides structures and helper functions to work with
4
-// terminal (state, sizes).
5
-//
6
-// Deprecated: use github.com/moby/term instead
7
-package term // import "github.com/docker/docker/pkg/term"
8
-
9
-import (
10
-	"errors"
11
-	"fmt"
12
-	"io"
13
-	"os"
14
-	"os/signal"
15
-
16
-	"golang.org/x/sys/unix"
17
-)
18
-
19
-var (
20
-	// ErrInvalidState is returned if the state of the terminal is invalid.
21
-	ErrInvalidState = errors.New("Invalid terminal state")
22
-)
23
-
24
-// State represents the state of the terminal.
25
-// Deprecated: use github.com/moby/term.State
26
-type State struct {
27
-	termios Termios
28
-}
29
-
30
-// Winsize represents the size of the terminal window.
31
-// Deprecated: use github.com/moby/term.Winsize
32
-type Winsize struct {
33
-	Height uint16
34
-	Width  uint16
35
-	x      uint16
36
-	y      uint16
37
-}
38
-
39
-// StdStreams returns the standard streams (stdin, stdout, stderr).
40
-// Deprecated: use github.com/moby/term.StdStreams
41
-func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
42
-	return os.Stdin, os.Stdout, os.Stderr
43
-}
44
-
45
-// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
46
-// Deprecated: use github.com/moby/term.GetFdInfo
47
-func GetFdInfo(in interface{}) (uintptr, bool) {
48
-	var inFd uintptr
49
-	var isTerminalIn bool
50
-	if file, ok := in.(*os.File); ok {
51
-		inFd = file.Fd()
52
-		isTerminalIn = IsTerminal(inFd)
53
-	}
54
-	return inFd, isTerminalIn
55
-}
56
-
57
-// IsTerminal returns true if the given file descriptor is a terminal.
58
-// Deprecated: use github.com/moby/term.IsTerminal
59
-func IsTerminal(fd uintptr) bool {
60
-	var termios Termios
61
-	return tcget(fd, &termios) == 0
62
-}
63
-
64
-// RestoreTerminal restores the terminal connected to the given file descriptor
65
-// to a previous state.
66
-// Deprecated: use github.com/moby/term.RestoreTerminal
67
-func RestoreTerminal(fd uintptr, state *State) error {
68
-	if state == nil {
69
-		return ErrInvalidState
70
-	}
71
-	if err := tcset(fd, &state.termios); err != 0 {
72
-		return err
73
-	}
74
-	return nil
75
-}
76
-
77
-// SaveState saves the state of the terminal connected to the given file descriptor.
78
-// Deprecated: use github.com/moby/term.SaveState
79
-func SaveState(fd uintptr) (*State, error) {
80
-	var oldState State
81
-	if err := tcget(fd, &oldState.termios); err != 0 {
82
-		return nil, err
83
-	}
84
-
85
-	return &oldState, nil
86
-}
87
-
88
-// DisableEcho applies the specified state to the terminal connected to the file
89
-// descriptor, with echo disabled.
90
-// Deprecated: use github.com/moby/term.DisableEcho
91
-func DisableEcho(fd uintptr, state *State) error {
92
-	newState := state.termios
93
-	newState.Lflag &^= unix.ECHO
94
-
95
-	if err := tcset(fd, &newState); err != 0 {
96
-		return err
97
-	}
98
-	handleInterrupt(fd, state)
99
-	return nil
100
-}
101
-
102
-// SetRawTerminal puts the terminal connected to the given file descriptor into
103
-// raw mode and returns the previous state. On UNIX, this puts both the input
104
-// and output into raw mode. On Windows, it only puts the input into raw mode.
105
-// Deprecated: use github.com/moby/term.SetRawTerminal
106
-func SetRawTerminal(fd uintptr) (*State, error) {
107
-	oldState, err := MakeRaw(fd)
108
-	if err != nil {
109
-		return nil, err
110
-	}
111
-	handleInterrupt(fd, oldState)
112
-	return oldState, err
113
-}
114
-
115
-// SetRawTerminalOutput puts the output of terminal connected to the given file
116
-// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
117
-// state. On Windows, it disables LF -> CRLF translation.
118
-// Deprecated: use github.com/moby/term.SetRawTerminalOutput
119
-func SetRawTerminalOutput(fd uintptr) (*State, error) {
120
-	return nil, nil
121
-}
122
-
123
-func handleInterrupt(fd uintptr, state *State) {
124
-	sigchan := make(chan os.Signal, 1)
125
-	signal.Notify(sigchan, os.Interrupt)
126
-	go func() {
127
-		for range sigchan {
128
-			// quit cleanly and the new terminal item is on a new line
129
-			fmt.Println()
130
-			signal.Stop(sigchan)
131
-			close(sigchan)
132
-			RestoreTerminal(fd, state)
133
-			os.Exit(1)
134
-		}
135
-	}()
136
-}
137 1
deleted file mode 100644
... ...
@@ -1,117 +0,0 @@
1
-//+build linux
2
-
3
-package term // import "github.com/docker/docker/pkg/term"
4
-
5
-import (
6
-	"io/ioutil"
7
-	"os"
8
-	"testing"
9
-
10
-	"github.com/google/go-cmp/cmp"
11
-	"gotest.tools/v3/assert"
12
-)
13
-
14
-// RequiresRoot skips tests that require root, unless the test.root flag has
15
-// been set
16
-func RequiresRoot(t *testing.T) {
17
-	if os.Getuid() != 0 {
18
-		t.Skip("skipping test that requires root")
19
-		return
20
-	}
21
-}
22
-
23
-func newTtyForTest(t *testing.T) (*os.File, error) {
24
-	RequiresRoot(t)
25
-	return os.OpenFile("/dev/tty", os.O_RDWR, os.ModeDevice)
26
-}
27
-
28
-func newTempFile() (*os.File, error) {
29
-	return ioutil.TempFile(os.TempDir(), "temp")
30
-}
31
-
32
-func TestGetWinsize(t *testing.T) {
33
-	tty, err := newTtyForTest(t)
34
-	assert.NilError(t, err)
35
-	defer tty.Close()
36
-	winSize, err := GetWinsize(tty.Fd())
37
-	assert.NilError(t, err)
38
-	assert.Assert(t, winSize != nil)
39
-
40
-	newSize := Winsize{Width: 200, Height: 200, x: winSize.x, y: winSize.y}
41
-	err = SetWinsize(tty.Fd(), &newSize)
42
-	assert.NilError(t, err)
43
-	winSize, err = GetWinsize(tty.Fd())
44
-	assert.NilError(t, err)
45
-	assert.DeepEqual(t, *winSize, newSize, cmpWinsize)
46
-}
47
-
48
-var cmpWinsize = cmp.AllowUnexported(Winsize{})
49
-
50
-func TestSetWinsize(t *testing.T) {
51
-	tty, err := newTtyForTest(t)
52
-	assert.NilError(t, err)
53
-	defer tty.Close()
54
-	winSize, err := GetWinsize(tty.Fd())
55
-	assert.NilError(t, err)
56
-	assert.Assert(t, winSize != nil)
57
-	newSize := Winsize{Width: 200, Height: 200, x: winSize.x, y: winSize.y}
58
-	err = SetWinsize(tty.Fd(), &newSize)
59
-	assert.NilError(t, err)
60
-	winSize, err = GetWinsize(tty.Fd())
61
-	assert.NilError(t, err)
62
-	assert.DeepEqual(t, *winSize, newSize, cmpWinsize)
63
-}
64
-
65
-func TestGetFdInfo(t *testing.T) {
66
-	tty, err := newTtyForTest(t)
67
-	assert.NilError(t, err)
68
-	defer tty.Close()
69
-	inFd, isTerminal := GetFdInfo(tty)
70
-	assert.Equal(t, inFd, tty.Fd())
71
-	assert.Equal(t, isTerminal, true)
72
-	tmpFile, err := newTempFile()
73
-	assert.NilError(t, err)
74
-	defer tmpFile.Close()
75
-	inFd, isTerminal = GetFdInfo(tmpFile)
76
-	assert.Equal(t, inFd, tmpFile.Fd())
77
-	assert.Equal(t, isTerminal, false)
78
-}
79
-
80
-func TestIsTerminal(t *testing.T) {
81
-	tty, err := newTtyForTest(t)
82
-	assert.NilError(t, err)
83
-	defer tty.Close()
84
-	isTerminal := IsTerminal(tty.Fd())
85
-	assert.Equal(t, isTerminal, true)
86
-	tmpFile, err := newTempFile()
87
-	assert.NilError(t, err)
88
-	defer tmpFile.Close()
89
-	isTerminal = IsTerminal(tmpFile.Fd())
90
-	assert.Equal(t, isTerminal, false)
91
-}
92
-
93
-func TestSaveState(t *testing.T) {
94
-	tty, err := newTtyForTest(t)
95
-	assert.NilError(t, err)
96
-	defer tty.Close()
97
-	state, err := SaveState(tty.Fd())
98
-	assert.NilError(t, err)
99
-	assert.Assert(t, state != nil)
100
-	tty, err = newTtyForTest(t)
101
-	assert.NilError(t, err)
102
-	defer tty.Close()
103
-	err = RestoreTerminal(tty.Fd(), state)
104
-	assert.NilError(t, err)
105
-}
106
-
107
-func TestDisableEcho(t *testing.T) {
108
-	tty, err := newTtyForTest(t)
109
-	assert.NilError(t, err)
110
-	defer tty.Close()
111
-	state, err := SetRawTerminal(tty.Fd())
112
-	defer RestoreTerminal(tty.Fd(), state)
113
-	assert.NilError(t, err)
114
-	assert.Assert(t, state != nil)
115
-	err = DisableEcho(tty.Fd(), state)
116
-	assert.NilError(t, err)
117
-}
118 1
deleted file mode 100644
... ...
@@ -1,233 +0,0 @@
1
-package term // import "github.com/docker/docker/pkg/term"
2
-
3
-import (
4
-	"io"
5
-	"os"
6
-	"os/signal"
7
-	"syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE
8
-
9
-	"github.com/Azure/go-ansiterm/winterm"
10
-	windowsconsole "github.com/docker/docker/pkg/term/windows"
11
-)
12
-
13
-// State holds the console mode for the terminal.
14
-// Deprecated: use github.com/moby/term.State
15
-type State struct {
16
-	mode uint32
17
-}
18
-
19
-// Winsize is used for window size.
20
-// Deprecated: use github.com/moby/term.Winsize
21
-type Winsize struct {
22
-	Height uint16
23
-	Width  uint16
24
-}
25
-
26
-// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
27
-var vtInputSupported bool
28
-
29
-// StdStreams returns the standard streams (stdin, stdout, stderr).
30
-// Deprecated: use github.com/moby/term.StdStreams
31
-func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
32
-	// Turn on VT handling on all std handles, if possible. This might
33
-	// fail, in which case we will fall back to terminal emulation.
34
-	var emulateStdin, emulateStdout, emulateStderr bool
35
-	fd := os.Stdin.Fd()
36
-	if mode, err := winterm.GetConsoleMode(fd); err == nil {
37
-		// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
38
-		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
39
-			emulateStdin = true
40
-		} else {
41
-			vtInputSupported = true
42
-		}
43
-		// Unconditionally set the console mode back even on failure because SetConsoleMode
44
-		// remembers invalid bits on input handles.
45
-		winterm.SetConsoleMode(fd, mode)
46
-	}
47
-
48
-	fd = os.Stdout.Fd()
49
-	if mode, err := winterm.GetConsoleMode(fd); err == nil {
50
-		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
51
-		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
52
-			emulateStdout = true
53
-		} else {
54
-			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
55
-		}
56
-	}
57
-
58
-	fd = os.Stderr.Fd()
59
-	if mode, err := winterm.GetConsoleMode(fd); err == nil {
60
-		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
61
-		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
62
-			emulateStderr = true
63
-		} else {
64
-			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
65
-		}
66
-	}
67
-
68
-	// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
69
-	// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
70
-	// go-ansiterm hasn't switch to x/sys/windows.
71
-	// TODO: switch back to x/sys/windows once go-ansiterm has switched
72
-	if emulateStdin {
73
-		stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE)
74
-	} else {
75
-		stdIn = os.Stdin
76
-	}
77
-
78
-	if emulateStdout {
79
-		stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
80
-	} else {
81
-		stdOut = os.Stdout
82
-	}
83
-
84
-	if emulateStderr {
85
-		stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
86
-	} else {
87
-		stdErr = os.Stderr
88
-	}
89
-
90
-	return
91
-}
92
-
93
-// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
94
-// Deprecated: use github.com/moby/term.GetFdInfo
95
-func GetFdInfo(in interface{}) (uintptr, bool) {
96
-	return windowsconsole.GetHandleInfo(in)
97
-}
98
-
99
-// GetWinsize returns the window size based on the specified file descriptor.
100
-// Deprecated: use github.com/moby/term.GetWinsize
101
-func GetWinsize(fd uintptr) (*Winsize, error) {
102
-	info, err := winterm.GetConsoleScreenBufferInfo(fd)
103
-	if err != nil {
104
-		return nil, err
105
-	}
106
-
107
-	winsize := &Winsize{
108
-		Width:  uint16(info.Window.Right - info.Window.Left + 1),
109
-		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
110
-	}
111
-
112
-	return winsize, nil
113
-}
114
-
115
-// IsTerminal returns true if the given file descriptor is a terminal.
116
-// Deprecated: use github.com/moby/term.IsTerminal
117
-func IsTerminal(fd uintptr) bool {
118
-	return windowsconsole.IsConsole(fd)
119
-}
120
-
121
-// RestoreTerminal restores the terminal connected to the given file descriptor
122
-// to a previous state.
123
-// Deprecated: use github.com/moby/term.RestoreTerminal
124
-func RestoreTerminal(fd uintptr, state *State) error {
125
-	return winterm.SetConsoleMode(fd, state.mode)
126
-}
127
-
128
-// SaveState saves the state of the terminal connected to the given file descriptor.
129
-// Deprecated: use github.com/moby/term.SaveState
130
-func SaveState(fd uintptr) (*State, error) {
131
-	mode, e := winterm.GetConsoleMode(fd)
132
-	if e != nil {
133
-		return nil, e
134
-	}
135
-
136
-	return &State{mode: mode}, nil
137
-}
138
-
139
-// DisableEcho disables echo for the terminal connected to the given file descriptor.
140
-// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
141
-// Deprecated: use github.com/moby/term.DisableEcho
142
-func DisableEcho(fd uintptr, state *State) error {
143
-	mode := state.mode
144
-	mode &^= winterm.ENABLE_ECHO_INPUT
145
-	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
146
-	err := winterm.SetConsoleMode(fd, mode)
147
-	if err != nil {
148
-		return err
149
-	}
150
-
151
-	// Register an interrupt handler to catch and restore prior state
152
-	restoreAtInterrupt(fd, state)
153
-	return nil
154
-}
155
-
156
-// SetRawTerminal puts the terminal connected to the given file descriptor into
157
-// raw mode and returns the previous state. On UNIX, this puts both the input
158
-// and output into raw mode. On Windows, it only puts the input into raw mode.
159
-// Deprecated: use github.com/moby/term.SetRawTerminal
160
-func SetRawTerminal(fd uintptr) (*State, error) {
161
-	state, err := MakeRaw(fd)
162
-	if err != nil {
163
-		return nil, err
164
-	}
165
-
166
-	// Register an interrupt handler to catch and restore prior state
167
-	restoreAtInterrupt(fd, state)
168
-	return state, err
169
-}
170
-
171
-// SetRawTerminalOutput puts the output of terminal connected to the given file
172
-// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
173
-// state. On Windows, it disables LF -> CRLF translation.
174
-// Deprecated: use github.com/moby/term.SetRawTerminalOutput
175
-func SetRawTerminalOutput(fd uintptr) (*State, error) {
176
-	state, err := SaveState(fd)
177
-	if err != nil {
178
-		return nil, err
179
-	}
180
-
181
-	// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
182
-	// version of Windows.
183
-	winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN)
184
-	return state, err
185
-}
186
-
187
-// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
188
-// mode and returns the previous state of the terminal so that it can be restored.
189
-// Deprecated: use github.com/moby/term.MakeRaw
190
-func MakeRaw(fd uintptr) (*State, error) {
191
-	state, err := SaveState(fd)
192
-	if err != nil {
193
-		return nil, err
194
-	}
195
-
196
-	mode := state.mode
197
-
198
-	// See
199
-	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
200
-	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
201
-
202
-	// Disable these modes
203
-	mode &^= winterm.ENABLE_ECHO_INPUT
204
-	mode &^= winterm.ENABLE_LINE_INPUT
205
-	mode &^= winterm.ENABLE_MOUSE_INPUT
206
-	mode &^= winterm.ENABLE_WINDOW_INPUT
207
-	mode &^= winterm.ENABLE_PROCESSED_INPUT
208
-
209
-	// Enable these modes
210
-	mode |= winterm.ENABLE_EXTENDED_FLAGS
211
-	mode |= winterm.ENABLE_INSERT_MODE
212
-	mode |= winterm.ENABLE_QUICK_EDIT_MODE
213
-	if vtInputSupported {
214
-		mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT
215
-	}
216
-
217
-	err = winterm.SetConsoleMode(fd, mode)
218
-	if err != nil {
219
-		return nil, err
220
-	}
221
-	return state, nil
222
-}
223
-
224
-func restoreAtInterrupt(fd uintptr, state *State) {
225
-	sigchan := make(chan os.Signal, 1)
226
-	signal.Notify(sigchan, os.Interrupt)
227
-
228
-	go func() {
229
-		_ = <-sigchan
230
-		RestoreTerminal(fd, state)
231
-		os.Exit(0)
232
-	}()
233
-}
234 1
deleted file mode 100644
... ...
@@ -1,44 +0,0 @@
1
-// +build darwin freebsd openbsd netbsd
2
-
3
-package term // import "github.com/docker/docker/pkg/term"
4
-
5
-import (
6
-	"unsafe"
7
-
8
-	"golang.org/x/sys/unix"
9
-)
10
-
11
-const (
12
-	getTermios = unix.TIOCGETA
13
-	setTermios = unix.TIOCSETA
14
-)
15
-
16
-// Termios is the Unix API for terminal I/O.
17
-// Deprecated: use github.com/moby/term.Termios
18
-type Termios unix.Termios
19
-
20
-// MakeRaw put the terminal connected to the given file descriptor into raw
21
-// mode and returns the previous state of the terminal so that it can be
22
-// restored.
23
-// Deprecated: use github.com/moby/term.MakeRaw
24
-func MakeRaw(fd uintptr) (*State, error) {
25
-	var oldState State
26
-	if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
27
-		return nil, err
28
-	}
29
-
30
-	newState := oldState.termios
31
-	newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
32
-	newState.Oflag &^= unix.OPOST
33
-	newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
34
-	newState.Cflag &^= (unix.CSIZE | unix.PARENB)
35
-	newState.Cflag |= unix.CS8
36
-	newState.Cc[unix.VMIN] = 1
37
-	newState.Cc[unix.VTIME] = 0
38
-
39
-	if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
40
-		return nil, err
41
-	}
42
-
43
-	return &oldState, nil
44
-}
45 1
deleted file mode 100644
... ...
@@ -1,41 +0,0 @@
1
-package term // import "github.com/docker/docker/pkg/term"
2
-
3
-import (
4
-	"golang.org/x/sys/unix"
5
-)
6
-
7
-const (
8
-	getTermios = unix.TCGETS
9
-	setTermios = unix.TCSETS
10
-)
11
-
12
-// Termios is the Unix API for terminal I/O.
13
-// Deprecated: use github.com/moby/term.Termios
14
-type Termios unix.Termios
15
-
16
-// MakeRaw put the terminal connected to the given file descriptor into raw
17
-// mode and returns the previous state of the terminal so that it can be
18
-// restored.
19
-// Deprecated: use github.com/moby/term.MakeRaw
20
-func MakeRaw(fd uintptr) (*State, error) {
21
-	termios, err := unix.IoctlGetTermios(int(fd), getTermios)
22
-	if err != nil {
23
-		return nil, err
24
-	}
25
-
26
-	var oldState State
27
-	oldState.termios = Termios(*termios)
28
-
29
-	termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
30
-	termios.Oflag &^= unix.OPOST
31
-	termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
32
-	termios.Cflag &^= (unix.CSIZE | unix.PARENB)
33
-	termios.Cflag |= unix.CS8
34
-	termios.Cc[unix.VMIN] = 1
35
-	termios.Cc[unix.VTIME] = 0
36
-
37
-	if err := unix.IoctlSetTermios(int(fd), setTermios, termios); err != nil {
38
-		return nil, err
39
-	}
40
-	return &oldState, nil
41
-}
42 1
deleted file mode 100644
... ...
@@ -1,264 +0,0 @@
1
-// +build windows
2
-
3
-package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
4
-
5
-import (
6
-	"bytes"
7
-	"errors"
8
-	"fmt"
9
-	"io"
10
-	"os"
11
-	"strings"
12
-	"unsafe"
13
-
14
-	ansiterm "github.com/Azure/go-ansiterm"
15
-	"github.com/Azure/go-ansiterm/winterm"
16
-)
17
-
18
-const (
19
-	escapeSequence = ansiterm.KEY_ESC_CSI
20
-)
21
-
22
-// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
23
-type ansiReader struct {
24
-	file     *os.File
25
-	fd       uintptr
26
-	buffer   []byte
27
-	cbBuffer int
28
-	command  []byte
29
-}
30
-
31
-// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
32
-// Windows console input handle.
33
-// Deprecated: use github.com/moby/term/windows.NewAnsiReader
34
-func NewAnsiReader(nFile int) io.ReadCloser {
35
-	initLogger()
36
-	file, fd := winterm.GetStdFile(nFile)
37
-	return &ansiReader{
38
-		file:    file,
39
-		fd:      fd,
40
-		command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
41
-		buffer:  make([]byte, 0),
42
-	}
43
-}
44
-
45
-// Close closes the wrapped file.
46
-func (ar *ansiReader) Close() (err error) {
47
-	return ar.file.Close()
48
-}
49
-
50
-// Fd returns the file descriptor of the wrapped file.
51
-func (ar *ansiReader) Fd() uintptr {
52
-	return ar.fd
53
-}
54
-
55
-// Read reads up to len(p) bytes of translated input events into p.
56
-func (ar *ansiReader) Read(p []byte) (int, error) {
57
-	if len(p) == 0 {
58
-		return 0, nil
59
-	}
60
-
61
-	// Previously read bytes exist, read as much as we can and return
62
-	if len(ar.buffer) > 0 {
63
-		logger.Debugf("Reading previously cached bytes")
64
-
65
-		originalLength := len(ar.buffer)
66
-		copiedLength := copy(p, ar.buffer)
67
-
68
-		if copiedLength == originalLength {
69
-			ar.buffer = make([]byte, 0, len(p))
70
-		} else {
71
-			ar.buffer = ar.buffer[copiedLength:]
72
-		}
73
-
74
-		logger.Debugf("Read from cache p[%d]: % x", copiedLength, p)
75
-		return copiedLength, nil
76
-	}
77
-
78
-	// Read and translate key events
79
-	events, err := readInputEvents(ar.fd, len(p))
80
-	if err != nil {
81
-		return 0, err
82
-	} else if len(events) == 0 {
83
-		logger.Debug("No input events detected")
84
-		return 0, nil
85
-	}
86
-
87
-	keyBytes := translateKeyEvents(events, []byte(escapeSequence))
88
-
89
-	// Save excess bytes and right-size keyBytes
90
-	if len(keyBytes) > len(p) {
91
-		logger.Debugf("Received %d keyBytes, only room for %d bytes", len(keyBytes), len(p))
92
-		ar.buffer = keyBytes[len(p):]
93
-		keyBytes = keyBytes[:len(p)]
94
-	} else if len(keyBytes) == 0 {
95
-		logger.Debug("No key bytes returned from the translator")
96
-		return 0, nil
97
-	}
98
-
99
-	copiedLength := copy(p, keyBytes)
100
-	if copiedLength != len(keyBytes) {
101
-		return 0, errors.New("unexpected copy length encountered")
102
-	}
103
-
104
-	logger.Debugf("Read        p[%d]: % x", copiedLength, p)
105
-	logger.Debugf("Read keyBytes[%d]: % x", copiedLength, keyBytes)
106
-	return copiedLength, nil
107
-}
108
-
109
-// readInputEvents polls until at least one event is available.
110
-func readInputEvents(fd uintptr, maxBytes int) ([]winterm.INPUT_RECORD, error) {
111
-	// Determine the maximum number of records to retrieve
112
-	// -- Cast around the type system to obtain the size of a single INPUT_RECORD.
113
-	//    unsafe.Sizeof requires an expression vs. a type-reference; the casting
114
-	//    tricks the type system into believing it has such an expression.
115
-	recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
116
-	countRecords := maxBytes / recordSize
117
-	if countRecords > ansiterm.MAX_INPUT_EVENTS {
118
-		countRecords = ansiterm.MAX_INPUT_EVENTS
119
-	} else if countRecords == 0 {
120
-		countRecords = 1
121
-	}
122
-	logger.Debugf("[windows] readInputEvents: Reading %v records (buffer size %v, record size %v)", countRecords, maxBytes, recordSize)
123
-
124
-	// Wait for and read input events
125
-	events := make([]winterm.INPUT_RECORD, countRecords)
126
-	nEvents := uint32(0)
127
-	eventsExist, err := winterm.WaitForSingleObject(fd, winterm.WAIT_INFINITE)
128
-	if err != nil {
129
-		return nil, err
130
-	}
131
-
132
-	if eventsExist {
133
-		err = winterm.ReadConsoleInput(fd, events, &nEvents)
134
-		if err != nil {
135
-			return nil, err
136
-		}
137
-	}
138
-
139
-	// Return a slice restricted to the number of returned records
140
-	logger.Debugf("[windows] readInputEvents: Read %v events", nEvents)
141
-	return events[:nEvents], nil
142
-}
143
-
144
-// KeyEvent Translation Helpers
145
-
146
-var arrowKeyMapPrefix = map[uint16]string{
147
-	winterm.VK_UP:    "%s%sA",
148
-	winterm.VK_DOWN:  "%s%sB",
149
-	winterm.VK_RIGHT: "%s%sC",
150
-	winterm.VK_LEFT:  "%s%sD",
151
-}
152
-
153
-var keyMapPrefix = map[uint16]string{
154
-	winterm.VK_UP:     "\x1B[%sA",
155
-	winterm.VK_DOWN:   "\x1B[%sB",
156
-	winterm.VK_RIGHT:  "\x1B[%sC",
157
-	winterm.VK_LEFT:   "\x1B[%sD",
158
-	winterm.VK_HOME:   "\x1B[1%s~", // showkey shows ^[[1
159
-	winterm.VK_END:    "\x1B[4%s~", // showkey shows ^[[4
160
-	winterm.VK_INSERT: "\x1B[2%s~",
161
-	winterm.VK_DELETE: "\x1B[3%s~",
162
-	winterm.VK_PRIOR:  "\x1B[5%s~",
163
-	winterm.VK_NEXT:   "\x1B[6%s~",
164
-	winterm.VK_F1:     "",
165
-	winterm.VK_F2:     "",
166
-	winterm.VK_F3:     "\x1B[13%s~",
167
-	winterm.VK_F4:     "\x1B[14%s~",
168
-	winterm.VK_F5:     "\x1B[15%s~",
169
-	winterm.VK_F6:     "\x1B[17%s~",
170
-	winterm.VK_F7:     "\x1B[18%s~",
171
-	winterm.VK_F8:     "\x1B[19%s~",
172
-	winterm.VK_F9:     "\x1B[20%s~",
173
-	winterm.VK_F10:    "\x1B[21%s~",
174
-	winterm.VK_F11:    "\x1B[23%s~",
175
-	winterm.VK_F12:    "\x1B[24%s~",
176
-}
177
-
178
-// translateKeyEvents converts the input events into the appropriate ANSI string.
179
-func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
180
-	var buffer bytes.Buffer
181
-	for _, event := range events {
182
-		if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
183
-			buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
184
-		}
185
-	}
186
-
187
-	return buffer.Bytes()
188
-}
189
-
190
-// keyToString maps the given input event record to the corresponding string.
191
-func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
192
-	if keyEvent.UnicodeChar == 0 {
193
-		return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
194
-	}
195
-
196
-	_, alt, control := getControlKeys(keyEvent.ControlKeyState)
197
-	if control {
198
-		// TODO(azlinux): Implement following control sequences
199
-		// <Ctrl>-D  Signals the end of input from the keyboard; also exits current shell.
200
-		// <Ctrl>-H  Deletes the first character to the left of the cursor. Also called the ERASE key.
201
-		// <Ctrl>-Q  Restarts printing after it has been stopped with <Ctrl>-s.
202
-		// <Ctrl>-S  Suspends printing on the screen (does not stop the program).
203
-		// <Ctrl>-U  Deletes all characters on the current line. Also called the KILL key.
204
-		// <Ctrl>-E  Quits current command and creates a core
205
-
206
-	}
207
-
208
-	// <Alt>+Key generates ESC N Key
209
-	if !control && alt {
210
-		return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
211
-	}
212
-
213
-	return string(keyEvent.UnicodeChar)
214
-}
215
-
216
-// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
217
-func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
218
-	shift, alt, control := getControlKeys(controlState)
219
-	modifier := getControlKeysModifier(shift, alt, control)
220
-
221
-	if format, ok := arrowKeyMapPrefix[key]; ok {
222
-		return fmt.Sprintf(format, escapeSequence, modifier)
223
-	}
224
-
225
-	if format, ok := keyMapPrefix[key]; ok {
226
-		return fmt.Sprintf(format, modifier)
227
-	}
228
-
229
-	return ""
230
-}
231
-
232
-// getControlKeys extracts the shift, alt, and ctrl key states.
233
-func getControlKeys(controlState uint32) (shift, alt, control bool) {
234
-	shift = 0 != (controlState & winterm.SHIFT_PRESSED)
235
-	alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
236
-	control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
237
-	return shift, alt, control
238
-}
239
-
240
-// getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
241
-func getControlKeysModifier(shift, alt, control bool) string {
242
-	if shift && alt && control {
243
-		return ansiterm.KEY_CONTROL_PARAM_8
244
-	}
245
-	if alt && control {
246
-		return ansiterm.KEY_CONTROL_PARAM_7
247
-	}
248
-	if shift && control {
249
-		return ansiterm.KEY_CONTROL_PARAM_6
250
-	}
251
-	if control {
252
-		return ansiterm.KEY_CONTROL_PARAM_5
253
-	}
254
-	if shift && alt {
255
-		return ansiterm.KEY_CONTROL_PARAM_4
256
-	}
257
-	if alt {
258
-		return ansiterm.KEY_CONTROL_PARAM_3
259
-	}
260
-	if shift {
261
-		return ansiterm.KEY_CONTROL_PARAM_2
262
-	}
263
-	return ""
264
-}
265 1
deleted file mode 100644
... ...
@@ -1,65 +0,0 @@
1
-// +build windows
2
-
3
-package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
4
-
5
-import (
6
-	"io"
7
-	"os"
8
-
9
-	ansiterm "github.com/Azure/go-ansiterm"
10
-	"github.com/Azure/go-ansiterm/winterm"
11
-)
12
-
13
-// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation.
14
-type ansiWriter struct {
15
-	file           *os.File
16
-	fd             uintptr
17
-	infoReset      *winterm.CONSOLE_SCREEN_BUFFER_INFO
18
-	command        []byte
19
-	escapeSequence []byte
20
-	inAnsiSequence bool
21
-	parser         *ansiterm.AnsiParser
22
-}
23
-
24
-// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
25
-// Windows console output handle.
26
-// Deprecated: use github.com/moby/term/windows.NewAnsiWriter
27
-func NewAnsiWriter(nFile int) io.Writer {
28
-	initLogger()
29
-	file, fd := winterm.GetStdFile(nFile)
30
-	info, err := winterm.GetConsoleScreenBufferInfo(fd)
31
-	if err != nil {
32
-		return nil
33
-	}
34
-
35
-	parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file))
36
-	logger.Infof("newAnsiWriter: parser %p", parser)
37
-
38
-	aw := &ansiWriter{
39
-		file:           file,
40
-		fd:             fd,
41
-		infoReset:      info,
42
-		command:        make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
43
-		escapeSequence: []byte(ansiterm.KEY_ESC_CSI),
44
-		parser:         parser,
45
-	}
46
-
47
-	logger.Infof("newAnsiWriter: aw.parser %p", aw.parser)
48
-	logger.Infof("newAnsiWriter: %v", aw)
49
-	return aw
50
-}
51
-
52
-func (aw *ansiWriter) Fd() uintptr {
53
-	return aw.fd
54
-}
55
-
56
-// Write writes len(p) bytes from p to the underlying data stream.
57
-func (aw *ansiWriter) Write(p []byte) (total int, err error) {
58
-	if len(p) == 0 {
59
-		return 0, nil
60
-	}
61
-
62
-	logger.Infof("Write: % x", p)
63
-	logger.Infof("Write: %s", string(p))
64
-	return aw.parser.Parse(p)
65
-}
66 1
deleted file mode 100644
... ...
@@ -1,37 +0,0 @@
1
-// +build windows
2
-
3
-package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
4
-
5
-import (
6
-	"os"
7
-
8
-	"github.com/Azure/go-ansiterm/winterm"
9
-)
10
-
11
-// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
12
-// Deprecated: use github.com/moby/term/windows.GetHandleInfo
13
-func GetHandleInfo(in interface{}) (uintptr, bool) {
14
-	switch t := in.(type) {
15
-	case *ansiReader:
16
-		return t.Fd(), true
17
-	case *ansiWriter:
18
-		return t.Fd(), true
19
-	}
20
-
21
-	var inFd uintptr
22
-	var isTerminal bool
23
-
24
-	if file, ok := in.(*os.File); ok {
25
-		inFd = file.Fd()
26
-		isTerminal = IsConsole(inFd)
27
-	}
28
-	return inFd, isTerminal
29
-}
30
-
31
-// IsConsole returns true if the given file descriptor is a Windows Console.
32
-// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
33
-// Deprecated: use github.com/moby/term/windows.IsConsole
34
-func IsConsole(fd uintptr) bool {
35
-	_, e := winterm.GetConsoleMode(fd)
36
-	return e == nil
37
-}
38 1
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+// +build windows
1
+
2
+// Package windowsconsole implements ANSI-aware input and output streams for use
3
+// by the Docker Windows client. When asked for the set of standard streams (e.g.,
4
+// stdin, stdout, stderr), the code will create and return pseudo-streams that
5
+// convert ANSI sequences to / from Windows Console API calls.
6
+//
7
+// Deprecated: use github.com/moby/term/windows instead
8
+package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
9
+
10
+import (
11
+	windowsconsole "github.com/moby/term/windows"
12
+)
13
+
14
+var (
15
+	// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
16
+	// Deprecated: use github.com/moby/term/windows.GetHandleInfo
17
+	GetHandleInfo = windowsconsole.GetHandleInfo
18
+
19
+	// IsConsole returns true if the given file descriptor is a Windows Console.
20
+	// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
21
+	// Deprecated: use github.com/moby/term/windows.IsConsole
22
+	IsConsole = windowsconsole.IsConsole
23
+
24
+	// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
25
+	// Windows console input handle.
26
+	// Deprecated: use github.com/moby/term/windows.NewAnsiReader
27
+	NewAnsiReader = windowsconsole.NewAnsiReader
28
+
29
+	// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
30
+	// Windows console output handle.
31
+	// Deprecated: use github.com/moby/term/windows.NewAnsiWriter
32
+	NewAnsiWriter = windowsconsole.NewAnsiWriter
33
+)
0 34
deleted file mode 100644
... ...
@@ -1,37 +0,0 @@
1
-// +build windows
2
-
3
-// Package windowsconsole implements ANSI-aware input and output streams for use
4
-// by the Docker Windows client. When asked for the set of standard streams (e.g.,
5
-// stdin, stdout, stderr), the code will create and return pseudo-streams that
6
-// convert ANSI sequences to / from Windows Console API calls.
7
-//
8
-// Deprecated: use github.com/moby/term/windows instead
9
-package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
10
-
11
-import (
12
-	"io/ioutil"
13
-	"os"
14
-	"sync"
15
-
16
-	ansiterm "github.com/Azure/go-ansiterm"
17
-	"github.com/sirupsen/logrus"
18
-)
19
-
20
-var logger *logrus.Logger
21
-var initOnce sync.Once
22
-
23
-func initLogger() {
24
-	initOnce.Do(func() {
25
-		logFile := ioutil.Discard
26
-
27
-		if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
28
-			logFile, _ = os.Create("ansiReaderWriter.log")
29
-		}
30
-
31
-		logger = &logrus.Logger{
32
-			Out:       logFile,
33
-			Formatter: new(logrus.TextFormatter),
34
-			Level:     logrus.DebugLevel,
35
-		}
36
-	})
37
-}
38 1
deleted file mode 100644
... ...
@@ -1,22 +0,0 @@
1
-// +build !windows
2
-
3
-package term // import "github.com/docker/docker/pkg/term"
4
-
5
-import (
6
-	"golang.org/x/sys/unix"
7
-)
8
-
9
-// GetWinsize returns the window size based on the specified file descriptor.
10
-// Deprecated: use github.com/moby/term.GetWinsize
11
-func GetWinsize(fd uintptr) (*Winsize, error) {
12
-	uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
13
-	ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
14
-	return ws, err
15
-}
16
-
17
-// SetWinsize tries to set the specified window size for the specified file descriptor.
18
-// Deprecated: use github.com/moby/term.GetWinsize
19
-func SetWinsize(fd uintptr, ws *Winsize) error {
20
-	uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y}
21
-	return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws)
22
-}
... ...
@@ -6,6 +6,7 @@ github.com/golang/gddo                              72a348e765d293ed6d1ded7b6995
6 6
 github.com/google/uuid                              0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1
7 7
 github.com/gorilla/mux                              00bdffe0f3c77e27d2cf6f5c70232a2d3e4d9c15 # v1.7.3
8 8
 github.com/Microsoft/opengcs                        a10967154e143a36014584a6f664344e3bb0aa64
9
+github.com/moby/term                                063f2cd0b49dbb0752774d1cb649998d91424fea
9 10
 
10 11
 github.com/creack/pty                               3a6a957789163cacdfe0e291617a1c8e80612c11 # v1.1.9
11 12
 github.com/konsorten/go-windows-terminal-sequences  f55edac94c9bbba5d6182a4be46d86a2c9b5b50e # v1.0.2
12 13
new file mode 100644
... ...
@@ -0,0 +1,191 @@
0
+
1
+                                 Apache License
2
+                           Version 2.0, January 2004
3
+                        https://www.apache.org/licenses/
4
+
5
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+   1. Definitions.
8
+
9
+      "License" shall mean the terms and conditions for use, reproduction,
10
+      and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+      "Licensor" shall mean the copyright owner or entity authorized by
13
+      the copyright owner that is granting the License.
14
+
15
+      "Legal Entity" shall mean the union of the acting entity and all
16
+      other entities that control, are controlled by, or are under common
17
+      control with that entity. For the purposes of this definition,
18
+      "control" means (i) the power, direct or indirect, to cause the
19
+      direction or management of such entity, whether by contract or
20
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+      outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+      "You" (or "Your") shall mean an individual or Legal Entity
24
+      exercising permissions granted by this License.
25
+
26
+      "Source" form shall mean the preferred form for making modifications,
27
+      including but not limited to software source code, documentation
28
+      source, and configuration files.
29
+
30
+      "Object" form shall mean any form resulting from mechanical
31
+      transformation or translation of a Source form, including but
32
+      not limited to compiled object code, generated documentation,
33
+      and conversions to other media types.
34
+
35
+      "Work" shall mean the work of authorship, whether in Source or
36
+      Object form, made available under the License, as indicated by a
37
+      copyright notice that is included in or attached to the work
38
+      (an example is provided in the Appendix below).
39
+
40
+      "Derivative Works" shall mean any work, whether in Source or Object
41
+      form, that is based on (or derived from) the Work and for which the
42
+      editorial revisions, annotations, elaborations, or other modifications
43
+      represent, as a whole, an original work of authorship. For the purposes
44
+      of this License, Derivative Works shall not include works that remain
45
+      separable from, or merely link (or bind by name) to the interfaces of,
46
+      the Work and Derivative Works thereof.
47
+
48
+      "Contribution" shall mean any work of authorship, including
49
+      the original version of the Work and any modifications or additions
50
+      to that Work or Derivative Works thereof, that is intentionally
51
+      submitted to Licensor for inclusion in the Work by the copyright owner
52
+      or by an individual or Legal Entity authorized to submit on behalf of
53
+      the copyright owner. For the purposes of this definition, "submitted"
54
+      means any form of electronic, verbal, or written communication sent
55
+      to the Licensor or its representatives, including but not limited to
56
+      communication on electronic mailing lists, source code control systems,
57
+      and issue tracking systems that are managed by, or on behalf of, the
58
+      Licensor for the purpose of discussing and improving the Work, but
59
+      excluding communication that is conspicuously marked or otherwise
60
+      designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+      "Contributor" shall mean Licensor and any individual or Legal Entity
63
+      on behalf of whom a Contribution has been received by Licensor and
64
+      subsequently incorporated within the Work.
65
+
66
+   2. Grant of Copyright License. Subject to the terms and conditions of
67
+      this License, each Contributor hereby grants to You a perpetual,
68
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+      copyright license to reproduce, prepare Derivative Works of,
70
+      publicly display, publicly perform, sublicense, and distribute the
71
+      Work and such Derivative Works in Source or Object form.
72
+
73
+   3. Grant of Patent License. Subject to the terms and conditions of
74
+      this License, each Contributor hereby grants to You a perpetual,
75
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+      (except as stated in this section) patent license to make, have made,
77
+      use, offer to sell, sell, import, and otherwise transfer the Work,
78
+      where such license applies only to those patent claims licensable
79
+      by such Contributor that are necessarily infringed by their
80
+      Contribution(s) alone or by combination of their Contribution(s)
81
+      with the Work to which such Contribution(s) was submitted. If You
82
+      institute patent litigation against any entity (including a
83
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+      or a Contribution incorporated within the Work constitutes direct
85
+      or contributory patent infringement, then any patent licenses
86
+      granted to You under this License for that Work shall terminate
87
+      as of the date such litigation is filed.
88
+
89
+   4. Redistribution. You may reproduce and distribute copies of the
90
+      Work or Derivative Works thereof in any medium, with or without
91
+      modifications, and in Source or Object form, provided that You
92
+      meet the following conditions:
93
+
94
+      (a) You must give any other recipients of the Work or
95
+          Derivative Works a copy of this License; and
96
+
97
+      (b) You must cause any modified files to carry prominent notices
98
+          stating that You changed the files; and
99
+
100
+      (c) You must retain, in the Source form of any Derivative Works
101
+          that You distribute, all copyright, patent, trademark, and
102
+          attribution notices from the Source form of the Work,
103
+          excluding those notices that do not pertain to any part of
104
+          the Derivative Works; and
105
+
106
+      (d) If the Work includes a "NOTICE" text file as part of its
107
+          distribution, then any Derivative Works that You distribute must
108
+          include a readable copy of the attribution notices contained
109
+          within such NOTICE file, excluding those notices that do not
110
+          pertain to any part of the Derivative Works, in at least one
111
+          of the following places: within a NOTICE text file distributed
112
+          as part of the Derivative Works; within the Source form or
113
+          documentation, if provided along with the Derivative Works; or,
114
+          within a display generated by the Derivative Works, if and
115
+          wherever such third-party notices normally appear. The contents
116
+          of the NOTICE file are for informational purposes only and
117
+          do not modify the License. You may add Your own attribution
118
+          notices within Derivative Works that You distribute, alongside
119
+          or as an addendum to the NOTICE text from the Work, provided
120
+          that such additional attribution notices cannot be construed
121
+          as modifying the License.
122
+
123
+      You may add Your own copyright statement to Your modifications and
124
+      may provide additional or different license terms and conditions
125
+      for use, reproduction, or distribution of Your modifications, or
126
+      for any such Derivative Works as a whole, provided Your use,
127
+      reproduction, and distribution of the Work otherwise complies with
128
+      the conditions stated in this License.
129
+
130
+   5. Submission of Contributions. Unless You explicitly state otherwise,
131
+      any Contribution intentionally submitted for inclusion in the Work
132
+      by You to the Licensor shall be under the terms and conditions of
133
+      this License, without any additional terms or conditions.
134
+      Notwithstanding the above, nothing herein shall supersede or modify
135
+      the terms of any separate license agreement you may have executed
136
+      with Licensor regarding such Contributions.
137
+
138
+   6. Trademarks. This License does not grant permission to use the trade
139
+      names, trademarks, service marks, or product names of the Licensor,
140
+      except as required for reasonable and customary use in describing the
141
+      origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+   7. Disclaimer of Warranty. Unless required by applicable law or
144
+      agreed to in writing, Licensor provides the Work (and each
145
+      Contributor provides its Contributions) on an "AS IS" BASIS,
146
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+      implied, including, without limitation, any warranties or conditions
148
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+      PARTICULAR PURPOSE. You are solely responsible for determining the
150
+      appropriateness of using or redistributing the Work and assume any
151
+      risks associated with Your exercise of permissions under this License.
152
+
153
+   8. Limitation of Liability. In no event and under no legal theory,
154
+      whether in tort (including negligence), contract, or otherwise,
155
+      unless required by applicable law (such as deliberate and grossly
156
+      negligent acts) or agreed to in writing, shall any Contributor be
157
+      liable to You for damages, including any direct, indirect, special,
158
+      incidental, or consequential damages of any character arising as a
159
+      result of this License or out of the use or inability to use the
160
+      Work (including but not limited to damages for loss of goodwill,
161
+      work stoppage, computer failure or malfunction, or any and all
162
+      other commercial damages or losses), even if such Contributor
163
+      has been advised of the possibility of such damages.
164
+
165
+   9. Accepting Warranty or Additional Liability. While redistributing
166
+      the Work or Derivative Works thereof, You may choose to offer,
167
+      and charge a fee for, acceptance of support, warranty, indemnity,
168
+      or other liability obligations and/or rights consistent with this
169
+      License. However, in accepting such obligations, You may act only
170
+      on Your own behalf and on Your sole responsibility, not on behalf
171
+      of any other Contributor, and only if You agree to indemnify,
172
+      defend, and hold each Contributor harmless for any liability
173
+      incurred by, or claims asserted against, such Contributor by reason
174
+      of your accepting any such warranty or additional liability.
175
+
176
+   END OF TERMS AND CONDITIONS
177
+
178
+   Copyright 2013-2018 Docker, Inc.
179
+
180
+   Licensed under the Apache License, Version 2.0 (the "License");
181
+   you may not use this file except in compliance with the License.
182
+   You may obtain a copy of the License at
183
+
184
+       https://www.apache.org/licenses/LICENSE-2.0
185
+
186
+   Unless required by applicable law or agreed to in writing, software
187
+   distributed under the License is distributed on an "AS IS" BASIS,
188
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+   See the License for the specific language governing permissions and
190
+   limitations under the License.
0 191
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+# term - utilities for dealing with terminals
1
+
2
+![](https://github.com/moby/term/workflows/.github/workflows/test.yml/badge.svg) [![GoDoc](https://godoc.org/github.com/moby/term?status.svg)](https://godoc.org/github.com/moby/term) [![Go Report Card](https://goreportcard.com/badge/github.com/moby/term)](https://goreportcard.com/report/github.com/moby/term)
3
+
4
+term provides structures and helper functions to work with terminal (state, sizes).
5
+
6
+#### Using term
7
+
8
+```go
9
+package main
10
+
11
+import (
12
+	"log"
13
+	"os"
14
+
15
+	"github.com/moby/term"
16
+)
17
+
18
+func main() {
19
+	fd := os.Stdin.Fd()
20
+	if term.IsTerminal(fd) {
21
+		ws, err := term.GetWinsize(fd)
22
+		if err != nil {
23
+			log.Fatalf("term.GetWinsize: %s", err)
24
+		}
25
+		log.Printf("%d:%d\n", ws.Height, ws.Width)
26
+	}
27
+}
28
+```
29
+
30
+## Contributing
31
+
32
+Want to hack on term? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply.
33
+
34
+## Copyright and license
35
+Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons.
0 36
new file mode 100644
... ...
@@ -0,0 +1,66 @@
0
+package term
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+)
6
+
7
+// ASCII list the possible supported ASCII key sequence
8
+var ASCII = []string{
9
+	"ctrl-@",
10
+	"ctrl-a",
11
+	"ctrl-b",
12
+	"ctrl-c",
13
+	"ctrl-d",
14
+	"ctrl-e",
15
+	"ctrl-f",
16
+	"ctrl-g",
17
+	"ctrl-h",
18
+	"ctrl-i",
19
+	"ctrl-j",
20
+	"ctrl-k",
21
+	"ctrl-l",
22
+	"ctrl-m",
23
+	"ctrl-n",
24
+	"ctrl-o",
25
+	"ctrl-p",
26
+	"ctrl-q",
27
+	"ctrl-r",
28
+	"ctrl-s",
29
+	"ctrl-t",
30
+	"ctrl-u",
31
+	"ctrl-v",
32
+	"ctrl-w",
33
+	"ctrl-x",
34
+	"ctrl-y",
35
+	"ctrl-z",
36
+	"ctrl-[",
37
+	"ctrl-\\",
38
+	"ctrl-]",
39
+	"ctrl-^",
40
+	"ctrl-_",
41
+}
42
+
43
+// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
44
+func ToBytes(keys string) ([]byte, error) {
45
+	codes := []byte{}
46
+next:
47
+	for _, key := range strings.Split(keys, ",") {
48
+		if len(key) != 1 {
49
+			for code, ctrl := range ASCII {
50
+				if ctrl == key {
51
+					codes = append(codes, byte(code))
52
+					continue next
53
+				}
54
+			}
55
+			if key == "DEL" {
56
+				codes = append(codes, 127)
57
+			} else {
58
+				return nil, fmt.Errorf("Unknown character: '%s'", key)
59
+			}
60
+		} else {
61
+			codes = append(codes, key[0])
62
+		}
63
+	}
64
+	return codes, nil
65
+}
0 66
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+module github.com/moby/term
1
+
2
+go 1.13
3
+
4
+require (
5
+	github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78
6
+	github.com/google/go-cmp v0.3.1
7
+	github.com/pkg/errors v0.9.1 // indirect
8
+	github.com/sirupsen/logrus v1.4.2
9
+	golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
10
+	gotest.tools v2.2.0+incompatible
11
+	gotest.tools/v3 v3.0.2 // indirect
12
+)
0 13
new file mode 100644
... ...
@@ -0,0 +1,88 @@
0
+package term
1
+
2
+import (
3
+	"io"
4
+)
5
+
6
+// EscapeError is special error which returned by a TTY proxy reader's Read()
7
+// method in case its detach escape sequence is read.
8
+type EscapeError struct{}
9
+
10
+func (EscapeError) Error() string {
11
+	return "read escape sequence"
12
+}
13
+
14
+// escapeProxy is used only for attaches with a TTY. It is used to proxy
15
+// stdin keypresses from the underlying reader and look for the passed in
16
+// escape key sequence to signal a detach.
17
+type escapeProxy struct {
18
+	escapeKeys   []byte
19
+	escapeKeyPos int
20
+	r            io.Reader
21
+	buf          []byte
22
+}
23
+
24
+// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
25
+// and detects when the specified escape keys are read, in which case the Read
26
+// method will return an error of type EscapeError.
27
+func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
28
+	return &escapeProxy{
29
+		escapeKeys: escapeKeys,
30
+		r:          r,
31
+	}
32
+}
33
+
34
+func (r *escapeProxy) Read(buf []byte) (n int, err error) {
35
+	if len(r.escapeKeys) > 0 && r.escapeKeyPos == len(r.escapeKeys) {
36
+		return 0, EscapeError{}
37
+	}
38
+
39
+	if len(r.buf) > 0 {
40
+		n = copy(buf, r.buf)
41
+		r.buf = r.buf[n:]
42
+	}
43
+
44
+	nr, err := r.r.Read(buf[n:])
45
+	n += nr
46
+	if len(r.escapeKeys) == 0 {
47
+		return n, err
48
+	}
49
+
50
+	for i := 0; i < n; i++ {
51
+		if buf[i] == r.escapeKeys[r.escapeKeyPos] {
52
+			r.escapeKeyPos++
53
+
54
+			// Check if the full escape sequence is matched.
55
+			if r.escapeKeyPos == len(r.escapeKeys) {
56
+				n = i + 1 - r.escapeKeyPos
57
+				if n < 0 {
58
+					n = 0
59
+				}
60
+				return n, EscapeError{}
61
+			}
62
+			continue
63
+		}
64
+
65
+		// If we need to prepend a partial escape sequence from the previous
66
+		// read, make sure the new buffer size doesn't exceed len(buf).
67
+		// Otherwise, preserve any extra data in a buffer for the next read.
68
+		if i < r.escapeKeyPos {
69
+			preserve := make([]byte, 0, r.escapeKeyPos+n)
70
+			preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
71
+			preserve = append(preserve, buf[:n]...)
72
+			n = copy(buf, preserve)
73
+			i += r.escapeKeyPos
74
+			r.buf = append(r.buf, preserve[n:]...)
75
+		}
76
+		r.escapeKeyPos = 0
77
+	}
78
+
79
+	// If we're in the middle of reading an escape sequence, make sure we don't
80
+	// let the caller read it. If later on we find that this is not the escape
81
+	// sequence, we'll prepend it back to buf.
82
+	n -= r.escapeKeyPos
83
+	if n < 0 {
84
+		n = 0
85
+	}
86
+	return n, err
87
+}
0 88
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// +build !windows
1
+
2
+package term
3
+
4
+import (
5
+	"syscall"
6
+	"unsafe"
7
+
8
+	"golang.org/x/sys/unix"
9
+)
10
+
11
+func tcget(fd uintptr, p *Termios) syscall.Errno {
12
+	_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p)))
13
+	return err
14
+}
15
+
16
+func tcset(fd uintptr, p *Termios) syscall.Errno {
17
+	_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p)))
18
+	return err
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,124 @@
0
+// +build !windows
1
+
2
+// Package term provides structures and helper functions to work with
3
+// terminal (state, sizes).
4
+package term
5
+
6
+import (
7
+	"errors"
8
+	"fmt"
9
+	"io"
10
+	"os"
11
+	"os/signal"
12
+
13
+	"golang.org/x/sys/unix"
14
+)
15
+
16
+var (
17
+	// ErrInvalidState is returned if the state of the terminal is invalid.
18
+	ErrInvalidState = errors.New("Invalid terminal state")
19
+)
20
+
21
+// State represents the state of the terminal.
22
+type State struct {
23
+	termios Termios
24
+}
25
+
26
+// Winsize represents the size of the terminal window.
27
+type Winsize struct {
28
+	Height uint16
29
+	Width  uint16
30
+	x      uint16
31
+	y      uint16
32
+}
33
+
34
+// StdStreams returns the standard streams (stdin, stdout, stderr).
35
+func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
36
+	return os.Stdin, os.Stdout, os.Stderr
37
+}
38
+
39
+// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
40
+func GetFdInfo(in interface{}) (uintptr, bool) {
41
+	var inFd uintptr
42
+	var isTerminalIn bool
43
+	if file, ok := in.(*os.File); ok {
44
+		inFd = file.Fd()
45
+		isTerminalIn = IsTerminal(inFd)
46
+	}
47
+	return inFd, isTerminalIn
48
+}
49
+
50
+// IsTerminal returns true if the given file descriptor is a terminal.
51
+func IsTerminal(fd uintptr) bool {
52
+	var termios Termios
53
+	return tcget(fd, &termios) == 0
54
+}
55
+
56
+// RestoreTerminal restores the terminal connected to the given file descriptor
57
+// to a previous state.
58
+func RestoreTerminal(fd uintptr, state *State) error {
59
+	if state == nil {
60
+		return ErrInvalidState
61
+	}
62
+	if err := tcset(fd, &state.termios); err != 0 {
63
+		return err
64
+	}
65
+	return nil
66
+}
67
+
68
+// SaveState saves the state of the terminal connected to the given file descriptor.
69
+func SaveState(fd uintptr) (*State, error) {
70
+	var oldState State
71
+	if err := tcget(fd, &oldState.termios); err != 0 {
72
+		return nil, err
73
+	}
74
+
75
+	return &oldState, nil
76
+}
77
+
78
+// DisableEcho applies the specified state to the terminal connected to the file
79
+// descriptor, with echo disabled.
80
+func DisableEcho(fd uintptr, state *State) error {
81
+	newState := state.termios
82
+	newState.Lflag &^= unix.ECHO
83
+
84
+	if err := tcset(fd, &newState); err != 0 {
85
+		return err
86
+	}
87
+	handleInterrupt(fd, state)
88
+	return nil
89
+}
90
+
91
+// SetRawTerminal puts the terminal connected to the given file descriptor into
92
+// raw mode and returns the previous state. On UNIX, this puts both the input
93
+// and output into raw mode. On Windows, it only puts the input into raw mode.
94
+func SetRawTerminal(fd uintptr) (*State, error) {
95
+	oldState, err := MakeRaw(fd)
96
+	if err != nil {
97
+		return nil, err
98
+	}
99
+	handleInterrupt(fd, oldState)
100
+	return oldState, err
101
+}
102
+
103
+// SetRawTerminalOutput puts the output of terminal connected to the given file
104
+// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
105
+// state. On Windows, it disables LF -> CRLF translation.
106
+func SetRawTerminalOutput(fd uintptr) (*State, error) {
107
+	return nil, nil
108
+}
109
+
110
+func handleInterrupt(fd uintptr, state *State) {
111
+	sigchan := make(chan os.Signal, 1)
112
+	signal.Notify(sigchan, os.Interrupt)
113
+	go func() {
114
+		for range sigchan {
115
+			// quit cleanly and the new terminal item is on a new line
116
+			fmt.Println()
117
+			signal.Stop(sigchan)
118
+			close(sigchan)
119
+			RestoreTerminal(fd, state)
120
+			os.Exit(1)
121
+		}
122
+	}()
123
+}
0 124
new file mode 100644
... ...
@@ -0,0 +1,221 @@
0
+package term
1
+
2
+import (
3
+	"io"
4
+	"os"
5
+	"os/signal"
6
+	"syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE
7
+
8
+	"github.com/Azure/go-ansiterm/winterm"
9
+	windowsconsole "github.com/moby/term/windows"
10
+)
11
+
12
+// State holds the console mode for the terminal.
13
+type State struct {
14
+	mode uint32
15
+}
16
+
17
+// Winsize is used for window size.
18
+type Winsize struct {
19
+	Height uint16
20
+	Width  uint16
21
+}
22
+
23
+// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
24
+var vtInputSupported bool
25
+
26
+// StdStreams returns the standard streams (stdin, stdout, stderr).
27
+func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
28
+	// Turn on VT handling on all std handles, if possible. This might
29
+	// fail, in which case we will fall back to terminal emulation.
30
+	var emulateStdin, emulateStdout, emulateStderr bool
31
+	fd := os.Stdin.Fd()
32
+	if mode, err := winterm.GetConsoleMode(fd); err == nil {
33
+		// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
34
+		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
35
+			emulateStdin = true
36
+		} else {
37
+			vtInputSupported = true
38
+		}
39
+		// Unconditionally set the console mode back even on failure because SetConsoleMode
40
+		// remembers invalid bits on input handles.
41
+		winterm.SetConsoleMode(fd, mode)
42
+	}
43
+
44
+	fd = os.Stdout.Fd()
45
+	if mode, err := winterm.GetConsoleMode(fd); err == nil {
46
+		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
47
+		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
48
+			emulateStdout = true
49
+		} else {
50
+			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
51
+		}
52
+	}
53
+
54
+	fd = os.Stderr.Fd()
55
+	if mode, err := winterm.GetConsoleMode(fd); err == nil {
56
+		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
57
+		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
58
+			emulateStderr = true
59
+		} else {
60
+			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
61
+		}
62
+	}
63
+
64
+	// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
65
+	// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
66
+	// go-ansiterm hasn't switch to x/sys/windows.
67
+	// TODO: switch back to x/sys/windows once go-ansiterm has switched
68
+	if emulateStdin {
69
+		stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE)
70
+	} else {
71
+		stdIn = os.Stdin
72
+	}
73
+
74
+	if emulateStdout {
75
+		stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
76
+	} else {
77
+		stdOut = os.Stdout
78
+	}
79
+
80
+	if emulateStderr {
81
+		stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
82
+	} else {
83
+		stdErr = os.Stderr
84
+	}
85
+
86
+	return
87
+}
88
+
89
+// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
90
+func GetFdInfo(in interface{}) (uintptr, bool) {
91
+	return windowsconsole.GetHandleInfo(in)
92
+}
93
+
94
+// GetWinsize returns the window size based on the specified file descriptor.
95
+func GetWinsize(fd uintptr) (*Winsize, error) {
96
+	info, err := winterm.GetConsoleScreenBufferInfo(fd)
97
+	if err != nil {
98
+		return nil, err
99
+	}
100
+
101
+	winsize := &Winsize{
102
+		Width:  uint16(info.Window.Right - info.Window.Left + 1),
103
+		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
104
+	}
105
+
106
+	return winsize, nil
107
+}
108
+
109
+// IsTerminal returns true if the given file descriptor is a terminal.
110
+func IsTerminal(fd uintptr) bool {
111
+	return windowsconsole.IsConsole(fd)
112
+}
113
+
114
+// RestoreTerminal restores the terminal connected to the given file descriptor
115
+// to a previous state.
116
+func RestoreTerminal(fd uintptr, state *State) error {
117
+	return winterm.SetConsoleMode(fd, state.mode)
118
+}
119
+
120
+// SaveState saves the state of the terminal connected to the given file descriptor.
121
+func SaveState(fd uintptr) (*State, error) {
122
+	mode, e := winterm.GetConsoleMode(fd)
123
+	if e != nil {
124
+		return nil, e
125
+	}
126
+
127
+	return &State{mode: mode}, nil
128
+}
129
+
130
+// DisableEcho disables echo for the terminal connected to the given file descriptor.
131
+// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
132
+func DisableEcho(fd uintptr, state *State) error {
133
+	mode := state.mode
134
+	mode &^= winterm.ENABLE_ECHO_INPUT
135
+	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
136
+	err := winterm.SetConsoleMode(fd, mode)
137
+	if err != nil {
138
+		return err
139
+	}
140
+
141
+	// Register an interrupt handler to catch and restore prior state
142
+	restoreAtInterrupt(fd, state)
143
+	return nil
144
+}
145
+
146
+// SetRawTerminal puts the terminal connected to the given file descriptor into
147
+// raw mode and returns the previous state. On UNIX, this puts both the input
148
+// and output into raw mode. On Windows, it only puts the input into raw mode.
149
+func SetRawTerminal(fd uintptr) (*State, error) {
150
+	state, err := MakeRaw(fd)
151
+	if err != nil {
152
+		return nil, err
153
+	}
154
+
155
+	// Register an interrupt handler to catch and restore prior state
156
+	restoreAtInterrupt(fd, state)
157
+	return state, err
158
+}
159
+
160
+// SetRawTerminalOutput puts the output of terminal connected to the given file
161
+// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
162
+// state. On Windows, it disables LF -> CRLF translation.
163
+func SetRawTerminalOutput(fd uintptr) (*State, error) {
164
+	state, err := SaveState(fd)
165
+	if err != nil {
166
+		return nil, err
167
+	}
168
+
169
+	// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
170
+	// version of Windows.
171
+	winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN)
172
+	return state, err
173
+}
174
+
175
+// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
176
+// mode and returns the previous state of the terminal so that it can be restored.
177
+func MakeRaw(fd uintptr) (*State, error) {
178
+	state, err := SaveState(fd)
179
+	if err != nil {
180
+		return nil, err
181
+	}
182
+
183
+	mode := state.mode
184
+
185
+	// See
186
+	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
187
+	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
188
+
189
+	// Disable these modes
190
+	mode &^= winterm.ENABLE_ECHO_INPUT
191
+	mode &^= winterm.ENABLE_LINE_INPUT
192
+	mode &^= winterm.ENABLE_MOUSE_INPUT
193
+	mode &^= winterm.ENABLE_WINDOW_INPUT
194
+	mode &^= winterm.ENABLE_PROCESSED_INPUT
195
+
196
+	// Enable these modes
197
+	mode |= winterm.ENABLE_EXTENDED_FLAGS
198
+	mode |= winterm.ENABLE_INSERT_MODE
199
+	mode |= winterm.ENABLE_QUICK_EDIT_MODE
200
+	if vtInputSupported {
201
+		mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT
202
+	}
203
+
204
+	err = winterm.SetConsoleMode(fd, mode)
205
+	if err != nil {
206
+		return nil, err
207
+	}
208
+	return state, nil
209
+}
210
+
211
+func restoreAtInterrupt(fd uintptr, state *State) {
212
+	sigchan := make(chan os.Signal, 1)
213
+	signal.Notify(sigchan, os.Interrupt)
214
+
215
+	go func() {
216
+		_ = <-sigchan
217
+		RestoreTerminal(fd, state)
218
+		os.Exit(0)
219
+	}()
220
+}
0 221
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+// +build darwin freebsd openbsd netbsd
1
+
2
+package term
3
+
4
+import (
5
+	"unsafe"
6
+
7
+	"golang.org/x/sys/unix"
8
+)
9
+
10
+const (
11
+	getTermios = unix.TIOCGETA
12
+	setTermios = unix.TIOCSETA
13
+)
14
+
15
+// Termios is the Unix API for terminal I/O.
16
+type Termios unix.Termios
17
+
18
+// MakeRaw put the terminal connected to the given file descriptor into raw
19
+// mode and returns the previous state of the terminal so that it can be
20
+// restored.
21
+func MakeRaw(fd uintptr) (*State, error) {
22
+	var oldState State
23
+	if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
24
+		return nil, err
25
+	}
26
+
27
+	newState := oldState.termios
28
+	newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
29
+	newState.Oflag &^= unix.OPOST
30
+	newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
31
+	newState.Cflag &^= (unix.CSIZE | unix.PARENB)
32
+	newState.Cflag |= unix.CS8
33
+	newState.Cc[unix.VMIN] = 1
34
+	newState.Cc[unix.VTIME] = 0
35
+
36
+	if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
37
+		return nil, err
38
+	}
39
+
40
+	return &oldState, nil
41
+}
0 42
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+package term
1
+
2
+import (
3
+	"golang.org/x/sys/unix"
4
+)
5
+
6
+const (
7
+	getTermios = unix.TCGETS
8
+	setTermios = unix.TCSETS
9
+)
10
+
11
+// Termios is the Unix API for terminal I/O.
12
+type Termios unix.Termios
13
+
14
+// MakeRaw put the terminal connected to the given file descriptor into raw
15
+// mode and returns the previous state of the terminal so that it can be
16
+// restored.
17
+func MakeRaw(fd uintptr) (*State, error) {
18
+	termios, err := unix.IoctlGetTermios(int(fd), getTermios)
19
+	if err != nil {
20
+		return nil, err
21
+	}
22
+
23
+	var oldState State
24
+	oldState.termios = Termios(*termios)
25
+
26
+	termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
27
+	termios.Oflag &^= unix.OPOST
28
+	termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
29
+	termios.Cflag &^= (unix.CSIZE | unix.PARENB)
30
+	termios.Cflag |= unix.CS8
31
+	termios.Cc[unix.VMIN] = 1
32
+	termios.Cc[unix.VTIME] = 0
33
+
34
+	if err := unix.IoctlSetTermios(int(fd), setTermios, termios); err != nil {
35
+		return nil, err
36
+	}
37
+	return &oldState, nil
38
+}
0 39
new file mode 100644
... ...
@@ -0,0 +1,263 @@
0
+// +build windows
1
+
2
+package windowsconsole // import "github.com/moby/term/windows"
3
+
4
+import (
5
+	"bytes"
6
+	"errors"
7
+	"fmt"
8
+	"io"
9
+	"os"
10
+	"strings"
11
+	"unsafe"
12
+
13
+	ansiterm "github.com/Azure/go-ansiterm"
14
+	"github.com/Azure/go-ansiterm/winterm"
15
+)
16
+
17
+const (
18
+	escapeSequence = ansiterm.KEY_ESC_CSI
19
+)
20
+
21
+// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
22
+type ansiReader struct {
23
+	file     *os.File
24
+	fd       uintptr
25
+	buffer   []byte
26
+	cbBuffer int
27
+	command  []byte
28
+}
29
+
30
+// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
31
+// Windows console input handle.
32
+func NewAnsiReader(nFile int) io.ReadCloser {
33
+	initLogger()
34
+	file, fd := winterm.GetStdFile(nFile)
35
+	return &ansiReader{
36
+		file:    file,
37
+		fd:      fd,
38
+		command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
39
+		buffer:  make([]byte, 0),
40
+	}
41
+}
42
+
43
+// Close closes the wrapped file.
44
+func (ar *ansiReader) Close() (err error) {
45
+	return ar.file.Close()
46
+}
47
+
48
+// Fd returns the file descriptor of the wrapped file.
49
+func (ar *ansiReader) Fd() uintptr {
50
+	return ar.fd
51
+}
52
+
53
+// Read reads up to len(p) bytes of translated input events into p.
54
+func (ar *ansiReader) Read(p []byte) (int, error) {
55
+	if len(p) == 0 {
56
+		return 0, nil
57
+	}
58
+
59
+	// Previously read bytes exist, read as much as we can and return
60
+	if len(ar.buffer) > 0 {
61
+		logger.Debugf("Reading previously cached bytes")
62
+
63
+		originalLength := len(ar.buffer)
64
+		copiedLength := copy(p, ar.buffer)
65
+
66
+		if copiedLength == originalLength {
67
+			ar.buffer = make([]byte, 0, len(p))
68
+		} else {
69
+			ar.buffer = ar.buffer[copiedLength:]
70
+		}
71
+
72
+		logger.Debugf("Read from cache p[%d]: % x", copiedLength, p)
73
+		return copiedLength, nil
74
+	}
75
+
76
+	// Read and translate key events
77
+	events, err := readInputEvents(ar.fd, len(p))
78
+	if err != nil {
79
+		return 0, err
80
+	} else if len(events) == 0 {
81
+		logger.Debug("No input events detected")
82
+		return 0, nil
83
+	}
84
+
85
+	keyBytes := translateKeyEvents(events, []byte(escapeSequence))
86
+
87
+	// Save excess bytes and right-size keyBytes
88
+	if len(keyBytes) > len(p) {
89
+		logger.Debugf("Received %d keyBytes, only room for %d bytes", len(keyBytes), len(p))
90
+		ar.buffer = keyBytes[len(p):]
91
+		keyBytes = keyBytes[:len(p)]
92
+	} else if len(keyBytes) == 0 {
93
+		logger.Debug("No key bytes returned from the translator")
94
+		return 0, nil
95
+	}
96
+
97
+	copiedLength := copy(p, keyBytes)
98
+	if copiedLength != len(keyBytes) {
99
+		return 0, errors.New("unexpected copy length encountered")
100
+	}
101
+
102
+	logger.Debugf("Read        p[%d]: % x", copiedLength, p)
103
+	logger.Debugf("Read keyBytes[%d]: % x", copiedLength, keyBytes)
104
+	return copiedLength, nil
105
+}
106
+
107
+// readInputEvents polls until at least one event is available.
108
+func readInputEvents(fd uintptr, maxBytes int) ([]winterm.INPUT_RECORD, error) {
109
+	// Determine the maximum number of records to retrieve
110
+	// -- Cast around the type system to obtain the size of a single INPUT_RECORD.
111
+	//    unsafe.Sizeof requires an expression vs. a type-reference; the casting
112
+	//    tricks the type system into believing it has such an expression.
113
+	recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
114
+	countRecords := maxBytes / recordSize
115
+	if countRecords > ansiterm.MAX_INPUT_EVENTS {
116
+		countRecords = ansiterm.MAX_INPUT_EVENTS
117
+	} else if countRecords == 0 {
118
+		countRecords = 1
119
+	}
120
+	logger.Debugf("[windows] readInputEvents: Reading %v records (buffer size %v, record size %v)", countRecords, maxBytes, recordSize)
121
+
122
+	// Wait for and read input events
123
+	events := make([]winterm.INPUT_RECORD, countRecords)
124
+	nEvents := uint32(0)
125
+	eventsExist, err := winterm.WaitForSingleObject(fd, winterm.WAIT_INFINITE)
126
+	if err != nil {
127
+		return nil, err
128
+	}
129
+
130
+	if eventsExist {
131
+		err = winterm.ReadConsoleInput(fd, events, &nEvents)
132
+		if err != nil {
133
+			return nil, err
134
+		}
135
+	}
136
+
137
+	// Return a slice restricted to the number of returned records
138
+	logger.Debugf("[windows] readInputEvents: Read %v events", nEvents)
139
+	return events[:nEvents], nil
140
+}
141
+
142
+// KeyEvent Translation Helpers
143
+
144
+var arrowKeyMapPrefix = map[uint16]string{
145
+	winterm.VK_UP:    "%s%sA",
146
+	winterm.VK_DOWN:  "%s%sB",
147
+	winterm.VK_RIGHT: "%s%sC",
148
+	winterm.VK_LEFT:  "%s%sD",
149
+}
150
+
151
+var keyMapPrefix = map[uint16]string{
152
+	winterm.VK_UP:     "\x1B[%sA",
153
+	winterm.VK_DOWN:   "\x1B[%sB",
154
+	winterm.VK_RIGHT:  "\x1B[%sC",
155
+	winterm.VK_LEFT:   "\x1B[%sD",
156
+	winterm.VK_HOME:   "\x1B[1%s~", // showkey shows ^[[1
157
+	winterm.VK_END:    "\x1B[4%s~", // showkey shows ^[[4
158
+	winterm.VK_INSERT: "\x1B[2%s~",
159
+	winterm.VK_DELETE: "\x1B[3%s~",
160
+	winterm.VK_PRIOR:  "\x1B[5%s~",
161
+	winterm.VK_NEXT:   "\x1B[6%s~",
162
+	winterm.VK_F1:     "",
163
+	winterm.VK_F2:     "",
164
+	winterm.VK_F3:     "\x1B[13%s~",
165
+	winterm.VK_F4:     "\x1B[14%s~",
166
+	winterm.VK_F5:     "\x1B[15%s~",
167
+	winterm.VK_F6:     "\x1B[17%s~",
168
+	winterm.VK_F7:     "\x1B[18%s~",
169
+	winterm.VK_F8:     "\x1B[19%s~",
170
+	winterm.VK_F9:     "\x1B[20%s~",
171
+	winterm.VK_F10:    "\x1B[21%s~",
172
+	winterm.VK_F11:    "\x1B[23%s~",
173
+	winterm.VK_F12:    "\x1B[24%s~",
174
+}
175
+
176
+// translateKeyEvents converts the input events into the appropriate ANSI string.
177
+func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
178
+	var buffer bytes.Buffer
179
+	for _, event := range events {
180
+		if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
181
+			buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
182
+		}
183
+	}
184
+
185
+	return buffer.Bytes()
186
+}
187
+
188
+// keyToString maps the given input event record to the corresponding string.
189
+func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
190
+	if keyEvent.UnicodeChar == 0 {
191
+		return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
192
+	}
193
+
194
+	_, alt, control := getControlKeys(keyEvent.ControlKeyState)
195
+	if control {
196
+		// TODO(azlinux): Implement following control sequences
197
+		// <Ctrl>-D  Signals the end of input from the keyboard; also exits current shell.
198
+		// <Ctrl>-H  Deletes the first character to the left of the cursor. Also called the ERASE key.
199
+		// <Ctrl>-Q  Restarts printing after it has been stopped with <Ctrl>-s.
200
+		// <Ctrl>-S  Suspends printing on the screen (does not stop the program).
201
+		// <Ctrl>-U  Deletes all characters on the current line. Also called the KILL key.
202
+		// <Ctrl>-E  Quits current command and creates a core
203
+
204
+	}
205
+
206
+	// <Alt>+Key generates ESC N Key
207
+	if !control && alt {
208
+		return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
209
+	}
210
+
211
+	return string(keyEvent.UnicodeChar)
212
+}
213
+
214
+// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
215
+func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
216
+	shift, alt, control := getControlKeys(controlState)
217
+	modifier := getControlKeysModifier(shift, alt, control)
218
+
219
+	if format, ok := arrowKeyMapPrefix[key]; ok {
220
+		return fmt.Sprintf(format, escapeSequence, modifier)
221
+	}
222
+
223
+	if format, ok := keyMapPrefix[key]; ok {
224
+		return fmt.Sprintf(format, modifier)
225
+	}
226
+
227
+	return ""
228
+}
229
+
230
+// getControlKeys extracts the shift, alt, and ctrl key states.
231
+func getControlKeys(controlState uint32) (shift, alt, control bool) {
232
+	shift = 0 != (controlState & winterm.SHIFT_PRESSED)
233
+	alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
234
+	control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
235
+	return shift, alt, control
236
+}
237
+
238
+// getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
239
+func getControlKeysModifier(shift, alt, control bool) string {
240
+	if shift && alt && control {
241
+		return ansiterm.KEY_CONTROL_PARAM_8
242
+	}
243
+	if alt && control {
244
+		return ansiterm.KEY_CONTROL_PARAM_7
245
+	}
246
+	if shift && control {
247
+		return ansiterm.KEY_CONTROL_PARAM_6
248
+	}
249
+	if control {
250
+		return ansiterm.KEY_CONTROL_PARAM_5
251
+	}
252
+	if shift && alt {
253
+		return ansiterm.KEY_CONTROL_PARAM_4
254
+	}
255
+	if alt {
256
+		return ansiterm.KEY_CONTROL_PARAM_3
257
+	}
258
+	if shift {
259
+		return ansiterm.KEY_CONTROL_PARAM_2
260
+	}
261
+	return ""
262
+}
0 263
new file mode 100644
... ...
@@ -0,0 +1,64 @@
0
+// +build windows
1
+
2
+package windowsconsole // import "github.com/moby/term/windows"
3
+
4
+import (
5
+	"io"
6
+	"os"
7
+
8
+	ansiterm "github.com/Azure/go-ansiterm"
9
+	"github.com/Azure/go-ansiterm/winterm"
10
+)
11
+
12
+// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation.
13
+type ansiWriter struct {
14
+	file           *os.File
15
+	fd             uintptr
16
+	infoReset      *winterm.CONSOLE_SCREEN_BUFFER_INFO
17
+	command        []byte
18
+	escapeSequence []byte
19
+	inAnsiSequence bool
20
+	parser         *ansiterm.AnsiParser
21
+}
22
+
23
+// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
24
+// Windows console output handle.
25
+func NewAnsiWriter(nFile int) io.Writer {
26
+	initLogger()
27
+	file, fd := winterm.GetStdFile(nFile)
28
+	info, err := winterm.GetConsoleScreenBufferInfo(fd)
29
+	if err != nil {
30
+		return nil
31
+	}
32
+
33
+	parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file))
34
+	logger.Infof("newAnsiWriter: parser %p", parser)
35
+
36
+	aw := &ansiWriter{
37
+		file:           file,
38
+		fd:             fd,
39
+		infoReset:      info,
40
+		command:        make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
41
+		escapeSequence: []byte(ansiterm.KEY_ESC_CSI),
42
+		parser:         parser,
43
+	}
44
+
45
+	logger.Infof("newAnsiWriter: aw.parser %p", aw.parser)
46
+	logger.Infof("newAnsiWriter: %v", aw)
47
+	return aw
48
+}
49
+
50
+func (aw *ansiWriter) Fd() uintptr {
51
+	return aw.fd
52
+}
53
+
54
+// Write writes len(p) bytes from p to the underlying data stream.
55
+func (aw *ansiWriter) Write(p []byte) (total int, err error) {
56
+	if len(p) == 0 {
57
+		return 0, nil
58
+	}
59
+
60
+	logger.Infof("Write: % x", p)
61
+	logger.Infof("Write: %s", string(p))
62
+	return aw.parser.Parse(p)
63
+}
0 64
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+// +build windows
1
+
2
+package windowsconsole // import "github.com/moby/term/windows"
3
+
4
+import (
5
+	"os"
6
+
7
+	"github.com/Azure/go-ansiterm/winterm"
8
+)
9
+
10
+// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
11
+func GetHandleInfo(in interface{}) (uintptr, bool) {
12
+	switch t := in.(type) {
13
+	case *ansiReader:
14
+		return t.Fd(), true
15
+	case *ansiWriter:
16
+		return t.Fd(), true
17
+	}
18
+
19
+	var inFd uintptr
20
+	var isTerminal bool
21
+
22
+	if file, ok := in.(*os.File); ok {
23
+		inFd = file.Fd()
24
+		isTerminal = IsConsole(inFd)
25
+	}
26
+	return inFd, isTerminal
27
+}
28
+
29
+// IsConsole returns true if the given file descriptor is a Windows Console.
30
+// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
31
+func IsConsole(fd uintptr) bool {
32
+	_, e := winterm.GetConsoleMode(fd)
33
+	return e == nil
34
+}
0 35
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+// +build windows
1
+// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
2
+// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
3
+// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.
4
+
5
+package windowsconsole // import "github.com/moby/term/windows"
6
+
7
+import (
8
+	"io/ioutil"
9
+	"os"
10
+	"sync"
11
+
12
+	ansiterm "github.com/Azure/go-ansiterm"
13
+	"github.com/sirupsen/logrus"
14
+)
15
+
16
+var logger *logrus.Logger
17
+var initOnce sync.Once
18
+
19
+func initLogger() {
20
+	initOnce.Do(func() {
21
+		logFile := ioutil.Discard
22
+
23
+		if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
24
+			logFile, _ = os.Create("ansiReaderWriter.log")
25
+		}
26
+
27
+		logger = &logrus.Logger{
28
+			Out:       logFile,
29
+			Formatter: new(logrus.TextFormatter),
30
+			Level:     logrus.DebugLevel,
31
+		}
32
+	})
33
+}
0 34
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// +build !windows
1
+
2
+package term
3
+
4
+import (
5
+	"golang.org/x/sys/unix"
6
+)
7
+
8
+// GetWinsize returns the window size based on the specified file descriptor.
9
+func GetWinsize(fd uintptr) (*Winsize, error) {
10
+	uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
11
+	ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
12
+	return ws, err
13
+}
14
+
15
+// SetWinsize tries to set the specified window size for the specified file descriptor.
16
+func SetWinsize(fd uintptr, ws *Winsize) error {
17
+	uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y}
18
+	return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws)
19
+}