1 /*
2
3 * Copyright (c) 1984, 1985, 1986 AT&T
4 * All Rights Reserved
5
6 * THIS IS UNPUBLISHED PROPRIETARY SOURCE
7 * CODE OF AT&T.
8 * The copyright notice above does not
9 * evidence any actual or intended
10 * publication of such source code.
11
12 */
13 /* @(#)cmd.c 1.1 */
14 /*
15 * UNIX shell
16 *
17 * S. R. Bourne
18 * Rewritten by David Korn
19 * AT&T Bell Laboratories
20 *
21 */
22
23 #include "defs.h"
24 #include "sym.h"
25 #include "flags.h"
26 #include "name.h"
27 #include "io.h"
28 #include "history.h"
29 #include "mode.h"
30 #include "stak.h"
31 #include "shtype.h"
32 #include "brkincr.h"
33 #include "builtins.h"
34
35
36 /* These routines are defined by this module */
37 void synbad();
38 TREPTR cmd();
39 TREPTR makefork();
40
41 /* These routines are referenced by this module */
42 extern void addblok();
43 extern void chkpr();
44 extern void exitsh();
45 extern void free();
46 extern STKPTR getstak();
47 extern void hist_cancel();
48 extern void hist_flush();
49 extern long hist_position();
50 extern char *malloc();
51 extern char *movstr();
52 extern void p_setout();
53 extern void p_str();
54 extern void p_prp();
55 extern void p_num();
56
57 static TREPTR makelist();
58 static ARGPTR qscan();
59 static IOPTR inout();
60 static void chkword();
61 static void chkflags();
62 static void chksym();
63 static TREPTR term();
64 static TREPTR list();
65 static REGPTR syncase();
66 static TREPTR item();
67 static int skipnl();
68 static void prsym();
69
70
71 static int heredoc;
72
73 /*
74 * ======== command line decoding ========
75 *
76 * This is the parser for a shell command line
77 */
78
79
80
81
82 /*
83 * Make a node which will cause the shell to fork
84 */
85
makefork(flgs,i)86 TREPTR makefork(flgs, i)
87 int flgs;
88 TREPTR i;
89 {
90 register FORKPTR t;
91 t=(FORKPTR) getstak(FORKTYPE);
92 t->forktyp = flgs|TFORK;
93 t->forktre = i;
94 t->forkio = 0;
95 return((TREPTR)t);
96 }
97
98 /*
99 * Make a node corresponding to a command list
100 */
101
makelist(type,i,r)102 static TREPTR makelist(type,i,r)
103 int type;
104 TREPTR i, r;
105 {
106 register LSTPTR t;
107 if(i==0 || r==0)
108 synbad();
109 else
110 {
111 t = (LSTPTR) getstak(LSTTYPE);
112 t->lsttyp = type;
113 t->lstlef = i;
114 t->lstrit = r;
115 }
116 return((TREPTR)t);
117 }
118
119 /*
120 * cmd
121 * empty
122 * list
123 * list & [ cmd ]
124 * list [ ; cmd ]
125 */
126
cmd(sym,flg)127 TREPTR cmd(sym,flg)
128 register int sym;
129 int flg;
130 {
131 register int flag = FINT|FPRS|FAMP;
132 register TREPTR i, e;
133 IOPTR saviotemp = iotemp;
134 /* parser output goes on standard error */
135 p_setout(stderr);
136 i = list(flg);
137 if(wdval==NL)
138 {
139 if(flg&NLFLG)
140 {
141 wdval=';';
142 chkpr(0);
143 }
144 }
145 else if(i==0 && (flg&MTFLG)==0)
146 synbad();
147 switch(wdval)
148 {
149 case COOPSYM: /* set up a cooperating process */
150 flag |= FPIN|FPOU;
151 case '&':
152 if(i)
153 {
154 if(saviotemp!=iotemp || heredoc)
155 flag |= FTMP;
156 i = (TREPTR)makefork(flag, i);
157 }
158 else
159 synbad();
160
161 case ';':
162 if(e=cmd(sym,flg|MTFLG))
163 i=(TREPTR)makelist(TLST, i, e);
164 break;
165
166 case EOFSYM:
167 if(sym==NL)
168 break;
169
170 default:
171 if(sym)
172 chksym(sym);
173
174 }
175 /* restore output stream */
176 return(i);
177 }
178
179 /*
180 * list
181 * term
182 * list && term
183 * list || term
184 */
185
list(flg)186 static TREPTR list(flg)
187 {
188 register TREPTR r;
189 register int b;
190 r = term(flg);
191 while(r && ((b=(wdval==ANDFSYM)) || wdval==ORFSYM))
192 {
193 r = makelist((b ? TAND : TORF), r, term(NLFLG));
194 }
195 return(r);
196 }
197
198 /*
199 * term
200 * item
201 * item |^ term
202 */
203
term(flg)204 static TREPTR term(flg)
205 register int flg;
206 {
207 register TREPTR t;
208 register PARPTR p = NULL;
209 heredoc = 0;
210 reserv++;
211 if(flg&NLFLG)
212 skipnl();
213 else
214 word();
215 /* check to see if pipeline is to be timed */
216 if(wdval==TIMSYM)
217 {
218 p=(PARPTR) getstak(PARTYPE);
219 p->partyp=TTIME;
220 reserv++;
221 word();
222 }
223 if((t=item(NLFLG|MTFLG)) && wdval=='|')
224 {
225 flg = heredoc|FPOU;
226 t=makelist(TFIL,makefork(flg,t),makefork(FPIN|FPCL,term(NLFLG)));
227 }
228 if(p)
229 {
230 p->partre= t;
231 return((TREPTR)p);
232 }
233 else
234 return(t);
235 }
236
237 /*
238 * case statement
239 */
240
syncase(esym)241 static REGPTR syncase(esym)
242 register int esym;
243 {
244 wdset |= E_FLAG; /* set to avoid aliasing expressions */
245 skipnl();
246 if(wdval==esym)
247 {
248 wdset &= ~E_FLAG;
249 return(0);
250 }
251 else
252 {
253 register REGPTR r=(REGPTR) getstak(REGTYPE);
254 r->regptr=0;
255 while(1)
256 {
257 chkflags(wdarg,1);
258 wdarg->argnxt=r->regptr;
259 r->regptr=wdarg;
260 if(wdval==')' || wdval=='|' || ( word()!=')' && wdval!='|' ))
261 synbad();
262 if(wdval=='|')
263 word();
264 else
265 break;
266 }
267 wdset &= ~E_FLAG;
268 r->regcom=cmd(0,NLFLG|MTFLG);
269 if(wdval==ECSYM)
270 r->regnxt=syncase(esym);
271 else
272 {
273 chksym(esym);
274 r->regnxt=0;
275 }
276 return(r);
277 }
278 }
279
280 /*
281 * item
282 *
283 * ( cmd ) [ < in ] [ > out ]
284 * word word* [ < in ] [ > out ]
285 * if ... then ... else ... fi
286 * for ... while ... do ... done
287 * case ... in ... esac
288 * begin ... end
289 */
290
item(flag)291 static TREPTR item(flag)
292 BOOL flag;
293 {
294 register TREPTR t;
295 register IOPTR io;
296 if(flag)
297 io=inout((IOPTR)0,1);
298 else
299 io=0;
300 switch(wdval)
301 {
302 /* case statement */
303 case CASYM:
304 {
305 t=(TREPTR) getstak(SWTYPE);
306 chkword();
307 ((SWPTR) t)->swarg=wdarg->argval;
308 skipnl();
309 chksym(INSYM|BRSYM);
310 ((SWPTR) t)->swlst=syncase(wdval==INSYM?ESSYM:KTSYM);
311 ((SWPTR) t)->swtyp=TSW;
312 break;
313 }
314
315 /* if statement */
316 case IFSYM:
317 {
318 register int w;
319 t=(TREPTR) getstak(IFTYPE);
320 ((IFPTR) t)->iftyp=TIF;
321 ((IFPTR) t)->iftre=cmd(THSYM,NLFLG);
322 ((IFPTR) t)->thtre=cmd(ELSYM|FISYM|EFSYM,NLFLG);
323 ((IFPTR) t)->eltre=((w=wdval)==ELSYM?cmd(FISYM,NLFLG):
324 (w==EFSYM?(wdval=IFSYM, item(0)):0));
325 if(w==EFSYM)
326 return(t);
327 break;
328 }
329
330 /* for and select statement */
331 case FORSYM:
332 case SELSYM:
333 {
334 t=(TREPTR) getstak(FORTYPE);
335 ((FORPTR) t)->fortyp=(wdval==FORSYM?TFOR:TSELECT);
336 ((FORPTR) t)->forlst=0;
337 chkword();
338 ((FORPTR) t)->fornam=(char*) wdarg->argval;
339 if(skipnl()==INSYM)
340 {
341 chkword();
342 ((FORPTR) t)->forlst=(COMPTR) item(0);
343 if(wdval!=NL && wdval!=';')
344 synbad();
345 if(wdval==NL)
346 chkpr(0);
347 skipnl();
348 }
349 /* 'for i;do cmd' is valid syntax */
350 else if(wdval==';')
351 {
352 reserv = 1;
353 word();
354 }
355 chksym(DOSYM|BRSYM);
356 ((FORPTR) t)->fortre=cmd(wdval==DOSYM?ODSYM:KTSYM,NLFLG);
357 break;
358 }
359
360 /* This is the code for parsing function definitions */
361 case PROCSYM:
362 funct_5_2:
363 {
364 TREPTR cmdptr;
365 BLKPTR blokptr;
366 int savstates = states;
367 int saveline = firstline;
368 register FILE *fd = NULL;
369 IOPTR saviotemp = iotemp;
370 t=(TREPTR) getstak(PROCTYPE);
371 ((PROCPTR) t)->proctyp=TPROC;
372 ((PROCPTR) t)->procloc = -1;
373 firstline = standin->flin;
374 if(wdval == PROCSYM)
375 chkword();
376 ((PROCPTR) t)->procnam=(char *) wdarg->argval;
377 skipnl();
378 chksym(BRSYM);
379 /* force a new stak frame to compile the command */
380 addblok(-1);
381 if(is_option(INTFLG))
382 {
383 /* just in case history file not open yet */
384 hist_open();
385 if(fc_fix)
386 {
387 fd = fc_fix->fixfd;
388 states |= FIXFLG;
389 ((PROCPTR)t)->procloc =
390 hist_position(fc_fix->fixind) +
391 fd->_ptr - fd->_base;
392 }
393 }
394 cmdptr = cmd(KTSYM,NLFLG);
395 /* force another stak frame to save the command */
396 addblok(-1);
397 blokptr = stakbsy;
398 stakbsy = stakbsy->word;
399 /* save the entry point in block */
400 blokptr->word = BLK(cmdptr);
401 ((PROCPTR) t)->proctre = blokptr;
402 if(iotemp != saviotemp)
403 {
404 iotemp = saviotemp;
405 states |= RM_TMP;
406 }
407 if(fd && (savstates&FIXFLG)==0)
408 {
409 hist_flush();
410 hist_cancel();
411 states &= ~FIXFLG;
412 }
413 firstline = saveline;
414 break;
415 }
416
417 /* while and until */
418 case WHSYM:
419 case UNSYM:
420 {
421 t=(TREPTR) getstak(WHTYPE);
422 ((WHPTR) t)->whtyp=(wdval==WHSYM ? TWH : TUN);
423 ((WHPTR) t)->whtre = cmd(DOSYM,NLFLG);
424 ((WHPTR) t)->dotre = cmd(ODSYM,NLFLG);
425 break;
426 }
427
428 /* command group with { */
429 case BRSYM:
430 t=cmd(KTSYM,NLFLG);
431 break;
432
433 case '(':
434 {
435 register PARPTR p;
436 p=(PARPTR) getstak(PARTYPE);
437 p->partre=cmd(')',NLFLG);
438 p->partyp=TPAR;
439 t=makefork(0,(TREPTR)p);
440 break;
441 }
442
443 default:
444 if(io==0)
445 return(0);
446
447 /* simple command */
448 case 0:
449 {
450 register ARGPTR argp;
451 register ARGPTR *argtail;
452 register ARGPTR *argset=0;
453 int keywd=KEYFLG;
454 int argno = 0;
455 int bltin = 0;
456 t=(TREPTR) getstak(COMTYPE);
457 ((COMPTR)t)->comio=io; /*initial io chain*/
458 /* set command line number for error messages */
459 ((COMPTR)t)->comline = (exec_flag?cmdline:
460 standin->flin-firstline-1);
461 argtail = &(((COMPTR)t)->comarg);
462 while(wdval==0)
463 {
464 argp = wdarg;
465 argp->argchn = 0;
466 /* test for keyword argument */
467 if(wdset&keywd)
468 {
469 chkflags(argp,0);
470 argp->argnxt=(ARGPTR) argset;
471 argset=(ARGPTR *) argp;
472 /* alias substitutions allowed */
473 wdset |= (KEYFLG|S_FLAG);
474 }
475 else
476 {
477 wdset = 0; /* don't hunt for aliases*/
478 chkflags(argp,1);
479 if((argp->argflag&A_RAW) == 0)
480 argno = -1;
481 if(argno>=0 && argno++==0)
482 {
483 /* check for builtin command */
484 bltin=syslook(argp->argval,commands);
485 }
486 *argtail = argp;
487 argtail = &(argp->argnxt);
488 wdset = keywd=is_option(KEYFLG);
489 }
490 #ifdef DEVFD
491 retry:
492 word();
493 if((wdval&STRIP)=='(')
494 {
495 TREPTR t;
496 int flag = (wdval==OPROC);
497 t = cmd(')',NLFLG|(argno==1&&wdval=='('?MTFLG:0));
498 if(t == NULL)
499 {
500 wdarg = argp;
501 goto funct_5_2;
502 }
503 argp = (ARGPTR)locstak();
504 argno = -1;
505 *argtail = argp;
506 argtail = &(argp->argnxt);
507 endstak(movstr(nullstr,argp->argval));
508 argp->argchn = (ARGPTR)makefork(flag?FPIN|FAMP|FPCL:FPOU,t);
509 argp->argflag = (A_EXP|flag);
510 goto retry;
511 }
512 #else
513 word();
514 if(argno==1 && argset==NULL && wdval== '(')
515 {
516 /* SVR2 style function */
517 word();
518 if(wdval == ')')
519 {
520 wdarg = argp;
521 goto funct_5_2;
522 }
523 wdval = '(';
524 }
525 #endif /* DEVFD */
526 if(flag)
527 {
528 if(io)
529 {
530 while(io->ionxt)
531 io = io->ionxt;
532 io->ionxt = inout((IOPTR)0,0);
533 }
534 else
535 ((COMPTR)t)->comio = io = inout((IOPTR)0,0);
536 }
537 }
538 *argtail = 0;
539 ((COMPTR)t)->comtyp = (TCOM|(bltin<<(COMBITS+1)));
540 /* expand argument list if possible */
541 if(argno>0)
542 ((COMPTR)t)->comarg = qscan(t,argno);
543 else if(((COMPTR)t)->comarg)
544 ((COMPTR)t)->comtyp |= COMSCAN;
545 ((COMPTR)t)->comset=(ARGPTR) argset;
546 wdset &= ~S_FLAG;
547 return(t);
548 }
549 }
550 reserv++;
551 word();
552 if(io=inout(io,0))
553 {
554 int type = t->tretyp&COMMSK;
555 t=makefork(0,t);
556 t->treio=io;
557 if(type != TFORK)
558 t->tretyp = TSETIO;
559 }
560 return(t);
561 }
562
563
564 /*
565 * skip past newlines but issue prompt if interactive
566 */
567
skipnl()568 static int skipnl()
569 {
570 while((reserv++, word()==NL))
571 chkpr(0);
572 return(wdval);
573 }
574
575 /*
576 * check for and process and i/o redirections
577 * if flag is set then an alias can be in the next word
578 */
579
inout(lastio,flag)580 static IOPTR inout(lastio,flag)
581 IOPTR lastio;
582 {
583 register int iof;
584 register IOPTR iop;
585 register int c;
586 iof=wdnum;
587 switch(wdval)
588 {
589 case DOCSYM: /* << */
590 iof |= IODOC;
591 heredoc = FTMP;
592 break;
593
594 case APPSYM: /* >> */
595 case '>':
596 if(wdnum==0)
597 iof |= 1;
598 iof |= IOPUT;
599 if(wdval==APPSYM)
600 {
601 iof |= IOAPP;
602 break;
603 }
604
605 case '<':
606 if((c=nextc())=='&')
607 iof |= IOMOV;
608 else if(c=='>')
609 /* <> is open for read and write */
610 /* unadvertised feature */
611 iof |= IORDW;
612 else
613 peekn=c|MARK;
614 break;
615
616 default:
617 return(lastio);
618 }
619 chkword();
620 iop=(IOPTR) getstak(IOTYPE);
621 iop->ioname=wdarg->argval;
622 iop->iofile=iof;
623 if(iof&IODOC)
624 {
625 iop->iolst=iopend;
626 iopend=iop;
627 }
628 word();
629 iop->ionxt=inout(lastio,0);
630 /* allow alias substitutions */
631 if(flag)
632 wdset |= S_FLAG;
633 return(iop);
634 }
635
636 /*
637 * get next token and make sure that it is not a keyword or meta-character
638 */
639
chkword()640 static void chkword()
641 {
642 if(word())
643 synbad();
644 }
645
646 /*
647 * see if this token is syntactically correct
648 */
649
chksym(sym)650 static void chksym(sym)
651 register int sym;
652 {
653 register int x = sym&wdval;
654 if(((x&SYMFLG) ? x : sym) != wdval)
655 synbad();
656 }
657
658 /*
659 * print the name of a syntactic token
660 */
661
prsym(sym)662 static void prsym(sym)
663 register int sym;
664 {
665 if(sym&SYMFLG)
666 {
667 register SYSPTR sp=reserved;
668 while(sp->sysval && sp->sysval!=sym)
669 sp++;
670 fputs(sp->sysnam,output);
671 }
672 else if(sym==EOFSYM)
673 fputs(endoffile,output);
674 else
675 {
676 if(sym&SYMREP)
677 putc(sym,output);
678 if(sym==NL)
679 fputs("newline or ;",output);
680 else
681 putc(sym,output);
682 }
683 putc('\'',output);
684 }
685
686 /*
687 * print a bad syntax message
688 */
689
synbad()690 void synbad()
691 {
692 register char *cp = unexpected;
693 register int w = wdval;
694 p_setout(stderr);
695 p_prp(synmsg,0);
696 if((states&TTYFLG)==0)
697 {
698 fputs(atline,output);
699 p_num((int)standin->flin,SP);
700 }
701 p_str(colon,'`');
702 if(w)
703 prsym(w);
704 else
705 p_str(wdarg->argval,'\'');
706 if((w&EOFSYM) && w!=EOFSYM)
707 cp = unmatched;
708 p_str(cp,NL);
709 hist_flush();
710 exitsh(SYNBAD);
711 }
712
713 /*
714 * check argument for possible optimizations
715 * in many cases we can skip macro and file name expansion
716 * The fexp flag is set when file expansion is possible
717 */
718
719 #define EXP_MACRO 2 /* macro expansion needed */
720 #define EXP_TRIM 4 /* quoted characters in string */
721 #define EXP_FILE 8 /* file expansion characters*/
722 #define EXP_QUOTE 16 /* string contains " character */
723
chkflags(argp,fexp)724 static void chkflags(argp,fexp)
725 register ARGPTR argp;
726 {
727 register int c;
728 argp->argflag = 0;
729 {
730 register int flag = 0;
731 char nquote = 0;
732 char *sp=argp->argval;
733 while(c= *sp++)
734 {
735 if(c==ESCAPE)
736 {
737 flag |= EXP_TRIM;
738 sp++;
739 }
740 else if(isexp(c))
741 {
742 if(c == '$' || c == '`')
743 {
744 flag |= EXP_MACRO;
745 if(c=='`')
746 subflag++;
747 }
748 else if(nquote==0)
749 {
750 /* special case of '[' */
751 if(*sp || c!='[')
752 flag |= EXP_FILE;
753 }
754 }
755 else if(c == '"')
756 {
757 /* toggle the quote count */
758 nquote = 1 - nquote;
759 flag |= EXP_QUOTE;
760 }
761 }
762 if(fexp==0)
763 flag &= ~EXP_FILE;
764 /* return if no macro expansion, file expansion or trimming required */
765 if(flag==0)
766 {
767 argp->argflag |= A_RAW;
768 return;
769 }
770 /* return if macro or command substitution needed */
771 if(flag&EXP_MACRO)
772 {
773 argp->argflag |= (A_MAC|A_EXP);
774 return;
775 }
776 /* check to see if file expansion is required */
777 if(flag&EXP_FILE)
778 {
779 argp->argflag|= A_EXP;
780 /* return if no quotes otherwise don't optimize */
781 if(flag&(EXP_QUOTE|EXP_TRIM))
782 {
783 argp->argflag= A_MAC;
784 return;
785 }
786 return;
787 }
788 argp->argflag |= A_RAW;
789 }
790 /* just get rid of quoting stuff and consider argument as expanded */
791 {
792 register char *dp,*sp;
793 char nquote = 0; /* set within quoted string */
794 dp = sp = argp->argval;
795 while(c= *sp++)
796 {
797 if(c != '"')
798 {
799 if(c==ESCAPE)
800 {
801 /* strip escchar's in double quotes */
802 c = *sp++;
803 if(nquote && !escchar(c) && c!='"')
804 *dp++ = ESCAPE;
805 }
806 *dp++ = c;
807 }
808 else /* toggle quote marker */
809 nquote = 1-nquote;
810 }
811 *dp = 0;
812 }
813 }
814
815 /*
816 * convert argument chain to argument list when no special arguments
817 */
818
qscan(ac,argn)819 static ARGPTR qscan(ac,argn)
820 COMPTR ac;
821 int argn;
822 {
823 register char **cp;
824 register ARGPTR ap;
825 register DOLPTR dp;
826 /* leave space for an extra argument at the front */
827 dp = (DOLPTR)getstak((unsigned)DOLTYPE + sizeof(char*) + argn*sizeof(char*));
828 cp = dp->dolarg+1;
829 dp->doluse = argn;
830 ap = ac->comarg;
831 while(ap)
832 {
833 *cp++ = ap->argval;
834 ap = ap->argnxt;
835 }
836 *cp = NULL;
837 return((ARGPTR)dp);
838 }
839
840