xref: /xv6-public/sh.c (revision 1b789e1d)
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
944e6909aSkaashoek #define MAXIO 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;
2544e6909aSkaashoek   struct ionode iolist[MAXIO];
26e00baa9fSkaashoek   struct ionode *io;
2721a88dd0Skaashoek };
2821a88dd0Skaashoek struct cmd cmdlist[MAXCMD];
29e00baa9fSkaashoek struct cmd *cmd;
308b58e810Skaashoek 
3121a88dd0Skaashoek char buf[BUFSIZ];
329736728dSrsc int debug;
338b58e810Skaashoek 
348b58e810Skaashoek int parse(char *s);
358b58e810Skaashoek void runcmd(void);
369736728dSrsc int getcmd(char *buf, int nbuf);
37e00baa9fSkaashoek int ioredirection(struct ionode *iolist, int nio);
388b58e810Skaashoek int gettoken(char *s, char **token);
398b58e810Skaashoek int _gettoken(char *s, char **p1, char **p2);
4017a85657Srtm 
4117a85657Srtm int
4217a85657Srtm main(void)
4317a85657Srtm {
44eaea18cbSrsc   while(getcmd(buf, sizeof(buf)) >= 0) {
459736728dSrsc     if(parse(buf) >= 0)
468b58e810Skaashoek       runcmd();
4717a85657Srtm   }
48f8f7fcbeSrsc   exit();
4917a85657Srtm }
50d7b3b802Skaashoek 
518b58e810Skaashoek int
529736728dSrsc getcmd(char *buf, int nbuf)
539736728dSrsc {
54*1b789e1dSrsc   printf(2, "$ ");
559736728dSrsc   memset(buf, 0, nbuf);
569736728dSrsc   gets(buf, nbuf);
579736728dSrsc   if(buf[0] == 0) // EOF
589736728dSrsc     return -1;
599736728dSrsc   return 0;
609736728dSrsc }
619736728dSrsc 
629736728dSrsc int
638b58e810Skaashoek parse(char *s)
64d7b3b802Skaashoek {
658b58e810Skaashoek   char *t;
6621a88dd0Skaashoek   int c, i;
678b58e810Skaashoek 
688b58e810Skaashoek   gettoken(s, 0);
698b58e810Skaashoek 
709736728dSrsc   cmd = &cmdlist[0];
7121a88dd0Skaashoek   for(i = 0; i < MAXCMD; i++) {
7221a88dd0Skaashoek     cmdlist[i].argc = 0;
7321a88dd0Skaashoek     cmdlist[i].token = 0;
74e00baa9fSkaashoek     cmdlist[i].io = cmdlist[i].iolist;
7521a88dd0Skaashoek   }
769736728dSrsc   for(;;){
778b58e810Skaashoek     switch((c = gettoken(0, &t))) {
788b58e810Skaashoek 
798b58e810Skaashoek     case 'w':   // Add an argument
80e00baa9fSkaashoek       if(cmd->argc >= MAXARGS) {
818b58e810Skaashoek         printf(2, "too many arguments\n");
828b58e810Skaashoek         return -1;
838b58e810Skaashoek       }
84e00baa9fSkaashoek       cmd->argv[cmd->argc++] = t;
858b58e810Skaashoek       break;
868b58e810Skaashoek 
8744e6909aSkaashoek     case '>':   // Input and output redirection
8844e6909aSkaashoek     case '<':
898b58e810Skaashoek       // Grab the filename from the argument list
908b58e810Skaashoek       if(gettoken(0, &t) != 'w') {
918b58e810Skaashoek         printf(2, "syntax error: > not followed by word\n");
928b58e810Skaashoek         return -1;
938b58e810Skaashoek       }
9444e6909aSkaashoek       if(cmd->io - cmd->iolist >= MAXIO) {
9544e6909aSkaashoek         printf(2, "too many redirections\n");
9644e6909aSkaashoek         return -1;
9744e6909aSkaashoek       }
9844e6909aSkaashoek       cmd->io->token = c;
99e00baa9fSkaashoek       cmd->io->s = t;
100e00baa9fSkaashoek       cmd->io++;
10121a88dd0Skaashoek       break;
10221a88dd0Skaashoek 
10321a88dd0Skaashoek     case ';':  // command sequence
10421a88dd0Skaashoek     case '|':  // pipe
10544e6909aSkaashoek       if(cmd->io - cmd->iolist >= MAXIO) {
10644e6909aSkaashoek         printf(2, "too many redirections\n");
10744e6909aSkaashoek         return -1;
10844e6909aSkaashoek       }
109e00baa9fSkaashoek       cmd->token = c;
110e00baa9fSkaashoek       cmd++;
1118b58e810Skaashoek       break;
1128b58e810Skaashoek 
1138b58e810Skaashoek     case 0:             // String is complete
1148b58e810Skaashoek       return 0;
1158b58e810Skaashoek 
1168b58e810Skaashoek     default:
1178b58e810Skaashoek       printf(2, "syntax error: bad return %d from gettoken", c);
1188b58e810Skaashoek       return -1;
1198b58e810Skaashoek 
1208b58e810Skaashoek     }
1218b58e810Skaashoek   }
1228b58e810Skaashoek }
1238b58e810Skaashoek 
1248b58e810Skaashoek void
1258b58e810Skaashoek runcmd(void)
1268b58e810Skaashoek {
127e00baa9fSkaashoek   int i, r, pid, tfd;
12821a88dd0Skaashoek   int fdarray[2];
129e00baa9fSkaashoek   struct cmd *c;
130e00baa9fSkaashoek   struct ionode *io;
1318b58e810Skaashoek 
1328b58e810Skaashoek   // Return immediately if command line was empty.
13321a88dd0Skaashoek   if(cmdlist[0].argc == 0) {
1348b58e810Skaashoek     if(debug)
1358b58e810Skaashoek       printf(2, "EMPTY COMMAND\n");
1368b58e810Skaashoek     return;
1378b58e810Skaashoek   }
1388b58e810Skaashoek 
139e00baa9fSkaashoek   for(c = &cmdlist[0]; c <= cmd; c++) {
1408b58e810Skaashoek     // Clean up command line.
1418b58e810Skaashoek     // Read all commands from the filesystem: add an initial '/' to
1428b58e810Skaashoek     // the command name.
1438b58e810Skaashoek     // This essentially acts like 'PATH=/'.
144e00baa9fSkaashoek     if(c->argv[0][0] != '/') {
145e00baa9fSkaashoek       c->argv0buf[0] = '/';
146e00baa9fSkaashoek       strcpy(c->argv0buf + 1, c->argv[0]);
147e00baa9fSkaashoek       c->argv[0] = c->argv0buf;
1488b58e810Skaashoek     }
149e00baa9fSkaashoek     c->argv[c->argc] = 0;
1508b58e810Skaashoek 
1518b58e810Skaashoek     // Print the command.
1528b58e810Skaashoek     if(debug) {
1538b58e810Skaashoek       printf(2, "[%d] SPAWN:", getpid());
154e00baa9fSkaashoek       for(i = 0; c->argv[i]; i++)
155e00baa9fSkaashoek         printf(2, " %s", c->argv[i]);
156e00baa9fSkaashoek       for(io = c->iolist; io <= c->io; io++) {
157e00baa9fSkaashoek         printf(2, "%c %s", io->token, io->s);
1588b58e810Skaashoek       }
1598b58e810Skaashoek       printf(2, "\n");
1608b58e810Skaashoek     }
1618b58e810Skaashoek 
162e00baa9fSkaashoek     if(strcmp(c->argv[0], "/cd") == 0) {
16348b82470Srsc       if(debug)
164e00baa9fSkaashoek         printf (2, "/cd %s is build in\n", c->argv[1]);
165e00baa9fSkaashoek       chdir(c->argv[1]);
1668b58e810Skaashoek       return;
1678b58e810Skaashoek     }
1688b58e810Skaashoek 
169e00baa9fSkaashoek     if(c->token == '|')
17021a88dd0Skaashoek       if(pipe(fdarray) < 0)
17121a88dd0Skaashoek         printf(2, "cmd %d pipe failed\n", c);
17221a88dd0Skaashoek 
1738b58e810Skaashoek     pid = fork();
1748b58e810Skaashoek     if(pid == 0) {
175e00baa9fSkaashoek       if(c->token == '|') {
17621a88dd0Skaashoek         if(close(1) < 0)
17721a88dd0Skaashoek           printf(2, "close 1 failed\n");
17821a88dd0Skaashoek         if((tfd = dup(fdarray[1])) < 0)
17921a88dd0Skaashoek           printf(2, "dup failed\n");
18021a88dd0Skaashoek         if(close(fdarray[0]) < 0)
18121a88dd0Skaashoek           printf(2, "close fdarray[0] failed\n");
18221a88dd0Skaashoek         if(close(fdarray[1]) < 0)
18321a88dd0Skaashoek           printf(2, "close fdarray[1] failed\n");
18421a88dd0Skaashoek       }
185e00baa9fSkaashoek       if(c > cmdlist && (c-1)->token == '|') {
18621a88dd0Skaashoek         if(close(0) < 0)
18721a88dd0Skaashoek           printf(2, "close 0 failed\n");
18821a88dd0Skaashoek         if((tfd = dup(fdarray[0])) < 0)
18921a88dd0Skaashoek           printf(2, "dup failed\n");
19021a88dd0Skaashoek         if(close(fdarray[0]) < 0)
19121a88dd0Skaashoek           printf(2, "close fdarray[0] failed\n");
19221a88dd0Skaashoek         if(close(fdarray[1]) < 0)
19321a88dd0Skaashoek           printf(2, "close fdarray[1] failed\n");
19421a88dd0Skaashoek       }
195e00baa9fSkaashoek       if(ioredirection(c->iolist, c->io - c->iolist) < 0)
1968b58e810Skaashoek         exit();
197e00baa9fSkaashoek       if((r = exec(c->argv0buf, (char**) c->argv)) < 0) {
198e00baa9fSkaashoek         printf(2, "exec %s: %d\n", c->argv[0], r);
199d7b3b802Skaashoek         exit();
200d7b3b802Skaashoek       }
20121a88dd0Skaashoek     } else if(pid > 0) {
20221a88dd0Skaashoek       int p;
2038b58e810Skaashoek       if(debug)
20421a88dd0Skaashoek         printf(2, "[%d] FORKED child %d\n", getpid(), pid);
20521a88dd0Skaashoek 
206e00baa9fSkaashoek       if(c > cmdlist && (c-1)->token == '|') {
20721a88dd0Skaashoek         close(fdarray[0]);
20821a88dd0Skaashoek         close(fdarray[1]);
20921a88dd0Skaashoek       }
210e00baa9fSkaashoek       if(c->token != '|') {
21121a88dd0Skaashoek         if(debug)
21221a88dd0Skaashoek           printf(2, "[%d] WAIT for children\n", getpid());
21321a88dd0Skaashoek         do {
21421a88dd0Skaashoek           p = wait();
21521a88dd0Skaashoek           if(debug)
21621a88dd0Skaashoek             printf(2, "[%d] WAIT child %d finished\n", getpid(), p);
21721a88dd0Skaashoek         } while(p > 0);
2188b58e810Skaashoek         if(debug)
2198b58e810Skaashoek           printf(2, "[%d] wait finished\n", getpid());
220d7b3b802Skaashoek       }
221d7b3b802Skaashoek     }
22221a88dd0Skaashoek   }
22321a88dd0Skaashoek }
2248b58e810Skaashoek 
2258b58e810Skaashoek int
226e00baa9fSkaashoek ioredirection(struct ionode *iolist, int nio)
2278b58e810Skaashoek {
228e00baa9fSkaashoek   int fd;
229e00baa9fSkaashoek   struct ionode *io;
2308b58e810Skaashoek 
231e00baa9fSkaashoek   for(io = iolist; io < &iolist[nio]; io++) {
232e00baa9fSkaashoek     switch(io->token) {
2338b58e810Skaashoek     case '<':
234d49a2d53Skaashoek       if(close(0) < 0)
235d49a2d53Skaashoek         printf(2, "close 0 failed\n");
236e00baa9fSkaashoek       if((fd = open(io->s, O_RDONLY)) < 0) {
237e00baa9fSkaashoek         printf(2, "failed to open %s for read: %d", io->s, fd);
2388b58e810Skaashoek         return -1;
2398b58e810Skaashoek       }
2408b58e810Skaashoek       if(debug)
241e00baa9fSkaashoek         printf(2, "redirect 0 from %s\n", io->s);
2428b58e810Skaashoek       break;
2438b58e810Skaashoek     case '>':
244d49a2d53Skaashoek       if(close(1) < 0)
245d49a2d53Skaashoek         printf(2, "close 1 failed\n");
246e00baa9fSkaashoek       if((fd = open(io->s, O_WRONLY|O_CREATE)) < 0) {
247e00baa9fSkaashoek         printf(2, "failed to open %s for write: %d", io->s, fd);
2488b58e810Skaashoek         exit();
2498b58e810Skaashoek       }
2508b58e810Skaashoek       if(debug)
251e00baa9fSkaashoek         printf(2, "redirect 1 to %s\n", io->s);
2528b58e810Skaashoek       break;
2538b58e810Skaashoek     }
2548b58e810Skaashoek   }
2558b58e810Skaashoek   return 0;
2568b58e810Skaashoek }
2578b58e810Skaashoek 
2588b58e810Skaashoek // gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
2598b58e810Skaashoek // gettoken(0, token) parses a shell token from the previously set string,
2608b58e810Skaashoek // null-terminates that token, stores the token pointer in '*token',
2618b58e810Skaashoek // and returns a token ID (0, '<', '>', '|', or 'w').
2628b58e810Skaashoek // Subsequent calls to 'gettoken(0, token)' will return subsequent
2638b58e810Skaashoek // tokens from the string.
2648b58e810Skaashoek 
2658b58e810Skaashoek int
2668b58e810Skaashoek gettoken(char *s, char **p1)
2678b58e810Skaashoek {
2688b58e810Skaashoek   static int c, nc;
2698b58e810Skaashoek   static char *np1, *np2;
2708b58e810Skaashoek 
2718b58e810Skaashoek   if(s) {
2728b58e810Skaashoek     nc = _gettoken(s, &np1, &np2);
2738b58e810Skaashoek     return 0;
2748b58e810Skaashoek   }
2758b58e810Skaashoek   c = nc;
2768b58e810Skaashoek   *p1 = np1;
2778b58e810Skaashoek   nc = _gettoken(np2, &np1, &np2);
2788b58e810Skaashoek   return c;
2798b58e810Skaashoek }
2808b58e810Skaashoek 
2818b58e810Skaashoek 
2828b58e810Skaashoek // Get the next token from string s.
2838b58e810Skaashoek // Set *p1 to the beginning of the token and *p2 just past the token.
2848b58e810Skaashoek // Returns
2858b58e810Skaashoek //      0 for end-of-string;
2868b58e810Skaashoek //      < for <;
2878b58e810Skaashoek //      > for >;
2888b58e810Skaashoek //      | for |;
2898b58e810Skaashoek //      w for a word.
2908b58e810Skaashoek //
2918b58e810Skaashoek // Eventually (once we parse the space where the \0 will go),
2928b58e810Skaashoek // words get nul-terminated.
2938b58e810Skaashoek #define WHITESPACE " \t\r\n"
2948b58e810Skaashoek #define SYMBOLS "<|>&;()"
2958b58e810Skaashoek 
2968b58e810Skaashoek int
2978b58e810Skaashoek _gettoken(char *s, char **p1, char **p2)
2988b58e810Skaashoek {
2998b58e810Skaashoek   int t;
3008b58e810Skaashoek 
3018b58e810Skaashoek   if(s == 0) {
3028b58e810Skaashoek     if(debug > 1)
30389ebd895Srsc       printf(2, "GETTOKEN 0\n");
3048b58e810Skaashoek     return 0;
3058b58e810Skaashoek   }
3068b58e810Skaashoek 
3078b58e810Skaashoek   if(debug > 1)
3088b58e810Skaashoek     printf(2, "GETTOKEN: %s\n", s);
3098b58e810Skaashoek 
3108b58e810Skaashoek   *p1 = 0;
3118b58e810Skaashoek   *p2 = 0;
3128b58e810Skaashoek 
3138b58e810Skaashoek   while(strchr(WHITESPACE, *s))
3148b58e810Skaashoek     *s++ = 0;
3158b58e810Skaashoek   if(*s == 0) {
3168b58e810Skaashoek     if(debug > 1)
3178b58e810Skaashoek       printf(2, "EOL\n");
3188b58e810Skaashoek     return 0;
3198b58e810Skaashoek   }
3208b58e810Skaashoek   if(strchr(SYMBOLS, *s)) {
3218b58e810Skaashoek     t = *s;
3228b58e810Skaashoek     *p1 = s;
3238b58e810Skaashoek     *s++ = 0;
3248b58e810Skaashoek     *p2 = s;
3258b58e810Skaashoek     if(debug > 1)
3268b58e810Skaashoek       printf(2, "TOK %c\n", t);
3278b58e810Skaashoek     return t;
3288b58e810Skaashoek   }
3298b58e810Skaashoek   *p1 = s;
3308b58e810Skaashoek   while(*s && !strchr(WHITESPACE SYMBOLS, *s))
3318b58e810Skaashoek     s++;
3328b58e810Skaashoek   *p2 = s;
3338b58e810Skaashoek   if(debug > 1) {
3348b58e810Skaashoek     t = **p2;
3358b58e810Skaashoek     **p2 = 0;
3368b58e810Skaashoek     printf(2, "WORD: %s\n", *p1);
3378b58e810Skaashoek     **p2 = t;
3388b58e810Skaashoek   }
3398b58e810Skaashoek   return 'w';
3408b58e810Skaashoek }
3418b58e810Skaashoek 
342