github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/cmds/exp/ash/ash.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 main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"flag"
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"os/exec"
    15  	"strings"
    16  
    17  	"github.com/u-root/u-root/pkg/complete"
    18  	"github.com/u-root/u-root/pkg/termios"
    19  )
    20  
    21  var (
    22  	debug   = flag.Bool("d", false, "enable debug prints")
    23  	userush = flag.Bool("R", false, "Use the rush interpreter for commands")
    24  	v       = func(string, ...interface{}) {}
    25  )
    26  
    27  func verbose(f string, a ...interface{}) {
    28  	v(f+"\r\n", a...)
    29  }
    30  
    31  func output(s chan string, w io.Writer) {
    32  	for l := range s {
    33  		if _, err := w.Write([]byte("\r")); err != nil {
    34  			log.Printf("output write: %v", err)
    35  		}
    36  		for _, b := range l {
    37  			var o string
    38  			switch b {
    39  			default:
    40  				o = string(b)
    41  			case '\b', 127:
    42  				o = "\b \b"
    43  			case '\r', '\n':
    44  				o = "\r\n"
    45  			}
    46  			if _, err := w.Write([]byte(o)); err != nil {
    47  				log.Printf("output write: %v", err)
    48  			}
    49  		}
    50  	}
    51  }
    52  
    53  func main() {
    54  	tty()
    55  	flag.Parse()
    56  	if *debug {
    57  		v = log.Printf
    58  		complete.Debug = verbose
    59  	}
    60  	t, err := termios.New()
    61  	if err != nil {
    62  		log.Fatal(err)
    63  	}
    64  	r, err := t.Raw()
    65  	if err != nil {
    66  		log.Printf("non-fatal cannot get tty: %v", err)
    67  	}
    68  	defer t.Set(r)
    69  	_, cw, err := os.Pipe()
    70  	if err != nil {
    71  		log.Fatal(err)
    72  	}
    73  	p, err := complete.NewPathCompleter()
    74  	if err != nil {
    75  		log.Fatal(err)
    76  	}
    77  	f := complete.NewFileCompleter("/")
    78  	bin := complete.NewMultiCompleter(complete.NewStringCompleter([]string{"exit"}), p)
    79  	rest := f
    80  	l := complete.NewLineReader(bin, t, cw)
    81  	lines := make(chan string)
    82  	go output(lines, os.Stdout)
    83  	var lineComplete bool
    84  	for !l.EOF {
    85  		lineComplete = false
    86  		l.C = bin
    87  		if l.Fields > 1 {
    88  			l.C = rest
    89  		}
    90  		// Read one byte, run it through the completer, then print the string
    91  		// as we have it.
    92  		v("start with %v", l)
    93  		var b [1]byte
    94  		n, err := l.R.Read(b[:])
    95  		if err != nil {
    96  			break
    97  		}
    98  		v("ReadLine: got %s, %v, %v", b, n, err)
    99  
   100  		if err := l.ReadChar(b[0]); err != nil {
   101  			v("ERR -> %v (%v)", l, err)
   102  			if err == io.EOF || err != complete.ErrEOL {
   103  				v("%v", err)
   104  				lines <- l.Line
   105  				continue
   106  			}
   107  			v("set linecomplete")
   108  			lineComplete = true
   109  		}
   110  
   111  		v("back from ReadChar, l is %v", l)
   112  		if l.Line == "exit" {
   113  			break
   114  		}
   115  		if lineComplete && l.Line != "" {
   116  			v("ash: Done reading args: line %q", l.Line)
   117  			// here we go.
   118  			lines <- "\n"
   119  			t.Set(r)
   120  			if !*userush {
   121  				f := strings.Fields(l.Line)
   122  				var args []string
   123  				if l.Exact != "" {
   124  					args = append(args, l.Exact)
   125  				}
   126  				args = append(args, l.Candidates...)
   127  				if len(f) > 1 && len(args) > 1 {
   128  					f = append(f[:len(f)-1], args...)
   129  				}
   130  
   131  				cmd := exec.Command(f[0], f[1:]...)
   132  				cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
   133  
   134  				if err := cmd.Run(); err != nil {
   135  					log.Print(err)
   136  				}
   137  
   138  			} else {
   139  				b := bufio.NewReader(bytes.NewBufferString(l.Line))
   140  				if err := rush(b); err != nil {
   141  					log.Print(err)
   142  				}
   143  			}
   144  			foreground()
   145  			t.Raw()
   146  
   147  			l.Line = ""
   148  			l.Candidates = []string{}
   149  			l.C = bin
   150  			l.Fields = 0
   151  			l.Exact = ""
   152  			continue
   153  		}
   154  		if l.Exact != "" {
   155  			lines <- "\n" + l.Exact
   156  		}
   157  		if len(l.Candidates) > 0 {
   158  			for _, ln := range l.Candidates {
   159  				lines <- "\n" + ln
   160  			}
   161  			lines <- "\n"
   162  		}
   163  		lines <- l.Line
   164  	}
   165  }