comparison uprogs/sh.c @ 0:ed10291ff195

first commit
author mir3636
date Sun, 06 Jan 2019 19:27:03 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:ed10291ff195
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 }