mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 10:00:01 +08:00 
			
		
		
		
	 8e4a42aa42
			
		
	
	8e4a42aa42
	
	
	
		
			
			Add support for short-name aliasing. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
		
			
				
	
	
		
			327 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Readline is a pure go implementation for GNU-Readline kind library.
 | |
| //
 | |
| // example:
 | |
| // 	rl, err := readline.New("> ")
 | |
| // 	if err != nil {
 | |
| // 		panic(err)
 | |
| // 	}
 | |
| // 	defer rl.Close()
 | |
| //
 | |
| // 	for {
 | |
| // 		line, err := rl.Readline()
 | |
| // 		if err != nil { // io.EOF
 | |
| // 			break
 | |
| // 		}
 | |
| // 		println(line)
 | |
| // 	}
 | |
| //
 | |
| package readline
 | |
| 
 | |
| import "io"
 | |
| 
 | |
| type Instance struct {
 | |
| 	Config    *Config
 | |
| 	Terminal  *Terminal
 | |
| 	Operation *Operation
 | |
| }
 | |
| 
 | |
| type Config struct {
 | |
| 	// prompt supports ANSI escape sequence, so we can color some characters even in windows
 | |
| 	Prompt string
 | |
| 
 | |
| 	// readline will persist historys to file where HistoryFile specified
 | |
| 	HistoryFile string
 | |
| 	// specify the max length of historys, it's 500 by default, set it to -1 to disable history
 | |
| 	HistoryLimit           int
 | |
| 	DisableAutoSaveHistory bool
 | |
| 	// enable case-insensitive history searching
 | |
| 	HistorySearchFold bool
 | |
| 
 | |
| 	// AutoCompleter will called once user press TAB
 | |
| 	AutoComplete AutoCompleter
 | |
| 
 | |
| 	// Any key press will pass to Listener
 | |
| 	// NOTE: Listener will be triggered by (nil, 0, 0) immediately
 | |
| 	Listener Listener
 | |
| 
 | |
| 	Painter Painter
 | |
| 
 | |
| 	// If VimMode is true, readline will in vim.insert mode by default
 | |
| 	VimMode bool
 | |
| 
 | |
| 	InterruptPrompt string
 | |
| 	EOFPrompt       string
 | |
| 
 | |
| 	FuncGetWidth func() int
 | |
| 
 | |
| 	Stdin       io.ReadCloser
 | |
| 	StdinWriter io.Writer
 | |
| 	Stdout      io.Writer
 | |
| 	Stderr      io.Writer
 | |
| 
 | |
| 	EnableMask bool
 | |
| 	MaskRune   rune
 | |
| 
 | |
| 	// erase the editing line after user submited it
 | |
| 	// it use in IM usually.
 | |
| 	UniqueEditLine bool
 | |
| 
 | |
| 	// filter input runes (may be used to disable CtrlZ or for translating some keys to different actions)
 | |
| 	// -> output = new (translated) rune and true/false if continue with processing this one
 | |
| 	FuncFilterInputRune func(rune) (rune, bool)
 | |
| 
 | |
| 	// force use interactive even stdout is not a tty
 | |
| 	FuncIsTerminal      func() bool
 | |
| 	FuncMakeRaw         func() error
 | |
| 	FuncExitRaw         func() error
 | |
| 	FuncOnWidthChanged  func(func())
 | |
| 	ForceUseInteractive bool
 | |
| 
 | |
| 	// private fields
 | |
| 	inited    bool
 | |
| 	opHistory *opHistory
 | |
| 	opSearch  *opSearch
 | |
| }
 | |
| 
 | |
| func (c *Config) useInteractive() bool {
 | |
| 	if c.ForceUseInteractive {
 | |
| 		return true
 | |
| 	}
 | |
| 	return c.FuncIsTerminal()
 | |
| }
 | |
| 
 | |
| func (c *Config) Init() error {
 | |
| 	if c.inited {
 | |
| 		return nil
 | |
| 	}
 | |
| 	c.inited = true
 | |
| 	if c.Stdin == nil {
 | |
| 		c.Stdin = NewCancelableStdin(Stdin)
 | |
| 	}
 | |
| 
 | |
| 	c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin)
 | |
| 
 | |
