|
|
@@ -5,42 +5,23 @@ import (
|
|
5
|
5
|
"fmt"
|
|
6
|
6
|
"io"
|
|
7
|
7
|
"os"
|
|
|
8
|
+ "os/exec"
|
|
8
|
9
|
"strings"
|
|
9
|
10
|
)
|
|
10
|
11
|
|
|
11
|
|
-func getenv(key string, fallback string) string {
|
|
12
|
|
- value, ok := os.LookupEnv(key)
|
|
13
|
|
- if ok {
|
|
14
|
|
- return value
|
|
15
|
|
- } else {
|
|
16
|
|
- return fallback
|
|
17
|
|
- }
|
|
18
|
|
-}
|
|
|
12
|
+type ExitStatus uint16
|
|
19
|
13
|
|
|
20
|
14
|
type Context struct {
|
|
21
|
15
|
stdout io.Writer
|
|
22
|
16
|
stderr io.Writer
|
|
23
|
17
|
stdin io.Reader
|
|
24
|
|
- paths []string
|
|
25
|
18
|
}
|
|
26
|
19
|
|
|
27
|
20
|
func makeContext() Context {
|
|
28
|
|
- path_str := getenv("PATH", "")
|
|
29
|
|
- paths := make([]string, 0)
|
|
30
|
|
- for elem := range strings.SplitSeq(path_str, ":") {
|
|
31
|
|
- if elem == "" {
|
|
32
|
|
- continue
|
|
33
|
|
- }
|
|
34
|
|
- if !strings.HasPrefix(elem, "/") {
|
|
35
|
|
- continue
|
|
36
|
|
- }
|
|
37
|
|
- paths = append(paths, elem)
|
|
38
|
|
- }
|
|
39
|
21
|
return Context{
|
|
40
|
22
|
stdout: os.Stdout,
|
|
41
|
23
|
stderr: os.Stderr,
|
|
42
|
24
|
stdin: os.Stdin,
|
|
43
|
|
- paths: paths,
|
|
44
|
25
|
}
|
|
45
|
26
|
}
|
|
46
|
27
|
|
|
|
@@ -103,7 +84,7 @@ func getCommand(ctx *Context) (Command, error) {
|
|
103
|
84
|
if err != nil {
|
|
104
|
85
|
return makeCommandNone(), err
|
|
105
|
86
|
}
|
|
106
|
|
- command, err := parseCommand(command_line, ctx.paths)
|
|
|
87
|
+ command, err := parseCommand(command_line)
|
|
107
|
88
|
// fmt.Printf("getCommand():command=%#v\n", command)
|
|
108
|
89
|
if err != nil {
|
|
109
|
90
|
fmt.Fprintln(ctx.stderr, err)
|
|
|
@@ -136,7 +117,7 @@ func (e ParseCommandError) Error() string {
|
|
136
|
117
|
}
|
|
137
|
118
|
}
|
|
138
|
119
|
|
|
139
|
|
-func parseCommand(command_line string, paths []string) (Command, error) {
|
|
|
120
|
+func parseCommand(command_line string) (Command, error) {
|
|
140
|
121
|
tokens := tokenize(command_line)
|
|
141
|
122
|
if len(tokens) == 0 {
|
|
142
|
123
|
return makeCommandNone(), nil
|
|
|
@@ -150,29 +131,18 @@ func parseCommand(command_line string, paths []string) (Command, error) {
|
|
150
|
131
|
if tokens[0] == "type" {
|
|
151
|
132
|
return makeCommandBuiltin(CommandTypeBuiltinType, tokens), nil
|
|
152
|
133
|
}
|
|
153
|
|
- for _, path := range paths {
|
|
154
|
|
- full_path := fmt.Sprintf("%s/%s", path, tokens[0])
|
|
155
|
|
- // fmt.Printf("parseCommand():full_path=%#v\n", full_path)
|
|
156
|
|
- stat, err := os.Stat(full_path)
|
|
157
|
|
- if err != nil {
|
|
158
|
|
- continue
|
|
159
|
|
- }
|
|
160
|
|
- if stat.IsDir() {
|
|
161
|
|
- continue
|
|
162
|
|
- }
|
|
163
|
|
- if stat.Mode()&0100 == 0 {
|
|
164
|
|
- continue
|
|
165
|
|
- }
|
|
166
|
|
- return makeCommandExternal(tokens, full_path), nil
|
|
|
134
|
+ exec_path, err := exec.LookPath(tokens[0])
|
|
|
135
|
+ if err != nil {
|
|
|
136
|
+ return makeCommandNone(), ParseCommandError{ParseCommandErrorCodeNotFound, tokens[0]}
|
|
167
|
137
|
}
|
|
168
|
|
- return makeCommandNone(), ParseCommandError{ParseCommandErrorCodeNotFound, tokens[0]}
|
|
|
138
|
+ return makeCommandExternal(tokens, exec_path), nil
|
|
169
|
139
|
}
|
|
170
|
140
|
|
|
171
|
|
-func handleCommand(ctx Context, command Command) {
|
|
|
141
|
+func handleCommand(ctx Context, command Command) ExitStatus {
|
|
172
|
142
|
// fmt.Printf("handleCommand():command=%#v\n", command)
|
|
173
|
143
|
switch command.command_type {
|
|
174
|
144
|
case CommandTypeNone:
|
|
175
|
|
- return
|
|
|
145
|
+ return 0
|
|
176
|
146
|
case CommandTypeBuiltinExit:
|
|
177
|
147
|
os.Exit(0)
|
|
178
|
148
|
case CommandTypeBuiltinEcho:
|
|
|
@@ -180,12 +150,12 @@ func handleCommand(ctx Context, command Command) {
|
|
180
|
150
|
case CommandTypeBuiltinType:
|
|
181
|
151
|
if len(command.tokens) != 2 {
|
|
182
|
152
|
fmt.Fprintln(ctx.stderr, "usage: type COMMAND")
|
|
183
|
|
- return
|
|
|
153
|
+ return 2
|
|
184
|
154
|
}
|
|
185
|
|
- parsed, err := parseCommand(command.tokens[1], ctx.paths)
|
|
|
155
|
+ parsed, err := parseCommand(command.tokens[1])
|
|
186
|
156
|
if err != nil {
|
|
187
|
157
|
fmt.Fprintln(ctx.stderr, err)
|
|
188
|
|
- return
|
|
|
158
|
+ return 0
|
|
189
|
159
|
}
|
|
190
|
160
|
switch parsed.command_type {
|
|
191
|
161
|
case CommandTypeNone:
|
|
|
@@ -195,7 +165,27 @@ func handleCommand(ctx Context, command Command) {
|
|
195
|
165
|
default:
|
|
196
|
166
|
fmt.Fprintf(ctx.stdout, "%s is a shell builtin\n", parsed.tokens[0])
|
|
197
|
167
|
}
|
|
|
168
|
+ case CommandTypeExternal:
|
|
|
169
|
+ cmd := exec.Command(command.exec_path, command.tokens[1:]...)
|
|
|
170
|
+ cmd.Args = command.tokens
|
|
|
171
|
+ cmd.Stdout = ctx.stdout
|
|
|
172
|
+ cmd.Stderr = ctx.stderr
|
|
|
173
|
+ cmd.Stdin = ctx.stdin
|
|
|
174
|
+ err := cmd.Run()
|
|
|
175
|
+ if err != nil {
|
|
|
176
|
+ if exiterr, ok := err.(*exec.ExitError); ok {
|
|
|
177
|
+ return ExitStatus(exiterr.ExitCode())
|
|
|
178
|
+ // fmt.Printf("handleCommand():exiterr=%#v\n", exiterr.ExitCode())
|
|
|
179
|
+ } else {
|
|
|
180
|
+ fmt.Printf("handleCommand():err=%#v\n", err)
|
|
|
181
|
+ return ExitStatus(65535)
|
|
|
182
|
+ }
|
|
|
183
|
+ }
|
|
|
184
|
+ return ExitStatus(0)
|
|
|
185
|
+ default:
|
|
|
186
|
+ panic("unknown command type")
|
|
198
|
187
|
}
|
|
|
188
|
+ return ExitStatus(65535)
|
|
199
|
189
|
}
|
|
200
|
190
|
|
|
201
|
191
|
func main() {
|
|
|
@@ -206,6 +196,6 @@ func main() {
|
|
206
|
196
|
fmt.Fprintln(os.Stderr, "Error reading input: ", err)
|
|
207
|
197
|
os.Exit(1)
|
|
208
|
198
|
}
|
|
209
|
|
- handleCommand(ctx, command)
|
|
|
199
|
+ _ = handleCommand(ctx, command)
|
|
210
|
200
|
}
|
|
211
|
201
|
}
|