github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/gosh/gosh.go (about)

     1  // Copyright 2021 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  // Derived work from Daniel Martí <mvdan@mvdan.cc>
     6  
     7  //go:build !plan9
     8  // +build !plan9
     9  
    10  package main
    11  
    12  import (
    13  	"context"
    14  	"flag"
    15  	"fmt"
    16  	"io"
    17  	"os"
    18  	"strings"
    19  
    20  	"github.com/u-root/prompt"
    21  	"github.com/u-root/prompt/completer"
    22  	"golang.org/x/term"
    23  
    24  	"mvdan.cc/sh/v3/interp"
    25  	"mvdan.cc/sh/v3/syntax"
    26  )
    27  
    28  type input interface {
    29  	Input(prefix string, completer prompt.Completer, opts ...prompt.Option) string
    30  }
    31  
    32  type inputPrompt struct{}
    33  
    34  func (i inputPrompt) Input(prefix string, completer prompt.Completer, opts ...prompt.Option) string {
    35  	return prompt.Input(prefix, completer, opts...)
    36  }
    37  
    38  type shell struct {
    39  	input
    40  }
    41  
    42  func main() {
    43  	flag.Parse()
    44  
    45  	sh := shell{
    46  		input: inputPrompt{},
    47  	}
    48  
    49  	err := sh.runAll(flag.NArg())
    50  
    51  	if e, ok := interp.IsExitStatus(err); ok {
    52  		os.Exit(int(e))
    53  	}
    54  
    55  	if err != nil {
    56  		fmt.Fprintln(os.Stderr, err)
    57  		os.Exit(1)
    58  	}
    59  }
    60  
    61  func (s shell) runAll(narg int) error {
    62  	r, err := interp.New(interp.StdIO(os.Stdin, os.Stdout, os.Stderr))
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	if narg > 0 {
    68  		return s.run(r, strings.NewReader(strings.Join(flag.Args(), " ")), "")
    69  	}
    70  
    71  	if narg == 0 {
    72  		if term.IsTerminal(int(os.Stdin.Fd())) {
    73  			return s.runInteractiveTabCompletion(r, os.Stdout)
    74  		}
    75  
    76  		return s.run(r, os.Stdin, "")
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  func (s shell) run(r *interp.Runner, reader io.Reader, name string) error {
    83  	prog, err := syntax.NewParser().Parse(reader, name)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	r.Reset()
    89  
    90  	return r.Run(context.Background(), prog)
    91  }
    92  
    93  func (s shell) runInteractiveTabCompletion(r *interp.Runner, stdout io.Writer) error {
    94  	parser := syntax.NewParser()
    95  
    96  	if s.input == nil {
    97  		s.input = inputPrompt{}
    98  	}
    99  
   100  	for {
   101  		in := s.Input(
   102  			"$ ",
   103  			completerFunc,
   104  			prompt.OptionCompletionWordSeparator(completer.FilePathCompletionSeparator),
   105  		)
   106  
   107  		if in == "exit" {
   108  			break
   109  		}
   110  
   111  		if err := parser.Stmts(strings.NewReader(in), func(stmt *syntax.Stmt) bool {
   112  			if parser.Incomplete() {
   113  				fmt.Fprintf(stdout, "> ")
   114  
   115  				return true
   116  			}
   117  
   118  			_ = r.Run(context.Background(), stmt)
   119  
   120  			return !r.Exited()
   121  		}); err != nil {
   122  			return err
   123  		}
   124  	}
   125  
   126  	return nil
   127  }