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