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