1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23
24 /*
25 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28
29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
31
32 #if defined(sun)
33 #pragma ident "@(#)cmd.c 1.17 08/01/29 SMI"
34 #endif
35
36 #include "defs.h"
37
38 /*
39 * Copyright 2008-2020 J. Schilling
40 *
41 * @(#)cmd.c 1.54 20/03/25 2008-2020 J. Schilling
42 */
43 #ifndef lint
44 static UConst char sccsid[] =
45 "@(#)cmd.c 1.54 20/03/25 2008-2020 J. Schilling";
46 #endif
47
48 /*
49 * UNIX shell
50 */
51
52 #include "sym.h"
53
54 static unsigned char *getstor __PR((int asize));
55 struct trenod *makefork __PR((int flgs, struct trenod *i));
56 static struct trenod *makelist __PR((int type, struct trenod *i,
57 struct trenod *r));
58 struct trenod *cmd __PR((int sym, int flg));
59 static struct trenod *list __PR((int flg));
60 static struct trenod *term __PR((int flg));
61 static struct regnod *syncase __PR((int esym));
62 static struct trenod *item __PR((BOOL flag));
63 static int skipnl __PR((int flag));
64 static struct ionod *inout __PR((struct ionod *lastio));
65 static void chkword __PR((void));
66 static void chksym __PR((int sym));
67 static void prsym __PR((int sym));
68 static void synbad __PR((void));
69
70 int abegin;
71
72 /* ======== storage allocation for functions ======== */
73
74 static unsigned char *
getstor(asize)75 getstor(asize)
76 int asize;
77 {
78 if (fndef)
79 return ((unsigned char *)alloc(asize));
80 else
81 return (getstak((Intptr_t)asize));
82 }
83
84
85 /* ======== command line decoding ======== */
86
87 struct trenod *
makefork(flgs,i)88 makefork(flgs, i)
89 int flgs;
90 struct trenod *i;
91 {
92 struct forknod *t;
93
94 t = (struct forknod *)getstor(sizeof (struct forknod));
95 t->forktyp = flgs|TFORK;
96 t->forktre = i;
97 t->forkio = 0;
98 return ((struct trenod *)t);
99 }
100
101 static struct trenod *
makelist(type,i,r)102 makelist(type, i, r)
103 int type;
104 struct trenod *i, *r;
105 {
106 struct lstnod *t = 0;
107
108 if (i == 0 || r == 0) {
109 synbad();
110 } else {
111 t = (struct lstnod *)getstor(sizeof (struct lstnod));
112 t->lsttyp = type;
113 t->lstlef = i;
114 t->lstrit = r;
115 }
116 return ((struct trenod *)t);
117 }
118
119 /*
120 * cmd
121 * empty (Only if called with MTFLG)
122 * list
123 * list & [ cmd ]
124 * list [ ; cmd ]
125 *
126 * This is the main parser entry point that is called as cmd(NL, MTFLG)
127 * from main.c::exfile(). MTFLG permits empty commands in the main loop.
128 *
129 * It cmd() is called with NLFLG in flg at top level, this causes the whole
130 * file to be read at once and a single treenode * to be constructed from that.
131 * This method is e.g. used by "eval" and ".".
132 */
133 struct trenod *
cmd(sym,flg)134 cmd(sym, flg)
135 int sym;
136 int flg;
137 {
138 struct trenod *i, *e;
139
140 wdset = 0;
141 i = list(flg);
142 if (wdval == NL) {
143 if (flg & NLFLG) {
144 wdval = ';';
145 chkpr();
146 }
147 } else if (i == 0 && (flg & MTFLG) == 0) {
148 synbad();
149 }
150 switch (wdval) {
151 case '&':
152 if (i)
153 i = makefork(FAMP, i);
154 else
155 synbad();
156 /* FALLTHROUGH */
157
158 case ';':
159 if ((e = cmd(sym, flg | MTFLG)) != NULL)
160 i = makelist(TLST, i, e);
161 else if (i == 0) {
162 synbad();
163 }
164 break;
165
166 case EOFSYM:
167 if (sym == NL)
168 break;
169 /* FALLTHROUGH */
170
171 default:
172 if (sym)
173 chksym(sym);
174 }
175 return (i);
176 }
177
178 /*
179 * list
180 * term
181 * list && term
182 * list || term
183 */
184 static struct trenod *
list(flg)185 list(flg)
186 int flg;
187 {
188 struct trenod *r;
189 int b;
190 r = term(flg);
191 while (r && ((b = (wdval == ANDFSYM)) != 0 || wdval == ORFSYM))
192 r = makelist((b ? TAND : TORF), r, term(NLFLG));
193 return (r);
194 }
195
196 /*
197 * term
198 * item
199 * item |^ term
200 */
201 static struct trenod *
term(flg)202 term(flg)
203 int flg;
204 {
205 struct trenod *t;
206
207 abegin = 1;
208 reserv++;
209 if (flg & NLFLG)
210 skipnl(flg);
211 else
212 word();
213
214 #if defined(DO_NOTSYM) || defined(DO_TIME)
215 #if defined(DO_NOTSYM) && !defined(DO_TIME)
216 if (wdval == NOTSYM) {
217 #else
218 #if defined(DO_TIME) && !defined(DO_NOTSYM)
219 if (wdval == TIMSYM) {
220 #else
221 if (wdval == NOTSYM || wdval == TIMSYM) {
222 #endif
223 #endif
224 struct parnod *p;
225
226 p = (struct parnod *)getstor(sizeof (struct parnod));
227 p->partyp = TTIME;
228 if (wdval == NOTSYM)
229 p->partyp = TNOT;
230 p->partre = term(0);
231 t = treptr(p);
232 } else
233 #endif
234
235 /*
236 * ^ is a relic from the days of UPPER CASE ONLY tty model 33s
237 */
238 if ((t = item(DOIOFLG | (flg&SEMIFLG))) != 0 &&
239 (wdval == '^' || wdval == '|')) {
240 struct trenod *left;
241 struct trenod *right;
242 struct trenod *tr;
243 int pio = wdnum & IOUFD;
244
245 if (wdnum == 0)
246 pio = STDOUT_FILENO;
247 left = makefork(FPOU|pio, t);
248 tr = term(NLFLG);
249 #if defined(DO_PIPE_SYNTAX_E) || defined(DO_PIPE_PARENT)
250 if (tr == NULL)
251 synbad();
252 #endif
253 #ifdef DO_PIPE_PARENT
254 /*
255 * Build a tree that allows us to make all pipe commands
256 * children from the main shell process.
257 *
258 * Special right nodes:
259 * - TFORK () Avoid to add another fork
260 * - TFIL Pipe to right pipe, avoid fork
261 */
262 switch (tr->tretyp & COMMSK) {
263 case TFORK:
264 tr->tretyp |= FPIN;
265 right = tr;
266 break;
267
268 case TFIL:
269 case TCOM:
270 right = makefork(FPIN, tr);
271 right->tretyp |= TNOFORK;
272 break;
273 default:
274 right = makefork(FPIN, tr);
275 }
276 if ((t->tretyp & COMMSK) == TCOM)
277 left->tretyp |= TNOFORK;
278 return (makelist(TFIL, left, right));
279 #else /* !DO_PIPE_PARENT */
280 right = makefork(FPIN, tr);
281 return (makefork(0, makelist(TFIL, left, right)));
282 #endif
283 }
284 return (t);
285 }
286
287 /*
288 * case statement, parse things after "case <word> in" here
289 */
290 static struct regnod *
syncase(esym)291 syncase(esym)
292 int esym;
293 {
294 #ifdef DO_POSIX_CASE
295 wdset |= IN_CASE;
296 #endif
297 skipnl(0);
298
299 #ifdef DO_POSIX_CASE
300 if (wdval == 0 &&
301 syslook(wdarg->argval,
302 reserved, no_reserved) == esym) {
303 wdval = esym;
304 }
305 #endif
306
307 if (wdval == esym) {
308 wdset &= ~IN_CASE;
309 return (0);
310 } else {
311 struct regnod *r =
312 (struct regnod *)getstor(sizeof (struct regnod));
313 struct argnod *argp;
314
315 r->regptr = 0;
316 r->regflag = 0;
317 #ifdef DO_POSIX_CASE
318 if (wdval == '(')
319 skipnl(0);
320 #endif
321 for (;;) {
322 if (fndef) {
323 unsigned char *p;
324
325 argp = wdarg;
326 wdarg = (struct argnod *)
327 alloc(length(argp->argval) +
328 1 + BYTESPERWORD);
329 p = movstr(argp->argval, wdarg->argval);
330 /*
331 * Hack: A second Nul byte is needed if the
332 * word ends in "\\\0".
333 */
334 *++p = '\0';
335 }
336
337 wdarg->argnxt = r->regptr;
338 r->regptr = wdarg;
339
340 /* 'in' is not a reserved word in this case */
341 if (wdval == INSYM) {
342 wdval = 0;
343 }
344 if (wdval || (word() != ')' && wdval != '|'))
345 synbad();
346 if (wdval == '|')
347 word();
348 else
349 break;
350 }
351 wdset &= ~IN_CASE;
352 r->regcom = cmd(0, NLFLG | MTFLG);
353 if (wdval == ECSYM) { /* ;; */
354 r->regnxt = syncase(esym);
355 #ifdef DO_FALLTHR_CASE
356 } else if (wdval == ECASYM) { /* ;& */
357 r->regnxt = syncase(esym);
358 r->regflag = 1;
359 } else if (wdval == ECARSYM) { /* ;;& */
360 r->regnxt = syncase(esym);
361 r->regflag = 2;
362 #endif
363 } else {
364 chksym(esym);
365 r->regnxt = 0;
366 }
367 return (r);
368 }
369 }
370
371 /*
372 * item
373 *
374 * ( cmd ) [ < in ] [ > out ]
375 * word word* [ < in ] [ > out ]
376 * if ... then ... else ... fi
377 * for ... while ... do ... done
378 * case ... in ... esac
379 * begin ... end
380 */
381 #ifdef PROTOTYPES
382 static struct trenod *
item(BOOL flag)383 item(BOOL flag)
384 #else
385 static struct trenod *
386 item(flag)
387 BOOL flag;
388 #endif
389 {
390 struct trenod *r;
391 struct ionod *io;
392
393 if (flag & DOIOFLG)
394 io = inout((struct ionod *)0);
395 else
396 io = 0;
397 abegin--;
398 switch (wdval) {
399 case CASYM:
400 {
401 struct swnod *t;
402
403 t = (struct swnod *)getstor(sizeof (struct swnod));
404 r = (struct trenod *)t;
405
406 chkword();
407 if (fndef)
408 t->swarg = make(wdarg->argval);
409 else
410 t->swarg = wdarg->argval;
411 skipnl(0);
412 chksym(INSYM | BRSYM);
413 t->swlst = syncase(wdval == INSYM ? ESSYM : KTSYM);
414 t->swtyp = TSW;
415 break;
416 }
417
418 case IFSYM:
419 {
420 int w;
421 struct ifnod *t;
422 struct trenod *tt = NULL; /* Make silly gcc quiet */
423
424 t = (struct ifnod *)getstor(sizeof (struct ifnod));
425 r = (struct trenod *)t;
426
427 t->iftyp = TIF;
428 t->iftre = cmd(THSYM, NLFLG);
429 t->thtre = cmd(ELSYM | FISYM | EFSYM, NLFLG);
430 t->eltre = ((w = wdval) == ELSYM ?
431 cmd(FISYM, NLFLG) :
432 (w == EFSYM ?
433 (wdval = IFSYM, tt = item(0)) :
434 0));
435 if (w == EFSYM) {
436 #ifdef DO_SETIO_NOFORK
437 if (tt->tretyp != TSETIO)
438 return (r);
439 /*
440 * Let post-command IO redirection apply to the
441 * whole command and not only to the else part.
442 */
443 t->eltre = forkptr(tt)->forktre;
444 forkptr(tt)->forktre = (struct trenod *)t;
445 r = tt;
446 #endif
447 return (r);
448 }
449 break;
450 }
451
452 case FORSYM:
453 case SELSYM:
454 {
455 struct fornod *t;
456
457 t = (struct fornod *)getstor(sizeof (struct fornod));
458 r = (struct trenod *)t;
459
460 t->fortyp = wdval == FORSYM ? TFOR : TSELECT;
461 t->forlst = 0;
462 chkword();
463 if (fndef)
464 t->fornam = make(wdarg->argval);
465 else
466 t->fornam = wdarg->argval;
467 if (skipnl(SEMIFLG) == INSYM) {
468 #ifdef DO_POSIX_FOR
469 if (word()) {
470 /*
471 * "for i in; do cmd ...; done" is valid
472 */
473 if (wdval != NL && wdval != ';')
474 synbad();
475 t->forlst = (struct comnod *)\
476 getstor(sizeof (struct comnod));
477 t->forlst->comtyp = TCOM;
478 t->forlst->comio = NULL;
479 t->forlst->comarg = NULL;
480 t->forlst->comset = NULL;
481 #else
482 if (word()) {
483 synbad();
484 #endif
485 } else {
486 nohash++;
487 t->forlst = (struct comnod *)item(0);
488 nohash--;
489 }
490
491 if (wdval != NL && wdval != ';')
492 synbad();
493 if (wdval == NL)
494 chkpr();
495 skipnl(0);
496 #ifdef DO_POSIX_FOR
497 } else if (wdval == ';') {
498 /*
499 * "for i; do cmd ...; done" is valid syntax
500 * see Austin bug #581
501 */
502 skipnl(0);
503 #endif
504 }
505 chksym(DOSYM | BRSYM);
506 t->fortre = cmd(wdval == DOSYM ? ODSYM : KTSYM, NLFLG);
507 break;
508 }
509
510 case WHSYM:
511 case UNSYM:
512 {
513 struct whnod *t;
514
515 t = (struct whnod *)getstor(sizeof (struct whnod));
516 r = (struct trenod *)t;
517
518 t->whtyp = (wdval == WHSYM ? TWH : TUN);
519 t->whtre = cmd(DOSYM, NLFLG);
520 t->dotre = cmd(ODSYM, NLFLG);
521 break;
522 }
523
524 case BRSYM:
525 r = cmd(KTSYM, NLFLG);
526 break;
527
528 case '(':
529 {
530 struct parnod *p;
531
532 p = (struct parnod *)getstor(sizeof (struct parnod));
533 p->partre = cmd(')', NLFLG);
534 p->partyp = TPAR;
535 r = makefork(0, (struct trenod *)p);
536 break;
537 }
538
539 default:
540 if (io == 0)
541 return (0);
542 /* FALLTHROUGH */
543
544 #ifdef DO_EMPTY_SEMI
545 case ';':
546 if (io == 0) {
547 if (!(flag&SEMIFLG))
548 return (0);
549
550 if (word() == ';')
551 synbad();
552 else if (wdval != 0)
553 return (item(flag));
554 }
555 /* FALLTHROUGH */
556 #endif
557 case 0:
558 {
559 struct comnod *t;
560 struct argnod *argp;
561 struct argnod **argtail;
562 struct argnod *argset = 0;
563 #ifndef ARGS_RIGHT_TO_LEFT
564 struct argnod **argstail = &argset;
565 #endif
566 int keywd = 1;
567 unsigned char *com;
568
569 if ((wdval != NL) && ((peekn = skipwc()) == '(')) {
570 struct fndnod *f;
571 struct ionod *saveio;
572
573 saveio = iotemp;
574 peekn = 0;
575 if (skipwc() != ')')
576 synbad();
577
578 /*
579 * We increase fndef before calling getstor(),
580 * so that getstor() uses malloc to allocate
581 * memory instead of stack. This is necessary
582 * since fndnod will be hung on np->funcval,
583 * which persists over command executions.
584 */
585 fndef++;
586 f = (struct fndnod *)
587 getstor(sizeof (struct fndnod));
588 r = (struct trenod *)f;
589
590 f->fndtyp = TFND;
591 if (fndef)
592 f->fndnam = make(wdarg->argval);
593 else
594 f->fndnam = wdarg->argval;
595 f->fndref = 0;
596 reserv++;
597 skipnl(0);
598 f->fndval = (struct trenod *)item(0);
599 fndef--;
600
601 if (iotemp != saveio) {
602 struct ionod *ioptr = iotemp;
603
604 while (ioptr->iolst != saveio)
605 ioptr = ioptr->iolst;
606
607 ioptr->iolst = fiotemp;
608 fiotemp = iotemp;
609 iotemp = saveio;
610 }
611 return (r);
612 } else {
613 int envbeg = 0;
614
615 t = (struct comnod *)
616 getstor(sizeof (struct comnod));
617 r = (struct trenod *)t;
618
619 t->comio = io; /* initial io chain */
620 argtail = &(t->comarg);
621
622 while (wdval == 0) {
623 if (fndef) {
624 unsigned char *p;
625
626 argp = wdarg;
627 wdarg = (struct argnod *)
628 alloc(length(argp->argval) +
629 1 + BYTESPERWORD);
630 p = movstr(argp->argval,
631 wdarg->argval);
632 /*
633 * Hack: A second Nul byte is
634 * needed if the word ends in
635 * "\\\0".
636 */
637 *++p = '\0';
638 }
639
640 argp = wdarg;
641 if (wdset && keywd) {
642 /*
643 * Revert the effect of abegin--
644 * at the begin of this function
645 * in case that we are in a list
646 * of env= definitions.
647 */
648 if (abegin == 0) {
649 abegin++;
650 envbeg++;
651 }
652 #ifdef ARGS_RIGHT_TO_LEFT /* old order: var2=val2 var1=val1 */
653 argp->argnxt = argset;
654 argset = argp;
655 #else
656 argp->argnxt =
657 (struct argnod *)0;
658 *argstail = argp;
659 argstail = &argp->argnxt;
660 #endif
661 } else {
662 /*
663 * If we had env= definitions,
664 * make sure to decrement abegin
665 * To disable begin alias
666 * expansions.
667 */
668 if (abegin > 0 && envbeg)
669 abegin--;
670 *argtail = argp;
671 argtail = &(argp->argnxt);
672 keywd = flags & keyflg;
673 }
674 word();
675 if (flag) {
676 if (io) {
677 while (io->ionxt)
678 io = io->ionxt;
679 io->ionxt = inout(
680 (struct ionod *)0);
681 } else {
682 t->comio = io = inout(
683 (struct ionod *)0);
684 }
685 }
686 }
687
688 t->comtyp = TCOM;
689 t->comset = argset;
690 *argtail = 0;
691
692 if (nohash == 0 &&
693 (fndef == 0 || (flags & hashflg))) {
694 if (t->comarg) {
695 com = t->comarg->argval;
696 if (*com && *com != DOLLAR) {
697 pathlook(com, 0,
698 t->comset);
699 }
700 }
701 }
702
703 return (r);
704 }
705 }
706
707 }
708 reserv++;
709 word();
710 if ((io = inout(io)) != NULL) {
711 #ifdef DO_SETIO_NOFORK
712 int type = r->tretyp & COMMSK;
713 #endif
714 r = makefork(0, r);
715 r->treio = io;
716 #ifdef DO_SETIO_NOFORK
717 if (type != TFORK)
718 r->tretyp = TSETIO;
719 #endif
720 }
721 return (r);
722 }
723
724 /* ARGSUSED */
725 static int
skipnl(flag)726 skipnl(flag)
727 int flag;
728 {
729 while ((reserv++, word() == NL))
730 chkpr();
731 #ifdef DO_PIPE_SEMI_SYNTAX_E
732 if (!(flag&SEMIFLG) && wdval == ';')
733 synbad();
734 #endif
735 return (wdval);
736 }
737
738 static struct ionod *
inout(lastio)739 inout(lastio)
740 struct ionod *lastio;
741 {
742 int iof;
743 struct ionod *iop;
744 unsigned int c;
745 int obegin = abegin;
746
747 iof = wdnum;
748 switch (wdval) {
749 case DOCSYM: /* << */
750 iof |= IODOC|IODOC_SUBST;
751 break;
752
753 case APPSYM: /* >> */
754 case '>':
755 if (wdnum == 0)
756 iof |= 1;
757 iof |= IOPUT;
758 if (wdval == APPSYM) {
759 iof |= IOAPP;
760 break;
761 }
762 #ifdef DO_NOCLOBBER
763 else {
764 if ((c = nextwc()) == '|')
765 iof |= IOCLOB;
766 else
767 peekn = c | MARK;
768 }
769 #endif
770 /* FALLTHROUGH */
771
772 case '<':
773 if ((c = nextwc()) == '&')
774 iof |= IOMOV;
775 else if (c == '>')
776 iof |= IORDW;
777 else
778 peekn = c | MARK;
779 break;
780
781 default:
782 return (lastio);
783 }
784
785 abegin = 0;
786 chkword();
787 abegin = obegin;
788 iop = (struct ionod *)getstor(sizeof (struct ionod));
789
790 if (fndef)
791 iop->ioname = (char *)make(wdarg->argval);
792 else
793 iop->ioname = (char *)(wdarg->argval);
794
795 iop->iolink = 0;
796 iop->iofile = iof;
797 if (iof & IODOC) {
798 iop->iolst = iopend;
799 iopend = iop;
800 }
801 word();
802 iop->ionxt = inout(lastio);
803 return (iop);
804 }
805
806 static void
chkword()807 chkword()
808 {
809 if (word())
810 synbad();
811 }
812
813 static void
chksym(sym)814 chksym(sym)
815 int sym;
816 {
817 int x = sym & wdval;
818
819 if (((x & SYMFLG) ? x : sym) != wdval)
820 synbad();
821 }
822
823 static void
prsym(sym)824 prsym(sym)
825 int sym;
826 {
827 if (sym & SYMFLG) {
828 const struct sysnod *sp = reserved;
829
830 while (sp->sysval && sp->sysval != sym)
831 sp++;
832 prs((unsigned char *)sp->sysnam);
833 } else if (sym == EOFSYM) {
834 prs(_gettext(endoffile));
835 } else {
836 if (sym & SYMREP)
837 prc(sym);
838 if (sym == NL)
839 prs(_gettext(nlorsemi));
840 else
841 prc(sym);
842 }
843 }
844
845 static void
synbad()846 synbad()
847 {
848 prp();
849 prs(_gettext(synmsg));
850 if ((flags & ttyflg) == 0) {
851 prs(_gettext(atline));
852 prn(standin->flin);
853 }
854 prs((unsigned char *)colon);
855 prc(LQ);
856 if (wdval)
857 prsym(wdval);
858 else
859 prs_cntl(wdarg->argval);
860 prc(RQ);
861 prs(_gettext(unexpected));
862 newline();
863 exitsh(SYNBAD);
864 }
865