xref: /openbsd/bin/csh/lex.c (revision 3aaa63eb)
1 /*	$OpenBSD: lex.c,v 1.31 2019/06/28 13:34:58 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 <termios.h>
35 #include <errno.h>
36 #include <fcntl.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
lex(struct wordent * hp)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 = 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
prlex(FILE * fp,struct wordent * sp0)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
copylex(struct wordent * hp,struct wordent * fp)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 = 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
freelex(struct wordent * vp)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 	free(fp->word);
215 	free(fp);
216     }
217     vp->prev = vp;
218 }
219 
220 static Char *
word(void)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
getC1(int flag)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
getdol(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 	    /* we know that np < &name[4] */
449 	    ep = &np[MAXVARLEN];
450 	    while ((c = getC(DOEXCL)) != '\0'){
451 		if (!Isdigit(c))
452 		    break;
453 		if (np < ep)
454 		    *np++ = c;
455 		else
456 		    toolong = 1;
457 	    }
458 	}
459 	else if (letter(c)) {
460 	    /* we know that np < &name[4] */
461 	    ep = &np[MAXVARLEN];
462 	    toolong = 0;
463 	    while ((c = getC(DOEXCL)) != '\0') {
464 		/* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
465 		if (!letter(c) && !Isdigit(c))
466 		    break;
467 		if (np < ep)
468 		    *np++ = c;
469 		else
470 		    toolong = 1;
471 	    }
472 	}
473 	else {
474 	    *np = 0;
475 	    seterror(ERR_VARILL);
476 	    addla(name);
477 	    return;
478 	}
479 	if (toolong) {
480 	    seterror(ERR_VARTOOLONG);
481 	    *np = 0;
482 	    addla(name);
483 	    return;
484 	}
485 	break;
486     }
487     if (c == '[') {
488 	*np++ = c;
489 	/*
490 	 * Name up to here is a max of MAXVARLEN + 8.
491 	 */
492 	ep = &np[2 * MAXVARLEN + 8];
493 	do {
494 	    /*
495 	     * Michael Greim: Allow $ expansion to take place in selector
496 	     * expressions. (limits the number of characters returned)
497 	     */
498 	    c = getC(DOEXCL | DODOL);
499 	    if (c == '\n') {
500 		ungetD(c);
501 		np--;
502 		seterror(ERR_NLINDEX);
503 		*np = 0;
504 		addla(name);
505 		return;
506 	    }
507 	    if (np < ep)
508 		*np++ = c;
509 	} while (c != ']');
510 	*np = '\0';
511 	if (np >= ep) {
512 	    seterror(ERR_SELOVFL);
513 	    addla(name);
514 	    return;
515 	}
516 	c = getC(DOEXCL);
517     }
518     /*
519      * Name up to here is a max of 2 * MAXVARLEN + 8.
520      */
521     if (c == ':') {
522 	/*
523 	 * if the :g modifier is followed by a newline, then error right away!
524 	 * -strike
525 	 */
526 
527 	int     gmodflag = 0, amodflag = 0;
528 
529 	do {
530 	    *np++ = c, c = getC(DOEXCL);
531 	    if (c == 'g' || c == 'a') {
532 		if (c == 'g')
533 		    gmodflag++;
534 		else
535 		    amodflag++;
536 		*np++ = c; c = getC(DOEXCL);
537 	    }
538 	    if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
539 		if (c == 'g')
540 		    gmodflag++;
541 		else
542 		    amodflag++;
543 		*np++ = c; c = getC(DOEXCL);
544 	    }
545 	    *np++ = c;
546 	    /* scan s// [eichin:19910926.0512EST] */
547 	    if (c == 's') {
548 		int delimcnt = 2;
549 		int delim = getC(0);
550 		*np++ = delim;
551 
552 		if (!delim || letter(delim)
553 		    || Isdigit(delim) || any(" \t\n", delim)) {
554 		    seterror(ERR_BADSUBST);
555 		    break;
556 		}
557 		while ((c = getC(0)) != (-1)) {
558 		    *np++ = c;
559 		    if(c == delim) delimcnt--;
560 		    if(!delimcnt) break;
561 		}
562 		if(delimcnt) {
563 		    seterror(ERR_BADSUBST);
564 		    break;
565 		}
566 		c = 's';
567 	    }
568 	    if (!any("htrqxes", c)) {
569 		if ((amodflag || gmodflag) && c == '\n')
570 		    stderror(ERR_VARSYN);	/* strike */
571 		seterror(ERR_VARMOD, c);
572 		*np = 0;
573 		addla(name);
574 		return;
575 	    }
576 	}
577 	while ((c = getC(DOEXCL)) == ':');
578 	ungetD(c);
579     }
580     else
581 	ungetD(c);
582     if (sc == '{') {
583 	c = getC(DOEXCL);
584 	if (c != '}') {
585 	    ungetD(c);
586 	    seterror(ERR_MISSING, '}');
587 	    *np = 0;
588 	    addla(name);
589 	    return;
590 	}
591 	*np++ = c;
592     }
593     *np = 0;
594     addla(name);
595     return;
596 }
597 
598 void
addla(Char * cp)599 addla(Char *cp)
600 {
601     Char    buf[BUFSIZ];
602 
603     if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
604 	(sizeof(labuf) - 4) / sizeof(Char)) {
605 	seterror(ERR_EXPOVFL);
606 	return;
607     }
608     if (lap)
609 	(void) Strlcpy(buf, lap, sizeof buf/sizeof(Char));
610     (void) Strlcpy(labuf, cp, sizeof labuf/sizeof(Char));
611     if (lap)
612 	(void) Strlcat(labuf, buf, sizeof labuf/sizeof(Char));
613     lap = labuf;
614 }
615 
616 static Char lhsb[32];
617 static Char slhs[32];
618 static Char rhsb[64];
619 static int quesarg;
620 
621 static void
getexcl(int sc)622 getexcl(int sc)
623 {
624     struct wordent *hp, *ip;
625     int     left, right, dol;
626     int c;
627 
628     if (sc == 0) {
629 	sc = getC(0);
630 	if (sc != '{') {
631 	    ungetC(sc);
632 	    sc = 0;
633 	}
634     }
635     quesarg = -1;
636     lastev = eventno;
637     hp = gethent(sc);
638     if (hp == 0)
639 	return;
640     hadhist = 1;
641     dol = 0;
642     if (hp == alhistp)
643 	for (ip = hp->next->next; ip != alhistt; ip = ip->next)
644 	    dol++;
645     else
646 	for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
647 	    dol++;
648     left = 0, right = dol;
649     if (sc == HISTSUB) {
650 	ungetC('s'), unreadc(HISTSUB), c = ':';
651 	goto subst;
652     }
653     c = getC(0);
654     if (!any(":^$*-%", c))
655 	goto subst;
656     left = right = -1;
657     if (c == ':') {
658 	c = getC(0);
659 	unreadc(c);
660 	if (letter(c) || c == '&') {
661 	    c = ':';
662 	    left = 0, right = dol;
663 	    goto subst;
664 	}
665     }
666     else
667 	ungetC(c);
668     if (!getsel(&left, &right, dol))
669 	return;
670     c = getC(0);
671     if (c == '*')
672 	ungetC(c), c = '-';
673     if (c == '-') {
674 	if (!getsel(&left, &right, dol))
675 	    return;
676 	c = getC(0);
677     }
678 subst:
679     exclc = right - left + 1;
680     while (--left >= 0)
681 	hp = hp->next;
682     if (sc == HISTSUB || c == ':') {
683 	do {
684 	    hp = getsub(hp);
685 	    c = getC(0);
686 	} while (c == ':');
687     }
688     unreadc(c);
689     if (sc == '{') {
690 	c = getC(0);
691 	if (c != '}')
692 	    seterror(ERR_BADBANG);
693     }
694     exclnxt = hp;
695 }
696 
697 static struct wordent *
getsub(struct wordent * en)698 getsub(struct wordent *en)
699 {
700     Char *cp;
701     int     delim;
702     int c;
703     int     sc;
704     bool global;
705     Char    orhsb[sizeof(rhsb) / sizeof(Char)];
706 
707     do {
708 	exclnxt = 0;
709 	global = 0;
710 	sc = c = getC(0);
711 	if (c == 'g' || c == 'a') {
712 	    global |= (c == 'g') ? 1 : 2;
713 	    sc = c = getC(0);
714 	}
715 	if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
716 	    global |= (c == 'g') ? 1 : 2;
717 	    sc = c = getC(0);
718 	}
719 
720 	switch (c) {
721 	case 'p':
722 	    justpr++;
723 	    return (en);
724 
725 	case 'x':
726 	case 'q':
727 	    global |= 1;
728 
729 	    /* fall into ... */
730 
731 	case 'h':
732 	case 'r':
733 	case 't':
734 	case 'e':
735 	    break;
736 
737 	case '&':
738 	    if (slhs[0] == 0) {
739 		seterror(ERR_NOSUBST);
740 		return (en);
741 	    }
742 	    (void) Strlcpy(lhsb, slhs, sizeof(lhsb)/sizeof(Char));
743 	    break;
744 
745 	case 's':
746 	    delim = getC(0);
747 	    if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
748 		unreadc(delim);
749 		lhsb[0] = 0;
750 		seterror(ERR_BADSUBST);
751 		return (en);
752 	    }
753 	    cp = lhsb;
754 	    for (;;) {
755 		c = getC(0);
756 		if (c == '\n') {
757 		    unreadc(c);
758 		    break;
759 		}
760 		if (c == delim)
761 		    break;
762 		if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) {
763 		    lhsb[0] = 0;
764 		    seterror(ERR_BADSUBST);
765 		    return (en);
766 		}
767 		if (c == '\\') {
768 		    c = getC(0);
769 		    if (c != delim && c != '\\')
770 			*cp++ = '\\';
771 		}
772 		*cp++ = c;
773 	    }
774 	    if (cp != lhsb)
775 		*cp++ = 0;
776 	    else if (lhsb[0] == 0) {
777 		seterror(ERR_LHS);
778 		return (en);
779 	    }
780 	    cp = rhsb;
781 	    (void) Strlcpy(orhsb, cp, sizeof(orhsb)/sizeof(Char));
782 	    for (;;) {
783 		c = getC(0);
784 		if (c == '\n') {
785 		    unreadc(c);
786 		    break;
787 		}
788 		if (c == delim)
789 		    break;
790 		if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
791 		    seterror(ERR_RHSLONG);
792 		    return (en);
793 		}
794 		if (c == '\\') {
795 		    c = getC(0);
796 		    if (c != delim /* && c != '~' */ )
797 			*cp++ = '\\';
798 		}
799 		*cp++ = c;
800 	    }
801 	    *cp++ = 0;
802 	    break;
803 
804 	default:
805 	    if (c == '\n')
806 		unreadc(c);
807 	    seterror(ERR_BADBANGMOD, c);
808 	    return (en);
809 	}
810 	(void) Strlcpy(slhs, lhsb, sizeof(slhs)/sizeof(Char));
811 	if (exclc)
812 	    en = dosub(sc, en, global);
813     }
814     while ((c = getC(0)) == ':');
815     unreadc(c);
816     return (en);
817 }
818 
819 static struct wordent *
dosub(int sc,struct wordent * en,bool global)820 dosub(int sc, struct wordent *en, bool global)
821 {
822     struct wordent lexi;
823     bool    didsub = 0, didone = 0;
824     struct wordent *hp = &lexi;
825     struct wordent *wdp;
826     int i = exclc;
827 
828     wdp = hp;
829     while (--i >= 0) {
830 	struct wordent *new = xcalloc(1, sizeof *wdp);
831 
832 	new->word = 0;
833 	new->prev = wdp;
834 	new->next = hp;
835 	wdp->next = new;
836 	wdp = new;
837 	en = en->next;
838 	if (en->word) {
839 	    Char *tword, *otword;
840 
841 	    if ((global & 1) || didsub == 0) {
842 		tword = subword(en->word, sc, &didone);
843 		if (didone)
844 		    didsub = 1;
845 		if (global & 2) {
846 		    while (didone && tword != STRNULL) {
847 			otword = tword;
848 			tword = subword(otword, sc, &didone);
849 			if (Strcmp(tword, otword) == 0) {
850 			    free(otword);
851 			    break;
852 			}
853 			else
854 			    free(otword);
855 		    }
856 		}
857 	    }
858 	    else
859 		tword = Strsave(en->word);
860 	    wdp->word = tword;
861 	}
862     }
863     if (didsub == 0)
864 	seterror(ERR_MODFAIL);
865     hp->prev = wdp;
866     return (&enthist(-1000, &lexi, 0)->Hlex);
867 }
868 
869 static Char *
subword(Char * cp,int type,bool * adid)870 subword(Char *cp, int type, bool *adid)
871 {
872     Char    wbuf[BUFSIZ];
873     Char *wp, *mp, *np;
874     int i;
875 
876     *adid = 0;
877     switch (type) {
878 
879     case 'r':
880     case 'e':
881     case 'h':
882     case 't':
883     case 'q':
884     case 'x':
885 	wp = domod(cp, type);
886 	if (wp == 0)
887 	    return (Strsave(cp));
888 	*adid = 1;
889 	return (wp);
890 
891     default:
892 	wp = wbuf;
893 	i = BUFSIZ - 4;
894 	for (mp = cp; *mp; mp++)
895 	    if (matchs(mp, lhsb)) {
896 		for (np = cp; np < mp;)
897 		    *wp++ = *np++, --i;
898 		for (np = rhsb; *np; np++)
899 		    switch (*np) {
900 
901 		    case '\\':
902 			if (np[1] == '&')
903 			    np++;
904 			/* fall into ... */
905 
906 		    default:
907 			if (--i < 0) {
908 			    seterror(ERR_SUBOVFL);
909 			    return (STRNULL);
910 			}
911 			*wp++ = *np;
912 			continue;
913 
914 		    case '&':
915 			i -= Strlen(lhsb);
916 			if (i < 0) {
917 			    seterror(ERR_SUBOVFL);
918 			    return (STRNULL);
919 			}
920 			*wp = 0;
921 			(void)Strlcat(wp, lhsb,
922 			    sizeof(wbuf)/sizeof(Char) - (wp - wbuf));
923 			wp = Strend(wp);
924 			continue;
925 		    }
926 		mp += Strlen(lhsb);
927 		i -= Strlen(mp);
928 		if (i < 0) {
929 		    seterror(ERR_SUBOVFL);
930 		    return (STRNULL);
931 		}
932 		*wp = 0;
933 		(void)Strlcat(wp, mp,
934 		    sizeof(wbuf)/sizeof(Char) - (wp - wbuf));
935 		*adid = 1;
936 		return (Strsave(wbuf));
937 	    }
938 	return (Strsave(cp));
939     }
940 }
941 
942 Char   *
domod(Char * cp,int type)943 domod(Char *cp, int type)
944 {
945     Char *wp, *xp;
946     int c;
947 
948     switch (type) {
949 
950     case 'x':
951     case 'q':
952 	wp = Strsave(cp);
953 	for (xp = wp; (c = *xp) != '\0'; xp++)
954 	    if ((c != ' ' && c != '\t') || type == 'q')
955 		*xp |= QUOTE;
956 	return (wp);
957 
958     case 'h':
959     case 't':
960 	if (!any(short2str(cp), '/'))
961 	    return (type == 't' ? Strsave(cp) : 0);
962 	wp = Strend(cp);
963 	while (*--wp != '/')
964 	    continue;
965 	if (type == 'h')
966 	    xp = Strsave(cp), xp[wp - cp] = 0;
967 	else
968 	    xp = Strsave(wp + 1);
969 	return (xp);
970 
971     case 'e':
972     case 'r':
973 	wp = Strend(cp);
974 	for (wp--; wp >= cp && *wp != '/'; wp--)
975 	    if (*wp == '.') {
976 		if (type == 'e')
977 		    xp = Strsave(wp + 1);
978 		else
979 		    xp = Strsave(cp), xp[wp - cp] = 0;
980 		return (xp);
981 	    }
982 	return (Strsave(type == 'e' ? STRNULL : cp));
983     default:
984 	break;
985     }
986     return (0);
987 }
988 
989 static int
matchs(Char * str,Char * pat)990 matchs(Char *str, Char *pat)
991 {
992     while (*str && *pat && *str == *pat)
993 	str++, pat++;
994     return (*pat == 0);
995 }
996 
997 static int
getsel(int * al,int * ar,int dol)998 getsel(int *al, int *ar, int dol)
999 {
1000     int c = getC(0);
1001     int i;
1002     bool    first = *al < 0;
1003 
1004     switch (c) {
1005 
1006     case '%':
1007 	if (quesarg == -1) {
1008 	    seterror(ERR_BADBANGARG);
1009 	    return (0);
1010 	}
1011 	if (*al < 0)
1012 	    *al = quesarg;
1013 	*ar = quesarg;
1014 	break;
1015 
1016     case '-':
1017 	if (*al < 0) {
1018 	    *al = 0;
1019 	    *ar = dol - 1;
1020 	    unreadc(c);
1021 	}
1022 	return (1);
1023 
1024     case '^':
1025 	if (*al < 0)
1026 	    *al = 1;
1027 	*ar = 1;
1028 	break;
1029 
1030     case '$':
1031 	if (*al < 0)
1032 	    *al = dol;
1033 	*ar = dol;
1034 	break;
1035 
1036     case '*':
1037 	if (*al < 0)
1038 	    *al = 1;
1039 	*ar = dol;
1040 	if (*ar < *al) {
1041 	    *ar = 0;
1042 	    *al = 1;
1043 	    return (1);
1044 	}
1045 	break;
1046 
1047     default:
1048 	if (Isdigit(c)) {
1049 	    i = 0;
1050 	    while (Isdigit(c)) {
1051 		i = i * 10 + c - '0';
1052 		c = getC(0);
1053 	    }
1054 	    if (i < 0)
1055 		i = dol + 1;
1056 	    if (*al < 0)
1057 		*al = i;
1058 	    *ar = i;
1059 	}
1060 	else if (*al < 0)
1061 	    *al = 0, *ar = dol;
1062 	else
1063 	    *ar = dol - 1;
1064 	unreadc(c);
1065 	break;
1066     }
1067     if (first) {
1068 	c = getC(0);
1069 	unreadc(c);
1070 	if (any("-$*", c))
1071 	    return (1);
1072     }
1073     if (*al > *ar || *ar > dol) {
1074 	seterror(ERR_BADBANGARG);
1075 	return (0);
1076     }
1077     return (1);
1078 
1079 }
1080 
1081 static struct wordent *
gethent(int sc)1082 gethent(int sc)
1083 {
1084     struct Hist *hp;
1085     Char *np;
1086     int c;
1087     int     event;
1088     bool    back = 0;
1089 
1090     c = sc == HISTSUB ? HIST : getC(0);
1091     if (c == HIST) {
1092 	if (alhistp)
1093 	    return (alhistp);
1094 	event = eventno;
1095     }
1096     else
1097 	switch (c) {
1098 
1099 	case ':':
1100 	case '^':
1101 	case '$':
1102 	case '*':
1103 	case '%':
1104 	    ungetC(c);
1105 	    if (lastev == eventno && alhistp)
1106 		return (alhistp);
1107 	    event = lastev;
1108 	    break;
1109 
1110 	case '#':		/* !# is command being typed in (mrh) */
1111 	    if (--hleft == 0) {
1112 		seterror(ERR_HISTLOOP);
1113 		return (0);
1114 	    }
1115 	    else
1116 		return (&paraml);
1117 	    /* NOTREACHED */
1118 
1119 	case '-':
1120 	    back = 1;
1121 	    c = getC(0);
1122 	    /* FALLSTHROUGH */
1123 
1124 	default:
1125 	    if (any("(=~", c)) {
1126 		unreadc(c);
1127 		ungetC(HIST);
1128 		return (0);
1129 	    }
1130 	    np = lhsb;
1131 	    event = 0;
1132 	    while (!cmap(c, _ESC | _META | _QF | _QB) && !any("${}:", c)) {
1133 		if (event != -1 && Isdigit(c))
1134 		    event = event * 10 + c - '0';
1135 		else
1136 		    event = -1;
1137 		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1138 		    *np++ = c;
1139 		c = getC(0);
1140 	    }
1141 	    unreadc(c);
1142 	    if (np == lhsb) {
1143 		ungetC(HIST);
1144 		return (0);
1145 	    }
1146 	    *np++ = 0;
1147 	    if (event != -1) {
1148 		/*
1149 		 * History had only digits
1150 		 */
1151 		if (back)
1152 		    event = eventno + (alhistp == 0) - (event ? event : 0);
1153 		break;
1154 	    }
1155 	    hp = findev(lhsb, 0);
1156 	    if (hp)
1157 		lastev = hp->Hnum;
1158 	    return (&hp->Hlex);
1159 
1160 	case '?':
1161 	    np = lhsb;
1162 	    for (;;) {
1163 		c = getC(0);
1164 		if (c == '\n') {
1165 		    unreadc(c);
1166 		    break;
1167 		}
1168 		if (c == '?')
1169 		    break;
1170 		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1171 		    *np++ = c;
1172 	    }
1173 	    if (np == lhsb) {
1174 		if (lhsb[0] == 0) {
1175 		    seterror(ERR_NOSEARCH);
1176 		    return (0);
1177 		}
1178 	    }
1179 	    else
1180 		*np++ = 0;
1181 	    hp = findev(lhsb, 1);
1182 	    if (hp)
1183 		lastev = hp->Hnum;
1184 	    return (&hp->Hlex);
1185 	}
1186 
1187     for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1188 	if (hp->Hnum == event) {
1189 	    hp->Href = eventno;
1190 	    lastev = hp->Hnum;
1191 	    return (&hp->Hlex);
1192 	}
1193     np = putn(event);
1194     seterror(ERR_NOEVENT, vis_str(np));
1195     return (0);
1196 }
1197 
1198 static struct Hist *
findev(Char * cp,bool anyarg)1199 findev(Char *cp, bool anyarg)
1200 {
1201     struct Hist *hp;
1202 
1203     for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1204 	Char   *dp;
1205 	Char *p, *q;
1206 	struct wordent *lp = hp->Hlex.next;
1207 	int     argno = 0;
1208 
1209 	/*
1210 	 * The entries added by alias substitution don't have a newline but do
1211 	 * have a negative event number. Savehist() trims off these entries,
1212 	 * but it happens before alias expansion, too early to delete those
1213 	 * from the previous command.
1214 	 */
1215 	if (hp->Hnum < 0)
1216 	    continue;
1217 	if (lp->word[0] == '\n')
1218 	    continue;
1219 	if (!anyarg) {
1220 	    p = cp;
1221 	    q = lp->word;
1222 	    do
1223 		if (!*p)
1224 		    return (hp);
1225 	    while (*p++ == *q++);
1226 	    continue;
1227 	}
1228 	do {
1229 	    for (dp = lp->word; *dp; dp++) {
1230 		p = cp;
1231 		q = dp;
1232 		do
1233 		    if (!*p) {
1234 			quesarg = argno;
1235 			return (hp);
1236 		    }
1237 		while (*p++ == *q++);
1238 	    }
1239 	    lp = lp->next;
1240 	    argno++;
1241 	} while (lp->word[0] != '\n');
1242     }
1243     seterror(ERR_NOEVENT, vis_str(cp));
1244     return (0);
1245 }
1246 
1247 
1248 static void
setexclp(Char * cp)1249 setexclp(Char *cp)
1250 {
1251     if (cp && cp[0] == '\n')
1252 	return;
1253     exclp = cp;
1254 }
1255 
1256 void
unreadc(int c)1257 unreadc(int c)
1258 {
1259     peekread = c;
1260 }
1261 
1262 int
readc(bool wanteof)1263 readc(bool wanteof)
1264 {
1265     int c;
1266     static int sincereal;
1267 
1268     aret = F_SEEK;
1269     if ((c = peekread) != '\0') {
1270 	peekread = 0;
1271 	return (c);
1272     }
1273 top:
1274     aret = F_SEEK;
1275     if (alvecp) {
1276 	aret = A_SEEK;
1277 	if ((c = *alvecp++) != '\0')
1278 	    return (c);
1279 	if (alvec && *alvec) {
1280 		alvecp = *alvec++;
1281 		return (' ');
1282 	}
1283 	else {
1284 	    aret = F_SEEK;
1285 	    alvecp = NULL;
1286 	    return('\n');
1287 	}
1288     }
1289     if (alvec) {
1290 	if ((alvecp = *alvec) != NULL) {
1291 	    alvec++;
1292 	    goto top;
1293 	}
1294 	/* Infinite source! */
1295 	return ('\n');
1296     }
1297     if (evalp) {
1298 	aret = E_SEEK;
1299 	if ((c = *evalp++) != '\0')
1300 	    return (c);
1301 	if (evalvec && *evalvec) {
1302 	    evalp = *evalvec++;
1303 	    return (' ');
1304 	}
1305 	aret = F_SEEK;
1306 	evalp = 0;
1307     }
1308     if (evalvec) {
1309 	if (evalvec == (Char **) 1) {
1310 	    doneinp = 1;
1311 	    reset();
1312 	}
1313 	if ((evalp = *evalvec) != NULL) {
1314 	    evalvec++;
1315 	    goto top;
1316 	}
1317 	evalvec = (Char **) 1;
1318 	return ('\n');
1319     }
1320     do {
1321 	if (arginp == (Char *) 1 || onelflg == 1) {
1322 	    if (wanteof)
1323 		return (-1);
1324 	    exitstat();
1325 	}
1326 	if (arginp) {
1327 	    if ((c = *arginp++) == 0) {
1328 		arginp = (Char *) 1;
1329 		return ('\n');
1330 	    }
1331 	    return (c);
1332 	}
1333 reread:
1334 	c = bgetc();
1335 	if (c < 0) {
1336 	    struct termios tty;
1337 	    if (wanteof)
1338 		return (-1);
1339 	    /* was isatty but raw with ignoreeof yields problems */
1340 	    if (isatty(SHIN) && tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON))
1341 	    {
1342 		pid_t     ctpgrp;
1343 
1344 		if (++sincereal > 25)
1345 		    goto oops;
1346 		if (tpgrp != -1 &&
1347 		    (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
1348 		    tpgrp != ctpgrp) {
1349 		    (void) tcsetpgrp(FSHTTY, tpgrp);
1350 		    (void) kill(-ctpgrp, SIGHUP);
1351 		    (void) fprintf(csherr, "Reset tty pgrp from %d to %d\n",
1352 				   ctpgrp, tpgrp);
1353 		    goto reread;
1354 		}
1355 		if (adrof(STRignoreeof)) {
1356 		    if (loginsh)
1357 			(void) fprintf(csherr,"\nUse \"logout\" to logout.\n");
1358 		    else
1359 			(void) fprintf(csherr,"\nUse \"exit\" to leave csh.\n");
1360 		    reset();
1361 		}
1362 		if (chkstop == 0)
1363 		    panystop(1);
1364 	    }
1365     oops:
1366 	    doneinp = 1;
1367 	    reset();
1368 	}
1369 	sincereal = 0;
1370 	if (c == '\n' && onelflg)
1371 	    onelflg--;
1372     } while (c == 0);
1373     return (c);
1374 }
1375 
1376 static int
bgetc(void)1377 bgetc(void)
1378 {
1379     int buf, off, c;
1380 
1381     int numleft = 0, roomleft;
1382     Char    ttyline[BUFSIZ];
1383     char    tbuf[BUFSIZ + 1];
1384 
1385     if (cantell) {
1386 	if (fseekp < fbobp || fseekp > feobp) {
1387 	    fbobp = feobp = fseekp;
1388 	    (void) lseek(SHIN, fseekp, SEEK_SET);
1389 	}
1390 	if (fseekp == feobp) {
1391 	    int     i;
1392 
1393 	    fbobp = feobp;
1394 	    do
1395 		c = read(SHIN, tbuf, BUFSIZ);
1396 	    while (c < 0 && errno == EINTR);
1397 	    if (c <= 0)
1398 		return (-1);
1399 	    for (i = 0; i < c; i++)
1400 		fbuf[0][i] = (unsigned char) tbuf[i];
1401 	    feobp += c;
1402 	}
1403 	c = fbuf[0][fseekp - fbobp];
1404 	fseekp++;
1405 	return (c);
1406     }
1407 
1408 again:
1409     buf = (int) fseekp / BUFSIZ;
1410     if (buf >= fblocks) {
1411 	Char **nfbuf = xcalloc(fblocks + 2, sizeof(*nfbuf));
1412 
1413 	if (fbuf) {
1414 	    (void) blkcpy(nfbuf, fbuf);
1415 	    free(fbuf);
1416 	}
1417 	fbuf = nfbuf;
1418 	fbuf[fblocks] = xcalloc(BUFSIZ, sizeof(Char));
1419 	fblocks++;
1420 	if (!intty)
1421 	    goto again;
1422     }
1423     if (fseekp >= feobp) {
1424 	buf = (int) feobp / BUFSIZ;
1425 	off = (int) feobp % BUFSIZ;
1426 	roomleft = BUFSIZ - off;
1427 
1428 	for (;;) {
1429 	    if (filec && intty) {
1430 		c = numleft ? numleft : tenex(ttyline, BUFSIZ);
1431 		if (c > roomleft) {
1432 		    /* start with fresh buffer */
1433 		    feobp = fseekp = fblocks * BUFSIZ;
1434 		    numleft = c;
1435 		    goto again;
1436 		}
1437 		if (c > 0)
1438 		    memcpy(fbuf[buf] + off, ttyline, c * sizeof(Char));
1439 		numleft = 0;
1440 	    }
1441 	    else {
1442 		c = read(SHIN, tbuf, roomleft);
1443 		if (c > 0) {
1444 		    int     i;
1445 		    Char   *ptr = fbuf[buf] + off;
1446 
1447 		    for (i = 0; i < c; i++)
1448 			ptr[i] = (unsigned char) tbuf[i];
1449 		}
1450 	    }
1451 	    if (c >= 0)
1452 		break;
1453 	    if (errno == EWOULDBLOCK) {
1454 		int     flags;
1455 
1456 		flags = fcntl(SHIN, F_GETFL);
1457 		(void) fcntl(SHIN, F_SETFL, (flags & ~O_NONBLOCK));
1458 	    }
1459 	    else if (errno != EINTR)
1460 		break;
1461 	}
1462 	if (c <= 0)
1463 	    return (-1);
1464 	feobp += c;
1465 	if (filec && !intty)
1466 	    goto again;
1467     }
1468     c = fbuf[buf][(int) fseekp % BUFSIZ];
1469     fseekp++;
1470     return (c);
1471 }
1472 
1473 static void
bfree(void)1474 bfree(void)
1475 {
1476     int sb, i;
1477 
1478     if (cantell)
1479 	return;
1480     if (whyles)
1481 	return;
1482     sb = (int) (fseekp - 1) / BUFSIZ;
1483     if (sb > 0) {
1484 	for (i = 0; i < sb; i++)
1485 	    free(fbuf[i]);
1486 	(void) blkcpy(fbuf, &fbuf[sb]);
1487 	fseekp -= BUFSIZ * sb;
1488 	feobp -= BUFSIZ * sb;
1489 	fblocks -= sb;
1490     }
1491 }
1492 
1493 void
bseek(struct Ain * l)1494 bseek(struct Ain *l)
1495 {
1496     switch (aret = l->type) {
1497     case E_SEEK:
1498 	evalvec = l->a_seek;
1499 	evalp = l->c_seek;
1500 	return;
1501     case A_SEEK:
1502 	alvec = l->a_seek;
1503 	alvecp = l->c_seek;
1504 	return;
1505     case F_SEEK:
1506 	fseekp = l->f_seek;
1507 	return;
1508     default:
1509 	(void) fprintf(csherr, "Bad seek type %d\n", aret);
1510 	abort();
1511     }
1512 }
1513 
1514 void
btell(struct Ain * l)1515 btell(struct Ain *l)
1516 {
1517     switch (l->type = aret) {
1518     case E_SEEK:
1519 	l->a_seek = evalvec;
1520 	l->c_seek = evalp;
1521 	return;
1522     case A_SEEK:
1523 	l->a_seek = alvec;
1524 	l->c_seek = alvecp;
1525 	return;
1526     case F_SEEK:
1527 	l->f_seek = fseekp;
1528 	l->a_seek = NULL;
1529 	return;
1530     default:
1531 	(void) fprintf(csherr, "Bad seek type %d\n", aret);
1532 	abort();
1533     }
1534 }
1535 
1536 void
btoeof(void)1537 btoeof(void)
1538 {
1539     (void) lseek(SHIN, (off_t) 0, SEEK_END);
1540     aret = F_SEEK;
1541     fseekp = feobp;
1542     alvec = NULL;
1543     alvecp = NULL;
1544     evalvec = NULL;
1545     evalp = NULL;
1546     wfree();
1547     bfree();
1548 }
1549 
1550 void
settell(void)1551 settell(void)
1552 {
1553     cantell = 0;
1554     if (arginp || onelflg || intty)
1555 	return;
1556     if (lseek(SHIN, (off_t) 0, SEEK_CUR) == -1 || errno == ESPIPE)
1557 	return;
1558     fbuf = xcalloc(2, sizeof(*fbuf));
1559     fblocks = 1;
1560     fbuf[0] = xcalloc(BUFSIZ, sizeof(Char));
1561     fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, SEEK_CUR);
1562     cantell = 1;
1563 }
1564