xref: /openbsd-src/bin/csh/lex.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: lex.c,v 1.14 2009/10/27 23:59:21 deraadt Exp $	*/
2 /*	$NetBSD: lex.c,v 1.9 1995/09/27 00:38:46 jtc Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <termios.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <stdarg.h>
41 
42 #include "csh.h"
43 #include "extern.h"
44 
45 /*
46  * These lexical routines read input and form lists of words.
47  * There is some involved processing here, because of the complications
48  * of input buffering, and especially because of history substitution.
49  */
50 
51 static Char	*word(void);
52 static int	 getC1(int);
53 static void	 getdol(void);
54 static void	 getexcl(int);
55 static struct Hist
56 		*findev(Char *, bool);
57 static void	 setexclp(Char *);
58 static int	 bgetc(void);
59 static void	 bfree(void);
60 static struct wordent
61 		*gethent(int);
62 static int	 matchs(Char *, Char *);
63 static int	 getsel(int *, int *, int);
64 static struct wordent
65 		*getsub(struct wordent *);
66 static Char	*subword(Char *, int, bool *);
67 static struct wordent
68 		*dosub(int, struct wordent *, bool);
69 
70 /*
71  * Peekc is a peek character for getC, peekread for readc.
72  * There is a subtlety here in many places... history routines
73  * will read ahead and then insert stuff into the input stream.
74  * If they push back a character then they must push it behind
75  * the text substituted by the history substitution.  On the other
76  * hand in several places we need 2 peek characters.  To make this
77  * all work, the history routines read with getC, and make use both
78  * of ungetC and unreadc.  The key observation is that the state
79  * of getC at the call of a history reference is such that calls
80  * to getC from the history routines will always yield calls of
81  * readc, unless this peeking is involved.  That is to say that during
82  * getexcl the variables lap, exclp, and exclnxt are all zero.
83  *
84  * Getdol invokes history substitution, hence the extra peek, peekd,
85  * which it can ungetD to be before history substitutions.
86  */
87 static Char peekc = 0, peekd = 0;
88 static Char peekread = 0;
89 
90 /* (Tail of) current word from ! subst */
91 static Char *exclp = NULL;
92 
93 /* The rest of the ! subst words */
94 static struct wordent *exclnxt = NULL;
95 
96 /* Count of remaining words in ! subst */
97 static int exclc = 0;
98 
99 /* "Globp" for alias resubstitution */
100 Char *alvecp = NULL;
101 int aret = F_SEEK;
102 
103 /*
104  * Labuf implements a general buffer for lookahead during lexical operations.
105  * Text which is to be placed in the input stream can be stuck here.
106  * We stick parsed ahead $ constructs during initial input,
107  * process id's from `$$', and modified variable values (from qualifiers
108  * during expansion in sh.dol.c) here.
109  */
110 static Char labuf[BUFSIZ];
111 
112 /*
113  * Lex returns to its caller not only a wordlist (as a "var" parameter)
114  * but also whether a history substitution occurred.  This is used in
115  * the main (process) routine to determine whether to echo, and also
116  * when called by the alias routine to determine whether to keep the
117  * argument list.
118  */
119 static bool hadhist = 0;
120 
121 /*
122  * Avoid alias expansion recursion via \!#
123  */
124 int     hleft;
125 
126 static Char getCtmp;
127 
128 #define getC(f)		((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
129 #define	ungetC(c)	peekc = c
130 #define	ungetD(c)	peekd = c
131 
132 int
133 lex(struct wordent *hp)
134 {
135     struct wordent *wdp;
136     int     c;
137 
138     btell(&lineloc);
139     hp->next = hp->prev = hp;
140     hp->word = STRNULL;
141     hadhist = 0;
142     do
143 	c = readc(0);
144     while (c == ' ' || c == '\t');
145     if (c == HISTSUB && intty)
146 	/* ^lef^rit	from tty is short !:s^lef^rit */
147 	getexcl(c);
148     else
149 	unreadc(c);
150     wdp = hp;
151     /*
152      * The following loop is written so that the links needed by freelex will
153      * be ready and rarin to go even if it is interrupted.
154      */
155     do {
156 	struct wordent *new;
157 
158 	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
159 	new->word = 0;
160 	new->prev = wdp;
161 	new->next = hp;
162 	wdp->next = new;
163 	wdp = new;
164 	wdp->word = word();
165     } while (wdp->word[0] != '\n');
166     hp->prev = wdp;
167     return (hadhist);
168 }
169 
170 void
171 prlex(FILE *fp, struct wordent *sp0)
172 {
173     struct wordent *sp = sp0->next;
174 
175     for (;;) {
176 	(void) fprintf(fp, "%s", vis_str(sp->word));
177 	sp = sp->next;
178 	if (sp == sp0)
179 	    break;
180 	if (sp->word[0] != '\n')
181 	    (void) fputc(' ', fp);
182     }
183 }
184 
185 void
186 copylex(struct wordent *hp, struct wordent *fp)
187 {
188     struct wordent *wdp;
189 
190     wdp = hp;
191     fp = fp->next;
192     do {
193 	struct wordent *new;
194 
195 	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
196 	new->prev = wdp;
197 	new->next = hp;
198 	wdp->next = new;
199 	wdp = new;
200 	wdp->word = Strsave(fp->word);
201 	fp = fp->next;
202     } while (wdp->word[0] != '\n');
203     hp->prev = wdp;
204 }
205 
206 void
207 freelex(struct wordent *vp)
208 {
209     struct wordent *fp;
210 
211     while (vp->next != vp) {
212 	fp = vp->next;
213 	vp->next = fp->next;
214 	xfree((ptr_t) fp->word);
215 	xfree((ptr_t) fp);
216     }
217     vp->prev = vp;
218 }
219 
220 static Char *
221 word(void)
222 {
223     Char c, c1;
224     Char *wp;
225     Char    wbuf[BUFSIZ];
226     bool dolflg;
227     int i;
228 
229     wp = wbuf;
230     i = BUFSIZ - 4;
231 loop:
232     while ((c = getC(DOALL)) == ' ' || c == '\t')
233 	continue;
234     if (cmap(c, _META | _ESC))
235 	switch (c) {
236 	case '&':
237 	case '|':
238 	case '<':
239 	case '>':
240 	    *wp++ = c;
241 	    c1 = getC(DOALL);
242 	    if (c1 == c)
243 		*wp++ = c1;
244 	    else
245 		ungetC(c1);
246 	    goto ret;
247 
248 	case '#':
249 	    if (intty)
250 		break;
251 	    c = 0;
252 	    do {
253 		c1 = c;
254 		c = getC(0);
255 	    } while (c != '\n');
256 	    if (c1 == '\\')
257 		goto loop;
258 	    /* fall into ... */
259 
260 	case ';':
261 	case '(':
262 	case ')':
263 	case '\n':
264 	    *wp++ = c;
265 	    goto ret;
266 
267 	case '\\':
268 	    c = getC(0);
269 	    if (c == '\n') {
270 		if (onelflg == 1)
271 		    onelflg = 2;
272 		goto loop;
273 	    }
274 	    if (c != HIST)
275 		*wp++ = '\\', --i;
276 	    c |= QUOTE;
277 	}
278     c1 = 0;
279     dolflg = DOALL;
280     for (;;) {
281 	if (c1) {
282 	    if (c == c1) {
283 		c1 = 0;
284 		dolflg = DOALL;
285 	    }
286 	    else if (c == '\\') {
287 		c = getC(0);
288 		if (c == HIST)
289 		    c |= QUOTE;
290 		else {
291 		    if (c == '\n')
292 			/*
293 			 * if (c1 == '`') c = ' '; else
294 			 */
295 			c |= QUOTE;
296 		    ungetC(c);
297 		    c = '\\';
298 		}
299 	    }
300 	    else if (c == '\n') {
301 		seterror(ERR_UNMATCHED, c1);
302 		ungetC(c);
303 		break;
304 	    }
305 	}
306 	else if (cmap(c, _META | _QF | _QB | _ESC)) {
307 	    if (c == '\\') {
308 		c = getC(0);
309 		if (c == '\n') {
310 		    if (onelflg == 1)
311 			onelflg = 2;
312 		    break;
313 		}
314 		if (c != HIST)
315 		    *wp++ = '\\', --i;
316 		c |= QUOTE;
317 	    }
318 	    else if (cmap(c, _QF | _QB)) {	/* '"` */
319 		c1 = c;
320 		dolflg = c == '"' ? DOALL : DOEXCL;
321 	    }
322 	    else if (c != '#' || !intty) {
323 		ungetC(c);
324 		break;
325 	    }
326 	}
327 	if (--i > 0) {
328 	    *wp++ = c;
329 	    c = getC(dolflg);
330 	}
331 	else {
332 	    seterror(ERR_WTOOLONG);
333 	    wp = &wbuf[1];
334 	    break;
335 	}
336     }
337 ret:
338     *wp = 0;
339     return (Strsave(wbuf));
340 }
341 
342 static int
343 getC1(int flag)
344 {
345     Char c;
346 
347     while (1) {
348 	if ((c = peekc) != '\0') {
349 	    peekc = 0;
350 	    return (c);
351 	}
352 	if (lap) {
353 	    if ((c = *lap++) == 0)
354 		lap = 0;
355 	    else {
356 		if (cmap(c, _META | _QF | _QB))
357 		    c |= QUOTE;
358 		return (c);
359 	    }
360 	}
361 	if ((c = peekd) != '\0') {
362 	    peekd = 0;
363 	    return (c);
364 	}
365 	if (exclp) {
366 	    if ((c = *exclp++) != '\0')
367 		return (c);
368 	    if (exclnxt && --exclc >= 0) {
369 		exclnxt = exclnxt->next;
370 		setexclp(exclnxt->word);
371 		return (' ');
372 	    }
373 	    exclp = 0;
374 	    exclnxt = 0;
375 	}
376 	if (exclnxt) {
377 	    exclnxt = exclnxt->next;
378 	    if (--exclc < 0)
379 		exclnxt = 0;
380 	    else
381 		setexclp(exclnxt->word);
382 	    continue;
383 	}
384 	c = readc(0);
385 	if (c == '$' && (flag & DODOL)) {
386 	    getdol();
387 	    continue;
388 	}
389 	if (c == HIST && (flag & DOEXCL)) {
390 	    getexcl(0);
391 	    continue;
392 	}
393 	break;
394     }
395     return (c);
396 }
397 
398 static void
399 getdol(void)
400 {
401     Char *np, *ep;
402     Char    name[4 * MAXVARLEN + 1];
403     int c;
404     int     sc;
405     bool    special = 0, toolong;
406 
407     np = name, *np++ = '$';
408     c = sc = getC(DOEXCL);
409     if (any("\t \n", c)) {
410 	ungetD(c);
411 	ungetC('$' | QUOTE);
412 	return;
413     }
414     if (c == '{')
415 	*np++ = c, c = getC(DOEXCL);
416     if (c == '#' || c == '?')
417 	special++, *np++ = c, c = getC(DOEXCL);
418     *np++ = c;
419     switch (c) {
420 
421     case '<':
422     case '$':
423     case '!':
424 	if (special)
425 	    seterror(ERR_SPDOLLT);
426 	*np = 0;
427 	addla(name);
428 	return;
429 
430     case '\n':
431 	ungetD(c);
432 	np--;
433 	seterror(ERR_NEWLINE);
434 	*np = 0;
435 	addla(name);
436 	return;
437 
438     case '*':
439 	if (special)
440 	    seterror(ERR_SPSTAR);
441 	*np = 0;
442 	addla(name);
443 	return;
444 
445     default:
446 	toolong = 0;
447 	if (Isdigit(c)) {
448 #ifdef notdef
449 	    /* let $?0 pass for now */
450 	    if (special) {
451 		seterror(ERR_DIGIT);
452 		*np = 0;
453 		addla(name);
454 		return;
455 	    }
456 #endif
457 	    /* we know that np < &name[4] */
458 	    ep = &np[MAXVARLEN];
459 	    while ((c = getC(DOEXCL)) != '\0'){
460 		if (!Isdigit(c))
461 		    break;
462 		if (np < ep)
463 		    *np++ = c;
464 		else
465 		    toolong = 1;
466 	    }
467 	}
468 	else if (letter(c)) {
469 	    /* we know that np < &name[4] */
470 	    ep = &np[MAXVARLEN];
471 	    toolong = 0;
472 	    while ((c = getC(DOEXCL)) != '\0') {
473 		/* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
474 		if (!letter(c) && !Isdigit(c))
475 		    break;
476 		if (np < ep)
477 		    *np++ = c;
478 		else
479 		    toolong = 1;
480 	    }
481 	}
482 	else {
483 	    *np = 0;
484 	    seterror(ERR_VARILL);
485 	    addla(name);
486 	    return;
487 	}
488 	if (toolong) {
489 	    seterror(ERR_VARTOOLONG);
490 	    *np = 0;
491 	    addla(name);
492 	    return;
493 	}
494 	break;
495     }
496     if (c == '[') {
497 	*np++ = c;
498 	/*
499 	 * Name up to here is a max of MAXVARLEN + 8.
500 	 */
501 	ep = &np[2 * MAXVARLEN + 8];
502 	do {
503 	    /*
504 	     * Michael Greim: Allow $ expansion to take place in selector
505 	     * expressions. (limits the number of characters returned)
506 	     */
507 	    c = getC(DOEXCL | DODOL);
508 	    if (c == '\n') {
509 		ungetD(c);
510 		np--;
511 		seterror(ERR_NLINDEX);
512 		*np = 0;
513 		addla(name);
514 		return;
515 	    }
516 	    if (np < ep)
517 		*np++ = c;
518 	} while (c != ']');
519 	*np = '\0';
520 	if (np >= ep) {
521 	    seterror(ERR_SELOVFL);
522 	    addla(name);
523 	    return;
524 	}
525 	c = getC(DOEXCL);
526     }
527     /*
528      * Name up to here is a max of 2 * MAXVARLEN + 8.
529      */
530     if (c == ':') {
531 	/*
532 	 * if the :g modifier is followed by a newline, then error right away!
533 	 * -strike
534 	 */
535 
536 	int     gmodflag = 0, amodflag = 0;
537 
538 	do {
539 	    *np++ = c, c = getC(DOEXCL);
540 	    if (c == 'g' || c == 'a') {
541 		if (c == 'g')
542 		    gmodflag++;
543 		else
544 		    amodflag++;
545 		*np++ = c; c = getC(DOEXCL);
546 	    }
547 	    if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
548 		if (c == 'g')
549 		    gmodflag++;
550 		else
551 		    amodflag++;
552 		*np++ = c; c = getC(DOEXCL);
553 	    }
554 	    *np++ = c;
555 	    /* scan s// [eichin:19910926.0512EST] */
556 	    if (c == 's') {
557 		int delimcnt = 2;
558 		int delim = getC(0);
559 		*np++ = delim;
560 
561 		if (!delim || letter(delim)
562 		    || Isdigit(delim) || any(" \t\n", delim)) {
563 		    seterror(ERR_BADSUBST);
564 		    break;
565 		}
566 		while ((c = getC(0)) != (-1)) {
567 		    *np++ = c;
568 		    if(c == delim) delimcnt--;
569 		    if(!delimcnt) break;
570 		}
571 		if(delimcnt) {
572 		    seterror(ERR_BADSUBST);
573 		    break;
574 		}
575 		c = 's';
576 	    }
577 	    if (!any("htrqxes", c)) {
578 		if ((amodflag || gmodflag) && c == '\n')
579 		    stderror(ERR_VARSYN);	/* strike */
580 		seterror(ERR_VARMOD, c);
581 		*np = 0;
582 		addla(name);
583 		return;
584 	    }
585 	}
586 	while ((c = getC(DOEXCL)) == ':');
587 	ungetD(c);
588     }
589     else
590 	ungetD(c);
591     if (sc == '{') {
592 	c = getC(DOEXCL);
593 	if (c != '}') {
594 	    ungetD(c);
595 	    seterror(ERR_MISSING, '}');
596 	    *np = 0;
597 	    addla(name);
598 	    return;
599 	}
600 	*np++ = c;
601     }
602     *np = 0;
603     addla(name);
604     return;
605 }
606 
607 void
608 addla(Char *cp)
609 {
610     Char    buf[BUFSIZ];
611 
612     if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
613 	(sizeof(labuf) - 4) / sizeof(Char)) {
614 	seterror(ERR_EXPOVFL);
615 	return;
616     }
617     if (lap)
618 	(void) Strlcpy(buf, lap, sizeof buf/sizeof(Char));
619     (void) Strlcpy(labuf, cp, sizeof labuf/sizeof(Char));
620     if (lap)
621 	(void) Strlcat(labuf, buf, sizeof labuf/sizeof(Char));
622     lap = labuf;
623 }
624 
625 static Char lhsb[32];
626 static Char slhs[32];
627 static Char rhsb[64];
628 static int quesarg;
629 
630 static void
631 getexcl(int sc)
632 {
633     struct wordent *hp, *ip;
634     int     left, right, dol;
635     int c;
636 
637     if (sc == 0) {
638 	sc = getC(0);
639 	if (sc != '{') {
640 	    ungetC(sc);
641 	    sc = 0;
642 	}
643     }
644     quesarg = -1;
645     lastev = eventno;
646     hp = gethent(sc);
647     if (hp == 0)
648 	return;
649     hadhist = 1;
650     dol = 0;
651     if (hp == alhistp)
652 	for (ip = hp->next->next; ip != alhistt; ip = ip->next)
653 	    dol++;
654     else
655 	for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
656 	    dol++;
657     left = 0, right = dol;
658     if (sc == HISTSUB) {
659 	ungetC('s'), unreadc(HISTSUB), c = ':';
660 	goto subst;
661     }
662     c = getC(0);
663     if (!any(":^$*-%", c))
664 	goto subst;
665     left = right = -1;
666     if (c == ':') {
667 	c = getC(0);
668 	unreadc(c);
669 	if (letter(c) || c == '&') {
670 	    c = ':';
671 	    left = 0, right = dol;
672 	    goto subst;
673 	}
674     }
675     else
676 	ungetC(c);
677     if (!getsel(&left, &right, dol))
678 	return;
679     c = getC(0);
680     if (c == '*')
681 	ungetC(c), c = '-';
682     if (c == '-') {
683 	if (!getsel(&left, &right, dol))
684 	    return;
685 	c = getC(0);
686     }
687 subst:
688     exclc = right - left + 1;
689     while (--left >= 0)
690 	hp = hp->next;
691     if (sc == HISTSUB || c == ':') {
692 	do {
693 	    hp = getsub(hp);
694 	    c = getC(0);
695 	} while (c == ':');
696     }
697     unreadc(c);
698     if (sc == '{') {
699 	c = getC(0);
700 	if (c != '}')
701 	    seterror(ERR_BADBANG);
702     }
703     exclnxt = hp;
704 }
705 
706 static struct wordent *
707 getsub(struct wordent *en)
708 {
709     Char *cp;
710     int     delim;
711     int c;
712     int     sc;
713     bool global;
714     Char    orhsb[sizeof(rhsb) / sizeof(Char)];
715 
716     do {
717 	exclnxt = 0;
718 	global = 0;
719 	sc = c = getC(0);
720 	if (c == 'g' || c == 'a') {
721 	    global |= (c == 'g') ? 1 : 2;
722 	    sc = c = getC(0);
723 	}
724 	if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
725 	    global |= (c == 'g') ? 1 : 2;
726 	    sc = c = getC(0);
727 	}
728 
729 	switch (c) {
730 	case 'p':
731 	    justpr++;
732 	    return (en);
733 
734 	case 'x':
735 	case 'q':
736 	    global |= 1;
737 
738 	    /* fall into ... */
739 
740 	case 'h':
741 	case 'r':
742 	case 't':
743 	case 'e':
744 	    break;
745 
746 	case '&':
747 	    if (slhs[0] == 0) {
748 		seterror(ERR_NOSUBST);
749 		return (en);
750 	    }
751 	    (void) Strlcpy(lhsb, slhs, sizeof(lhsb)/sizeof(Char));
752 	    break;
753 
754 #ifdef notdef
755 	case '~':
756 	    if (lhsb[0] == 0)
757 		goto badlhs;
758 	    break;
759 #endif
760 
761 	case 's':
762 	    delim = getC(0);
763 	    if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
764 		unreadc(delim);
765 		lhsb[0] = 0;
766 		seterror(ERR_BADSUBST);
767 		return (en);
768 	    }
769 	    cp = lhsb;
770 	    for (;;) {
771 		c = getC(0);
772 		if (c == '\n') {
773 		    unreadc(c);
774 		    break;
775 		}
776 		if (c == delim)
777 		    break;
778 		if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) {
779 		    lhsb[0] = 0;
780 		    seterror(ERR_BADSUBST);
781 		    return (en);
782 		}
783 		if (c == '\\') {
784 		    c = getC(0);
785 		    if (c != delim && c != '\\')
786 			*cp++ = '\\';
787 		}
788 		*cp++ = c;
789 	    }
790 	    if (cp != lhsb)
791 		*cp++ = 0;
792 	    else if (lhsb[0] == 0) {
793 		seterror(ERR_LHS);
794 		return (en);
795 	    }
796 	    cp = rhsb;
797 	    (void) Strlcpy(orhsb, cp, sizeof(orhsb)/sizeof(Char));
798 	    for (;;) {
799 		c = getC(0);
800 		if (c == '\n') {
801 		    unreadc(c);
802 		    break;
803 		}
804 		if (c == delim)
805 		    break;
806 		if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
807 		    seterror(ERR_RHSLONG);
808 		    return (en);
809 		}
810 		if (c == '\\') {
811 		    c = getC(0);
812 		    if (c != delim /* && c != '~' */ )
813 			*cp++ = '\\';
814 		}
815 		*cp++ = c;
816 	    }
817 	    *cp++ = 0;
818 	    break;
819 
820 	default:
821 	    if (c == '\n')
822 		unreadc(c);
823 	    seterror(ERR_BADBANGMOD, c);
824 	    return (en);
825 	}
826 	(void) Strlcpy(slhs, lhsb, sizeof(slhs)/sizeof(Char));
827 	if (exclc)
828 	    en = dosub(sc, en, global);
829     }
830     while ((c = getC(0)) == ':');
831     unreadc(c);
832     return (en);
833 }
834 
835 static struct wordent *
836 dosub(int sc, struct wordent *en, bool global)
837 {
838     struct wordent lexi;
839     bool    didsub = 0, didone = 0;
840     struct wordent *hp = &lexi;
841     struct wordent *wdp;
842     int i = exclc;
843 
844     wdp = hp;
845     while (--i >= 0) {
846 	struct wordent *new =
847 		(struct wordent *) xcalloc(1, sizeof *wdp);
848 
849 	new->word = 0;
850 	new->prev = wdp;
851 	new->next = hp;
852 	wdp->next = new;
853 	wdp = new;
854 	en = en->next;
855 	if (en->word) {
856 	    Char *tword, *otword;
857 
858 	    if ((global & 1) || didsub == 0) {
859 		tword = subword(en->word, sc, &didone);
860 		if (didone)
861 		    didsub = 1;
862 		if (global & 2) {
863 		    while (didone && tword != STRNULL) {
864 			otword = tword;
865 			tword = subword(otword, sc, &didone);
866 			if (Strcmp(tword, otword) == 0) {
867 			    xfree((ptr_t) otword);
868 			    break;
869 			}
870 			else
871 			    xfree((ptr_t) otword);
872 		    }
873 		}
874 	    }
875 	    else
876 		tword = Strsave(en->word);
877 	    wdp->word = tword;
878 	}
879     }
880     if (didsub == 0)
881 	seterror(ERR_MODFAIL);
882     hp->prev = wdp;
883     return (&enthist(-1000, &lexi, 0)->Hlex);
884 }
885 
886 static Char *
887 subword(Char *cp, int type, bool *adid)
888 {
889     Char    wbuf[BUFSIZ];
890     Char *wp, *mp, *np;
891     int i;
892 
893     *adid = 0;
894     switch (type) {
895 
896     case 'r':
897     case 'e':
898     case 'h':
899     case 't':
900     case 'q':
901     case 'x':
902 	wp = domod(cp, type);
903 	if (wp == 0)
904 	    return (Strsave(cp));
905 	*adid = 1;
906 	return (wp);
907 
908     default:
909 	wp = wbuf;
910 	i = BUFSIZ - 4;
911 	for (mp = cp; *mp; mp++)
912 	    if (matchs(mp, lhsb)) {
913 		for (np = cp; np < mp;)
914 		    *wp++ = *np++, --i;
915 		for (np = rhsb; *np; np++)
916 		    switch (*np) {
917 
918 		    case '\\':
919 			if (np[1] == '&')
920 			    np++;
921 			/* fall into ... */
922 
923 		    default:
924 			if (--i < 0) {
925 			    seterror(ERR_SUBOVFL);
926 			    return (STRNULL);
927 			}
928 			*wp++ = *np;
929 			continue;
930 
931 		    case '&':
932 			i -= Strlen(lhsb);
933 			if (i < 0) {
934 			    seterror(ERR_SUBOVFL);
935 			    return (STRNULL);
936 			}
937 			*wp = 0;
938 			(void)Strlcat(wp, lhsb,
939 			    sizeof(wbuf)/sizeof(Char) - (wp - wbuf));
940 			wp = Strend(wp);
941 			continue;
942 		    }
943 		mp += Strlen(lhsb);
944 		i -= Strlen(mp);
945 		if (i < 0) {
946 		    seterror(ERR_SUBOVFL);
947 		    return (STRNULL);
948 		}
949 		*wp = 0;
950 		(void)Strlcat(wp, mp,
951 		    sizeof(wbuf)/sizeof(Char) - (wp - wbuf));
952 		*adid = 1;
953 		return (Strsave(wbuf));
954 	    }
955 	return (Strsave(cp));
956     }
957 }
958 
959 Char   *
960 domod(Char *cp, int type)
961 {
962     Char *wp, *xp;
963     int c;
964 
965     switch (type) {
966 
967     case 'x':
968     case 'q':
969 	wp = Strsave(cp);
970 	for (xp = wp; (c = *xp) != '\0'; xp++)
971 	    if ((c != ' ' && c != '\t') || type == 'q')
972 		*xp |= QUOTE;
973 	return (wp);
974 
975     case 'h':
976     case 't':
977 	if (!any(short2str(cp), '/'))
978 	    return (type == 't' ? Strsave(cp) : 0);
979 	wp = Strend(cp);
980 	while (*--wp != '/')
981 	    continue;
982 	if (type == 'h')
983 	    xp = Strsave(cp), xp[wp - cp] = 0;
984 	else
985 	    xp = Strsave(wp + 1);
986 	return (xp);
987 
988     case 'e':
989     case 'r':
990 	wp = Strend(cp);
991 	for (wp--; wp >= cp && *wp != '/'; wp--)
992 	    if (*wp == '.') {
993 		if (type == 'e')
994 		    xp = Strsave(wp + 1);
995 		else
996 		    xp = Strsave(cp), xp[wp - cp] = 0;
997 		return (xp);
998 	    }
999 	return (Strsave(type == 'e' ? STRNULL : cp));
1000     default:
1001 	break;
1002     }
1003     return (0);
1004 }
1005 
1006 static int
1007 matchs(Char *str, Char *pat)
1008 {
1009     while (*str && *pat && *str == *pat)
1010 	str++, pat++;
1011     return (*pat == 0);
1012 }
1013 
1014 static int
1015 getsel(int *al, int *ar, int dol)
1016 {
1017     int c = getC(0);
1018     int i;
1019     bool    first = *al < 0;
1020 
1021     switch (c) {
1022 
1023     case '%':
1024 	if (quesarg == -1) {
1025 	    seterror(ERR_BADBANGARG);
1026 	    return (0);
1027 	}
1028 	if (*al < 0)
1029 	    *al = quesarg;
1030 	*ar = quesarg;
1031 	break;
1032 
1033     case '-':
1034 	if (*al < 0) {
1035 	    *al = 0;
1036 	    *ar = dol - 1;
1037 	    unreadc(c);
1038 	}
1039 	return (1);
1040 
1041     case '^':
1042 	if (*al < 0)
1043 	    *al = 1;
1044 	*ar = 1;
1045 	break;
1046 
1047     case '$':
1048 	if (*al < 0)
1049 	    *al = dol;
1050 	*ar = dol;
1051 	break;
1052 
1053     case '*':
1054 	if (*al < 0)
1055 	    *al = 1;
1056 	*ar = dol;
1057 	if (*ar < *al) {
1058 	    *ar = 0;
1059 	    *al = 1;
1060 	    return (1);
1061 	}
1062 	break;
1063 
1064     default:
1065 	if (Isdigit(c)) {
1066 	    i = 0;
1067 	    while (Isdigit(c)) {
1068 		i = i * 10 + c - '0';
1069 		c = getC(0);
1070 	    }
1071 	    if (i < 0)
1072 		i = dol + 1;
1073 	    if (*al < 0)
1074 		*al = i;
1075 	    *ar = i;
1076 	}
1077 	else if (*al < 0)
1078 	    *al = 0, *ar = dol;
1079 	else
1080 	    *ar = dol - 1;
1081 	unreadc(c);
1082 	break;
1083     }
1084     if (first) {
1085 	c = getC(0);
1086 	unreadc(c);
1087 	if (any("-$*", c))
1088 	    return (1);
1089     }
1090     if (*al > *ar || *ar > dol) {
1091 	seterror(ERR_BADBANGARG);
1092 	return (0);
1093     }
1094     return (1);
1095 
1096 }
1097 
1098 static struct wordent *
1099 gethent(int sc)
1100 {
1101     struct Hist *hp;
1102     Char *np;
1103     int c;
1104     int     event;
1105     bool    back = 0;
1106 
1107     c = sc == HISTSUB ? HIST : getC(0);
1108     if (c == HIST) {
1109 	if (alhistp)
1110 	    return (alhistp);
1111 	event = eventno;
1112     }
1113     else
1114 	switch (c) {
1115 
1116 	case ':':
1117 	case '^':
1118 	case '$':
1119 	case '*':
1120 	case '%':
1121 	    ungetC(c);
1122 	    if (lastev == eventno && alhistp)
1123 		return (alhistp);
1124 	    event = lastev;
1125 	    break;
1126 
1127 	case '#':		/* !# is command being typed in (mrh) */
1128 	    if (--hleft == 0) {
1129 		seterror(ERR_HISTLOOP);
1130 		return (0);
1131 	    }
1132 	    else
1133 		return (&paraml);
1134 	    /* NOTREACHED */
1135 
1136 	case '-':
1137 	    back = 1;
1138 	    c = getC(0);
1139 	    /* FALLSTHROUGH */
1140 
1141 	default:
1142 	    if (any("(=~", c)) {
1143 		unreadc(c);
1144 		ungetC(HIST);
1145 		return (0);
1146 	    }
1147 	    np = lhsb;
1148 	    event = 0;
1149 	    while (!cmap(c, _ESC | _META | _QF | _QB) && !any("${}:", c)) {
1150 		if (event != -1 && Isdigit(c))
1151 		    event = event * 10 + c - '0';
1152 		else
1153 		    event = -1;
1154 		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1155 		    *np++ = c;
1156 		c = getC(0);
1157 	    }
1158 	    unreadc(c);
1159 	    if (np == lhsb) {
1160 		ungetC(HIST);
1161 		return (0);
1162 	    }
1163 	    *np++ = 0;
1164 	    if (event != -1) {
1165 		/*
1166 		 * History had only digits
1167 		 */
1168 		if (back)
1169 		    event = eventno + (alhistp == 0) - (event ? event : 0);
1170 		break;
1171 	    }
1172 	    hp = findev(lhsb, 0);
1173 	    if (hp)
1174 		lastev = hp->Hnum;
1175 	    return (&hp->Hlex);
1176 
1177 	case '?':
1178 	    np = lhsb;
1179 	    for (;;) {
1180 		c = getC(0);
1181 		if (c == '\n') {
1182 		    unreadc(c);
1183 		    break;
1184 		}
1185 		if (c == '?')
1186 		    break;
1187 		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1188 		    *np++ = c;
1189 	    }
1190 	    if (np == lhsb) {
1191 		if (lhsb[0] == 0) {
1192 		    seterror(ERR_NOSEARCH);
1193 		    return (0);
1194 		}
1195 	    }
1196 	    else
1197 		*np++ = 0;
1198 	    hp = findev(lhsb, 1);
1199 	    if (hp)
1200 		lastev = hp->Hnum;
1201 	    return (&hp->Hlex);
1202 	}
1203 
1204     for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1205 	if (hp->Hnum == event) {
1206 	    hp->Href = eventno;
1207 	    lastev = hp->Hnum;
1208 	    return (&hp->Hlex);
1209 	}
1210     np = putn(event);
1211     seterror(ERR_NOEVENT, vis_str(np));
1212     return (0);
1213 }
1214 
1215 static struct Hist *
1216 findev(Char *cp, bool anyarg)
1217 {
1218     struct Hist *hp;
1219 
1220     for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1221 	Char   *dp;
1222 	Char *p, *q;
1223 	struct wordent *lp = hp->Hlex.next;
1224 	int     argno = 0;
1225 
1226 	/*
1227 	 * The entries added by alias substitution don't have a newline but do
1228 	 * have a negative event number. Savehist() trims off these entries,
1229 	 * but it happens before alias expansion, too early to delete those
1230 	 * from the previous command.
1231 	 */
1232 	if (hp->Hnum < 0)
1233 	    continue;
1234 	if (lp->word[0] == '\n')
1235 	    continue;
1236 	if (!anyarg) {
1237 	    p = cp;
1238 	    q = lp->word;
1239 	    do
1240 		if (!*p)
1241 		    return (hp);
1242 	    while (*p++ == *q++);
1243 	    continue;
1244 	}
1245 	do {
1246 	    for (dp = lp->word; *dp; dp++) {
1247 		p = cp;
1248 		q = dp;
1249 		do
1250 		    if (!*p) {
1251 			quesarg = argno;
1252 			return (hp);
1253 		    }
1254 		while (*p++ == *q++);
1255 	    }
1256 	    lp = lp->next;
1257 	    argno++;
1258 	} while (lp->word[0] != '\n');
1259     }
1260     seterror(ERR_NOEVENT, vis_str(cp));
1261     return (0);
1262 }
1263 
1264 
1265 static void
1266 setexclp(Char *cp)
1267 {
1268     if (cp && cp[0] == '\n')
1269 	return;
1270     exclp = cp;
1271 }
1272 
1273 void
1274 unreadc(int c)
1275 {
1276     peekread = c;
1277 }
1278 
1279 int
1280 readc(bool wanteof)
1281 {
1282     int c;
1283     static int sincereal;
1284 
1285     aret = F_SEEK;
1286     if ((c = peekread) != '\0') {
1287 	peekread = 0;
1288 	return (c);
1289     }
1290 top:
1291     aret = F_SEEK;
1292     if (alvecp) {
1293 	aret = A_SEEK;
1294 	if ((c = *alvecp++) != '\0')
1295 	    return (c);
1296 	if (alvec && *alvec) {
1297 		alvecp = *alvec++;
1298 		return (' ');
1299 	}
1300 	else {
1301 	    aret = F_SEEK;
1302 	    alvecp = NULL;
1303 	    return('\n');
1304 	}
1305     }
1306     if (alvec) {
1307 	if ((alvecp = *alvec) != '\0') {
1308 	    alvec++;
1309 	    goto top;
1310 	}
1311 	/* Infinite source! */
1312 	return ('\n');
1313     }
1314     if (evalp) {
1315 	aret = E_SEEK;
1316 	if ((c = *evalp++) != '\0')
1317 	    return (c);
1318 	if (evalvec && *evalvec) {
1319 	    evalp = *evalvec++;
1320 	    return (' ');
1321 	}
1322 	aret = F_SEEK;
1323 	evalp = 0;
1324     }
1325     if (evalvec) {
1326 	if (evalvec == (Char **) 1) {
1327 	    doneinp = 1;
1328 	    reset();
1329 	}
1330 	if ((evalp = *evalvec) != '\0') {
1331 	    evalvec++;
1332 	    goto top;
1333 	}
1334 	evalvec = (Char **) 1;
1335 	return ('\n');
1336     }
1337     do {
1338 	if (arginp == (Char *) 1 || onelflg == 1) {
1339 	    if (wanteof)
1340 		return (-1);
1341 	    exitstat();
1342 	}
1343 	if (arginp) {
1344 	    if ((c = *arginp++) == 0) {
1345 		arginp = (Char *) 1;
1346 		return ('\n');
1347 	    }
1348 	    return (c);
1349 	}
1350 reread:
1351 	c = bgetc();
1352 	if (c < 0) {
1353 	    struct termios tty;
1354 	    if (wanteof)
1355 		return (-1);
1356 	    /* was isatty but raw with ignoreeof yields problems */
1357 	    if (tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON))
1358 	    {
1359 		/* was 'short' for FILEC */
1360 		pid_t     ctpgrp;
1361 
1362 		if (++sincereal > 25)
1363 		    goto oops;
1364 		if (tpgrp != -1 &&
1365 		    (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
1366 		    tpgrp != ctpgrp) {
1367 		    (void) tcsetpgrp(FSHTTY, tpgrp);
1368 		    (void) kill(-ctpgrp, SIGHUP);
1369 		    (void) fprintf(csherr, "Reset tty pgrp from %d to %d\n",
1370 				   ctpgrp, tpgrp);
1371 		    goto reread;
1372 		}
1373 		if (adrof(STRignoreeof)) {
1374 		    if (loginsh)
1375 			(void) fprintf(csherr,"\nUse \"logout\" to logout.\n");
1376 		    else
1377 			(void) fprintf(csherr,"\nUse \"exit\" to leave csh.\n");
1378 		    reset();
1379 		}
1380 		if (chkstop == 0)
1381 		    panystop(1);
1382 	    }
1383     oops:
1384 	    doneinp = 1;
1385 	    reset();
1386 	}
1387 	sincereal = 0;
1388 	if (c == '\n' && onelflg)
1389 	    onelflg--;
1390     } while (c == 0);
1391     return (c);
1392 }
1393 
1394 static int
1395 bgetc(void)
1396 {
1397     int buf, off, c;
1398 
1399 #ifdef FILEC
1400     int numleft = 0, roomleft;
1401     Char    ttyline[BUFSIZ];
1402 #endif
1403     char    tbuf[BUFSIZ + 1];
1404 
1405     if (cantell) {
1406 	if (fseekp < fbobp || fseekp > feobp) {
1407 	    fbobp = feobp = fseekp;
1408 	    (void) lseek(SHIN, fseekp, SEEK_SET);
1409 	}
1410 	if (fseekp == feobp) {
1411 	    int     i;
1412 
1413 	    fbobp = feobp;
1414 	    do
1415 		c = read(SHIN, tbuf, BUFSIZ);
1416 	    while (c < 0 && errno == EINTR);
1417 	    if (c <= 0)
1418 		return (-1);
1419 	    for (i = 0; i < c; i++)
1420 		fbuf[0][i] = (unsigned char) tbuf[i];
1421 	    feobp += c;
1422 	}
1423 	c = fbuf[0][fseekp - fbobp];
1424 	fseekp++;
1425 	return (c);
1426     }
1427 
1428 again:
1429     buf = (int) fseekp / BUFSIZ;
1430     if (buf >= fblocks) {
1431 	Char **nfbuf =
1432 	(Char **) xcalloc((size_t) (fblocks + 2),
1433 			  sizeof(Char **));
1434 
1435 	if (fbuf) {
1436 	    (void) blkcpy(nfbuf, fbuf);
1437 	    xfree((ptr_t) fbuf);
1438 	}
1439 	fbuf = nfbuf;
1440 	fbuf[fblocks] = (Char *) xcalloc(BUFSIZ, sizeof(Char));
1441 	fblocks++;
1442 	if (!intty)
1443 	    goto again;
1444     }
1445     if (fseekp >= feobp) {
1446 	buf = (int) feobp / BUFSIZ;
1447 	off = (int) feobp % BUFSIZ;
1448 	roomleft = BUFSIZ - off;
1449 
1450 #ifdef FILEC
1451 	roomleft = BUFSIZ - off;
1452 	for (;;) {
1453 	    if (filec && intty) {
1454 		c = numleft ? numleft : tenex(ttyline, BUFSIZ);
1455 		if (c > roomleft) {
1456 		    /* start with fresh buffer */
1457 		    feobp = fseekp = fblocks * BUFSIZ;
1458 		    numleft = c;
1459 		    goto again;
1460 		}
1461 		if (c > 0)
1462 		    memcpy(fbuf[buf] + off, ttyline, c * sizeof(Char));
1463 		numleft = 0;
1464 	    }
1465 	    else {
1466 #endif
1467 		c = read(SHIN, tbuf, roomleft);
1468 		if (c > 0) {
1469 		    int     i;
1470 		    Char   *ptr = fbuf[buf] + off;
1471 
1472 		    for (i = 0; i < c; i++)
1473 			ptr[i] = (unsigned char) tbuf[i];
1474 		}
1475 #ifdef FILEC
1476 	    }
1477 #endif
1478 	    if (c >= 0)
1479 		break;
1480 	    if (errno == EWOULDBLOCK) {
1481 		int     off = 0;
1482 
1483 		(void) ioctl(SHIN, FIONBIO, (ioctl_t) & off);
1484 	    }
1485 	    else if (errno != EINTR)
1486 		break;
1487 	}
1488 	if (c <= 0)
1489 	    return (-1);
1490 	feobp += c;
1491 #ifndef FILEC
1492 	goto again;
1493 #else
1494 	if (filec && !intty)
1495 	    goto again;
1496 #endif
1497     }
1498     c = fbuf[buf][(int) fseekp % BUFSIZ];
1499     fseekp++;
1500     return (c);
1501 }
1502 
1503 static void
1504 bfree(void)
1505 {
1506     int sb, i;
1507 
1508     if (cantell)
1509 	return;
1510     if (whyles)
1511 	return;
1512     sb = (int) (fseekp - 1) / BUFSIZ;
1513     if (sb > 0) {
1514 	for (i = 0; i < sb; i++)
1515 	    xfree((ptr_t) fbuf[i]);
1516 	(void) blkcpy(fbuf, &fbuf[sb]);
1517 	fseekp -= BUFSIZ * sb;
1518 	feobp -= BUFSIZ * sb;
1519 	fblocks -= sb;
1520     }
1521 }
1522 
1523 void
1524 bseek(struct Ain *l)
1525 {
1526     switch (aret = l->type) {
1527     case E_SEEK:
1528 	evalvec = l->a_seek;
1529 	evalp = l->c_seek;
1530 	return;
1531     case A_SEEK:
1532 	alvec = l->a_seek;
1533 	alvecp = l->c_seek;
1534 	return;
1535     case F_SEEK:
1536 	fseekp = l->f_seek;
1537 	return;
1538     default:
1539 	(void) fprintf(csherr, "Bad seek type %d\n", aret);
1540 	abort();
1541     }
1542 }
1543 
1544 void
1545 btell(struct Ain *l)
1546 {
1547     switch (l->type = aret) {
1548     case E_SEEK:
1549 	l->a_seek = evalvec;
1550 	l->c_seek = evalp;
1551 	return;
1552     case A_SEEK:
1553 	l->a_seek = alvec;
1554 	l->c_seek = alvecp;
1555 	return;
1556     case F_SEEK:
1557 	l->f_seek = fseekp;
1558 	l->a_seek = NULL;
1559 	return;
1560     default:
1561 	(void) fprintf(csherr, "Bad seek type %d\n", aret);
1562 	abort();
1563     }
1564 }
1565 
1566 void
1567 btoeof(void)
1568 {
1569     (void) lseek(SHIN, (off_t) 0, SEEK_END);
1570     aret = F_SEEK;
1571     fseekp = feobp;
1572     alvec = NULL;
1573     alvecp = NULL;
1574     evalvec = NULL;
1575     evalp = NULL;
1576     wfree();
1577     bfree();
1578 }
1579 
1580 void
1581 settell(void)
1582 {
1583     cantell = 0;
1584     if (arginp || onelflg || intty)
1585 	return;
1586     if (lseek(SHIN, (off_t) 0, SEEK_CUR) < 0 || errno == ESPIPE)
1587 	return;
1588     fbuf = (Char **) xcalloc(2, sizeof(Char **));
1589     fblocks = 1;
1590     fbuf[0] = (Char *) xcalloc(BUFSIZ, sizeof(Char));
1591     fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, SEEK_CUR);
1592     cantell = 1;
1593 }
1594