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