user/sh.c
499 lines
1// Shell.23#include "kernel/types.h"4#include "user/user.h"5#include "kernel/fcntl.h"67// Parsed command representation8#define EXEC 19#define REDIR 210#define PIPE 311#define LIST 412#define BACK 51314#define MAXARGS 101516struct cmd {17 int type;18};1920struct execcmd {21 int type;22 char *argv[MAXARGS];23 char *eargv[MAXARGS];24};2526struct redircmd {27 int type;28 struct cmd *cmd;29 char *file;30 char *efile;31 int mode;32 int fd;33};3435struct pipecmd {36 int type;37 struct cmd *left;38 struct cmd *right;39};4041struct listcmd {42 int type;43 struct cmd *left;44 struct cmd *right;45};4647struct backcmd {48 int type;49 struct cmd *cmd;50};5152int fork1(void); // Fork but panics on failure.53void panic(char*);54struct cmd *parsecmd(char*);55void runcmd(struct cmd*) __attribute__((noreturn));5657// Execute cmd. Never returns.58void59runcmd(struct cmd *cmd)60{61 int p[2];62 struct backcmd *bcmd;63 struct execcmd *ecmd;64 struct listcmd *lcmd;65 struct pipecmd *pcmd;66 struct redircmd *rcmd;6768 if(cmd == 0)69 exit(1);7071 switch(cmd->type){72 default:73 panic("runcmd");7475 case EXEC:76 ecmd = (struct execcmd*)cmd;77 if(ecmd->argv[0] == 0)78 exit(1);79 exec(ecmd->argv[0], ecmd->argv);80 fprintf(2, "exec %s failed\n", ecmd->argv[0]);81 break;8283 case REDIR:84 rcmd = (struct redircmd*)cmd;85 close(rcmd->fd);86 if(open(rcmd->file, rcmd->mode) < 0){87 fprintf(2, "open %s failed\n", rcmd->file);88 exit(1);89 }90 runcmd(rcmd->cmd);91 break;9293 case LIST:94 lcmd = (struct listcmd*)cmd;95 if(fork1() == 0)96 runcmd(lcmd->left);97 wait(0);98 runcmd(lcmd->right);99 break;100101 case PIPE:102 pcmd = (struct pipecmd*)cmd;103 if(pipe(p) < 0)104 panic("pipe");105 if(fork1() == 0){106 close(1);107 dup(p[1]);108 close(p[0]);109 close(p[1]);110 runcmd(pcmd->left);111 }112 if(fork1() == 0){113 close(0);114 dup(p[0]);115 close(p[0]);116 close(p[1]);117 runcmd(pcmd->right);118 }119 close(p[0]);120 close(p[1]);121 wait(0);122 wait(0);123 break;124125 case BACK:126 bcmd = (struct backcmd*)cmd;127 if(fork1() == 0)128 runcmd(bcmd->cmd);129 break;130 }131 exit(0);132}133134int135getcmd(char *buf, int nbuf)136{137 write(2, "$ ", 2);138 memset(buf, 0, nbuf);139 gets(buf, nbuf);140 if(buf[0] == 0) // EOF141 return -1;142 return 0;143}144145int146main(void)147{148 static char buf[100];149 int fd;150151 // Ensure that three file descriptors are open.152 while((fd = open("console", O_RDWR)) >= 0){153 if(fd >= 3){154 close(fd);155 break;156 }157 }158159 // Read and run input commands.160 while(getcmd(buf, sizeof(buf)) >= 0){161 char *cmd = buf;162 while (*cmd == ' ' || *cmd == '\t')163 cmd++;164 if (*cmd == '\n') // is a blank command165 continue;166 if(cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' '){167 // Chdir must be called by the parent, not the child.168 cmd[strlen(cmd)-1] = 0; // chop \n169 if(chdir(cmd+3) < 0)170 fprintf(2, "cannot cd %s\n", cmd+3);171 } else {172 if(fork1() == 0)173 runcmd(parsecmd(cmd));174 wait(0);175 }176 }177 exit(0);178}179180void181panic(char *s)182{183 fprintf(2, "%s\n", s);184 exit(1);185}186187int188fork1(void)189{190 int pid;191192 pid = fork();193 if(pid == -1)194 panic("fork");195 return pid;196}197198//PAGEBREAK!199// Constructors200201struct cmd*202execcmd(void)203{204 struct execcmd *cmd;205206 cmd = malloc(sizeof(*cmd));207 memset(cmd, 0, sizeof(*cmd));208 cmd->type = EXEC;209 return (struct cmd*)cmd;210}211212struct cmd*213redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)214{215 struct redircmd *cmd;216217 cmd = malloc(sizeof(*cmd));218 memset(cmd, 0, sizeof(*cmd));219 cmd->type = REDIR;220 cmd->cmd = subcmd;221 cmd->file = file;222 cmd->efile = efile;223 cmd->mode = mode;224 cmd->fd = fd;225 return (struct cmd*)cmd;226}227228struct cmd*229pipecmd(struct cmd *left, struct cmd *right)230{231 struct pipecmd *cmd;232233 cmd = malloc(sizeof(*cmd));234 memset(cmd, 0, sizeof(*cmd));235 cmd->type = PIPE;236 cmd->left = left;237 cmd->right = right;238 return (struct cmd*)cmd;239}240241struct cmd*242listcmd(struct cmd *left, struct cmd *right)243{244 struct listcmd *cmd;245246 cmd = malloc(sizeof(*cmd));247 memset(cmd, 0, sizeof(*cmd));248 cmd->type = LIST;249 cmd->left = left;250 cmd->right = right;251 return (struct cmd*)cmd;252}253254struct cmd*255backcmd(struct cmd *subcmd)256{257 struct backcmd *cmd;258259 cmd = malloc(sizeof(*cmd));260 memset(cmd, 0, sizeof(*cmd));261 cmd->type = BACK;262 cmd->cmd = subcmd;263 return (struct cmd*)cmd;264}265//PAGEBREAK!266// Parsing267268char whitespace[] = " \t\r\n\v";269char symbols[] = "<|>&;()";270271int272gettoken(char **ps, char *es, char **q, char **eq)273{274 char *s;275 int ret;276277 s = *ps;278 while(s < es && strchr(whitespace, *s))279 s++;280 if(q)281 *q = s;282 ret = *s;283 switch(*s){284 case 0:285 break;286 case '|':287 case '(':288 case ')':289 case ';':290 case '&':291 case '<':292 s++;293 break;294 case '>':295 s++;296 if(*s == '>'){297 ret = '+';298 s++;299 }300 break;301 default:302 ret = 'a';303 while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))304 s++;305 break;306 }307 if(eq)308 *eq = s;309310 while(s < es && strchr(whitespace, *s))311 s++;312 *ps = s;313 return ret;314}315316int317peek(char **ps, char *es, char *toks)318{319 char *s;320321 s = *ps;322 while(s < es && strchr(whitespace, *s))323 s++;324 *ps = s;325 return *s && strchr(toks, *s);326}327328struct cmd *parseline(char**, char*);329struct cmd *parsepipe(char**, char*);330struct cmd *parseexec(char**, char*);331struct cmd *nulterminate(struct cmd*);332333struct cmd*334parsecmd(char *s)335{336 char *es;337 struct cmd *cmd;338339 es = s + strlen(s);340 cmd = parseline(&s, es);341 peek(&s, es, "");342 if(s != es){343 fprintf(2, "leftovers: %s\n", s);344 panic("syntax");345 }346 nulterminate(cmd);347 return cmd;348}349350struct cmd*351parseline(char **ps, char *es)352{353 struct cmd *cmd;354355 cmd = parsepipe(ps, es);356 while(peek(ps, es, "&")){357 gettoken(ps, es, 0, 0);358 cmd = backcmd(cmd);359 }360 if(peek(ps, es, ";")){361 gettoken(ps, es, 0, 0);362 cmd = listcmd(cmd, parseline(ps, es));363 }364 return cmd;365}366367struct cmd*368parsepipe(char **ps, char *es)369{370 struct cmd *cmd;371372 cmd = parseexec(ps, es);373 if(peek(ps, es, "|")){374 gettoken(ps, es, 0, 0);375 cmd = pipecmd(cmd, parsepipe(ps, es));376 }377 return cmd;378}379380struct cmd*381parseredirs(struct cmd *cmd, char **ps, char *es)382{383 int tok;384 char *q, *eq;385386 while(peek(ps, es, "<>")){387 tok = gettoken(ps, es, 0, 0);388 if(gettoken(ps, es, &q, &eq) != 'a')389 panic("missing file for redirection");390 switch(tok){391 case '<':392 cmd = redircmd(cmd, q, eq, O_RDONLY, 0);393 break;394 case '>':395 cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);396 break;397 case '+': // >>398 cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);399 break;400 }401 }402 return cmd;403}404405struct cmd*406parseblock(char **ps, char *es)407{408 struct cmd *cmd;409410 if(!peek(ps, es, "("))411 panic("parseblock");412 gettoken(ps, es, 0, 0);413 cmd = parseline(ps, es);414 if(!peek(ps, es, ")"))415 panic("syntax - missing )");416 gettoken(ps, es, 0, 0);417 cmd = parseredirs(cmd, ps, es);418 return cmd;419}420421struct cmd*422parseexec(char **ps, char *es)423{424 char *q, *eq;425 int tok, argc;426 struct execcmd *cmd;427 struct cmd *ret;428429 if(peek(ps, es, "("))430 return parseblock(ps, es);431432 ret = execcmd();433 cmd = (struct execcmd*)ret;434435 argc = 0;436 ret = parseredirs(ret, ps, es);437 while(!peek(ps, es, "|)&;")){438 if((tok=gettoken(ps, es, &q, &eq)) == 0)439 break;440 if(tok != 'a')441 panic("syntax");442 cmd->argv[argc] = q;443 cmd->eargv[argc] = eq;444 argc++;445 if(argc >= MAXARGS)446 panic("too many args");447 ret = parseredirs(ret, ps, es);448 }449 cmd->argv[argc] = 0;450 cmd->eargv[argc] = 0;451 return ret;452}453454// NUL-terminate all the counted strings.455struct cmd*456nulterminate(struct cmd *cmd)457{458 int i;459 struct backcmd *bcmd;460 struct execcmd *ecmd;461 struct listcmd *lcmd;462 struct pipecmd *pcmd;463 struct redircmd *rcmd;464465 if(cmd == 0)466 return 0;467468 switch(cmd->type){469 case EXEC:470 ecmd = (struct execcmd*)cmd;471 for(i=0; ecmd->argv[i]; i++)472 *ecmd->eargv[i] = 0;473 break;474475 case REDIR:476 rcmd = (struct redircmd*)cmd;477 nulterminate(rcmd->cmd);478 *rcmd->efile = 0;479 break;480481 case PIPE:482 pcmd = (struct pipecmd*)cmd;483 nulterminate(pcmd->left);484 nulterminate(pcmd->right);485 break;486487 case LIST:488 lcmd = (struct listcmd*)cmd;489 nulterminate(lcmd->left);490 nulterminate(lcmd->right);491 break;492493 case BACK:494 bcmd = (struct backcmd*)cmd;495 nulterminate(bcmd->cmd);496 break;497 }498 return cmd;499}500