xref: /freebsd/contrib/tcsh/sh.lex.c (revision 7bd6fde3)
1 /* $Header: /src/pub/tcsh/sh.lex.c,v 3.62 2004/12/25 21:15:07 christos Exp $ */
2 /*
3  * sh.lex.c: Lexical analysis into tokens
4  */
5 /*-
6  * Copyright (c) 1980, 1991 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. 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 #include "sh.h"
34 
35 RCSID("$Id: sh.lex.c,v 3.62 2004/12/25 21:15:07 christos Exp $")
36 
37 #include "ed.h"
38 
39 #include <assert.h>
40 /* #define DEBUG_INP */
41 /* #define DEBUG_SEEK */
42 
43 /*
44  * C shell
45  */
46 
47 /*
48  * These lexical routines read input and form lists of words.
49  * There is some involved processing here, because of the complications
50  * of input buffering, and especially because of history substitution.
51  */
52 static	Char		*word		__P((int));
53 static	eChar	 	 getC1		__P((int));
54 static	void	 	 getdol		__P((void));
55 static	void	 	 getexcl	__P((Char));
56 static	struct Hist 	*findev		__P((Char *, int));
57 static	void	 	 setexclp	__P((Char *));
58 static	eChar	 	 bgetc		__P((void));
59 static	void		 balloc		__P((int));
60 static	void	 	 bfree		__P((void));
61 static	struct wordent	*gethent	__P((Char));
62 static	int	 	 matchs		__P((Char *, Char *));
63 static	int	 	 getsel		__P((int *, int *, int));
64 static	struct wordent	*getsub		__P((struct wordent *));
65 static	Char 		*subword	__P((Char *, Char, int *));
66 static	struct wordent	*dosub		__P((Char, struct wordent *, int));
67 static	ssize_t		 wide_read	__P((int, Char *, size_t, int));
68 
69 /*
70  * Peekc is a peek character for getC, peekread for readc.
71  * There is a subtlety here in many places... history routines
72  * will read ahead and then insert stuff into the input stream.
73  * If they push back a character then they must push it behind
74  * the text substituted by the history substitution.  On the other
75  * hand in several places we need 2 peek characters.  To make this
76  * all work, the history routines read with getC, and make use both
77  * of ungetC and unreadc.  The key observation is that the state
78  * of getC at the call of a history reference is such that calls
79  * to getC from the history routines will always yield calls of
80  * readc, unless this peeking is involved.  That is to say that during
81  * getexcl the variables lap, exclp, and exclnxt are all zero.
82  *
83  * Getdol invokes history substitution, hence the extra peek, peekd,
84  * which it can ungetD to be before history substitutions.
85  */
86 static Char peekc = 0, peekd = 0;
87 static Char peekread = 0;
88 
89 /* (Tail of) current word from ! subst */
90 static Char *exclp = NULL;
91 
92 /* The rest of the ! subst words */
93 static struct wordent *exclnxt = NULL;
94 
95 /* Count of remaining words in ! subst */
96 static int exclc = 0;
97 
98 /* "Globp" for alias resubstitution */
99 int aret = TCSH_F_SEEK;
100 
101 /*
102  * Labuf implements a general buffer for lookahead during lexical operations.
103  * Text which is to be placed in the input stream can be stuck here.
104  * We stick parsed ahead $ constructs during initial input,
105  * process id's from `$$', and modified variable values (from qualifiers
106  * during expansion in sh.dol.c) here.
107  */
108 static Char labuf[BUFSIZE];
109 
110 /*
111  * Lex returns to its caller not only a wordlist (as a "var" parameter)
112  * but also whether a history substitution occurred.  This is used in
113  * the main (process) routine to determine whether to echo, and also
114  * when called by the alias routine to determine whether to keep the
115  * argument list.
116  */
117 static int hadhist = 0;
118 
119 /*
120  * Avoid alias expansion recursion via \!#
121  */
122 int     hleft;
123 
124 Char    histline[BUFSIZE + 2];	/* last line input */
125 
126  /* The +2 is to fool hp's optimizer */
127 int    histvalid = 0;		/* is histline valid */
128 static Char *histlinep = NULL;	/* current pointer into histline */
129 
130 static Char getCtmp;
131 
132 #define getC(f)		(((getCtmp = peekc) != '\0') ? (peekc = 0, (eChar)getCtmp) : getC1(f))
133 #define	ungetC(c)	peekc = (Char) c
134 #define	ungetD(c)	peekd = (Char) c
135 
136 /* Use Htime to store timestamps picked up from history file for enthist()
137  * if reading saved history (sg)
138  */
139 time_t Htime = (time_t)0;
140 static time_t a2time_t __P((Char *));
141 
142 /*
143  * special parsing rules apply for source -h
144  */
145 extern int enterhist;
146 
147 /*
148  * for history event processing
149  * in the command 'echo !?foo?:1 !$' we want the !$ to expand from the line
150  * 'foo' was found instead of the last command
151  */
152 static int uselastevent = 1;
153 
154 int
155 lex(hp)
156     struct wordent *hp;
157 {
158     struct wordent *wdp;
159     eChar    c;
160     int     parsehtime = enterhist;
161 
162 
163     uselastevent = 1;
164     histvalid = 0;
165     histlinep = histline;
166     *histlinep = '\0';
167 
168     btell(&lineloc);
169     hp->next = hp->prev = hp;
170     hp->word = STRNULL;
171     hadhist = 0;
172     do
173 	c = readc(0);
174     while (c == ' ' || c == '\t');
175     if (c == (eChar)HISTSUB && intty)
176 	/* ^lef^rit	from tty is short !:s^lef^rit */
177 	getexcl(c);
178     else
179 	unreadc(c);
180     wdp = hp;
181     /*
182      * The following loop is written so that the links needed by freelex will
183      * be ready and rarin to go even if it is interrupted.
184      */
185     do {
186 	struct wordent *new;
187 
188 	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
189 	new->word = STRNULL;
190 	new->prev = wdp;
191 	new->next = hp;
192 	wdp->next = new;
193 	hp->prev = new;
194 	wdp = new;
195 	wdp->word = word(parsehtime);
196 	parsehtime = 0;
197     } while (wdp->word[0] != '\n');
198     if (histlinep < histline + BUFSIZE) {
199 	*histlinep = '\0';
200 	if (histlinep > histline && histlinep[-1] == '\n')
201 	    histlinep[-1] = '\0';
202 	histvalid = 1;
203     }
204     else {
205 	histline[BUFSIZE - 1] = '\0';
206     }
207 
208     return (hadhist);
209 }
210 
211 static time_t
212 a2time_t(wordx)
213     Char *wordx;
214 {
215     /* Attempt to distinguish timestamps from other possible entries.
216      * Format: "+NNNNNNNNNN" (10 digits, left padded with ascii '0') */
217 
218     time_t ret;
219     Char *s;
220     int ct;
221 
222     if (!wordx || *(s = wordx) != '+')
223 	return (time_t)0;
224 
225     for (++s, ret = 0, ct = 0; *s; ++s, ++ct)
226     {
227 	if (!isdigit((unsigned char)*s))
228 	    return (time_t)0;
229 	ret = ret * 10 + (time_t)((unsigned char)*s - '0');
230     }
231 
232     if (ct != 10)
233 	return (time_t)0;
234 
235     return ret;
236 }
237 
238 void
239 prlex(sp0)
240     struct wordent *sp0;
241 {
242     struct wordent *sp = sp0->next;
243 
244     for (;;) {
245 	xprintf("%S", sp->word);
246 	sp = sp->next;
247 	if (sp == sp0)
248 	    break;
249 	if (sp->word[0] != '\n')
250 	    xputchar(' ');
251     }
252 }
253 
254 void
255 copylex(hp, fp)
256     struct wordent *hp;
257     struct wordent *fp;
258 {
259     struct wordent *wdp;
260 
261     wdp = hp;
262     fp = fp->next;
263     do {
264 	struct wordent *new;
265 
266 	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
267 	new->word = STRNULL;
268 	new->prev = wdp;
269 	new->next = hp;
270 	wdp->next = new;
271 	hp->prev = new;
272 	wdp = new;
273 	wdp->word = Strsave(fp->word);
274 	fp = fp->next;
275     } while (wdp->word[0] != '\n');
276 }
277 
278 void
279 freelex(vp)
280     struct wordent *vp;
281 {
282     struct wordent *fp;
283 
284     while (vp->next != vp) {
285 	fp = vp->next;
286 	vp->next = fp->next;
287 	if (fp->word != STRNULL)
288 	    xfree((ptr_t) fp->word);
289 	xfree((ptr_t) fp);
290     }
291     vp->prev = vp;
292 }
293 
294 static Char *
295 word(parsehtime)
296     int parsehtime;
297 {
298     eChar c, c1;
299     Char *wp, *unfinished = 0;
300     Char    wbuf[BUFSIZE];
301     Char    hbuf[12];
302     int	    h;
303     int dolflg;
304     int i;
305 
306     wp = wbuf;
307     i = BUFSIZE - 4;
308 loop:
309     while ((c = getC(DOALL)) == ' ' || c == '\t')
310 	continue;
311     if (cmap(c, _META | _ESC))
312 	switch (c) {
313 	case '&':
314 	case '|':
315 	case '<':
316 	case '>':
317 	    *wp++ = c;
318 	    c1 = getC(DOALL);
319 	    if (c1 == c)
320 		*wp++ = c1;
321 	    else
322 		ungetC(c1);
323 	    goto ret;
324 
325 	case '#':
326 	    if (intty || (enterhist && !parsehtime))
327 		break;
328 	    c = 0;
329 	    h = 0;
330 	    do {
331 		c1 = c;
332 		c = getC(0);
333 		if (h < 11 && parsehtime)
334 		    hbuf[h++] = c;
335 	    } while (c != '\n');
336 	    if (parsehtime) {
337 		hbuf[11] = '\0';
338 		Htime = a2time_t(hbuf);
339 	    }
340 	    if (c1 == '\\')
341 		goto loop;
342 	    /*FALLTHROUGH*/
343 
344 	case ';':
345 	case '(':
346 	case ')':
347 	case '\n':
348 	    *wp++ = c;
349 	    goto ret;
350 
351 	case '\\':
352 	    c = getC(0);
353 	    if (c == '\n') {
354 		if (onelflg == 1)
355 		    onelflg = 2;
356 		goto loop;
357 	    }
358 	    if (c != (eChar)HIST)
359 		*wp++ = '\\', --i;
360 	    c |= QUOTE;
361 	default:
362 	    break;
363 	}
364     c1 = 0;
365     dolflg = DOALL;
366     for (;;) {
367 	if (c1) {
368 	    if (c == c1) {
369 		c1 = 0;
370 		dolflg = DOALL;
371 	    }
372 	    else if (c == '\\') {
373 		c = getC(0);
374 /*
375  * PWP: this is dumb, but how all of the other shells work.  If \ quotes
376  * a character OUTSIDE of a set of ''s, why shouldn't it quote EVERY
377  * following character INSIDE a set of ''s.
378  *
379  * Actually, all I really want to be able to say is 'foo\'bar' --> foo'bar
380  */
381 		if (c == (eChar)HIST)
382 		    c |= QUOTE;
383 		else {
384 		    if (bslash_quote &&
385 			((c == '\'') || (c == '"') ||
386 			 (c == '\\'))) {
387 			c |= QUOTE;
388 		    }
389 		    else {
390 			if (c == '\n')
391 			    /*
392 			     * if (c1 == '`') c = ' '; else
393 			     */
394 			    c |= QUOTE;
395 			ungetC(c);
396 			c = '\\';
397 		    }
398 		}
399 	    }
400 	    else if (c == '\n') {
401 		seterror(ERR_UNMATCHED, c1);
402 		ungetC(c);
403 		break;
404 	    }
405 	}
406 	else if (cmap(c, _META | _QF | _QB | _ESC)) {
407 	    if (c == '\\') {
408 		c = getC(0);
409 		if (c == '\n') {
410 		    if (onelflg == 1)
411 			onelflg = 2;
412 		    break;
413 		}
414 		if (c != (eChar)HIST)
415 		    *wp++ = '\\', --i;
416 		c |= QUOTE;
417 	    }
418 	    else if (cmap(c, _QF | _QB)) {	/* '"` */
419 		c1 = c;
420 		dolflg = c == '"' ? DOALL : DOEXCL;
421 	    }
422 	    else if (c != '#' || (!intty && !enterhist)) {
423 		ungetC(c);
424 		break;
425 	    }
426 	}
427 	if (--i > 0) {
428 	    *wp++ = c;
429 	    c = getC(dolflg);
430 	    if (!unfinished)
431 		unfinished = wp - 1;
432 	    switch (NLSFinished(unfinished, wp - unfinished, c)) {
433 		case 1:
434 		case 0:
435 		    c |= QUOTE;
436 		    break;
437 		default:
438 		    unfinished = 0;
439 		    break;
440 	    }
441 	}
442 	else {
443 	    seterror(ERR_WTOOLONG);
444 	    wp = &wbuf[1];
445 	    break;
446 	}
447     }
448 ret:
449     *wp = 0;
450     return (Strsave(wbuf));
451 }
452 
453 static eChar
454 getC1(flag)
455     int flag;
456 {
457     eChar c;
458 
459     for (;;) {
460 	if ((c = peekc) != 0) {
461 	    peekc = 0;
462 	    return (c);
463 	}
464 	if (lap) {
465 	    if ((c = *lap++) == 0)
466 		lap = 0;
467 	    else {
468 		if (cmap(c, _META | _QF | _QB))
469 		    c |= QUOTE;
470 		return (c);
471 	    }
472 	}
473 	if ((c = peekd) != 0) {
474 	    peekd = 0;
475 	    return (c);
476 	}
477 	if (exclp) {
478 	    if ((c = *exclp++) != 0)
479 		return (c);
480 	    if (exclnxt && --exclc >= 0) {
481 		exclnxt = exclnxt->next;
482 		setexclp(exclnxt->word);
483 		return (' ');
484 	    }
485 	    exclp = 0;
486 	    exclnxt = 0;
487 	    /* this will throw away the dummy history entries */
488 	    savehist(NULL, 0);
489 
490 	}
491 	if (exclnxt) {
492 	    exclnxt = exclnxt->next;
493 	    if (--exclc < 0)
494 		exclnxt = 0;
495 	    else
496 		setexclp(exclnxt->word);
497 	    continue;
498 	}
499 	c = readc(0);
500 	if (c == '$' && (flag & DODOL)) {
501 	    getdol();
502 	    continue;
503 	}
504 	if (c == (eChar)HIST && (flag & DOEXCL)) {
505 	    getexcl(0);
506 	    continue;
507 	}
508 	break;
509     }
510     return (c);
511 }
512 
513 static void
514 getdol()
515 {
516     Char *np, *ep;
517     Char    name[4 * MAXVARLEN + 1];
518     eChar c;
519     eChar   sc;
520     int    special = 0, toolong;
521 
522     np = name, *np++ = '$';
523     c = sc = getC(DOEXCL);
524     if (any("\t \n", c)) {
525 	ungetD(c);
526 	ungetC('$' | QUOTE);
527 	return;
528     }
529     if (c == '{')
530 	*np++ = (Char) c, c = getC(DOEXCL);
531     if (c == '#' || c == '?' || c == '%')
532 	special++, *np++ = (Char) c, c = getC(DOEXCL);
533     *np++ = (Char) c;
534     switch (c) {
535 
536     case '<':
537     case '$':
538     case '!':
539 	if (special)
540 	    seterror(ERR_SPDOLLT);
541 	*np = 0;
542 	addla(name);
543 	return;
544 
545     case '\n':
546 	ungetD(c);
547 	np--;
548 	if (!special)
549 	    seterror(ERR_NEWLINE);
550 	*np = 0;
551 	addla(name);
552 	return;
553 
554     case '*':
555 	if (special)
556 	    seterror(ERR_SPSTAR);
557 	*np = 0;
558 	addla(name);
559 	return;
560 
561     default:
562 	toolong = 0;
563 	if (Isdigit(c)) {
564 #ifdef notdef
565 	    /* let $?0 pass for now */
566 	    if (special) {
567 		seterror(ERR_DIGIT);
568 		*np = 0;
569 		addla(name);
570 		return;
571 	    }
572 #endif
573 	    /* we know that np < &name[4] */
574 	    ep = &np[MAXVARLEN];
575 	    while ((c = getC(DOEXCL)) != 0) {
576 		if (!Isdigit(c))
577 		    break;
578 		if (np < ep)
579 		    *np++ = (Char) c;
580 		else
581 		    toolong = 1;
582 	    }
583 	}
584 	else if (letter(c)) {
585 	    /* we know that np < &name[4] */
586 	    ep = &np[MAXVARLEN];
587 	    toolong = 0;
588 	    while ((c = getC(DOEXCL)) != 0) {
589 		/* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
590 		if (!letter(c) && !Isdigit(c))
591 		    break;
592 		if (np < ep)
593 		    *np++ = (Char) c;
594 		else
595 		    toolong = 1;
596 	    }
597 	}
598 	else {
599 	    if (!special)
600 		seterror(ERR_VARILL);
601 	    else {
602 		ungetD(c);
603 		--np;
604 	    }
605 	    *np = 0;
606 	    addla(name);
607 	    return;
608 	}
609 	if (toolong) {
610 	    seterror(ERR_VARTOOLONG);
611 	    *np = 0;
612 	    addla(name);
613 	    return;
614 	}
615 	break;
616     }
617     if (c == '[') {
618 	*np++ = (Char) c;
619 	/*
620 	 * Name up to here is a max of MAXVARLEN + 8.
621 	 */
622 	ep = &np[2 * MAXVARLEN + 8];
623 	do {
624 	    /*
625 	     * Michael Greim: Allow $ expansion to take place in selector
626 	     * expressions. (limits the number of characters returned)
627 	     */
628 	    c = getC(DOEXCL | DODOL);
629 	    if (c == '\n') {
630 		ungetD(c);
631 		np--;
632 		seterror(ERR_NLINDEX);
633 		*np = 0;
634 		addla(name);
635 		return;
636 	    }
637 	    if (np < ep)
638 		*np++ = (Char) c;
639 	} while (c != ']');
640 	*np = '\0';
641 	if (np >= ep) {
642 	    seterror(ERR_SELOVFL);
643 	    addla(name);
644 	    return;
645 	}
646 	c = getC(DOEXCL);
647     }
648     /*
649      * Name up to here is a max of 2 * MAXVARLEN + 8.
650      */
651     if (c == ':') {
652 	/*
653 	 * if the :g modifier is followed by a newline, then error right away!
654 	 * -strike
655 	 */
656 
657 	int     gmodflag = 0, amodflag = 0;
658 
659 #ifndef COMPAT
660 	do {
661 #endif /* COMPAT */
662 	    *np++ = (Char) c, c = getC(DOEXCL);
663 	    if (c == 'g' || c == 'a') {
664 		if (c == 'g')
665 		    gmodflag++;
666 		else
667 		    amodflag++;
668 		*np++ = (Char) c; c = getC(DOEXCL);
669 	    }
670 	    if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
671 		if (c == 'g')
672 		    gmodflag++;
673 		else
674 		    amodflag++;
675 		*np++ = (Char) c; c = getC(DOEXCL);
676 	    }
677 	    *np++ = (Char) c;
678 	    /* scan s// [eichin:19910926.0512EST] */
679 	    if (c == 's') {
680 		int delimcnt = 2;
681 		eChar delim = getC(0);
682 		*np++ = (Char) delim;
683 
684 		if (!delim || letter(delim)
685 		    || Isdigit(delim) || any(" \t\n", delim)) {
686 		    seterror(ERR_BADSUBST);
687 		    break;
688 		}
689 		while ((c = getC(0)) != CHAR_ERR) {
690 		    *np++ = (Char) c;
691 		    if(c == delim) delimcnt--;
692 		    if(!delimcnt) break;
693 		}
694 		if(delimcnt) {
695 		    seterror(ERR_BADSUBST);
696 		    break;
697 		}
698 		c = 's';
699 	    }
700 	    if (!any("htrqxesul", c)) {
701 		if ((amodflag || gmodflag) && c == '\n')
702 		    stderror(ERR_VARSYN);	/* strike */
703 		seterror(ERR_BADMOD, c);
704 		*np = 0;
705 		addla(name);
706 		return;
707 	    }
708 #ifndef COMPAT
709 	}
710 	while ((c = getC(DOEXCL)) == ':');
711 	ungetD(c);
712 #endif /* COMPAT */
713     }
714     else
715 	ungetD(c);
716     if (sc == '{') {
717 	c = getC(DOEXCL);
718 	if (c != '}') {
719 	    ungetD(c);
720 	    seterror(ERR_MISSING, '}');
721 	    *np = 0;
722 	    addla(name);
723 	    return;
724 	}
725 	*np++ = (Char) c;
726     }
727     *np = 0;
728     addla(name);
729     return;
730 }
731 
732 void
733 addla(cp)
734     Char   *cp;
735 {
736     Char    buf[BUFSIZE];
737 
738     if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
739 	(sizeof(labuf) - 4) / sizeof(Char)) {
740 	seterror(ERR_EXPOVFL);
741 	return;
742     }
743     if (lap)
744 	(void) Strcpy(buf, lap);
745     (void) Strcpy(labuf, cp);
746     NLSQuote(labuf);
747     if (lap)
748 	(void) Strcat(labuf, buf);
749     lap = labuf;
750 }
751 
752 static Char lhsb[32];
753 static Char slhs[32];
754 static Char rhsb[64];
755 static int quesarg;
756 
757 static void
758 getexcl(sc)
759     Char    sc;
760 {
761     struct wordent *hp, *ip;
762     int     left, right, dol;
763     eChar c;
764 
765     if (sc == 0) {
766 	sc = getC(0);
767 	if (sc != '{') {
768 	    ungetC(sc);
769 	    sc = 0;
770 	}
771     }
772     quesarg = -1;
773 
774     if (uselastevent) {
775 	uselastevent = 0;
776 	lastev = eventno;
777     }
778     else
779 	lastev = eventno;
780     hp = gethent(sc);
781     if (hp == 0)
782 	return;
783     hadhist = 1;
784     dol = 0;
785     if (hp == alhistp)
786 	for (ip = hp->next->next; ip != alhistt; ip = ip->next)
787 	    dol++;
788     else
789 	for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
790 	    dol++;
791     left = 0, right = dol;
792     if (sc == HISTSUB) {
793 	ungetC('s'), unreadc(HISTSUB), c = ':';
794 	goto subst;
795     }
796     c = getC(0);
797     if (!any(":^$*-%", c))
798 	goto subst;
799     left = right = -1;
800     if (c == ':') {
801 	c = getC(0);
802 	unreadc(c);
803 	if (letter(c) || c == '&') {
804 	    c = ':';
805 	    left = 0, right = dol;
806 	    goto subst;
807 	}
808     }
809     else
810 	ungetC(c);
811     if (!getsel(&left, &right, dol))
812 	return;
813     c = getC(0);
814     if (c == '*')
815 	ungetC(c), c = '-';
816     if (c == '-') {
817 	if (!getsel(&left, &right, dol))
818 	    return;
819 	c = getC(0);
820     }
821 subst:
822     exclc = right - left + 1;
823     while (--left >= 0)
824 	hp = hp->next;
825     if (sc == HISTSUB || c == ':') {
826 	do {
827 	    hp = getsub(hp);
828 	    c = getC(0);
829 	} while (c == ':');
830     }
831     unreadc(c);
832     if (sc == '{') {
833 	c = getC(0);
834 	if (c != '}')
835 	    seterror(ERR_BADBANG);
836     }
837     exclnxt = hp;
838 }
839 
840 static struct wordent *
841 getsub(en)
842     struct wordent *en;
843 {
844     Char *cp;
845     eChar   delim;
846     eChar   c;
847     eChar   sc;
848     int global;
849     Char    orhsb[sizeof(rhsb) / sizeof(Char)];
850 
851 #ifndef COMPAT
852     do {
853 #endif /* COMPAT */
854 	exclnxt = 0;
855 	global = 0;
856 	sc = c = getC(0);
857 	if (c == 'g' || c == 'a') {
858 	    global |= (c == 'g') ? 1 : 2;
859 	    sc = c = getC(0);
860 	}
861 	if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
862 	    global |= (c == 'g') ? 1 : 2;
863 	    sc = c = getC(0);
864 	}
865 
866 	switch (c) {
867 	case 'p':
868 	    justpr++;
869 	    return (en);
870 
871 	case 'x':
872 	case 'q':
873 	    global |= 1;
874 	    /*FALLTHROUGH*/
875 
876 	case 'h':
877 	case 'r':
878 	case 't':
879 	case 'e':
880 	case 'u':
881 	case 'l':
882 	    break;
883 
884 	case '&':
885 	    if (slhs[0] == 0) {
886 		seterror(ERR_NOSUBST);
887 		return (en);
888 	    }
889 	    (void) Strcpy(lhsb, slhs);
890 	    break;
891 
892 #ifdef notdef
893 	case '~':
894 	    if (lhsb[0] == 0)
895 		goto badlhs;
896 	    break;
897 #endif
898 
899 	case 's':
900 	    delim = getC(0);
901 	    if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
902 		unreadc(delim);
903 		lhsb[0] = 0;
904 		seterror(ERR_BADSUBST);
905 		return (en);
906 	    }
907 	    cp = lhsb;
908 	    for (;;) {
909 		c = getC(0);
910 		if (c == '\n') {
911 		    unreadc(c);
912 		    break;
913 		}
914 		if (c == delim)
915 		    break;
916 		if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) {
917 		    lhsb[0] = 0;
918 		    seterror(ERR_BADSUBST);
919 		    return (en);
920 		}
921 		if (c == '\\') {
922 		    c = getC(0);
923 		    if (c != delim && c != '\\')
924 			*cp++ = '\\';
925 		}
926 		*cp++ = (Char) c;
927 	    }
928 	    if (cp != lhsb)
929 		*cp++ = 0;
930 	    else if (lhsb[0] == 0) {
931 		seterror(ERR_LHS);
932 		return (en);
933 	    }
934 	    cp = rhsb;
935 	    (void) Strcpy(orhsb, cp);
936 	    for (;;) {
937 		c = getC(0);
938 		if (c == '\n') {
939 		    unreadc(c);
940 		    break;
941 		}
942 		if (c == delim)
943 		    break;
944 #ifdef notdef
945 		if (c == '~') {
946 		    if (&cp[Strlen(orhsb)] > &rhsb[sizeof(rhsb) /
947 						   sizeof(Char) - 2])
948 			goto toorhs;
949 		    (void) Strcpy(cp, orhsb);
950 		    cp = Strend(cp);
951 		    continue;
952 		}
953 #endif
954 		if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
955 		    seterror(ERR_RHSLONG);
956 		    return (en);
957 		}
958 		if (c == '\\') {
959 		    c = getC(0);
960 		    if (c != delim /* && c != '~' */ )
961 			*cp++ = '\\';
962 		}
963 		*cp++ = (Char) c;
964 	    }
965 	    *cp++ = 0;
966 	    break;
967 
968 	default:
969 	    if (c == '\n')
970 		unreadc(c);
971 	    seterror(ERR_BADBANGMOD, (int)c);
972 	    return (en);
973 	}
974 	(void) Strcpy(slhs, lhsb);
975 	if (exclc)
976 	    en = dosub(sc, en, global);
977 #ifndef COMPAT
978     }
979     while ((c = getC(0)) == ':');
980     unreadc(c);
981 #endif /* COMPAT */
982     return (en);
983 }
984 
985 /*
986  *
987  * From Beto Appleton (beto@aixwiz.austin.ibm.com)
988  *
989  * when using history substitution, and the variable
990  * 'history' is set to a value higher than 1000,
991  * the shell might either freeze (hang) or core-dump.
992  * We raise the limit to 50000000
993  */
994 
995 #define HIST_PURGE -50000000
996 static struct wordent *
997 dosub(sc, en, global)
998     Char   sc;
999     struct wordent *en;
1000     int global;
1001 {
1002     struct wordent lexi;
1003     int    didsub = 0, didone = 0;
1004     struct wordent *hp = &lexi;
1005     struct wordent *wdp;
1006     int i = exclc;
1007     struct Hist *hst;
1008 
1009     wdp = hp;
1010     while (--i >= 0) {
1011 	struct wordent *new =
1012 		(struct wordent *) xcalloc(1, sizeof *wdp);
1013 
1014 	new->word = 0;
1015 	new->prev = wdp;
1016 	new->next = hp;
1017 	wdp->next = new;
1018 	wdp = new;
1019 	en = en->next;
1020 	if (en->word) {
1021 	    Char *tword, *otword;
1022 
1023 	    if ((global & 1) || didsub == 0) {
1024 		tword = subword(en->word, sc, &didone);
1025 		if (didone)
1026 		    didsub = 1;
1027 		if (global & 2) {
1028 		    while (didone && tword != STRNULL) {
1029 			otword = tword;
1030 			tword = subword(otword, sc, &didone);
1031 			if (Strcmp(tword, otword) == 0) {
1032 			    xfree((ptr_t) otword);
1033 			    break;
1034 			}
1035 			else
1036 			    xfree((ptr_t) otword);
1037 		    }
1038 		}
1039 	    }
1040 	    else
1041 		tword = Strsave(en->word);
1042 	    wdp->word = tword;
1043 	}
1044     }
1045     if (didsub == 0)
1046 	seterror(ERR_MODFAIL);
1047     hp->prev = wdp;
1048     /*
1049      * ANSI mode HP/UX compiler chokes on
1050      * return &enthist(HIST_PURGE, &lexi, 0)->Hlex;
1051      */
1052     hst = enthist(HIST_PURGE, &lexi, 0, 0);
1053     return &(hst->Hlex);
1054 }
1055 
1056 static Char *
1057 subword(cp, type, adid)
1058     Char   *cp;
1059     Char    type;
1060     int   *adid;
1061 {
1062     Char    wbuf[BUFSIZE];
1063     Char *wp, *mp, *np;
1064     int i;
1065 
1066     *adid = 0;
1067     switch (type) {
1068 
1069     case 'r':
1070     case 'e':
1071     case 'h':
1072     case 't':
1073     case 'q':
1074     case 'x':
1075     case 'u':
1076     case 'l':
1077 	wp = domod(cp, type);
1078 	if (wp == 0)
1079 	    return (Strsave(cp));
1080 	*adid = 1;
1081 	return (wp);
1082 
1083     default:
1084 	wp = wbuf;
1085 	i = BUFSIZE - 4;
1086 	for (mp = cp; *mp; mp++)
1087 	    if (matchs(mp, lhsb)) {
1088 		for (np = cp; np < mp;)
1089 		    *wp++ = *np++, --i;
1090 		for (np = rhsb; *np; np++)
1091 		    switch (*np) {
1092 
1093 		    case '\\':
1094 			if (np[1] == '&')
1095 			    np++;
1096 			/* fall into ... */
1097 
1098 		    default:
1099 			if (--i < 0) {
1100 			    seterror(ERR_SUBOVFL);
1101 			    return (STRNULL);
1102 			}
1103 			*wp++ = *np;
1104 			continue;
1105 
1106 		    case '&':
1107 			i -= Strlen(lhsb);
1108 			if (i < 0) {
1109 			    seterror(ERR_SUBOVFL);
1110 			    return (STRNULL);
1111 			}
1112 			*wp = 0;
1113 			(void) Strcat(wp, lhsb);
1114 			wp = Strend(wp);
1115 			continue;
1116 		    }
1117 		mp += Strlen(lhsb);
1118 		i -= Strlen(mp);
1119 		if (i < 0) {
1120 		    seterror(ERR_SUBOVFL);
1121 		    return (STRNULL);
1122 		}
1123 		*wp = 0;
1124 		(void) Strcat(wp, mp);
1125 		*adid = 1;
1126 		return (Strsave(wbuf));
1127 	    }
1128 	return (Strsave(cp));
1129     }
1130 }
1131 
1132 Char   *
1133 domod(cp, type)
1134     Char   *cp;
1135     Char    type;
1136 {
1137     Char *wp, *xp;
1138     int c;
1139 
1140     switch (type) {
1141 
1142     case 'x':
1143     case 'q':
1144 	wp = Strsave(cp);
1145 	for (xp = wp; (c = *xp) != 0; xp++)
1146 	    if ((c != ' ' && c != '\t') || type == 'q')
1147 		*xp |= QUOTE;
1148 	return (wp);
1149 
1150     case 'l':
1151 	wp = NLSChangeCase(cp, 1);
1152 	return wp ? wp : Strsave(cp);
1153 
1154     case 'u':
1155 	wp = NLSChangeCase(cp, 0);
1156 	return wp ? wp : Strsave(cp);
1157 
1158     case 'h':
1159     case 't':
1160 	if (!any(short2str(cp), '/'))
1161 	    return (type == 't' ? Strsave(cp) : 0);
1162 	wp = Strend(cp);
1163 	while (*--wp != '/')
1164 	    continue;
1165 	if (type == 'h')
1166 	    xp = Strsave(cp), xp[wp - cp] = 0;
1167 	else
1168 	    xp = Strsave(wp + 1);
1169 	return (xp);
1170 
1171     case 'e':
1172     case 'r':
1173 	wp = Strend(cp);
1174 	for (wp--; wp >= cp && *wp != '/'; wp--)
1175 	    if (*wp == '.') {
1176 		if (type == 'e')
1177 		    xp = Strsave(wp + 1);
1178 		else
1179 		    xp = Strsave(cp), xp[wp - cp] = 0;
1180 		return (xp);
1181 	    }
1182 	return (Strsave(type == 'e' ? STRNULL : cp));
1183     default:
1184 	break;
1185     }
1186     return (0);
1187 }
1188 
1189 static int
1190 matchs(str, pat)
1191     Char *str, *pat;
1192 {
1193     while (*str && *pat && *str == *pat)
1194 	str++, pat++;
1195     return (*pat == 0);
1196 }
1197 
1198 static int
1199 getsel(al, ar, dol)
1200     int *al, *ar;
1201     int     dol;
1202 {
1203     eChar c = getC(0);
1204     int i;
1205     int    first = *al < 0;
1206 
1207     switch (c) {
1208 
1209     case '%':
1210 	if (quesarg == -1) {
1211 	    seterror(ERR_BADBANGARG);
1212 	    return (0);
1213 	}
1214 	if (*al < 0)
1215 	    *al = quesarg;
1216 	*ar = quesarg;
1217 	break;
1218 
1219     case '-':
1220 	if (*al < 0) {
1221 	    *al = 0;
1222 	    *ar = dol - 1;
1223 	    unreadc(c);
1224 	}
1225 	return (1);
1226 
1227     case '^':
1228 	if (*al < 0)
1229 	    *al = 1;
1230 	*ar = 1;
1231 	break;
1232 
1233     case '$':
1234 	if (*al < 0)
1235 	    *al = dol;
1236 	*ar = dol;
1237 	break;
1238 
1239     case '*':
1240 	if (*al < 0)
1241 	    *al = 1;
1242 	*ar = dol;
1243 	if (*ar < *al) {
1244 	    *ar = 0;
1245 	    *al = 1;
1246 	    return (1);
1247 	}
1248 	break;
1249 
1250     default:
1251 	if (Isdigit(c)) {
1252 	    i = 0;
1253 	    while (Isdigit(c)) {
1254 		i = i * 10 + c - '0';
1255 		c = getC(0);
1256 	    }
1257 	    if (i < 0)
1258 		i = dol + 1;
1259 	    if (*al < 0)
1260 		*al = i;
1261 	    *ar = i;
1262 	}
1263 	else if (*al < 0)
1264 	    *al = 0, *ar = dol;
1265 	else
1266 	    *ar = dol - 1;
1267 	unreadc(c);
1268 	break;
1269     }
1270     if (first) {
1271 	c = getC(0);
1272 	unreadc(c);
1273 	if (any("-$*", c))
1274 	    return (1);
1275     }
1276     if (*al > *ar || *ar > dol) {
1277 	seterror(ERR_BADBANGARG);
1278 	return (0);
1279     }
1280     return (1);
1281 
1282 }
1283 
1284 static struct wordent *
1285 gethent(sc)
1286     Char   sc;
1287 {
1288     struct Hist *hp;
1289     Char *np;
1290     eChar c;
1291     int     event;
1292     int    back = 0;
1293 
1294     c = sc == HISTSUB ? (eChar)HIST : getC(0);
1295     if (c == (eChar)HIST) {
1296 	if (alhistp)
1297 	    return (alhistp);
1298 	event = eventno;
1299     }
1300     else
1301 	switch (c) {
1302 
1303 	case ':':
1304 	case '^':
1305 	case '$':
1306 	case '*':
1307 	case '%':
1308 	    ungetC(c);
1309 	    if (lastev == eventno && alhistp)
1310 		return (alhistp);
1311 	    event = lastev;
1312 	    break;
1313 
1314 	case '#':		/* !# is command being typed in (mrh) */
1315 	    if (--hleft == 0) {
1316 		seterror(ERR_HISTLOOP);
1317 		return (0);
1318 	    }
1319 	    else
1320 		return (&paraml);
1321 	    /* NOTREACHED */
1322 
1323 	case '-':
1324 	    back = 1;
1325 	    c = getC(0);
1326 	    /* FALLSTHROUGH */
1327 
1328 	default:
1329 	    if (any("(=~", c)) {
1330 		unreadc(c);
1331 		ungetC(HIST);
1332 		return (0);
1333 	    }
1334 	    np = lhsb;
1335 	    event = 0;
1336 	    while (!cmap(c, _ESC | _META | _QF | _QB) && !any("^*-%${}:#", c)) {
1337 		if (event != -1 && Isdigit(c))
1338 		    event = event * 10 + c - '0';
1339 		else
1340 		    event = -1;
1341 		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1342 		    *np++ = (Char) c;
1343 		c = getC(0);
1344 	    }
1345 	    unreadc(c);
1346 	    if (np == lhsb) {
1347 		ungetC(HIST);
1348 		return (0);
1349 	    }
1350 	    *np++ = 0;
1351 	    if (event != -1) {
1352 		/*
1353 		 * History had only digits
1354 		 */
1355 		if (back)
1356 		    event = eventno + (alhistp == 0) - (event ? event : 0);
1357 		break;
1358 	    }
1359 	    if (back) {
1360 		event = sizeof(lhsb) / sizeof(lhsb[0]);
1361 		np = &lhsb[--event];
1362 		*np-- = '\0';
1363 		for (event--; np > lhsb; *np-- = lhsb[--event])
1364 		    continue;
1365 		*np = '-';
1366 	    }
1367 	    hp = findev(lhsb, 0);
1368 	    if (hp)
1369 		lastev = hp->Hnum;
1370 	    return (&hp->Hlex);
1371 
1372 	case '?':
1373 	    np = lhsb;
1374 	    for (;;) {
1375 		c = getC(0);
1376 		if (c == '\n') {
1377 		    unreadc(c);
1378 		    break;
1379 		}
1380 		if (c == '?')
1381 		    break;
1382 		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1383 		    *np++ = (Char) c;
1384 	    }
1385 	    if (np == lhsb) {
1386 		if (lhsb[0] == 0) {
1387 		    seterror(ERR_NOSEARCH);
1388 		    return (0);
1389 		}
1390 	    }
1391 	    else
1392 		*np++ = 0;
1393 	    hp = findev(lhsb, 1);
1394 	    if (hp)
1395 		lastev = hp->Hnum;
1396 	    return (&hp->Hlex);
1397 	}
1398 
1399     for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1400 	if (hp->Hnum == event) {
1401 	    hp->Href = eventno;
1402 	    lastev = hp->Hnum;
1403 	    return (&hp->Hlex);
1404 	}
1405     np = putn(event);
1406     seterror(ERR_NOEVENT, short2str(np));
1407     return (0);
1408 }
1409 
1410 static struct Hist *
1411 findev(cp, anyarg)
1412     Char   *cp;
1413     int    anyarg;
1414 {
1415     struct Hist *hp;
1416 
1417     for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1418 	Char   *dp;
1419 	Char *p, *q;
1420 	struct wordent *lp = hp->Hlex.next;
1421 	int     argno = 0;
1422 
1423 	/*
1424 	 * The entries added by alias substitution don't have a newline but do
1425 	 * have a negative event number. Savehist() trims off these entries,
1426 	 * but it happens before alias expansion, too early to delete those
1427 	 * from the previous command.
1428 	 */
1429 	if (hp->Hnum < 0)
1430 	    continue;
1431 	if (lp->word[0] == '\n')
1432 	    continue;
1433 	if (!anyarg) {
1434 	    p = cp;
1435 	    q = lp->word;
1436 	    do
1437 		if (!*p)
1438 		    return (hp);
1439 	    while (*p++ == *q++);
1440 	    continue;
1441 	}
1442 	do {
1443 	    for (dp = lp->word; *dp; dp++) {
1444 		p = cp;
1445 		q = dp;
1446 		do
1447 		    if (!*p) {
1448 			quesarg = argno;
1449 			return (hp);
1450 		    }
1451 		while (*p++ == *q++);
1452 	    }
1453 	    lp = lp->next;
1454 	    argno++;
1455 	} while (lp->word[0] != '\n');
1456     }
1457     seterror(ERR_NOEVENT, short2str(cp));
1458     return (0);
1459 }
1460 
1461 
1462 static void
1463 setexclp(cp)
1464     Char *cp;
1465 {
1466     if (cp && cp[0] == '\n')
1467 	return;
1468     exclp = cp;
1469 }
1470 
1471 void
1472 unreadc(c)
1473     Char    c;
1474 {
1475     peekread = (Char) c;
1476 }
1477 
1478 eChar
1479 readc(wanteof)
1480     int    wanteof;
1481 {
1482     eChar c;
1483     static  int sincereal;	/* Number of real EOFs we've seen */
1484 
1485 #ifdef DEBUG_INP
1486     xprintf("readc\n");
1487 #endif
1488     if ((c = peekread) != 0) {
1489 	peekread = 0;
1490 	return (c);
1491     }
1492 
1493 top:
1494     aret = TCSH_F_SEEK;
1495     if (alvecp) {
1496 	arun = 1;
1497 #ifdef DEBUG_INP
1498 	xprintf("alvecp %c\n", *alvecp & 0xff);
1499 #endif
1500 	aret = TCSH_A_SEEK;
1501 	if ((c = *alvecp++) != 0)
1502 	    return (c);
1503 	if (alvec && *alvec) {
1504 		alvecp = *alvec++;
1505 		return (' ');
1506 	}
1507 	else {
1508 	    alvecp = NULL;
1509 	    aret = TCSH_F_SEEK;
1510 	    return('\n');
1511 	}
1512     }
1513     if (alvec) {
1514 	arun = 1;
1515 	if ((alvecp = *alvec) != 0) {
1516 	    alvec++;
1517 	    goto top;
1518 	}
1519 	/* Infinite source! */
1520 	return ('\n');
1521     }
1522     arun = 0;
1523     if (evalp) {
1524 	aret = TCSH_E_SEEK;
1525 	if ((c = *evalp++) != 0)
1526 	    return (c);
1527 	if (evalvec && *evalvec) {
1528 	    evalp = *evalvec++;
1529 	    return (' ');
1530 	}
1531 	aret = TCSH_F_SEEK;
1532 	evalp = 0;
1533     }
1534     if (evalvec) {
1535 	if (evalvec == INVPPTR) {
1536 	    doneinp = 1;
1537 	    reset();
1538 	}
1539 	if ((evalp = *evalvec) != 0) {
1540 	    evalvec++;
1541 	    goto top;
1542 	}
1543 	evalvec = INVPPTR;
1544 	return ('\n');
1545     }
1546     do {
1547 	if (arginp == INVPTR || onelflg == 1) {
1548 	    if (wanteof)
1549 		return CHAR_ERR;
1550 	    exitstat();
1551 	}
1552 	if (arginp) {
1553 	    if ((c = *arginp++) == 0) {
1554 		arginp = INVPTR;
1555 		return ('\n');
1556 	    }
1557 	    return (c);
1558 	}
1559 #ifdef BSDJOBS
1560 reread:
1561 #endif /* BSDJOBS */
1562 	c = bgetc();
1563 	if (c == CHAR_ERR) {
1564 #ifndef WINNT_NATIVE
1565 # ifndef POSIX
1566 #  ifdef TERMIO
1567 	    struct termio tty;
1568 #  else /* SGTTYB */
1569 	    struct sgttyb tty;
1570 #  endif /* TERMIO */
1571 # else /* POSIX */
1572 	    struct termios tty;
1573 # endif /* POSIX */
1574 #endif /* !WINNT_NATIVE */
1575 	    if (wanteof)
1576 		return CHAR_ERR;
1577 	    /* was isatty but raw with ignoreeof yields problems */
1578 #ifndef WINNT_NATIVE
1579 # ifndef POSIX
1580 #  ifdef TERMIO
1581 	    if (ioctl(SHIN, TCGETA, (ioctl_t) & tty) == 0 &&
1582 		(tty.c_lflag & ICANON))
1583 #  else /* GSTTYB */
1584 	    if (ioctl(SHIN, TIOCGETP, (ioctl_t) & tty) == 0 &&
1585 		(tty.sg_flags & RAW) == 0)
1586 #  endif /* TERMIO */
1587 # else /* POSIX */
1588 	    if (tcgetattr(SHIN, &tty) == 0 &&
1589 		(tty.c_lflag & ICANON))
1590 # endif /* POSIX */
1591 #else /* WINNT_NATIVE */
1592 	    if (isatty(SHIN))
1593 #endif /* !WINNT_NATIVE */
1594 	    {
1595 #ifdef BSDJOBS
1596 		int     ctpgrp;
1597 #endif /* BSDJOBS */
1598 
1599 		if (numeof != 0 && ++sincereal >= numeof)	/* Too many EOFs?  Bye! */
1600 		    goto oops;
1601 #ifdef BSDJOBS
1602 		if (tpgrp != -1 &&
1603 		    (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
1604 		    tpgrp != ctpgrp) {
1605 		    (void) tcsetpgrp(FSHTTY, tpgrp);
1606 # ifdef _SEQUENT_
1607 		    if (ctpgrp)
1608 # endif /* _SEQUENT */
1609 		    (void) killpg((pid_t) ctpgrp, SIGHUP);
1610 # ifdef notdef
1611 		    /*
1612 		     * With the walking process group fix, this message
1613 		     * is now obsolete. As the foreground process group
1614 		     * changes, the shell needs to adjust. Well too bad.
1615 		     */
1616 		    xprintf(CGETS(16, 1, "Reset tty pgrp from %d to %d\n"),
1617 			    ctpgrp, tpgrp);
1618 # endif /* notdef */
1619 		    goto reread;
1620 		}
1621 #endif /* BSDJOBS */
1622 		/* What follows is complicated EOF handling -- sterling@netcom.com */
1623 		/* First, we check to see if we have ignoreeof set */
1624 		if (adrof(STRignoreeof)) {
1625 			/* If so, we check for any stopped jobs only on the first EOF */
1626 			if ((sincereal == 1) && (chkstop == 0)) {
1627 				panystop(1);
1628 			}
1629 		} else {
1630 			/* If we don't have ignoreeof set, always check for stopped jobs */
1631 			if (chkstop == 0) {
1632 				panystop(1);
1633 			}
1634 		}
1635 		/* At this point, if there were stopped jobs, we would have already
1636 		 * called reset().  If we got this far, assume we can print an
1637 		 * exit/logout message if we ignoreeof, or just exit.
1638 		 */
1639 		if (adrof(STRignoreeof)) {
1640 			/* If so, tell the user to use exit or logout */
1641 		    if (loginsh) {
1642 				xprintf(CGETS(16, 2,
1643 					"\nUse \"logout\" to logout.\n"));
1644 		   	} else {
1645 				xprintf(CGETS(16, 3,
1646 					"\nUse \"exit\" to leave %s.\n"),
1647 					progname);
1648 			}
1649 			reset();
1650 		} else {
1651 			/* If we don't have ignoreeof set, just fall through */
1652 			;	/* EMPTY */
1653 		}
1654 	    }
1655     oops:
1656 	    doneinp = 1;
1657 	    reset();
1658 	}
1659 	sincereal = 0;
1660 	if (c == '\n' && onelflg)
1661 	    onelflg--;
1662     } while (c == 0);
1663     if (histlinep < histline + BUFSIZE)
1664 	*histlinep++ = (Char) c;
1665     return (c);
1666 }
1667 
1668 static void
1669 balloc(buf)
1670     int buf;
1671 {
1672     Char **nfbuf;
1673 
1674     while (buf >= fblocks) {
1675 	nfbuf = (Char **) xcalloc((size_t) (fblocks + 2),
1676 			  sizeof(Char **));
1677 	if (fbuf) {
1678 	    (void) blkcpy(nfbuf, fbuf);
1679 	    xfree((ptr_t) fbuf);
1680 	}
1681 	fbuf = nfbuf;
1682 	fbuf[fblocks] = (Char *) xcalloc(BUFSIZE, sizeof(Char));
1683 	fblocks++;
1684     }
1685 }
1686 
1687 static ssize_t
1688 wide_read(fildes, buf, nchars, use_fclens)
1689     int fildes;
1690     Char *buf;
1691     size_t nchars;
1692     int use_fclens;
1693 {
1694     char cbuf[BUFSIZE + 1];
1695     ssize_t res, r;
1696     size_t partial;
1697 
1698     assert (nchars <= sizeof(cbuf)/sizeof(*cbuf));
1699     USE(use_fclens);
1700     res = 0;
1701     partial = 0;
1702     do {
1703 	size_t i;
1704 
1705 	do
1706 	    r = read(fildes, cbuf + partial,
1707 		     nchars > partial ? nchars - partial : 1);
1708 	while (partial != 0 && r < 0 && errno == EINTR);
1709 	if (partial == 0 && r <= 0)
1710 	    break;
1711 	partial += r;
1712 	i = 0;
1713 	while (i < partial) {
1714 	    int len;
1715 
1716 	    len = normal_mbtowc(buf + res, cbuf + i, partial - i);
1717 	    if (len == -1) {
1718 	        reset_mbtowc();
1719 		if (partial < MB_LEN_MAX && r > 0)
1720 		    /* Maybe a partial character and there is still a chance
1721 		       to read more */
1722 		    break;
1723 		buf[res] = (unsigned char)cbuf[i] | INVALID_BYTE;
1724 	    }
1725 	    if (len <= 0)
1726 		len = 1;
1727 #ifdef WIDE_STRINGS
1728 	    if (use_fclens)
1729 		fclens[res] = len;
1730 #endif
1731 	    i += len;
1732 	    res++;
1733 	    nchars--;
1734 	}
1735 	if (i != partial)
1736 	    memmove(cbuf, cbuf + i, partial - i);
1737 	partial -= i;
1738     } while (partial != 0);
1739     /* Throwing away possible partial multibyte characters on error */
1740     return res != 0 ? res : r;
1741 }
1742 
1743 static eChar
1744 bgetc()
1745 {
1746     Char ch;
1747     int c, off, buf;
1748     int numleft = 0, roomleft;
1749 
1750     if (cantell) {
1751 	if (fseekp < fbobp || fseekp > feobp) {
1752 	    fbobp = feobp = fseekp;
1753 	    (void) lseek(SHIN, fseekp, L_SET);
1754 	}
1755 	if (fseekp == feobp) {
1756 	    fbobp = feobp;
1757 	    do
1758 		c = wide_read(SHIN, fbuf[0], BUFSIZE, 1);
1759 	    while (c < 0 && errno == EINTR);
1760 #ifdef convex
1761 	    if (c < 0)
1762 		stderror(ERR_SYSTEM, progname, strerror(errno));
1763 #endif /* convex */
1764 	    if (c <= 0)
1765 		return CHAR_ERR;
1766 	    feobp += c;
1767 	}
1768 #ifndef WINNT_NATIVE
1769 	ch = fbuf[0][fseekp - fbobp];
1770 	fseekp++;
1771 #else
1772 	do {
1773 	    ch = fbuf[0][fseekp - fbobp];
1774 	    fseekp++;
1775 	} while(ch == '\r');
1776 #endif /* !WINNT_NATIVE */
1777 	return (ch);
1778     }
1779 
1780     while (fseekp >= feobp) {
1781 	if ((editing
1782 #if defined(FILEC) && defined(TIOCSTI)
1783 	    || filec
1784 #endif /* FILEC && TIOCSTI */
1785 	    ) && intty) {		/* then use twenex routine */
1786 	    fseekp = feobp;		/* where else? */
1787 #if defined(FILEC) && defined(TIOCSTI)
1788 	    if (!editing)
1789 		c = numleft = tenex(InputBuf, BUFSIZE);
1790 	    else
1791 #endif /* FILEC && TIOCSTI */
1792 	    c = numleft = Inputl();	/* PWP: get a line */
1793 	    while (numleft > 0) {
1794 		off = (int) feobp % BUFSIZE;
1795 		buf = (int) feobp / BUFSIZE;
1796 		balloc(buf);
1797 		roomleft = BUFSIZE - off;
1798 		if (roomleft > numleft)
1799 		    roomleft = numleft;
1800 		(void) memmove((ptr_t) (fbuf[buf] + off),
1801 		    (ptr_t) (InputBuf + c - numleft),
1802 		    (size_t) (roomleft * sizeof(Char)));
1803 		numleft -= roomleft;
1804 		feobp += roomleft;
1805 	    }
1806 	} else {
1807 	    off = (int) feobp % BUFSIZE;
1808 	    buf = (int) feobp / BUFSIZE;
1809 	    balloc(buf);
1810 	    roomleft = BUFSIZE - off;
1811 	    c = wide_read(SHIN, fbuf[buf] + off, (size_t) roomleft, 0);
1812 	    if (c > 0)
1813 		feobp += c;
1814 	}
1815 	if (c == 0 || (c < 0 && fixio(SHIN, errno) == -1))
1816 	    return CHAR_ERR;
1817     }
1818 #ifdef SIG_WINDOW
1819     if (windowchg)
1820 	(void) check_window_size(0);	/* for window systems */
1821 #endif /* SIG_WINDOW */
1822 #ifndef WINNT_NATIVE
1823     ch = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE];
1824     fseekp++;
1825 #else
1826     do {
1827 	ch = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE];
1828 	fseekp++;
1829     } while(ch == '\r');
1830 #endif /* !WINNT_NATIVE */
1831     return (ch);
1832 }
1833 
1834 static void
1835 bfree()
1836 {
1837     int sb, i;
1838 
1839     if (cantell)
1840 	return;
1841     if (whyles)
1842 	return;
1843     sb = (int) (fseekp - 1) / BUFSIZE;
1844     if (sb > 0) {
1845 	for (i = 0; i < sb; i++)
1846 	    xfree((ptr_t) fbuf[i]);
1847 	(void) blkcpy(fbuf, &fbuf[sb]);
1848 	fseekp -= BUFSIZE * sb;
1849 	feobp -= BUFSIZE * sb;
1850 	fblocks -= sb;
1851     }
1852 }
1853 
1854 void
1855 bseek(l)
1856     struct Ain   *l;
1857 {
1858     switch (aret = l->type) {
1859     case TCSH_E_SEEK:
1860 	evalvec = l->a_seek;
1861 	evalp = l->c_seek;
1862 #ifdef DEBUG_SEEK
1863 	xprintf(CGETS(16, 4, "seek to eval %x %x\n"), evalvec, evalp);
1864 #endif
1865 	return;
1866     case TCSH_A_SEEK:
1867 	alvec = l->a_seek;
1868 	alvecp = l->c_seek;
1869 #ifdef DEBUG_SEEK
1870 	xprintf(CGETS(16, 5, "seek to alias %x %x\n"), alvec, alvecp);
1871 #endif
1872 	return;
1873     case TCSH_F_SEEK:
1874 #ifdef DEBUG_SEEK
1875 	xprintf(CGETS(16, 6, "seek to file %x\n"), fseekp);
1876 #endif
1877 	fseekp = l->f_seek;
1878 #ifdef WIDE_STRINGS
1879 	if (cantell) {
1880 	    if (fseekp >= fbobp) {
1881 		size_t i;
1882 		off_t o;
1883 
1884 		o = fbobp;
1885 		for (i = 0; i < feobp - fbobp; i++) {
1886 		    if (fseekp == o) {
1887 			fseekp = fbobp + i;
1888 			return;
1889 		    }
1890 		    o += fclens[i];
1891 		}
1892 		if (fseekp == o) {
1893 		    fseekp = feobp;
1894 		    return;
1895 		}
1896 	    }
1897 	    fbobp = feobp = fseekp + 1; /* To force lseek() */
1898 	}
1899 #endif
1900 	return;
1901     default:
1902 	xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret);
1903 	abort();
1904     }
1905 }
1906 
1907 /* any similarity to bell telephone is purely accidental */
1908 void
1909 btell(l)
1910 struct Ain *l;
1911 {
1912     switch (l->type = aret) {
1913     case TCSH_E_SEEK:
1914 	l->a_seek = evalvec;
1915 	l->c_seek = evalp;
1916 #ifdef DEBUG_SEEK
1917 	xprintf(CGETS(16, 8, "tell eval %x %x\n"), evalvec, evalp);
1918 #endif
1919 	return;
1920     case TCSH_A_SEEK:
1921 	l->a_seek = alvec;
1922 	l->c_seek = alvecp;
1923 #ifdef DEBUG_SEEK
1924 	xprintf(CGETS(16, 9, "tell alias %x %x\n"), alvec, alvecp);
1925 #endif
1926 	return;
1927     case TCSH_F_SEEK:
1928 #ifdef WIDE_STRINGS
1929 	if (cantell && fseekp >= fbobp && fseekp < feobp) {
1930 	    size_t i;
1931 
1932 	    l->f_seek = fbobp;
1933 	    for (i = 0; i < fseekp - fbobp; i++)
1934 		l->f_seek += fclens[i];
1935 	} else
1936 #endif
1937 	    /*SUPPRESS 112*/
1938 	    l->f_seek = fseekp;
1939 	l->a_seek = NULL;
1940 #ifdef DEBUG_SEEK
1941 	xprintf(CGETS(16, 10, "tell file %x\n"), fseekp);
1942 #endif
1943 	return;
1944     default:
1945 	xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret);
1946 	abort();
1947     }
1948 }
1949 
1950 void
1951 btoeof()
1952 {
1953     (void) lseek(SHIN, (off_t) 0, L_XTND);
1954     aret = TCSH_F_SEEK;
1955     fseekp = feobp;
1956     alvec = NULL;
1957     alvecp = NULL;
1958     evalvec = NULL;
1959     evalp = NULL;
1960     wfree();
1961     bfree();
1962 }
1963 
1964 void
1965 settell()
1966 {
1967     off_t x;
1968     cantell = 0;
1969     if (arginp || onelflg || intty)
1970 	return;
1971     if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1)
1972 	return;
1973     fbuf = (Char **) xcalloc(2, sizeof(Char **));
1974     fblocks = 1;
1975     fbuf[0] = (Char *) xcalloc(BUFSIZE, sizeof(Char));
1976     fseekp = fbobp = feobp = x;
1977     cantell = 1;
1978 }
1979