xref: /original-bsd/local/toolchest/ksh/sh/cmd.c (revision f1324ba5)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
640 static void	chkword()
641 {
642 	if(word())
643 		synbad();
644 }
645 
646 /*
647  * see if this token is syntactically correct
648  */
649 
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 
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 
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 
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 
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