github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/pkg/complete/line.go (about) 1 // Copyright 2012-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package complete 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "log" 12 ) 13 14 // LineReader has three things and returns on string. 15 // The three things are an io.Reader, an io.Writer, and a Completer 16 // Bytes are read one at a time, and depending on their value, 17 // are written to the io.Writer. 18 // If the completer returns 0 or 1 answers, a 0 or 1 length string is returned. 19 // If there are two or more answers, they are printed and the line is 20 // printed out again. 21 // Most characters are just echoed. 22 // Special handling: newline or space returns. 23 // tab tries to complete 24 // backspace erases. Since everything is ansi, we assume ansi. 25 // 26 // LineReader is used to implement input for a Completer. 27 // It uses a Completer, io.Reader, io.Writer, and bytes.Buffer. 28 // bytes are read from the reader, processed, held in the 29 // bytes.Buffer and, as a side effect, some information is 30 // written to the io.Writer. 31 type LineReader struct { 32 // Completer for this LineReader 33 C Completer 34 // R is used for input. Most characters are stored in the 35 // Line, while some initiate special processing. 36 R io.Reader 37 // W is used for output, usually for showing completions. 38 W io.Writer 39 // Lines holds incoming data as it is read. 40 Line *bytes.Buffer 41 } 42 43 // NewLineReader returns a LineReader. 44 func NewLineReader(c Completer, r io.Reader, w io.Writer) *LineReader { 45 return &LineReader{C: c, R: r, W: w, Line: bytes.NewBufferString("")} 46 } 47 48 // ReadOne tries to read one choice from l.R, printing out progress to l.W. 49 // In the case of \t it will try to complete given what it has. 50 // It will show the result of trying to complete it. 51 // If there is only one possible completion, it will return the result. 52 // In the case of \r or \n, if there is more than one choice, 53 // it will return the list of choices, preprended with what has been typed so far. 54 func (l *LineReader) ReadOne() ([]string, error) { 55 Debug("LineReader: start with %v", l) 56 for { 57 var b [1]byte 58 n, err := l.R.Read(b[:]) 59 if err != nil { 60 if err == io.EOF { 61 ln := l.Line.String() 62 if ln == "" { 63 return []string{}, nil 64 } 65 return l.C.Complete(ln) 66 } 67 return nil, err 68 } 69 Debug("LineReader.ReadOne: got %s, %v, %v", b, n, err) 70 if n == 0 { 71 continue 72 } 73 switch b[0] { 74 default: 75 Debug("LineReader.Just add it to line and pipe") 76 l.Line.Write(b[:]) 77 l.W.Write(b[:]) 78 case 8, 127: 79 // We may have found a use for the io stuff, we'll see. 80 s := l.Line.String() 81 if len(s) > 0 { 82 s = s[:len(s)-1] 83 l.Line = bytes.NewBufferString(s) 84 l.W.Write(b[:]) 85 } 86 case '\n', '\r': 87 err = ErrEOL 88 fallthrough 89 case ' ': 90 if b[0] == ' ' { 91 l.W.Write(b[:]) 92 } 93 ln := l.Line.String() 94 if ln == "" { 95 return []string{}, err 96 } 97 s, _ := l.C.Complete(ln) 98 // If there is no match or too many matches, 99 // return what is typed so far. 100 Debug("LineReader ln %v, err %v, s %v", ln, err, s) 101 if len(s) != 1 { 102 s = []string{ln} 103 } 104 return s, err 105 case '\t': 106 Debug("LineReader.Try complete with %s", l.Line.String()) 107 s, err := l.C.Complete(l.Line.String()) 108 Debug("LineReader.Complete returns %v, %v", s, err) 109 if err != nil { 110 return nil, err 111 } 112 if len(s) < 2 { 113 Debug("Return is %v", s) 114 return s, nil 115 } 116 if _, err := l.W.Write([]byte(fmt.Sprintf("\n\r%v\n\r%v", s, l.Line.String()))); err != nil { 117 log.Printf("Write %v: %v", s, err) 118 } 119 120 } 121 } 122 }