github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/exp/rush/rush.go (about) 1 // Copyright 2012-2017 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 // Rush is an interactive shell similar to sh. 6 // 7 // Description: 8 // 9 // Prompt is '% '. 10 package main 11 12 import ( 13 "bufio" 14 "fmt" 15 "io" 16 "os" 17 "os/exec" 18 "path/filepath" 19 ) 20 21 type builtin func(c *Command) error 22 23 // TODO: probably have one builtin map and use it for both types? 24 var ( 25 urpath = "/go/bin:/ubin:/buildbin:/bbin:/bin:/usr/local/bin:" 26 27 builtins map[string]builtin 28 ) 29 30 func addBuiltIn(name string, f builtin) error { 31 if builtins == nil { 32 builtins = make(map[string]builtin) 33 } 34 if _, ok := builtins[name]; ok { 35 return fmt.Errorf("%v already a builtin", name) 36 } 37 builtins[name] = f 38 return nil 39 } 40 41 func wire(cmds []*Command) error { 42 for i, c := range cmds { 43 // IO defaults. 44 var err error 45 if c.Stdin == nil { 46 if c.Stdin, err = openRead(c, os.Stdin, 0); err != nil { 47 return err 48 } 49 } 50 if c.Link != "|" { 51 if c.Stdout, err = openWrite(c, os.Stdout, 1); err != nil { 52 return err 53 } 54 } 55 if c.Stderr, err = openWrite(c, os.Stderr, 2); err != nil { 56 return err 57 } 58 // The validation is such that "|" is not set on the last one. 59 // Also, there won't be redirects and "|" inappropriately. 60 if c.Link != "|" { 61 continue 62 } 63 w, err := cmds[i+1].StdinPipe() 64 if err != nil { 65 return err 66 } 67 r, err := cmds[i].StdoutPipe() 68 if err != nil { 69 return err 70 } 71 // Oh, yuck. 72 // There seems to be no way to do the classic 73 // inherited pipes thing in Go. Hard to believe. 74 go func() { 75 io.Copy(w, r) 76 w.Close() 77 }() 78 } 79 return nil 80 } 81 82 func runit(c *Command) error { 83 defer func() { 84 for fd, f := range c.files { 85 f.Close() 86 delete(c.files, fd) 87 } 88 }() 89 if b, ok := builtins[c.cmd]; ok { 90 if err := b(c); err != nil { 91 return err 92 } 93 } else { 94 95 forkAttr(c) 96 if err := c.Start(); err != nil { 97 return fmt.Errorf("%v: Path %v", err, os.Getenv("PATH")) 98 } 99 if err := c.Wait(); err != nil { 100 return fmt.Errorf("wait: %v", err) 101 } 102 } 103 return nil 104 } 105 106 func openRead(c *Command, r io.Reader, fd int) (io.Reader, error) { 107 if c.fdmap[fd] != "" { 108 f, err := os.Open(c.fdmap[fd]) 109 c.files[fd] = f 110 return f, err 111 } 112 return r, nil 113 } 114 115 func openWrite(c *Command, w io.Writer, fd int) (io.Writer, error) { 116 if c.fdmap[fd] != "" { 117 f, err := os.OpenFile(c.fdmap[fd], os.O_CREATE|os.O_WRONLY, 0o666) 118 c.files[fd] = f 119 return f, err 120 } 121 return w, nil 122 } 123 124 func doArgs(cmds []*Command) { 125 for _, c := range cmds { 126 globargv := []string{} 127 for _, v := range c.Args { 128 if v.mod == "ENV" { 129 globargv = append(globargv, os.Getenv(v.val)) 130 } else if globs, err := filepath.Glob(v.val); err == nil && len(globs) > 0 { 131 globargv = append(globargv, globs...) 132 } else { 133 globargv = append(globargv, v.val) 134 } 135 } 136 137 c.cmd = globargv[0] 138 c.argv = globargv[1:] 139 } 140 } 141 142 // There seems to be no harm in creating a Cmd struct 143 // even for builtins, so for now, we do. 144 // It will, however, do a path lookup, which we really don't need, 145 // and we may change it later. 146 func commands(cmds []*Command) error { 147 for _, c := range cmds { 148 c.Cmd = exec.Command(c.cmd, c.argv[:]...) 149 // this is a Very Special Case related to a Go issue. 150 // we're not able to unshare correctly in builtin. 151 // Not sure of the issue but this hack will have to do until 152 // we understand it. Barf. 153 if c.cmd == "builtin" { 154 builtinAttr(c) 155 } 156 } 157 return nil 158 } 159 160 func command(c *Command) error { 161 // for now, bg will just happen in background. 162 if c.BG { 163 go func() { 164 if err := runit(c); err != nil { 165 fmt.Fprintf(os.Stderr, "%v", err) 166 } 167 }() 168 } 169 return runit(c) 170 } 171 172 func main() { 173 if len(os.Args) != 1 { 174 fmt.Println("no scripts/args yet") 175 os.Exit(1) 176 } 177 178 b := bufio.NewReader(os.Stdin) 179 tty() 180 fmt.Printf("%% ") 181 for { 182 foreground() 183 cmds, status, err := getCommand(b) 184 if err != nil { 185 fmt.Fprintf(os.Stderr, "%v\n", err) 186 } 187 doArgs(cmds) 188 if err := commands(cmds); err != nil { 189 fmt.Fprintf(os.Stderr, "%v\n", err) 190 continue 191 } 192 if err := wire(cmds); err != nil { 193 fmt.Fprintf(os.Stderr, "%v\n", err) 194 continue 195 } 196 for _, c := range cmds { 197 if err := command(c); err != nil { 198 fmt.Fprintf(os.Stderr, "%v\n", err) 199 if c.Link == "||" { 200 continue 201 } 202 // yes, not needed, but useful so you know 203 // what goes on here. 204 if c.Link == "&&" { 205 break 206 } 207 break 208 } else { 209 if c.Link == "||" { 210 break 211 } 212 } 213 } 214 if status == "EOF" { 215 break 216 } 217 fmt.Printf("%% ") 218 } 219 }