xref: /xv6-public/sh.c (revision 21a88dd0)
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
10*21a88dd0Skaashoek #define MAXCMD  2
118b58e810Skaashoek 
12*21a88dd0Skaashoek // an embarrassingly naive shell
13*21a88dd0Skaashoek 
14*21a88dd0Skaashoek // some day a real parse tree; for now ad-hoc
15*21a88dd0Skaashoek struct ionode {
168b58e810Skaashoek   int token;
178b58e810Skaashoek   char *s;
188b58e810Skaashoek };
19*21a88dd0Skaashoek struct ionode iolist[MAXNODE];
20*21a88dd0Skaashoek int nextio;
218b58e810Skaashoek 
22*21a88dd0Skaashoek struct cmd {
238b58e810Skaashoek   char *argv[MAXARGS];
248b58e810Skaashoek   char argv0buf[BUFSIZ];
258b58e810Skaashoek   int argc;
26*21a88dd0Skaashoek   int token;
27*21a88dd0Skaashoek };
28*21a88dd0Skaashoek struct cmd cmdlist[MAXCMD];
29*21a88dd0Skaashoek int nextcmd;
308b58e810Skaashoek 
31*21a88dd0Skaashoek char buf[BUFSIZ];
327a37578eSrtm int debug = 0;
338b58e810Skaashoek 
348b58e810Skaashoek int parse(char *s);
358b58e810Skaashoek void runcmd(void);
368b58e810Skaashoek int ioredirection(void);
378b58e810Skaashoek int gettoken(char *s, char **token);
388b58e810Skaashoek int _gettoken(char *s, char **p1, char **p2);
39*21a88dd0Skaashoek void addio(int token, char *s);
4017a85657Srtm 
4117a85657Srtm int
4217a85657Srtm main(void)
4317a85657Srtm {
4417a85657Srtm   while(1){
4543572072Srtm     puts("$ ");
468787cd01Skaashoek     memset (buf, '\0', sizeof(buf));
4717a85657Srtm     gets(buf, sizeof(buf));
488b58e810Skaashoek     if (parse(buf) < 0)
4917a85657Srtm       continue;
508b58e810Skaashoek     runcmd();
5117a85657Srtm   }
5217a85657Srtm }
53d7b3b802Skaashoek 
548b58e810Skaashoek int
558b58e810Skaashoek parse(char *s)
56d7b3b802Skaashoek {
578b58e810Skaashoek   char *t;
58*21a88dd0Skaashoek   int c, i;
598b58e810Skaashoek 
608b58e810Skaashoek   gettoken(s, 0);
618b58e810Skaashoek 
62*21a88dd0Skaashoek   nextio = 0;
63*21a88dd0Skaashoek   nextcmd = 0;
64*21a88dd0Skaashoek   for (i = 0; i < MAXCMD; i++) {
65*21a88dd0Skaashoek     cmdlist[i].argc = 0;
66*21a88dd0Skaashoek     cmdlist[i].token = 0;
67*21a88dd0Skaashoek   }
688b58e810Skaashoek   while (1) {
698b58e810Skaashoek     switch ((c = gettoken(0, &t))) {
708b58e810Skaashoek 
718b58e810Skaashoek     case 'w':	// Add an argument
72*21a88dd0Skaashoek       if (cmdlist[nextcmd].argc >= MAXARGS) {
738b58e810Skaashoek 	printf(2, "too many arguments\n");
748b58e810Skaashoek 	return -1;
758b58e810Skaashoek       }
76*21a88dd0Skaashoek       cmdlist[nextcmd].argv[cmdlist[nextcmd].argc++] = t;
778b58e810Skaashoek       break;
788b58e810Skaashoek 
798b58e810Skaashoek     case '<':	// Input redirection
808b58e810Skaashoek       // Grab the filename from the argument list
818b58e810Skaashoek       if (gettoken(0, &t) != 'w') {
828b58e810Skaashoek 	printf(2, "syntax error: < not followed by word\n");
838b58e810Skaashoek 	return -1;
848b58e810Skaashoek       }
85*21a88dd0Skaashoek       addio('<', t);
868b58e810Skaashoek       break;
878b58e810Skaashoek 
888b58e810Skaashoek     case '>':	// Output redirection
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       }
94*21a88dd0Skaashoek       addio('>', t);
95*21a88dd0Skaashoek       break;
96*21a88dd0Skaashoek 
97*21a88dd0Skaashoek     case ';':  // command sequence
98*21a88dd0Skaashoek     case '|':  // pipe
99*21a88dd0Skaashoek       cmdlist[nextcmd].token = c;
100*21a88dd0Skaashoek       nextcmd++;
1018b58e810Skaashoek       break;
1028b58e810Skaashoek 
1038b58e810Skaashoek     case 0:		// String is complete
1048b58e810Skaashoek       return 0;
1058b58e810Skaashoek 
1068b58e810Skaashoek     default:
1078b58e810Skaashoek       printf(2, "syntax error: bad return %d from gettoken", c);
1088b58e810Skaashoek       return -1;
1098b58e810Skaashoek 
1108b58e810Skaashoek     }
1118b58e810Skaashoek   }
1128b58e810Skaashoek }
1138b58e810Skaashoek 
1148b58e810Skaashoek 
1158b58e810Skaashoek void
1168b58e810Skaashoek runcmd(void)
1178b58e810Skaashoek {
118*21a88dd0Skaashoek   int c, i, r, pid, tfd;
119*21a88dd0Skaashoek   int fdarray[2];
1208b58e810Skaashoek 
1218b58e810Skaashoek   // Return immediately if command line was empty.
122*21a88dd0Skaashoek   if(cmdlist[0].argc == 0) {
1238b58e810Skaashoek     if (debug)
1248b58e810Skaashoek       printf(2, "EMPTY COMMAND\n");
1258b58e810Skaashoek     return;
1268b58e810Skaashoek   }
1278b58e810Skaashoek 
128*21a88dd0Skaashoek   for (c = 0; c <= nextcmd; c++) {
1298b58e810Skaashoek     // Clean up command line.
1308b58e810Skaashoek     // Read all commands from the filesystem: add an initial '/' to
1318b58e810Skaashoek     // the command name.
1328b58e810Skaashoek     // This essentially acts like 'PATH=/'.
133*21a88dd0Skaashoek     if (cmdlist[c].argv[0][0] != '/') {
134*21a88dd0Skaashoek       cmdlist[c].argv0buf[0] = '/';
135*21a88dd0Skaashoek       strcpy(cmdlist[c].argv0buf + 1, cmdlist[c].argv[0]);
136*21a88dd0Skaashoek       cmdlist[c].argv[0] = cmdlist[c].argv0buf;
1378b58e810Skaashoek     }
138*21a88dd0Skaashoek     cmdlist[c].argv[cmdlist[c].argc] = 0;
1398b58e810Skaashoek 
1408b58e810Skaashoek     // Print the command.
1418b58e810Skaashoek     if (debug) {
1428b58e810Skaashoek       printf(2, "[%d] SPAWN:", getpid());
143*21a88dd0Skaashoek       for (i = 0; cmdlist[c].argv[i]; i++)
144*21a88dd0Skaashoek 	printf(2, " %s", cmdlist[c].argv[i]);
145*21a88dd0Skaashoek       for (i = 0; i < nextio; i++) {
146*21a88dd0Skaashoek 	printf(2, "%c %s", iolist[i].token, iolist[i].s);
1478b58e810Skaashoek       }
1488b58e810Skaashoek       printf(2, "\n");
1498b58e810Skaashoek     }
1508b58e810Skaashoek 
151*21a88dd0Skaashoek     if (strcmp(cmdlist[c].argv[0], "/cd") == 0) {
152*21a88dd0Skaashoek       if (debug) printf (2, "/cd %s is build in\n", cmdlist[c].argv[1]);
153*21a88dd0Skaashoek       chdir(cmdlist[c].argv[1]);
1548b58e810Skaashoek       return;
1558b58e810Skaashoek     }
1568b58e810Skaashoek 
157*21a88dd0Skaashoek     if (cmdlist[c].token == '|')
158*21a88dd0Skaashoek       if (pipe(fdarray) < 0)
159*21a88dd0Skaashoek 	printf(2, "cmd %d pipe failed\n", c);
160*21a88dd0Skaashoek 
1618b58e810Skaashoek     pid = fork();
1628b58e810Skaashoek     if (pid == 0) {
163*21a88dd0Skaashoek       if (cmdlist[c].token == '|') {
164*21a88dd0Skaashoek 	if (close(1) < 0)
165*21a88dd0Skaashoek 	  printf(2, "close 1 failed\n");
166*21a88dd0Skaashoek 	if ((tfd = dup(fdarray[1])) < 0)
167*21a88dd0Skaashoek 	  printf(2, "dup failed\n");
168*21a88dd0Skaashoek 	if (close(fdarray[0]) < 0)
169*21a88dd0Skaashoek 	  printf(2, "close fdarray[0] failed\n");
170*21a88dd0Skaashoek 	if (close(fdarray[1]) < 0)
171*21a88dd0Skaashoek 	  printf(2, "close fdarray[1] failed\n");
172*21a88dd0Skaashoek       }
173*21a88dd0Skaashoek       if (c > 0 && cmdlist[c-1].token == '|') {
174*21a88dd0Skaashoek 	if (close(0) < 0)
175*21a88dd0Skaashoek 	  printf(2, "close 0 failed\n");
176*21a88dd0Skaashoek 	if ((tfd = dup(fdarray[0])) < 0)
177*21a88dd0Skaashoek 	  printf(2, "dup failed\n");
178*21a88dd0Skaashoek 	if (close(fdarray[0]) < 0)
179*21a88dd0Skaashoek 	  printf(2, "close fdarray[0] failed\n");
180*21a88dd0Skaashoek 	if (close(fdarray[1]) < 0)
181*21a88dd0Skaashoek 	  printf(2, "close fdarray[1] failed\n");
182*21a88dd0Skaashoek       }
1838b58e810Skaashoek       if (ioredirection() < 0)
1848b58e810Skaashoek 	exit();
185*21a88dd0Skaashoek       if ((r = exec(cmdlist[c].argv0buf, (char**) cmdlist[c].argv)) < 0) {
186*21a88dd0Skaashoek 	printf(2, "exec %s: %d\n", cmdlist[c].argv[0], r);
187d7b3b802Skaashoek 	exit();
188d7b3b802Skaashoek       }
189*21a88dd0Skaashoek     } else if (pid > 0) {
190*21a88dd0Skaashoek       int p;
1918b58e810Skaashoek       if (debug)
192*21a88dd0Skaashoek 	printf(2, "[%d] FORKED child %d\n", getpid(), pid);
193*21a88dd0Skaashoek 
194*21a88dd0Skaashoek       if (c > 0 && cmdlist[c-1].token == '|') {
195*21a88dd0Skaashoek 	close(fdarray[0]);
196*21a88dd0Skaashoek 	close(fdarray[1]);
197*21a88dd0Skaashoek       }
198*21a88dd0Skaashoek       if (cmdlist[c].token != '|') {
199*21a88dd0Skaashoek 	if (debug)
200*21a88dd0Skaashoek 	  printf(2, "[%d] WAIT for children\n", getpid());
201*21a88dd0Skaashoek 	do {
202*21a88dd0Skaashoek 	  p = wait();
203*21a88dd0Skaashoek 	  if (debug)
204*21a88dd0Skaashoek 	    printf(2, "[%d] WAIT child %d finished\n", getpid(), p);
205*21a88dd0Skaashoek 	} while (p > 0);
2068b58e810Skaashoek 	if (debug)
2078b58e810Skaashoek 	  printf(2, "[%d] wait finished\n", getpid());
208d7b3b802Skaashoek       }
209d7b3b802Skaashoek     }
210*21a88dd0Skaashoek   }
211*21a88dd0Skaashoek }
2128b58e810Skaashoek 
2138b58e810Skaashoek int
2148b58e810Skaashoek ioredirection(void)
2158b58e810Skaashoek {
216*21a88dd0Skaashoek   int i, fd;
2178b58e810Skaashoek 
218*21a88dd0Skaashoek   for (i = 0; i < nextio; i++) {
219*21a88dd0Skaashoek     switch (iolist[i].token) {
2208b58e810Skaashoek     case '<':
221d49a2d53Skaashoek       if (close(0) < 0)
222d49a2d53Skaashoek 	printf(2, "close 0 failed\n");
223*21a88dd0Skaashoek       if ((fd = open(iolist[i].s, O_RDONLY)) < 0) {
224*21a88dd0Skaashoek 	printf(2, "failed to open %s for read: %d", iolist[i].s, fd);
2258b58e810Skaashoek 	return -1;
2268b58e810Skaashoek       }
2278b58e810Skaashoek       if (debug)
228*21a88dd0Skaashoek 	printf(2, "redirect 0 from %s\n", iolist[i].s);
2298b58e810Skaashoek       break;
2308b58e810Skaashoek     case '>':
231d49a2d53Skaashoek       if (close(1) < 0)
232d49a2d53Skaashoek 	printf(2, "close 1 failed\n");
233*21a88dd0Skaashoek       if ((fd = open(iolist[i].s, O_WRONLY|O_CREATE)) < 0) {
234*21a88dd0Skaashoek 	printf(2, "failed to open %s for write: %d", iolist[i].s, fd);
2358b58e810Skaashoek 	exit();
2368b58e810Skaashoek       }
2378b58e810Skaashoek       if (debug)
238*21a88dd0Skaashoek 	printf(2, "redirect 1 to %s\n", iolist[i].s);
2398b58e810Skaashoek       break;
2408b58e810Skaashoek     }
2418b58e810Skaashoek   }
2428b58e810Skaashoek   return 0;
2438b58e810Skaashoek }
2448b58e810Skaashoek 
2458b58e810Skaashoek void
246*21a88dd0Skaashoek addio(int token, char *s)
2478b58e810Skaashoek {
248*21a88dd0Skaashoek   if (nextio >= MAXNODE) {
249*21a88dd0Skaashoek     printf(2, "addio: ran out of nodes\n");
2508b58e810Skaashoek     return;
2518b58e810Skaashoek   }
2528b58e810Skaashoek 
253*21a88dd0Skaashoek   iolist[nextio].token = token;
254*21a88dd0Skaashoek   iolist[nextio].s = s;
255*21a88dd0Skaashoek   nextio++;
2568b58e810Skaashoek }
2578b58e810Skaashoek 
2588b58e810Skaashoek 
2598b58e810Skaashoek // gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
2608b58e810Skaashoek // gettoken(0, token) parses a shell token from the previously set string,
2618b58e810Skaashoek // null-terminates that token, stores the token pointer in '*token',
2628b58e810Skaashoek // and returns a token ID (0, '<', '>', '|', or 'w').
2638b58e810Skaashoek // Subsequent calls to 'gettoken(0, token)' will return subsequent
2648b58e810Skaashoek // tokens from the string.
2658b58e810Skaashoek 
2668b58e810Skaashoek int
2678b58e810Skaashoek gettoken(char *s, char **p1)
2688b58e810Skaashoek {
2698b58e810Skaashoek   static int c, nc;
2708b58e810Skaashoek   static char* np1, *np2;
2718b58e810Skaashoek 
2728b58e810Skaashoek   if (s) {
2738b58e810Skaashoek     nc = _gettoken(s, &np1, &np2);
2748b58e810Skaashoek     return 0;
2758b58e810Skaashoek   }
2768b58e810Skaashoek   c = nc;
2778b58e810Skaashoek   *p1 = np1;
2788b58e810Skaashoek   nc = _gettoken(np2, &np1, &np2);
2798b58e810Skaashoek   return c;
2808b58e810Skaashoek }
2818b58e810Skaashoek 
2828b58e810Skaashoek 
2838b58e810Skaashoek // Get the next token from string s.
2848b58e810Skaashoek // Set *p1 to the beginning of the token and *p2 just past the token.
2858b58e810Skaashoek // Returns
2868b58e810Skaashoek //	0 for end-of-string;
2878b58e810Skaashoek //	< for <;
2888b58e810Skaashoek //	> for >;
2898b58e810Skaashoek //	| for |;
2908b58e810Skaashoek //	w for a word.
2918b58e810Skaashoek //
2928b58e810Skaashoek // Eventually (once we parse the space where the \0 will go),
2938b58e810Skaashoek // words get nul-terminated.
2948b58e810Skaashoek #define WHITESPACE " \t\r\n"
2958b58e810Skaashoek #define SYMBOLS "<|>&;()"
2968b58e810Skaashoek 
2978b58e810Skaashoek int
2988b58e810Skaashoek _gettoken(char *s, char **p1, char **p2)
2998b58e810Skaashoek {
3008b58e810Skaashoek   int t;
3018b58e810Skaashoek 
3028b58e810Skaashoek   if (s == 0) {
3038b58e810Skaashoek     if (debug > 1)
3048b58e810Skaashoek       printf(2, "GETTOKEN NULL\n");
3058b58e810Skaashoek     return 0;
3068b58e810Skaashoek   }
3078b58e810Skaashoek 
3088b58e810Skaashoek   if (debug > 1)
3098b58e810Skaashoek     printf(2, "GETTOKEN: %s\n", s);
3108b58e810Skaashoek 
3118b58e810Skaashoek   *p1 = 0;
3128b58e810Skaashoek   *p2 = 0;
3138b58e810Skaashoek 
3148b58e810Skaashoek   while (strchr(WHITESPACE, *s))
3158b58e810Skaashoek     *s++ = 0;
3168b58e810Skaashoek   if (*s == 0) {
3178b58e810Skaashoek     if (debug > 1)
3188b58e810Skaashoek       printf(2, "EOL\n");
3198b58e810Skaashoek     return 0;
3208b58e810Skaashoek   }
3218b58e810Skaashoek   if (strchr(SYMBOLS, *s)) {
3228b58e810Skaashoek     t = *s;
3238b58e810Skaashoek     *p1 = s;
3248b58e810Skaashoek     *s++ = 0;
3258b58e810Skaashoek     *p2 = s;
3268b58e810Skaashoek     if (debug > 1)
3278b58e810Skaashoek       printf(2, "TOK %c\n", t);
3288b58e810Skaashoek     return t;
3298b58e810Skaashoek   }
3308b58e810Skaashoek   *p1 = s;
3318b58e810Skaashoek   while (*s && !strchr(WHITESPACE SYMBOLS, *s))
3328b58e810Skaashoek     s++;
3338b58e810Skaashoek   *p2 = s;
3348b58e810Skaashoek   if (debug > 1) {
3358b58e810Skaashoek     t = **p2;
3368b58e810Skaashoek     **p2 = 0;
3378b58e810Skaashoek     printf(2, "WORD: %s\n", *p1);
3388b58e810Skaashoek     **p2 = t;
3398b58e810Skaashoek   }
3408b58e810Skaashoek   return 'w';
3418b58e810Skaashoek }
3428b58e810Skaashoek 
343