summary refs log tree commit diff
path: root/vendor/github.com/chzyer/readline/std.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/chzyer/readline/std.go')
-rw-r--r--vendor/github.com/chzyer/readline/std.go197
1 files changed, 197 insertions, 0 deletions
diff --git a/vendor/github.com/chzyer/readline/std.go b/vendor/github.com/chzyer/readline/std.go
new file mode 100644
index 0000000..61d44b7
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/std.go
@@ -0,0 +1,197 @@
+package readline
+
+import (
+	"io"
+	"os"
+	"sync"
+	"sync/atomic"
+)
+
+var (
+	Stdin  io.ReadCloser  = os.Stdin
+	Stdout io.WriteCloser = os.Stdout
+	Stderr io.WriteCloser = os.Stderr
+)
+
+var (
+	std     *Instance
+	stdOnce sync.Once
+)
+
+// global instance will not submit history automatic
+func getInstance() *Instance {
+	stdOnce.Do(func() {
+		std, _ = NewEx(&Config{
+			DisableAutoSaveHistory: true,
+		})
+	})
+	return std
+}
+
+// let readline load history from filepath
+// and try to persist history into disk
+// set fp to "" to prevent readline persisting history to disk
+// so the `AddHistory` will return nil error forever.
+func SetHistoryPath(fp string) {
+	ins := getInstance()
+	cfg := ins.Config.Clone()
+	cfg.HistoryFile = fp
+	ins.SetConfig(cfg)
+}
+
+// set auto completer to global instance
+func SetAutoComplete(completer AutoCompleter) {
+	ins := getInstance()
+	cfg := ins.Config.Clone()
+	cfg.AutoComplete = completer
+	ins.SetConfig(cfg)
+}
+
+// add history to global instance manually
+// raise error only if `SetHistoryPath` is set with a non-empty path
+func AddHistory(content string) error {
+	ins := getInstance()
+	return ins.SaveHistory(content)
+}
+
+func Password(prompt string) ([]byte, error) {
+	ins := getInstance()
+	return ins.ReadPassword(prompt)
+}
+
+// readline with global configs
+func Line(prompt string) (string, error) {
+	ins := getInstance()
+	ins.SetPrompt(prompt)
+	return ins.Readline()
+}
+
+type CancelableStdin struct {
+	r      io.Reader
+	mutex  sync.Mutex
+	stop   chan struct{}
+	closed int32
+	notify chan struct{}
+	data   []byte
+	read   int
+	err    error
+}
+
+func NewCancelableStdin(r io.Reader) *CancelableStdin {
+	c := &CancelableStdin{
+		r:      r,
+		notify: make(chan struct{}),
+		stop:   make(chan struct{}),
+	}
+	go c.ioloop()
+	return c
+}
+
+func (c *CancelableStdin) ioloop() {
+loop:
+	for {
+		select {
+		case <-c.notify:
+			c.read, c.err = c.r.Read(c.data)
+			select {
+			case c.notify <- struct{}{}:
+			case <-c.stop:
+				break loop
+			}
+		case <-c.stop:
+			break loop
+		}
+	}
+}
+
+func (c *CancelableStdin) Read(b []byte) (n int, err error) {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+	if atomic.LoadInt32(&c.closed) == 1 {
+		return 0, io.EOF
+	}
+
+	c.data = b
+	select {
+	case c.notify <- struct{}{}:
+	case <-c.stop:
+		return 0, io.EOF
+	}
+	select {
+	case <-c.notify:
+		return c.read, c.err
+	case <-c.stop:
+		return 0, io.EOF
+	}
+}
+
+func (c *CancelableStdin) Close() error {
+	if atomic.CompareAndSwapInt32(&c.closed, 0, 1) {
+		close(c.stop)
+	}
+	return nil
+}
+
+// FillableStdin is a stdin reader which can prepend some data before
+// reading into the real stdin
+type FillableStdin struct {
+	sync.Mutex
+	stdin       io.Reader
+	stdinBuffer io.ReadCloser
+	buf         []byte
+	bufErr      error
+}
+
+// NewFillableStdin gives you FillableStdin
+func NewFillableStdin(stdin io.Reader) (io.ReadCloser, io.Writer) {
+	r, w := io.Pipe()
+	s := &FillableStdin{
+		stdinBuffer: r,
+		stdin:       stdin,
+	}
+	s.ioloop()
+	return s, w
+}
+
+func (s *FillableStdin) ioloop() {
+	go func() {
+		for {
+			bufR := make([]byte, 100)
+			var n int
+			n, s.bufErr = s.stdinBuffer.Read(bufR)
+			if s.bufErr != nil {
+				if s.bufErr == io.ErrClosedPipe {
+					break
+				}
+			}
+			s.Lock()
+			s.buf = append(s.buf, bufR[:n]...)
+			s.Unlock()
+		}
+	}()
+}
+
+// Read will read from the local buffer and if no data, read from stdin
+func (s *FillableStdin) Read(p []byte) (n int, err error) {
+	s.Lock()
+	i := len(s.buf)
+	if len(p) < i {
+		i = len(p)
+	}
+	if i > 0 {
+		n := copy(p, s.buf)
+		s.buf = s.buf[:0]
+		cerr := s.bufErr
+		s.bufErr = nil
+		s.Unlock()
+		return n, cerr
+	}
+	s.Unlock()
+	n, err = s.stdin.Read(p)
+	return n, err
+}
+
+func (s *FillableStdin) Close() error {
+	s.stdinBuffer.Close()
+	return nil
+}