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