1 | // Shell. |
2 | |
3 | #include "types.h" |
4 | #include "user.h" |
5 | #include "fcntl.h" |
6 | |
7 | // Parsed command representation |
8 | #define EXEC 1 |
9 | #define REDIR 2 |
10 | #define PIPE 3 |
11 | #define LIST 4 |
12 | #define BACK 5 |
13 | |
14 | #define MAXARGS 10 |
15 | |
16 | struct cmd { |
17 | int type; |
18 | }; |
19 | |
20 | struct execcmd { |
21 | int type; |
22 | char *argv[MAXARGS]; |
23 | char *eargv[MAXARGS]; |
24 | }; |
25 | |
26 | struct redircmd { |
27 | int type; |
28 | struct cmd *cmd; |
29 | char *file; |
30 | char *efile; |
31 | int mode; |
32 | int fd; |
33 | }; |
34 | |
35 | struct pipecmd { |
36 | int type; |
37 | struct cmd *left; |
38 | struct cmd *right; |
39 | }; |
40 | |
41 | struct listcmd { |
42 | int type; |
43 | struct cmd *left; |
44 | struct cmd *right; |
45 | }; |
46 | |
47 | struct backcmd { |
48 | int type; |
49 | struct cmd *cmd; |
50 | }; |
51 | |
52 | int fork1(void); // Fork but panics on failure. |
53 | void panic(char*); |
54 | struct cmd *parsecmd(char*); |
55 | |
56 | // Execute cmd. Never returns. |
57 | void |
58 | runcmd(struct cmd *cmd) |
59 | { |
60 | int p[2]; |
61 | struct backcmd *bcmd; |
62 | struct execcmd *ecmd; |
63 | struct listcmd *lcmd; |
64 | struct pipecmd *pcmd; |
65 | struct redircmd *rcmd; |
66 | |
67 | if(cmd == 0) |
68 | exit(); |
69 | |
70 | switch(cmd->type){ |
71 | default: |
72 | panic("runcmd" ); |
73 | |
74 | case EXEC: |
75 | ecmd = (struct execcmd*)cmd; |
76 | if(ecmd->argv[0] == 0) |
77 | exit(); |
78 | exec(ecmd->argv[0], ecmd->argv); |
79 | printf(2, "exec %s failed\n" , ecmd->argv[0]); |
80 | break; |
81 | |
82 | case REDIR: |
83 | rcmd = (struct redircmd*)cmd; |
84 | close(rcmd->fd); |
85 | if(open(rcmd->file, rcmd->mode) < 0){ |
86 | printf(2, "open %s failed\n" , rcmd->file); |
87 | exit(); |
88 | } |
89 | runcmd(rcmd->cmd); |
90 | break; |
91 | |
92 | case LIST: |
93 | lcmd = (struct listcmd*)cmd; |
94 | if(fork1() == 0) |
95 | runcmd(lcmd->left); |
96 | wait(); |
97 | runcmd(lcmd->right); |
98 | break; |
99 | |
100 | case PIPE: |
101 | pcmd = (struct pipecmd*)cmd; |
102 | if(pipe(p) < 0) |
103 | panic("pipe" ); |
104 | if(fork1() == 0){ |
105 | close(1); |
106 | dup(p[1]); |
107 | close(p[0]); |
108 | close(p[1]); |
109 | runcmd(pcmd->left); |
110 | } |
111 | if(fork1() == 0){ |
112 | close(0); |
113 | dup(p[0]); |
114 | close(p[0]); |
115 | close(p[1]); |
116 | runcmd(pcmd->right); |
117 | } |
118 | close(p[0]); |
119 | close(p[1]); |
120 | wait(); |
121 | wait(); |
122 | break; |
123 | |
124 | case BACK: |
125 | bcmd = (struct backcmd*)cmd; |
126 | if(fork1() == 0) |
127 | runcmd(bcmd->cmd); |
128 | break; |
129 | } |
130 | exit(); |
131 | } |
132 | |
133 | int |
134 | getcmd(char *buf, int nbuf) |
135 | { |
136 | printf(2, "$ " ); |
137 | memset(buf, 0, nbuf); |
138 | gets(buf, nbuf); |
139 | if(buf[0] == 0) // EOF |
140 | return -1; |
141 | return 0; |
142 | } |
143 | |
144 | int |
145 | main(void) |
146 | { |
147 | static char buf[100]; |
148 | int fd; |
149 | |
150 | // Ensure that three file descriptors are open. |
151 | while((fd = open("console" , O_RDWR)) >= 0){ |
152 | if(fd >= 3){ |
153 | close(fd); |
154 | break; |
155 | } |
156 | } |
157 | |
158 | // Read and run input commands. |
159 | while(getcmd(buf, sizeof(buf)) >= 0){ |
160 | if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ |
161 | // Chdir must be called by the parent, not the child. |
162 | buf[strlen(buf)-1] = 0; // chop \n |
163 | if(chdir(buf+3) < 0) |
164 | printf(2, "cannot cd %s\n" , buf+3); |
165 | continue; |
166 | } |
167 | if(fork1() == 0) |
168 | runcmd(parsecmd(buf)); |
169 | wait(); |
170 | } |
171 | exit(); |
172 | } |
173 | |
174 | void |
175 | panic(char *s) |
176 | { |
177 | printf(2, "%s\n" , s); |
178 | exit(); |
179 | } |
180 | |
181 | int |
182 | fork1(void) |
183 | { |
184 | int pid; |
185 | |
186 | pid = fork(); |
187 | if(pid == -1) |
188 | panic("fork" ); |
189 | return pid; |
190 | } |
191 | |
192 | //PAGEBREAK! |
193 | // Constructors |
194 | |
195 | struct cmd* |
196 | execcmd(void) |
197 | { |
198 | struct execcmd *cmd; |
199 | |
200 | cmd = malloc(sizeof(*cmd)); |
201 | memset(cmd, 0, sizeof(*cmd)); |
202 | cmd->type = EXEC; |
203 | return (struct cmd*)cmd; |
204 | } |
205 | |
206 | struct cmd* |
207 | redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) |
208 | { |
209 | struct redircmd *cmd; |
210 | |
211 | cmd = malloc(sizeof(*cmd)); |
212 | memset(cmd, 0, sizeof(*cmd)); |
213 | cmd->type = REDIR; |
214 | cmd->cmd = subcmd; |
215 | cmd->file = file; |
216 | cmd->efile = efile; |
217 | cmd->mode = mode; |
218 | cmd->fd = fd; |
219 | return (struct cmd*)cmd; |
220 | } |
221 | |
222 | struct cmd* |
223 | pipecmd(struct cmd *left, struct cmd *right) |
224 | { |
225 | struct pipecmd *cmd; |
226 | |
227 | cmd = malloc(sizeof(*cmd)); |
228 | memset(cmd, 0, sizeof(*cmd)); |
229 | cmd->type = PIPE; |
230 | cmd->left = left; |
231 | cmd->right = right; |
232 | return (struct cmd*)cmd; |
233 | } |
234 | |
235 | struct cmd* |
236 | listcmd(struct cmd *left, struct cmd *right) |
237 | { |
238 | struct listcmd *cmd; |
239 | |
240 | cmd = malloc(sizeof(*cmd)); |
241 | memset(cmd, 0, sizeof(*cmd)); |
242 | cmd->type = LIST; |
243 | cmd->left = left; |
244 | cmd->right = right; |
245 | return (struct cmd*)cmd; |
246 | } |
247 | |
248 | struct cmd* |
249 | backcmd(struct cmd *subcmd) |
250 | { |
251 | struct backcmd *cmd; |
252 | |
253 | cmd = malloc(sizeof(*cmd)); |
254 | memset(cmd, 0, sizeof(*cmd)); |
255 | cmd->type = BACK; |
256 | cmd->cmd = subcmd; |
257 | return (struct cmd*)cmd; |
258 | } |
259 | //PAGEBREAK! |
260 | // Parsing |
261 | |
262 | char whitespace[] = " \t\r\n\v" ; |
263 | char symbols[] = "<|>&;()" ; |
264 | |
265 | int |
266 | gettoken(char **ps, char *es, char **q, char **eq) |
267 | { |
268 | char *s; |
269 | int ret; |
270 | |
271 | s = *ps; |
272 | while(s < es && strchr(whitespace, *s)) |
273 | s++; |
274 | if(q) |
275 | *q = s; |
276 | ret = *s; |
277 | switch(*s){ |
278 | case 0: |
279 | break; |
280 | case '|': |
281 | case '(': |
282 | case ')': |
283 | case ';': |
284 | case '&': |
285 | case '<': |
286 | s++; |
287 | break; |
288 | case '>': |
289 | s++; |
290 | if(*s == '>'){ |
291 | ret = '+'; |
292 | s++; |
293 | } |
294 | break; |
295 | default: |
296 | ret = 'a'; |
297 | while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) |
298 | s++; |
299 | break; |
300 | } |
301 | if(eq) |
302 | *eq = s; |
303 | |
304 | while(s < es && strchr(whitespace, *s)) |
305 | s++; |
306 | *ps = s; |
307 | return ret; |
308 | } |
309 | |
310 | int |
311 | peek(char **ps, char *es, char *toks) |
312 | { |
313 | char *s; |
314 | |
315 | s = *ps; |
316 | while(s < es && strchr(whitespace, *s)) |
317 | s++; |
318 | *ps = s; |
319 | return *s && strchr(toks, *s); |
320 | } |
321 | |
322 | struct cmd *parseline(char**, char*); |
323 | struct cmd *parsepipe(char**, char*); |
324 | struct cmd *parseexec(char**, char*); |
325 | struct cmd *nulterminate(struct cmd*); |
326 | |
327 | struct cmd* |
328 | parsecmd(char *s) |
329 | { |
330 | char *es; |
331 | struct cmd *cmd; |
332 | |
333 | es = s + strlen(s); |
334 | cmd = parseline(&s, es); |
335 | peek(&s, es, "" ); |
336 | if(s != es){ |
337 | printf(2, "leftovers: %s\n" , s); |
338 | panic("syntax" ); |
339 | } |
340 | nulterminate(cmd); |
341 | return cmd; |
342 | } |
343 | |
344 | struct cmd* |
345 | parseline(char **ps, char *es) |
346 | { |
347 | struct cmd *cmd; |
348 | |
349 | cmd = parsepipe(ps, es); |
350 | while(peek(ps, es, "&" )){ |
351 | gettoken(ps, es, 0, 0); |
352 | cmd = backcmd(cmd); |
353 | } |
354 | if(peek(ps, es, ";" )){ |
355 | gettoken(ps, es, 0, 0); |
356 | cmd = listcmd(cmd, parseline(ps, es)); |
357 | } |
358 | return cmd; |
359 | } |
360 | |
361 | struct cmd* |
362 | parsepipe(char **ps, char *es) |
363 | { |
364 | struct cmd *cmd; |
365 | |
366 | cmd = parseexec(ps, es); |
367 | if(peek(ps, es, "|" )){ |
368 | gettoken(ps, es, 0, 0); |
369 | cmd = pipecmd(cmd, parsepipe(ps, es)); |
370 | } |
371 | return cmd; |
372 | } |
373 | |
374 | struct cmd* |
375 | parseredirs(struct cmd *cmd, char **ps, char *es) |
376 | { |
377 | int tok; |
378 | char *q, *eq; |
379 | |
380 | while(peek(ps, es, "<>" )){ |
381 | tok = gettoken(ps, es, 0, 0); |
382 | if(gettoken(ps, es, &q, &eq) != 'a') |
383 | panic("missing file for redirection" ); |
384 | switch(tok){ |
385 | case '<': |
386 | cmd = redircmd(cmd, q, eq, O_RDONLY, 0); |
387 | break; |
388 | case '>': |
389 | cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); |
390 | break; |
391 | case '+': // >> |
392 | cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); |
393 | break; |
394 | } |
395 | } |
396 | return cmd; |
397 | } |
398 | |
399 | struct cmd* |
400 | parseblock(char **ps, char *es) |
401 | { |
402 | struct cmd *cmd; |
403 | |
404 | if(!peek(ps, es, "(" )) |
405 | panic("parseblock" ); |
406 | gettoken(ps, es, 0, 0); |
407 | cmd = parseline(ps, es); |
408 | if(!peek(ps, es, ")" )) |
409 | panic("syntax - missing )" ); |
410 | gettoken(ps, es, 0, 0); |
411 | cmd = parseredirs(cmd, ps, es); |
412 | return cmd; |
413 | } |
414 | |
415 | struct cmd* |
416 | parseexec(char **ps, char *es) |
417 | { |
418 | char *q, *eq; |
419 | int tok, argc; |
420 | struct execcmd *cmd; |
421 | struct cmd *ret; |
422 | |
423 | if(peek(ps, es, "(" )) |
424 | return parseblock(ps, es); |
425 | |
426 | ret = execcmd(); |
427 | cmd = (struct execcmd*)ret; |
428 | |
429 | argc = 0; |
430 | ret = parseredirs(ret, ps, es); |
431 | while(!peek(ps, es, "|)&;" )){ |
432 | if((tok=gettoken(ps, es, &q, &eq)) == 0) |
433 | break; |
434 | if(tok != 'a') |
435 | panic("syntax" ); |
436 | cmd->argv[argc] = q; |
437 | cmd->eargv[argc] = eq; |
438 | argc++; |
439 | if(argc >= MAXARGS) |
440 | panic("too many args" ); |
441 | ret = parseredirs(ret, ps, es); |
442 | } |
443 | cmd->argv[argc] = 0; |
444 | cmd->eargv[argc] = 0; |
445 | return ret; |
446 | } |
447 | |
448 | // NUL-terminate all the counted strings. |
449 | struct cmd* |
450 | nulterminate(struct cmd *cmd) |
451 | { |
452 | int i; |
453 | struct backcmd *bcmd; |
454 | struct execcmd *ecmd; |
455 | struct listcmd *lcmd; |
456 | struct pipecmd *pcmd; |
457 | struct redircmd *rcmd; |
458 | |
459 | if(cmd == 0) |
460 | return 0; |
461 | |
462 | switch(cmd->type){ |
463 | case EXEC: |
464 | ecmd = (struct execcmd*)cmd; |
465 | for(i=0; ecmd->argv[i]; i++) |
466 | *ecmd->eargv[i] = 0; |
467 | break; |
468 | |
469 | case REDIR: |
470 | rcmd = (struct redircmd*)cmd; |
471 | nulterminate(rcmd->cmd); |
472 | *rcmd->efile = 0; |
473 | break; |
474 | |
475 | case PIPE: |
476 | pcmd = (struct pipecmd*)cmd; |
477 | nulterminate(pcmd->left); |
478 | nulterminate(pcmd->right); |
479 | break; |
480 | |
481 | case LIST: |
482 | lcmd = (struct listcmd*)cmd; |
483 | nulterminate(lcmd->left); |
484 | nulterminate(lcmd->right); |
485 | break; |
486 | |
487 | case BACK: |
488 | bcmd = (struct backcmd*)cmd; |
489 | nulterminate(bcmd->cmd); |
490 | break; |
491 | } |
492 | return cmd; |
493 | } |
494 | |