xref: /dragonfly/contrib/tcsh-6/sh.dol.c (revision 84d884bf08edef6c02f15218458cd5df8010b654)
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
Dfix(struct command * t)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   *
Dfix1(Char * cp)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 **
Dfix2(Char * const * v)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
Dpack(struct Strbuf * wbuf)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
Dword(struct blk_buf * bb)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
DgetC(int flag)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
dolerror(Char * s)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
Dgetdol(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
fixDolMod(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
all_dolmcnts_are_0(void)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
setDolp(Char * cp)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
unDredc(eChar c)945 unDredc(eChar c)
946 {
947 
948     Dpeekrd = c;
949 }
950 
951 static eChar
Dredc(void)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
Dtestq(Char c)973 Dtestq(Char c)
974 {
975 
976     if (cmap(c, QUOTES))
977           gflag = 1;
978 }
979 
980 static void
inheredoc_cleanup(void * dummy)981 inheredoc_cleanup(void *dummy)
982 {
983     USE(dummy);
984     inheredoc = 0;
985 }
986 
987 Char *
randsuf(void)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
heredoc(Char * term)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 }
1189