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