| 	if c.Stdout == nil {
 | |
| 		c.Stdout = Stdout
 | |
| 	}
 | |
| 	if c.Stderr == nil {
 | |
| 		c.Stderr = Stderr
 | |
| 	}
 | |
| 	if c.HistoryLimit == 0 {
 | |
| 		c.HistoryLimit = 500
 | |
| 	}
 | |
| 
 | |
| 	if c.InterruptPrompt == "" {
 | |
| 		c.InterruptPrompt = "^C"
 | |
| 	} else if c.InterruptPrompt == "\n" {
 | |
| 		c.InterruptPrompt = ""
 | |
| 	}
 | |
| 	if c.EOFPrompt == "" {
 | |
| 		c.EOFPrompt = "^D"
 | |
| 	} else if c.EOFPrompt == "\n" {
 | |
| 		c.EOFPrompt = ""
 | |
| 	}
 | |
| 
 | |
| 	if c.AutoComplete == nil {
 | |
| 		c.AutoComplete = &TabCompleter{}
 | |
| 	}
 | |
| 	if c.FuncGetWidth == nil {
 | |
| 		c.FuncGetWidth = GetScreenWidth
 | |
| 	}
 | |
| 	if c.FuncIsTerminal == nil {
 | |
| 		c.FuncIsTerminal = DefaultIsTerminal
 | |
| 	}
 | |
| 	rm := new(RawMode)
 | |
| 	if c.FuncMakeRaw == nil {
 | |
| 		c.FuncMakeRaw = rm.Enter
 | |
| 	}
 | |
| 	if c.FuncExitRaw == nil {
 | |
| 		c.FuncExitRaw = rm.Exit
 | |
| 	}
 | |
| 	if c.FuncOnWidthChanged == nil {
 | |
| 		c.FuncOnWidthChanged = DefaultOnWidthChanged
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c Config) Clone() *Config {
 | |
| 	c.opHistory = nil
 | |
| 	c.opSearch = nil
 | |
| 	return &c
 | |
| }
 | |
| 
 | |
| func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {
 | |
| 	c.Listener = FuncListener(f)
 | |
| }
 | |
| 
 | |
| func (c *Config) SetPainter(p Painter) {
 | |
| 	c.Painter = p
 | |
| }
 | |
| 
 | |
| func NewEx(cfg *Config) (*Instance, error) {
 | |
| 	t, err := NewTerminal(cfg)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	rl := t.Readline()
 | |
| 	if cfg.Painter == nil {
 | |
| 		cfg.Painter = &defaultPainter{}
 | |
| 	}
 | |
| 	return &Instance{
 | |
| 		Config:    cfg,
 | |
| 		Terminal:  t,
 | |
| 		Operation: rl,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func New(prompt string) (*Instance, error) {
 | |
| 	return NewEx(&Config{Prompt: prompt})
 | |
| }
 | |
| 
 | |
| func (i *Instance) ResetHistory() {
 | |
| 	i.Operation.ResetHistory()
 | |
| }
 | |
| 
 | |
| func (i *Instance) SetPrompt(s string) {
 | |
| 	i.Operation.SetPrompt(s)
 | |
| }
 | |
| 
 | |
| func (i *Instance) SetMaskRune(r rune) {
 | |
| 	i.Operation.SetMaskRune(r)
 | |
| }
 | |
| 
 | |
| // change history persistence in runtime
 | |
| func (i *Instance) SetHistoryPath(p string) {
 | |
| 	i.Operation.SetHistoryPath(p)
 | |
| }
 | |
| 
 | |
| // readline will refresh automatic when write through Stdout()
 | |
| func (i *Instance) Stdout() io.Writer {
 | |
| 	return i.Operation.Stdout()
 | |
| }
 | |
| 
 | |
| // readline will refresh automatic when write through Stdout()
 | |
| func (i *Instance) Stderr() io.Writer {
 | |
| 	return i.Operation.Stderr()
 | |
| }
 | |
| 
 | |
| // switch VimMode in runtime
 | |
| func (i *Instance) SetVimMode(on bool) {
 | |
| 	i.Operation.SetVimMode(on)
 | |
| }
 | |
| 
 | |
| func (i *Instance) IsVimMode() bool {
 | |
| 	return i.Operation.IsEnableVimMode()
 | |
| }
 | |
| 
 | |
| func (i *Instance) GenPasswordConfig() *Config {
 | |
| 	return i.Operation.GenPasswordConfig()
 | |
| }
 | |
| 
 | |
| // we can generate a config by `i.GenPasswordConfig()`
 | |
| func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
 | |
| 	return i.Operation.PasswordWithConfig(cfg)
 | |
| }
 | |
| 
 | |
| func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {
 | |
| 	return i.Operation.PasswordEx(prompt, l)
 | |
| }
 | |
| 
 | |
| func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
 | |
| 	return i.Operation.Password(prompt)
 | |
| }
 | |
