| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | // Copyright 2016 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package console | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/peterh/liner" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | // Stdin holds the stdin line reader (also using stdout for printing prompts). | 
					
						
							|  |  |  | // Only this reader may be used for input because it keeps an internal buffer. | 
					
						
							|  |  |  | var Stdin = newTerminalPrompter() | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 06:07:20 +09:00
										 |  |  | // UserPrompter defines the methods needed by the console to prompt the user for | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | // various types of inputs. | 
					
						
							|  |  |  | type UserPrompter interface { | 
					
						
							|  |  |  | 	// PromptInput displays the given prompt to the user and requests some textual | 
					
						
							|  |  |  | 	// data to be entered, returning the input of the user. | 
					
						
							|  |  |  | 	PromptInput(prompt string) (string, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// PromptPassword displays the given prompt to the user and requests some textual | 
					
						
							|  |  |  | 	// data to be entered, but one which must not be echoed out into the terminal. | 
					
						
							|  |  |  | 	// The method returns the input provided by the user. | 
					
						
							|  |  |  | 	PromptPassword(prompt string) (string, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// PromptConfirm displays the given prompt to the user and requests a boolean | 
					
						
							|  |  |  | 	// choice to be made, returning that choice. | 
					
						
							|  |  |  | 	PromptConfirm(prompt string) (bool, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-27 16:49:29 +08:00
										 |  |  | 	// SetHistory sets the input scrollback history that the prompter will allow | 
					
						
							| 
									
										
										
										
											2017-01-06 19:44:35 +02:00
										 |  |  | 	// the user to scroll back to. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	SetHistory(history []string) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// AppendHistory appends an entry to the scrollback history. It should be called | 
					
						
							|  |  |  | 	// if and only if the prompt to append was a valid command. | 
					
						
							|  |  |  | 	AppendHistory(command string) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-08 06:14:14 -08:00
										 |  |  | 	// ClearHistory clears the entire history | 
					
						
							|  |  |  | 	ClearHistory() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	// SetWordCompleter sets the completion function that the prompter will call to | 
					
						
							|  |  |  | 	// fetch completion candidates when the user presses tab. | 
					
						
							|  |  |  | 	SetWordCompleter(completer WordCompleter) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WordCompleter takes the currently edited line with the cursor position and | 
					
						
							|  |  |  | // returns the completion candidates for the partial word to be completed. If | 
					
						
							|  |  |  | // the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, | 
					
						
							|  |  |  | // wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world", | 
					
						
							|  |  |  | // "Word"}, "!!!") to have "Hello, world!!!". | 
					
						
							|  |  |  | type WordCompleter func(line string, pos int) (string, []string, string) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // terminalPrompter is a UserPrompter backed by the liner package. It supports | 
					
						
							|  |  |  | // prompting the user for various input, among others for non-echoing password | 
					
						
							|  |  |  | // input. | 
					
						
							|  |  |  | type terminalPrompter struct { | 
					
						
							|  |  |  | 	*liner.State | 
					
						
							|  |  |  | 	warned     bool | 
					
						
							|  |  |  | 	supported  bool | 
					
						
							|  |  |  | 	normalMode liner.ModeApplier | 
					
						
							|  |  |  | 	rawMode    liner.ModeApplier | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newTerminalPrompter creates a liner based user input prompter working off the | 
					
						
							|  |  |  | // standard input and output streams. | 
					
						
							|  |  |  | func newTerminalPrompter() *terminalPrompter { | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	p := new(terminalPrompter) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	// Get the original mode before calling NewLiner. | 
					
						
							|  |  |  | 	// This is usually regular "cooked" mode where characters echo. | 
					
						
							|  |  |  | 	normalMode, _ := liner.TerminalMode() | 
					
						
							|  |  |  | 	// Turn on liner. It switches to raw mode. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	p.State = liner.NewLiner() | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	rawMode, err := liner.TerminalMode() | 
					
						
							|  |  |  | 	if err != nil || !liner.TerminalSupported() { | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 		p.supported = false | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 		p.supported = true | 
					
						
							|  |  |  | 		p.normalMode = normalMode | 
					
						
							|  |  |  | 		p.rawMode = rawMode | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 		// Switch back to normal mode while we're not prompting. | 
					
						
							|  |  |  | 		normalMode.ApplyMode() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	p.SetCtrlCAborts(true) | 
					
						
							|  |  |  | 	p.SetTabCompletionStyle(liner.TabPrints) | 
					
						
							| 
									
										
										
										
											2016-11-03 19:51:37 +01:00
										 |  |  | 	p.SetMultiLineMode(true) | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	return p | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PromptInput displays the given prompt to the user and requests some textual | 
					
						
							|  |  |  | // data to be entered, returning the input of the user. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | func (p *terminalPrompter) PromptInput(prompt string) (string, error) { | 
					
						
							|  |  |  | 	if p.supported { | 
					
						
							|  |  |  | 		p.rawMode.ApplyMode() | 
					
						
							|  |  |  | 		defer p.normalMode.ApplyMode() | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// liner tries to be smart about printing the prompt | 
					
						
							|  |  |  | 		// and doesn't print anything if input is redirected. | 
					
						
							|  |  |  | 		// Un-smart it by printing the prompt always. | 
					
						
							|  |  |  | 		fmt.Print(prompt) | 
					
						
							|  |  |  | 		prompt = "" | 
					
						
							|  |  |  | 		defer fmt.Println() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	return p.State.Prompt(prompt) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PromptPassword displays the given prompt to the user and requests some textual | 
					
						
							|  |  |  | // data to be entered, but one which must not be echoed out into the terminal. | 
					
						
							|  |  |  | // The method returns the input provided by the user. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) { | 
					
						
							|  |  |  | 	if p.supported { | 
					
						
							|  |  |  | 		p.rawMode.ApplyMode() | 
					
						
							|  |  |  | 		defer p.normalMode.ApplyMode() | 
					
						
							|  |  |  | 		return p.State.PasswordPrompt(prompt) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	if !p.warned { | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 		fmt.Println("!! Unsupported terminal, password will be echoed.") | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 		p.warned = true | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Just as in Prompt, handle printing the prompt here instead of relying on liner. | 
					
						
							|  |  |  | 	fmt.Print(prompt) | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | 	passwd, err = p.State.Prompt("") | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	fmt.Println() | 
					
						
							|  |  |  | 	return passwd, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PromptConfirm displays the given prompt to the user and requests a boolean | 
					
						
							|  |  |  | // choice to be made, returning that choice. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) { | 
					
						
							|  |  |  | 	input, err := p.Prompt(prompt + " [y/N] ") | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" { | 
					
						
							|  |  |  | 		return true, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-27 16:49:29 +08:00
										 |  |  | // SetHistory sets the input scrollback history that the prompter will allow | 
					
						
							| 
									
										
										
										
											2017-01-06 19:44:35 +02:00
										 |  |  | // the user to scroll back to. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | func (p *terminalPrompter) SetHistory(history []string) { | 
					
						
							|  |  |  | 	p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n"))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-02 07:00:13 -05:00
										 |  |  | // AppendHistory appends an entry to the scrollback history. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | func (p *terminalPrompter) AppendHistory(command string) { | 
					
						
							|  |  |  | 	p.State.AppendHistory(command) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-08 06:14:14 -08:00
										 |  |  | // ClearHistory clears the entire history | 
					
						
							|  |  |  | func (p *terminalPrompter) ClearHistory() { | 
					
						
							|  |  |  | 	p.State.ClearHistory() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | // SetWordCompleter sets the completion function that the prompter will call to | 
					
						
							|  |  |  | // fetch completion candidates when the user presses tab. | 
					
						
							| 
									
										
										
										
											2016-05-30 17:30:17 +03:00
										 |  |  | func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) { | 
					
						
							|  |  |  | 	p.State.SetWordCompleter(liner.WordCompleter(completer)) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | } |