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