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