| 
 | |
| type Result struct {
 | |
| 	Line  string
 | |
| 	Error error
 | |
| }
 | |
| 
 | |
| func (l *Result) CanContinue() bool {
 | |
| 	return len(l.Line) != 0 && l.Error == ErrInterrupt
 | |
| }
 | |
| 
 | |
| func (l *Result) CanBreak() bool {
 | |
| 	return !l.CanContinue() && l.Error != nil
 | |
| }
 | |
| 
 | |
| func (i *Instance) Line() *Result {
 | |
| 	ret, err := i.Readline()
 | |
| 	return &Result{ret, err}
 | |
| }
 | |
| 
 | |
| // err is one of (nil, io.EOF, readline.ErrInterrupt)
 | |
| func (i *Instance) Readline() (string, error) {
 | |
| 	return i.Operation.String()
 | |
| }
 | |
| 
 | |
| func (i *Instance) ReadlineWithDefault(what string) (string, error) {
 | |
| 	i.Operation.SetBuffer(what)
 | |
| 	return i.Operation.String()
 | |
| }
 | |
| 
 | |
| func (i *Instance) SaveHistory(content string) error {
 | |
| 	return i.Operation.SaveHistory(content)
 | |
| }
 | |
| 
 | |
| // same as readline
 | |
| func (i *Instance) ReadSlice() ([]byte, error) {
 | |
| 	return i.Operation.Slice()
 | |
| }
 | |
| 
 | |
| // we must make sure that call Close() before process exit.
 | |
| func (i *Instance) Close() error {
 | |
| 	if err := i.Terminal.Close(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	i.Config.Stdin.Close()
 | |
| 	i.Operation.Close()
 | |
| 	return nil
 | |
| }
 | |
| func (i *Instance) Clean() {
 | |
| 	i.Operation.Clean()
 | |
| }
 | |
| 
 | |
| func (i *Instance) Write(b []byte) (int, error) {
 | |
| 	return i.Stdout().Write(b)
 | |
| }
 | |
| 
 | |
| // WriteStdin prefill the next Stdin fetch
 | |
| // Next time you call ReadLine() this value will be writen before the user input
 | |
| // ie :
 | |
| //  i := readline.New()
 | |
| //  i.WriteStdin([]byte("test"))
 | |
| //  _, _= i.Readline()
 | |
| //
 | |
| // gives
 | |
| //
 | |
| // > test[cursor]
 | |
| func (i *Instance) WriteStdin(val []byte) (int, error) {
 | |
| 	return i.Terminal.WriteStdin(val)
 | |
| }
 | |
| 
 | |
| func (i *Instance) SetConfig(cfg *Config) *Config {
 | |
| 	if i.Config == cfg {
 | |
| 		return cfg
 | |
| 	}
 | |
| 	old := i.Config
 | |
| 	i.Config = cfg
 | |
| 	i.Operation.SetConfig(cfg)
 | |
| 	i.Terminal.SetConfig(cfg)
 | |
| 	return old
 | |
| }
 | |
| 
 | |
| func (i *Instance) Refresh() {
 | |
| 	i.Operation.Refresh()
 | |
| }
 | |
| 
 | |
| // HistoryDisable the save of the commands into the history
 | |
| func (i *Instance) HistoryDisable() {
 | |
| 	i.Operation.history.Disable()
 | |
| }
 | |
| 
 | |
| // HistoryEnable the save of the commands into the history (default on)
 | |
| func (i *Instance) HistoryEnable() {
 | |
| 	i.Operation.history.Enable()
 | |
| }
 |