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
16struct cmd {
17 int type;
18};
19
20struct execcmd {
21 int type;
22 char *argv[MAXARGS];
23 char *eargv[MAXARGS];
24};
25
26struct redircmd {
27 int type;
28 struct cmd *cmd;
29 char *file;
30 char *efile;
31 int mode;
32 int fd;
33};
34
35struct pipecmd {
36 int type;
37 struct cmd *left;
38 struct cmd *right;
39};
40
41struct listcmd {
42 int type;
43 struct cmd *left;
44 struct cmd *right;
45};
46
47struct backcmd {
48 int type;
49 struct cmd *cmd;
50};
51
52int fork1(void); // Fork but panics on failure.
53void panic(char*);
54struct cmd *parsecmd(char*);
55
56// Execute cmd. Never returns.
57void
58runcmd(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
133int
134getcmd(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
144int
145main(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
174void
175panic(char *s)
176{
177 printf(2, "%s\n", s);
178 exit();
179}
180
181int
182fork1(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
195struct cmd*
196execcmd(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
206struct cmd*
207redircmd(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
222struct cmd*
223pipecmd(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
235struct cmd*
236listcmd(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
248struct cmd*
249backcmd(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
262char whitespace[] = " \t\r\n\v";
263char symbols[] = "<|>&;()";
264
265int
266gettoken(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
310int
311peek(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
322struct cmd *parseline(char**, char*);
323struct cmd *parsepipe(char**, char*);
324struct cmd *parseexec(char**, char*);
325struct cmd *nulterminate(struct cmd*);
326
327struct cmd*
328parsecmd(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
344struct cmd*
345parseline(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
361struct cmd*
362parsepipe(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
374struct cmd*
375parseredirs(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
399struct cmd*
400parseblock(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
415struct cmd*
416parseexec(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.
449struct cmd*
450nulterminate(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