Merge tcsh 6.22.03-ceccc7f
[freebsd.git] / contrib / tcsh / sh.dol.c
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 }