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