瀏覽代碼

Implement #IP1

Alois Mahdal 1 周之前
父節點
當前提交
f5cdcd40c5
共有 1 個文件被更改,包括 34 次插入44 次删除
  1. 34
    44
      app/main.go

+ 34
- 44
app/main.go 查看文件

5
 	"fmt"
5
 	"fmt"
6
 	"io"
6
 	"io"
7
 	"os"
7
 	"os"
8
+	"os/exec"
8
 	"strings"
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
 type Context struct {
14
 type Context struct {
21
 	stdout io.Writer
15
 	stdout io.Writer
22
 	stderr io.Writer
16
 	stderr io.Writer
23
 	stdin  io.Reader
17
 	stdin  io.Reader
24
-	paths  []string
25
 }
18
 }
26
 
19
 
27
 func makeContext() Context {
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
 	return Context{
21
 	return Context{
40
 		stdout: os.Stdout,
22
 		stdout: os.Stdout,
41
 		stderr: os.Stderr,
23
 		stderr: os.Stderr,
42
 		stdin:  os.Stdin,
24
 		stdin:  os.Stdin,
43
-		paths:  paths,
44
 	}
25
 	}
45
 }
26
 }
46
 
27
 
103
 	if err != nil {
84
 	if err != nil {
104
 		return makeCommandNone(), err
85
 		return makeCommandNone(), err
105
 	}
86
 	}
106
-	command, err := parseCommand(command_line, ctx.paths)
87
+	command, err := parseCommand(command_line)
107
 	// fmt.Printf("getCommand():command=%#v\n", command)
88
 	// fmt.Printf("getCommand():command=%#v\n", command)
108
 	if err != nil {
89
 	if err != nil {
109
 		fmt.Fprintln(ctx.stderr, err)
90
 		fmt.Fprintln(ctx.stderr, err)
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
 	tokens := tokenize(command_line)
121
 	tokens := tokenize(command_line)
141
 	if len(tokens) == 0 {
122
 	if len(tokens) == 0 {
142
 		return makeCommandNone(), nil
123
 		return makeCommandNone(), nil
150
 	if tokens[0] == "type" {
131
 	if tokens[0] == "type" {
151
 		return makeCommandBuiltin(CommandTypeBuiltinType, tokens), nil
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
 	// fmt.Printf("handleCommand():command=%#v\n", command)
142
 	// fmt.Printf("handleCommand():command=%#v\n", command)
173
 	switch command.command_type {
143
 	switch command.command_type {
174
 	case CommandTypeNone:
144
 	case CommandTypeNone:
175
-		return
145
+		return 0
176
 	case CommandTypeBuiltinExit:
146
 	case CommandTypeBuiltinExit:
177
 		os.Exit(0)
147
 		os.Exit(0)
178
 	case CommandTypeBuiltinEcho:
148
 	case CommandTypeBuiltinEcho:
180
 	case CommandTypeBuiltinType:
150
 	case CommandTypeBuiltinType:
181
 		if len(command.tokens) != 2 {
151
 		if len(command.tokens) != 2 {
182
 			fmt.Fprintln(ctx.stderr, "usage: type COMMAND")
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
 		if err != nil {
156
 		if err != nil {
187
 			fmt.Fprintln(ctx.stderr, err)
157
 			fmt.Fprintln(ctx.stderr, err)
188
-			return
158
+			return 0
189
 		}
159
 		}
190
 		switch parsed.command_type {
160
 		switch parsed.command_type {
191
 		case CommandTypeNone:
161
 		case CommandTypeNone:
195
 		default:
165
 		default:
196
 			fmt.Fprintf(ctx.stdout, "%s is a shell builtin\n", parsed.tokens[0])
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
 func main() {
191
 func main() {
206
 			fmt.Fprintln(os.Stderr, "Error reading input: ", err)
196
 			fmt.Fprintln(os.Stderr, "Error reading input: ", err)
207
 			os.Exit(1)
197
 			os.Exit(1)
208
 		}
198
 		}
209
-		handleCommand(ctx, command)
199
+		_ = handleCommand(ctx, command)
210
 	}
200
 	}
211
 }
201
 }