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