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