sh.lex.c revision 59243
167754Smsmith/* $Header: /src/pub/tcsh/sh.lex.c,v 3.49 1998/04/08 13:58:54 christos Exp $ */
267754Smsmith/*
377424Smsmith * sh.lex.c: Lexical analysis into tokens
467754Smsmith */
567754Smsmith/*-
667754Smsmith * Copyright (c) 1980, 1991 The Regents of the University of California.
7217365Sjkim * All rights reserved.
8306536Sjkim *
970243Smsmith * Redistribution and use in source and binary forms, with or without
1067754Smsmith * modification, are permitted provided that the following conditions
11217365Sjkim * are met:
12217365Sjkim * 1. Redistributions of source code must retain the above copyright
13217365Sjkim *    notice, this list of conditions and the following disclaimer.
14217365Sjkim * 2. Redistributions in binary form must reproduce the above copyright
15217365Sjkim *    notice, this list of conditions and the following disclaimer in the
16217365Sjkim *    documentation and/or other materials provided with the distribution.
17217365Sjkim * 3. All advertising materials mentioning features or use of this software
18217365Sjkim *    must display the following acknowledgement:
19217365Sjkim *	This product includes software developed by the University of
20217365Sjkim *	California, Berkeley and its contributors.
21217365Sjkim * 4. Neither the name of the University nor the names of its contributors
22217365Sjkim *    may be used to endorse or promote products derived from this software
23217365Sjkim *    without specific prior written permission.
24217365Sjkim *
2567754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28217365Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2967754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35217365Sjkim * SUCH DAMAGE.
36217365Sjkim */
37217365Sjkim#include "sh.h"
38217365Sjkim
39217365SjkimRCSID("$Id: sh.lex.c,v 3.49 1998/04/08 13:58:54 christos Exp $")
40217365Sjkim
41217365Sjkim#include "ed.h"
42217365Sjkim/* #define DEBUG_INP */
4367754Smsmith/* #define DEBUG_SEEK */
44193341Sjkim
45193341Sjkim/*
46193341Sjkim * C shell
4767754Smsmith */
4877424Smsmith
4991116Smsmith/*
5067754Smsmith * These lexical routines read input and form lists of words.
5167754Smsmith * There is some involved processing here, because of the complications
5267754Smsmith * of input buffering, and especially because of history substitution.
5367754Smsmith */
5477424Smsmithstatic	Char		*word		__P((void));
5567754Smsmithstatic	int	 	 getC1		__P((int));
56151937Sjkimstatic	void	 	 getdol		__P((void));
57151937Sjkimstatic	void	 	 getexcl	__P((int));
5867754Smsmithstatic	struct Hist 	*findev		__P((Char *, bool));
5967754Smsmithstatic	void	 	 setexclp	__P((Char *));
6067754Smsmithstatic	int	 	 bgetc		__P((void));
6167754Smsmithstatic	void		 balloc		__P((int));
62241973Sjkimstatic	void	 	 bfree		__P((void));
63167802Sjkimstatic	struct wordent	*gethent	__P((int));
6467754Smsmithstatic	int	 	 matchs		__P((Char *, Char *));
6567754Smsmithstatic	int	 	 getsel		__P((int *, int *, int));
6667754Smsmithstatic	struct wordent	*getsub		__P((struct wordent *));
6767754Smsmithstatic	Char 		*subword	__P((Char *, int, bool *));
6877424Smsmithstatic	struct wordent	*dosub		__P((int, struct wordent *, bool));
69167802Sjkim
70107325Siwasaki/*
7167754Smsmith * Peekc is a peek character for getC, peekread for readc.
7267754Smsmith * There is a subtlety here in many places... history routines
7367754Smsmith * will read ahead and then insert stuff into the input stream.
7467754Smsmith * If they push back a character then they must push it behind
75167802Sjkim * the text substituted by the history substitution.  On the other
7667754Smsmith * hand in several places we need 2 peek characters.  To make this
7783174Smsmith * all work, the history routines read with getC, and make use both
78167802Sjkim * of ungetC and unreadc.  The key observation is that the state
7967754Smsmith * of getC at the call of a history reference is such that calls
8067754Smsmith * to getC from the history routines will always yield calls of
8167754Smsmith * readc, unless this peeking is involved.  That is to say that during
8267754Smsmith * getexcl the variables lap, exclp, and exclnxt are all zero.
8367754Smsmith *
8467754Smsmith * Getdol invokes history substitution, hence the extra peek, peekd,
8567754Smsmith * which it can ungetD to be before history substitutions.
8667754Smsmith */
8767754Smsmithstatic Char peekc = 0, peekd = 0;
88281075Sdimstatic Char peekread = 0;
8967754Smsmith
9067754Smsmith/* (Tail of) current word from ! subst */
91151937Sjkimstatic Char *exclp = NULL;
92151937Sjkim
9380062Smsmith/* The rest of the ! subst words */
9477424Smsmithstatic struct wordent *exclnxt = NULL;
9567754Smsmith
9667754Smsmith/* Count of remaining words in ! subst */
97306536Sjkimstatic int exclc = 0;
98167802Sjkim
9967754Smsmith/* "Globp" for alias resubstitution */
100167802Sjkimint aret = F_SEEK;
101167802Sjkim
102167802Sjkim/*
103167802Sjkim * Labuf implements a general buffer for lookahead during lexical operations.
104167802Sjkim * Text which is to be placed in the input stream can be stuck here.
105167802Sjkim * We stick parsed ahead $ constructs during initial input,
106167802Sjkim * process id's from `$$', and modified variable values (from qualifiers
107167802Sjkim * during expansion in sh.dol.c) here.
108167802Sjkim */
109167802Sjkimstatic Char labuf[BUFSIZE];
110167802Sjkim
111167802Sjkim/*
112167802Sjkim * Lex returns to its caller not only a wordlist (as a "var" parameter)
113167802Sjkim * but also whether a history substitution occurred.  This is used in
114241973Sjkim * the main (process) routine to determine whether to echo, and also
115167802Sjkim * when called by the alias routine to determine whether to keep the
116167802Sjkim * argument list.
117167802Sjkim */
118167802Sjkimstatic bool hadhist = 0;
119167802Sjkim
120167802Sjkim/*
121167802Sjkim * Avoid alias expansion recursion via \!#
122167802Sjkim */
123167802Sjkimint     hleft;
124167802Sjkim
125167802SjkimChar    histline[BUFSIZE + 2];	/* last line input */
126167802Sjkim
127167802Sjkim /* The +2 is to fool hp's optimizer */
128167802Sjkimbool    histvalid = 0;		/* is histline valid */
129167802Sjkimstatic Char *histlinep = NULL;	/* current pointer into histline */
130167802Sjkim
131167802Sjkimstatic Char getCtmp;
132167802Sjkim
133167802Sjkim#define getC(f)		(((getCtmp = peekc) != '\0') ? (peekc = 0, getCtmp) : getC1(f))
13467754Smsmith#define	ungetC(c)	peekc = (Char) c
13567754Smsmith#define	ungetD(c)	peekd = (Char) c
136167802Sjkim
137167802Sjkim/* Use Htime to store timestamps picked up from history file for enthist()
138167802Sjkim * if reading saved history (sg)
139167802Sjkim */
140281075Sdimtime_t Htime = (time_t)0;
141167802Sjkimstatic time_t a2time_t __P((Char *));
142167802Sjkim
143167802Sjkim/*
144167802Sjkim * for history event processing
145167802Sjkim * in the command 'echo !?foo?:1 !$' we want the !$ to expand from the line
146167802Sjkim * 'foo' was found instead of the last command
147167802Sjkim */
148167802Sjkimstatic int uselastevent = 1;
149281075Sdim
150167802Sjkimint
151167802Sjkimlex(hp)
15267754Smsmith    struct wordent *hp;
15367754Smsmith{
15467754Smsmith    struct wordent *wdp;
15567754Smsmith    int     c;
15667754Smsmith
15767754Smsmith
15877424Smsmith    uselastevent = 1;
15967754Smsmith    histvalid = 0;
160151937Sjkim    histlinep = histline;
161151937Sjkim    *histlinep = '\0';
16267754Smsmith
16391116Smsmith    btell(&lineloc);
16467754Smsmith    hp->next = hp->prev = hp;
16567754Smsmith    hp->word = STRNULL;
166123315Snjl    hadhist = 0;
167123315Snjl    do
168241973Sjkim	c = readc(0);
169123315Snjl    while (c == ' ' || c == '\t');
17067754Smsmith    if (c == HISTSUB && intty)
17167754Smsmith	/* ^lef^rit	from tty is short !:s^lef^rit */
17267754Smsmith	getexcl(c);
17391116Smsmith    else
17477424Smsmith	unreadc(c);
17567754Smsmith    wdp = hp;
17667754Smsmith    /*
17791116Smsmith     * The following loop is written so that the links needed by freelex will
17867754Smsmith     * be ready and rarin to go even if it is interrupted.
17983174Smsmith     */
18091116Smsmith    do {
18191116Smsmith	struct wordent *new;
18291116Smsmith
183123315Snjl	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
18467754Smsmith	new->word = STRNULL;
185123315Snjl	new->prev = wdp;
186123315Snjl	new->next = hp;
187123315Snjl	wdp->next = new;
188123315Snjl	hp->prev = new;
189123315Snjl	wdp = new;
190123315Snjl	wdp->word = word();
191306536Sjkim    } while (wdp->word[0] != '\n');
192306536Sjkim    if (histlinep < histline + BUFSIZE) {
193123315Snjl	*histlinep = '\0';
19467754Smsmith	if (histlinep > histline && histlinep[-1] == '\n')
19567754Smsmith	    histlinep[-1] = '\0';
19667754Smsmith	histvalid = 1;
197117521Snjl    }
19867754Smsmith    else {
19991116Smsmith	histline[BUFSIZE - 1] = '\0';
20091116Smsmith    }
20167754Smsmith
20267754Smsmith    return (hadhist);
20367754Smsmith}
20467754Smsmith
20567754Smsmithstatic time_t
206207344Sjkima2time_t(word)
20767754Smsmith    Char *word;
208207344Sjkim{
209151937Sjkim    /* Attempt to distinguish timestamps from other possible entries.
21067754Smsmith     * Format: "+NNNNNNNNNN" (10 digits, left padded with ascii '0') */
21167754Smsmith
21267754Smsmith    time_t ret;
213207344Sjkim    Char *s;
21467754Smsmith    int ct;
21567754Smsmith
21667754Smsmith    if (!word || *(s = word) != '+')
21791116Smsmith	return (time_t)0;
218207344Sjkim
219202771Sjkim    for (++s, ret = 0, ct = 0; *s; ++s, ++ct)
22067754Smsmith    {
22191116Smsmith	if (!isdigit((unsigned char)*s))
22283174Smsmith	    return (time_t)0;
22391116Smsmith	ret = ret * 10 + (time_t)((unsigned char)*s - '0');
22467754Smsmith    }
22567754Smsmith
226281075Sdim    if (ct != 10)
22767754Smsmith	return (time_t)0;
228209746Sjkim
229209746Sjkim    return ret;
230209746Sjkim}
231209746Sjkim
232209746Sjkimvoid
233209746Sjkimprlex(sp0)
234209746Sjkim    struct wordent *sp0;
235209746Sjkim{
236209746Sjkim    struct wordent *sp = sp0->next;
237138287Smarks
23867754Smsmith    for (;;) {
23967754Smsmith	xprintf("%S", sp->word);
24067754Smsmith	sp = sp->next;
241281075Sdim	if (sp == sp0)
242167802Sjkim	    break;
24367754Smsmith	if (sp->word[0] != '\n')
24467754Smsmith	    xputchar(' ');
24567754Smsmith    }
24667754Smsmith}
24767754Smsmith
24877424Smsmithvoid
24967754Smsmithcopylex(hp, fp)
250151937Sjkim    struct wordent *hp;
25167754Smsmith    struct wordent *fp;
252151937Sjkim{
25367754Smsmith    struct wordent *wdp;
25467754Smsmith
25567754Smsmith    wdp = hp;
25667754Smsmith    fp = fp->next;
25767754Smsmith    do {
25867754Smsmith	struct wordent *new;
25967754Smsmith
26077424Smsmith	new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
26167754Smsmith	new->word = STRNULL;
26267754Smsmith	new->prev = wdp;
26367754Smsmith	new->next = hp;
26467754Smsmith	wdp->next = new;
26567754Smsmith	hp->prev = new;
266167802Sjkim	wdp = new;
26767754Smsmith	wdp->word = Strsave(fp->word);
26867754Smsmith	fp = fp->next;
26967754Smsmith    } while (wdp->word[0] != '\n');
27067754Smsmith}
271167802Sjkim
27267754Smsmithvoid
27367754Smsmithfreelex(vp)
27467754Smsmith    struct wordent *vp;
27567754Smsmith{
27667754Smsmith    struct wordent *fp;
27767754Smsmith
27867754Smsmith    while (vp->next != vp) {
27967754Smsmith	fp = vp->next;
28077424Smsmith	vp->next = fp->next;
28167754Smsmith	if (fp->word != STRNULL)
282151937Sjkim	    xfree((ptr_t) fp->word);
283151937Sjkim	xfree((ptr_t) fp);
28467754Smsmith    }
28567754Smsmith    vp->prev = vp;
28667754Smsmith}
28767754Smsmith
288241973Sjkimstatic Char *
28967754Smsmithword()
29067754Smsmith{
29167754Smsmith    Char c, c1;
29267754Smsmith    Char *wp;
29367754Smsmith    Char    wbuf[BUFSIZE];
29477424Smsmith    Char    hbuf[12];
29567754Smsmith    int	    h;
29667754Smsmith    bool dolflg;
29767754Smsmith    int i;
29867754Smsmith
29967754Smsmith    wp = wbuf;
30067754Smsmith    i = BUFSIZE - 4;
301167802Sjkimloop:
30267754Smsmith    while ((c = getC(DOALL)) == ' ' || c == '\t')
30367754Smsmith	continue;
30467754Smsmith    if (cmap(c, _META | _ESC))
30567754Smsmith	switch (c) {
306167802Sjkim	case '&':
307306536Sjkim	case '|':
30867754Smsmith	case '<':
30967754Smsmith	case '>':
31067754Smsmith	    *wp++ = c;
31167754Smsmith	    c1 = getC(DOALL);
31267754Smsmith	    if (c1 == c)
31367754Smsmith		*wp++ = c1;
31467754Smsmith	    else
31567754Smsmith		ungetC(c1);
31677424Smsmith	    goto ret;
31767754Smsmith
318151937Sjkim	case '#':
31967754Smsmith	    if (intty)
32067754Smsmith		break;
32167754Smsmith	    c = 0;
32277424Smsmith	    h = 0;
32367754Smsmith	    do {
32467754Smsmith		c1 = c;
32567754Smsmith		c = getC(0);
32667754Smsmith		if (h < 12)
32777424Smsmith		    hbuf[h++] = c;
32867754Smsmith	    } while (c != '\n');
32967754Smsmith	    hbuf[11] = '\0';
33067754Smsmith	    Htime = a2time_t(hbuf);
331167802Sjkim	    if (c1 == '\\')
33267754Smsmith		goto loop;
33367754Smsmith	    /*FALLTHROUGH*/
33491116Smsmith
33583174Smsmith	case ';':
33683174Smsmith	case '(':
33767754Smsmith	case ')':
33867754Smsmith	case '\n':
33967754Smsmith	    *wp++ = c;
34067754Smsmith	    goto ret;
34167754Smsmith
34267754Smsmith	case '\\':
34367754Smsmith	    c = getC(0);
344167802Sjkim	    if (c == '\n') {
345167802Sjkim		if (onelflg == 1)
34667754Smsmith		    onelflg = 2;
34767754Smsmith		goto loop;
34867754Smsmith	    }
34967754Smsmith	    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