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