mirror of
https://github.com/golang/go
synced 2024-11-24 23:07:56 -07:00
exp/terminal: split terminal handling from exp/ssh
This change splits terminal handling from exp/ssh, as suggested several times in the ssh code review. shell.go and shell_test.go are copies from exp/ssh with minimal changes, so don't need another full review. A future CL will remove that code from exp/ssh. R=bradfitz, r, dave, rsc CC=golang-dev https://golang.org/cl/5278049
This commit is contained in:
parent
ec158f77bd
commit
7bc4f8de0f
15
src/pkg/exp/terminal/Makefile
Normal file
15
src/pkg/exp/terminal/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2011 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include ../../../Make.inc
|
||||
|
||||
TARG=exp/terminal
|
||||
GOFILES=\
|
||||
shell.go\
|
||||
|
||||
ifneq ($(GOOS),windows)
|
||||
GOFILES+=terminal.go
|
||||
endif
|
||||
|
||||
include ../../../Make.pkg
|
359
src/pkg/exp/terminal/shell.go
Normal file
359
src/pkg/exp/terminal/shell.go
Normal file
@ -0,0 +1,359 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Shell contains the state for running a VT100 terminal that is capable of
|
||||
// reading lines of input.
|
||||
type Shell struct {
|
||||
c io.ReadWriter
|
||||
prompt string
|
||||
|
||||
// line is the current line being entered.
|
||||
line []byte
|
||||
// pos is the logical position of the cursor in line
|
||||
pos int
|
||||
|
||||
// cursorX contains the current X value of the cursor where the left
|
||||
// edge is 0. cursorY contains the row number where the first row of
|
||||
// the current line is 0.
|
||||
cursorX, cursorY int
|
||||
// maxLine is the greatest value of cursorY so far.
|
||||
maxLine int
|
||||
|
||||
termWidth, termHeight int
|
||||
|
||||
// outBuf contains the terminal data to be sent.
|
||||
outBuf []byte
|
||||
// remainder contains the remainder of any partial key sequences after
|
||||
// a read. It aliases into inBuf.
|
||||
remainder []byte
|
||||
inBuf [256]byte
|
||||
}
|
||||
|
||||
// NewShell runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
|
||||
// a local terminal, that terminal must first have been put into raw mode.
|
||||
// prompt is a string that is written at the start of each input line (i.e.
|
||||
// "> ").
|
||||
func NewShell(c io.ReadWriter, prompt string) *Shell {
|
||||
return &Shell{
|
||||
c: c,
|
||||
prompt: prompt,
|
||||
termWidth: 80,
|
||||
termHeight: 24,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
keyCtrlD = 4
|
||||
keyEnter = '\r'
|
||||
keyEscape = 27
|
||||
keyBackspace = 127
|
||||
keyUnknown = 256 + iota
|
||||
keyUp
|
||||
keyDown
|
||||
keyLeft
|
||||
keyRight
|
||||
keyAltLeft
|
||||
keyAltRight
|
||||
)
|
||||
|
||||
// bytesToKey tries to parse a key sequence from b. If successful, it returns
|
||||
// the key and the remainder of the input. Otherwise it returns -1.
|
||||
func bytesToKey(b []byte) (int, []byte) {
|
||||
if len(b) == 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
if b[0] != keyEscape {
|
||||
return int(b[0]), b[1:]
|
||||
}
|
||||
|
||||
if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
|
||||
switch b[2] {
|
||||
case 'A':
|
||||
return keyUp, b[3:]
|
||||
case 'B':
|
||||
return keyDown, b[3:]
|
||||
case 'C':
|
||||
return keyRight, b[3:]
|
||||
case 'D':
|
||||
return keyLeft, b[3:]
|
||||
}
|
||||
}
|
||||
|
||||
if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
|
||||
switch b[5] {
|
||||
case 'C':
|
||||
return keyAltRight, b[6:]
|
||||
case 'D':
|
||||
return keyAltLeft, b[6:]
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here then we have a key that we don't recognise, or a
|
||||
// partial sequence. It's not clear how one should find the end of a
|
||||
// sequence without knowing them all, but it seems that [a-zA-Z] only
|
||||
// appears at the end of a sequence.
|
||||
for i, c := range b[0:] {
|
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
|
||||
return keyUnknown, b[i+1:]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, b
|
||||
}
|
||||
|
||||
// queue appends data to the end of ss.outBuf
|
||||
func (ss *Shell) queue(data []byte) {
|
||||
if len(ss.outBuf)+len(data) > cap(ss.outBuf) {
|
||||
newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data)))
|
||||
copy(newOutBuf, ss.outBuf)
|
||||
ss.outBuf = newOutBuf
|
||||
}
|
||||
|
||||
oldLen := len(ss.outBuf)
|
||||
ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)]
|
||||
copy(ss.outBuf[oldLen:], data)
|
||||
}
|
||||
|
||||
var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'}
|
||||
|
||||
func isPrintable(key int) bool {
|
||||
return key >= 32 && key < 127
|
||||
}
|
||||
|
||||
// moveCursorToPos appends data to ss.outBuf which will move the cursor to the
|
||||
// given, logical position in the text.
|
||||
func (ss *Shell) moveCursorToPos(pos int) {
|
||||
x := len(ss.prompt) + pos
|
||||
y := x / ss.termWidth
|
||||
x = x % ss.termWidth
|
||||
|
||||
up := 0
|
||||
if y < ss.cursorY {
|
||||
up = ss.cursorY - y
|
||||
}
|
||||
|
||||
down := 0
|
||||
if y > ss.cursorY {
|
||||
down = y - ss.cursorY
|
||||
}
|
||||
|
||||
left := 0
|
||||
if x < ss.cursorX {
|
||||
left = ss.cursorX - x
|
||||
}
|
||||
|
||||
right := 0
|
||||
if x > ss.cursorX {
|
||||
right = x - ss.cursorX
|
||||
}
|
||||
|
||||
movement := make([]byte, 3*(up+down+left+right))
|
||||
m := movement
|
||||
for i := 0; i < up; i++ {
|
||||
m[0] = keyEscape
|
||||
m[1] = '['
|
||||
m[2] = 'A'
|
||||
m = m[3:]
|
||||
}
|
||||
for i := 0; i < down; i++ {
|
||||
m[0] = keyEscape
|
||||
m[1] = '['
|
||||
m[2] = 'B'
|
||||
m = m[3:]
|
||||
}
|
||||
for i := 0; i < left; i++ {
|
||||
m[0] = keyEscape
|
||||
m[1] = '['
|
||||
m[2] = 'D'
|
||||
m = m[3:]
|
||||
}
|
||||
for i := 0; i < right; i++ {
|
||||
m[0] = keyEscape
|
||||
m[1] = '['
|
||||
m[2] = 'C'
|
||||
m = m[3:]
|
||||
}
|
||||
|
||||
ss.cursorX = x
|
||||
ss.cursorY = y
|
||||
ss.queue(movement)
|
||||
}
|
||||
|
||||
const maxLineLength = 4096
|
||||
|
||||
// handleKey processes the given key and, optionally, returns a line of text
|
||||
// that the user has entered.
|
||||
func (ss *Shell) handleKey(key int) (line string, ok bool) {
|
||||
switch key {
|
||||
case keyBackspace:
|
||||
if ss.pos == 0 {
|
||||
return
|
||||
}
|
||||
ss.pos--
|
||||
|
||||
copy(ss.line[ss.pos:], ss.line[1+ss.pos:])
|
||||
ss.line = ss.line[:len(ss.line)-1]
|
||||
ss.writeLine(ss.line[ss.pos:])
|
||||
ss.moveCursorToPos(ss.pos)
|
||||
ss.queue(eraseUnderCursor)
|
||||
case keyAltLeft:
|
||||
// move left by a word.
|
||||
if ss.pos == 0 {
|
||||
return
|
||||
}
|
||||
ss.pos--
|
||||
for ss.pos > 0 {
|
||||
if ss.line[ss.pos] != ' ' {
|
||||
break
|
||||
}
|
||||
ss.pos--
|
||||
}
|
||||
for ss.pos > 0 {
|
||||
if ss.line[ss.pos] == ' ' {
|
||||
ss.pos++
|
||||
break
|
||||
}
|
||||
ss.pos--
|
||||
}
|
||||
ss.moveCursorToPos(ss.pos)
|
||||
case keyAltRight:
|
||||
// move right by a word.
|
||||
for ss.pos < len(ss.line) {
|
||||
if ss.line[ss.pos] == ' ' {
|
||||
break
|
||||
}
|
||||
ss.pos++
|
||||
}
|
||||
for ss.pos < len(ss.line) {
|
||||
if ss.line[ss.pos] != ' ' {
|
||||
break
|
||||
}
|
||||
ss.pos++
|
||||
}
|
||||
ss.moveCursorToPos(ss.pos)
|
||||
case keyLeft:
|
||||
if ss.pos == 0 {
|
||||
return
|
||||
}
|
||||
ss.pos--
|
||||
ss.moveCursorToPos(ss.pos)
|
||||
case keyRight:
|
||||
if ss.pos == len(ss.line) {
|
||||
return
|
||||
}
|
||||
ss.pos++
|
||||
ss.moveCursorToPos(ss.pos)
|
||||
case keyEnter:
|
||||
ss.moveCursorToPos(len(ss.line))
|
||||
ss.queue([]byte("\r\n"))
|
||||
line = string(ss.line)
|
||||
ok = true
|
||||
ss.line = ss.line[:0]
|
||||
ss.pos = 0
|
||||
ss.cursorX = 0
|
||||
ss.cursorY = 0
|
||||
ss.maxLine = 0
|
||||
default:
|
||||
if !isPrintable(key) {
|
||||
return
|
||||
}
|
||||
if len(ss.line) == maxLineLength {
|
||||
return
|
||||
}
|
||||
if len(ss.line) == cap(ss.line) {
|
||||
newLine := make([]byte, len(ss.line), 2*(1+len(ss.line)))
|
||||
copy(newLine, ss.line)
|
||||
ss.line = newLine
|
||||
}
|
||||
ss.line = ss.line[:len(ss.line)+1]
|
||||
copy(ss.line[ss.pos+1:], ss.line[ss.pos:])
|
||||
ss.line[ss.pos] = byte(key)
|
||||
ss.writeLine(ss.line[ss.pos:])
|
||||
ss.pos++
|
||||
ss.moveCursorToPos(ss.pos)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ss *Shell) writeLine(line []byte) {
|
||||
for len(line) != 0 {
|
||||
if ss.cursorX == ss.termWidth {
|
||||
ss.queue([]byte("\r\n"))
|
||||
ss.cursorX = 0
|
||||
ss.cursorY++
|
||||
if ss.cursorY > ss.maxLine {
|
||||
ss.maxLine = ss.cursorY
|
||||
}
|
||||
}
|
||||
|
||||
remainingOnLine := ss.termWidth - ss.cursorX
|
||||
todo := len(line)
|
||||
if todo > remainingOnLine {
|
||||
todo = remainingOnLine
|
||||
}
|
||||
ss.queue(line[:todo])
|
||||
ss.cursorX += todo
|
||||
line = line[todo:]
|
||||
}
|
||||
}
|
||||
|
||||
func (ss *Shell) Write(buf []byte) (n int, err os.Error) {
|
||||
return ss.c.Write(buf)
|
||||
}
|
||||
|
||||
// ReadLine returns a line of input from the terminal.
|
||||
func (ss *Shell) ReadLine() (line string, err os.Error) {
|
||||
ss.writeLine([]byte(ss.prompt))
|
||||
ss.c.Write(ss.outBuf)
|
||||
ss.outBuf = ss.outBuf[:0]
|
||||
|
||||
for {
|
||||
// ss.remainder is a slice at the beginning of ss.inBuf
|
||||
// containing a partial key sequence
|
||||
readBuf := ss.inBuf[len(ss.remainder):]
|
||||
var n int
|
||||
n, err = ss.c.Read(readBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
ss.remainder = ss.inBuf[:n+len(ss.remainder)]
|
||||
rest := ss.remainder
|
||||
lineOk := false
|
||||
for !lineOk {
|
||||
var key int
|
||||
key, rest = bytesToKey(rest)
|
||||
if key < 0 {
|
||||
break
|
||||
}
|
||||
if key == keyCtrlD {
|
||||
return "", os.EOF
|
||||
}
|
||||
line, lineOk = ss.handleKey(key)
|
||||
}
|
||||
if len(rest) > 0 {
|
||||
n := copy(ss.inBuf[:], rest)
|
||||
ss.remainder = ss.inBuf[:n]
|
||||
} else {
|
||||
ss.remainder = nil
|
||||
}
|
||||
ss.c.Write(ss.outBuf)
|
||||
ss.outBuf = ss.outBuf[:0]
|
||||
if lineOk {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
110
src/pkg/exp/terminal/shell_test.go
Normal file
110
src/pkg/exp/terminal/shell_test.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"os"
|
||||
)
|
||||
|
||||
type MockTerminal struct {
|
||||
toSend []byte
|
||||
bytesPerRead int
|
||||
received []byte
|
||||
}
|
||||
|
||||
func (c *MockTerminal) Read(data []byte) (n int, err os.Error) {
|
||||
n = len(data)
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
if n > len(c.toSend) {
|
||||
n = len(c.toSend)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, os.EOF
|
||||
}
|
||||
if c.bytesPerRead > 0 && n > c.bytesPerRead {
|
||||
n = c.bytesPerRead
|
||||
}
|
||||
copy(data, c.toSend[:n])
|
||||
c.toSend = c.toSend[n:]
|
||||
return
|
||||
}
|
||||
|
||||
func (c *MockTerminal) Write(data []byte) (n int, err os.Error) {
|
||||
c.received = append(c.received, data...)
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func TestClose(t *testing.T) {
|
||||
c := &MockTerminal{}
|
||||
ss := NewShell(c, "> ")
|
||||
line, err := ss.ReadLine()
|
||||
if line != "" {
|
||||
t.Errorf("Expected empty line but got: %s", line)
|
||||
}
|
||||
if err != os.EOF {
|
||||
t.Errorf("Error should have been EOF but got: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
var keyPressTests = []struct {
|
||||
in string
|
||||
line string
|
||||
err os.Error
|
||||
}{
|
||||
{
|
||||
"",
|
||||
"",
|
||||
os.EOF,
|
||||
},
|
||||
{
|
||||
"\r",
|
||||
"",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"foo\r",
|
||||
"foo",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"a\x1b[Cb\r", // right
|
||||
"ab",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"a\x1b[Db\r", // left
|
||||
"ba",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"a\177b\r", // backspace
|
||||
"b",
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
func TestKeyPresses(t *testing.T) {
|
||||
for i, test := range keyPressTests {
|
||||
for j := 0; j < len(test.in); j++ {
|
||||
c := &MockTerminal{
|
||||
toSend: []byte(test.in),
|
||||
bytesPerRead: j,
|
||||
}
|
||||
ss := NewShell(c, "> ")
|
||||
line, err := ss.ReadLine()
|
||||
if line != test.line {
|
||||
t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
|
||||
break
|
||||
}
|
||||
if err != test.err {
|
||||
t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
103
src/pkg/exp/terminal/terminal.go
Normal file
103
src/pkg/exp/terminal/terminal.go
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package terminal provides support functions for dealing with terminals, as
|
||||
// commonly found on UNIX systems.
|
||||
//
|
||||
// Putting a terminal into raw mode is the most common requirement:
|
||||
//
|
||||
// oldState, err := terminal.MakeRaw(0)
|
||||
// if err != nil {
|
||||
// panic(err.String())
|
||||
// }
|
||||
// defer terminal.Restore(0, oldState)
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
termios syscall.Termios
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
var termios syscall.Termios
|
||||
_, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return e == 0
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, os.Error) {
|
||||
var oldState State
|
||||
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); e != 0 {
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
|
||||
newState := oldState.termios
|
||||
newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
|
||||
newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
|
||||
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 {
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) os.Error {
|
||||
_, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
|
||||
return os.Errno(e)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, os.Error) {
|
||||
var oldState syscall.Termios
|
||||
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 {
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
|
||||
newState := oldState
|
||||
newState.Lflag &^= syscall.ECHO
|
||||
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 {
|
||||
return nil, os.Errno(e)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&oldState)), 0, 0, 0)
|
||||
}()
|
||||
|
||||
var buf [16]byte
|
||||
var ret []byte
|
||||
for {
|
||||
n, errno := syscall.Read(fd, buf[:])
|
||||
if errno != 0 {
|
||||
return nil, os.Errno(errno)
|
||||
}
|
||||
if n == 0 {
|
||||
if len(ret) == 0 {
|
||||
return nil, os.EOF
|
||||
}
|
||||
break
|
||||
}
|
||||
if buf[n-1] == '\n' {
|
||||
n--
|
||||
}
|
||||
ret = append(ret, buf[:n]...)
|
||||
if n < len(buf) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
@ -41,6 +41,7 @@ Input to godefs. See also mkerrors.sh and mkall.sh
|
||||
#include <linux/filter.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <ustat.h>
|
||||
@ -288,3 +289,109 @@ struct my_epoll_event {
|
||||
};
|
||||
|
||||
typedef struct my_epoll_event $EpollEvent;
|
||||
|
||||
// Terminal handling
|
||||
|
||||
typedef struct termios $Termios;
|
||||
|
||||
enum {
|
||||
$VINTR = VINTR,
|
||||
$VQUIT = VQUIT,
|
||||
$VERASE = VERASE,
|
||||
$VKILL = VKILL,
|
||||
$VEOF = VEOF,
|
||||
$VTIME = VTIME,
|
||||
$VMIN = VMIN,
|
||||
$VSWTC = VSWTC,
|
||||
$VSTART = VSTART,
|
||||
$VSTOP = VSTOP,
|
||||
$VSUSP = VSUSP,
|
||||
$VEOL = VEOL,
|
||||
$VREPRINT = VREPRINT,
|
||||
$VDISCARD = VDISCARD,
|
||||
$VWERASE = VWERASE,
|
||||
$VLNEXT = VLNEXT,
|
||||
$VEOL2 = VEOL2,
|
||||
$IGNBRK = IGNBRK,
|
||||
$BRKINT = BRKINT,
|
||||
$IGNPAR = IGNPAR,
|
||||
$PARMRK = PARMRK,
|
||||
$INPCK = INPCK,
|
||||
$ISTRIP = ISTRIP,
|
||||
$INLCR = INLCR,
|
||||
$IGNCR = IGNCR,
|
||||
$ICRNL = ICRNL,
|
||||
$IUCLC = IUCLC,
|
||||
$IXON = IXON,
|
||||
$IXANY = IXANY,
|
||||
$IXOFF = IXOFF,
|
||||
$IMAXBEL = IMAXBEL,
|
||||
$IUTF8 = IUTF8,
|
||||
$OPOST = OPOST,
|
||||
$OLCUC = OLCUC,
|
||||
$ONLCR = ONLCR,
|
||||
$OCRNL = OCRNL,
|
||||
$ONOCR = ONOCR,
|
||||
$ONLRET = ONLRET,
|
||||
$OFILL = OFILL,
|
||||
$OFDEL = OFDEL,
|
||||
$B0 = B0,
|
||||
$B50 = B50,
|
||||
$B75 = B75,
|
||||
$B110 = B110,
|
||||
$B134 = B134,
|
||||
$B150 = B150,
|
||||
$B200 = B200,
|
||||
$B300 = B300,
|
||||
$B600 = B600,
|
||||
$B1200 = B1200,
|
||||
$B1800 = B1800,
|
||||
$B2400 = B2400,
|
||||
$B4800 = B4800,
|
||||
$B9600 = B9600,
|
||||
$B19200 = B19200,
|
||||
$B38400 = B38400,
|
||||
$CSIZE = CSIZE,
|
||||
$CS5 = CS5,
|
||||
$CS6 = CS6,
|
||||
$CS7 = CS7,
|
||||
$CS8 = CS8,
|
||||
$CSTOPB = CSTOPB,
|
||||
$CREAD = CREAD,
|
||||
$PARENB = PARENB,
|
||||
$PARODD = PARODD,
|
||||
$HUPCL = HUPCL,
|
||||
$CLOCAL = CLOCAL,
|
||||
$B57600 = B57600,
|
||||
$B115200 = B115200,
|
||||
$B230400 = B230400,
|
||||
$B460800 = B460800,
|
||||
$B500000 = B500000,
|
||||
$B576000 = B576000,
|
||||
$B921600 = B921600,
|
||||
$B1000000 = B1000000,
|
||||
$B1152000 = B1152000,
|
||||
$B1500000 = B1500000,
|
||||
$B2000000 = B2000000,
|
||||
$B2500000 = B2500000,
|
||||
$B3000000 = B3000000,
|
||||
$B3500000 = B3500000,
|
||||
$B4000000 = B4000000,
|
||||
$ISIG = ISIG,
|
||||
$ICANON = ICANON,
|
||||
$XCASE = XCASE,
|
||||
$ECHO = ECHO,
|
||||
$ECHOE = ECHOE,
|
||||
$ECHOK = ECHOK,
|
||||
$ECHONL = ECHONL,
|
||||
$NOFLSH = NOFLSH,
|
||||
$TOSTOP = TOSTOP,
|
||||
$ECHOCTL = ECHOCTL,
|
||||
$ECHOPRT = ECHOPRT,
|
||||
$ECHOKE = ECHOKE,
|
||||
$FLUSHO = FLUSHO,
|
||||
$PENDIN = PENDIN,
|
||||
$IEXTEN = IEXTEN,
|
||||
$TCGETS = TCGETS,
|
||||
$TCSETS = TCSETS,
|
||||
};
|
||||
|
@ -105,6 +105,105 @@ const (
|
||||
SizeofSockFilter = 0x8
|
||||
SizeofSockFprog = 0x10
|
||||
SizeofInotifyEvent = 0x10
|
||||
VINTR = 0
|
||||
VQUIT = 0x1
|
||||
VERASE = 0x2
|
||||
VKILL = 0x3
|
||||
VEOF = 0x4
|
||||
VTIME = 0x5
|
||||
VMIN = 0x6
|
||||
VSWTC = 0x7
|
||||
VSTART = 0x8
|
||||
VSTOP = 0x9
|
||||
VSUSP = 0xa
|
||||
VEOL = 0xb
|
||||
VREPRINT = 0xc
|
||||
VDISCARD = 0xd
|
||||
VWERASE = 0xe
|
||||
VLNEXT = 0xf
|
||||
VEOL2 = 0x10
|
||||
IGNBRK = 0x1
|
||||
BRKINT = 0x2
|
||||
IGNPAR = 0x4
|
||||
PARMRK = 0x8
|
||||
INPCK = 0x10
|
||||
ISTRIP = 0x20
|
||||
INLCR = 0x40
|
||||
IGNCR = 0x80
|
||||
ICRNL = 0x100
|
||||
IUCLC = 0x200
|
||||
IXON = 0x400
|
||||
IXANY = 0x800
|
||||
IXOFF = 0x1000
|
||||
IMAXBEL = 0x2000
|
||||
IUTF8 = 0x4000
|
||||
OPOST = 0x1
|
||||
OLCUC = 0x2
|
||||
ONLCR = 0x4
|
||||
OCRNL = 0x8
|
||||
ONOCR = 0x10
|
||||
ONLRET = 0x20
|
||||
OFILL = 0x40
|
||||
OFDEL = 0x80
|
||||
B0 = 0
|
||||
B50 = 0x1
|
||||
B75 = 0x2
|
||||
B110 = 0x3
|
||||
B134 = 0x4
|
||||
B150 = 0x5
|
||||
B200 = 0x6
|
||||
B300 = 0x7
|
||||
B600 = 0x8
|
||||
B1200 = 0x9
|
||||
B1800 = 0xa
|
||||
B2400 = 0xb
|
||||
B4800 = 0xc
|
||||
B9600 = 0xd
|
||||
B19200 = 0xe
|
||||
B38400 = 0xf
|
||||
CSIZE = 0x30
|
||||
CS5 = 0
|
||||
CS6 = 0x10
|
||||
CS7 = 0x20
|
||||
CS8 = 0x30
|
||||
CSTOPB = 0x40
|
||||
CREAD = 0x80
|
||||
PARENB = 0x100
|
||||
PARODD = 0x200
|
||||
HUPCL = 0x400
|
||||
CLOCAL = 0x800
|
||||
B57600 = 0x1001
|
||||
B115200 = 0x1002
|
||||
B230400 = 0x1003
|
||||
B460800 = 0x1004
|
||||
B500000 = 0x1005
|
||||
B576000 = 0x1006
|
||||
B921600 = 0x1007
|
||||
B1000000 = 0x1008
|
||||
B1152000 = 0x1009
|
||||
B1500000 = 0x100a
|
||||
B2000000 = 0x100b
|
||||
B2500000 = 0x100c
|
||||
B3000000 = 0x100d
|
||||
B3500000 = 0x100e
|
||||
B4000000 = 0x100f
|
||||
ISIG = 0x1
|
||||
ICANON = 0x2
|
||||
XCASE = 0x4
|
||||
ECHO = 0x8
|
||||
ECHOE = 0x10
|
||||
ECHOK = 0x20
|
||||
ECHONL = 0x40
|
||||
NOFLSH = 0x80
|
||||
TOSTOP = 0x100
|
||||
ECHOCTL = 0x200
|
||||
ECHOPRT = 0x400
|
||||
ECHOKE = 0x800
|
||||
FLUSHO = 0x1000
|
||||
PENDIN = 0x4000
|
||||
IEXTEN = 0x8000
|
||||
TCGETS = 0x5401
|
||||
TCSETS = 0x5402
|
||||
)
|
||||
|
||||
// Types
|
||||
@ -514,3 +613,15 @@ type EpollEvent struct {
|
||||
Fd int32
|
||||
Pad int32
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Line uint8
|
||||
Cc [32]uint8
|
||||
Pad_godefs_0 [3]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user