sh.lex.c revision 59243
1147476Sdumbbell/* $Header: /src/pub/tcsh/sh.lex.c,v 3.49 1998/04/08 13:58:54 christos Exp $ */
2147476Sdumbbell/*
3147476Sdumbbell * sh.lex.c: Lexical analysis into tokens
4147476Sdumbbell */
5230132Suqs/*-
6147476Sdumbbell * Copyright (c) 1980, 1991 The Regents of the University of California.
7147476Sdumbbell * All rights reserved.
8147476Sdumbbell *
9147476Sdumbbell * Redistribution and use in source and binary forms, with or without
10147476Sdumbbell * modification, are permitted provided that the following conditions
11147476Sdumbbell * are met:
12147476Sdumbbell * 1. Redistributions of source code must retain the above copyright
13147476Sdumbbell *    notice, this list of conditions and the following disclaimer.
14147476Sdumbbell * 2. Redistributions in binary form must reproduce the above copyright
15147476Sdumbbell *    notice, this list of conditions and the following disclaimer in the
16147476Sdumbbell *    documentation and/or other materials provided with the distribution.
17147476Sdumbbell * 3. All advertising materials mentioning features or use of this software
18147476Sdumbbell *    must display the following acknowledgement:
19147476Sdumbbell *	This product includes software developed by the University of
20147476Sdumbbell *	California, Berkeley and its contributors.
21147476Sdumbbell * 4. Neither the name of the University nor the names of its contributors
22147476Sdumbbell *    may be used to endorse or promote products derived from this software
23147476Sdumbbell *    without specific prior written permission.
24147476Sdumbbell *
25147476Sdumbbell * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26147476Sdumbbell * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27147476Sdumbbell * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28147476Sdumbbell * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29147476Sdumbbell * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30147476Sdumbbell * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31147476Sdumbbell * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32147476Sdumbbell * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33147476Sdumbbell * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34147476Sdumbbell * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35147476Sdumbbell * SUCH DAMAGE.
36147476Sdumbbell */
37147476Sdumbbell#include "sh.h"
38147476Sdumbbell
39147476SdumbbellRCSID("$Id: sh.lex.c,v 3.49 1998/04/08 13:58:54 christos Exp $")
40147476Sdumbbell
41147476Sdumbbell#include "ed.h"
42147476Sdumbbell/* #define DEBUG_INP */
43147476Sdumbbell/* #define DEBUG_SEEK */
44147476Sdumbbell
45147476Sdumbbell/*
46147476Sdumbbell * C shell
47147476Sdumbbell */
48147476Sdumbbell
49147476Sdumbbell/*
50147476Sdumbbell * These lexical routines read input and form lists of words.
51147476Sdumbbell * There is some involved processing here, because of the complications
52147476Sdumbbell * of input buffering, and especially because of history substitution.
53147476Sdumbbell */
54147476Sdumbbellstatic	Char		*word		__P((void));
55147476Sdumbbellstatic	int	 	 getC1		__P((int));
56147476Sdumbbellstatic	void	 	 getdol		__P((void));
57147476Sdumbbellstatic	void	 	 getexcl	__P((int));
58147476Sdumbbellstatic	struct Hist 	*findev		__P((Char *, bool));
59147476Sdumbbellstatic	void	 	 setexclp	__P((Char *));
60147476Sdumbbellstatic	int	 	 bgetc		__P((void));
61147476Sdumbbellstatic	void		 balloc		__P((int));
62147476Sdumbbellstatic	void	 	 bfree		__P((void));
63147476Sdumbbellstatic	struct wordent	*gethent	__P((int));
64147476Sdumbbellstatic	int	 	 matchs		__P((Char *, Char *));
65147476Sdumbbellstatic	int	 	 getsel		__P((int *, int *, int));
66147476Sdumbbellstatic	struct wordent	*getsub		__P((struct wordent *));
67147476Sdumbbellstatic	Char 		*subword	__P((Char *, int, bool *));
68147476Sdumbbellstatic	struct wordent	*dosub		__P((int, struct wordent *, bool));
69147476Sdumbbell
70147476Sdumbbell/*
71147476Sdumbbell * Peekc is a peek character for getC, peekread for readc.
72147476Sdumbbell * There is a subtlety here in many places... history routines
73147476Sdumbbell * will read ahead and then insert stuff into the input stream.
74147476Sdumbbell * If they push back a character then they must push it behind
75147476Sdumbbell * the text substituted by the history substitution.  On the other
76147476Sdumbbell * hand in several places we need 2 peek characters.  To make this
77147476Sdumbbell * all work, the history routines read with getC, and make use both
78147476Sdumbbell * of ungetC and unreadc.  The key observation is that the state
79147476Sdumbbell * of getC at the call of a history reference is such that calls
80147476Sdumbbell * to getC from the history routines will always yield calls of
81147476Sdumbbell * readc, unless this peeking is involved.  That is to say that during
82147476Sdumbbell * getexcl the variables lap, exclp, and exclnxt are all zero.
83147476Sdumbbell *
84147476Sdumbbell * Getdol invokes history substitution, hence the extra peek, peekd,
85147476Sdumbbell * which it can ungetD to be before history substitutions.
86147476Sdumbbell */
87147476Sdumbbellstatic Char peekc = 0, peekd = 0;
88147476Sdumbbellstatic Char peekread = 0;
89147476Sdumbbell
90147476Sdumbbell/* (Tail of) current word from ! subst */
91147476Sdumbbellstatic Char *exclp = NULL;
92147476Sdumbbell
93147476Sdumbbell/* The rest of the ! subst words */
94147476Sdumbbellstatic struct wordent *exclnxt = NULL;
95147476Sdumbbell
96147476Sdumbbell/* Count of remaining words in ! subst */
97147476Sdumbbellstatic int exclc = 0;
98147476Sdumbbell
99147476Sdumbbell/* "Globp" for alias resubstitution */
100147476Sdumbbellint aret = F_SEEK;
101147476Sdumbbell
102147476Sdumbbell/*
103147476Sdumbbell * Labuf implements a general buffer for lookahead during lexical operations.
104147476Sdumbbell * Text which is to be placed in the input stream can be stuck here.
105147476Sdumbbell * We stick parsed ahead $ constructs during initial input,
106147476Sdumbbell * process id's from `$$', and modified variable values (from qualifiers
107147476Sdumbbell * during expansion in sh.dol.c) here.
108147476Sdumbbell */
109147476Sdumbbellstatic Char labuf[BUFSIZE];
110147476Sdumbbell
111147476Sdumbbell/*
112147476Sdumbbell * Lex returns to its caller not only a wordlist (as a "var" parameter)
113147476Sdumbbell * but also whether a history substitution occurred.  This is used in
114147476Sdumbbell * the main (process) routine to determine whether to echo, and also
115147476Sdumbbell * when called by the alias routine to determine whether to keep the
116147476Sdumbbell * argument list.
117147476Sdumbbell */
118147476Sdumbbellstatic bool hadhist = 0;
119147476Sdumbbell
120147476Sdumbbell/*
121147476Sdumbbell * Avoid alias expansion recursion via \!#
122147476Sdumbbell */
123147476Sdumbbellint     hleft;
124147476Sdumbbell
125147476SdumbbellChar    histline[BUFSIZE + 2];	/* last line input */
126147476Sdumbbell
127147476Sdumbbell /* The +2 is to fool hp's optimizer */
128147476Sdumbbellbool    histvalid = 0;		/* is histline valid */
129147476Sdumbbellstatic Char *histlinep = NULL;	/* current pointer into histline */
130147476Sdumbbell
131147476Sdumbbellstatic Char getCtmp;
132147476Sdumbbell
133147476Sdumbbell#define getC(f)		(((getCtmp = peekc) != '\0') ? (peekc = 0, getCtmp) : getC1(f))
134147476Sdumbbell#define	ungetC(c)	peekc = (Char) c
135147476Sdumbbell#define	ungetD(c)	peekd = (Char) c
136147476Sdumbbell
137147476Sdumbbell/* Use Htime to store timestamps picked up from history file for enthist()
138147476Sdumbbell * if reading saved history (sg)
139147476Sdumbbell */
140147476Sdumbbelltime_t Htime = (time_t)0;
141147476Sdumbbellstatic time_t a2time_t __P((Char *));
142147476Sdumbbell
143147476Sdumbbell/*
144147476Sdumbbell * for history event processing
145147476Sdumbbell * in the command 'echo !?foo?:1 !$' we want the !$ to expand from the line
146147476Sdumbbell * 'foo' was found instead of the last command
147147476Sdumbbell */
148147476Sdumbbellstatic int uselastevent = 1;
149147476Sdumbbell
150147476Sdumbbellint
151147476Sdumbbelllex(hp)
152147476Sdumbbell    struct wordent *hp;
153147476Sdumbbell{
154147476Sdumbbell    struct wordent *wdp;
155147476Sdumbbell    int     c;
156147476Sdumbbell
157147476Sdumbbell
158147476Sdumbbell    uselastevent = 1;
159147476Sdumbbell    histvalid = 0;
160147476Sdumbbell    histlinep = histline;
161147476Sdumbbell    *histlinep = '\0';
162147476Sdumbbell
163147476Sdumbbell    btell(&lineloc);
164147476Sdumbbell    hp->next = hp->prev = hp;
165147476Sdumbbell    hp->word = STRNULL;
166147476Sdumbbell    hadhist = 0;
167147476Sdumbbell    do
168147476Sdumbbell	c = readc(0);
169147476Sdumbbell    while (c == ' ' || c == '\t');
170147476Sdumbbell    if (c == HISTSUB && intty)
171147476Sdumbbell	/* ^lef^rit	from tty is short !:s^lef^rit */
172147476Sdumbbell	getexcl(c);
173147476Sdumbbell    else
174147476Sdumbbell	unreadc(c);
175147476Sdumbbell    wdp = hp;
176147476Sdumbbell    /*
177147476Sdumbbell     * The following loop is written so that the links needed by freelex will
178147476Sdumbbell     * be ready and rarin to go even if it is interrupted.
179147476Sdumbbell     */
180147476Sdumbbell    do {
181147476Sdumbbell	struct wordent *new;
182147476Sdumbbell
183147476Sdumbbell	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
184147476Sdumbbell	new->word = STRNULL;
185147476Sdumbbell	new->prev = wdp;
186147476Sdumbbell	new->next = hp;
187147476Sdumbbell	wdp->next = new;
188147476Sdumbbell	hp->prev = new;
189147476Sdumbbell	wdp = new;
190147476Sdumbbell	wdp->word = word();
191147476Sdumbbell    } while (wdp->word[0] != '\n');
192147476Sdumbbell    if (histlinep < histline + BUFSIZE) {
193147476Sdumbbell	*histlinep = '\0';
194147476Sdumbbell	if (histlinep > histline && histlinep[-1] == '\n')
195147476Sdumbbell	    histlinep[-1] = '\0';
196147476Sdumbbell	histvalid = 1;
197147476Sdumbbell    }
198147476Sdumbbell    else {
199147476Sdumbbell	histline[BUFSIZE - 1] = '\0';
200147476Sdumbbell    }
201147476Sdumbbell
202147476Sdumbbell    return (hadhist);
203147476Sdumbbell}
204147476Sdumbbell
205147476Sdumbbellstatic time_t
206147476Sdumbbella2time_t(word)
207147476Sdumbbell    Char *word;
208147476Sdumbbell{
209147476Sdumbbell    /* Attempt to distinguish timestamps from other possible entries.
210147476Sdumbbell     * Format: "+NNNNNNNNNN" (10 digits, left padded with ascii '0') */
211147476Sdumbbell
212147476Sdumbbell    time_t ret;
213147476Sdumbbell    Char *s;
214147476Sdumbbell    int ct;
215147476Sdumbbell
216147476Sdumbbell    if (!word || *(s = word) != '+')
217147476Sdumbbell	return (time_t)0;
218
219    for (++s, ret = 0, ct = 0; *s; ++s, ++ct)
220    {
221	if (!isdigit((unsigned char)*s))
222	    return (time_t)0;
223	ret = ret * 10 + (time_t)((unsigned char)*s - '0');
224    }
225
226    if (ct != 10)
227	return (time_t)0;
228
229    return ret;
230}
231
232void
233prlex(sp0)
234    struct wordent *sp0;
235{
236    struct wordent *sp = sp0->next;
237
238    for (;;) {
239	xprintf("%S", sp->word);
240	sp = sp->next;
241	if (sp == sp0)
242	    break;
243	if (sp->word[0] != '\n')
244	    xputchar(' ');
245    }
246}
247
248void
249copylex(hp, fp)
250    struct wordent *hp;
251    struct wordent *fp;
252{
253    struct wordent *wdp;
254
255    wdp = hp;
256    fp = fp->next;
257    do {
258	struct wordent *new;
259
260	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
261	new->word = STRNULL;
262	new->prev = wdp;
263	new->next = hp;
264	wdp->next = new;
265	hp->prev = new;
266	wdp = new;
267	wdp->word = Strsave(fp->word);
268	fp = fp->next;
269    } while (wdp->word[0] != '\n');
270}
271
272void
273freelex(vp)
274    struct wordent *vp;
275{
276    struct wordent *fp;
277
278    while (vp->next != vp) {
279	fp = vp->next;
280	vp->next = fp->next;
281	if (fp->word != STRNULL)
282	    xfree((ptr_t) fp->word);
283	xfree((ptr_t) fp);
284    }
285    vp->prev = vp;
286}
287
288static Char *
289word()
290{
291    Char c, c1;
292    Char *wp;
293    Char    wbuf[BUFSIZE];
294    Char    hbuf[12];
295    int	    h;
296    bool dolflg;
297    int i;
298
299    wp = wbuf;
300    i = BUFSIZE - 4;
301loop:
302    while ((c = getC(DOALL)) == ' ' || c == '\t')
303	continue;
304    if (cmap(c, _META | _ESC))
305	switch (c) {
306	case '&':
307	case '|':
308	case '<':
309	case '>':
310	    *wp++ = c;
311	    c1 = getC(DOALL);
312	    if (c1 == c)
313		*wp++ = c1;
314	    else
315		ungetC(c1);
316	    goto ret;
317
318	case '#':
319	    if (intty)
320		break;
321	    c = 0;
322	    h = 0;
323	    do {
324		c1 = c;
325		c = getC(0);
326		if (h < 12)
327		    hbuf[h++] = c;
328	    } while (c != '\n');
329	    hbuf[11] = '\0';
330	    Htime = a2time_t(hbuf);
331	    if (c1 == '\\')
332		goto loop;
333	    /*FALLTHROUGH*/
334
335	case ';':
336	case '(':
337	case ')':
338	case '\n':
339	    *wp++ = c;
340	    goto ret;
341
342	case '\\':
343	    c = getC(0);
344	    if (c == '\n') {
345		if (onelflg == 1)
346		    onelflg = 2;
347		goto loop;
348	    }
349	    if (c != HIST)
350		*wp++ = '\\', --i;
351	    c |= QUOTE;
352	default:
353	    break;
354	}
355    c1 = 0;
356    dolflg = DOALL;
357    for (;;) {
358	if (c1) {
359	    if (c == c1) {
360		c1 = 0;
361		dolflg = DOALL;
362	    }
363	    else if (c == '\\') {
364		c = getC(0);
365/*
366 * PWP: this is dumb, but how all of the other shells work.  If \ quotes
367 * a character OUTSIDE of a set of ''s, why shouldn't it quote EVERY
368 * following character INSIDE a set of ''s.
369 *
370 * Actually, all I really want to be able to say is 'foo\'bar' --> foo'bar
371 */
372		if (c == HIST)
373		    c |= QUOTE;
374		else {
375		    if (bslash_quote &&
376			((c == '\'') || (c == '"') ||
377			 (c == '\\'))) {
378			c |= QUOTE;
379		    }
380		    else {
381			if (c == '\n')
382			    /*
383			     * if (c1 == '`') c = ' '; else
384			     */
385			    c |= QUOTE;
386			ungetC(c);
387			c = '\\';
388		    }
389		}
390	    }
391	    else if (c == '\n') {
392		seterror(ERR_UNMATCHED, c1);
393		ungetC(c);
394		break;
395	    }
396	}
397	else if (cmap(c, _META | _QF | _QB | _ESC)) {
398	    if (c == '\\') {
399		c = getC(0);
400		if (c == '\n') {
401		    if (onelflg == 1)
402			onelflg = 2;
403		    break;
404		}
405		if (c != HIST)
406		    *wp++ = '\\', --i;
407		c |= QUOTE;
408	    }
409	    else if (cmap(c, _QF | _QB)) {	/* '"` */
410		c1 = c;
411		dolflg = c == '"' ? DOALL : DOEXCL;
412	    }
413	    else if (c != '#' || !intty) {
414		ungetC(c);
415		break;
416	    }
417	}
418	if (--i > 0) {
419	    *wp++ = c;
420	    c = getC(dolflg);
421	}
422	else {
423	    seterror(ERR_WTOOLONG);
424	    wp = &wbuf[1];
425	    break;
426	}
427    }
428ret:
429    *wp = 0;
430    return (Strsave(wbuf));
431}
432
433static int
434getC1(flag)
435    int flag;
436{
437    Char c;
438
439    for (;;) {
440	if ((c = peekc) != 0) {
441	    peekc = 0;
442	    return (c);
443	}
444	if (lap) {
445	    if ((c = *lap++) == 0)
446		lap = 0;
447	    else {
448		if (cmap(c, _META | _QF | _QB))
449		    c |= QUOTE;
450		return (c);
451	    }
452	}
453	if ((c = peekd) != 0) {
454	    peekd = 0;
455	    return (c);
456	}
457	if (exclp) {
458	    if ((c = *exclp++) != 0)
459		return (c);
460	    if (exclnxt && --exclc >= 0) {
461		exclnxt = exclnxt->next;
462		setexclp(exclnxt->word);
463		return (' ');
464	    }
465	    exclp = 0;
466	    exclnxt = 0;
467	    /* this will throw away the dummy history entries */
468	    savehist(NULL, 0);
469
470	}
471	if (exclnxt) {
472	    exclnxt = exclnxt->next;
473	    if (--exclc < 0)
474		exclnxt = 0;
475	    else
476		setexclp(exclnxt->word);
477	    continue;
478	}
479	c = readc(0);
480	if (c == '$' && (flag & DODOL)) {
481	    getdol();
482	    continue;
483	}
484	if (c == HIST && (flag & DOEXCL)) {
485	    getexcl(0);
486	    continue;
487	}
488	break;
489    }
490    return (c);
491}
492
493static void
494getdol()
495{
496    Char *np, *ep;
497    Char    name[4 * MAXVARLEN + 1];
498    int c;
499    int     sc;
500    bool    special = 0, toolong;
501
502    np = name, *np++ = '$';
503    c = sc = getC(DOEXCL);
504    if (any("\t \n", c)) {
505	ungetD(c);
506	ungetC('$' | QUOTE);
507	return;
508    }
509    if (c == '{')
510	*np++ = (Char) c, c = getC(DOEXCL);
511    if (c == '#' || c == '?' || c == '%')
512	special++, *np++ = (Char) c, c = getC(DOEXCL);
513    *np++ = (Char) c;
514    switch (c) {
515
516    case '<':
517    case '$':
518    case '!':
519	if (special)
520	    seterror(ERR_SPDOLLT);
521	*np = 0;
522	addla(name);
523	return;
524
525    case '\n':
526	ungetD(c);
527	np--;
528	if (!special)
529	    seterror(ERR_NEWLINE);
530	*np = 0;
531	addla(name);
532	return;
533
534    case '*':
535	if (special)
536	    seterror(ERR_SPSTAR);
537	*np = 0;
538	addla(name);
539	return;
540
541    default:
542	toolong = 0;
543	if (Isdigit(c)) {
544#ifdef notdef
545	    /* let $?0 pass for now */
546	    if (special) {
547		seterror(ERR_DIGIT);
548		*np = 0;
549		addla(name);
550		return;
551	    }
552#endif
553	    /* we know that np < &name[4] */
554	    ep = &np[MAXVARLEN];
555	    while ((c = getC(DOEXCL)) != 0) {
556		if (!Isdigit(c))
557		    break;
558		if (np < ep)
559		    *np++ = (Char) c;
560		else
561		    toolong = 1;
562	    }
563	}
564	else if (letter(c)) {
565	    /* we know that np < &name[4] */
566	    ep = &np[MAXVARLEN];
567	    toolong = 0;
568	    while ((c = getC(DOEXCL)) != 0) {
569		/* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
570		if (!letter(c) && !Isdigit(c))
571		    break;
572		if (np < ep)
573		    *np++ = (Char) c;
574		else
575		    toolong = 1;
576	    }
577	}
578	else {
579	    if (!special)
580		seterror(ERR_VARILL);
581	    else {
582		ungetD(c);
583		--np;
584	    }
585	    *np = 0;
586	    addla(name);
587	    return;
588	}
589	if (toolong) {
590	    seterror(ERR_VARTOOLONG);
591	    *np = 0;
592	    addla(name);
593	    return;
594	}
595	break;
596    }
597    if (c == '[') {
598	*np++ = (Char) c;
599	/*
600	 * Name up to here is a max of MAXVARLEN + 8.
601	 */
602	ep = &np[2 * MAXVARLEN + 8];
603	do {
604	    /*
605	     * Michael Greim: Allow $ expansion to take place in selector
606	     * expressions. (limits the number of characters returned)
607	     */
608	    c = getC(DOEXCL | DODOL);
609	    if (c == '\n') {
610		ungetD(c);
611		np--;
612		seterror(ERR_NLINDEX);
613		*np = 0;
614		addla(name);
615		return;
616	    }
617	    if (np < ep)
618		*np++ = (Char) c;
619	} while (c != ']');
620	*np = '\0';
621	if (np >= ep) {
622	    seterror(ERR_SELOVFL);
623	    addla(name);
624	    return;
625	}
626	c = getC(DOEXCL);
627    }
628    /*
629     * Name up to here is a max of 2 * MAXVARLEN + 8.
630     */
631    if (c == ':') {
632	/*
633	 * if the :g modifier is followed by a newline, then error right away!
634	 * -strike
635	 */
636
637	int     gmodflag = 0, amodflag = 0;
638
639#ifndef COMPAT
640	do {
641#endif /* COMPAT */
642	    *np++ = (Char) c, c = getC(DOEXCL);
643	    if (c == 'g' || c == 'a') {
644		if (c == 'g')
645		    gmodflag++;
646		else
647		    amodflag++;
648		*np++ = (Char) c; c = getC(DOEXCL);
649	    }
650	    if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
651		if (c == 'g')
652		    gmodflag++;
653		else
654		    amodflag++;
655		*np++ = (Char) c; c = getC(DOEXCL);
656	    }
657	    *np++ = (Char) c;
658	    /* scan s// [eichin:19910926.0512EST] */
659	    if (c == 's') {
660		int delimcnt = 2;
661		int delim = getC(0);
662		*np++ = (Char) delim;
663
664		if (!delim || letter(delim)
665		    || Isdigit(delim) || any(" \t\n", delim)) {
666		    seterror(ERR_BADSUBST);
667		    break;
668		}
669		while ((c = getC(0)) != (-1)) {
670		    *np++ = (Char) c;
671		    if(c == delim) delimcnt--;
672		    if(!delimcnt) break;
673		}
674		if(delimcnt) {
675		    seterror(ERR_BADSUBST);
676		    break;
677		}
678		c = 's';
679	    }
680	    if (!any("htrqxesul", c)) {
681		if ((amodflag || gmodflag) && c == '\n')
682		    stderror(ERR_VARSYN);	/* strike */
683		seterror(ERR_BADMOD, c);
684		*np = 0;
685		addla(name);
686		return;
687	    }
688#ifndef COMPAT
689	}
690	while ((c = getC(DOEXCL)) == ':');
691	ungetD(c);
692#endif /* COMPAT */
693    }
694    else
695	ungetD(c);
696    if (sc == '{') {
697	c = getC(DOEXCL);
698	if (c != '}') {
699	    ungetD(c);
700	    seterror(ERR_MISSING, '}');
701	    *np = 0;
702	    addla(name);
703	    return;
704	}
705	*np++ = (Char) c;
706    }
707    *np = 0;
708    addla(name);
709    return;
710}
711
712void
713addla(cp)
714    Char   *cp;
715{
716    Char    buf[BUFSIZE];
717
718    if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
719	(sizeof(labuf) - 4) / sizeof(Char)) {
720	seterror(ERR_EXPOVFL);
721	return;
722    }
723    if (lap)
724	(void) Strcpy(buf, lap);
725    (void) Strcpy(labuf, cp);
726    if (lap)
727	(void) Strcat(labuf, buf);
728    lap = labuf;
729}
730
731static Char lhsb[32];
732static Char slhs[32];
733static Char rhsb[64];
734static int quesarg;
735
736static void
737getexcl(sc)
738    int    sc;
739{
740    struct wordent *hp, *ip;
741    int     left, right, dol;
742    int c;
743
744    if (sc == 0) {
745	sc = getC(0);
746	if (sc != '{') {
747	    ungetC(sc);
748	    sc = 0;
749	}
750    }
751    quesarg = -1;
752
753    if (uselastevent) {
754	uselastevent = 0;
755	lastev = eventno;
756    }
757    else
758	lastev = eventno;
759    hp = gethent(sc);
760    if (hp == 0)
761	return;
762    hadhist = 1;
763    dol = 0;
764    if (hp == alhistp)
765	for (ip = hp->next->next; ip != alhistt; ip = ip->next)
766	    dol++;
767    else
768	for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
769	    dol++;
770    left = 0, right = dol;
771    if (sc == HISTSUB) {
772	ungetC('s'), unreadc(HISTSUB), c = ':';
773	goto subst;
774    }
775    c = getC(0);
776    if (!any(":^$*-%", c))
777	goto subst;
778    left = right = -1;
779    if (c == ':') {
780	c = getC(0);
781	unreadc(c);
782	if (letter(c) || c == '&') {
783	    c = ':';
784	    left = 0, right = dol;
785	    goto subst;
786	}
787    }
788    else
789	ungetC(c);
790    if (!getsel(&left, &right, dol))
791	return;
792    c = getC(0);
793    if (c == '*')
794	ungetC(c), c = '-';
795    if (c == '-') {
796	if (!getsel(&left, &right, dol))
797	    return;
798	c = getC(0);
799    }
800subst:
801    exclc = right - left + 1;
802    while (--left >= 0)
803	hp = hp->next;
804    if (sc == HISTSUB || c == ':') {
805	do {
806	    hp = getsub(hp);
807	    c = getC(0);
808	} while (c == ':');
809    }
810    unreadc(c);
811    if (sc == '{') {
812	c = getC(0);
813	if (c != '}')
814	    seterror(ERR_BADBANG);
815    }
816    exclnxt = hp;
817}
818
819static struct wordent *
820getsub(en)
821    struct wordent *en;
822{
823    Char *cp;
824    int     delim;
825    int c;
826    int     sc;
827    bool global;
828    Char    orhsb[sizeof(rhsb) / sizeof(Char)];
829
830#ifndef COMPAT
831    do {
832#endif /* COMPAT */
833	exclnxt = 0;
834	global = 0;
835	sc = c = getC(0);
836	if (c == 'g' || c == 'a') {
837	    global |= (c == 'g') ? 1 : 2;
838	    sc = c = getC(0);
839	}
840	if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
841	    global |= (c == 'g') ? 1 : 2;
842	    sc = c = getC(0);
843	}
844
845	switch (c) {
846	case 'p':
847	    justpr++;
848	    return (en);
849
850	case 'x':
851	case 'q':
852	    global |= 1;
853	    /*FALLTHROUGH*/
854
855	case 'h':
856	case 'r':
857	case 't':
858	case 'e':
859	case 'u':
860	case 'l':
861	    break;
862
863	case '&':
864	    if (slhs[0] == 0) {
865		seterror(ERR_NOSUBST);
866		return (en);
867	    }
868	    (void) Strcpy(lhsb, slhs);
869	    break;
870
871#ifdef notdef
872	case '~':
873	    if (lhsb[0] == 0)
874		goto badlhs;
875	    break;
876#endif
877
878	case 's':
879	    delim = getC(0);
880	    if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
881		unreadc(delim);
882		lhsb[0] = 0;
883		seterror(ERR_BADSUBST);
884		return (en);
885	    }
886	    cp = lhsb;
887	    for (;;) {
888		c = getC(0);
889		if (c == '\n') {
890		    unreadc(c);
891		    break;
892		}
893		if (c == delim)
894		    break;
895		if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) {
896		    lhsb[0] = 0;
897		    seterror(ERR_BADSUBST);
898		    return (en);
899		}
900		if (c == '\\') {
901		    c = getC(0);
902		    if (c != delim && c != '\\')
903			*cp++ = '\\';
904		}
905		*cp++ = (Char) c;
906	    }
907	    if (cp != lhsb)
908		*cp++ = 0;
909	    else if (lhsb[0] == 0) {
910		seterror(ERR_LHS);
911		return (en);
912	    }
913	    cp = rhsb;
914	    (void) Strcpy(orhsb, cp);
915	    for (;;) {
916		c = getC(0);
917		if (c == '\n') {
918		    unreadc(c);
919		    break;
920		}
921		if (c == delim)
922		    break;
923#ifdef notdef
924		if (c == '~') {
925		    if (&cp[Strlen(orhsb)] > &rhsb[sizeof(rhsb) /
926						   sizeof(Char) - 2])
927			goto toorhs;
928		    (void) Strcpy(cp, orhsb);
929		    cp = Strend(cp);
930		    continue;
931		}
932#endif
933		if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
934		    seterror(ERR_RHSLONG);
935		    return (en);
936		}
937		if (c == '\\') {
938		    c = getC(0);
939		    if (c != delim /* && c != '~' */ )
940			*cp++ = '\\';
941		}
942		*cp++ = (Char) c;
943	    }
944	    *cp++ = 0;
945	    break;
946
947	default:
948	    if (c == '\n')
949		unreadc(c);
950	    seterror(ERR_BADBANGMOD, c);
951	    return (en);
952	}
953	(void) Strcpy(slhs, lhsb);
954	if (exclc)
955	    en = dosub(sc, en, global);
956#ifndef COMPAT
957    }
958    while ((c = getC(0)) == ':');
959    unreadc(c);
960#endif /* COMPAT */
961    return (en);
962}
963
964/*
965 *
966 * From Beto Appleton (beto@aixwiz.austin.ibm.com)
967 *
968 * when using history substitution, and the variable
969 * 'history' is set to a value higher than 1000,
970 * the shell might either freeze (hang) or core-dump.
971 * We raise the limit to 50000000
972 */
973
974#define HIST_PURGE -50000000
975static struct wordent *
976dosub(sc, en, global)
977    int     sc;
978    struct wordent *en;
979    bool global;
980{
981    struct wordent lexi;
982    bool    didsub = 0, didone = 0;
983    struct wordent *hp = &lexi;
984    struct wordent *wdp;
985    int i = exclc;
986    struct Hist *hst;
987
988    wdp = hp;
989    while (--i >= 0) {
990	struct wordent *new =
991		(struct wordent *) xcalloc(1, sizeof *wdp);
992
993	new->word = 0;
994	new->prev = wdp;
995	new->next = hp;
996	wdp->next = new;
997	wdp = new;
998	en = en->next;
999	if (en->word) {
1000	    Char *tword, *otword;
1001
1002	    if ((global & 1) || didsub == 0) {
1003		tword = subword(en->word, sc, &didone);
1004		if (didone)
1005		    didsub = 1;
1006		if (global & 2) {
1007		    while (didone && tword != STRNULL) {
1008			otword = tword;
1009			tword = subword(otword, sc, &didone);
1010			if (Strcmp(tword, otword) == 0) {
1011			    xfree((ptr_t) otword);
1012			    break;
1013			}
1014			else
1015			    xfree((ptr_t) otword);
1016		    }
1017		}
1018	    }
1019	    else
1020		tword = Strsave(en->word);
1021	    wdp->word = tword;
1022	}
1023    }
1024    if (didsub == 0)
1025	seterror(ERR_MODFAIL);
1026    hp->prev = wdp;
1027    /*
1028     * ANSI mode HP/UX compiler chokes on
1029     * return &enthist(HIST_PURGE, &lexi, 0)->Hlex;
1030     */
1031    hst = enthist(HIST_PURGE, &lexi, 0, 0);
1032    return &(hst->Hlex);
1033}
1034
1035static Char *
1036subword(cp, type, adid)
1037    Char   *cp;
1038    int     type;
1039    bool   *adid;
1040{
1041    Char    wbuf[BUFSIZE];
1042    Char *wp, *mp, *np;
1043    int i;
1044
1045    *adid = 0;
1046    switch (type) {
1047
1048    case 'r':
1049    case 'e':
1050    case 'h':
1051    case 't':
1052    case 'q':
1053    case 'x':
1054    case 'u':
1055    case 'l':
1056	wp = domod(cp, type);
1057	if (wp == 0)
1058	    return (Strsave(cp));
1059	*adid = 1;
1060	return (wp);
1061
1062    default:
1063	wp = wbuf;
1064	i = BUFSIZE - 4;
1065	for (mp = cp; *mp; mp++)
1066	    if (matchs(mp, lhsb)) {
1067		for (np = cp; np < mp;)
1068		    *wp++ = *np++, --i;
1069		for (np = rhsb; *np; np++)
1070		    switch (*np) {
1071
1072		    case '\\':
1073			if (np[1] == '&')
1074			    np++;
1075			/* fall into ... */
1076
1077		    default:
1078			if (--i < 0) {
1079			    seterror(ERR_SUBOVFL);
1080			    return (STRNULL);
1081			}
1082			*wp++ = *np;
1083			continue;
1084
1085		    case '&':
1086			i -= Strlen(lhsb);
1087			if (i < 0) {
1088			    seterror(ERR_SUBOVFL);
1089			    return (STRNULL);
1090			}
1091			*wp = 0;
1092			(void) Strcat(wp, lhsb);
1093			wp = Strend(wp);
1094			continue;
1095		    }
1096		mp += Strlen(lhsb);
1097		i -= Strlen(mp);
1098		if (i < 0) {
1099		    seterror(ERR_SUBOVFL);
1100		    return (STRNULL);
1101		}
1102		*wp = 0;
1103		(void) Strcat(wp, mp);
1104		*adid = 1;
1105		return (Strsave(wbuf));
1106	    }
1107	return (Strsave(cp));
1108    }
1109}
1110
1111Char   *
1112domod(cp, type)
1113    Char   *cp;
1114    int     type;
1115{
1116    Char *wp, *xp;
1117    int c;
1118
1119    switch (type) {
1120
1121    case 'x':
1122    case 'q':
1123	wp = Strsave(cp);
1124	for (xp = wp; (c = *xp) != 0; xp++)
1125	    if ((c != ' ' && c != '\t') || type == 'q')
1126		*xp |= QUOTE;
1127	return (wp);
1128
1129    case 'l':
1130	wp = Strsave(cp);
1131	for (cp = wp; *cp; cp++)
1132	    if (Isupper(*cp)) {
1133		*cp = Tolower(*cp);
1134		return wp;
1135	    }
1136	return wp;
1137
1138    case 'u':
1139	wp = Strsave(cp);
1140	for (cp = wp; *cp; cp++)
1141	    if (Islower(*cp)) {
1142		*cp = Toupper(*cp);
1143		return wp;
1144	    }
1145	return wp;
1146
1147    case 'h':
1148    case 't':
1149	if (!any(short2str(cp), '/'))
1150	    return (type == 't' ? Strsave(cp) : 0);
1151	wp = Strend(cp);
1152	while (*--wp != '/')
1153	    continue;
1154	if (type == 'h')
1155	    xp = Strsave(cp), xp[wp - cp] = 0;
1156	else
1157	    xp = Strsave(wp + 1);
1158	return (xp);
1159
1160    case 'e':
1161    case 'r':
1162	wp = Strend(cp);
1163	for (wp--; wp >= cp && *wp != '/'; wp--)
1164	    if (*wp == '.') {
1165		if (type == 'e')
1166		    xp = Strsave(wp + 1);
1167		else
1168		    xp = Strsave(cp), xp[wp - cp] = 0;
1169		return (xp);
1170	    }
1171	return (Strsave(type == 'e' ? STRNULL : cp));
1172    default:
1173	break;
1174    }
1175    return (0);
1176}
1177
1178static int
1179matchs(str, pat)
1180    Char *str, *pat;
1181{
1182    while (*str && *pat && *str == *pat)
1183	str++, pat++;
1184    return (*pat == 0);
1185}
1186
1187static int
1188getsel(al, ar, dol)
1189    int *al, *ar;
1190    int     dol;
1191{
1192    int c = getC(0);
1193    int i;
1194    bool    first = *al < 0;
1195
1196    switch (c) {
1197
1198    case '%':
1199	if (quesarg == -1) {
1200	    seterror(ERR_BADBANGARG);
1201	    return (0);
1202	}
1203	if (*al < 0)
1204	    *al = quesarg;
1205	*ar = quesarg;
1206	break;
1207
1208    case '-':
1209	if (*al < 0) {
1210	    *al = 0;
1211	    *ar = dol - 1;
1212	    unreadc(c);
1213	}
1214	return (1);
1215
1216    case '^':
1217	if (*al < 0)
1218	    *al = 1;
1219	*ar = 1;
1220	break;
1221
1222    case '$':
1223	if (*al < 0)
1224	    *al = dol;
1225	*ar = dol;
1226	break;
1227
1228    case '*':
1229	if (*al < 0)
1230	    *al = 1;
1231	*ar = dol;
1232	if (*ar < *al) {
1233	    *ar = 0;
1234	    *al = 1;
1235	    return (1);
1236	}
1237	break;
1238
1239    default:
1240	if (Isdigit(c)) {
1241	    i = 0;
1242	    while (Isdigit(c)) {
1243		i = i * 10 + c - '0';
1244		c = getC(0);
1245	    }
1246	    if (i < 0)
1247		i = dol + 1;
1248	    if (*al < 0)
1249		*al = i;
1250	    *ar = i;
1251	}
1252	else if (*al < 0)
1253	    *al = 0, *ar = dol;
1254	else
1255	    *ar = dol - 1;
1256	unreadc(c);
1257	break;
1258    }
1259    if (first) {
1260	c = getC(0);
1261	unreadc(c);
1262	if (any("-$*", c))
1263	    return (1);
1264    }
1265    if (*al > *ar || *ar > dol) {
1266	seterror(ERR_BADBANGARG);
1267	return (0);
1268    }
1269    return (1);
1270
1271}
1272
1273static struct wordent *
1274gethent(sc)
1275    int     sc;
1276{
1277    struct Hist *hp;
1278    Char *np;
1279    int c;
1280    int     event;
1281    bool    back = 0;
1282
1283    c = sc == HISTSUB ? HIST : getC(0);
1284    if (c == HIST) {
1285	if (alhistp)
1286	    return (alhistp);
1287	event = eventno;
1288    }
1289    else
1290	switch (c) {
1291
1292	case ':':
1293	case '^':
1294	case '$':
1295	case '*':
1296	case '%':
1297	    ungetC(c);
1298	    if (lastev == eventno && alhistp)
1299		return (alhistp);
1300	    event = lastev;
1301	    break;
1302
1303	case '#':		/* !# is command being typed in (mrh) */
1304	    if (--hleft == 0) {
1305		seterror(ERR_HISTLOOP);
1306		return (0);
1307	    }
1308	    else
1309		return (&paraml);
1310	    /* NOTREACHED */
1311
1312	case '-':
1313	    back = 1;
1314	    c = getC(0);
1315	    /* FALLSTHROUGH */
1316
1317	default:
1318	    if (any("(=~", c)) {
1319		unreadc(c);
1320		ungetC(HIST);
1321		return (0);
1322	    }
1323	    np = lhsb;
1324	    event = 0;
1325	    while (!cmap(c, _ESC | _META | _QF | _QB) && !any("^*-%${}:#", c)) {
1326		if (event != -1 && Isdigit(c))
1327		    event = event * 10 + c - '0';
1328		else
1329		    event = -1;
1330		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1331		    *np++ = (Char) c;
1332		c = getC(0);
1333	    }
1334	    unreadc(c);
1335	    if (np == lhsb) {
1336		ungetC(HIST);
1337		return (0);
1338	    }
1339	    *np++ = 0;
1340	    if (event != -1) {
1341		/*
1342		 * History had only digits
1343		 */
1344		if (back)
1345		    event = eventno + (alhistp == 0) - (event ? event : 0);
1346		break;
1347	    }
1348	    if (back) {
1349		event = sizeof(lhsb) / sizeof(lhsb[0]);
1350		np = &lhsb[--event];
1351		*np-- = '\0';
1352		for (event--; np > lhsb; *np-- = lhsb[--event])
1353		    continue;
1354		*np = '-';
1355	    }
1356	    hp = findev(lhsb, 0);
1357	    if (hp)
1358		lastev = hp->Hnum;
1359	    return (&hp->Hlex);
1360
1361	case '?':
1362	    np = lhsb;
1363	    for (;;) {
1364		c = getC(0);
1365		if (c == '\n') {
1366		    unreadc(c);
1367		    break;
1368		}
1369		if (c == '?')
1370		    break;
1371		if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1372		    *np++ = (Char) c;
1373	    }
1374	    if (np == lhsb) {
1375		if (lhsb[0] == 0) {
1376		    seterror(ERR_NOSEARCH);
1377		    return (0);
1378		}
1379	    }
1380	    else
1381		*np++ = 0;
1382	    hp = findev(lhsb, 1);
1383	    if (hp)
1384		lastev = hp->Hnum;
1385	    return (&hp->Hlex);
1386	}
1387
1388    for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1389	if (hp->Hnum == event) {
1390	    hp->Href = eventno;
1391	    lastev = hp->Hnum;
1392	    return (&hp->Hlex);
1393	}
1394    np = putn(event);
1395    seterror(ERR_NOEVENT, short2str(np));
1396    return (0);
1397}
1398
1399static struct Hist *
1400findev(cp, anyarg)
1401    Char   *cp;
1402    bool    anyarg;
1403{
1404    struct Hist *hp;
1405
1406    for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1407	Char   *dp;
1408	Char *p, *q;
1409	struct wordent *lp = hp->Hlex.next;
1410	int     argno = 0;
1411
1412	/*
1413	 * The entries added by alias substitution don't have a newline but do
1414	 * have a negative event number. Savehist() trims off these entries,
1415	 * but it happens before alias expansion, too early to delete those
1416	 * from the previous command.
1417	 */
1418	if (hp->Hnum < 0)
1419	    continue;
1420	if (lp->word[0] == '\n')
1421	    continue;
1422	if (!anyarg) {
1423	    p = cp;
1424	    q = lp->word;
1425	    do
1426		if (!*p)
1427		    return (hp);
1428	    while (*p++ == *q++);
1429	    continue;
1430	}
1431	do {
1432	    for (dp = lp->word; *dp; dp++) {
1433		p = cp;
1434		q = dp;
1435		do
1436		    if (!*p) {
1437			quesarg = argno;
1438			return (hp);
1439		    }
1440		while (*p++ == *q++);
1441	    }
1442	    lp = lp->next;
1443	    argno++;
1444	} while (lp->word[0] != '\n');
1445    }
1446    seterror(ERR_NOEVENT, short2str(cp));
1447    return (0);
1448}
1449
1450
1451static void
1452setexclp(cp)
1453    Char *cp;
1454{
1455    if (cp && cp[0] == '\n')
1456	return;
1457    exclp = cp;
1458}
1459
1460void
1461unreadc(c)
1462    int    c;
1463{
1464    peekread = (Char) c;
1465}
1466
1467int
1468readc(wanteof)
1469    bool    wanteof;
1470{
1471    int c;
1472    static  int sincereal;	/* Number of real EOFs we've seen */
1473    Char *ptr;			/* For STRignoreeof */
1474    int numeof = 0;		/* Value of STRignoreeof */
1475
1476#ifdef DEBUG_INP
1477    xprintf("readc\n");
1478#endif
1479    if ((c = peekread) != 0) {
1480	peekread = 0;
1481	return (c);
1482    }
1483
1484    /* Compute the value of EOFs */
1485    if ((ptr = varval(STRignoreeof)) != STRNULL) {
1486	while (*ptr) {
1487	    if (!Isdigit(*ptr)) {
1488		numeof = 0;
1489		break;
1490	    }
1491	    numeof = numeof * 10 + *ptr++ - '0';
1492	}
1493    }
1494    if (numeof < 1) numeof = 26;	/* Sanity check */
1495
1496top:
1497    aret = F_SEEK;
1498    if (alvecp) {
1499	arun = 1;
1500#ifdef DEBUG_INP
1501	xprintf("alvecp %c\n", *alvecp & 0xff);
1502#endif
1503	aret = A_SEEK;
1504	if ((c = *alvecp++) != 0)
1505	    return (c);
1506	if (alvec && *alvec) {
1507		alvecp = *alvec++;
1508		return (' ');
1509	}
1510	else {
1511	    alvecp = NULL;
1512	    aret = F_SEEK;
1513	    return('\n');
1514	}
1515    }
1516    if (alvec) {
1517	arun = 1;
1518	if ((alvecp = *alvec) != 0) {
1519	    alvec++;
1520	    goto top;
1521	}
1522	/* Infinite source! */
1523	return ('\n');
1524    }
1525    arun = 0;
1526    if (evalp) {
1527	aret = E_SEEK;
1528	if ((c = *evalp++) != 0)
1529	    return (c);
1530	if (evalvec && *evalvec) {
1531	    evalp = *evalvec++;
1532	    return (' ');
1533	}
1534	aret = F_SEEK;
1535	evalp = 0;
1536    }
1537    if (evalvec) {
1538	if (evalvec == INVPPTR) {
1539	    doneinp = 1;
1540	    reset();
1541	}
1542	if ((evalp = *evalvec) != 0) {
1543	    evalvec++;
1544	    goto top;
1545	}
1546	evalvec = INVPPTR;
1547	return ('\n');
1548    }
1549    do {
1550	if (arginp == INVPTR || onelflg == 1) {
1551	    if (wanteof)
1552		return (-1);
1553	    exitstat();
1554	}
1555	if (arginp) {
1556	    if ((c = *arginp++) == 0) {
1557		arginp = INVPTR;
1558		return ('\n');
1559	    }
1560	    return (c);
1561	}
1562#ifdef BSDJOBS
1563reread:
1564#endif /* BSDJOBS */
1565	c = bgetc();
1566	if (c < 0) {
1567#ifndef WINNT
1568# ifndef POSIX
1569#  ifdef TERMIO
1570	    struct termio tty;
1571#  else /* SGTTYB */
1572	    struct sgttyb tty;
1573#  endif /* TERMIO */
1574# else /* POSIX */
1575	    struct termios tty;
1576# endif /* POSIX */
1577#endif /* !WINNT */
1578	    if (wanteof)
1579		return (-1);
1580	    /* was isatty but raw with ignoreeof yields problems */
1581#ifndef WINNT
1582# ifndef POSIX
1583#  ifdef TERMIO
1584	    if (ioctl(SHIN, TCGETA, (ioctl_t) & tty) == 0 &&
1585		(tty.c_lflag & ICANON))
1586#  else /* GSTTYB */
1587	    if (ioctl(SHIN, TIOCGETP, (ioctl_t) & tty) == 0 &&
1588		(tty.sg_flags & RAW) == 0)
1589#  endif /* TERMIO */
1590# else /* POSIX */
1591	    if (tcgetattr(SHIN, &tty) == 0 &&
1592		(tty.c_lflag & ICANON))
1593# endif /* POSIX */
1594#else /* WINNT */
1595	    if (isatty(SHIN))
1596#endif /* !WINNT */
1597	    {
1598		/* was 'short' for FILEC */
1599#ifdef BSDJOBS
1600		int     ctpgrp;
1601#endif /* BSDJOBS */
1602
1603		if (++sincereal >= numeof)	/* Too many EOFs?  Bye! */
1604		    goto oops;
1605#ifdef BSDJOBS
1606		if (tpgrp != -1 &&
1607		    (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
1608		    tpgrp != ctpgrp) {
1609		    (void) tcsetpgrp(FSHTTY, tpgrp);
1610# ifdef _SEQUENT_
1611		    if (ctpgrp)
1612# endif /* _SEQUENT */
1613		    (void) killpg((pid_t) ctpgrp, SIGHUP);
1614# ifdef notdef
1615		    /*
1616		     * With the walking process group fix, this message
1617		     * is now obsolete. As the foreground process group
1618		     * changes, the shell needs to adjust. Well too bad.
1619		     */
1620		    xprintf(CGETS(16, 1, "Reset tty pgrp from %d to %d\n"),
1621			    ctpgrp, tpgrp);
1622# endif /* notdef */
1623		    goto reread;
1624		}
1625#endif /* BSDJOBS */
1626		/* What follows is complicated EOF handling -- sterling@netcom.com */
1627		/* First, we check to see if we have ignoreeof set */
1628		if (adrof(STRignoreeof)) {
1629			/* If so, we check for any stopped jobs only on the first EOF */
1630			if ((sincereal == 1) && (chkstop == 0)) {
1631				panystop(1);
1632			}
1633		} else {
1634			/* If we don't have ignoreeof set, always check for stopped jobs */
1635			if (chkstop == 0) {
1636				panystop(1);
1637			}
1638		}
1639		/* At this point, if there were stopped jobs, we would have already
1640		 * called reset().  If we got this far, assume we can print an
1641		 * exit/logout message if we ignoreeof, or just exit.
1642		 */
1643		if (adrof(STRignoreeof)) {
1644			/* If so, tell the user to use exit or logout */
1645		    if (loginsh) {
1646				xprintf(CGETS(16, 2,
1647					"\nUse \"logout\" to logout.\n"));
1648		   	} else {
1649				xprintf(CGETS(16, 3,
1650					"\nUse \"exit\" to leave %s.\n"),
1651					progname);
1652			}
1653			reset();
1654		} else {
1655			/* If we don't have ignoreeof set, just fall through */
1656			;	/* EMPTY */
1657		}
1658	    }
1659    oops:
1660	    doneinp = 1;
1661	    reset();
1662	}
1663	sincereal = 0;
1664	if (c == '\n' && onelflg)
1665	    onelflg--;
1666    } while (c == 0);
1667    if (histlinep < histline + BUFSIZE)
1668	*histlinep++ = (Char) c;
1669    return (c);
1670}
1671
1672static void
1673balloc(buf)
1674    int buf;
1675{
1676    Char **nfbuf;
1677
1678    while (buf >= fblocks) {
1679	nfbuf = (Char **) xcalloc((size_t) (fblocks + 2),
1680			  sizeof(Char **));
1681	if (fbuf) {
1682	    (void) blkcpy(nfbuf, fbuf);
1683	    xfree((ptr_t) fbuf);
1684	}
1685	fbuf = nfbuf;
1686	fbuf[fblocks] = (Char *) xcalloc(BUFSIZE, sizeof(Char));
1687	fblocks++;
1688    }
1689}
1690
1691static int
1692bgetc()
1693{
1694    int c, off, buf;
1695    int numleft = 0, roomleft;
1696    char    tbuf[BUFSIZE + 1];
1697
1698    if (cantell) {
1699	if (fseekp < fbobp || fseekp > feobp) {
1700	    fbobp = feobp = fseekp;
1701	    (void) lseek(SHIN, fseekp, L_SET);
1702	}
1703	if (fseekp == feobp) {
1704	    int     i;
1705
1706	    fbobp = feobp;
1707	    do
1708		c = read(SHIN, tbuf, BUFSIZE);
1709	    while (c < 0 && errno == EINTR);
1710#ifdef convex
1711	    if (c < 0)
1712		stderror(ERR_SYSTEM, progname, strerror(errno));
1713#endif /* convex */
1714	    if (c <= 0)
1715		return (-1);
1716	    for (i = 0; i < c; i++)
1717		fbuf[0][i] = (unsigned char) tbuf[i];
1718	    feobp += c;
1719	}
1720#ifndef WINNT
1721	c = fbuf[0][fseekp - fbobp];
1722	fseekp++;
1723#else
1724	do {
1725	    c = fbuf[0][fseekp - fbobp];
1726	    fseekp++;
1727	} while(c == '\r');
1728#endif /* !WINNT */
1729	return (c);
1730    }
1731
1732    while (fseekp >= feobp) {
1733	if (editing && intty) {		/* then use twenex routine */
1734	    fseekp = feobp;		/* where else? */
1735	    c = numleft = Inputl();	/* PWP: get a line */
1736	    while (numleft > 0) {
1737		off = (int) feobp % BUFSIZE;
1738		buf = (int) feobp / BUFSIZE;
1739		balloc(buf);
1740		roomleft = BUFSIZE - off;
1741		if (roomleft > numleft)
1742		    roomleft = numleft;
1743		(void) memmove((ptr_t) (fbuf[buf] + off), (ptr_t) (InputBuf + c - numleft), (size_t) (roomleft * sizeof(Char)));
1744		numleft -= roomleft;
1745		feobp += roomleft;
1746	    }
1747	}
1748	else {
1749	    off = (int) feobp % BUFSIZE;
1750	    buf = (int) feobp / BUFSIZE;
1751	    balloc(buf);
1752	    roomleft = BUFSIZE - off;
1753	    c = read(SHIN, tbuf, (size_t) roomleft);
1754	    if (c > 0) {
1755		int     i;
1756		Char   *ptr = fbuf[buf] + off;
1757
1758		for (i = 0; i < c; i++)
1759		    ptr[i] = (unsigned char) tbuf[i];
1760		feobp += c;
1761	    }
1762	}
1763	if (c == 0 || (c < 0 && fixio(SHIN, errno) == -1))
1764	    return (-1);
1765    }
1766#ifndef WINNT
1767    c = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE];
1768    fseekp++;
1769#else
1770    do {
1771	c = fbuf[(int) fseekp / BUFSIZE][(int) fseekp % BUFSIZE];
1772	fseekp++;
1773    } while(c == '\r');
1774#endif /* !WINNT */
1775    return (c);
1776}
1777
1778static void
1779bfree()
1780{
1781    int sb, i;
1782
1783    if (cantell)
1784	return;
1785    if (whyles)
1786	return;
1787    sb = (int) (fseekp - 1) / BUFSIZE;
1788    if (sb > 0) {
1789	for (i = 0; i < sb; i++)
1790	    xfree((ptr_t) fbuf[i]);
1791	(void) blkcpy(fbuf, &fbuf[sb]);
1792	fseekp -= BUFSIZE * sb;
1793	feobp -= BUFSIZE * sb;
1794	fblocks -= sb;
1795    }
1796}
1797
1798void
1799bseek(l)
1800    struct Ain   *l;
1801{
1802    switch (aret = l->type) {
1803    case E_SEEK:
1804	evalvec = l->a_seek;
1805	evalp = l->c_seek;
1806#ifdef DEBUG_SEEK
1807	xprintf(CGETS(16, 4, "seek to eval %x %x\n"), evalvec, evalp);
1808#endif
1809	return;
1810    case A_SEEK:
1811	alvec = l->a_seek;
1812	alvecp = l->c_seek;
1813#ifdef DEBUG_SEEK
1814	xprintf(CGETS(16, 5, "seek to alias %x %x\n"), alvec, alvecp);
1815#endif
1816	return;
1817    case F_SEEK:
1818#ifdef DEBUG_SEEK
1819	xprintf(CGETS(16, 6, "seek to file %x\n"), fseekp);
1820#endif
1821	fseekp = l->f_seek;
1822	return;
1823    default:
1824	xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret);
1825	abort();
1826    }
1827}
1828
1829/* any similarity to bell telephone is purely accidental */
1830void
1831btell(l)
1832struct Ain *l;
1833{
1834    switch (l->type = aret) {
1835    case E_SEEK:
1836	l->a_seek = evalvec;
1837	l->c_seek = evalp;
1838#ifdef DEBUG_SEEK
1839	xprintf(CGETS(16, 8, "tell eval %x %x\n"), evalvec, evalp);
1840#endif
1841	return;
1842    case A_SEEK:
1843	l->a_seek = alvec;
1844	l->c_seek = alvecp;
1845#ifdef DEBUG_SEEK
1846	xprintf(CGETS(16, 9, "tell alias %x %x\n"), alvec, alvecp);
1847#endif
1848	return;
1849    case F_SEEK:
1850	/*SUPPRESS 112*/
1851	l->f_seek = fseekp;
1852	l->a_seek = NULL;
1853#ifdef DEBUG_SEEK
1854	xprintf(CGETS(16, 10, "tell file %x\n"), fseekp);
1855#endif
1856	return;
1857    default:
1858	xprintf(CGETS(16, 7, "Bad seek type %d\n"), aret);
1859	abort();
1860    }
1861}
1862
1863void
1864btoeof()
1865{
1866    (void) lseek(SHIN, (off_t) 0, L_XTND);
1867    aret = F_SEEK;
1868    fseekp = feobp;
1869    alvec = NULL;
1870    alvecp = NULL;
1871    evalvec = NULL;
1872    evalp = NULL;
1873    wfree();
1874    bfree();
1875}
1876
1877void
1878settell()
1879{
1880    off_t x;
1881    cantell = 0;
1882    if (arginp || onelflg || intty)
1883	return;
1884    if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1)
1885	return;
1886    fbuf = (Char **) xcalloc(2, sizeof(Char **));
1887    fblocks = 1;
1888    fbuf[0] = (Char *) xcalloc(BUFSIZE, sizeof(Char));
1889    fseekp = fbobp = feobp = x;
1890    cantell = 1;
1891}
1892