summaryrefslogtreecommitdiff
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 000000000..61d44b759
--- /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
+}