├── .github └── workflows │ └── push.yml ├── .travis.yml ├── LICENSE ├── README.md ├── example_test.go ├── go.mod ├── go.sum ├── term.go ├── term_bsdi.go ├── term_bsdu.go ├── term_darwin.go ├── term_linux.go ├── term_open_posix.go ├── term_posix.go ├── term_solaris.go ├── term_test.go ├── term_windows.go └── termios ├── doc.go ├── ioctl.go ├── pty.go ├── pty_cgo.go ├── pty_darwin.go ├── pty_freebsd.go ├── pty_linux.go ├── pty_netbsd.go ├── pty_test.go ├── termios.go ├── termios_bsd.go ├── termios_const.go ├── termios_const_solaris.go ├── termios_linux.go ├── termios_linux_test.go ├── termios_solaris.go └── termios_test.go /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | name: Push 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | goos: [darwin, linux, freebsd, netbsd, openbsd, solaris] 15 | steps: 16 | - name: Install Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.15.x 20 | - name: Checkout code 21 | uses: actions/checkout@v2 22 | - name: Build 23 | env: 24 | GOOS: ${{ matrix.goos }} 25 | CGO_ENABLED: 1 26 | run: go build ./... 27 | test: 28 | strategy: 29 | matrix: 30 | go-version: [1.14.x, 1.15.x] 31 | platform: [ubuntu-latest, macos-latest] 32 | runs-on: ${{ matrix.platform }} 33 | steps: 34 | - name: Install Go 35 | uses: actions/setup-go@v2 36 | with: 37 | go-version: ${{ matrix.go-version }} 38 | - name: Checkout code 39 | uses: actions/checkout@v2 40 | - name: Test 41 | run: go test ./... 42 | lint: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - name: Install Go 46 | uses: actions/setup-go@v2 47 | with: 48 | go-version: 1.15.x 49 | - name: Checkout code 50 | uses: actions/checkout@v2 51 | - name: Vet 52 | run: go vet ./... 53 | - name: Staticcheck 54 | run: | 55 | go get honnef.co/go/tools/cmd/staticcheck 56 | staticcheck ./... 57 | 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/pkg/term 3 | go: 4 | - 1.13.x 5 | - 1.14.x 6 | - tip 7 | 8 | script: 9 | - go test -v ./... 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, David Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # term 3 | import "github.com/pkg/term" 4 | 5 | Package term manages POSIX terminals. As POSIX terminals are connected to, 6 | or emulate, a UART, this package also provides control over the various 7 | UART and serial line parameters. 8 | 9 | 10 | 11 | 12 | 13 | 14 | ## func CBreakMode 15 | ``` go 16 | func CBreakMode(t *Term) error 17 | ``` 18 | CBreakMode places the terminal into cbreak mode. 19 | 20 | 21 | ## func RawMode 22 | ``` go 23 | func RawMode(t *Term) error 24 | ``` 25 | RawMode places the terminal into raw mode. 26 | 27 | 28 | ## func Speed 29 | ``` go 30 | func Speed(baud int) func(*Term) error 31 | ``` 32 | Speed sets the baud rate option for the terminal. 33 | 34 | 35 | 36 | ## type Term 37 | ``` go 38 | type Term struct { 39 | // contains filtered or unexported fields 40 | } 41 | ``` 42 | Term represents an asynchronous communications port. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ### func Open 53 | ``` go 54 | func Open(name string, options ...func(*Term) error) (*Term, error) 55 | ``` 56 | Open opens an asynchronous communications port. 57 | 58 | 59 | 60 | 61 | ### func (\*Term) Available 62 | ``` go 63 | func (t *Term) Available() (int, error) 64 | ``` 65 | Available returns how many bytes are unused in the buffer. 66 | 67 | 68 | 69 | ### func (\*Term) Buffered 70 | ``` go 71 | func (t *Term) Buffered() (int, error) 72 | ``` 73 | Buffered returns the number of bytes that have been written into the current buffer. 74 | 75 | 76 | 77 | ### func (\*Term) Close 78 | ``` go 79 | func (t *Term) Close() error 80 | ``` 81 | Close closes the device and releases any associated resources. 82 | 83 | 84 | 85 | ### func (\*Term) DTR 86 | ``` go 87 | func (t *Term) DTR() (bool, error) 88 | ``` 89 | DTR returns the state of the DTR (data terminal ready) signal. 90 | 91 | 92 | 93 | ### func (\*Term) Flush 94 | ``` go 95 | func (t *Term) Flush() error 96 | ``` 97 | Flush flushes both data received but not read, and data written but not transmitted. 98 | 99 | 100 | 101 | ### func (\*Term) RTS 102 | ``` go 103 | func (t *Term) RTS() (bool, error) 104 | ``` 105 | RTS returns the state of the RTS (data terminal ready) signal. 106 | 107 | 108 | 109 | ### func (\*Term) Read 110 | ``` go 111 | func (t *Term) Read(b []byte) (int, error) 112 | ``` 113 | Read reads up to len(b) bytes from the terminal. It returns the number of 114 | bytes read and an error, if any. EOF is signaled by a zero count with 115 | err set to io.EOF. 116 | 117 | 118 | 119 | ### func (\*Term) Restore 120 | ``` go 121 | func (t *Term) Restore() error 122 | ``` 123 | Restore restores the state of the terminal captured at the point that 124 | the terminal was originally opened. 125 | 126 | 127 | 128 | ### func (\*Term) SendBreak 129 | ``` go 130 | func (t *Term) SendBreak() error 131 | ``` 132 | SendBreak sends a break signal. 133 | 134 | 135 | 136 | ### func (\*Term) SetCbreak 137 | ``` go 138 | func (t *Term) SetCbreak() error 139 | ``` 140 | SetCbreak sets cbreak mode. 141 | 142 | 143 | 144 | ### func (\*Term) SetDTR 145 | ``` go 146 | func (t *Term) SetDTR(v bool) error 147 | ``` 148 | SetDTR sets the DTR (data terminal ready) signal. 149 | 150 | 151 | 152 | ### func (\*Term) SetOption 153 | ``` go 154 | func (t *Term) SetOption(options ...func(*Term) error) error 155 | ``` 156 | SetOption takes one or more option function and applies them in order to Term. 157 | 158 | 159 | 160 | ### func (\*Term) SetRTS 161 | ``` go 162 | func (t *Term) SetRTS(v bool) error 163 | ``` 164 | SetRTS sets the RTS (data terminal ready) signal. 165 | 166 | 167 | 168 | ### func (\*Term) SetRaw 169 | ``` go 170 | func (t *Term) SetRaw() error 171 | ``` 172 | SetRaw sets raw mode. 173 | 174 | 175 | 176 | ### func (\*Term) SetSpeed 177 | ``` go 178 | func (t *Term) SetSpeed(baud int) error 179 | ``` 180 | SetSpeed sets the receive and transmit baud rates. 181 | 182 | 183 | 184 | ### func (\*Term) Write 185 | ``` go 186 | func (t *Term) Write(b []byte) (int, error) 187 | ``` 188 | Write writes len(b) bytes to the terminal. It returns the number of bytes 189 | written and an error, if any. Write returns a non-nil error when n != 190 | len(b). 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | - - - 201 | Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package term 4 | 5 | import ( 6 | "log" 7 | "time" 8 | ) 9 | 10 | // Open a terminal in raw mode at 19200 baud. 11 | func ExampleOpen() { 12 | Open("/dev/ttyUSB0", Speed(19200), RawMode) 13 | } 14 | 15 | // Reset an Arduino by toggling the DTR signal. 16 | func ExampleTerm_SetDTR() { 17 | t, _ := Open("/dev/USB0") 18 | t.SetDTR(false) // toggle DTR low 19 | time.Sleep(250 * time.Millisecond) 20 | t.SetDTR(true) // raise DTR, resets Ardunio 21 | } 22 | 23 | // Send Break to the remote DTE. 24 | func ExampleTerm_SendBreak() { 25 | t, _ := Open("/dev/ttyUSB0") 26 | for { 27 | time.Sleep(3 * time.Second) 28 | log.Println("Break...") 29 | t.SendBreak() 30 | } 31 | } 32 | 33 | // Restore the terminal state 34 | func ExampleTerm_Restore() { 35 | t, _ := Open("/dev/tty") 36 | // mutate terminal state 37 | t.Restore() 38 | } 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pkg/term 2 | 3 | go 1.14 4 | 5 | require golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= 2 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 3 | -------------------------------------------------------------------------------- /term.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | // Package term manages POSIX terminals. As POSIX terminals are connected to, 4 | // or emulate, a UART, this package also provides control over the various 5 | // UART and serial line parameters. 6 | package term 7 | 8 | import ( 9 | "io" 10 | "os" 11 | 12 | "github.com/pkg/term/termios" 13 | "golang.org/x/sys/unix" 14 | ) 15 | 16 | const ( 17 | NONE = iota // flow control off 18 | XONXOFF // software flow control 19 | HARDWARE // hardware flow control 20 | ) 21 | 22 | // Read reads up to len(b) bytes from the terminal. It returns the number of 23 | // bytes read and an error, if any. EOF is signaled by a zero count with 24 | // err set to io.EOF. 25 | func (t *Term) Read(b []byte) (int, error) { 26 | n, e := unix.Read(t.fd, b) 27 | if n < 0 { 28 | n = 0 29 | } 30 | if n == 0 && len(b) > 0 && e == nil { 31 | return 0, io.EOF 32 | } 33 | if e != nil { 34 | return n, &os.PathError{ 35 | Op: "read", 36 | Path: t.name, 37 | Err: e} 38 | } 39 | return n, nil 40 | } 41 | 42 | // SetOption takes one or more option function and applies them in order to Term. 43 | func (t *Term) SetOption(options ...func(*Term) error) error { 44 | for _, opt := range options { 45 | if err := opt(t); err != nil { 46 | return err 47 | } 48 | } 49 | return nil 50 | } 51 | 52 | // Write writes len(b) bytes to the terminal. It returns the number of bytes 53 | // written and an error, if any. Write returns a non-nil error when n != 54 | // len(b). 55 | func (t *Term) Write(b []byte) (int, error) { 56 | n, e := unix.Write(t.fd, b) 57 | if n < 0 { 58 | n = 0 59 | } 60 | if n != len(b) { 61 | return n, io.ErrShortWrite 62 | } 63 | if e != nil { 64 | return n, &os.PathError{ 65 | Op: "write", 66 | Path: t.name, 67 | Err: e, 68 | } 69 | } 70 | return n, nil 71 | } 72 | 73 | // Available returns how many bytes are unused in the buffer. 74 | func (t *Term) Available() (int, error) { 75 | return termios.Tiocinq(uintptr(t.fd)) 76 | } 77 | 78 | // Buffered returns the number of bytes that have been written into the current buffer. 79 | func (t *Term) Buffered() (int, error) { 80 | return termios.Tiocoutq(uintptr(t.fd)) 81 | } 82 | -------------------------------------------------------------------------------- /term_bsdi.go: -------------------------------------------------------------------------------- 1 | // +build netbsd openbsd 2 | 3 | package term 4 | 5 | import ( 6 | "syscall" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | type attr unix.Termios 12 | 13 | func (a *attr) getSpeed() (int, error) { 14 | // We generally only care about ospeed, since that's what would 15 | // be used for padding characters, for example. 16 | 17 | switch a.Ospeed { 18 | case syscall.B50: 19 | return 50, nil 20 | case syscall.B75: 21 | return 75, nil 22 | case syscall.B110: 23 | return 110, nil 24 | case syscall.B134: 25 | return 134, nil 26 | case syscall.B150: 27 | return 150, nil 28 | case syscall.B200: 29 | return 200, nil 30 | case syscall.B300: 31 | return 300, nil 32 | case syscall.B600: 33 | return 600, nil 34 | case syscall.B1200: 35 | return 1200, nil 36 | case syscall.B1800: 37 | return 1800, nil 38 | case syscall.B2400: 39 | return 2400, nil 40 | case syscall.B4800: 41 | return 4800, nil 42 | case syscall.B9600: 43 | return 9600, nil 44 | case syscall.B19200: 45 | return 19200, nil 46 | case syscall.B38400: 47 | return 38400, nil 48 | case syscall.B57600: 49 | return 57600, nil 50 | case syscall.B115200: 51 | return 115200, nil 52 | case syscall.B230400: 53 | return 230400, nil 54 | default: 55 | return 0, syscall.EINVAL 56 | } 57 | } 58 | 59 | func (a *attr) setSpeed(baud int) error { 60 | var rate int32 61 | switch baud { 62 | case 50: 63 | rate = unix.B50 64 | case 75: 65 | rate = unix.B75 66 | case 110: 67 | rate = unix.B110 68 | case 134: 69 | rate = unix.B134 70 | case 150: 71 | rate = unix.B150 72 | case 200: 73 | rate = unix.B200 74 | case 300: 75 | rate = unix.B300 76 | case 600: 77 | rate = unix.B600 78 | case 1200: 79 | rate = unix.B1200 80 | case 1800: 81 | rate = unix.B1800 82 | case 2400: 83 | rate = unix.B2400 84 | case 4800: 85 | rate = unix.B4800 86 | case 9600: 87 | rate = unix.B9600 88 | case 19200: 89 | rate = unix.B19200 90 | case 38400: 91 | rate = unix.B38400 92 | case 57600: 93 | rate = unix.B57600 94 | case 115200: 95 | rate = unix.B115200 96 | case 230400: 97 | rate = unix.B230400 98 | default: 99 | return unix.EINVAL 100 | } 101 | (*unix.Termios)(a).Cflag = unix.CS8 | unix.CREAD | unix.CLOCAL | uint32(rate) 102 | (*unix.Termios)(a).Ispeed = rate 103 | (*unix.Termios)(a).Ospeed = rate 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /term_bsdu.go: -------------------------------------------------------------------------------- 1 | // +build dragonfly freebsd 2 | 3 | package term 4 | 5 | import ( 6 | "syscall" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | type attr unix.Termios 12 | 13 | func (a *attr) getSpeed() (int, error) { 14 | // We generally only care about ospeed, since that's what would 15 | // be used for padding characters, for example. 16 | 17 | switch a.Ospeed { 18 | case syscall.B50: 19 | return 50, nil 20 | case syscall.B75: 21 | return 75, nil 22 | case syscall.B110: 23 | return 110, nil 24 | case syscall.B134: 25 | return 134, nil 26 | case syscall.B150: 27 | return 150, nil 28 | case syscall.B200: 29 | return 200, nil 30 | case syscall.B300: 31 | return 300, nil 32 | case syscall.B600: 33 | return 600, nil 34 | case syscall.B1200: 35 | return 1200, nil 36 | case syscall.B1800: 37 | return 1800, nil 38 | case syscall.B2400: 39 | return 2400, nil 40 | case syscall.B4800: 41 | return 4800, nil 42 | case syscall.B9600: 43 | return 9600, nil 44 | case syscall.B19200: 45 | return 19200, nil 46 | case syscall.B38400: 47 | return 38400, nil 48 | case syscall.B57600: 49 | return 57600, nil 50 | case syscall.B115200: 51 | return 115200, nil 52 | case syscall.B230400: 53 | return 230400, nil 54 | default: 55 | return 0, syscall.EINVAL 56 | } 57 | } 58 | 59 | func (a *attr) setSpeed(baud int) error { 60 | var rate uint32 61 | switch baud { 62 | case 50: 63 | rate = unix.B50 64 | case 75: 65 | rate = unix.B75 66 | case 110: 67 | rate = unix.B110 68 | case 134: 69 | rate = unix.B134 70 | case 150: 71 | rate = unix.B150 72 | case 200: 73 | rate = unix.B200 74 | case 300: 75 | rate = unix.B300 76 | case 600: 77 | rate = unix.B600 78 | case 1200: 79 | rate = unix.B1200 80 | case 1800: 81 | rate = unix.B1800 82 | case 2400: 83 | rate = unix.B2400 84 | case 4800: 85 | rate = unix.B4800 86 | case 9600: 87 | rate = unix.B9600 88 | case 19200: 89 | rate = unix.B19200 90 | case 38400: 91 | rate = unix.B38400 92 | case 57600: 93 | rate = unix.B57600 94 | case 115200: 95 | rate = unix.B115200 96 | case 230400: 97 | rate = unix.B230400 98 | default: 99 | return unix.EINVAL 100 | } 101 | (*unix.Termios)(a).Cflag = unix.CS8 | unix.CREAD | unix.CLOCAL | rate 102 | (*unix.Termios)(a).Ispeed = rate 103 | (*unix.Termios)(a).Ospeed = rate 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /term_darwin.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | type attr unix.Termios 6 | 7 | func (a *attr) getSpeed() (int, error) { 8 | // We generally only care about ospeed, since that's what would 9 | // be used for padding characters, for example. 10 | 11 | switch a.Ospeed { 12 | case unix.B50: 13 | return 50, nil 14 | case unix.B75: 15 | return 75, nil 16 | case unix.B110: 17 | return 110, nil 18 | case unix.B134: 19 | return 134, nil 20 | case unix.B150: 21 | return 150, nil 22 | case unix.B200: 23 | return 200, nil 24 | case unix.B300: 25 | return 300, nil 26 | case unix.B600: 27 | return 600, nil 28 | case unix.B1200: 29 | return 1200, nil 30 | case unix.B1800: 31 | return 1800, nil 32 | case unix.B2400: 33 | return 2400, nil 34 | case unix.B4800: 35 | return 4800, nil 36 | case unix.B9600: 37 | return 9600, nil 38 | case unix.B19200: 39 | return 19200, nil 40 | case unix.B38400: 41 | return 38400, nil 42 | case unix.B57600: 43 | return 57600, nil 44 | case unix.B115200: 45 | return 115200, nil 46 | case unix.B230400: 47 | return 230400, nil 48 | default: 49 | return 0, unix.EINVAL 50 | } 51 | } 52 | 53 | func (a *attr) setSpeed(baud int) error { 54 | var rate uint64 55 | switch baud { 56 | case 50: 57 | rate = unix.B50 58 | case 75: 59 | rate = unix.B75 60 | case 110: 61 | rate = unix.B110 62 | case 134: 63 | rate = unix.B134 64 | case 150: 65 | rate = unix.B150 66 | case 200: 67 | rate = unix.B200 68 | case 300: 69 | rate = unix.B300 70 | case 600: 71 | rate = unix.B600 72 | case 1200: 73 | rate = unix.B1200 74 | case 1800: 75 | rate = unix.B1800 76 | case 2400: 77 | rate = unix.B2400 78 | case 4800: 79 | rate = unix.B4800 80 | case 9600: 81 | rate = unix.B9600 82 | case 19200: 83 | rate = unix.B19200 84 | case 38400: 85 | rate = unix.B38400 86 | case 57600: 87 | rate = unix.B57600 88 | case 115200: 89 | rate = unix.B115200 90 | case 230400: 91 | rate = unix.B230400 92 | default: 93 | return unix.EINVAL 94 | } 95 | (*unix.Termios)(a).Cflag = unix.CS8 | unix.CREAD | unix.CLOCAL | rate 96 | (*unix.Termios)(a).Ispeed = rate 97 | (*unix.Termios)(a).Ospeed = rate 98 | return nil 99 | } 100 | -------------------------------------------------------------------------------- /term_linux.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | type attr unix.Termios 6 | 7 | const ( 8 | // CBaudMask is the logical of CBAUD and CBAUDEX, except 9 | // that those values were not exposed via the syscall 10 | // package. Many of these values will be redundant, but 11 | // this long definition ensures we are portable if some 12 | // architecture defines different values for them (unlikely). 13 | CBaudMask = unix.B50 | 14 | unix.B75 | 15 | unix.B110 | 16 | unix.B134 | 17 | unix.B150 | 18 | unix.B200 | 19 | unix.B300 | 20 | unix.B600 | 21 | unix.B1200 | 22 | unix.B1800 | 23 | unix.B2400 | 24 | unix.B4800 | 25 | unix.B9600 | 26 | unix.B19200 | 27 | unix.B38400 | 28 | unix.B57600 | 29 | unix.B115200 | 30 | unix.B230400 | 31 | unix.B460800 | 32 | unix.B500000 | 33 | unix.B576000 | 34 | unix.B921600 | 35 | unix.B1000000 | 36 | unix.B1152000 | 37 | unix.B1500000 | 38 | unix.B2000000 | 39 | unix.B2500000 | 40 | unix.B3000000 | 41 | unix.B3500000 | 42 | unix.B4000000 43 | ) 44 | 45 | func (a *attr) getSpeed() (int, error) { 46 | switch a.Cflag & CBaudMask { 47 | case unix.B50: 48 | return 50, nil 49 | case unix.B75: 50 | return 75, nil 51 | case unix.B110: 52 | return 110, nil 53 | case unix.B134: 54 | return 134, nil 55 | case unix.B150: 56 | return 150, nil 57 | case unix.B200: 58 | return 200, nil 59 | case unix.B300: 60 | return 300, nil 61 | case unix.B600: 62 | return 600, nil 63 | case unix.B1200: 64 | return 1200, nil 65 | case unix.B1800: 66 | return 1800, nil 67 | case unix.B2400: 68 | return 2400, nil 69 | case unix.B4800: 70 | return 4800, nil 71 | case unix.B9600: 72 | return 9600, nil 73 | case unix.B19200: 74 | return 19200, nil 75 | case unix.B38400: 76 | return 38400, nil 77 | case unix.B57600: 78 | return 57600, nil 79 | case unix.B115200: 80 | return 115200, nil 81 | case unix.B230400: 82 | return 230400, nil 83 | case unix.B460800: 84 | return 460800, nil 85 | case unix.B500000: 86 | return 500000, nil 87 | case unix.B576000: 88 | return 576000, nil 89 | case unix.B921600: 90 | return 921600, nil 91 | case unix.B1000000: 92 | return 1000000, nil 93 | case unix.B1152000: 94 | return 1152000, nil 95 | case unix.B1500000: 96 | return 1500000, nil 97 | case unix.B2000000: 98 | return 2000000, nil 99 | case unix.B2500000: 100 | return 2500000, nil 101 | case unix.B3000000: 102 | return 3000000, nil 103 | case unix.B3500000: 104 | return 3500000, nil 105 | case unix.B4000000: 106 | return 4000000, nil 107 | default: 108 | return 0, unix.EINVAL 109 | } 110 | } 111 | 112 | func (a *attr) setSpeed(baud int) error { 113 | var rate uint32 114 | switch baud { 115 | case 50: 116 | rate = unix.B50 117 | case 75: 118 | rate = unix.B75 119 | case 110: 120 | rate = unix.B110 121 | case 134: 122 | rate = unix.B134 123 | case 150: 124 | rate = unix.B150 125 | case 200: 126 | rate = unix.B200 127 | case 300: 128 | rate = unix.B300 129 | case 600: 130 | rate = unix.B600 131 | case 1200: 132 | rate = unix.B1200 133 | case 1800: 134 | rate = unix.B1800 135 | case 2400: 136 | rate = unix.B2400 137 | case 4800: 138 | rate = unix.B4800 139 | case 9600: 140 | rate = unix.B9600 141 | case 19200: 142 | rate = unix.B19200 143 | case 38400: 144 | rate = unix.B38400 145 | case 57600: 146 | rate = unix.B57600 147 | case 115200: 148 | rate = unix.B115200 149 | case 230400: 150 | rate = unix.B230400 151 | case 460800: 152 | rate = unix.B460800 153 | case 500000: 154 | rate = unix.B500000 155 | case 576000: 156 | rate = unix.B576000 157 | case 921600: 158 | rate = unix.B921600 159 | case 1000000: 160 | rate = unix.B1000000 161 | case 1152000: 162 | rate = unix.B1152000 163 | case 1500000: 164 | rate = unix.B1500000 165 | case 2000000: 166 | rate = unix.B2000000 167 | case 2500000: 168 | rate = unix.B2500000 169 | case 3000000: 170 | rate = unix.B3000000 171 | case 3500000: 172 | rate = unix.B3500000 173 | case 4000000: 174 | rate = unix.B4000000 175 | default: 176 | return unix.EINVAL 177 | } 178 | a.Cflag = unix.CS8 | unix.CREAD | unix.CLOCAL | rate 179 | a.Ispeed = rate 180 | a.Ospeed = rate 181 | return nil 182 | } 183 | -------------------------------------------------------------------------------- /term_open_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!solaris 2 | 3 | package term 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/pkg/term/termios" 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | // Open opens an asynchronous communications port. 13 | func Open(name string, options ...func(*Term) error) (*Term, error) { 14 | fd, e := unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666) 15 | if e != nil { 16 | return nil, &os.PathError{ 17 | Op: "open", 18 | Path: name, 19 | Err: e, 20 | } 21 | } 22 | 23 | orig, err := termios.Tcgetattr(uintptr(fd)) 24 | if err != nil { 25 | unix.Close(fd) 26 | return nil, err 27 | } 28 | t := Term{name: name, fd: fd, orig: *orig} 29 | if err := t.SetOption(options...); err != nil { 30 | unix.Close(fd) 31 | return nil, err 32 | } 33 | 34 | if err := unix.SetNonblock(t.fd, false); err != nil { 35 | unix.Close(fd) 36 | return nil, err 37 | } 38 | 39 | return &t, nil 40 | } 41 | 42 | // Restore restores the state of the terminal captured at the point that 43 | // the terminal was originally opened. 44 | func (t *Term) Restore() error { 45 | return termios.Tcsetattr(uintptr(t.fd), termios.TCIOFLUSH, &t.orig) 46 | } 47 | -------------------------------------------------------------------------------- /term_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package term 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/pkg/term/termios" 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | // Term represents an asynchronous communications port. 13 | type Term struct { 14 | name string 15 | fd int 16 | orig unix.Termios // original state of the terminal, see Open and Restore 17 | } 18 | 19 | // SetAttr returns an option function which will apply the provided modifier to 20 | // a unix.Termios before using that unix.Termios to set the state of the 21 | // Term. This allows a developer to manually set attributes for the terminal. 22 | // Here's an example case to set a terminal into raw mode, but then re-enable 23 | // the 'opost' attribute: 24 | // 25 | // func EnableOutputPostprocess(a *unix.Termios) uintptr { 26 | // a.Oflag |= unix.OPOST 27 | // return termios.TCSANOW 28 | // } 29 | // 30 | // func init() { 31 | // t, _ = term.Open("/dev/tty") 32 | // t.SetRaw() 33 | // t.SetOption(term.SetAttr(EnableOutputPostprocess)) 34 | // } 35 | func SetAttr(modifier func(*unix.Termios) uintptr) func(*Term) error { 36 | return func(t *Term) error { 37 | a, err := termios.Tcgetattr(uintptr(t.fd)) 38 | if err != nil { 39 | return err 40 | } 41 | action := modifier(a) 42 | return termios.Tcsetattr(uintptr(t.fd), action, a) 43 | } 44 | } 45 | 46 | // SetCbreak sets cbreak mode. 47 | func (t *Term) SetCbreak() error { 48 | return t.SetOption(CBreakMode) 49 | } 50 | 51 | // CBreakMode places the terminal into cbreak mode. 52 | func CBreakMode(t *Term) error { 53 | a, err := termios.Tcgetattr(uintptr(t.fd)) 54 | if err != nil { 55 | return err 56 | } 57 | termios.Cfmakecbreak(a) 58 | return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, a) 59 | } 60 | 61 | // SetRaw sets raw mode. 62 | func (t *Term) SetRaw() error { 63 | return t.SetOption(RawMode) 64 | } 65 | 66 | // RawMode places the terminal into raw mode. 67 | func RawMode(t *Term) error { 68 | a, err := termios.Tcgetattr(uintptr(t.fd)) 69 | if err != nil { 70 | return err 71 | } 72 | termios.Cfmakeraw(a) 73 | return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, a) 74 | } 75 | 76 | // Speed sets the baud rate option for the terminal. 77 | func Speed(baud int) func(*Term) error { 78 | return func(t *Term) error { 79 | return t.setSpeed(baud) 80 | } 81 | } 82 | 83 | // SetSpeed sets the receive and transmit baud rates. 84 | func (t *Term) SetSpeed(baud int) error { 85 | return t.SetOption(Speed(baud)) 86 | } 87 | 88 | func (t *Term) setSpeed(baud int) error { 89 | a, err := termios.Tcgetattr(uintptr(t.fd)) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | err = (*attr)(a).setSpeed(baud) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, a) 100 | } 101 | 102 | // GetSpeed gets the current output baud rate. 103 | func (t *Term) GetSpeed() (int, error) { 104 | a, err := termios.Tcgetattr(uintptr(t.fd)) 105 | if err != nil { 106 | return 0, err 107 | } 108 | return (*attr)(a).getSpeed() 109 | } 110 | 111 | func clamp(v, lo, hi int64) int64 { 112 | if v < lo { 113 | return lo 114 | } 115 | if v > hi { 116 | return hi 117 | } 118 | return v 119 | } 120 | 121 | // timeoutVals converts d into values suitable for termios VMIN and VTIME ctrl chars 122 | func timeoutVals(d time.Duration) (uint8, uint8) { 123 | if d > 0 { 124 | // VTIME is expressed in terms of deciseconds 125 | vtimeDeci := d.Nanoseconds() / 1e6 / 100 126 | // ensure valid range 127 | vtime := uint8(clamp(vtimeDeci, 1, 0xff)) 128 | return 0, vtime 129 | } 130 | // block indefinitely until we receive at least 1 byte 131 | return 1, 0 132 | } 133 | 134 | // ReadTimeout sets the read timeout option for the terminal. 135 | func ReadTimeout(d time.Duration) func(*Term) error { 136 | return func(t *Term) error { 137 | return t.setReadTimeout(d) 138 | } 139 | } 140 | 141 | // SetReadTimeout sets the read timeout. 142 | // A zero value for d means read operations will not time out. 143 | func (t *Term) SetReadTimeout(d time.Duration) error { 144 | return t.SetOption(ReadTimeout(d)) 145 | } 146 | 147 | func (t *Term) setReadTimeout(d time.Duration) error { 148 | a, err := termios.Tcgetattr(uintptr(t.fd)) 149 | if err != nil { 150 | return err 151 | } 152 | a.Cc[unix.VMIN], a.Cc[unix.VTIME] = timeoutVals(d) 153 | return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, a) 154 | } 155 | 156 | // FlowControl sets the flow control option for the terminal. 157 | func FlowControl(kind int) func(*Term) error { 158 | return func(t *Term) error { 159 | return t.setFlowControl(kind) 160 | } 161 | } 162 | 163 | // SetFlowControl sets whether hardware flow control is enabled. 164 | func (t *Term) SetFlowControl(kind int) error { 165 | return t.SetOption(FlowControl(kind)) 166 | } 167 | 168 | func (t *Term) setFlowControl(kind int) error { 169 | a, err := termios.Tcgetattr(uintptr(t.fd)) 170 | if err != nil { 171 | return err 172 | } 173 | switch kind { 174 | case NONE: 175 | a.Iflag &^= termios.IXON | termios.IXOFF | termios.IXANY 176 | a.Cflag &^= termios.CRTSCTS 177 | 178 | case XONXOFF: 179 | a.Cflag &^= termios.CRTSCTS 180 | a.Iflag |= termios.IXON | termios.IXOFF | termios.IXANY 181 | 182 | case HARDWARE: 183 | a.Iflag &^= termios.IXON | termios.IXOFF | termios.IXANY 184 | a.Cflag |= termios.CRTSCTS 185 | } 186 | return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, a) 187 | } 188 | 189 | // Flush flushes both data received but not read, and data written but not transmitted. 190 | func (t *Term) Flush() error { 191 | return termios.Tcflush(uintptr(t.fd), termios.TCIOFLUSH) 192 | } 193 | 194 | // SendBreak sends a break signal. 195 | func (t *Term) SendBreak() error { 196 | return termios.Tcsendbreak(uintptr(t.fd), 0) 197 | } 198 | 199 | // DCD returns the state of the DCD (data carrier detect) signal. 200 | func (t *Term) DCD() (bool, error) { 201 | status, err := termios.Tiocmget(uintptr(t.fd)) 202 | return status&unix.TIOCM_CD == unix.TIOCM_CD, err 203 | } 204 | 205 | // SetDTR sets the DTR (data terminal ready) signal. 206 | func (t *Term) SetDTR(v bool) error { 207 | bits := unix.TIOCM_DTR 208 | if v { 209 | return termios.Tiocmbis(uintptr(t.fd), bits) 210 | } else { 211 | return termios.Tiocmbic(uintptr(t.fd), bits) 212 | } 213 | } 214 | 215 | // DTR returns the state of the DTR (data terminal ready) signal. 216 | func (t *Term) DTR() (bool, error) { 217 | status, err := termios.Tiocmget(uintptr(t.fd)) 218 | return status&unix.TIOCM_DTR == unix.TIOCM_DTR, err 219 | } 220 | 221 | // DSR returns the state of the DSR (data set ready) signal. 222 | func (t *Term) DSR() (bool, error) { 223 | status, err := termios.Tiocmget(uintptr(t.fd)) 224 | return status&unix.TIOCM_DSR == unix.TIOCM_DSR, err 225 | } 226 | 227 | // SetRTS sets the RTS (data terminal ready) signal. 228 | func (t *Term) SetRTS(v bool) error { 229 | bits := unix.TIOCM_RTS 230 | if v { 231 | return termios.Tiocmbis(uintptr(t.fd), bits) 232 | } else { 233 | return termios.Tiocmbic(uintptr(t.fd), bits) 234 | } 235 | } 236 | 237 | // RTS returns the state of the RTS (request to send) signal. 238 | func (t *Term) RTS() (bool, error) { 239 | status, err := termios.Tiocmget(uintptr(t.fd)) 240 | return status&unix.TIOCM_RTS == unix.TIOCM_RTS, err 241 | } 242 | 243 | // CTS returns the state of the CTS (clear to send) signal. 244 | func (t *Term) CTS() (bool, error) { 245 | status, err := termios.Tiocmget(uintptr(t.fd)) 246 | return status&unix.TIOCM_CTS == unix.TIOCM_CTS, err 247 | } 248 | 249 | // RI returns the state of the RI (ring indicator) signal. 250 | func (t *Term) RI() (bool, error) { 251 | status, err := termios.Tiocmget(uintptr(t.fd)) 252 | return status&unix.TIOCM_RI == unix.TIOCM_RI, err 253 | } 254 | 255 | // Close closes the device and releases any associated resources. 256 | func (t *Term) Close() error { 257 | err := unix.Close(t.fd) 258 | t.fd = -1 259 | return err 260 | } 261 | -------------------------------------------------------------------------------- /term_solaris.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | "unsafe" 7 | 8 | "github.com/pkg/term/termios" 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | type attr unix.Termios 13 | 14 | func (a *attr) getSpeed() (int, error) { 15 | // We generally only care about ospeed, since that's what would 16 | // be used for padding characters, for example. 17 | 18 | rate := termios.Cfgetospeed((*unix.Termios)(a)) 19 | 20 | switch rate { 21 | case unix.B50: 22 | return 50, nil 23 | case unix.B75: 24 | return 75, nil 25 | case unix.B110: 26 | return 110, nil 27 | case unix.B134: 28 | return 134, nil 29 | case unix.B150: 30 | return 150, nil 31 | case unix.B200: 32 | return 200, nil 33 | case unix.B300: 34 | return 300, nil 35 | case unix.B600: 36 | return 600, nil 37 | case unix.B1200: 38 | return 1200, nil 39 | case unix.B1800: 40 | return 1800, nil 41 | case unix.B2400: 42 | return 2400, nil 43 | case unix.B4800: 44 | return 4800, nil 45 | case unix.B9600: 46 | return 9600, nil 47 | case unix.B19200: 48 | return 19200, nil 49 | case unix.B38400: 50 | return 38400, nil 51 | case unix.B57600: 52 | return 57600, nil 53 | case unix.B115200: 54 | return 115200, nil 55 | case unix.B230400: 56 | return 230400, nil 57 | case unix.B460800: 58 | return 460800, nil 59 | case unix.B921600: 60 | return 921600, nil 61 | default: 62 | return 0, unix.EINVAL 63 | } 64 | } 65 | 66 | func (a *attr) setSpeed(baud int) error { 67 | var rate uint32 68 | switch baud { 69 | case 50: 70 | rate = unix.B50 71 | case 75: 72 | rate = unix.B75 73 | case 110: 74 | rate = unix.B110 75 | case 134: 76 | rate = unix.B134 77 | case 150: 78 | rate = unix.B150 79 | case 200: 80 | rate = unix.B200 81 | case 300: 82 | rate = unix.B300 83 | case 600: 84 | rate = unix.B600 85 | case 1200: 86 | rate = unix.B1200 87 | case 1800: 88 | rate = unix.B1800 89 | case 2400: 90 | rate = unix.B2400 91 | case 4800: 92 | rate = unix.B4800 93 | case 9600: 94 | rate = unix.B9600 95 | case 19200: 96 | rate = unix.B19200 97 | case 38400: 98 | rate = unix.B38400 99 | case 57600: 100 | rate = unix.B57600 101 | case 115200: 102 | rate = unix.B115200 103 | case 230400: 104 | rate = unix.B230400 105 | case 460800: 106 | rate = unix.B460800 107 | case 921600: 108 | rate = unix.B921600 109 | default: 110 | return unix.EINVAL 111 | } 112 | 113 | err := termios.Cfsetispeed((*unix.Termios)(a), uintptr(rate)) 114 | if err != nil { 115 | return err 116 | } 117 | 118 | err = termios.Cfsetospeed((*unix.Termios)(a), uintptr(rate)) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | return nil 124 | } 125 | 126 | // Open opens an asynchronous communications port. 127 | func Open(name string, options ...func(*Term) error) (*Term, error) { 128 | 129 | // copied from https://github.com/kofemann/opensolaris/blob/master/usr/src/uts/common/sys/stropts.h#L229 130 | // to avoid cgo dependency. 131 | const ( 132 | STR = ('S' << 8) 133 | I_PUSH = (STR | 02) 134 | ) 135 | 136 | fd, e := unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666) 137 | if e != nil { 138 | return nil, &os.PathError{"open", name, e} 139 | } 140 | 141 | modules := [2]string{"ptem", "ldterm"} 142 | for _, mod := range modules { 143 | err := unix.IoctlSetInt(fd, I_PUSH, int(uintptr(unsafe.Pointer(syscall.StringBytePtr(mod))))) 144 | if err != nil { 145 | unix.Close(fd) 146 | return nil, err 147 | } 148 | } 149 | 150 | orig, err := termios.Tcgetattr(uintptr(t.fd)) 151 | if err != nil { 152 | unix.Close(fd) 153 | return nil, err 154 | } 155 | t := Term{name: name, fd: fd, *orig} 156 | 157 | if err := t.SetOption(options...); err != nil { 158 | unix.Close(fd) 159 | return nil, err 160 | } 161 | 162 | if err := unix.SetNonblock(t.fd, false); err != nil { 163 | unix.Close(fd) 164 | return nil, err 165 | } 166 | 167 | return &t, nil 168 | } 169 | 170 | // Restore restores the state of the terminal captured at the point that 171 | // the terminal was originally opened. 172 | func (t *Term) Restore() error { 173 | return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.orig) 174 | } 175 | -------------------------------------------------------------------------------- /term_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package term 4 | 5 | import ( 6 | "testing" 7 | 8 | "time" 9 | 10 | "github.com/pkg/term/termios" 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | // assert that Term implements the same method set across 15 | // all supported platforms 16 | var _ interface { 17 | Available() (int, error) 18 | Buffered() (int, error) 19 | CTS() (bool, error) 20 | Close() error 21 | DCD() (bool, error) 22 | DSR() (bool, error) 23 | DTR() (bool, error) 24 | Flush() error 25 | RI() (bool, error) 26 | RTS() (bool, error) 27 | Read(b []byte) (int, error) 28 | Restore() error 29 | SendBreak() error 30 | SetCbreak() error 31 | SetDTR(v bool) error 32 | SetOption(options ...func(*Term) error) error 33 | SetRTS(v bool) error 34 | SetRaw() error 35 | SetSpeed(baud int) error 36 | GetSpeed() (int, error) 37 | Write(b []byte) (int, error) 38 | } = new(Term) 39 | 40 | func TestTermSetCbreak(t *testing.T) { 41 | tt := opendev(t) 42 | defer tt.Close() 43 | if err := tt.SetCbreak(); err != nil { 44 | t.Fatal(err) 45 | } 46 | } 47 | 48 | func TestTermSetRaw(t *testing.T) { 49 | tt := opendev(t) 50 | defer tt.Close() 51 | if err := tt.SetRaw(); err != nil { 52 | t.Fatal(err) 53 | } 54 | } 55 | 56 | func TestTermSetSpeed(t *testing.T) { 57 | tt := opendev(t) 58 | defer tt.Close() 59 | if err := tt.SetSpeed(57600); err != nil { 60 | t.Fatal(err) 61 | } 62 | 63 | if spd, err := tt.GetSpeed(); err != nil { 64 | t.Fatal(err) 65 | } else if spd != 57600 { 66 | t.Errorf("speed mismatch %d != 57600", spd) 67 | } 68 | } 69 | 70 | func TestTermSetInvalidSpeed(t *testing.T) { 71 | tt := opendev(t) 72 | defer tt.Close() 73 | if err := tt.SetSpeed(12345); err != unix.EINVAL { 74 | t.Fatal(err) 75 | } 76 | } 77 | 78 | func TestTermSetReadTimeout(t *testing.T) { 79 | tt := opendev(t) 80 | defer tt.Close() 81 | if err := tt.SetReadTimeout(1 * time.Second); err != nil { 82 | t.Fatal(err) 83 | } 84 | } 85 | 86 | func TestTermSetFlowControl(t *testing.T) { 87 | tt := opendev(t) 88 | defer tt.Close() 89 | 90 | kinds := []int{XONXOFF, NONE, HARDWARE, NONE, XONXOFF, HARDWARE, NONE} 91 | 92 | for _, kind := range kinds { 93 | if err := tt.SetFlowControl(kind); err != nil { 94 | t.Fatal(err) 95 | } 96 | } 97 | } 98 | 99 | func TestTermRestore(t *testing.T) { 100 | tt := opendev(t) 101 | defer tt.Close() 102 | if err := tt.Restore(); err != nil { 103 | t.Fatal(err) 104 | } 105 | } 106 | 107 | func opendev(t *testing.T) *Term { 108 | _, pts, err := termios.Pty() 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | term, err := Open(pts.Name()) 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | pts.Close() 117 | return term 118 | } 119 | -------------------------------------------------------------------------------- /term_windows.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Term struct { 8 | } 9 | 10 | var errNotSupported = errors.New("not supported") 11 | 12 | // Open opens an asynchronous communications port. 13 | func Open(name string, options ...func(*Term) error) (*Term, error) { 14 | return nil, errNotSupported 15 | } 16 | 17 | // SetOption takes one or more option function and applies them in order to Term. 18 | func (t *Term) SetOption(options ...func(*Term) error) error { 19 | return errNotSupported 20 | } 21 | 22 | // Read reads up to len(b) bytes from the terminal. It returns the number of 23 | // bytes read and an error, if any. EOF is signaled by a zero count with 24 | // err set to io.EOF. 25 | func (t *Term) Read(b []byte) (int, error) { 26 | return 0, errNotSupported 27 | } 28 | 29 | // Write writes len(b) bytes to the terminal. It returns the number of bytes 30 | // written and an error, if any. Write returns a non-nil error when n != 31 | // len(b). 32 | func (t *Term) Write(b []byte) (int, error) { 33 | return 0, errNotSupported 34 | } 35 | 36 | // Close closes the device and releases any associated resources. 37 | func (t *Term) Close() error { 38 | return errNotSupported 39 | } 40 | 41 | // SetCbreak sets cbreak mode. 42 | func (t *Term) SetCbreak() error { 43 | return errNotSupported 44 | } 45 | 46 | // CBreakMode places the terminal into cbreak mode. 47 | func CBreakMode(t *Term) error { 48 | return errNotSupported 49 | } 50 | 51 | // SetRaw sets raw mode. 52 | func (t *Term) SetRaw() error { 53 | return errNotSupported 54 | } 55 | 56 | // RawMode places the terminal into raw mode. 57 | func RawMode(t *Term) error { 58 | return errNotSupported 59 | } 60 | 61 | // Speed sets the baud rate option for the terminal. 62 | func Speed(baud int) func(*Term) error { 63 | return func(*Term) error { return errNotSupported } 64 | } 65 | 66 | // SetSpeed sets the receive and transmit baud rates. 67 | func (t *Term) SetSpeed(baud int) error { 68 | return errNotSupported 69 | } 70 | 71 | // GetSpeed gets the transmit baud rate. 72 | func (t *Term) GetSpeed() (int, error) { 73 | return 0, errNotSupported 74 | } 75 | 76 | // Flush flushes both data received but not read, and data written but not transmitted. 77 | func (t *Term) Flush() error { 78 | return errNotSupported 79 | } 80 | 81 | // SendBreak sends a break signal. 82 | func (t *Term) SendBreak() error { 83 | return errNotSupported 84 | } 85 | 86 | // SetDTR sets the DTR (data terminal ready) signal. 87 | func (t *Term) SetDTR(v bool) error { 88 | return errNotSupported 89 | } 90 | 91 | // DTR returns the state of the DTR (data terminal ready) signal. 92 | func (t *Term) DTR() (bool, error) { 93 | return false, errNotSupported 94 | } 95 | 96 | // SetRTS sets the RTS (data terminal ready) signal. 97 | func (t *Term) SetRTS(v bool) error { 98 | return errNotSupported 99 | } 100 | 101 | // RTS returns the state of the RTS (data terminal ready) signal. 102 | func (t *Term) RTS() (bool, error) { 103 | return false, errNotSupported 104 | } 105 | 106 | // Restore restores the state of the terminal captured at the point that 107 | // the terminal was originally opened. 108 | func (t *Term) Restore() error { 109 | return errNotSupported 110 | } 111 | -------------------------------------------------------------------------------- /termios/doc.go: -------------------------------------------------------------------------------- 1 | // Package termios implements the low level termios(3) terminal line discipline facilities. 2 | // 3 | // For a higher level interface please use the github.com/pkg/term package. 4 | package termios 5 | -------------------------------------------------------------------------------- /termios/ioctl.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package termios 4 | 5 | import "golang.org/x/sys/unix" 6 | 7 | func ioctl(fd, request, argp uintptr) error { 8 | return unix.IoctlSetInt(int(fd), uint(request), int(argp)) 9 | } 10 | -------------------------------------------------------------------------------- /termios/pty.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package termios 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | func open_device(path string) (uintptr, error) { 13 | fd, err := unix.Open(path, unix.O_NOCTTY|unix.O_RDWR|unix.O_CLOEXEC, 0666) 14 | if err != nil { 15 | return 0, fmt.Errorf("unable to open %q: %v", path, err) 16 | } 17 | return uintptr(fd), nil 18 | } 19 | 20 | // Pty returns a UNIX 98 pseudoterminal device. 21 | // Pty returns a pair of fds representing the master and slave pair. 22 | func Pty() (*os.File, *os.File, error) { 23 | ptm, err := open_pty_master() 24 | if err != nil { 25 | return nil, nil, err 26 | } 27 | 28 | sname, err := Ptsname(ptm) 29 | if err != nil { 30 | unix.Close(int(ptm)) 31 | return nil, nil, err 32 | } 33 | 34 | err = grantpt(ptm) 35 | if err != nil { 36 | unix.Close(int(ptm)) 37 | return nil, nil, err 38 | } 39 | 40 | err = unlockpt(ptm) 41 | if err != nil { 42 | unix.Close(int(ptm)) 43 | return nil, nil, err 44 | } 45 | 46 | pts, err := open_device(sname) 47 | if err != nil { 48 | unix.Close(int(ptm)) 49 | return nil, nil, err 50 | } 51 | return os.NewFile(uintptr(ptm), "ptm"), os.NewFile(uintptr(pts), sname), nil 52 | } 53 | -------------------------------------------------------------------------------- /termios/pty_cgo.go: -------------------------------------------------------------------------------- 1 | // +build dragonfly openbsd solaris 2 | 3 | package termios 4 | 5 | // #include 6 | import "C" 7 | 8 | import "syscall" 9 | 10 | func open_pty_master() (uintptr, error) { 11 | rc := C.posix_openpt(syscall.O_NOCTTY | syscall.O_RDWR) 12 | if rc < 0 { 13 | return 0, syscall.Errno(rc) 14 | } 15 | return uintptr(rc), nil 16 | } 17 | 18 | func Ptsname(fd uintptr) (string, error) { 19 | slavename := C.GoString(C.ptsname(C.int(fd))) 20 | return slavename, nil 21 | } 22 | 23 | func grantpt(fd uintptr) error { 24 | rc := C.grantpt(C.int(fd)) 25 | if rc == 0 { 26 | return nil 27 | } 28 | return syscall.Errno(rc) 29 | } 30 | 31 | func unlockpt(fd uintptr) error { 32 | rc := C.unlockpt(C.int(fd)) 33 | if rc == 0 { 34 | return nil 35 | } 36 | return syscall.Errno(rc) 37 | } 38 | -------------------------------------------------------------------------------- /termios/pty_darwin.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import ( 4 | "errors" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | func open_pty_master() (uintptr, error) { 11 | return open_device("/dev/ptmx") 12 | } 13 | 14 | const ( 15 | _IOC_PARAM_SHIFT = 13 16 | _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1 17 | ) 18 | 19 | func _IOC_PARM_LEN(ioctl uintptr) uintptr { 20 | return (ioctl >> 16) & _IOC_PARAM_MASK 21 | } 22 | 23 | func Ptsname(fd uintptr) (string, error) { 24 | n := make([]byte, _IOC_PARM_LEN(unix.TIOCPTYGNAME)) 25 | 26 | err := ioctl(fd, unix.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) 27 | if err != nil { 28 | return "", err 29 | } 30 | 31 | for i, c := range n { 32 | if c == 0 { 33 | return string(n[:i]), nil 34 | } 35 | } 36 | return "", errors.New("TIOCPTYGNAME string not NUL-terminated") 37 | } 38 | 39 | func grantpt(fd uintptr) error { 40 | return unix.IoctlSetInt(int(fd), unix.TIOCPTYGRANT, 0) 41 | } 42 | 43 | func unlockpt(fd uintptr) error { 44 | return unix.IoctlSetInt(int(fd), unix.TIOCPTYUNLK, 0) 45 | } 46 | -------------------------------------------------------------------------------- /termios/pty_freebsd.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func posix_openpt(oflag int) (fd uintptr, err error) { 10 | // Copied from debian-golang-pty/pty_freebsd.go. 11 | r0, _, e1 := unix.Syscall(unix.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0) 12 | fd = uintptr(r0) 13 | if e1 != 0 { 14 | err = e1 15 | } 16 | return 17 | } 18 | 19 | func open_pty_master() (uintptr, error) { 20 | return posix_openpt(unix.O_NOCTTY | unix.O_RDWR | unix.O_CLOEXEC) 21 | } 22 | 23 | func Ptsname(fd uintptr) (string, error) { 24 | n, err := unix.IoctlGetInt(int(fd), unix.TIOCGPTN) 25 | return fmt.Sprintf("/dev/pts/%d", n), err 26 | } 27 | 28 | func grantpt(fd uintptr) error { 29 | return unix.IoctlSetInt(int(fd), unix.TIOCGPTN, 0) 30 | } 31 | 32 | func unlockpt(fd uintptr) error { 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /termios/pty_linux.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | func open_pty_master() (uintptr, error) { 11 | return open_device("/dev/ptmx") 12 | } 13 | 14 | func Ptsname(fd uintptr) (string, error) { 15 | n, err := unix.IoctlGetInt(int(fd), unix.TIOCGPTN) 16 | return fmt.Sprintf("/dev/pts/%d", n), err 17 | } 18 | 19 | func grantpt(fd uintptr) error { 20 | var n uintptr 21 | return ioctl(fd, unix.TIOCGPTN, uintptr(unsafe.Pointer(&n))) 22 | } 23 | 24 | func unlockpt(fd uintptr) error { 25 | var n uintptr 26 | return ioctl(fd, unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&n))) 27 | } 28 | -------------------------------------------------------------------------------- /termios/pty_netbsd.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import ( 4 | "bytes" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func open_pty_master() (uintptr, error) { 10 | return open_device("/dev/ptmx") 11 | } 12 | 13 | func Ptsname(fd uintptr) (string, error) { 14 | ptm, err := unix.IoctlGetPtmget(int(fd), unix.TIOCPTSNAME) 15 | if err != nil { 16 | return "", err 17 | } 18 | return string(ptm.Sn[:bytes.IndexByte(ptm.Sn[:], 0)]), nil 19 | } 20 | 21 | func grantpt(fd uintptr) error { 22 | return unix.IoctlSetInt(int(fd), unix.TIOCGRANTPT, 0) 23 | } 24 | 25 | func unlockpt(fd uintptr) error { 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /termios/pty_test.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPtsname(t *testing.T) { 8 | fd, err := open_pty_master() 9 | if err != nil { 10 | t.Fatal(err) 11 | } 12 | 13 | name, err := Ptsname(fd) 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | t.Log(name) 18 | } 19 | -------------------------------------------------------------------------------- /termios/termios.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package termios 4 | 5 | import ( 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | // Tiocmget returns the state of the MODEM bits. 10 | func Tiocmget(fd uintptr) (int, error) { 11 | return unix.IoctlGetInt(int(fd), unix.TIOCMGET) 12 | } 13 | 14 | // Tiocmset sets the state of the MODEM bits. 15 | func Tiocmset(fd uintptr, status int) error { 16 | return unix.IoctlSetInt(int(fd), unix.TIOCMSET, status) 17 | } 18 | 19 | // Tiocmbis sets the indicated modem bits. 20 | func Tiocmbis(fd uintptr, status int) error { 21 | return unix.IoctlSetPointerInt(int(fd), unix.TIOCMBIS, status) 22 | } 23 | 24 | // Tiocmbic clears the indicated modem bits. 25 | func Tiocmbic(fd uintptr, status int) error { 26 | return unix.IoctlSetPointerInt(int(fd), unix.TIOCMBIC, status) 27 | } 28 | 29 | // Tiocoutq return the number of bytes in the output buffer. 30 | func Tiocoutq(fd uintptr) (int, error) { 31 | return unix.IoctlGetInt(int(fd), unix.TIOCOUTQ) 32 | } 33 | 34 | // Cfmakecbreak modifies attr for cbreak mode. 35 | func Cfmakecbreak(attr *unix.Termios) { 36 | attr.Lflag &^= unix.ECHO | unix.ICANON 37 | attr.Cc[unix.VMIN] = 1 38 | attr.Cc[unix.VTIME] = 0 39 | } 40 | 41 | // Cfmakeraw modifies attr for raw mode. 42 | func Cfmakeraw(attr *unix.Termios) { 43 | attr.Iflag &^= unix.BRKINT | unix.ICRNL | unix.INPCK | unix.ISTRIP | unix.IXON 44 | attr.Oflag &^= unix.OPOST 45 | attr.Cflag &^= unix.CSIZE | unix.PARENB 46 | attr.Cflag |= unix.CS8 47 | attr.Lflag &^= unix.ECHO | unix.ICANON | unix.IEXTEN | unix.ISIG 48 | attr.Cc[unix.VMIN] = 1 49 | attr.Cc[unix.VTIME] = 0 50 | } 51 | -------------------------------------------------------------------------------- /termios/termios_bsd.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd openbsd netbsd dragonfly 2 | 3 | package termios 4 | 5 | import ( 6 | "time" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | const ( 12 | IXON = 0x00000200 13 | IXOFF = 0x00000400 14 | IXANY = 0x00000800 15 | CCTS_OFLOW = 0x00010000 16 | CRTS_IFLOW = 0x00020000 17 | CRTSCTS = CCTS_OFLOW | CRTS_IFLOW 18 | ) 19 | 20 | // Tcgetattr gets the current serial port settings. 21 | func Tcgetattr(fd uintptr) (*unix.Termios, error) { 22 | return unix.IoctlGetTermios(int(fd), unix.TIOCGETA) 23 | } 24 | 25 | // Tcsetattr sets the current serial port settings. 26 | func Tcsetattr(fd, opt uintptr, argp *unix.Termios) error { 27 | switch opt { 28 | case TCSANOW: 29 | opt = unix.TIOCSETA 30 | case TCSADRAIN: 31 | opt = unix.TIOCSETAW 32 | case TCSAFLUSH: 33 | opt = unix.TIOCSETAF 34 | default: 35 | return unix.EINVAL 36 | } 37 | return unix.IoctlSetTermios(int(fd), uint(opt), argp) 38 | } 39 | 40 | // Tcsendbreak function transmits a continuous stream of zero-valued bits for 41 | // four-tenths of a second to the terminal referenced by fildes. The duration 42 | // parameter is ignored in this implementation. 43 | func Tcsendbreak(fd uintptr, duration int) error { 44 | if err := unix.IoctlSetInt(int(fd), unix.TIOCSBRK, 0); err != nil { 45 | return err 46 | } 47 | time.Sleep(4 / 10 * time.Second) 48 | return unix.IoctlSetInt(int(fd), unix.TIOCCBRK, 0) 49 | } 50 | 51 | // Tcdrain waits until all output written to the terminal referenced by fd has been transmitted to the terminal. 52 | func Tcdrain(fd uintptr) error { 53 | return unix.IoctlSetInt(int(fd), unix.TIOCDRAIN, 0) 54 | } 55 | 56 | // Tcflush discards data written to the object referred to by fd but not transmitted, or data received but not read, depending on the value of which. 57 | func Tcflush(fd, which uintptr) error { 58 | return unix.IoctlSetPointerInt(int(fd), unix.TIOCFLUSH, int(which)) 59 | } 60 | 61 | // Cfgetispeed returns the input baud rate stored in the termios structure. 62 | func Cfgetispeed(attr *unix.Termios) uint32 { return uint32(attr.Ispeed) } 63 | 64 | // Cfgetospeed returns the output baud rate stored in the termios structure. 65 | func Cfgetospeed(attr *unix.Termios) uint32 { return uint32(attr.Ospeed) } 66 | 67 | // Tiocinq returns the number of bytes in the input buffer. 68 | func Tiocinq(fd uintptr) (int, error) { 69 | return 0, nil 70 | } 71 | -------------------------------------------------------------------------------- /termios/termios_const.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!solaris 2 | 3 | package termios 4 | 5 | const ( 6 | TCIFLUSH = 0 7 | TCOFLUSH = 1 8 | TCIOFLUSH = 2 9 | 10 | TCSANOW = 0 11 | TCSADRAIN = 1 12 | TCSAFLUSH = 2 13 | ) 14 | -------------------------------------------------------------------------------- /termios/termios_const_solaris.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | // #include 4 | import "C" 5 | 6 | const ( 7 | TCIFLUSH = C.TCIFLUSH 8 | TCOFLUSH = C.TCOFLUSH 9 | TCIOFLUSH = C.TCIOFLUSH 10 | 11 | TCSANOW = C.TCSANOW 12 | TCSADRAIN = C.TCSADRAIN 13 | TCSAFLUSH = C.TCSAFLUSH 14 | ) 15 | -------------------------------------------------------------------------------- /termios/termios_linux.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import ( 4 | "golang.org/x/sys/unix" 5 | ) 6 | 7 | const ( 8 | IXON = 0x00000400 9 | IXANY = 0x00000800 10 | IXOFF = 0x00001000 11 | CRTSCTS = 0x80000000 12 | ) 13 | 14 | // Tcgetattr gets the current serial port settings. 15 | func Tcgetattr(fd uintptr) (*unix.Termios, error) { 16 | return unix.IoctlGetTermios(int(fd), unix.TCGETS) 17 | } 18 | 19 | // Tcsetattr sets the current serial port settings. 20 | func Tcsetattr(fd, action uintptr, argp *unix.Termios) error { 21 | var request uintptr 22 | switch action { 23 | case TCSANOW: 24 | request = unix.TCSETS 25 | case TCSADRAIN: 26 | request = unix.TCSETSW 27 | case TCSAFLUSH: 28 | request = unix.TCSETSF 29 | default: 30 | return unix.EINVAL 31 | } 32 | return unix.IoctlSetTermios(int(fd), uint(request), argp) 33 | } 34 | 35 | // Tcsendbreak transmits a continuous stream of zero-valued bits for a specific 36 | // duration, if the terminal is using asynchronous serial data transmission. If 37 | // duration is zero, it transmits zero-valued bits for at least 0.25 seconds, and not more that 0.5 seconds. 38 | // If duration is not zero, it sends zero-valued bits for some 39 | // implementation-defined length of time. 40 | func Tcsendbreak(fd uintptr, duration int) error { 41 | return unix.IoctlSetInt(int(fd), unix.TCSBRKP, duration) 42 | } 43 | 44 | // Tcdrain waits until all output written to the object referred to by fd has been transmitted. 45 | func Tcdrain(fd uintptr) error { 46 | // simulate drain with TCSADRAIN 47 | attr, err := Tcgetattr(fd) 48 | if err != nil { 49 | return err 50 | } 51 | return Tcsetattr(fd, TCSADRAIN, attr) 52 | } 53 | 54 | // Tcflush discards data written to the object referred to by fd but not transmitted, or data received but not read, depending on the value of selector. 55 | func Tcflush(fd, selector uintptr) error { 56 | return unix.IoctlSetInt(int(fd), unix.TCFLSH, int(selector)) 57 | } 58 | 59 | // Tiocinq returns the number of bytes in the input buffer. 60 | func Tiocinq(fd uintptr) (int, error) { 61 | return unix.IoctlGetInt(int(fd), unix.TIOCINQ) 62 | } 63 | 64 | // Cfgetispeed returns the input baud rate stored in the termios structure. 65 | func Cfgetispeed(attr *unix.Termios) uint32 { return attr.Ispeed } 66 | 67 | // Cfgetospeed returns the output baud rate stored in the termios structure. 68 | func Cfgetospeed(attr *unix.Termios) uint32 { return attr.Ospeed } 69 | -------------------------------------------------------------------------------- /termios/termios_linux_test.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | import "testing" 4 | 5 | func TestTcflush(t *testing.T) { 6 | f := opendev(t) 7 | defer f.Close() 8 | 9 | if err := Tcflush(f.Fd(), TCIOFLUSH); err != nil { 10 | t.Fatal(err) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /termios/termios_solaris.go: -------------------------------------------------------------------------------- 1 | package termios 2 | 3 | // #include 4 | // typedef struct termios termios_t; 5 | import "C" 6 | 7 | import ( 8 | "golang.org/x/sys/unix" 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | TCSETS = 0x5402 14 | TCSETSW = 0x5403 15 | TCSETSF = 0x5404 16 | TCSBRK = 0x5409 17 | TCSBRKP = 0x5425 18 | 19 | IXON = 0x00000400 20 | IXANY = 0x00000800 21 | IXOFF = 0x00001000 22 | CRTSCTS = 0x80000000 23 | ) 24 | 25 | // Tcgetattr gets the current serial port settings. 26 | func Tcgetattr(fd uintptr) (*unix.Termios, error) { 27 | return unix.IoctlGetTermios(int(fd), unix.TCGETS) 28 | } 29 | 30 | // Tcsetattr sets the current serial port settings. 31 | func Tcsetattr(fd, action uintptr, argp *unix.Termios) error { 32 | return unix.IoctlSetTermios(int(fd), uint(action), tiosToUnix(argp)) 33 | } 34 | 35 | // Tcsendbreak transmits a continuous stream of zero-valued bits for a specific 36 | // duration, if the terminal is using asynchronous serial data transmission. If 37 | // duration is zero, it transmits zero-valued bits for at least 0.25 seconds, and not more that 0.5 seconds. 38 | // If duration is not zero, it sends zero-valued bits for some 39 | // implementation-defined length of time. 40 | func Tcsendbreak(fd uintptr, duration int) error { 41 | return ioctl(fd, TCSBRKP, uintptr(duration)) 42 | } 43 | 44 | // Tcdrain waits until all output written to the object referred to by fd has been transmitted. 45 | func Tcdrain(fd uintptr) error { 46 | // simulate drain with TCSADRAIN 47 | var attr unix.Termios 48 | if err := Tcgetattr(fd, &attr); err != nil { 49 | return err 50 | } 51 | return Tcsetattr(fd, TCSADRAIN, &attr) 52 | } 53 | 54 | // Tcflush discards data written to the object referred to by fd but not transmitted, or data received but not read, depending on the value of selector. 55 | func Tcflush(fd, selector uintptr) error { 56 | return ioctl(fd, unix.TCFLSH, selector) 57 | } 58 | 59 | // Tiocinq returns the number of bytes in the input buffer. 60 | func Tiocinq(fd uintptr) (int, error) { 61 | return unix.IoctlGetInt(int(fd), unix.FIORDCHK) 62 | } 63 | 64 | // Cfgetispeed returns the input baud rate stored in the termios structure. 65 | func Cfgetispeed(attr *unix.Termios) uint32 { 66 | solTermios := tiosToUnix(attr) 67 | return uint32(C.cfgetispeed((*C.termios_t)(unsafe.Pointer(solTermios)))) 68 | } 69 | 70 | // Cfsetispeed sets the input baud rate stored in the termios structure. 71 | func Cfsetispeed(attr *unix.Termios, speed uintptr) error { 72 | solTermios := tiosToUnix(attr) 73 | _, err := C.cfsetispeed((*C.termios_t)(unsafe.Pointer(solTermios)), C.speed_t(speed)) 74 | return err 75 | } 76 | 77 | // Cfgetospeed returns the output baud rate stored in the termios structure. 78 | func Cfgetospeed(attr *unix.Termios) uint32 { 79 | solTermios := tiosToUnix(attr) 80 | return uint32(C.cfgetospeed((*C.termios_t)(unsafe.Pointer(solTermios)))) 81 | } 82 | 83 | // Cfsetospeed sets the output baud rate stored in the termios structure. 84 | func Cfsetospeed(attr *unix.Termios, speed uintptr) error { 85 | solTermios := tiosToUnix(attr) 86 | _, err := C.cfsetospeed((*C.termios_t)(unsafe.Pointer(solTermios)), C.speed_t(speed)) 87 | return err 88 | } 89 | 90 | // tiosToUnix copies a unix.Termios to a x/sys/unix.Termios. 91 | // This is needed since type conversions between the two fail due to 92 | // more recent x/sys/unix.Termios renaming the padding field. 93 | func tiosToUnix(st *unix.Termios) *unix.Termios { 94 | return &unix.Termios{ 95 | Iflag: st.Iflag, 96 | Oflag: st.Oflag, 97 | Cflag: st.Cflag, 98 | Lflag: st.Lflag, 99 | Cc: st.Cc, 100 | } 101 | } 102 | 103 | // tiosTounix copies a x/sys/unix.Termios to a unix.Termios. 104 | func tiosTounix(ut *unix.Termios) *unix.Termios { 105 | return &unix.Termios{ 106 | Iflag: ut.Iflag, 107 | Oflag: ut.Oflag, 108 | Cflag: ut.Cflag, 109 | Lflag: ut.Lflag, 110 | Cc: ut.Cc, 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /termios/termios_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package termios 4 | 5 | import ( 6 | "os" 7 | "runtime" 8 | "testing" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | func TestTcgetattr(t *testing.T) { 14 | f := opendev(t) 15 | 16 | if _, err := Tcgetattr(f.Fd()); err != nil { 17 | t.Fatal(err) 18 | } 19 | } 20 | 21 | func TestTcsetattr(t *testing.T) { 22 | f := opendev(t) 23 | 24 | termios, err := Tcgetattr(f.Fd()) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | for _, opt := range []uintptr{TCSANOW, TCSADRAIN, TCSAFLUSH} { 29 | if err := Tcsetattr(f.Fd(), opt, termios); err != nil { 30 | t.Fatal(err) 31 | } 32 | } 33 | } 34 | 35 | func TestTcsendbreak(t *testing.T) { 36 | f := opendev(t) 37 | 38 | if err := Tcsendbreak(f.Fd(), 0); err != nil { 39 | t.Fatal(err) 40 | } 41 | } 42 | 43 | func TestTcdrain(t *testing.T) { 44 | f := opendev(t) 45 | 46 | if err := Tcdrain(f.Fd()); err != nil { 47 | t.Fatal(err) 48 | } 49 | } 50 | 51 | func TestTiocmget(t *testing.T) { 52 | f := opendev(t) 53 | 54 | if _, err := Tiocmget(f.Fd()); err != nil { 55 | checktty(t, err) 56 | t.Fatal(err) 57 | } 58 | } 59 | 60 | func TestTiocmset(t *testing.T) { 61 | f := opendev(t) 62 | 63 | status, err := Tiocmget(f.Fd()) 64 | if err != nil { 65 | checktty(t, err) 66 | t.Fatal(err) 67 | } 68 | if err := Tiocmset(f.Fd(), status); err != nil { 69 | checktty(t, err) 70 | t.Fatal(err) 71 | } 72 | } 73 | 74 | func TestTiocmbis(t *testing.T) { 75 | f := opendev(t) 76 | 77 | if err := Tiocmbis(f.Fd(), 0); err != nil { 78 | checktty(t, err) 79 | t.Fatal(err) 80 | } 81 | } 82 | 83 | func TestTiocmbic(t *testing.T) { 84 | f := opendev(t) 85 | 86 | if err := Tiocmbic(f.Fd(), 0); err != nil { 87 | checktty(t, err) 88 | t.Fatal(err) 89 | } 90 | } 91 | 92 | func TestTiocinq(t *testing.T) { 93 | f := opendev(t) 94 | 95 | inq, err := Tiocinq(f.Fd()) 96 | if err != nil { 97 | t.Fatal(err) 98 | } 99 | if inq != 0 { 100 | t.Fatalf("Expected 0 bytes, got %v", inq) 101 | } 102 | } 103 | 104 | func TestTiocoutq(t *testing.T) { 105 | f := opendev(t) 106 | 107 | inq, err := Tiocoutq(f.Fd()) 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | if inq != 0 { 112 | t.Fatalf("Expected 0 bytes, got %v", inq) 113 | } 114 | } 115 | 116 | func TestCfgetispeed(t *testing.T) { 117 | f := opendev(t) 118 | 119 | termios, err := Tcgetattr(f.Fd()) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | if baud := Cfgetispeed(termios); baud == 0 && runtime.GOOS != "linux" { 124 | t.Fatalf("Cfgetispeed: expected > 0, got %v", baud) 125 | } 126 | } 127 | 128 | func TestCfgetospeed(t *testing.T) { 129 | f := opendev(t) 130 | 131 | termios, err := Tcgetattr(f.Fd()) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | if baud := Cfgetospeed(termios); baud == 0 && runtime.GOOS != "linux" { 136 | t.Fatalf("Cfgetospeed: expected > 0, got %v", baud) 137 | } 138 | } 139 | 140 | func opendev(t *testing.T) *os.File { 141 | _, pts, err := Pty() 142 | if err != nil { 143 | t.Fatal(err) 144 | } 145 | t.Cleanup(func() { 146 | pts.Close() 147 | }) 148 | return pts 149 | } 150 | 151 | func checktty(t *testing.T, err error) { 152 | t.Helper() 153 | // some ioctls fail against char devices if they do not 154 | // support a particular feature 155 | if (runtime.GOOS == "darwin" && err == unix.ENOTTY) || (runtime.GOOS == "linux" && err == unix.EINVAL) { 156 | t.Skip(err) 157 | } 158 | } 159 | --------------------------------------------------------------------------------