1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris 6 7// Package terminal provides support functions for dealing with terminals, as 8// commonly found on UNIX systems. 9// 10// Putting a terminal into raw mode is the most common requirement: 11// 12// oldState, err := terminal.MakeRaw(0) 13// if err != nil { 14// panic(err) 15// } 16// defer terminal.Restore(0, oldState) 17package readline 18 19import ( 20 "io" 21 "syscall" 22) 23 24// State contains the state of a terminal. 25type State struct { 26 termios Termios 27} 28 29// IsTerminal returns true if the given file descriptor is a terminal. 30func IsTerminal(fd int) bool { 31 _, err := getTermios(fd) 32 return err == nil 33} 34 35// MakeRaw put the terminal connected to the given file descriptor into raw 36// mode and returns the previous state of the terminal so that it can be 37// restored. 38func MakeRaw(fd int) (*State, error) { 39 var oldState State 40 41 if termios, err := getTermios(fd); err != nil { 42 return nil, err 43 } else { 44 oldState.termios = *termios 45 } 46 47 newState := oldState.termios 48 // This attempts to replicate the behaviour documented for cfmakeraw in 49 // the termios(3) manpage. 50 newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON 51 // newState.Oflag &^= syscall.OPOST 52 newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN 53 newState.Cflag &^= syscall.CSIZE | syscall.PARENB 54 newState.Cflag |= syscall.CS8 55 56 newState.Cc[syscall.VMIN] = 1 57 newState.Cc[syscall.VTIME] = 0 58 59 return &oldState, setTermios(fd, &newState) 60} 61 62// GetState returns the current state of a terminal which may be useful to 63// restore the terminal after a signal. 64func GetState(fd int) (*State, error) { 65 termios, err := getTermios(fd) 66 if err != nil { 67 return nil, err 68 } 69 70 return &State{termios: *termios}, nil 71} 72 73// Restore restores the terminal connected to the given file descriptor to a 74// previous state. 75func restoreTerm(fd int, state *State) error { 76 return setTermios(fd, &state.termios) 77} 78 79// ReadPassword reads a line of input from a terminal without local echo. This 80// is commonly used for inputting passwords and other sensitive data. The slice 81// returned does not include the \n. 82func ReadPassword(fd int) ([]byte, error) { 83 oldState, err := getTermios(fd) 84 if err != nil { 85 return nil, err 86 } 87 88 newState := oldState 89 newState.Lflag &^= syscall.ECHO 90 newState.Lflag |= syscall.ICANON | syscall.ISIG 91 newState.Iflag |= syscall.ICRNL 92 if err := setTermios(fd, newState); err != nil { 93 return nil, err 94 } 95 96 defer func() { 97 setTermios(fd, oldState) 98 }() 99 100 var buf [16]byte 101 var ret []byte 102 for { 103 n, err := syscall.Read(fd, buf[:]) 104 if err != nil { 105 return nil, err 106 } 107 if n == 0 { 108 if len(ret) == 0 { 109 return nil, io.EOF 110 } 111 break 112 } 113 if buf[n-1] == '\n' { 114 n-- 115 } 116 ret = append(ret, buf[:n]...) 117 if n < len(buf) { 118 break 119 } 120 } 121 122 return ret, nil 123} 124