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 }