github.com/insomniacslk/u-root@v0.0.0-20200717035308-96b791510d76/cmds/core/elvish/edit/tty/rune_reader_unix.go (about) 1 // +build !windows,!plan9 2 3 package tty 4 5 import ( 6 "fmt" 7 "os" 8 "syscall" 9 10 "github.com/u-root/u-root/cmds/core/elvish/sys" 11 ) 12 13 const ( 14 runeReaderChanSize int = 128 15 ) 16 17 // runeReader reads a Unix file continuously, assemble the bytes it reads into 18 // runes (assuming UTF-8), and delivers them on a channel. 19 type runeReader struct { 20 file *os.File 21 rStop *os.File 22 wStop *os.File 23 stopChan chan struct{} 24 runeChan chan rune 25 errorChan chan error 26 debug bool 27 } 28 29 // newRuneReader creates a new runeReader from a file. 30 func newRuneReader(file *os.File) *runeReader { 31 rStop, wStop, err := os.Pipe() 32 if err != nil { 33 panic(err) 34 } 35 return &runeReader{ 36 file, 37 rStop, wStop, 38 make(chan struct{}), 39 make(chan rune, runeReaderChanSize), 40 make(chan error), 41 false, 42 } 43 } 44 45 // Chan returns a channel onto which the runeReader writes the runes it reads. 46 func (ar *runeReader) Chan() <-chan rune { 47 return ar.runeChan 48 } 49 50 // ErrorChan returns a channel onto which the runeReader writes the errors it 51 // encounters. 52 func (ar *runeReader) ErrorChan() <-chan error { 53 return ar.errorChan 54 } 55 56 // Start starts the runeReader. 57 func (ar *runeReader) Start() { 58 go ar.run() 59 } 60 61 // run runs the runeReader. It blocks until Quit is called and should be called 62 // in a separate goroutine. 63 func (ar *runeReader) run() { 64 var buf [1]byte 65 66 for { 67 ready, err := sys.WaitForRead(ar.file, ar.rStop) 68 if err != nil { 69 if err == syscall.EINTR { 70 continue 71 } 72 ar.fatal(err) 73 return 74 } 75 if ready[1] { 76 // Consume the written byte 77 ar.rStop.Read(buf[:]) 78 <-ar.stopChan 79 return 80 } 81 82 nr, err := ar.file.Read(buf[:]) 83 if nr != 1 { 84 continue 85 } else if err != nil { 86 ar.fatal(err) 87 return 88 } 89 90 leader := buf[0] 91 var ( 92 r rune 93 pending int 94 ) 95 switch { 96 case leader>>7 == 0: 97 r = rune(leader) 98 case leader>>5 == 0x6: 99 r = rune(leader & 0x1f) 100 pending = 1 101 case leader>>4 == 0xe: 102 r = rune(leader & 0xf) 103 pending = 2 104 case leader>>3 == 0x1e: 105 r = rune(leader & 0x7) 106 pending = 3 107 } 108 if ar.debug { 109 fmt.Printf("leader 0x%x, pending %d, r = 0x%x\n", leader, pending, r) 110 } 111 for i := 0; i < pending; i++ { 112 nr, err := ar.file.Read(buf[:]) 113 if nr != 1 { 114 r = 0xfffd 115 break 116 } else if err != nil { 117 ar.fatal(err) 118 return 119 } 120 r = r<<6 + rune(buf[0]&0x3f) 121 if ar.debug { 122 fmt.Printf(" got 0x%d, r = 0x%x\n", buf[0], r) 123 } 124 } 125 126 // Write rune to ch, unless termination is requested. 127 select { 128 case ar.runeChan <- r: 129 case <-ar.stopChan: 130 ar.rStop.Read(buf[:]) 131 return 132 } 133 } 134 } 135 136 func (ar *runeReader) fatal(err error) { 137 var cBuf [1]byte 138 139 select { 140 case ar.errorChan <- err: 141 case <-ar.stopChan: 142 ar.rStop.Read(cBuf[:]) 143 return 144 } 145 <-ar.stopChan 146 ar.rStop.Read(cBuf[:]) 147 } 148 149 // Stop terminates the loop of Run. 150 func (ar *runeReader) Stop() { 151 _, err := ar.wStop.Write([]byte{'q'}) 152 if err != nil { 153 panic(err) 154 } 155 ar.stopChan <- struct{}{} 156 } 157 158 // Close releases files and channels associated with the AsyncReader. It does 159 // not close the file used to create it. 160 func (ar *runeReader) Close() { 161 ar.rStop.Close() 162 ar.wStop.Close() 163 close(ar.stopChan) 164 close(ar.runeChan) 165 }