"Fossies" - the Fresh Open Source Software Archive

Member "tcsh-6.22.03/sh.lex.c" (18 Nov 2020, 38426 Bytes) of package /linux/misc/tcsh-6.22.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "sh.lex.c": 6.22.02_vs_6.22.03.

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