1 /* $NetBSD: dol.c,v 1.31 2019/01/05 16:54:00 christos Exp $ */
2 
3 /*-
4  * Copyright (c) 1980, 1991, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)dol.c       8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: dol.c,v 1.31 2019/01/05 16:54:00 christos Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/types.h>
42 
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdarg.h>
46 #include <stddef.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include "csh.h"
52 #include "extern.h"
53 
54 /*
55  * These routines perform variable substitution and quoting via ' and ".
56  * To this point these constructs have been preserved in the divided
57  * input words.  Here we expand variables and turn quoting via ' and " into
58  * QUOTE bits on characters (which prevent further interpretation).
59  * If the `:q' modifier was applied during history expansion, then
60  * some QUOTEing may have occurred already, so we dont "trim()" here.
61  */
62 
63 static int Dpeekc, Dpeekrd;   /* Peeks for DgetC and Dreadc */
64 static Char *Dcp, **Dvp;      /* Input vector for Dreadc */
65 
66 #define   DEOF -1
67 #define   unDgetC(c) Dpeekc = c
68 #define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */
69 
70 /*
71  * The following variables give the information about the current
72  * $ expansion, recording the current word position, the remaining
73  * words within this expansion, the count of remaining words, and the
74  * information about any : modifier which is being applied.
75  */
76 #define MAXWLEN (BUFSIZE - 4)
77 #define MAXMOD MAXWLEN                  /* This cannot overflow       */
78 static Char dolmod[MAXMOD];   /* : modifier character */
79 static Char *dolp;            /* Remaining chars from this word */
80 static Char **dolnxt;                   /* Further words */
81 static int dolcnt;            /* Count of further words */
82 static int dolnmod;           /* Number of modifiers */
83 static int dolmcnt;           /* :gx -> 10000, else 1 */
84 static int dolwcnt;           /* :wx -> 10000, else 1 */
85 
86 static void Dfix2(Char **);
87 static Char *Dpack(Char *, Char *);
88 static int Dword(void);
89 __dead static void dolerror(Char *);
90 static int DgetC(int);
91 static void Dgetdol(void);
92 static void fixDolMod(void);
93 static void setDolp(Char *);
94 static void unDredc(int);
95 static int Dredc(void);
96 static void Dtestq(int);
97 
98 
99 /*
100  * Fix up the $ expansions and quotations in the
101  * argument list to command t.
102  */
103 void
Dfix(struct command * t)104 Dfix(struct command *t)
105 {
106     Char *p, **pp;
107 
108     if (noexec)
109           return;
110     /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
111     for (pp = t->t_dcom; (p = *pp++) != NULL;)
112           for (; *p; p++) {
113               if (cmap(*p, _DOL | QUOTES)) {      /* $, \, ', ", ` */
114                     Dfix2(t->t_dcom);   /* found one */
115                     blkfree(t->t_dcom);
116                     t->t_dcom = gargv;
117                     gargv = 0;
118                     return;
119               }
120           }
121 }
122 
123 /*
124  * $ substitute one word, for i/o redirection
125  */
126 Char *
Dfix1(Char * cp)127 Dfix1(Char *cp)
128 {
129     Char *Dv[2];
130 
131     if (noexec)
132           return (0);
133     Dv[0] = cp;
134     Dv[1] = NULL;
135     Dfix2(Dv);
136     if (gargc != 1) {
137           setname(vis_str(cp));
138           stderror(ERR_NAME | ERR_AMBIG);
139     }
140     cp = Strsave(gargv[0]);
141     blkfree(gargv), gargv = 0;
142     return (cp);
143 }
144 
145 /*
146  * Subroutine to do actual fixing after state initialization.
147  */
148 static void
Dfix2(Char ** v)149 Dfix2(Char **v)
150 {
151     ginit();                            /* Initialize glob's area pointers */
152     Dvp = v;
153     Dcp = STRNULL;            /* Setup input vector for Dreadc */
154     unDgetC(0);
155     unDredc(0);                         /* Clear out any old peeks (at error) */
156     dolp = 0;
157     dolcnt = 0;                         /* Clear out residual $ expands (...) */
158     while (Dword())
159           continue;
160 }
161 
162 /*
163  * Pack up more characters in this word
164  */
165 static Char *
Dpack(Char * wbuf,Char * wp)166 Dpack(Char *wbuf, Char *wp)
167 {
168     int c;
169     ptrdiff_t i;
170 
171     i = MAXWLEN - (wp - wbuf);
172     for (;;) {
173           c = DgetC(DODOL);
174           if (c == '\\') {
175               c = DgetC(0);
176               if (c == DEOF) {
177                     unDredc(c);
178                     *wp = 0;
179                     Gcat(STRNULL, wbuf);
180                     return (NULL);
181               }
182               if (c == '\n')
183                     c = ' ';
184               else
185                     c |= QUOTE;
186           }
187           if (c == DEOF) {
188               unDredc(c);
189               *wp = 0;
190               Gcat(STRNULL, wbuf);
191               return (NULL);
192           }
193           if (cmap(c, _SP | _NL | _QF | _QB)) {   /* sp \t\n'"` */
194               unDgetC(c);
195               if (cmap(c, QUOTES))
196                     return (wp);
197               *wp++ = 0;
198               Gcat(STRNULL, wbuf);
199               return (NULL);
200           }
201           if (--i <= 0)
202               stderror(ERR_WTOOLONG);
203           *wp++ = (Char)c;
204     }
205 }
206 
207 /*
208  * Get a word.  This routine is analogous to the routine
209  * word() in sh.lex.c for the main lexical input.  One difference
210  * here is that we don't get a newline to terminate our expansion.
211  * Rather, DgetC will return a DEOF when we hit the end-of-input.
212  */
213 static int
Dword(void)214 Dword(void)
215 {
216     Char wbuf[BUFSIZE], *wp;
217     int c, c1;
218     ptrdiff_t i;
219     int dolflg, done, sofar;
220 
221     done = 0;
222     i = MAXWLEN;
223     sofar = 0;
224     wp = wbuf;
225 
226     while (!done) {
227           done = 1;
228           c = DgetC(DODOL);
229           switch (c) {
230           case DEOF:
231               if (sofar == 0)
232                     return (0);
233               /* finish this word and catch the code above the next time */
234               unDredc(c);
235               /* FALLTHROUGH */
236           case '\n':
237               *wp = 0;
238               Gcat(STRNULL, wbuf);
239               return (1);
240           case ' ':
241           case '\t':
242               done = 0;
243               break;
244           case '`':
245               /* We preserve ` quotations which are done yet later */
246               *wp++ = (Char)c, --i;
247               /* FALLTHROUGH */
248           case '\'':
249           case '"':
250               /*
251                * Note that DgetC never returns a QUOTES character from an
252                * expansion, so only true input quotes will get us here or out.
253                */
254               c1 = c;
255               dolflg = c1 == '"' ? DODOL : 0;
256               for (;;) {
257                     c = DgetC(dolflg);
258                     if (c == c1)
259                         break;
260                     if (c == '\n' || c == DEOF)
261                         stderror(ERR_UNMATCHED, c1);
262                     if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE))
263                         --wp, ++i;
264                     if (--i <= 0)
265                         stderror(ERR_WTOOLONG);
266                     switch (c1) {
267                     case '"':
268                         /*
269                          * Leave any `s alone for later. Other chars are all
270                          * quoted, thus `...` can tell it was within "...".
271                          */
272                         *wp++ = (Char)(c == '`' ? '`' : (c | QUOTE));
273                         break;
274                     case '\'':
275                         /* Prevent all further interpretation */
276                         *wp++ = (Char)(c | QUOTE);
277                         break;
278                     case '`':
279                         /* Leave all text alone for later */
280                         *wp++ = (Char)c;
281                         break;
282                     default:
283                         break;
284                     }
285               }
286               if (c1 == '`')
287                     *wp++ = '`' /* i--; eliminated */;
288               sofar = 1;
289               if ((wp = Dpack(wbuf, wp)) == NULL)
290                     return (1);
291               else {
292                     i = MAXWLEN - (wp - wbuf);
293                     done = 0;
294               }
295               break;
296           case '\\':
297               c = DgetC(0);   /* No $ subst! */
298               if (c == '\n' || c == DEOF) {
299                     done = 0;
300                     break;
301               }
302               c |= QUOTE;
303               break;
304           default:
305               break;
306           }
307           if (done) {
308               unDgetC(c);
309               sofar = 1;
310               if ((wp = Dpack(wbuf, wp)) == NULL)
311                     return (1);
312               else {
313                     i = MAXWLEN - (wp - wbuf);
314                     done = 0;
315               }
316           }
317     }
318     /* Really NOTREACHED */
319     return (0);
320 }
321 
322 
323 /*
324  * Get a character, performing $ substitution unless flag is 0.
325  * Any QUOTES character which is returned from a $ expansion is
326  * QUOTEd so that it will not be recognized above.
327  */
328 static int
DgetC(int flag)329 DgetC(int flag)
330 {
331     int c;
332 top:
333     if ((c = Dpeekc) != '\0') {
334           Dpeekc = 0;
335           return (c);
336     }
337     if (lap) {
338           c = *lap++ & (QUOTE | TRIM);
339           if (c == 0) {
340               lap = 0;
341               goto top;
342           }
343 quotspec:
344           if (cmap(c, QUOTES))
345               return (c | QUOTE);
346           return (c);
347     }
348     if (dolp) {
349           if ((c = *dolp++ & (QUOTE | TRIM)) != '\0')
350               goto quotspec;
351           if (dolcnt > 0) {
352               setDolp(*dolnxt++);
353               --dolcnt;
354               return (' ');
355           }
356           dolp = 0;
357     }
358     if (dolcnt > 0) {
359           setDolp(*dolnxt++);
360           --dolcnt;
361           goto top;
362     }
363     c = Dredc();
364     if (c == '$' && flag) {
365           Dgetdol();
366           goto top;
367     }
368     return (c);
369 }
370 
371 static Char *nulvec[] = {0};
372 static struct varent nulargv = {nulvec, STRargv, { NULL, NULL, NULL }, 0};
373 
374 static void
dolerror(Char * s)375 dolerror(Char *s)
376 {
377     setname(vis_str(s));
378     stderror(ERR_NAME | ERR_RANGE);
379     /* NOTREACHED */
380 }
381 
382 /*
383  * Handle the multitudinous $ expansion forms.
384  * Ugh.
385  */
386 static void
Dgetdol(void)387 Dgetdol(void)
388 {
389     static Char *dolbang = NULL;
390     Char name[4*MAXVARLEN+1];
391     Char wbuf[BUFSIZE];
392     struct varent *vp;
393     Char *np;
394     int c, lwb, sc, subscr, upb;
395     int dimen, bitset;
396     char tnp;
397 
398     bitset = 0;
399     dimen = 0;
400     lwb = 1;
401     upb = 0;
402     subscr = 0;
403     vp = NULL;
404 
405     dolnmod = dolmcnt = dolwcnt = 0;
406     c = sc = DgetC(0);
407     if (c == '{')
408           c = DgetC(0);                 /* sc is { to take } later */
409     if ((c & TRIM) == '#')
410           dimen++, c = DgetC(0);        /* $# takes dimension */
411     else if (c == '?')
412           bitset++, c = DgetC(0);       /* $? tests existence */
413     switch (c) {
414     case '!':
415           if (dimen || bitset)
416               stderror(ERR_SYNTAX);
417           if (backpid != 0) {
418               if (dolbang)
419                     free(dolbang);
420               setDolp(dolbang = putn(backpid));
421           }
422           goto eatbrac;
423     case '$':
424           if (dimen || bitset)
425               stderror(ERR_SYNTAX);
426           setDolp(doldol);
427           goto eatbrac;
428     case '<' | QUOTE:
429           if (bitset)
430               stderror(ERR_NOTALLOWED, "$?<");
431           if (dimen)
432               stderror(ERR_NOTALLOWED, "$?#");
433           for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) {
434               *np = (unsigned char)tnp;
435               if (np >= &wbuf[BUFSIZE - 1])
436                     stderror(ERR_LTOOLONG);
437               if (tnp == '\n')
438                     break;
439           }
440           *np = 0;
441           /*
442            * KLUDGE: dolmod is set here because it will cause setDolp to call
443            * domod and thus to copy wbuf. Otherwise setDolp would use it
444            * directly. If we saved it ourselves, no one would know when to free
445            * it. The actual function of the 'q' causes filename expansion not to
446            * be done on the interpolated value.
447            */
448           dolmod[dolnmod++] = 'q';
449           dolmcnt = 10000;
450           setDolp(wbuf);
451           goto eatbrac;
452     case DEOF:
453     case '\n':
454           stderror(ERR_SYNTAX);
455           /* NOTREACHED */
456     case '*':
457           (void) Strcpy(name, STRargv);
458           vp = adrof(STRargv);
459           subscr = -1;                  /* Prevent eating [...] */
460           break;
461     default:
462           np = name;
463           if (Isdigit(c)) {
464               if (dimen)
465                     stderror(ERR_NOTALLOWED, "$#<num>");
466               subscr = 0;
467               do {
468                     subscr = subscr * 10 + c - '0';
469                     c = DgetC(0);
470               } while (Isdigit(c));
471               unDredc(c);
472               if (subscr < 0)
473                     stderror(ERR_RANGE);
474               if (subscr == 0) {
475                     if (bitset) {
476                         dolp = ffile ? STR1 : STR0;
477                         goto eatbrac;
478                     }
479                     if (ffile == 0)
480                         stderror(ERR_DOLZERO);
481                     fixDolMod();
482                     setDolp(ffile);
483                     goto eatbrac;
484               }
485               if (bitset)
486                     stderror(ERR_DOLQUEST);
487               vp = adrof(STRargv);
488               if (vp == 0) {
489                     vp = &nulargv;
490                     goto eatmod;
491               }
492               break;
493           }
494           if (!alnum(c))
495               stderror(ERR_VARALNUM);
496           for (;;) {
497               *np++ = (Char)c;
498               c = DgetC(0);
499               if (!alnum(c))
500                     break;
501               if (np >= &name[MAXVARLEN])
502                     stderror(ERR_VARTOOLONG);
503           }
504           *np++ = 0;
505           unDredc(c);
506           vp = adrof(name);
507     }
508     if (bitset) {
509           dolp = (vp || getenv(short2str(name))) ? STR1 : STR0;
510           goto eatbrac;
511     }
512     if (vp == 0) {
513           np = str2short(getenv(short2str(name)));
514           if (np) {
515               fixDolMod();
516               setDolp(np);
517               goto eatbrac;
518           }
519           udvar(name);
520     }
521     c = DgetC(0);
522     upb = blklen(vp->vec);
523     if (dimen == 0 && subscr == 0 && c == '[') {
524           np = name;
525           for (;;) {
526               c = DgetC(DODOL);         /* Allow $ expand within [ ] */
527               if (c == ']')
528                     break;
529               if (c == '\n' || c == DEOF)
530                     stderror(ERR_INCBR);
531               if (np >= &name[sizeof(name) / sizeof(Char) - 2])
532                     stderror(ERR_VARTOOLONG);
533               *np++ = (Char)c;
534           }
535           *np = 0, np = name;
536           if (dolp || dolcnt) /* $ exp must end before ] */
537               stderror(ERR_EXPORD);
538           if (!*np)
539               stderror(ERR_SYNTAX);
540           if (Isdigit(*np)) {
541               int     i;
542 
543               for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
544                     continue;
545               if ((i < 0 || i > upb) && !any("-*", *np)) {
546                     dolerror(vp->v_name);
547                     return;
548               }
549               lwb = i;
550               if (!*np)
551                     upb = lwb, np = STRstar;
552           }
553           if (*np == '*')
554               np++;
555           else if (*np != '-')
556               stderror(ERR_MISSING, '-');
557           else {
558               int i = upb;
559 
560               np++;
561               if (Isdigit(*np)) {
562                     i = 0;
563                     while (Isdigit(*np))
564                         i = i * 10 + *np++ - '0';
565                     if (i < 0 || i > upb) {
566                         dolerror(vp->v_name);
567                         return;
568                     }
569               }
570               if (i < lwb)
571                     upb = lwb - 1;
572               else
573                     upb = i;
574           }
575           if (lwb == 0) {
576               if (upb != 0) {
577                     dolerror(vp->v_name);
578                     return;
579               }
580               upb = -1;
581           }
582           if (*np)
583               stderror(ERR_SYNTAX);
584     }
585     else {
586           if (subscr > 0) {
587               if (subscr > upb)
588                     lwb = 1, upb = 0;
589               else
590                     lwb = upb = subscr;
591           }
592           unDredc(c);
593     }
594     if (dimen) {
595           Char   *cp = putn(upb - lwb + 1);
596 
597           addla(cp);
598           free(cp);
599     }
600     else {
601 eatmod:
602           fixDolMod();
603           dolnxt = &vp->vec[lwb - 1];
604           dolcnt = upb - lwb + 1;
605     }
606 eatbrac:
607     if (sc == '{') {
608           c = Dredc();
609           if (c != '}')
610               stderror(ERR_MISSING, '}');
611     }
612 }
613 
614 static void
fixDolMod(void)615 fixDolMod(void)
616 {
617     int c;
618 
619     c = DgetC(0);
620     if (c == ':') {
621           do {
622               c = DgetC(0), dolmcnt = 1, dolwcnt = 1;
623               if (c == 'g' || c == 'a') {
624                     if (c == 'g')
625                         dolmcnt = 10000;
626                     else
627                         dolwcnt = 10000;
628                     c = DgetC(0);
629               }
630               if ((c == 'g' && dolmcnt != 10000) ||
631                     (c == 'a' && dolwcnt != 10000)) {
632                     if (c == 'g')
633                         dolmcnt = 10000;
634                     else
635                         dolwcnt = 10000;
636                     c = DgetC(0);
637               }
638 
639               if (c == 's') { /* [eichin:19910926.0755EST] */
640                     int delimcnt = 2;
641                     int delim = DgetC(0);
642                     dolmod[dolnmod++] = (Char)c;
643                     dolmod[dolnmod++] = (Char)delim;
644 
645                     if (!delim || letter(delim)
646                         || Isdigit(delim) || any(" \t\n", delim)) {
647                         seterror(ERR_BADSUBST);
648                         break;
649                     }
650                     while ((c = DgetC(0)) != (-1)) {
651                         dolmod[dolnmod++] = (Char)c;
652                         if(c == delim) delimcnt--;
653                         if(!delimcnt) break;
654                     }
655                     if(delimcnt) {
656                         seterror(ERR_BADSUBST);
657                         break;
658                     }
659                     continue;
660               }
661               if (!any("htrqxes", c))
662                     stderror(ERR_BADMOD, c);
663               dolmod[dolnmod++] = (Char)c;
664               if (c == 'q')
665                     dolmcnt = 10000;
666           }
667           while ((c = DgetC(0)) == ':');
668           unDredc(c);
669     }
670     else
671           unDredc(c);
672 }
673 
674 static void
setDolp(Char * cp)675 setDolp(Char *cp)
676 {
677     Char *dp;
678     int i;
679 
680     if (dolnmod == 0 || dolmcnt == 0) {
681           dolp = cp;
682           return;
683     }
684     dp = cp = Strsave(cp);
685     for (i = 0; i < dolnmod; i++) {
686           /* handle s// [eichin:19910926.0510EST] */
687           if(dolmod[i] == 's') {
688               int delim;
689               Char *lhsub, *rhsub, *np;
690               size_t lhlen = 0, rhlen = 0;
691               int didmod = 0;
692 
693               delim = dolmod[++i];
694               if (!delim || letter(delim)
695                     || Isdigit(delim) || any(" \t\n", delim)) {
696                     seterror(ERR_BADSUBST);
697                     break;
698               }
699               lhsub = &dolmod[++i];
700               while(dolmod[i] != delim && dolmod[++i]) {
701                     lhlen++;
702               }
703               dolmod[i] = 0;
704               rhsub = &dolmod[++i];
705               while(dolmod[i] != delim && dolmod[++i]) {
706                     rhlen++;
707               }
708               dolmod[i] = 0;
709 
710               do {
711                     dp = Strstr(cp, lhsub);
712                     if (dp) {
713                         np = xmalloc(
714                             (size_t)((Strlen(cp) + 1 - lhlen + rhlen) *
715                             sizeof(*np)));
716                         (void)Strncpy(np, cp, (size_t)(dp - cp));
717                         (void)Strcpy(np + (dp - cp), rhsub);
718                         (void)Strcpy(np + (dp - cp) + rhlen, dp + lhlen);
719 
720                         free(cp);
721                         dp = cp = np;
722                         didmod = 1;
723                     } else {
724                         /* should this do a seterror? */
725                         break;
726                     }
727               }
728               while (dolwcnt == 10000);
729               /*
730                * restore dolmod for additional words
731                */
732               dolmod[i] = rhsub[-1] = (Char)delim;
733               if (didmod)
734                     dolmcnt--;
735               else
736                     break;
737         } else {
738               int didmod = 0;
739 
740               do {
741                     if ((dp = domod(cp, dolmod[i]))) {
742                         didmod = 1;
743                         if (Strcmp(cp, dp) == 0) {
744                               free(cp);
745                               cp = dp;
746                               break;
747                         }
748                         else {
749                               free(cp);
750                               cp = dp;
751                         }
752                     }
753                     else
754                         break;
755               }
756               while (dolwcnt == 10000);
757               dp = cp;
758               if (didmod)
759                     dolmcnt--;
760               else
761                     break;
762           }
763     }
764 
765     if (dp) {
766           addla(dp);
767           free(dp);
768     }
769     else {
770           addla(cp);
771           free(cp);
772     }
773 
774     dolp = STRNULL;
775     if (seterr)
776           stderror(ERR_OLD);
777 }
778 
779 static void
unDredc(int c)780 unDredc(int c)
781 {
782     Dpeekrd = c;
783 }
784 
785 static int
Dredc(void)786 Dredc(void)
787 {
788     int c;
789 
790     if ((c = Dpeekrd) != '\0') {
791           Dpeekrd = 0;
792           return (c);
793     }
794     if (Dcp && (c = *Dcp++))
795           return (c & (QUOTE | TRIM));
796     if (*Dvp == 0) {
797           Dcp = 0;
798           return (DEOF);
799     }
800     Dcp = *Dvp++;
801     return (' ');
802 }
803 
804 static void
Dtestq(int c)805 Dtestq(int c)
806 {
807     if (cmap(c, QUOTES))
808           gflag = 1;
809 }
810 
811 /*
812  * Form a shell temporary file (in unit 0) from the words
813  * of the shell input up to EOF or a line the same as "term".
814  * Unit 0 should have been closed before this call.
815  */
816 void
817 /*ARGSUSED*/
heredoc(Char * term)818 heredoc(Char *term)
819 {
820     Char obuf[BUFSIZE], lbuf[BUFSIZE], mbuf[BUFSIZE];
821     struct timespec tv;
822     Char *Dv[2], *lbp, *obp, *mbp, **vp;
823     char *tmp;
824     int c, ocnt, lcnt, mcnt;
825     int quoted;
826 
827 again:
828     tmp = short2str(shtemp);
829     if (open(tmp, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600) < 0) {
830           if (errno == EEXIST) {
831               if (unlink(tmp) == -1) {
832                     (void)clock_gettime(CLOCK_MONOTONIC, &tv);
833                     mbp = putn((((int)tv.tv_sec) ^
834                         ((int)tv.tv_nsec) ^ ((int)getpid())) & 0x00ffffff);
835                     shtemp = Strspl(STRtmpsh, mbp);
836                     free(mbp);
837               }
838               goto again;
839           }
840           stderror(ERR_SYSTEM, tmp, strerror(errno));
841     }
842     (void)unlink(tmp);                  /* 0 0 inode! */
843     Dv[0] = term;
844     Dv[1] = NULL;
845     gflag = 0;
846     trim(Dv);
847     rscan(Dv, Dtestq);
848     quoted = gflag;
849     ocnt = BUFSIZE;
850     obp = obuf;
851     for (;;) {
852           /*
853            * Read up a line
854            */
855           lbp = lbuf;
856           lcnt = BUFSIZE - 4;
857           for (;;) {
858               c = readc(1);   /* 1 -> Want EOF returns */
859               if (c < 0 || c == '\n')
860                     break;
861               if ((c &= TRIM) != '\0') {
862                     *lbp++ = (Char)c;
863                     if (--lcnt < 0) {
864                         setname("<<");
865                         stderror(ERR_NAME | ERR_OVERFLOW);
866                     }
867               }
868           }
869           *lbp = 0;
870 
871           /*
872            * Check for EOF or compare to terminator -- before expansion
873            */
874           if (c < 0 || eq(lbuf, term)) {
875               (void)write(0, short2str(obuf), (size_t)(BUFSIZE - ocnt));
876               (void)lseek(0, (off_t)0, SEEK_SET);
877               return;
878           }
879 
880           /*
881            * If term was quoted or -n just pass it on
882            */
883           if (quoted || noexec) {
884               *lbp++ = '\n';
885               *lbp = 0;
886               for (lbp = lbuf; (c = *lbp++) != '\0';) {
887                     *obp++ = (Char)c;
888                     if (--ocnt == 0) {
889                         (void) write(0, short2str(obuf), BUFSIZE);
890                         obp = obuf;
891                         ocnt = BUFSIZE;
892                     }
893               }
894               continue;
895           }
896 
897           /*
898            * Term wasn't quoted so variable and then command expand the input
899            * line
900            */
901           Dcp = lbuf;
902           Dvp = Dv + 1;
903           mbp = mbuf;
904           mcnt = BUFSIZE - 4;
905           for (;;) {
906               c = DgetC(DODOL);
907               if (c == DEOF)
908                     break;
909               if ((c &= TRIM) == 0)
910                     continue;
911               /* \ quotes \ $ ` here */
912               if (c == '\\') {
913                     c = DgetC(0);
914                     if (!any("$\\`", c))
915                         unDgetC(c | QUOTE), c = '\\';
916                     else
917                         c |= QUOTE;
918               }
919               *mbp++ = (Char)c;
920               if (--mcnt == 0) {
921                     setname("<<");
922                     stderror(ERR_NAME | ERR_OVERFLOW);
923               }
924           }
925           *mbp++ = 0;
926 
927           /*
928            * If any ` in line do command substitution
929            */
930           mbp = mbuf;
931           if (any(short2str(mbp), '`')) {
932               /*
933                * 1 arg to dobackp causes substitution to be literal. Words are
934                * broken only at newlines so that all blanks and tabs are
935                * preserved.  Blank lines (null words) are not discarded.
936                */
937               vp = dobackp(mbuf, 1);
938           }
939           else
940               /* Setup trivial vector similar to return of dobackp */
941               Dv[0] = mbp, Dv[1] = NULL, vp = Dv;
942 
943           /*
944            * Resurrect the words from the command substitution each separated by
945            * a newline.  Note that the last newline of a command substitution
946            * will have been discarded, but we put a newline after the last word
947            * because this represents the newline after the last input line!
948            */
949           for (; *vp; vp++) {
950               for (mbp = *vp; *mbp; mbp++) {
951                     *obp++ = *mbp & TRIM;
952                     if (--ocnt == 0) {
953                         (void)write(0, short2str(obuf), BUFSIZE);
954                         obp = obuf;
955                         ocnt = BUFSIZE;
956                     }
957               }
958               *obp++ = '\n';
959               if (--ocnt == 0) {
960                     (void)write(0, short2str(obuf), BUFSIZE);
961                     obp = obuf;
962                     ocnt = BUFSIZE;
963               }
964           }
965           if (pargv)
966               blkfree(pargv), pargv = 0;
967     }
968 }
969