input.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // +build linux darwin openbsd freebsd netbsd
  2. package liner
  3. import (
  4. "bufio"
  5. "errors"
  6. "os"
  7. "os/signal"
  8. "strconv"
  9. "strings"
  10. "syscall"
  11. "time"
  12. )
  13. type nexter struct {
  14. r rune
  15. err error
  16. }
  17. // State represents an open terminal
  18. type State struct {
  19. commonState
  20. origMode termios
  21. defaultMode termios
  22. next <-chan nexter
  23. winch chan os.Signal
  24. pending []rune
  25. useCHA bool
  26. }
  27. // NewLiner initializes a new *State, and sets the terminal into raw mode. To
  28. // restore the terminal to its previous state, call State.Close().
  29. func NewLiner() *State {
  30. var s State
  31. s.r = bufio.NewReader(os.Stdin)
  32. s.terminalSupported = TerminalSupported()
  33. if m, err := TerminalMode(); err == nil {
  34. s.origMode = *m.(*termios)
  35. } else {
  36. s.inputRedirected = true
  37. }
  38. if _, err := getMode(syscall.Stdout); err != 0 {
  39. s.outputRedirected = true
  40. }
  41. if s.inputRedirected && s.outputRedirected {
  42. s.terminalSupported = false
  43. }
  44. if s.terminalSupported && !s.inputRedirected && !s.outputRedirected {
  45. mode := s.origMode
  46. mode.Iflag &^= icrnl | inpck | istrip | ixon
  47. mode.Cflag |= cs8
  48. mode.Lflag &^= syscall.ECHO | icanon | iexten
  49. mode.ApplyMode()
  50. winch := make(chan os.Signal, 1)
  51. signal.Notify(winch, syscall.SIGWINCH)
  52. s.winch = winch
  53. s.checkOutput()
  54. }
  55. if !s.outputRedirected {
  56. s.outputRedirected = !s.getColumns()
  57. }
  58. return &s
  59. }
  60. var errTimedOut = errors.New("timeout")
  61. func (s *State) startPrompt() {
  62. if s.terminalSupported {
  63. if m, err := TerminalMode(); err == nil {
  64. s.defaultMode = *m.(*termios)
  65. mode := s.defaultMode
  66. mode.Lflag &^= isig
  67. mode.ApplyMode()
  68. }
  69. }
  70. s.restartPrompt()
  71. }
  72. func (s *State) inputWaiting() bool {
  73. return len(s.next) > 0
  74. }
  75. func (s *State) restartPrompt() {
  76. next := make(chan nexter, 200)
  77. go func() {
  78. for {
  79. var n nexter
  80. n.r, _, n.err = s.r.ReadRune()
  81. next <- n
  82. // Shut down nexter loop when an end condition has been reached
  83. if n.err != nil || n.r == '\n' || n.r == '\r' || n.r == ctrlC || n.r == ctrlD {
  84. close(next)
  85. return
  86. }
  87. }
  88. }()
  89. s.next = next
  90. }
  91. func (s *State) stopPrompt() {
  92. if s.terminalSupported {
  93. s.defaultMode.ApplyMode()
  94. }
  95. }
  96. func (s *State) nextPending(timeout <-chan time.Time) (rune, error) {
  97. select {
  98. case thing, ok := <-s.next:
  99. if !ok {
  100. return 0, ErrInternal
  101. }
  102. if thing.err != nil {
  103. return 0, thing.err
  104. }
  105. s.pending = append(s.pending, thing.r)
  106. return thing.r, nil
  107. case <-timeout:
  108. rv := s.pending[0]
  109. s.pending = s.pending[1:]
  110. return rv, errTimedOut
  111. }
  112. }
  113. func (s *State) readNext() (interface{}, error) {
  114. if len(s.pending) > 0 {
  115. rv := s.pending[0]
  116. s.pending = s.pending[1:]
  117. return rv, nil
  118. }
  119. var r rune
  120. select {
  121. case thing, ok := <-s.next:
  122. if !ok {
  123. return 0, ErrInternal
  124. }
  125. if thing.err != nil {
  126. return nil, thing.err
  127. }
  128. r = thing.r
  129. case <-s.winch:
  130. s.getColumns()
  131. return winch, nil
  132. }
  133. if r != esc {
  134. return r, nil
  135. }
  136. s.pending = append(s.pending, r)
  137. // Wait at most 50 ms for the rest of the escape sequence
  138. // If nothing else arrives, it was an actual press of the esc key
  139. timeout := time.After(50 * time.Millisecond)
  140. flag, err := s.nextPending(timeout)
  141. if err != nil {
  142. if err == errTimedOut {
  143. return flag, nil
  144. }
  145. return unknown, err
  146. }
  147. switch flag {
  148. case '[':
  149. code, err := s.nextPending(timeout)
  150. if err != nil {
  151. if err == errTimedOut {
  152. return code, nil
  153. }
  154. return unknown, err
  155. }
  156. switch code {
  157. case 'A':
  158. s.pending = s.pending[:0] // escape code complete
  159. return up, nil
  160. case 'B':
  161. s.pending = s.pending[:0] // escape code complete
  162. return down, nil
  163. case 'C':
  164. s.pending = s.pending[:0] // escape code complete
  165. return right, nil
  166. case 'D':
  167. s.pending = s.pending[:0] // escape code complete
  168. return left, nil
  169. case 'F':
  170. s.pending = s.pending[:0] // escape code complete
  171. return end, nil
  172. case 'H':
  173. s.pending = s.pending[:0] // escape code complete
  174. return home, nil
  175. case 'Z':
  176. s.pending = s.pending[:0] // escape code complete
  177. return shiftTab, nil
  178. case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
  179. num := []rune{code}
  180. for {
  181. code, err := s.nextPending(timeout)
  182. if err != nil {
  183. if err == errTimedOut {
  184. return code, nil
  185. }
  186. return nil, err
  187. }
  188. switch code {
  189. case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
  190. num = append(num, code)
  191. case ';':
  192. // Modifier code to follow
  193. // This only supports Ctrl-left and Ctrl-right for now
  194. x, _ := strconv.ParseInt(string(num), 10, 32)
  195. if x != 1 {
  196. // Can't be left or right
  197. rv := s.pending[0]
  198. s.pending = s.pending[1:]
  199. return rv, nil
  200. }
  201. num = num[:0]
  202. for {
  203. code, err = s.nextPending(timeout)
  204. if err != nil {
  205. if err == errTimedOut {
  206. rv := s.pending[0]
  207. s.pending = s.pending[1:]
  208. return rv, nil
  209. }
  210. return nil, err
  211. }
  212. switch code {
  213. case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
  214. num = append(num, code)
  215. case 'C', 'D':
  216. // right, left
  217. mod, _ := strconv.ParseInt(string(num), 10, 32)
  218. if mod != 5 {
  219. // Not bare Ctrl
  220. rv := s.pending[0]
  221. s.pending = s.pending[1:]
  222. return rv, nil
  223. }
  224. s.pending = s.pending[:0] // escape code complete
  225. if code == 'C' {
  226. return wordRight, nil
  227. }
  228. return wordLeft, nil
  229. default:
  230. // Not left or right
  231. rv := s.pending[0]
  232. s.pending = s.pending[1:]
  233. return rv, nil
  234. }
  235. }
  236. case '~':
  237. s.pending = s.pending[:0] // escape code complete
  238. x, _ := strconv.ParseInt(string(num), 10, 32)
  239. switch x {
  240. case 2:
  241. return insert, nil
  242. case 3:
  243. return del, nil
  244. case 5:
  245. return pageUp, nil
  246. case 6:
  247. return pageDown, nil
  248. case 1, 7:
  249. return home, nil
  250. case 4, 8:
  251. return end, nil
  252. case 15:
  253. return f5, nil
  254. case 17:
  255. return f6, nil
  256. case 18:
  257. return f7, nil
  258. case 19:
  259. return f8, nil
  260. case 20:
  261. return f9, nil
  262. case 21:
  263. return f10, nil
  264. case 23:
  265. return f11, nil
  266. case 24:
  267. return f12, nil
  268. default:
  269. return unknown, nil
  270. }
  271. default:
  272. // unrecognized escape code
  273. rv := s.pending[0]
  274. s.pending = s.pending[1:]
  275. return rv, nil
  276. }
  277. }
  278. }
  279. case 'O':
  280. code, err := s.nextPending(timeout)
  281. if err != nil {
  282. if err == errTimedOut {
  283. return code, nil
  284. }
  285. return nil, err
  286. }
  287. s.pending = s.pending[:0] // escape code complete
  288. switch code {
  289. case 'c':
  290. return wordRight, nil
  291. case 'd':
  292. return wordLeft, nil
  293. case 'H':
  294. return home, nil
  295. case 'F':
  296. return end, nil
  297. case 'P':
  298. return f1, nil
  299. case 'Q':
  300. return f2, nil
  301. case 'R':
  302. return f3, nil
  303. case 'S':
  304. return f4, nil
  305. default:
  306. return unknown, nil
  307. }
  308. case 'b':
  309. s.pending = s.pending[:0] // escape code complete
  310. return altB, nil
  311. case 'd':
  312. s.pending = s.pending[:0] // escape code complete
  313. return altD, nil
  314. case bs:
  315. s.pending = s.pending[:0] // escape code complete
  316. return altBs, nil
  317. case 'f':
  318. s.pending = s.pending[:0] // escape code complete
  319. return altF, nil
  320. case 'y':
  321. s.pending = s.pending[:0] // escape code complete
  322. return altY, nil
  323. default:
  324. rv := s.pending[0]
  325. s.pending = s.pending[1:]
  326. return rv, nil
  327. }
  328. // not reached
  329. return r, nil
  330. }
  331. // Close returns the terminal to its previous mode
  332. func (s *State) Close() error {
  333. signal.Stop(s.winch)
  334. if !s.inputRedirected {
  335. s.origMode.ApplyMode()
  336. }
  337. return nil
  338. }
  339. // TerminalSupported returns true if the current terminal supports
  340. // line editing features, and false if liner will use the 'dumb'
  341. // fallback for input.
  342. // Note that TerminalSupported does not check all factors that may
  343. // cause liner to not fully support the terminal (such as stdin redirection)
  344. func TerminalSupported() bool {
  345. bad := map[string]bool{"": true, "dumb": true, "cons25": true}
  346. return !bad[strings.ToLower(os.Getenv("TERM"))]
  347. }