xref: /illumos-gate/usr/src/cmd/csh/sh.lex.c (revision b7daf79982d77b491ef9662483cd4549e0e5da9a)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved.  The Berkeley Software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #include <unistd.h>	/* for lseek prototype */
18 #include "sh.h"
19 #include "sh.tconst.h"
20 #include <sys/filio.h>
21 #include <sys/ttold.h>
22 #define	RAW 	O_RAW
23 /*
24  * C shell
25  */
26 
27 /*
28  * These lexical routines read input and form lists of words.
29  * There is some involved processing here, because of the complications
30  * of input buffering, and especially because of history substitution.
31  */
32 
33 tchar	*word(void);
34 tchar	getC1(int);
35 tchar	*subword(tchar *, int, bool *);
36 void	getdol(void);
37 void	addla(tchar *);
38 void	getexcl(tchar);
39 void	noev(tchar *);
40 void	setexclp(tchar *);
41 void	unreadc(tchar);
42 int	readc(bool);
43 struct wordent	*dosub(int, struct wordent *, bool);
44 struct Hist	*findev(tchar *, bool);
45 struct wordent	*gethent(int);
46 struct wordent	*getsub(struct wordent *);
47 
48 /*
49  * Peekc is a peek characer for getC, peekread for readc.
50  * There is a subtlety here in many places... history routines
51  * will read ahead and then insert stuff into the input stream.
52  * If they push back a character then they must push it behind
53  * the text substituted by the history substitution.  On the other
54  * hand in several places we need 2 peek characters.  To make this
55  * all work, the history routines read with getC, and make use both
56  * of ungetC and unreadc.  The key observation is that the state
57  * of getC at the call of a history reference is such that calls
58  * to getC from the history routines will always yield calls of
59  * readc, unless this peeking is involved.  That is to say that during
60  * getexcl the variables lap, exclp, and exclnxt are all zero.
61  *
62  * Getdol invokes history substitution, hence the extra peek, peekd,
63  * which it can ungetD to be before history substitutions.
64  */
65 tchar peekc, peekd;
66 tchar peekread;
67 
68 tchar *exclp;			/* (Tail of) current word from ! subst */
69 struct	wordent *exclnxt;	/* The rest of the ! subst words */
70 int	exclc;			/* Count of remainig words in ! subst */
71 tchar *alvecp;		/* "Globp" for alias resubstitution */
72 
73 /*
74  * Lex returns to its caller not only a wordlist (as a "var" parameter)
75  * but also whether a history substitution occurred.  This is used in
76  * the main (process) routine to determine whether to echo, and also
77  * when called by the alias routine to determine whether to keep the
78  * argument list.
79  */
80 bool	hadhist;
81 
82 tchar getCtmp;
83 #define	getC(f)		((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
84 #define	ungetC(c)	peekc = c
85 #define	ungetD(c)	peekd = c
86 
87 bool
88 lex(struct wordent *hp)
89 {
90 	struct wordent *wdp;
91 	int c;
92 
93 #ifdef TRACE
94 	tprintf("TRACE- lex()\n");
95 #endif
96 	lineloc = btell();
97 	hp->next = hp->prev = hp;
98 	hp->word = S_ /* "" */;
99 	alvecp = 0, hadhist = 0;
100 	do
101 		c = readc(0);
102 	while (issp(c));
103 	/* make sure history is enabled */
104 	if (HIST && c == HISTSUB && intty)
105 		/* ^lef^rit	from tty is short !:s^lef^rit */
106 		getexcl(c);
107 	else
108 		unreadc(c);
109 	wdp = hp;
110 	/*
111 	 * The following loop is written so that the links needed
112 	 * by freelex will be ready and rarin to go even if it is
113 	 * interrupted.
114 	 */
115 	do {
116 		struct wordent *new = (struct wordent *)xalloc(sizeof *wdp);
117 
118 		new->word = 0;
119 		new->prev = wdp;
120 		new->next = hp;
121 		wdp->next = new;
122 		wdp = new;
123 		wdp->word = word();
124 	} while (wdp->word[0] != '\n');
125 #ifdef TRACE
126 	tprintf("Exiting lex()\n");
127 #endif
128 	hp->prev = wdp;
129 	return (hadhist);
130 }
131 
132 void
133 prlex(struct wordent *sp0)
134 {
135 	struct wordent *sp = sp0->next;
136 
137 #ifdef TRACE
138 	tprintf("TRACE- prlex()\n");
139 #endif
140 	for (;;) {
141 		printf("%t", sp->word);
142 		sp = sp->next;
143 		if (sp == sp0)
144 			break;
145 		if (sp->word[0] != '\n')
146 			Putchar(' ');
147 	}
148 }
149 
150 void
151 copylex(struct wordent *hp, struct wordent *fp)
152 {
153 	struct wordent *wdp;
154 
155 #ifdef TRACE
156 	tprintf("TRACE- copylex()\n");
157 #endif
158 	wdp = hp;
159 	fp = fp->next;
160 	do {
161 		struct wordent *new = (struct wordent *)xalloc(sizeof *wdp);
162 
163 		new->prev = wdp;
164 		new->next = hp;
165 		wdp->next = new;
166 		wdp = new;
167 		wdp->word = savestr(fp->word);
168 		fp = fp->next;
169 	} while (wdp->word[0] != '\n');
170 	hp->prev = wdp;
171 }
172 
173 void
174 freelex(struct wordent *vp)
175 {
176 	struct wordent *fp;
177 
178 #ifdef TRACE
179 	tprintf("TRACE- freelex()\n");
180 #endif
181 	while (vp->next != vp) {
182 		fp = vp->next;
183 		vp->next = fp->next;
184 		xfree(fp->word);
185 		xfree(fp);
186 	}
187 	vp->prev = vp;
188 }
189 
190 tchar *
191 word(void)
192 {
193 	tchar c, c1;
194 	tchar *wp;
195 	tchar wbuf[BUFSIZ];
196 	bool dolflg;
197 	int i;
198 
199 #ifdef TRACE
200 	tprintf("TRACE- word()\n");
201 #endif
202 	wp = wbuf;
203 	i = BUFSIZ - 4;
204 loop:
205 	while (issp(c = getC(DOALL)))
206 		;
207 	if (cmap(c, _META|_ESC)||isauxsp(c))
208 		switch (c) {
209 		case '&':
210 		case '|':
211 		case '<':
212 		case '>':
213 			*wp++ = c;
214 			c1 = getC(DOALL);
215 			if (c1 == c)
216 				*wp++ = c1;
217 			else
218 				ungetC(c1);
219 			goto ret;
220 
221 		case '#':
222 			if (intty)
223 				break;
224 			c = 0;
225 			do {
226 				c1 = c;
227 				c = getC(0);
228 			} while (c != '\n');
229 			if (c1 == '\\')
230 				goto loop;
231 			/* fall into ... */
232 
233 		case ';':
234 		case '(':
235 		case ')':
236 		case '\n':
237 			*wp++ = c;
238 			goto ret;
239 
240 		case '\\':
241 			c = getC(0);
242 			if (c == '\n') {
243 				if (onelflg == 1)
244 					onelflg = 2;
245 				goto loop;
246 			}
247 			if (c != HIST)
248 				*wp++ = '\\', --i;
249 			c |= QUOTE;
250 		}
251 	c1 = 0;
252 	dolflg = DOALL;
253 	for (;;) {
254 		if (c1) {
255 			if (c == c1) {
256 				c1 = 0;
257 				dolflg = DOALL;
258 			} else if (c == '\\') {
259 				c = getC(0);
260 				if (c == HIST)
261 					c |= QUOTE;
262 				else {
263 					if (c == '\n')
264 #if 0
265 						if (c1 == '`')
266 							c = ' ';
267 						else
268 #endif
269 							c |= QUOTE;
270 					ungetC(c);
271 					c = '\\';
272 				}
273 			} else if (c == '\n') {
274 				seterrc(gettext("Unmatched "), c1);
275 				ungetC(c);
276 				break;
277 			}
278 		} else if (cmap(c, _META|_Q|_Q1|_ESC)||isauxsp(c)) {
279 			if (c == '\\') {
280 				c = getC(0);
281 				if (c == '\n') {
282 					if (onelflg == 1)
283 						onelflg = 2;
284 					break;
285 				}
286 				if (c != HIST)
287 					*wp++ = '\\', --i;
288 				c |= QUOTE;
289 			} else if (cmap(c, _Q|_Q1)) {		/* '"` */
290 				c1 = c;
291 				dolflg = c == '"' ? DOALL : DOEXCL;
292 			} else if (c != '#' || !intty) {
293 				ungetC(c);
294 				break;
295 			}
296 		}
297 		if (--i > 0) {
298 			*wp++ = c;
299 			c = getC(dolflg);
300 		} else {
301 			seterr("Word too long");
302 			wp = &wbuf[1];
303 			break;
304 		}
305 	}
306 ret:
307 	*wp = 0;
308 #ifdef TRACE
309 	tprintf("word() returning:%t\n", wbuf);
310 #endif
311 	return (savestr(wbuf));
312 }
313 
314 tchar
315 getC1(int flag)
316 {
317 	tchar c;
318 
319 top:
320 	if (c = peekc) {
321 		peekc = 0;
322 		return (c);
323 	}
324 	if (lap) {
325 		if ((c = *lap++) == 0)
326 			lap = 0;
327 		else {
328 			/*
329 			 * don't quote things if there was an error (err!=0)
330 			 * the input is original, not from a substitution and
331 			 * therefore should not be quoted
332 			 */
333 			if (!err && cmap(c, _META|_Q|_Q1)||isauxsp(c))
334 				c |= QUOTE;
335 			return (c);
336 		}
337 	}
338 	if (c = peekd) {
339 		peekd = 0;
340 		return (c);
341 	}
342 	if (exclp) {
343 		if (c = *exclp++)
344 			return (c);
345 		if (exclnxt && --exclc >= 0) {
346 			exclnxt = exclnxt->next;
347 			setexclp(exclnxt->word);
348 			return (' ');
349 		}
350 		exclp = 0;
351 		exclnxt = 0;
352 	}
353 	if (exclnxt) {
354 		exclnxt = exclnxt->next;
355 		if (--exclc < 0)
356 			exclnxt = 0;
357 		else
358 			setexclp(exclnxt->word);
359 		goto top;
360 	}
361 	c = readc(0);
362 	if (c == '$' && (flag & DODOL)) {
363 		getdol();
364 		goto top;
365 	}
366 	if (c == HIST && (flag & DOEXCL)) {
367 		getexcl(0);
368 		goto top;
369 	}
370 	return (c);
371 }
372 
373 void
374 getdol(void)
375 {
376 	tchar *np, *p;
377 	tchar name[MAX_VREF_LEN];
378 	int c;
379 	int sc;
380 	bool special = 0;
381 
382 #ifdef TRACE
383 	tprintf("TRACE- getdol()\n");
384 #endif
385 	np = name, *np++ = '$';
386 	c = sc = getC(DOEXCL);
387 	if (isspnl(c)) {
388 		ungetD(c);
389 		ungetC('$' | QUOTE);
390 		return;
391 	}
392 	if (c == '{')
393 		*np++ = c, c = getC(DOEXCL);
394 	if (c == '#' || c == '?')
395 		special++, *np++ = c, c = getC(DOEXCL);
396 	*np++ = c;
397 	switch (c) {
398 
399 	case '<':
400 	case '$':
401 	case '*':
402 		if (special)
403 			goto vsyn;
404 		goto ret;
405 
406 	case '\n':
407 		ungetD(c);
408 		np--;
409 		goto vsyn;
410 
411 	default:
412 		p = np;
413 		if (digit(c)) {
414 			/* make sure the variable names are MAX_VAR_LEN chars or less */
415 			while (digit(c = getC(DOEXCL)) && (np - p) < MAX_VAR_LEN) {
416 				*np++ = c;
417 			}
418 		} else if (letter(c)) {
419 			while ((letter(c = getC(DOEXCL)) || digit(c)) &&
420 				(np - p) < MAX_VAR_LEN) {
421 				*np++ = c;
422 			}
423 		}
424 		else
425 			goto vsyn;
426 
427 		if ((np - p) > MAX_VAR_LEN)
428 		{
429 			seterr("Variable name too long");
430 			goto ret;
431 		}
432 	}
433 	if (c == '[') {
434 		*np++ = c;
435 		do {
436 			c = getC(DOEXCL);
437 			if (c == '\n') {
438 				ungetD(c);
439 				np--;
440 				goto vsyn;
441 			}
442 			/* need to leave space for possible modifiers */
443 			if (np >= &name[MAX_VREF_LEN - 8])
444 			{
445 				seterr("Variable reference too long");
446 				goto ret;
447 			}
448 			*np++ = c;
449 		} while (c != ']');
450 		c = getC(DOEXCL);
451 	}
452 	if (c == ':') {
453 		*np++ = c, c = getC(DOEXCL);
454 		if (c == 'g')
455 			*np++ = c, c = getC(DOEXCL);
456 		*np++ = c;
457 		if (!any(c, S_htrqxe))
458 			goto vsyn;
459 	} else
460 		ungetD(c);
461 	if (sc == '{') {
462 		c = getC(DOEXCL);
463 		if (c != '}') {
464 			ungetC(c);
465 			goto vsyn;
466 		}
467 		*np++ = c;
468 	}
469 ret:
470 	*np = 0;
471 	addla(name);
472 	return;
473 
474 vsyn:
475 	seterr("Variable syntax");
476 	goto ret;
477 }
478 
479 void
480 addla(tchar *cp)
481 {
482 	tchar *buf;
483 	int len = 0;
484 
485 #ifdef TRACE
486 	tprintf("TRACE- addla()\n");
487 #endif
488 	if (lap) {
489 		len = strlen_(lap);
490 		buf = xalloc((len+1) * sizeof (tchar));
491 		(void) strcpy_(buf, lap);
492 	}
493 	len += strlen_(cp);
494 
495 	/* len+5 is allow 4 additional charecters just to be safe */
496 	labuf = xrealloc(labuf, (len+5) * sizeof (tchar));
497 	(void) strcpy_(labuf, cp);
498 	if (lap) {
499 		(void) strcat_(labuf, buf);
500 		xfree(buf);
501 	}
502 	lap = labuf;
503 }
504 
505 tchar	lhsb[256];
506 tchar	slhs[256];
507 tchar	rhsb[512];
508 int	quesarg;
509 
510 void
511 getexcl(tchar sc)
512 {
513 	struct wordent *hp, *ip;
514 	int left, right, dol;
515 	int c;
516 
517 #ifdef TRACE
518 	tprintf("TRACE- getexcl()\n");
519 #endif
520 	if (sc == 0) {
521 		sc = getC(0);
522 		if (sc != '{') {
523 			ungetC(sc);
524 			sc = 0;
525 		}
526 	}
527 	quesarg = -1;
528 	lastev = eventno;
529 	hp = gethent(sc);
530 	if (hp == 0)
531 		return;
532 	hadhist = 1;
533 	dol = 0;
534 	if (hp == alhistp)
535 		for (ip = hp->next->next; ip != alhistt; ip = ip->next)
536 			dol++;
537 	else
538 		for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
539 			dol++;
540 	left = 0, right = dol;
541 	if (sc == HISTSUB) {
542 		ungetC('s'), unreadc(HISTSUB), c = ':';
543 		goto subst;
544 	}
545 	c = getC(0);
546 	/* if (!any(c, ":^$*-%")) */	/* change needed for char -> tchar */
547 	if (! (c == ':' || c == '^' || c == '$' || c == '*' ||
548 	    c == '-' || c == '%'))
549 		goto subst;
550 	left = right = -1;
551 	if (c == ':') {
552 		c = getC(0);
553 		unreadc(c);
554 		if (letter(c) || c == '&') {
555 			c = ':';
556 			left = 0, right = dol;
557 			goto subst;
558 		}
559 	} else
560 		ungetC(c);
561 	if (!getsel(&left, &right, dol))
562 		return;
563 	c = getC(0);
564 	if (c == '*')
565 		ungetC(c), c = '-';
566 	if (c == '-') {
567 		if (!getsel(&left, &right, dol))
568 			return;
569 		c = getC(0);
570 	}
571 subst:
572 	exclc = right - left + 1;
573 	while (--left >= 0)
574 		hp = hp->next;
575 	if (sc == HISTSUB || c == ':') {
576 		do {
577 			hp = getsub(hp);
578 			c = getC(0);
579 		} while (c == ':');
580 	}
581 	unreadc(c);
582 	if (sc == '{') {
583 		c = getC(0);
584 		if (c != '}')
585 			seterr("Bad ! form");
586 	}
587 	exclnxt = hp;
588 }
589 
590 struct wordent *
591 getsub(struct wordent *en)
592 {
593 	tchar *cp;
594 	int delim;
595 	int c;
596 	int sc;
597 	bool global = 0;
598 	tchar orhsb[(sizeof rhsb)/(sizeof rhsb[0])];
599 
600 #ifdef TRACE
601 	tprintf("TRACE- getsub()\n");
602 #endif
603 	exclnxt = 0;
604 	sc = c = getC(0);
605 	if (c == 'g')
606 		global++, c = getC(0);
607 	switch (c) {
608 
609 	case 'p':
610 		justpr++;
611 		goto ret;
612 
613 	case 'x':
614 	case 'q':
615 		global++;
616 		/* fall into ... */
617 
618 	case 'h':
619 	case 'r':
620 	case 't':
621 	case 'e':
622 		break;
623 
624 	case '&':
625 		if (slhs[0] == 0) {
626 			seterr("No prev sub");
627 			goto ret;
628 		}
629 		(void) strcpy_(lhsb, slhs);
630 		break;
631 
632 #if 0
633 	case '~':
634 		if (lhsb[0] == 0)
635 			goto badlhs;
636 		break;
637 #endif
638 
639 	case 's':
640 		delim = getC(0);
641 		if (alnum(delim) || isspnl(delim)) {
642 			unreadc(delim);
643 bads:
644 			lhsb[0] = 0;
645 			seterr("Bad substitute");
646 			goto ret;
647 		}
648 		cp = lhsb;
649 		for (;;) {
650 			c = getC(0);
651 			if (c == '\n') {
652 				unreadc(c);
653 				break;
654 			}
655 			if (c == delim)
656 				break;
657 			if (cp > &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2])
658 				goto bads;
659 			if (c == '\\') {
660 				c = getC(0);
661 				if (c != delim && c != '\\')
662 					*cp++ = '\\';
663 			}
664 			*cp++ = c;
665 		}
666 		if (cp != lhsb)
667 			*cp++ = 0;
668 		else if (lhsb[0] == 0) {
669 /* badlhs: */
670 			seterr("No prev lhs");
671 			goto ret;
672 		}
673 		cp = rhsb;
674 		(void) strcpy_(orhsb, cp);
675 		for (;;) {
676 			c = getC(0);
677 			if (c == '\n') {
678 				unreadc(c);
679 				break;
680 			}
681 			if (c == delim)
682 				break;
683 #if 0
684 			if (c == '~') {
685 				if (&cp[strlen_(orhsb)]
686 				> &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2])
687 					goto toorhs;
688 				(void) strcpy_(cp, orhsb);
689 				cp = strend(cp);
690 				continue;
691 			}
692 #endif
693 			if (cp > &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2]) {
694 /* toorhs: */
695 				seterr("Rhs too long");
696 				goto ret;
697 			}
698 			if (c == '\\') {
699 				c = getC(0);
700 				if (c != delim /* && c != '~' */)
701 					*cp++ = '\\';
702 			}
703 			*cp++ = c;
704 		}
705 		*cp++ = 0;
706 		break;
707 
708 	default:
709 		if (c == '\n')
710 			unreadc(c);
711 		seterrc(gettext("Bad ! modifier: "), c);
712 		goto ret;
713 	}
714 	(void) strcpy_(slhs, lhsb);
715 	if (exclc)
716 		en = dosub(sc, en, global);
717 ret:
718 	return (en);
719 }
720 
721 struct wordent *
722 dosub(int sc, struct wordent *en, bool global)
723 {
724 	struct wordent lex;
725 	bool didsub = 0;
726 	struct wordent *hp = &lex;
727 	struct wordent *wdp;
728 	int i = exclc;
729 
730 #ifdef TRACE
731 	tprintf("TRACE- dosub()\n");
732 #endif
733 	wdp = hp;
734 	while (--i >= 0) {
735 		struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp);
736 
737 		new->prev = wdp;
738 		new->next = hp;
739 		wdp->next = new;
740 		wdp = new;
741 		en = en->next;
742 		wdp->word = global || didsub == 0 ?
743 		    subword(en->word, sc, &didsub) : savestr(en->word);
744 	}
745 	if (didsub == 0)
746 		seterr("Modifier failed");
747 	hp->prev = wdp;
748 	return (&enthist(-1000, &lex, 0)->Hlex);
749 }
750 
751 tchar *
752 subword(tchar *cp, int type, bool *adid)
753 {
754 	tchar wbuf[BUFSIZ];
755 	tchar *wp, *mp, *np;
756 	int i;
757 
758 #ifdef TRACE
759 	tprintf("TRACE- subword()\n");
760 #endif
761 	switch (type) {
762 
763 	case 'r':
764 	case 'e':
765 	case 'h':
766 	case 't':
767 	case 'q':
768 	case 'x':
769 		wp = domod(cp, type);
770 		if (wp == 0)
771 			return (savestr(cp));
772 		*adid = 1;
773 		return (wp);
774 
775 	default:
776 		wp = wbuf;
777 		i = BUFSIZ - 4;
778 		for (mp = cp; *mp; mp++)
779 			if (matchs(mp, lhsb)) {
780 				for (np = cp; np < mp; )
781 					*wp++ = *np++, --i;
782 				for (np = rhsb; *np; np++) switch (*np) {
783 
784 				case '\\':
785 					if (np[1] == '&')
786 						np++;
787 					/* fall into ... */
788 
789 				default:
790 					if (--i < 0)
791 						goto ovflo;
792 					*wp++ = *np;
793 					continue;
794 
795 				case '&':
796 					i -= strlen_(lhsb);
797 					if (i < 0)
798 						goto ovflo;
799 					*wp = 0;
800 					(void) strcat_(wp, lhsb);
801 					wp = strend(wp);
802 					continue;
803 				}
804 				mp += strlen_(lhsb);
805 				i -= strlen_(mp);
806 				if (i < 0) {
807 ovflo:
808 					seterr("Subst buf ovflo");
809 					return (S_ /* "" */);
810 				}
811 				*wp = 0;
812 				(void) strcat_(wp, mp);
813 				*adid = 1;
814 				return (savestr(wbuf));
815 			}
816 		return (savestr(cp));
817 	}
818 }
819 
820 tchar *
821 domod(tchar *cp, int type)
822 {
823 	tchar *wp, *xp;
824 	int c;
825 
826 #ifdef TRACE
827 	tprintf("TRACE- domod()\n");
828 #endif
829 	switch (type) {
830 
831 	case 'x':
832 	case 'q':
833 		wp = savestr(cp);
834 		for (xp = wp; c = *xp; xp++)
835 			if (!issp(c) || type == 'q')
836 				*xp |= QUOTE;
837 		return (wp);
838 
839 	case 'h':
840 	case 't':
841 		if (!any('/', cp))
842 			return (type == 't' ? savestr(cp) : 0);
843 		wp = strend(cp);
844 		while (*--wp != '/')
845 			continue;
846 		if (type == 'h')
847 			xp = savestr(cp), xp[wp - cp] = 0;
848 		else
849 			xp = savestr(wp + 1);
850 		return (xp);
851 
852 	case 'e':
853 	case 'r':
854 		wp = strend(cp);
855 		for (wp--; wp >= cp && *wp != '/'; wp--)
856 			if (*wp == '.') {
857 				if (type == 'e')
858 					xp = savestr(wp + 1);
859 				else
860 					xp = savestr(cp), xp[wp - cp] = 0;
861 				return (xp);
862 			}
863 		return (savestr(type == 'e' ? S_ /* "" */ : cp));
864 	}
865 	return (0);
866 }
867 
868 int
869 matchs(tchar *str, tchar *pat)
870 {
871 
872 #ifdef TRACE
873 	tprintf("TRACE- matchs()\n");
874 #endif
875 	while (*str && *pat && *str == *pat)
876 		str++, pat++;
877 	return (*pat == 0);
878 }
879 
880 int
881 getsel(int *al, int *ar, int dol)
882 {
883 	int c = getC(0);
884 	int i;
885 	bool first = *al < 0;
886 
887 #ifdef TRACE
888 	tprintf("TRACE- getsel()\n");
889 #endif
890 	switch (c) {
891 
892 	case '%':
893 		if (quesarg == -1)
894 			goto bad;
895 		if (*al < 0)
896 			*al = quesarg;
897 		*ar = quesarg;
898 		break;
899 
900 	case '-':
901 		if (*al < 0) {
902 			*al = 0;
903 			*ar = dol - 1;
904 			unreadc(c);
905 		}
906 		return (1);
907 
908 	case '^':
909 		if (*al < 0)
910 			*al = 1;
911 		*ar = 1;
912 		break;
913 
914 	case '$':
915 		if (*al < 0)
916 			*al = dol;
917 		*ar = dol;
918 		break;
919 
920 	case '*':
921 		if (*al < 0)
922 			*al = 1;
923 		*ar = dol;
924 		if (*ar < *al) {
925 			*ar = 0;
926 			*al = 1;
927 			return (1);
928 		}
929 		break;
930 
931 	default:
932 		if (digit(c)) {
933 			i = 0;
934 			while (digit(c)) {
935 				i = i * 10 + c - '0';
936 				c = getC(0);
937 			}
938 			if (i < 0)
939 				i = dol + 1;
940 			if (*al < 0)
941 				*al = i;
942 			*ar = i;
943 		} else
944 			if (*al < 0)
945 				*al = 0, *ar = dol;
946 			else
947 				*ar = dol - 1;
948 		unreadc(c);
949 		break;
950 	}
951 	if (first) {
952 		c = getC(0);
953 		unreadc(c);
954 		/* if (any(c, "-$*")) */	/* char -> tchar */
955 		if (c == '-' || c == '$' || c == '*')
956 			return (1);
957 	}
958 	if (*al > *ar || *ar > dol) {
959 bad:
960 		seterr("Bad ! arg selector");
961 		return (0);
962 	}
963 	return (1);
964 
965 }
966 
967 struct wordent *
968 gethent(int sc)
969 {
970 	struct Hist *hp;
971 	tchar *np;
972 	int c;
973 	int event;
974 	bool back = 0;
975 
976 #ifdef TRACE
977 	tprintf("TRACE- gethent()\n");
978 #endif
979 	c = sc == HISTSUB ? HIST : getC(0);
980 	if (c == HIST) {
981 		if (alhistp)
982 			return (alhistp);
983 		event = eventno;
984 		goto skip;
985 	}
986 	switch (c) {
987 
988 	case ':':
989 	case '^':
990 	case '$':
991 	case '*':
992 	case '%':
993 		ungetC(c);
994 		if (lastev == eventno && alhistp)
995 			return (alhistp);
996 		event = lastev;
997 		break;
998 
999 	case '-':
1000 		back = 1;
1001 		c = getC(0);
1002 		goto number;
1003 
1004 	case '#':			/* !# is command being typed in (mrh) */
1005 		return (&paraml);
1006 
1007 	default:
1008 		/* if (any(c, "(=~")) { */
1009 		if (c == '(' || c == '=' || c == '~') {
1010 			unreadc(c);
1011 			ungetC(HIST);
1012 			return (0);
1013 		}
1014 		if (digit(c))
1015 			goto number;
1016 		np = lhsb;
1017 		/* while (!any(c, ": \t\\\n}")) { */
1018 		while (! (c == ':' || c == '\\' || isspnl(c) || c == '}')) {
1019 			if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2])
1020 				*np++ = c;
1021 			c = getC(0);
1022 		}
1023 		unreadc(c);
1024 		if (np == lhsb) {
1025 			ungetC(HIST);
1026 			return (0);
1027 		}
1028 		*np++ = 0;
1029 		hp = findev(lhsb, 0);
1030 		if (hp)
1031 			lastev = hp->Hnum;
1032 		return (&hp->Hlex);
1033 
1034 	case '?':
1035 		np = lhsb;
1036 		for (;;) {
1037 			c = getC(0);
1038 			if (c == '\n') {
1039 				unreadc(c);
1040 				break;
1041 			}
1042 			if (c == '?')
1043 				break;
1044 			if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2])
1045 				*np++ = c;
1046 		}
1047 		if (np == lhsb) {
1048 			if (lhsb[0] == 0) {
1049 				seterr("No prev search");
1050 				return (0);
1051 			}
1052 		} else
1053 			*np++ = 0;
1054 		hp = findev(lhsb, 1);
1055 		if (hp)
1056 			lastev = hp->Hnum;
1057 		return (&hp->Hlex);
1058 
1059 	number:
1060 		event = 0;
1061 		while (digit(c)) {
1062 			event = event * 10 + c - '0';
1063 			c = getC(0);
1064 		}
1065 		if (back)
1066 			event = eventno + (alhistp == 0) - (event ? event : 0);
1067 		unreadc(c);
1068 		break;
1069 	}
1070 skip:
1071 	for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1072 		if (hp->Hnum == event) {
1073 			hp->Href = eventno;
1074 			lastev = hp->Hnum;
1075 			return (&hp->Hlex);
1076 		}
1077 	np = putn(event);
1078 	noev(np);
1079 	return (0);
1080 }
1081 
1082 struct Hist *
1083 findev(tchar *cp, bool anyarg)
1084 {
1085 	struct Hist *hp;
1086 
1087 #ifdef TRACE
1088 	tprintf("TRACE- findev()\n");
1089 #endif
1090 	for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1091 		tchar *dp;
1092 		tchar *p, *q;
1093 		struct wordent *lp = hp->Hlex.next;
1094 		int argno = 0;
1095 
1096 		if (lp->word[0] == '\n')
1097 			continue;
1098 		if (!anyarg) {
1099 			p = cp;
1100 			q = lp->word;
1101 			do
1102 				if (!*p)
1103 					return (hp);
1104 			while (*p++ == *q++);
1105 			continue;
1106 		}
1107 		do {
1108 			for (dp = lp->word; *dp; dp++) {
1109 				p = cp;
1110 				q = dp;
1111 				do
1112 					if (!*p) {
1113 						quesarg = argno;
1114 						return (hp);
1115 					}
1116 				while (*p++ == *q++);
1117 			}
1118 			lp = lp->next;
1119 			argno++;
1120 		} while (lp->word[0] != '\n');
1121 	}
1122 	noev(cp);
1123 	return (0);
1124 }
1125 
1126 void
1127 noev(tchar *cp)
1128 {
1129 
1130 #ifdef TRACE
1131 	tprintf("TRACE- noev()\n");
1132 #endif
1133 	seterr2(cp, ": Event not found");
1134 }
1135 
1136 void
1137 setexclp(tchar *cp)
1138 {
1139 
1140 #ifdef TRACE
1141 	tprintf("TRACE- setexclp()\n");
1142 #endif
1143 	if (cp && cp[0] == '\n')
1144 		return;
1145 	exclp = cp;
1146 }
1147 
1148 void
1149 unreadc(tchar c)
1150 {
1151 
1152 	peekread = c;
1153 }
1154 
1155 int
1156 readc(bool wanteof)
1157 {
1158 	int c;
1159 	static int sincereal;
1160 
1161 	if (c = peekread) {
1162 		peekread = 0;
1163 		return (c);
1164 	}
1165 top:
1166 	if (alvecp) {
1167 		if (c = *alvecp++)
1168 			return (c);
1169 		if (*alvec) {
1170 			alvecp = *alvec++;
1171 			return (' ');
1172 		}
1173 	}
1174 	if (alvec) {
1175 		if (alvecp = *alvec) {
1176 			alvec++;
1177 			goto top;
1178 		}
1179 		/* Infinite source! */
1180 		return ('\n');
1181 	}
1182 	if (evalp) {
1183 		if (c = *evalp++)
1184 			return (c);
1185 		if (*evalvec) {
1186 			evalp = *evalvec++;
1187 			return (' ');
1188 		}
1189 		evalp = 0;
1190 	}
1191 	if (evalvec) {
1192 		if (evalvec ==  (tchar **)1) {
1193 			doneinp = 1;
1194 			reset();
1195 		}
1196 		if (evalp = *evalvec) {
1197 			evalvec++;
1198 			goto top;
1199 		}
1200 		evalvec =  (tchar **)1;
1201 		return ('\n');
1202 	}
1203 	do {
1204 		if (arginp ==  (tchar *) 1 || onelflg == 1) {
1205 			if (wanteof)
1206 				return (-1);
1207 			exitstat();
1208 		}
1209 		if (arginp) {
1210 			if ((c = *arginp++) == 0) {
1211 				arginp =  (tchar *) 1;
1212 				return ('\n');
1213 			}
1214 			return (c);
1215 		}
1216 reread:
1217 		c = bgetc();
1218 		if (c < 0) {
1219 			struct sgttyb tty;
1220 
1221 			if (wanteof)
1222 				return (-1);
1223 			/* was isatty but raw with ignoreeof yields problems */
1224 			if (ioctl(SHIN, TIOCGETP,  (char *)&tty) == 0 &&
1225 			    (tty.sg_flags & RAW) == 0) {
1226 				/* was 'short' for FILEC */
1227 				int ctpgrp;
1228 
1229 				if (++sincereal > 25)
1230 					goto oops;
1231 				if (tpgrp != -1 &&
1232 				    ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp) == 0 &&
1233 				    tpgrp != ctpgrp) {
1234 					(void) ioctl(FSHTTY, TIOCSPGRP,
1235 						(char *)&tpgrp);
1236 					(void) killpg(ctpgrp, SIGHUP);
1237 printf("Reset tty pgrp from %d to %d\n", ctpgrp, tpgrp);
1238 					goto reread;
1239 				}
1240 				if (adrof(S_ignoreeof /* "ignoreeof" */)) {
1241 					if (loginsh)
1242 				printf("\nUse \"logout\" to logout.\n");
1243 					else
1244 				printf("\nUse \"exit\" to leave csh.\n");
1245 					reset();
1246 				}
1247 				if (chkstop == 0) {
1248 					panystop(1);
1249 				}
1250 			}
1251 oops:
1252 			doneinp = 1;
1253 			reset();
1254 		}
1255 		sincereal = 0;
1256 		if (c == '\n' && onelflg)
1257 			onelflg--;
1258 	} while (c == 0);
1259 	return (c);
1260 }
1261 
1262 static void
1263 expand_fbuf(void)
1264 {
1265 	tchar **nfbuf =
1266 	    (tchar **)xcalloc((unsigned)(fblocks + 2), sizeof (tchar **));
1267 
1268 	if (fbuf) {
1269 		(void) blkcpy(nfbuf, fbuf);
1270 		xfree((char *)fbuf);
1271 	}
1272 	fbuf = nfbuf;
1273 	fbuf[fblocks] = (tchar *)xcalloc(BUFSIZ + MB_LEN_MAX,
1274 		sizeof (tchar));
1275 	fblocks++;
1276 }
1277 
1278 int
1279 bgetc(void)
1280 {
1281 	int buf, off, c;
1282 #ifdef FILEC
1283 	tchar ttyline[BUFSIZ + MB_LEN_MAX]; /* read_() can return extra bytes */
1284 	int roomleft;
1285 #endif
1286 
1287 #ifdef TELL
1288 	if (cantell) {
1289 		if (fseekp < fbobp || fseekp > feobp) {
1290 			fbobp = feobp = fseekp;
1291 			(void) lseek(SHIN, fseekp, 0);
1292 		}
1293 		if (fseekp == feobp) {
1294 			fbobp = feobp;
1295 			do
1296 				c = read_(SHIN, fbuf[0], BUFSIZ);
1297 			while (c < 0 && errno == EINTR);
1298 			if (c <= 0)
1299 				return (-1);
1300 			feobp += c;
1301 		}
1302 		c = fbuf[0][fseekp - fbobp];
1303 		fseekp++;
1304 		return (c);
1305 	}
1306 #endif
1307 again:
1308 	buf = (int)fseekp / BUFSIZ;
1309 	if (buf >= fblocks) {
1310 		expand_fbuf();
1311 		goto again;
1312 	}
1313 	if (fseekp >= feobp) {
1314 		buf = (int)feobp / BUFSIZ;
1315 		off = (int)feobp % BUFSIZ;
1316 #ifndef FILEC
1317 		for (;;) {
1318 			c = read_(SHIN, fbuf[buf] + off, BUFSIZ - off);
1319 #else
1320 		roomleft = BUFSIZ - off;
1321 		for (;;) {
1322 			if (filec && intty) {
1323 				c = tenex(ttyline, BUFSIZ);
1324 				if (c > roomleft) {
1325 					expand_fbuf();
1326 					copy(fbuf[buf] + off, ttyline,
1327 					    roomleft * sizeof (tchar));
1328 					copy(fbuf[buf + 1], ttyline + roomleft,
1329 					    (c - roomleft) * sizeof (tchar));
1330 				} else if (c > 0) {
1331 					copy(fbuf[buf] + off, ttyline,
1332 						c * sizeof (tchar));
1333 				}
1334 			} else {
1335 				c = read_(SHIN, fbuf[buf] + off, roomleft);
1336 				if (c > roomleft) {
1337 					expand_fbuf();
1338 					copy(fbuf[buf + 1],
1339 					    fbuf[buf] + off + roomleft,
1340 					    (c - roomleft) * sizeof (tchar));
1341 				}
1342 			}
1343 #endif
1344 			if (c >= 0)
1345 				break;
1346 			if (errno == EWOULDBLOCK) {
1347 				int off = 0;
1348 
1349 				(void) ioctl(SHIN, FIONBIO,  (char *)&off);
1350 			} else if (errno != EINTR)
1351 				break;
1352 		}
1353 		if (c <= 0)
1354 			return (-1);
1355 		feobp += c;
1356 #ifndef FILEC
1357 		goto again;
1358 #else
1359 		if (filec && !intty)
1360 			goto again;
1361 #endif
1362 	}
1363 	c = fbuf[buf][(int)fseekp % BUFSIZ];
1364 	fseekp++;
1365 	return (c);
1366 }
1367 
1368 void
1369 bfree(void)
1370 {
1371 	int sb, i;
1372 
1373 #ifdef TELL
1374 	if (cantell)
1375 		return;
1376 #endif
1377 	if (whyles)
1378 		return;
1379 	sb = (int)(fseekp - 1) / BUFSIZ;
1380 	if (sb > 0) {
1381 		for (i = 0; i < sb; i++)
1382 			xfree(fbuf[i]);
1383 		(void) blkcpy(fbuf, &fbuf[sb]);
1384 		fseekp -= BUFSIZ * sb;
1385 		feobp -= BUFSIZ * sb;
1386 		fblocks -= sb;
1387 	}
1388 }
1389 
1390 void
1391 bseek(off_t l)
1392 {
1393 	struct whyle *wp;
1394 
1395 	fseekp = l;
1396 #ifdef TELL
1397 	if (!cantell) {
1398 #endif
1399 		if (!whyles)
1400 			return;
1401 		for (wp = whyles; wp->w_next; wp = wp->w_next)
1402 			continue;
1403 		if (wp->w_start > l)
1404 			l = wp->w_start;
1405 #ifdef TELL
1406 	}
1407 #endif
1408 }
1409 
1410 /* any similarity to bell telephone is purely accidental */
1411 #ifndef btell
1412 off_t
1413 btell(void)
1414 {
1415 
1416 	return (fseekp);
1417 }
1418 #endif
1419 
1420 void
1421 btoeof(void)
1422 {
1423 
1424 	(void) lseek(SHIN, (off_t)0, 2);
1425 	fseekp = feobp;
1426 	wfree();
1427 	bfree();
1428 }
1429 
1430 #ifdef TELL
1431 void
1432 settell(void)
1433 {
1434 
1435 	cantell = 0;
1436 	if (arginp || onelflg || intty)
1437 		return;
1438 	if (lseek(SHIN, (off_t)0, 1) < 0 || errno == ESPIPE)
1439 		return;
1440 	fbuf = (tchar **)xcalloc(2, sizeof (tchar **));
1441 	fblocks = 1;
1442 	fbuf[0] = (tchar *)xcalloc(BUFSIZ + MB_LEN_MAX, sizeof (tchar));
1443 	fseekp = fbobp = feobp = lseek(SHIN, (off_t)0, 1);
1444 	cantell = 1;
1445 }
1446 #endif
1447