xref: /xv6-public/sh.c (revision e00baa9f)
117a85657Srtm #include "types.h"
2d7b3b802Skaashoek #include "stat.h"
3d7b3b802Skaashoek #include "user.h"
417a85657Srtm #include "fs.h"
517a85657Srtm #include "fcntl.h"
617a85657Srtm 
78b58e810Skaashoek #define BUFSIZ  512
88b58e810Skaashoek #define MAXARGS  10
98b58e810Skaashoek #define MAXNODE 2
1021a88dd0Skaashoek #define MAXCMD  2
118b58e810Skaashoek 
1221a88dd0Skaashoek // an embarrassingly naive shell
1321a88dd0Skaashoek 
1421a88dd0Skaashoek // some day a real parse tree; for now ad-hoc
1521a88dd0Skaashoek struct ionode {
168b58e810Skaashoek   int token;
178b58e810Skaashoek   char *s;
188b58e810Skaashoek };
198b58e810Skaashoek 
2021a88dd0Skaashoek struct cmd {
218b58e810Skaashoek   char *argv[MAXARGS];
228b58e810Skaashoek   char argv0buf[BUFSIZ];
238b58e810Skaashoek   int argc;
2421a88dd0Skaashoek   int token;
25*e00baa9fSkaashoek   struct ionode iolist[MAXNODE];
26*e00baa9fSkaashoek   struct ionode *io;
2721a88dd0Skaashoek };
2821a88dd0Skaashoek struct cmd cmdlist[MAXCMD];
29*e00baa9fSkaashoek struct cmd *cmd;
308b58e810Skaashoek 
3121a88dd0Skaashoek char buf[BUFSIZ];
327a37578eSrtm int debug = 0;
338b58e810Skaashoek 
348b58e810Skaashoek int parse(char *s);
358b58e810Skaashoek void runcmd(void);
36*e00baa9fSkaashoek int ioredirection(struct ionode *iolist, int nio);
378b58e810Skaashoek int gettoken(char *s, char **token);
388b58e810Skaashoek int _gettoken(char *s, char **p1, char **p2);
3917a85657Srtm 
4017a85657Srtm int
4117a85657Srtm main(void)
4217a85657Srtm {
4317a85657Srtm   while(1){
4443572072Srtm     puts("$ ");
458787cd01Skaashoek     memset(buf, '\0', sizeof(buf));
4617a85657Srtm     gets(buf, sizeof(buf));
478b58e810Skaashoek     if(parse(buf) < 0)
4817a85657Srtm       continue;
498b58e810Skaashoek     runcmd();
5017a85657Srtm   }
5117a85657Srtm }
52d7b3b802Skaashoek 
538b58e810Skaashoek int
548b58e810Skaashoek parse(char *s)
55d7b3b802Skaashoek {
568b58e810Skaashoek   char *t;
5721a88dd0Skaashoek   int c, i;
588b58e810Skaashoek 
598b58e810Skaashoek   gettoken(s, 0);
608b58e810Skaashoek 
61*e00baa9fSkaashoek   cmd = &cmdlist[0];;
6221a88dd0Skaashoek   for(i = 0; i < MAXCMD; i++) {
6321a88dd0Skaashoek     cmdlist[i].argc = 0;
6421a88dd0Skaashoek     cmdlist[i].token = 0;
65*e00baa9fSkaashoek     cmdlist[i].io = cmdlist[i].iolist;
6621a88dd0Skaashoek   }
678b58e810Skaashoek   while(1) {
688b58e810Skaashoek     switch((c = gettoken(0, &t))) {
698b58e810Skaashoek 
708b58e810Skaashoek     case 'w':   // Add an argument
71*e00baa9fSkaashoek       if(cmd->argc >= MAXARGS) {
728b58e810Skaashoek         printf(2, "too many arguments\n");
738b58e810Skaashoek         return -1;
748b58e810Skaashoek       }
75*e00baa9fSkaashoek       cmd->argv[cmd->argc++] = t;
768b58e810Skaashoek       break;
778b58e810Skaashoek 
788b58e810Skaashoek     case '<':   // Input redirection
798b58e810Skaashoek       // Grab the filename from the argument list
808b58e810Skaashoek       if(gettoken(0, &t) != 'w') {
818b58e810Skaashoek         printf(2, "syntax error: < not followed by word\n");
828b58e810Skaashoek         return -1;
838b58e810Skaashoek       }
84*e00baa9fSkaashoek       cmd->io->token = '<';
85*e00baa9fSkaashoek       cmd->io->s = t;
86*e00baa9fSkaashoek       cmd->io++;
878b58e810Skaashoek       break;
888b58e810Skaashoek 
898b58e810Skaashoek     case '>':   // Output redirection
908b58e810Skaashoek       // Grab the filename from the argument list
918b58e810Skaashoek       if(gettoken(0, &t) != 'w') {
928b58e810Skaashoek         printf(2, "syntax error: > not followed by word\n");
938b58e810Skaashoek         return -1;
948b58e810Skaashoek       }
95*e00baa9fSkaashoek       cmd->io->token = '>';
96*e00baa9fSkaashoek       cmd->io->s = t;
97*e00baa9fSkaashoek       cmd->io++;
9821a88dd0Skaashoek       break;
9921a88dd0Skaashoek 
10021a88dd0Skaashoek     case ';':  // command sequence
10121a88dd0Skaashoek     case '|':  // pipe
102*e00baa9fSkaashoek       cmd->token = c;
103*e00baa9fSkaashoek       cmd++;
1048b58e810Skaashoek       break;
1058b58e810Skaashoek 
1068b58e810Skaashoek     case 0:             // String is complete
1078b58e810Skaashoek       return 0;
1088b58e810Skaashoek 
1098b58e810Skaashoek     default:
1108b58e810Skaashoek       printf(2, "syntax error: bad return %d from gettoken", c);
1118b58e810Skaashoek       return -1;
1128b58e810Skaashoek 
1138b58e810Skaashoek     }
1148b58e810Skaashoek   }
1158b58e810Skaashoek }
1168b58e810Skaashoek 
1178b58e810Skaashoek 
1188b58e810Skaashoek void
1198b58e810Skaashoek runcmd(void)
1208b58e810Skaashoek {
121*e00baa9fSkaashoek   int i, r, pid, tfd;
12221a88dd0Skaashoek   int fdarray[2];
123*e00baa9fSkaashoek   struct cmd *c;
124*e00baa9fSkaashoek   struct ionode *io;
1258b58e810Skaashoek 
1268b58e810Skaashoek   // Return immediately if command line was empty.
12721a88dd0Skaashoek   if(cmdlist[0].argc == 0) {
1288b58e810Skaashoek     if(debug)
1298b58e810Skaashoek       printf(2, "EMPTY COMMAND\n");
1308b58e810Skaashoek     return;
1318b58e810Skaashoek   }
1328b58e810Skaashoek 
133*e00baa9fSkaashoek   for(c = &cmdlist[0]; c <= cmd; c++) {
1348b58e810Skaashoek     // Clean up command line.
1358b58e810Skaashoek     // Read all commands from the filesystem: add an initial '/' to
1368b58e810Skaashoek     // the command name.
1378b58e810Skaashoek     // This essentially acts like 'PATH=/'.
138*e00baa9fSkaashoek     if(c->argv[0][0] != '/') {
139*e00baa9fSkaashoek       c->argv0buf[0] = '/';
140*e00baa9fSkaashoek       strcpy(c->argv0buf + 1, c->argv[0]);
141*e00baa9fSkaashoek       c->argv[0] = c->argv0buf;
1428b58e810Skaashoek     }
143*e00baa9fSkaashoek     c->argv[c->argc] = 0;
1448b58e810Skaashoek 
1458b58e810Skaashoek     // Print the command.
1468b58e810Skaashoek     if(debug) {
1478b58e810Skaashoek       printf(2, "[%d] SPAWN:", getpid());
148*e00baa9fSkaashoek       for(i = 0; c->argv[i]; i++)
149*e00baa9fSkaashoek         printf(2, " %s", c->argv[i]);
150*e00baa9fSkaashoek       for(io = c->iolist; io <= c->io; io++) {
151*e00baa9fSkaashoek         printf(2, "%c %s", io->token, io->s);
1528b58e810Skaashoek       }
1538b58e810Skaashoek       printf(2, "\n");
1548b58e810Skaashoek     }
1558b58e810Skaashoek 
156*e00baa9fSkaashoek     if(strcmp(c->argv[0], "/cd") == 0) {
15748b82470Srsc       if(debug)
158*e00baa9fSkaashoek         printf (2, "/cd %s is build in\n", c->argv[1]);
159*e00baa9fSkaashoek       chdir(c->argv[1]);
1608b58e810Skaashoek       return;
1618b58e810Skaashoek     }
1628b58e810Skaashoek 
163*e00baa9fSkaashoek     if(c->token == '|')
16421a88dd0Skaashoek       if(pipe(fdarray) < 0)
16521a88dd0Skaashoek         printf(2, "cmd %d pipe failed\n", c);
16621a88dd0Skaashoek 
1678b58e810Skaashoek     pid = fork();
1688b58e810Skaashoek     if(pid == 0) {
169*e00baa9fSkaashoek       if(c->token == '|') {
17021a88dd0Skaashoek         if(close(1) < 0)
17121a88dd0Skaashoek           printf(2, "close 1 failed\n");
17221a88dd0Skaashoek         if((tfd = dup(fdarray[1])) < 0)
17321a88dd0Skaashoek           printf(2, "dup failed\n");
17421a88dd0Skaashoek         if(close(fdarray[0]) < 0)
17521a88dd0Skaashoek           printf(2, "close fdarray[0] failed\n");
17621a88dd0Skaashoek         if(close(fdarray[1]) < 0)
17721a88dd0Skaashoek           printf(2, "close fdarray[1] failed\n");
17821a88dd0Skaashoek       }
179*e00baa9fSkaashoek       if(c > cmdlist && (c-1)->token == '|') {
18021a88dd0Skaashoek         if(close(0) < 0)
18121a88dd0Skaashoek           printf(2, "close 0 failed\n");
18221a88dd0Skaashoek         if((tfd = dup(fdarray[0])) < 0)
18321a88dd0Skaashoek           printf(2, "dup failed\n");
18421a88dd0Skaashoek         if(close(fdarray[0]) < 0)
18521a88dd0Skaashoek           printf(2, "close fdarray[0] failed\n");
18621a88dd0Skaashoek         if(close(fdarray[1]) < 0)
18721a88dd0Skaashoek           printf(2, "close fdarray[1] failed\n");
18821a88dd0Skaashoek       }
189*e00baa9fSkaashoek       if(ioredirection(c->iolist, c->io - c->iolist) < 0)
1908b58e810Skaashoek         exit();
191*e00baa9fSkaashoek       if((r = exec(c->argv0buf, (char**) c->argv)) < 0) {
192*e00baa9fSkaashoek         printf(2, "exec %s: %d\n", c->argv[0], r);
193d7b3b802Skaashoek         exit();
194d7b3b802Skaashoek       }
19521a88dd0Skaashoek     } else if(pid > 0) {
19621a88dd0Skaashoek       int p;
1978b58e810Skaashoek       if(debug)
19821a88dd0Skaashoek         printf(2, "[%d] FORKED child %d\n", getpid(), pid);
19921a88dd0Skaashoek 
200*e00baa9fSkaashoek       if(c > cmdlist && (c-1)->token == '|') {
20121a88dd0Skaashoek         close(fdarray[0]);
20221a88dd0Skaashoek         close(fdarray[1]);
20321a88dd0Skaashoek       }
204*e00baa9fSkaashoek       if(c->token != '|') {
20521a88dd0Skaashoek         if(debug)
20621a88dd0Skaashoek           printf(2, "[%d] WAIT for children\n", getpid());
20721a88dd0Skaashoek         do {
20821a88dd0Skaashoek           p = wait();
20921a88dd0Skaashoek           if(debug)
21021a88dd0Skaashoek             printf(2, "[%d] WAIT child %d finished\n", getpid(), p);
21121a88dd0Skaashoek         } while(p > 0);
2128b58e810Skaashoek         if(debug)
2138b58e810Skaashoek           printf(2, "[%d] wait finished\n", getpid());
214d7b3b802Skaashoek       }
215d7b3b802Skaashoek     }
21621a88dd0Skaashoek   }
21721a88dd0Skaashoek }
2188b58e810Skaashoek 
2198b58e810Skaashoek int
220*e00baa9fSkaashoek ioredirection(struct ionode *iolist, int nio)
2218b58e810Skaashoek {
222*e00baa9fSkaashoek   int fd;
223*e00baa9fSkaashoek   struct ionode *io;
2248b58e810Skaashoek 
225*e00baa9fSkaashoek   for(io = iolist; io < &iolist[nio]; io++) {
226*e00baa9fSkaashoek     switch(io->token) {
2278b58e810Skaashoek     case '<':
228d49a2d53Skaashoek       if(close(0) < 0)
229d49a2d53Skaashoek         printf(2, "close 0 failed\n");
230*e00baa9fSkaashoek       if((fd = open(io->s, O_RDONLY)) < 0) {
231*e00baa9fSkaashoek         printf(2, "failed to open %s for read: %d", io->s, fd);
2328b58e810Skaashoek         return -1;
2338b58e810Skaashoek       }
2348b58e810Skaashoek       if(debug)
235*e00baa9fSkaashoek         printf(2, "redirect 0 from %s\n", io->s);
2368b58e810Skaashoek       break;
2378b58e810Skaashoek     case '>':
238d49a2d53Skaashoek       if(close(1) < 0)
239d49a2d53Skaashoek         printf(2, "close 1 failed\n");
240*e00baa9fSkaashoek       if((fd = open(io->s, O_WRONLY|O_CREATE)) < 0) {
241*e00baa9fSkaashoek         printf(2, "failed to open %s for write: %d", io->s, fd);
2428b58e810Skaashoek         exit();
2438b58e810Skaashoek       }
2448b58e810Skaashoek       if(debug)
245*e00baa9fSkaashoek         printf(2, "redirect 1 to %s\n", io->s);
2468b58e810Skaashoek       break;
2478b58e810Skaashoek     }
2488b58e810Skaashoek   }
2498b58e810Skaashoek   return 0;
2508b58e810Skaashoek }
2518b58e810Skaashoek 
2528b58e810Skaashoek // gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
2538b58e810Skaashoek // gettoken(0, token) parses a shell token from the previously set string,
2548b58e810Skaashoek // null-terminates that token, stores the token pointer in '*token',
2558b58e810Skaashoek // and returns a token ID (0, '<', '>', '|', or 'w').
2568b58e810Skaashoek // Subsequent calls to 'gettoken(0, token)' will return subsequent
2578b58e810Skaashoek // tokens from the string.
2588b58e810Skaashoek 
2598b58e810Skaashoek int
2608b58e810Skaashoek gettoken(char *s, char **p1)
2618b58e810Skaashoek {
2628b58e810Skaashoek   static int c, nc;
2638b58e810Skaashoek   static char *np1, *np2;
2648b58e810Skaashoek 
2658b58e810Skaashoek   if(s) {
2668b58e810Skaashoek     nc = _gettoken(s, &np1, &np2);
2678b58e810Skaashoek     return 0;
2688b58e810Skaashoek   }
2698b58e810Skaashoek   c = nc;
2708b58e810Skaashoek   *p1 = np1;
2718b58e810Skaashoek   nc = _gettoken(np2, &np1, &np2);
2728b58e810Skaashoek   return c;
2738b58e810Skaashoek }
2748b58e810Skaashoek 
2758b58e810Skaashoek 
2768b58e810Skaashoek // Get the next token from string s.
2778b58e810Skaashoek // Set *p1 to the beginning of the token and *p2 just past the token.
2788b58e810Skaashoek // Returns
2798b58e810Skaashoek //      0 for end-of-string;
2808b58e810Skaashoek //      < for <;
2818b58e810Skaashoek //      > for >;
2828b58e810Skaashoek //      | for |;
2838b58e810Skaashoek //      w for a word.
2848b58e810Skaashoek //
2858b58e810Skaashoek // Eventually (once we parse the space where the \0 will go),
2868b58e810Skaashoek // words get nul-terminated.
2878b58e810Skaashoek #define WHITESPACE " \t\r\n"
2888b58e810Skaashoek #define SYMBOLS "<|>&;()"
2898b58e810Skaashoek 
2908b58e810Skaashoek int
2918b58e810Skaashoek _gettoken(char *s, char **p1, char **p2)
2928b58e810Skaashoek {
2938b58e810Skaashoek   int t;
2948b58e810Skaashoek 
2958b58e810Skaashoek   if(s == 0) {
2968b58e810Skaashoek     if(debug > 1)
29789ebd895Srsc       printf(2, "GETTOKEN 0\n");
2988b58e810Skaashoek     return 0;
2998b58e810Skaashoek   }
3008b58e810Skaashoek 
3018b58e810Skaashoek   if(debug > 1)
3028b58e810Skaashoek     printf(2, "GETTOKEN: %s\n", s);
3038b58e810Skaashoek 
3048b58e810Skaashoek   *p1 = 0;
3058b58e810Skaashoek   *p2 = 0;
3068b58e810Skaashoek 
3078b58e810Skaashoek   while(strchr(WHITESPACE, *s))
3088b58e810Skaashoek     *s++ = 0;
3098b58e810Skaashoek   if(*s == 0) {
3108b58e810Skaashoek     if(debug > 1)
3118b58e810Skaashoek       printf(2, "EOL\n");
3128b58e810Skaashoek     return 0;
3138b58e810Skaashoek   }
3148b58e810Skaashoek   if(strchr(SYMBOLS, *s)) {
3158b58e810Skaashoek     t = *s;
3168b58e810Skaashoek     *p1 = s;
3178b58e810Skaashoek     *s++ = 0;
3188b58e810Skaashoek     *p2 = s;
3198b58e810Skaashoek     if(debug > 1)
3208b58e810Skaashoek       printf(2, "TOK %c\n", t);
3218b58e810Skaashoek     return t;
3228b58e810Skaashoek   }
3238b58e810Skaashoek   *p1 = s;
3248b58e810Skaashoek   while(*s && !strchr(WHITESPACE SYMBOLS, *s))
3258b58e810Skaashoek     s++;
3268b58e810Skaashoek   *p2 = s;
3278b58e810Skaashoek   if(debug > 1) {
3288b58e810Skaashoek     t = **p2;
3298b58e810Skaashoek     **p2 = 0;
3308b58e810Skaashoek     printf(2, "WORD: %s\n", *p1);
3318b58e810Skaashoek     **p2 = t;
3328b58e810Skaashoek   }
3338b58e810Skaashoek   return 'w';
3348b58e810Skaashoek }
3358b58e810Skaashoek 
336