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