├── LICENSE ├── README.md ├── const_bsd.go ├── const_linux.go ├── getpass.go ├── getpass_nix.go ├── getpass_windows.go ├── isatty-c.go ├── isatty.go ├── isatty_nix.go └── isatty_windows.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Andrew Dunham 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-termutil 2 | 3 | This package exposes some very basic, useful functions: 4 | 5 | Isatty(file *os.File) bool 6 | 7 | This function will return whether or not the given file is a TTY, attempting to use native 8 | operations when possible. It wil fall back to using the `isatty()` function from `unistd.h` 9 | through cgo if on an unknown platform. 10 | 11 | GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) 12 | 13 | This function will print the `prompt` string to the file identified by `prompt_fd`, prompt the user 14 | for a password without echoing the password to the terminal, print a newline, and then return the 15 | given password to the user. NOTE: not yet tested on anything except Linux & OS X. 16 | -------------------------------------------------------------------------------- /const_bsd.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd 2 | 3 | package termutil 4 | 5 | import "syscall" 6 | 7 | const ioctlReadTermios = syscall.TIOCGETA 8 | const ioctlWriteTermios = syscall.TIOCSETA 9 | -------------------------------------------------------------------------------- /const_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package termutil 4 | 5 | import "syscall" 6 | 7 | const ioctlReadTermios = syscall.TCGETS 8 | const ioctlWriteTermios = syscall.TCSETS 9 | -------------------------------------------------------------------------------- /getpass.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!linux,!darwin,!freebsd 2 | 3 | package termutil 4 | 5 | func GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) { 6 | panic("not implemented") 7 | } 8 | -------------------------------------------------------------------------------- /getpass_nix.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd 2 | 3 | package termutil 4 | 5 | import ( 6 | "io" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | func GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) { 12 | // Firstly, print the prompt. 13 | written := 0 14 | buf := []byte(prompt) 15 | for written < len(prompt) { 16 | n, err := syscall.Write(int(prompt_fd), buf[written:]) 17 | if err != nil { 18 | return nil, err 19 | } 20 | if n == 0 { 21 | return nil, io.EOF 22 | } 23 | 24 | written += n 25 | } 26 | 27 | // Write a newline after we're done, since it won't be echoed when the 28 | // user presses 'Enter'. 29 | defer syscall.Write(int(prompt_fd), []byte("\n")) 30 | 31 | // Get the current state of the terminal 32 | var oldState syscall.Termios 33 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, 34 | uintptr(input_fd), 35 | ioctlReadTermios, 36 | uintptr(unsafe.Pointer(&oldState)), 37 | 0, 0, 0); err != 0 { 38 | return nil, err 39 | } 40 | 41 | // Turn off echo and write the new state. 42 | newState := oldState 43 | newState.Lflag &^= syscall.ECHO 44 | newState.Lflag |= syscall.ICANON | syscall.ISIG 45 | newState.Iflag |= syscall.ICRNL 46 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, 47 | uintptr(input_fd), 48 | ioctlWriteTermios, 49 | uintptr(unsafe.Pointer(&newState)), 50 | 0, 0, 0); err != 0 { 51 | return nil, err 52 | } 53 | 54 | // Regardless of how we exit, we need to restore the old state. 55 | defer func() { 56 | syscall.Syscall6(syscall.SYS_IOCTL, 57 | uintptr(input_fd), 58 | ioctlWriteTermios, 59 | uintptr(unsafe.Pointer(&oldState)), 60 | 0, 0, 0) 61 | }() 62 | 63 | // Read in increments of 16 bytes. 64 | var readBuf [16]byte 65 | var ret []byte 66 | for { 67 | n, err := syscall.Read(int(input_fd), readBuf[:]) 68 | if err != nil { 69 | return nil, err 70 | } 71 | if n == 0 { 72 | if len(ret) == 0 { 73 | return nil, io.EOF 74 | } 75 | break 76 | } 77 | 78 | // Trim the trailing newline. 79 | if readBuf[n-1] == '\n' { 80 | n-- 81 | } 82 | 83 | ret = append(ret, readBuf[:n]...) 84 | if n < len(readBuf) { 85 | break 86 | } 87 | } 88 | 89 | return ret, nil 90 | } 91 | -------------------------------------------------------------------------------- /getpass_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package termutil 4 | 5 | import ( 6 | "syscall" 7 | "io" 8 | "errors" 9 | ) 10 | 11 | var ( 12 | f_getwch uintptr // wint_t _getwch(void) 13 | ) 14 | 15 | func init() { 16 | f_getwch = syscall.MustLoadDLL("msvcrt.dll").MustFindProc("_getwch").Addr() 17 | } 18 | 19 | func GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) { 20 | // Firstly, print the prompt. 21 | written := 0 22 | buf := []byte(prompt) 23 | for written < len(prompt) { 24 | n, err := syscall.Write(syscall.Handle(prompt_fd), buf[written:]) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if n == 0 { 29 | return nil, io.EOF 30 | } 31 | 32 | written += n 33 | } 34 | 35 | // Write a newline after we're done, since it won't be echoed when the 36 | // user presses 'Enter'. 37 | defer syscall.Write(syscall.Handle(prompt_fd), []byte("\r\n")) 38 | 39 | // Read the characters 40 | var chars []uint16 41 | for { 42 | ret, _, _ := syscall.Syscall(f_getwch, 0, 0, 0, 0) 43 | if ret == 0x000A || ret == 0x000D { 44 | break 45 | } else if ret == 0x0003 { 46 | return nil, errors.New("Input has been interrupted by user.") 47 | } else if ret == 0x0008 { 48 | chars = chars[0:len(chars)-2] 49 | } else { 50 | chars = append(chars, uint16(ret)) 51 | } 52 | } 53 | 54 | // Convert to string... 55 | s := syscall.UTF16ToString(chars) 56 | 57 | // ... and back to UTF-8 bytes. 58 | return []byte(s), nil 59 | } 60 | -------------------------------------------------------------------------------- /isatty-c.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!linux,!darwin,!freebsd,cgo 2 | 3 | package termutil 4 | 5 | /* 6 | #include 7 | */ 8 | import "C" 9 | 10 | import "os" 11 | 12 | func Isatty(fd uintptr) bool { 13 | return int(C.isatty(C.int(fd))) != 0 14 | } 15 | -------------------------------------------------------------------------------- /isatty.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!linux,!darwin,!freebsd,!cgo 2 | 3 | package termutil 4 | 5 | func Isatty(fd uintptr) bool { 6 | panic("Not implemented") 7 | } 8 | -------------------------------------------------------------------------------- /isatty_nix.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd 2 | 3 | package termutil 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | func Isatty(fd uintptr) bool { 11 | var termios syscall.Termios 12 | 13 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, 14 | uintptr(ioctlReadTermios), 15 | uintptr(unsafe.Pointer(&termios)), 16 | 0, 17 | 0, 18 | 0) 19 | return err == 0 20 | } 21 | -------------------------------------------------------------------------------- /isatty_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package termutil 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | var ( 11 | kernel32 = syscall.MustLoadDLL("kernel32.dll") 12 | fGetConsoleMode = kernel32.MustFindProc("GetConsoleMode") 13 | ) 14 | 15 | func Isatty(fd uintptr) bool { 16 | var x uint32 17 | return getConsoleMode(syscall.Handle(fd), &x) == nil 18 | } 19 | 20 | func getConsoleMode(hConsoleHandle syscall.Handle, lpMode *uint32) error { 21 | ret, _, err := syscall.Syscall(fGetConsoleMode.Addr(), 2, 22 | uintptr(hConsoleHandle), 23 | uintptr(unsafe.Pointer(lpMode)), 24 | 0) 25 | 26 | if int(ret) == 0 { 27 | if err != 0 { 28 | return error(err) 29 | } else { 30 | return syscall.EINVAL 31 | } 32 | } 33 | return nil 34 | } 35 | --------------------------------------------------------------------------------