You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.0 KiB

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