github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/pkg/term/term.go (about) 1 // +build !windows 2 3 // Package term provides structures and helper functions to work with 4 // terminal (state, sizes). 5 package term 6 7 import ( 8 "errors" 9 "fmt" 10 "io" 11 "os" 12 "os/signal" 13 "syscall" 14 ) 15 16 var ( 17 // ErrInvalidState is returned if the state of the terminal is invalid. 18 ErrInvalidState = errors.New("Invalid terminal state") 19 ) 20 21 // State represents the state of the terminal. 22 type State struct { 23 termios Termios 24 } 25 26 // Winsize represents the size of the terminal window. 27 type Winsize struct { 28 Height uint16 29 Width uint16 30 x uint16 31 y uint16 32 } 33 34 // StdStreams returns the standard streams (stdin, stdout, stedrr). 35 func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { 36 return os.Stdin, os.Stdout, os.Stderr 37 } 38 39 // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. 40 func GetFdInfo(in interface{}) (uintptr, bool) { 41 var inFd uintptr 42 var isTerminalIn bool 43 if file, ok := in.(*os.File); ok { 44 inFd = file.Fd() 45 isTerminalIn = IsTerminal(inFd) 46 } 47 return inFd, isTerminalIn 48 } 49 50 // IsTerminal returns true if the given file descriptor is a terminal. 51 func IsTerminal(fd uintptr) bool { 52 var termios Termios 53 return tcget(fd, &termios) == 0 54 } 55 56 // RestoreTerminal restores the terminal connected to the given file descriptor 57 // to a previous state. 58 func RestoreTerminal(fd uintptr, state *State) error { 59 if state == nil { 60 return ErrInvalidState 61 } 62 if err := tcset(fd, &state.termios); err != 0 { 63 return err 64 } 65 return nil 66 } 67 68 // SaveState saves the state of the terminal connected to the given file descriptor. 69 func SaveState(fd uintptr) (*State, error) { 70 var oldState State 71 if err := tcget(fd, &oldState.termios); err != 0 { 72 return nil, err 73 } 74 75 return &oldState, nil 76 } 77 78 // DisableEcho applies the specified state to the terminal connected to the file 79 // descriptor, with echo disabled. 80 func DisableEcho(fd uintptr, state *State) error { 81 newState := state.termios 82 newState.Lflag &^= syscall.ECHO 83 84 if err := tcset(fd, &newState); err != 0 { 85 return err 86 } 87 handleInterrupt(fd, state) 88 return nil 89 } 90 91 // SetRawTerminal puts the terminal connected to the given file descriptor into 92 // raw mode and returns the previous state. On UNIX, this puts both the input 93 // and output into raw mode. On Windows, it only puts the input into raw mode. 94 func SetRawTerminal(fd uintptr) (*State, error) { 95 oldState, err := MakeRaw(fd) 96 if err != nil { 97 return nil, err 98 } 99 handleInterrupt(fd, oldState) 100 return oldState, err 101 } 102 103 // SetRawTerminalOutput puts the output of terminal connected to the given file 104 // descriptor into raw mode. On UNIX, this does nothing and returns nil for the 105 // state. On Windows, it disables LF -> CRLF translation. 106 func SetRawTerminalOutput(fd uintptr) (*State, error) { 107 return nil, nil 108 } 109 110 func handleInterrupt(fd uintptr, state *State) { 111 sigchan := make(chan os.Signal, 1) 112 signal.Notify(sigchan, os.Interrupt) 113 go func() { 114 for range sigchan { 115 // quit cleanly and the new terminal item is on a new line 116 fmt.Println() 117 signal.Stop(sigchan) 118 close(sigchan) 119 RestoreTerminal(fd, state) 120 os.Exit(1) 121 } 122 }() 123 }