github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/pogosh/shell.go (about) 1 // Copyright 2020 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 pogosh implements a small POSIX-compatible shell. 6 package pogosh 7 8 import ( 9 "fmt" 10 "io/ioutil" 11 ) 12 13 // Run executes the given fragment of shell. 14 // 15 // There are three circumstances for this function to return: 16 // 17 // 1. There is a shell error (compiler error, file not found, ...) 18 // 2. The script calls the exit builtin. 19 // 3. The script reaches the end of input. 20 // 21 // In the case the shell script execs another process, this function will not 22 // return (unless the Exec function is appropriately overriden). 23 func (s *State) Run(script string) (exitCode int, err error) { 24 err = isASCII(script) 25 if err != nil { 26 return 0, err 27 } 28 29 // Lex 30 tokens, err := tokenize(script) 31 if err != nil { 32 return 0, err 33 } 34 35 // Parse 36 eof := token{script[len(script):], ttEOF} 37 // TODO: append newline instead of EOF, or make EOF the only empty value "" 38 tokens = append(tokens, eof) // augment 39 t := tokenizer{tokens} 40 cmd := parseProgram(s, &t) 41 if t.ts[0].ttype != ttEOF { 42 panic("expected EOF") // TODO: better error message 43 } 44 45 // Exit code 46 defer func() { 47 switch r := recover().(type) { 48 case nil: 49 case exitError: 50 exitCode = r.code 51 case error: 52 exitCode = 1 53 err = r 54 default: 55 panic(r) // TODO: clobbers stack trace 56 } 57 }() 58 59 // Execute 60 cmd.exec(s) 61 62 return 0, err 63 } 64 65 // RunFile is a convenient wrapper around Run. 66 func (s *State) RunFile(filename string) (int, error) { 67 script, err := ioutil.ReadFile(filename) 68 if err != nil { 69 return 0, err 70 } 71 return s.Run(string(script)) 72 } 73 74 func isASCII(str string) error { 75 lineNumber := 0 76 lineStart := 0 77 for i, v := range []byte(str) { 78 if v == '\n' { 79 lineNumber++ 80 lineStart = i + 1 81 } else if v > 127 { 82 // TODO: include filename if possible 83 return fmt.Errorf("<pogosh>:%v:%v: non-ascii character, '\\x%x'", lineNumber+1, i-lineStart+1, v) 84 } 85 } 86 return nil 87 }