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