"Fossies" - the Fresh Open Source Software Archive

Member "tcsh-6.22.03/sh.dol.c" (18 Nov 2020, 25180 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.dol.c": 6.22.02_vs_6.22.03.

    1 /*
    2  * sh.dol.c: Variable substitutions
    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 
   34 /*
   35  * C shell
   36  */
   37 
   38 /*
   39  * These routines perform variable substitution and quoting via ' and ".
   40  * To this point these constructs have been preserved in the divided
   41  * input words.  Here we expand variables and turn quoting via ' and " into
   42  * QUOTE bits on characters (which prevent further interpretation).
   43  * If the `:q' modifier was applied during history expansion, then
   44  * some QUOTEing may have occurred already, so we dont "trim()" here.
   45  */
   46 
   47 static eChar Dpeekc;        /* Peek for DgetC */
   48 static eChar Dpeekrd;       /* Peek for Dreadc */
   49 static Char *Dcp, *const *Dvp;  /* Input vector for Dreadc */
   50 
   51 #define DEOF    CHAR_ERR
   52 
   53 #define unDgetC(c)  Dpeekc = c
   54 
   55 #define QUOTES      (_QF|_QB|_ESC)  /* \ ' " ` */
   56 
   57 /*
   58  * The following variables give the information about the current
   59  * $ expansion, recording the current word position, the remaining
   60  * words within this expansion, the count of remaining words, and the
   61  * information about any : modifier which is being applied.
   62  */
   63 static Char *dolp;      /* Remaining chars from this word */
   64 static Char **dolnxt;       /* Further words */
   65 static int dolcnt;      /* Count of further words */
   66 static struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */
   67 
   68 static int ndolflags;       /* keep track of mod counts for each modifier */
   69 static int *dolmcnts;       /* :gx -> INT_MAX, else 1 */
   70 static int *dolaflags;      /* :ax -> 1, else 0 */
   71 
   72 static  Char     **Dfix2    (Char *const *);
   73 static  int      Dpack      (struct Strbuf *);
   74 static  int  Dword      (struct blk_buf *);
   75 static  void     dolerror   (Char *);
   76 static  eChar    DgetC      (int);
   77 static  void     Dgetdol    (void);
   78 static  void     fixDolMod  (void);
   79 static  void     setDolp    (Char *);
   80 static  void     unDredc    (eChar);
   81 static  eChar    Dredc      (void);
   82 static  void     Dtestq     (Char);
   83 
   84 /*
   85  * Fix up the $ expansions and quotations in the
   86  * argument list to command t.
   87  */
   88 void
   89 Dfix(struct command *t)
   90 {
   91     Char **pp;
   92     Char *p;
   93 
   94     if (noexec)
   95     return;
   96     /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
   97     for (pp = t->t_dcom; (p = *pp++) != NULL;) {
   98     for (; *p; p++) {
   99         if (cmap(*p, _DOL | QUOTES)) {  /* $, \, ', ", ` */
  100         Char **expanded;
  101 
  102         expanded = Dfix2(t->t_dcom);    /* found one */
  103         blkfree(t->t_dcom);
  104         t->t_dcom = expanded;
  105         return;
  106         }
  107     }
  108     }
  109 }
  110 
  111 /*
  112  * $ substitute one word, for i/o redirection
  113  */
  114 Char   *
  115 Dfix1(Char *cp)
  116 {
  117     Char *Dv[2], **expanded;
  118 
  119     if (noexec)
  120     return (0);
  121     Dv[0] = cp;
  122     Dv[1] = NULL;
  123     expanded = Dfix2(Dv);
  124     if (expanded[0] == NULL || expanded[1] != NULL) {
  125     blkfree(expanded);
  126     setname(short2str(cp));
  127     stderror(ERR_NAME | ERR_AMBIG);
  128     }
  129     cp = Strsave(expanded[0]);
  130     blkfree(expanded);
  131     return (cp);
  132 }
  133 
  134 /*
  135  * Subroutine to do actual fixing after state initialization.
  136  */
  137 static Char **
  138 Dfix2(Char *const *v)
  139 {
  140     struct blk_buf *bb = bb_alloc();
  141     Char **vec;
  142 
  143     Dvp = v;
  144     Dcp = STRNULL;      /* Setup input vector for Dreadc */
  145     unDgetC(0);
  146     unDredc(0);         /* Clear out any old peeks (at error) */
  147     dolp = 0;
  148     dolcnt = 0;         /* Clear out residual $ expands (...) */
  149     cleanup_push(bb, bb_free);
  150     while (Dword(bb))
  151     continue;
  152     cleanup_ignore(bb);
  153     cleanup_until(bb);
  154     vec = bb_finish(bb);
  155     xfree(bb);
  156     return vec;
  157 }
  158 
  159 /*
  160  * Pack up more characters in this word
  161  */
  162 static int
  163 Dpack(struct Strbuf *wbuf)
  164 {
  165     eChar c;
  166 
  167     for (;;) {
  168     c = DgetC(DODOL);
  169     if (c == '\\') {
  170         c = DgetC(0);
  171         if (c == DEOF) {
  172         unDredc(c);
  173         return 1;
  174         }
  175         if (c == '\n')
  176         c = ' ';
  177         else
  178         c |= QUOTE;
  179     }
  180     if (c == DEOF) {
  181         unDredc(c);
  182         return 1;
  183     }
  184     if (cmap(c, _SP | _NL | _QF | _QB)) {   /* sp \t\n'"` */
  185         unDgetC(c);
  186         if (cmap(c, QUOTES))
  187         return 0;
  188         return 1;
  189     }
  190     Strbuf_append1(wbuf, (Char) c);
  191     }
  192 }
  193 
  194 /*
  195  * Get a word.  This routine is analogous to the routine
  196  * word() in sh.lex.c for the main lexical input.  One difference
  197  * here is that we don't get a newline to terminate our expansion.
  198  * Rather, DgetC will return a DEOF when we hit the end-of-input.
  199  */
  200 static int
  201 Dword(struct blk_buf *bb)
  202 {
  203     eChar c, c1;
  204     struct Strbuf *wbuf = Strbuf_alloc();
  205     int dolflg;
  206     int    sofar = 0;
  207     Char *str;
  208 
  209     cleanup_push(wbuf, Strbuf_free);
  210     for (;;) {
  211     c = DgetC(DODOL);
  212     switch (c) {
  213 
  214     case DEOF:
  215         if (sofar == 0) {
  216         cleanup_until(wbuf);
  217         return (0);
  218         }
  219         /* finish this word and catch the code above the next time */
  220         unDredc(c);
  221         /*FALLTHROUGH*/
  222 
  223     case '\n':
  224         goto end;
  225 
  226     case ' ':
  227     case '\t':
  228         continue;
  229 
  230     case '`':
  231         /* We preserve ` quotations which are done yet later */
  232         Strbuf_append1(wbuf, (Char) c);
  233         /*FALLTHROUGH*/
  234     case '\'':
  235     case '"':
  236         /*
  237          * Note that DgetC never returns a QUOTES character from an
  238          * expansion, so only true input quotes will get us here or out.
  239          */
  240         c1 = c;
  241         dolflg = c1 == '"' ? DODOL : 0;
  242         for (;;) {
  243         c = DgetC(dolflg);
  244         if (c == c1)
  245             break;
  246         if (c == '\n' || c == DEOF) {
  247             cleanup_until(bb);
  248             stderror(ERR_UNMATCHED, (int)c1);
  249         }
  250         if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
  251             if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\')
  252             wbuf->len--;
  253         }
  254         switch (c1) {
  255 
  256         case '"':
  257             /*
  258              * Leave any `s alone for later. Other chars are all
  259              * quoted, thus `...` can tell it was within "...".
  260              */
  261             Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE);
  262             break;
  263 
  264         case '\'':
  265             /* Prevent all further interpretation */
  266             Strbuf_append1(wbuf, c | QUOTE);
  267             break;
  268 
  269         case '`':
  270             /* Leave all text alone for later */
  271             Strbuf_append1(wbuf, (Char) c);
  272             break;
  273 
  274         default:
  275             break;
  276         }
  277         }
  278         if (c1 == '`')
  279         Strbuf_append1(wbuf, '`');
  280         sofar = 1;
  281         if (Dpack(wbuf) != 0)
  282         goto end;
  283         continue;
  284 
  285     case '\\':
  286         c = DgetC(0);   /* No $ subst! */
  287         if (c == '\n' || c == DEOF)
  288         continue;
  289         c |= QUOTE;
  290         break;
  291 
  292     default:
  293         break;
  294     }
  295     unDgetC(c);
  296     sofar = 1;
  297     if (Dpack(wbuf) != 0)
  298         goto end;
  299     }
  300 
  301  end:
  302     cleanup_ignore(wbuf);
  303     cleanup_until(wbuf);
  304     str = Strbuf_finish(wbuf);
  305     bb_append(bb, str);
  306     xfree(wbuf);
  307     return 1;
  308 }
  309 
  310 
  311 /*
  312  * Get a character, performing $ substitution unless flag is 0.
  313  * Any QUOTES character which is returned from a $ expansion is
  314  * QUOTEd so that it will not be recognized above.
  315  */
  316 static eChar
  317 DgetC(int flag)
  318 {
  319     eChar c;
  320 
  321 top:
  322     if ((c = Dpeekc) != 0) {
  323     Dpeekc = 0;
  324     return (c);
  325     }
  326     if (lap < labuf.len) {
  327     c = labuf.s[lap++] & (QUOTE | TRIM);
  328 quotspec:
  329     if (cmap(c, QUOTES))
  330         return (c | QUOTE);
  331     return (c);
  332     }
  333     if (dolp) {
  334     if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
  335         goto quotspec;
  336     if (dolcnt > 0) {
  337         setDolp(*dolnxt++);
  338         --dolcnt;
  339         return (' ');
  340     }
  341     dolp = 0;
  342     }
  343     if (dolcnt > 0) {
  344     setDolp(*dolnxt++);
  345     --dolcnt;
  346     goto top;
  347     }
  348     c = Dredc();
  349     if (c == '$' && flag) {
  350     Dgetdol();
  351     goto top;
  352     }
  353     return (c);
  354 }
  355 
  356 static Char *nulvec[] = { NULL };
  357 static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE, 
  358                 { NULL, NULL, NULL }, 0 };
  359 
  360 static void
  361 dolerror(Char *s)
  362 {
  363     setname(short2str(s));
  364     stderror(ERR_NAME | ERR_RANGE);
  365 }
  366 
  367 /*
  368  * Handle the multitudinous $ expansion forms.
  369  * Ugh.
  370  */
  371 static void
  372 Dgetdol(void)
  373 {
  374     Char *np;
  375     struct varent *vp = NULL;
  376     struct Strbuf *name = Strbuf_alloc();
  377     eChar   c, sc;
  378     int     subscr = 0, lwb = 1, upb = 0;
  379     int    dimen = 0, bitset = 0, length = 0;
  380     static Char *dolbang = NULL;
  381 
  382     cleanup_push(name, Strbuf_free);
  383     dolmod.len = ndolflags = 0;
  384     c = sc = DgetC(0);
  385     if (c == DEOF) {
  386       stderror(ERR_SYNTAX);
  387       return;
  388     }
  389     if (c == '{')
  390     c = DgetC(0);       /* sc is { to take } later */
  391     if ((c & TRIM) == '#')
  392     dimen++, c = DgetC(0);  /* $# takes dimension */
  393     else if (c == '?')
  394     bitset++, c = DgetC(0); /* $? tests existence */
  395     else if (c == '%')
  396     length++, c = DgetC(0); /* $% returns length in chars */
  397     switch (c) {
  398 
  399     case '!':
  400     if (dimen || bitset || length)
  401         stderror(ERR_SYNTAX);
  402     if (backpid != 0) {
  403         xfree(dolbang);
  404         setDolp(dolbang = putn((tcsh_number_t)backpid));
  405     }
  406     cleanup_until(name);
  407     goto eatbrac;
  408 
  409     case '$':
  410     if (dimen || bitset || length)
  411         stderror(ERR_SYNTAX);
  412     setDolp(doldol);
  413     cleanup_until(name);
  414     goto eatbrac;
  415 
  416     case '<'|QUOTE: {
  417     static struct Strbuf wbuf; /* = Strbuf_INIT; */
  418 
  419     if (bitset)
  420         stderror(ERR_NOTALLOWED, "$?<");
  421     if (dimen)
  422         stderror(ERR_NOTALLOWED, "$#<");
  423     if (length)
  424         stderror(ERR_NOTALLOWED, "$%<");
  425     wbuf.len = 0;
  426     {
  427         char cbuf[MB_LEN_MAX];
  428         size_t cbp = 0;
  429         int old_pintr_disabled;
  430 
  431         for (;;) {
  432             int len;
  433         ssize_t res;
  434         Char wc;
  435 
  436         pintr_push_enable(&old_pintr_disabled);
  437         res = force_read(OLDSTD, cbuf + cbp, 1);
  438         cleanup_until(&old_pintr_disabled);
  439         if (res != 1)
  440             break;
  441         cbp++;
  442         len = normal_mbtowc(&wc, cbuf, cbp);
  443         if (len == -1) {
  444             reset_mbtowc();
  445             if (cbp < MB_LEN_MAX)
  446                 continue; /* Maybe a partial character */
  447             wc = (unsigned char)*cbuf | INVALID_BYTE;
  448         }
  449         if (len <= 0)
  450             len = 1;
  451         if (cbp != (size_t)len)
  452             memmove(cbuf, cbuf + len, cbp - len);
  453         cbp -= len;
  454         if (wc == '\n')
  455             break;
  456         Strbuf_append1(&wbuf, wc);
  457         }
  458         while (cbp != 0) {
  459         int len;
  460         Char wc;
  461 
  462         len = normal_mbtowc(&wc, cbuf, cbp);
  463         if (len == -1) {
  464             reset_mbtowc();
  465             wc = (unsigned char)*cbuf | INVALID_BYTE;
  466         }
  467         if (len <= 0)
  468             len = 1;
  469         if (cbp != (size_t)len)
  470             memmove(cbuf, cbuf + len, cbp - len);
  471         cbp -= len;
  472         if (wc == '\n')
  473             break;
  474         Strbuf_append1(&wbuf, wc);
  475         }
  476         Strbuf_terminate(&wbuf);
  477     }
  478 
  479     fixDolMod();
  480     setDolp(wbuf.s); /* Kept allocated until next $< expansion */
  481     cleanup_until(name);
  482     goto eatbrac;
  483     }
  484 
  485     case '*':
  486     Strbuf_append(name, STRargv);
  487     Strbuf_terminate(name);
  488     vp = adrof(STRargv);
  489     subscr = -1;        /* Prevent eating [...] */
  490     break;
  491 
  492     case DEOF:
  493     case '\n':
  494     np = dimen ? STRargv : (bitset ? STRstatus : NULL);
  495     if (np) {
  496         bitset = 0;
  497         Strbuf_append(name, np);
  498         Strbuf_terminate(name);
  499         vp = adrof(np);
  500         subscr = -1;        /* Prevent eating [...] */
  501         unDredc(c);
  502         break;
  503     }
  504     else
  505         stderror(ERR_SYNTAX);
  506     /*NOTREACHED*/
  507 
  508     default:
  509     if (Isdigit(c)) {
  510         if (dimen)
  511         stderror(ERR_NOTALLOWED, "$#<num>");
  512         subscr = 0;
  513         do {
  514         subscr = subscr * 10 + c - '0';
  515         c = DgetC(0);
  516         } while (c != DEOF && Isdigit(c));
  517         unDredc(c);
  518         if (subscr < 0)
  519         stderror(ERR_RANGE);
  520         if (subscr == 0) {
  521         if (bitset) {
  522             dolp = dolzero ? STR1 : STR0;
  523             cleanup_until(name);
  524             goto eatbrac;
  525         }
  526         if (ffile == 0)
  527             stderror(ERR_DOLZERO);
  528         if (length) {
  529             length = Strlen(ffile);
  530             addla(putn((tcsh_number_t)length));
  531         }
  532         else {
  533             fixDolMod();
  534             setDolp(ffile);
  535         }
  536         cleanup_until(name);
  537         goto eatbrac;
  538         }
  539 #if 0
  540         if (bitset)
  541         stderror(ERR_NOTALLOWED, "$?<num>");
  542         if (length)
  543         stderror(ERR_NOTALLOWED, "$%<num>");
  544 #endif
  545         vp = adrof(STRargv);
  546         if (vp == 0) {
  547         vp = &nulargv;
  548         cleanup_until(name);
  549         goto eatmod;
  550         }
  551         break;
  552     }
  553     if (c == DEOF || !alnum(c)) {
  554         np = dimen ? STRargv : (bitset ? STRstatus : NULL);
  555         if (np) {
  556         bitset = 0;
  557         Strbuf_append(name, np);
  558         Strbuf_terminate(name);
  559         vp = adrof(np);
  560         subscr = -1;        /* Prevent eating [...] */
  561         unDredc(c);
  562         break;
  563         }
  564         else
  565         stderror(ERR_VARALNUM);
  566     }
  567     for (;;) {
  568         Strbuf_append1(name, (Char) c);
  569         c = DgetC(0);
  570         if (c == DEOF || !alnum(c))
  571         break;
  572     }
  573     Strbuf_terminate(name);
  574     unDredc(c);
  575     vp = adrof(name->s);
  576     }
  577     if (bitset) {
  578     dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0;
  579     cleanup_until(name);
  580     goto eatbrac;
  581     }
  582     if (vp == NULL || vp->vec == NULL) {
  583     np = str2short(getenv(short2str(name->s)));
  584     if (np) {
  585         static Char *env_val; /* = NULL; */
  586 
  587         cleanup_until(name);
  588         fixDolMod();
  589         if (length) {
  590             addla(putn((tcsh_number_t)Strlen(np)));
  591         } else {
  592             xfree(env_val);
  593             env_val = Strsave(np);
  594             setDolp(env_val);
  595         }
  596         goto eatbrac;
  597     }
  598     udvar(name->s);
  599     /* NOTREACHED */
  600     }
  601     cleanup_until(name);
  602     c = DgetC(0);
  603     upb = blklen(vp->vec);
  604     if (dimen == 0 && subscr == 0 && c == '[') {
  605     name = Strbuf_alloc();
  606     cleanup_push(name, Strbuf_free);
  607     np = name->s;
  608     for (;;) {
  609         c = DgetC(DODOL);   /* Allow $ expand within [ ] */
  610         if (c == ']')
  611         break;
  612         if (c == '\n' || c == DEOF)
  613         stderror(ERR_INCBR);
  614         Strbuf_append1(name, (Char) c);
  615     }
  616     Strbuf_terminate(name);
  617     np = name->s;
  618     if (dolp || dolcnt) /* $ exp must end before ] */
  619         stderror(ERR_EXPORD);
  620     if (!*np)
  621         stderror(ERR_SYNTAX);
  622     if (Isdigit(*np)) {
  623         int     i;
  624 
  625         for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
  626         continue;
  627         if (i < 0 || (i > upb && !any("-*", *np))) {
  628         cleanup_until(name);
  629         dolerror(vp->v_name);
  630         return;
  631         }
  632         lwb = i;
  633         if (!*np)
  634         upb = lwb, np = STRstar;
  635     }
  636     if (*np == '*')
  637         np++;
  638     else if (*np != '-')
  639         stderror(ERR_MISSING, '-');
  640     else {
  641         int i = upb;
  642 
  643         np++;
  644         if (Isdigit(*np)) {
  645         i = 0;
  646         while (Isdigit(*np))
  647             i = i * 10 + *np++ - '0';
  648         if (i < 0 || i > upb) {
  649             cleanup_until(name);
  650             dolerror(vp->v_name);
  651             return;
  652         }
  653         }
  654         if (i < lwb)
  655         upb = lwb - 1;
  656         else
  657         upb = i;
  658     }
  659     if (lwb == 0) {
  660         if (upb != 0) {
  661         cleanup_until(name);
  662         dolerror(vp->v_name);
  663         return;
  664         }
  665         upb = -1;
  666     }
  667     if (*np)
  668         stderror(ERR_SYNTAX);
  669     cleanup_until(name);
  670     }
  671     else {
  672     if (subscr > 0) {
  673         if (subscr > upb)
  674         lwb = 1, upb = 0;
  675         else
  676         lwb = upb = subscr;
  677     }
  678     unDredc(c);
  679     }
  680     if (dimen) {
  681     /* this is a kludge. It prevents Dgetdol() from */
  682     /* pushing erroneous ${#<error> values into the labuf. */
  683     if (sc == '{') {
  684         c = Dredc();
  685         if (c != '}')
  686         stderror(ERR_MISSING, '}');
  687         unDredc(c);
  688     }
  689     addla(putn((tcsh_number_t)(upb - lwb + 1)));
  690     }
  691     else if (length) {
  692     int i;
  693 
  694     for (i = lwb - 1, length = 0; i < upb; i++)
  695         length += Strlen(vp->vec[i]);
  696 #ifdef notdef
  697     /* We don't want that, since we can always compute it by adding $#xxx */
  698     length += i - 1;    /* Add the number of spaces in */
  699 #endif
  700     addla(putn((tcsh_number_t)length));
  701     }
  702     else {
  703 eatmod:
  704     fixDolMod();
  705     dolnxt = &vp->vec[lwb - 1];
  706     dolcnt = upb - lwb + 1;
  707     }
  708 eatbrac:
  709     if (sc == '{') {
  710     c = Dredc();
  711     if (c != '}')
  712         stderror(ERR_MISSING, '}');
  713     }
  714 }
  715 
  716 static void
  717 fixDolMod(void)
  718 {
  719     eChar c;
  720 
  721     c = DgetC(0);
  722     if (c == ':') {
  723     ndolflags = 0;
  724     do {
  725         ++ndolflags;
  726         dolmcnts = xrealloc(dolmcnts, ndolflags * sizeof(int));
  727         dolaflags = xrealloc(dolaflags, ndolflags * sizeof(int));
  728         c = DgetC(0), dolmcnts[ndolflags - 1] = 1, dolaflags[ndolflags - 1] = 0;
  729         if (c == 'g' || c == 'a') {
  730         if (c == 'g') {
  731             dolmcnts[ndolflags - 1] = INT_MAX;
  732         } else {
  733             dolaflags[ndolflags - 1] = 1;
  734         }
  735         c = DgetC(0);
  736         }
  737         if ((c == 'g' && dolmcnts[ndolflags - 1] != INT_MAX) || 
  738         (c == 'a' && dolaflags[ndolflags - 1] == 0)) {
  739         if (c == 'g') {
  740             dolmcnts[ndolflags - 1] = INT_MAX;
  741         } else {
  742             dolaflags[ndolflags - 1] = 1;
  743         }
  744         c = DgetC(0);
  745         }
  746 
  747         if (c == 's') { /* [eichin:19910926.0755EST] */
  748         int delimcnt = 2;
  749         eChar delim = DgetC(0);
  750         Strbuf_append1(&dolmod, (Char) c);
  751         Strbuf_append1(&dolmod, (Char) delim);
  752 
  753         if (delim == DEOF || !delim || letter(delim)
  754             || Isdigit(delim) || any(" \t\n", delim)) {
  755             seterror(ERR_BADSUBST);
  756             break;
  757         }   
  758         while ((c = DgetC(0)) != DEOF) {
  759             Strbuf_append1(&dolmod, (Char) c);
  760             if(c == delim) delimcnt--;
  761             if(!delimcnt) break;
  762         }
  763         if(delimcnt) {
  764             seterror(ERR_BADSUBST);
  765             break;
  766         }
  767         continue;
  768         }
  769         if (!any(TCSH_MODIFIERS, c))
  770         stderror(ERR_BADMOD, (int)c);
  771         Strbuf_append1(&dolmod, (Char) c);
  772         if (c == 'q') {
  773         dolmcnts[ndolflags - 1] = INT_MAX;
  774         }
  775     }
  776     while ((c = DgetC(0)) == ':');
  777     unDredc(c);
  778     }
  779     else
  780     unDredc(c);
  781 }
  782 
  783 static int
  784 all_dolmcnts_are_0()
  785 {
  786     int i = 0;
  787     for(; i < ndolflags; ++i) {
  788     if(dolmcnts[i] != 0)
  789         return 0;
  790     }
  791     return 1;
  792 }
  793 
  794 static void
  795 setDolp(Char *cp)
  796 {
  797     Char *dp;
  798     size_t i;
  799     int nthMod = 0;
  800 
  801     if (dolmod.len == 0 || all_dolmcnts_are_0()) {
  802     dolp = cp;
  803     return;
  804     }
  805     cp = Strsave(cp);
  806     for (i = 0; i < dolmod.len; i++) {
  807     int didmod = 0;
  808 
  809     /* handle s// [eichin:19910926.0510EST] */
  810     if(dolmod.s[i] == 's') {
  811         Char delim;
  812         Char *lhsub, *rhsub, *np;
  813         size_t lhlen = 0, rhlen = 0;
  814         /* keep track of where the last :a match hit */
  815         ptrdiff_t last_match = 0;
  816 
  817         delim = dolmod.s[++i];
  818         if (!delim || letter(delim)
  819         || Isdigit(delim) || any(" \t\n", delim)) {
  820         seterror(ERR_BADSUBST);
  821         break;
  822         }
  823         lhsub = &dolmod.s[++i];
  824         while(dolmod.s[i] != delim && dolmod.s[++i]) {
  825         lhlen++;
  826         }
  827         dolmod.s[i] = 0;
  828         rhsub = &dolmod.s[++i];
  829         while(dolmod.s[i] != delim && dolmod.s[++i]) {
  830         rhlen++;
  831         }
  832         dolmod.s[i] = 0;
  833 
  834         strip(lhsub);
  835         strip(rhsub);
  836         if(dolmcnts[nthMod] != 0) {
  837             strip(cp);
  838             dp = cp;
  839             do {
  840                 dp = Strstr(dp + last_match, lhsub);
  841                 if (dp) {
  842                     ptrdiff_t diff = dp - cp;
  843                     size_t len = (Strlen(cp) + 1 - lhlen + rhlen);
  844                     np = xmalloc(len * sizeof(Char));
  845                     (void) Strncpy(np, cp, diff);
  846                     (void) Strcpy(np + diff, rhsub);
  847                     (void) Strcpy(np + diff + rhlen, dp + lhlen);
  848             last_match = diff + rhlen;
  849 
  850                     xfree(cp);
  851                     dp = cp = np;
  852                     cp[--len] = '\0';
  853                     didmod = 1;
  854                     if (diff >= (ssize_t)len)
  855                     break;
  856                 } else {
  857                     /* should this do a seterror? */
  858                     break;
  859                 }
  860             }
  861             while (dolaflags[nthMod] != 0);
  862             }
  863         /*
  864          * restore dolmod for additional words
  865          */
  866         dolmod.s[i] = rhsub[-1] = (Char) delim;
  867     } else if(dolmcnts[nthMod] != 0) {
  868 
  869         do {
  870         if ((dp = domod(cp, dolmod.s[i])) != NULL) {
  871             didmod = 1;
  872             if (Strcmp(cp, dp) == 0) {
  873             xfree(cp);
  874             cp = dp;
  875             break;
  876             }
  877             else {
  878             xfree(cp);
  879             cp = dp;
  880             }
  881         }
  882         else
  883             break;
  884         }
  885         while (dolaflags[nthMod] != 0);
  886     }
  887     if(didmod && dolmcnts[nthMod] != INT_MAX)
  888         dolmcnts[nthMod]--;
  889 #ifdef notdef
  890     else
  891         break;
  892 #endif
  893 
  894     ++nthMod;
  895     }
  896 
  897     addla(cp);
  898 
  899     dolp = STRNULL;
  900     if (seterr)
  901     stderror(ERR_OLD);
  902 }
  903 
  904 static void
  905 unDredc(eChar c)
  906 {
  907 
  908     Dpeekrd = c;
  909 }
  910 
  911 static eChar
  912 Dredc(void)
  913 {
  914     eChar c;
  915 
  916     if ((c = Dpeekrd) != 0) {
  917     Dpeekrd = 0;
  918     return (c);
  919     }
  920     if (Dcp && (c = *Dcp++))
  921     return (c & (QUOTE | TRIM));
  922     if (*Dvp == 0) {
  923     Dcp = 0;
  924     return (DEOF);
  925     }
  926     Dcp = *Dvp++;
  927     return (' ');
  928 }
  929 
  930 static int gflag;
  931 
  932 static void
  933 Dtestq(Char c)
  934 {
  935 
  936     if (cmap(c, QUOTES))
  937     gflag = 1;
  938 }
  939 
  940 static void
  941 inheredoc_cleanup(void *dummy)
  942 {
  943     USE(dummy);
  944     inheredoc = 0;
  945 }
  946 
  947 Char *
  948 randsuf(void) {
  949 #ifndef WINNT_NATIVE
  950     struct timeval tv;
  951     (void) gettimeofday(&tv, NULL);
  952     return putn((((tcsh_number_t)tv.tv_sec) ^ 
  953         ((tcsh_number_t)tv.tv_usec) ^
  954         ((tcsh_number_t)getpid())) & 0x00ffffff);
  955 #else
  956     return putn(getpid());
  957 #endif
  958 }
  959 
  960 /*
  961  * Form a shell temporary file (in unit 0) from the words
  962  * of the shell input up to EOF or a line the same as "term".
  963  * Unit 0 should have been closed before this call.
  964  */
  965 void
  966 heredoc(Char *term)
  967 {
  968     eChar  c;
  969     Char   *Dv[2];
  970     struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
  971     Char    obuf[BUFSIZE + 1];
  972 #define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
  973     Char *lbp, *obp, *mbp;
  974     Char  **vp;
  975     int    quoted;
  976 #ifdef HAVE_MKSTEMP
  977     char   *tmp = short2str(shtemp);
  978     char   *dot = strrchr(tmp, '.');
  979 
  980     if (!dot)
  981     stderror(ERR_NAME | ERR_NOMATCH);
  982     strcpy(dot, TMP_TEMPLATE);
  983 
  984     xclose(0);
  985     if (mkstemp(tmp) == -1)
  986     stderror(ERR_SYSTEM, tmp, strerror(errno));
  987 #else /* !HAVE_MKSTEMP */
  988     char   *tmp;
  989 # ifndef WINNT_NATIVE
  990 
  991 again:
  992 # endif /* WINNT_NATIVE */
  993     tmp = short2str(shtemp);
  994 # if O_CREAT == 0
  995     if (xcreat(tmp, 0600) < 0)
  996     stderror(ERR_SYSTEM, tmp, strerror(errno));
  997 # endif
  998     xclose(0);
  999     if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
 1000     -1) {
 1001     int oerrno = errno;
 1002 # ifndef WINNT_NATIVE
 1003     if (errno == EEXIST) {
 1004         if (unlink(tmp) == -1) {
 1005         xfree(shtemp);
 1006         mbp = randsuf();
 1007         shtemp = Strspl(STRtmpsh, mbp);
 1008         xfree(mbp);
 1009         }
 1010         goto again;
 1011     }
 1012 # endif /* WINNT_NATIVE */
 1013     (void) unlink(tmp);
 1014     errno = oerrno;
 1015     stderror(ERR_SYSTEM, tmp, strerror(errno));
 1016     }
 1017 #endif /* HAVE_MKSTEMP */
 1018     (void) unlink(tmp);     /* 0 0 inode! */
 1019     Dv[0] = term;
 1020     Dv[1] = NULL;
 1021     gflag = 0;
 1022     trim(Dv);
 1023     rscan(Dv, Dtestq);
 1024     quoted = gflag;
 1025     obp = obuf;
 1026     obuf[BUFSIZE] = 0;
 1027     inheredoc = 1;
 1028     cleanup_push(&inheredoc, inheredoc_cleanup);
 1029 #ifdef WINNT_NATIVE
 1030     __dup_stdin = 1;
 1031 #endif /* WINNT_NATIVE */
 1032     cleanup_push(&lbuf, Strbuf_cleanup);
 1033     cleanup_push(&mbuf, Strbuf_cleanup);
 1034     for (;;) {
 1035     Char **words;
 1036 
 1037     /*
 1038      * Read up a line
 1039      */
 1040     lbuf.len = 0;
 1041     for (;;) {
 1042         c = readc(1);   /* 1 -> Want EOF returns */
 1043         if (c == CHAR_ERR || c == '\n')
 1044         break;
 1045         if ((c &= TRIM) != 0)
 1046         Strbuf_append1(&lbuf, (Char) c);
 1047     }
 1048     Strbuf_terminate(&lbuf);
 1049 
 1050     /* Catch EOF in the middle of a line. */
 1051     if (c == CHAR_ERR && lbuf.len != 0)
 1052         c = '\n';
 1053 
 1054     /*
 1055      * Check for EOF or compare to terminator -- before expansion
 1056      */
 1057     if (c == CHAR_ERR || eq(lbuf.s, term))
 1058         break;
 1059 
 1060     /*
 1061      * If term was quoted or -n just pass it on
 1062      */
 1063     if (quoted || noexec) {
 1064         Strbuf_append1(&lbuf, '\n');
 1065         Strbuf_terminate(&lbuf);
 1066         for (lbp = lbuf.s; (c = *lbp++) != 0;) {
 1067         *obp++ = (Char) c;
 1068         if (obp == OBUF_END) {
 1069             tmp = short2str(obuf);
 1070             (void) xwrite(0, tmp, strlen (tmp));
 1071             obp = obuf;
 1072         }
 1073         }
 1074         continue;
 1075     }
 1076 
 1077     /*
 1078      * Term wasn't quoted so variable and then command expand the input
 1079      * line
 1080      */
 1081     Dcp = lbuf.s;
 1082     Dvp = Dv + 1;
 1083     mbuf.len = 0;
 1084     for (;;) {
 1085         c = DgetC(DODOL);
 1086         if (c == DEOF)
 1087         break;
 1088         if ((c &= TRIM) == 0)
 1089         continue;
 1090         /* \ quotes \ $ ` here */
 1091         if (c == '\\') {
 1092         c = DgetC(0);
 1093         if (!any("$\\`", c))
 1094             unDgetC(c | QUOTE), c = '\\';
 1095         else
 1096             c |= QUOTE;
 1097         }
 1098         Strbuf_append1(&mbuf, (Char) c);
 1099     }
 1100     Strbuf_terminate(&mbuf);
 1101 
 1102     /*
 1103      * If any ` in line do command substitution
 1104      */
 1105     mbp = mbuf.s;
 1106     if (Strchr(mbp, '`') != NULL) {
 1107         /*
 1108          * 1 arg to dobackp causes substitution to be literal. Words are
 1109          * broken only at newlines so that all blanks and tabs are
 1110          * preserved.  Blank lines (null words) are not discarded.
 1111          */
 1112         words = dobackp(mbp, 1);
 1113     }
 1114     else
 1115         /* Setup trivial vector similar to return of dobackp */
 1116         Dv[0] = mbp, Dv[1] = NULL, words = Dv;
 1117 
 1118     /*
 1119      * Resurrect the words from the command substitution each separated by
 1120      * a newline.  Note that the last newline of a command substitution
 1121      * will have been discarded, but we put a newline after the last word
 1122      * because this represents the newline after the last input line!
 1123      */
 1124     for (vp= words; *vp; vp++) {
 1125         for (mbp = *vp; *mbp; mbp++) {
 1126         *obp++ = *mbp & TRIM;
 1127         if (obp == OBUF_END) {
 1128             tmp = short2str(obuf);
 1129             (void) xwrite(0, tmp, strlen (tmp));
 1130             obp = obuf;
 1131         }
 1132         }
 1133         *obp++ = '\n';
 1134         if (obp == OBUF_END) {
 1135             tmp = short2str(obuf);
 1136         (void) xwrite(0, tmp, strlen (tmp));
 1137         obp = obuf;
 1138         }
 1139     }
 1140     if (words != Dv)
 1141         blkfree(words);
 1142     }
 1143     *obp = 0;
 1144     tmp = short2str(obuf);
 1145     (void) xwrite(0, tmp, strlen (tmp));
 1146     (void) lseek(0, (off_t) 0, L_SET);
 1147     cleanup_until(&inheredoc);
 1148 }