input.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package utils
  17. import (
  18. "fmt"
  19. "strings"
  20. "github.com/peterh/liner"
  21. )
  22. // Holds the stdin line reader.
  23. // Only this reader may be used for input because it keeps
  24. // an internal buffer.
  25. var Stdin = newUserInputReader()
  26. type userInputReader struct {
  27. *liner.State
  28. warned bool
  29. supported bool
  30. normalMode liner.ModeApplier
  31. rawMode liner.ModeApplier
  32. }
  33. func newUserInputReader() *userInputReader {
  34. r := new(userInputReader)
  35. // Get the original mode before calling NewLiner.
  36. // This is usually regular "cooked" mode where characters echo.
  37. normalMode, _ := liner.TerminalMode()
  38. // Turn on liner. It switches to raw mode.
  39. r.State = liner.NewLiner()
  40. rawMode, err := liner.TerminalMode()
  41. if err != nil || !liner.TerminalSupported() {
  42. r.supported = false
  43. } else {
  44. r.supported = true
  45. r.normalMode = normalMode
  46. r.rawMode = rawMode
  47. // Switch back to normal mode while we're not prompting.
  48. normalMode.ApplyMode()
  49. }
  50. return r
  51. }
  52. func (r *userInputReader) Prompt(prompt string) (string, error) {
  53. if r.supported {
  54. r.rawMode.ApplyMode()
  55. defer r.normalMode.ApplyMode()
  56. } else {
  57. // liner tries to be smart about printing the prompt
  58. // and doesn't print anything if input is redirected.
  59. // Un-smart it by printing the prompt always.
  60. fmt.Print(prompt)
  61. prompt = ""
  62. defer fmt.Println()
  63. }
  64. return r.State.Prompt(prompt)
  65. }
  66. func (r *userInputReader) PasswordPrompt(prompt string) (passwd string, err error) {
  67. if r.supported {
  68. r.rawMode.ApplyMode()
  69. defer r.normalMode.ApplyMode()
  70. return r.State.PasswordPrompt(prompt)
  71. }
  72. if !r.warned {
  73. fmt.Println("!! Unsupported terminal, password will be echoed.")
  74. r.warned = true
  75. }
  76. // Just as in Prompt, handle printing the prompt here instead of relying on liner.
  77. fmt.Print(prompt)
  78. passwd, err = r.State.Prompt("")
  79. fmt.Println()
  80. return passwd, err
  81. }
  82. func (r *userInputReader) ConfirmPrompt(prompt string) (bool, error) {
  83. prompt = prompt + " [y/N] "
  84. input, err := r.Prompt(prompt)
  85. if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
  86. return true, nil
  87. }
  88. return false, err
  89. }