github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/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/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  }