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