xref: /xv6-public/sh.c (revision 7894fcd2)
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
runcmd(struct cmd * cmd)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
getcmd(char * buf,int nbuf)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
main(void)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
panic(char * s)175 panic(char *s)
176 {
177   printf(2, "%s\n", s);
178   exit();
179 }
180 
181 int
fork1(void)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*
execcmd(void)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*
redircmd(struct cmd * subcmd,char * file,char * efile,int mode,int fd)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*
pipecmd(struct cmd * left,struct cmd * right)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*
listcmd(struct cmd * left,struct cmd * right)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*
backcmd(struct cmd * subcmd)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
gettoken(char ** ps,char * es,char ** q,char ** eq)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
peek(char ** ps,char * es,char * toks)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*
parsecmd(char * s)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*
parseline(char ** ps,char * es)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*
parsepipe(char ** ps,char * es)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*
parseredirs(struct cmd * cmd,char ** ps,char * es)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*
parseblock(char ** ps,char * es)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*
parseexec(char ** ps,char * es)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*
nulterminate(struct cmd * 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