github.com/cilki/sh@v2.6.4+incompatible/cmd/gosh/main.go (about) 1 // Copyright (c) 2017, Daniel Martà <mvdan@mvdan.cc> 2 // See LICENSE for licensing information 3 4 package main 5 6 import ( 7 "context" 8 "flag" 9 "fmt" 10 "io" 11 "os" 12 "strings" 13 14 "golang.org/x/crypto/ssh/terminal" 15 16 "mvdan.cc/sh/interp" 17 "mvdan.cc/sh/syntax" 18 ) 19 20 var ( 21 command = flag.String("c", "", "command to be executed") 22 23 parser = syntax.NewParser() 24 25 mainRunner, _ = interp.New(interp.StdIO(os.Stdin, os.Stdout, os.Stderr)) 26 ) 27 28 func main() { 29 flag.Parse() 30 if err := runAll(); err != nil { 31 fmt.Fprintln(os.Stderr, err) 32 os.Exit(1) 33 } 34 } 35 36 func runAll() error { 37 if *command != "" { 38 return run(strings.NewReader(*command), "") 39 } 40 if flag.NArg() == 0 { 41 if terminal.IsTerminal(int(os.Stdin.Fd())) { 42 return interactive(mainRunner) 43 } 44 return run(os.Stdin, "") 45 } 46 for _, path := range flag.Args() { 47 if err := runPath(path); err != nil { 48 return err 49 } 50 } 51 return nil 52 } 53 54 func runPath(path string) error { 55 f, err := os.Open(path) 56 if err != nil { 57 return err 58 } 59 defer f.Close() 60 return run(f, path) 61 } 62 63 func run(reader io.Reader, name string) error { 64 prog, err := parser.Parse(reader, name) 65 if err != nil { 66 return err 67 } 68 mainRunner.Reset() 69 ctx := context.Background() 70 return mainRunner.Run(ctx, prog) 71 } 72 73 func interactive(runner *interp.Runner) error { 74 fmt.Fprintf(runner.Stdout, "$ ") 75 fn := func(stmts []*syntax.Stmt) bool { 76 if parser.Incomplete() { 77 fmt.Fprintf(runner.Stdout, "> ") 78 return true 79 } 80 ctx := context.Background() 81 for _, stmt := range stmts { 82 if err := runner.Run(ctx, stmt); err != nil { 83 switch x := err.(type) { 84 case interp.ShellExitStatus: 85 os.Exit(int(x)) 86 case interp.ExitStatus: 87 default: 88 fmt.Fprintln(runner.Stderr, err) 89 os.Exit(1) 90 } 91 } 92 } 93 fmt.Fprintf(runner.Stdout, "$ ") 94 return true 95 } 96 return parser.Interactive(runner.Stdin, fn) 97 }