]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/sh.C
ircrc: freenode -> oftc
[plan9front.git] / sys / src / cmd / sh.C
1 /* sh - simple shell - great for early stages of porting */
2 #include "u.h"
3 #include "libc.h"
4
5 #define MAXLINE 200             /* maximum line length */
6 #define WORD 256                /* token code for words */
7 #define EOF -1                  /* token code for end of file */
8 #define ispunct(c)              (c=='|' || c=='&' || c==';' || c=='<' || \
9                                  c=='>' || c=='(' || c==')' || c=='\n')
10 #define isspace(c)              (c==' ' || c=='\t')
11 #define execute(np)             (ignored = (np? (*(np)->op)(np) : 0))
12
13 typedef struct Node     Node;
14 struct Node{                    /* parse tree node */
15         int (*op)(Node *);      /* operator function */
16         Node *args[2];          /* argument nodes */
17         char *argv[100];        /* argument pointers */
18         char *io[3];            /* i/o redirection */
19 };
20
21 Node    nodes[25];              /* node pool */
22 Node    *nfree;                 /* next available node */
23 char    strspace[10*MAXLINE];   /* string storage */
24 char    *sfree;                 /* next free character in strspace */
25 int     t;                      /* current token code */
26 char    *token;                 /* current token text (in strspace) */
27 int     putback = 0;            /* lookahead */
28 char    status[256];            /* exit status of most recent command */
29 int     cflag = 0;              /* command is argument to sh */
30 int     tflag = 0;              /* read only one line */
31 int     interactive = 0;        /* prompt */
32 char    *cflagp;                /* command line for cflag */
33 char    *path[] ={"/bin", 0};
34 int     ignored;
35
36 Node    *alloc(int (*op)(Node *));
37 int     builtin(Node *np);
38 Node    *command(void);
39 int     getch(void);
40 int     gettoken(void);
41 Node    *list(void);
42 void    error(char *s, char *t);
43 Node    *pipeline(void);
44 void    redirect(Node *np);
45 int     setio(Node *np);
46 Node    *simple(void);
47 int     xpipeline(Node *np);
48 int     xsimple(Node *np);
49 int     xsubshell(Node *np);
50 int     xnowait(Node *np);
51 int     xwait(Node *np);
52
53 void
54 main(int argc, char *argv[])
55 {
56         Node *np;
57
58         if(argc>1 && strcmp(argv[1], "-t")==0)
59                 tflag++;
60         else if(argc>2 && strcmp(argv[1], "-c")==0){
61                 cflag++;
62                 cflagp = argv[2];
63         }else if(argc>1){
64                 close(0);
65                 if(open(argv[1], 0) != 0){
66                         error(": can't open", argv[1]);
67                         exits("argument");
68                 }
69         }else
70                 interactive = 1;
71         for(;;){
72                 if(interactive)
73                         fprint(2, "%d$ ", getpid());
74                 nfree = nodes;
75                 sfree = strspace;
76                 if((t=gettoken()) == EOF)
77                         break;
78                 if(t != '\n')
79                         if(np = list())
80                                 execute(np);
81                         else
82                                 error("syntax error", "");
83                 while(t!=EOF && t!='\n')        /* flush syntax errors */
84                         t = gettoken();
85         }
86         exits(status);
87 }
88
89 /* alloc - allocate for op and return a node */
90 Node*
91 alloc(int (*op)(Node *))
92 {
93         if(nfree < nodes+sizeof(nodes)){
94                 nfree->op = op;
95                 nfree->args[0] = nfree->args[1] = 0;
96                 nfree->argv[0] = nfree->argv[1] = 0;
97                 nfree->io[0] = nfree->io[1] = nfree->io[2] = 0;
98                 return nfree++;
99         }
100         error("node storage overflow", "");
101         exits("node storage overflow");
102         return nil;
103 }
104
105 /* builtin - check np for builtin command and, if found, execute it */
106 int
107 builtin(Node *np)
108 {
109         int n = 0;
110         char name[MAXLINE];
111         Waitmsg *wmsg;
112
113         if(np->argv[1])
114                 n = strtoul(np->argv[1], 0, 0);
115         if(strcmp(np->argv[0], "cd") == 0){
116                 if(chdir(np->argv[1]? np->argv[1] : "/") == -1)
117                         error(": bad directory", np->argv[0]);
118                 return 1;
119         }else if(strcmp(np->argv[0], "exit") == 0)
120                 exits(np->argv[1]? np->argv[1] : status);
121         else if(strcmp(np->argv[0], "bind") == 0){
122                 if(np->argv[1]==0 || np->argv[2]==0)
123                         error("usage: bind new old", "");
124                 else if(bind(np->argv[1], np->argv[2], 0)==-1)
125                         error("bind failed", "");
126                 return 1;
127 #ifdef asdf
128         }else if(strcmp(np->argv[0], "unmount") == 0){
129                 if(np->argv[1] == 0)
130                         error("usage: unmount [new] old", "");
131                 else if(np->argv[2] == 0){
132                         if(unmount((char *)0, np->argv[1]) == -1)
133                                 error("unmount:", "");
134                 }else if(unmount(np->argv[1], np->argv[2]) == -1)
135                         error("unmount", "");
136                 return 1;
137 #endif
138         }else if(strcmp(np->argv[0], "wait") == 0){
139                 while((wmsg = wait()) != nil){
140                         strncpy(status, wmsg->msg, sizeof(status)-1);
141                         if(n && wmsg->pid==n){
142                                 n = 0;
143                                 free(wmsg);
144                                 break;
145                         }
146                         free(wmsg);
147                 }
148                 if(n)
149                         error("wait error", "");
150                 return 1;
151         }else if(strcmp(np->argv[0], "rfork") == 0){
152                 char *p;
153                 int mask;
154
155                 p = np->argv[1];
156                 if(p == 0 || *p == 0)
157                         p = "ens";
158                 mask = 0;
159
160                 while(*p)
161                         switch(*p++){
162                         case 'n': mask |= RFNAMEG; break;
163                         case 'N': mask |= RFCNAMEG; break;
164                         case 'e': mask |= RFENVG; break;
165                         case 'E': mask |= RFCENVG; break;
166                         case 's': mask |= RFNOTEG; break;
167                         case 'f': mask |= RFFDG; break;
168                         case 'F': mask |= RFCFDG; break;
169                         case 'm': mask |= RFNOMNT; break;
170                         default: error(np->argv[1], "bad rfork flag");
171                         }
172                 rfork(mask);
173
174                 return 1;
175         }else if(strcmp(np->argv[0], "exec") == 0){
176                 redirect(np);
177                 if(np->argv[1] == (char *) 0)
178                         return 1;
179                 exec(np->argv[1], &np->argv[1]);
180                 n = np->argv[1][0];
181                 if(n!='/' && n!='#' && (n!='.' || np->argv[1][1]!='/'))
182                         for(n = 0; path[n]; n++){
183                                 sprint(name, "%s/%s", path[n], np->argv[1]);
184                                 exec(name, &np->argv[1]);
185                         }
186                 error(": not found", np->argv[1]);
187                 return 1;
188         }
189         return 0;
190 }
191
192 /* command - ( list ) [ ( < | > | >> ) word ]* | simple */
193 Node*
194 command(void)
195 {
196         Node *np;
197
198         if(t != '(')
199                 return simple();
200         np = alloc(xsubshell);
201         t = gettoken();
202         if((np->args[0]=list())==0 || t!=')')
203                 return 0;
204         while((t=gettoken())=='<' || t=='>')
205                 if(!setio(np))
206                         return 0;
207         return np;
208 }
209
210 /* getch - get next, possibly pushed back, input character */
211 int
212 getch(void)
213 {
214         unsigned char c;
215         static done=0;
216
217         if(putback){
218                 c = putback;
219                 putback = 0;
220         }else if(tflag){
221                 if(done || read(0, &c, 1)!=1){
222                         done = 1;
223                         return EOF;
224                 }
225                 if(c == '\n')
226                         done = 1;
227         }else if(cflag){
228                 if(done)
229                         return EOF;
230                 if((c=*cflagp++) == 0){
231                         done = 1;
232                         c = '\n';
233                 }
234         }else if(read(0, &c, 1) != 1)
235                 return EOF;
236         return c;
237 }
238
239 /* gettoken - get next token into string space, return token code */
240 int
241 gettoken(void)
242 {
243         int c;
244
245         while((c = getch()) != EOF)
246                 if(!isspace(c))
247                         break;
248         if(c==EOF || ispunct(c))
249                 return c;
250         token = sfree;
251         do{
252                 if(sfree >= strspace+sizeof(strspace) - 1){
253                         error("string storage overflow", "");
254                         exits("string storage overflow");
255                 }
256                 *sfree++ = c;
257         }while((c=getch()) != EOF && !ispunct(c) && !isspace(c));
258         *sfree++ = 0;
259         putback = c;
260         return WORD;
261 }
262
263 /* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ]  (not LL(1), but ok) */
264 Node*
265 list(void)
266 {
267         Node *np, *np1;
268
269         np = alloc(0);
270         if((np->args[1]=pipeline()) == 0)
271                 return 0;
272         while(t==';' || t=='&'){
273                 np->op = (t==';')? xwait : xnowait;
274                 t = gettoken();
275                 if(t==')' || t=='\n')   /* tests ~first(pipeline) */
276                         break;
277                 np1 = alloc(0);
278                 np1->args[0] = np;
279                 if((np1->args[1]=pipeline()) == 0)
280                         return 0;
281                 np = np1;
282         }
283         if(np->op == 0)
284                 np->op = xwait;
285         return np;
286 }
287
288 /* error - print error message s, prefixed by t */
289 void
290 error(char *s, char *t)
291 {
292         char buf[256];
293
294         fprint(2, "%s%s", t, s);
295         errstr(buf, sizeof buf);
296         fprint(2, ": %s\n", buf);
297 }
298
299 /* pipeline - command ( | command )* */
300 Node*
301 pipeline(void)
302 {
303         Node *np, *np1;
304
305         if((np=command()) == 0)
306                 return 0;
307         while(t == '|'){
308                 np1 = alloc(xpipeline);
309                 np1->args[0] = np;
310                 t = gettoken();
311                 if((np1->args[1]=command()) == 0)
312                         return 0;
313                 np = np1;
314         }
315         return np;
316 }
317
318 /* redirect - redirect i/o according to np->io[] values */
319 void
320 redirect(Node *np)
321 {
322         int fd;
323
324         if(np->io[0]){
325                 if((fd = open(np->io[0], 0)) < 0){
326                         error(": can't open", np->io[0]);
327                         exits("open");
328                 }
329                 dup(fd, 0);
330                 close(fd);
331         }
332         if(np->io[1]){
333                 if((fd = create(np->io[1], 1, 0666L)) < 0){
334                         error(": can't create", np->io[1]);
335                         exits("create");
336                 }
337                 dup(fd, 1);
338                 close(fd);
339         }
340         if(np->io[2]){
341                 if((fd = open(np->io[2], 1)) < 0 && (fd = create(np->io[2], 1, 0666L)) < 0){
342                         error(": can't write", np->io[2]);
343                         exits("write");
344                 }
345                 dup(fd, 1);
346                 close(fd);
347                 seek(1, 0, 2);
348         }
349 }
350
351 /* setio - ( < | > | >> ) word; fill in np->io[] */
352 int
353 setio(Node *np)
354 {
355         if(t == '<'){
356                 t = gettoken();
357                 np->io[0] = token;
358         }else if(t == '>'){
359                 t = gettoken();
360                 if(t == '>'){
361                         t = gettoken();
362                         np->io[2] = token;
363                         }else
364                         np->io[1] = token;
365         }else
366                 return 0;
367         if(t != WORD)
368                 return 0;
369         return 1;
370 }
371                         
372 /* simple - word ( [ < | > | >> ] word )* */
373 Node*
374 simple(void)
375 {
376         Node *np;
377         int n = 1;
378
379         if(t != WORD)
380                 return 0;
381         np = alloc(xsimple);
382         np->argv[0] = token;
383         while((t = gettoken())==WORD || t=='<' || t=='>')
384                 if(t == WORD)
385                         np->argv[n++] = token;
386                 else if(!setio(np))
387                         return 0;
388         np->argv[n] = 0;
389         return np;
390 }
391
392 /* xpipeline - execute cmd | cmd */
393 int
394 xpipeline(Node *np)
395 {
396         int pid, fd[2];
397
398         if(pipe(fd) < 0){
399                 error("can't create pipe", "");
400                 return 0;
401         }
402         if((pid=fork()) == 0){  /* left side; redirect stdout */
403                 dup(fd[1], 1);
404                 close(fd[0]);
405                 close(fd[1]);
406                 execute(np->args[0]);
407                 exits(status);
408         }else if(pid == -1){
409                 error("can't create process", "");
410                 return 0;
411         }
412         if((pid=fork()) == 0){  /* right side; redirect stdin */
413                 dup(fd[0], 0);
414                 close(fd[0]);
415                 close(fd[1]);
416                 pid = execute(np->args[1]); /*BUG: this is wrong sometimes*/
417                 if(pid > 0)
418                         while(waitpid()!=pid)
419                                 ;
420                 exits(0);
421         }else if(pid == -1){
422                 error("can't create process", "");
423                 return 0;
424         }
425         close(fd[0]);   /* avoid using up fd's */
426         close(fd[1]);
427         return pid;
428 }
429
430 /* xsimple - execute a simple command */
431 int
432 xsimple(Node *np)
433 {
434         char name[MAXLINE];
435         int pid, i;
436
437         if(builtin(np))
438                 return 0;
439         if(pid = fork()){
440                 if(pid == -1)
441                         error(": can't create process", np->argv[0]);
442                 return pid;
443         }
444         redirect(np);   /* child process */
445         exec(np->argv[0], &np->argv[0]);
446         i = np->argv[0][0];
447         if(i!='/' && i!='#' && (i!='.' || np->argv[0][1]!='/'))
448                 for(i = 0; path[i]; i++){
449                         sprint(name, "%s/%s", path[i], np->argv[0]);
450                         exec(name, &np->argv[0]);
451                 }
452         error(": not found", np->argv[0]);
453         exits("not found");
454         return -1;              // suppress compiler warnings
455 }
456
457 /* xsubshell - execute (cmd) */
458 int
459 xsubshell(Node *np)
460 {
461         int pid;
462
463         if(pid = fork()){
464                 if(pid == -1)
465                         error("can't create process", "");
466                 return pid;
467         }
468         redirect(np);   /* child process */
469         execute(np->args[0]);
470         exits(status);
471         return -1;              // suppress compiler warnings
472 }
473
474 /* xnowait - execute cmd & */
475 int
476 xnowait(Node *np)
477 {
478         int pid;
479
480         execute(np->args[0]);
481         pid = execute(np->args[1]);
482         if(interactive)
483                 fprint(2, "%d\n", pid);
484         return 0;
485 }
486
487 /* xwait - execute cmd ; */
488 int xwait(Node *np)
489 {
490         int pid;
491         Waitmsg *wmsg;
492
493         execute(np->args[0]);
494         pid = execute(np->args[1]);
495         if(pid > 0){
496                 while((wmsg = wait()) != nil){
497                         if(wmsg->pid == pid)
498                                 break;
499                         free(wmsg);
500                 }
501                 if(wmsg == nil)
502                         error("wait error", "");
503                 else {
504                         strncpy(status, wmsg->msg, sizeof(status)-1);
505                         free(wmsg);
506                 }
507         }
508         return 0;
509 }