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