0
|
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 // Assumes three file descriptors 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 // Clumsy but will have to do for now.
|
|
162 // Chdir has no effect on the parent if run in the child.
|
|
163 buf[strlen(buf)-1] = 0; // chop \n
|
|
164 if(chdir(buf+3) < 0)
|
|
165 printf(2, "cannot cd %s\n", buf+3);
|
|
166 continue;
|
|
167 }
|
|
168 if(fork1() == 0)
|
|
169 runcmd(parsecmd(buf));
|
|
170 wait();
|
|
171 }
|
|
172 exit();
|
|
173 }
|
|
174
|
|
175 void
|
|
176 panic(char *s)
|
|
177 {
|
|
178 printf(2, "%s\n", s);
|
|
179 exit();
|
|
180 }
|
|
181
|
|
182 int
|
|
183 fork1(void)
|
|
184 {
|
|
185 int pid;
|
|
186
|
|
187 pid = fork();
|
|
188 if(pid == -1)
|
|
189 panic("fork");
|
|
190 return pid;
|
|
191 }
|
|
192
|
|
193 //PAGEBREAK!
|
|
194 // Constructors
|
|
195
|
|
196 struct cmd*
|
|
197 execcmd(void)
|
|
198 {
|
|
199 struct execcmd *cmd;
|
|
200
|
|
201 cmd = malloc(sizeof(*cmd));
|
|
202 memset(cmd, 0, sizeof(*cmd));
|
|
203 cmd->type = EXEC;
|
|
204 return (struct cmd*)cmd;
|
|
205 }
|
|
206
|
|
207 struct cmd*
|
|
208 redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
|
|
209 {
|
|
210 struct redircmd *cmd;
|
|
211
|
|
212 cmd = malloc(sizeof(*cmd));
|
|
213 memset(cmd, 0, sizeof(*cmd));
|
|
214 cmd->type = REDIR;
|
|
215 cmd->cmd = subcmd;
|
|
216 cmd->file = file;
|
|
217 cmd->efile = efile;
|
|
218 cmd->mode = mode;
|
|
219 cmd->fd = fd;
|
|
220 return (struct cmd*)cmd;
|
|
221 }
|
|
222
|
|
223 struct cmd*
|
|
224 pipecmd(struct cmd *left, struct cmd *right)
|
|
225 {
|
|
226 struct pipecmd *cmd;
|
|
227
|
|
228 cmd = malloc(sizeof(*cmd));
|
|
229 memset(cmd, 0, sizeof(*cmd));
|
|
230 cmd->type = PIPE;
|
|
231 cmd->left = left;
|
|
232 cmd->right = right;
|
|
233 return (struct cmd*)cmd;
|
|
234 }
|
|
235
|
|
236 struct cmd*
|
|
237 listcmd(struct cmd *left, struct cmd *right)
|
|
238 {
|
|
239 struct listcmd *cmd;
|
|
240
|
|
241 cmd = malloc(sizeof(*cmd));
|
|
242 memset(cmd, 0, sizeof(*cmd));
|
|
243 cmd->type = LIST;
|
|
244 cmd->left = left;
|
|
245 cmd->right = right;
|
|
246 return (struct cmd*)cmd;
|
|
247 }
|
|
248
|
|
249 struct cmd*
|
|
250 backcmd(struct cmd *subcmd)
|
|
251 {
|
|
252 struct backcmd *cmd;
|
|
253
|
|
254 cmd = malloc(sizeof(*cmd));
|
|
255 memset(cmd, 0, sizeof(*cmd));
|
|
256 cmd->type = BACK;
|
|
257 cmd->cmd = subcmd;
|
|
258 return (struct cmd*)cmd;
|
|
259 }
|
|
260 //PAGEBREAK!
|
|
261 // Parsing
|
|
262
|
|
263 char whitespace[] = " \t\r\n\v";
|
|
264 char symbols[] = "<|>&;()";
|
|
265
|
|
266 int
|
|
267 gettoken(char **ps, char *es, char **q, char **eq)
|
|
268 {
|
|
269 char *s;
|
|
270 int ret;
|
|
271
|
|
272 s = *ps;
|
|
273 while(s < es && strchr(whitespace, *s))
|
|
274 s++;
|
|
275 if(q)
|
|
276 *q = s;
|
|
277 ret = *s;
|
|
278 switch(*s){
|
|
279 case 0:
|
|
280 break;
|
|
281 case '|':
|
|
282 case '(':
|
|
283 case ')':
|
|
284 case ';':
|
|
285 case '&':
|
|
286 case '<':
|
|
287 s++;
|
|
288 break;
|
|
289 case '>':
|
|
290 s++;
|
|
291 if(*s == '>'){
|
|
292 ret = '+';
|
|
293 s++;
|
|
294 }
|
|
295 break;
|
|
296 default:
|
|
297 ret = 'a';
|
|
298 while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
|
|
299 s++;
|
|
300 break;
|
|
301 }
|
|
302 if(eq)
|
|
303 *eq = s;
|
|
304
|
|
305 while(s < es && strchr(whitespace, *s))
|
|
306 s++;
|
|
307 *ps = s;
|
|
308 return ret;
|
|
309 }
|
|
310
|
|
311 int
|
|
312 peek(char **ps, char *es, char *toks)
|
|
313 {
|
|
314 char *s;
|
|
315
|
|
316 s = *ps;
|
|
317 while(s < es && strchr(whitespace, *s))
|
|
318 s++;
|
|
319 *ps = s;
|
|
320 return *s && strchr(toks, *s);
|
|
321 }
|
|
322
|
|
323 struct cmd *parseline(char**, char*);
|
|
324 struct cmd *parsepipe(char**, char*);
|
|
325 struct cmd *parseexec(char**, char*);
|
|
326 struct cmd *nulterminate(struct cmd*);
|
|
327
|
|
328 struct cmd*
|
|
329 parsecmd(char *s)
|
|
330 {
|
|
331 char *es;
|
|
332 struct cmd *cmd;
|
|
333
|
|
334 es = s + strlen(s);
|
|
335 cmd = parseline(&s, es);
|
|
336 peek(&s, es, "");
|
|
337 if(s != es){
|
|
338 printf(2, "leftovers: %s\n", s);
|
|
339 panic("syntax");
|
|
340 }
|
|
341 nulterminate(cmd);
|
|
342 return cmd;
|
|
343 }
|
|
344
|
|
345 struct cmd*
|
|
346 parseline(char **ps, char *es)
|
|
347 {
|
|
348 struct cmd *cmd;
|
|
349
|
|
350 cmd = parsepipe(ps, es);
|
|
351 while(peek(ps, es, "&")){
|
|
352 gettoken(ps, es, 0, 0);
|
|
353 cmd = backcmd(cmd);
|
|
354 }
|
|
355 if(peek(ps, es, ";")){
|
|
356 gettoken(ps, es, 0, 0);
|
|
357 cmd = listcmd(cmd, parseline(ps, es));
|
|
358 }
|
|
359 return cmd;
|
|
360 }
|
|
361
|
|
362 struct cmd*
|
|
363 parsepipe(char **ps, char *es)
|
|
364 {
|
|
365 struct cmd *cmd;
|
|
366
|
|
367 cmd = parseexec(ps, es);
|
|
368 if(peek(ps, es, "|")){
|
|
369 gettoken(ps, es, 0, 0);
|
|
370 cmd = pipecmd(cmd, parsepipe(ps, es));
|
|
371 }
|
|
372 return cmd;
|
|
373 }
|
|
374
|
|
375 struct cmd*
|
|
376 parseredirs(struct cmd *cmd, char **ps, char *es)
|
|
377 {
|
|
378 int tok;
|
|
379 char *q, *eq;
|
|
380
|
|
381 while(peek(ps, es, "<>")){
|
|
382 tok = gettoken(ps, es, 0, 0);
|
|
383 if(gettoken(ps, es, &q, &eq) != 'a')
|
|
384 panic("missing file for redirection");
|
|
385 switch(tok){
|
|
386 case '<':
|
|
387 cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
|
|
388 break;
|
|
389 case '>':
|
|
390 cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
|
391 break;
|
|
392 case '+': // >>
|
|
393 cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
|
394 break;
|
|
395 }
|
|
396 }
|
|
397 return cmd;
|
|
398 }
|
|
399
|
|
400 struct cmd*
|
|
401 parseblock(char **ps, char *es)
|
|
402 {
|
|
403 struct cmd *cmd;
|
|
404
|
|
405 if(!peek(ps, es, "("))
|
|
406 panic("parseblock");
|
|
407 gettoken(ps, es, 0, 0);
|
|
408 cmd = parseline(ps, es);
|
|
409 if(!peek(ps, es, ")"))
|
|
410 panic("syntax - missing )");
|
|
411 gettoken(ps, es, 0, 0);
|
|
412 cmd = parseredirs(cmd, ps, es);
|
|
413 return cmd;
|
|
414 }
|
|
415
|
|
416 struct cmd*
|
|
417 parseexec(char **ps, char *es)
|
|
418 {
|
|
419 char *q, *eq;
|
|
420 int tok, argc;
|
|
421 struct execcmd *cmd;
|
|
422 struct cmd *ret;
|
|
423
|
|
424 if(peek(ps, es, "("))
|
|
425 return parseblock(ps, es);
|
|
426
|
|
427 ret = execcmd();
|
|
428 cmd = (struct execcmd*)ret;
|
|
429
|
|
430 argc = 0;
|
|
431 ret = parseredirs(ret, ps, es);
|
|
432 while(!peek(ps, es, "|)&;")){
|
|
433 if((tok=gettoken(ps, es, &q, &eq)) == 0)
|
|
434 break;
|
|
435 if(tok != 'a')
|
|
436 panic("syntax");
|
|
437 cmd->argv[argc] = q;
|
|
438 cmd->eargv[argc] = eq;
|
|
439 argc++;
|
|
440 if(argc >= MAXARGS)
|
|
441 panic("too many args");
|
|
442 ret = parseredirs(ret, ps, es);
|
|
443 }
|
|
444 cmd->argv[argc] = 0;
|
|
445 cmd->eargv[argc] = 0;
|
|
446 return ret;
|
|
447 }
|
|
448
|
|
449 // NUL-terminate all the counted strings.
|
|
450 struct cmd*
|
|
451 nulterminate(struct cmd *cmd)
|
|
452 {
|
|
453 int i;
|
|
454 struct backcmd *bcmd;
|
|
455 struct execcmd *ecmd;
|
|
456 struct listcmd *lcmd;
|
|
457 struct pipecmd *pcmd;
|
|
458 struct redircmd *rcmd;
|
|
459
|
|
460 if(cmd == 0)
|
|
461 return 0;
|
|
462
|
|
463 switch(cmd->type){
|
|
464 case EXEC:
|
|
465 ecmd = (struct execcmd*)cmd;
|
|
466 for(i=0; ecmd->argv[i]; i++)
|
|
467 *ecmd->eargv[i] = 0;
|
|
468 break;
|
|
469
|
|
470 case REDIR:
|
|
471 rcmd = (struct redircmd*)cmd;
|
|
472 nulterminate(rcmd->cmd);
|
|
473 *rcmd->efile = 0;
|
|
474 break;
|
|
475
|
|
476 case PIPE:
|
|
477 pcmd = (struct pipecmd*)cmd;
|
|
478 nulterminate(pcmd->left);
|
|
479 nulterminate(pcmd->right);
|
|
480 break;
|
|
481
|
|
482 case LIST:
|
|
483 lcmd = (struct listcmd*)cmd;
|
|
484 nulterminate(lcmd->left);
|
|
485 nulterminate(lcmd->right);
|
|
486 break;
|
|
487
|
|
488 case BACK:
|
|
489 bcmd = (struct backcmd*)cmd;
|
|
490 nulterminate(bcmd->cmd);
|
|
491 break;
|
|
492 }
|
|
493 return cmd;
|
|
494 }
|