xref: /openbsd/bin/csh/dol.c (revision 5b133f3f)
1 /*	$OpenBSD: dol.c,v 1.27 2023/03/08 04:43:04 guenther 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
Dfix(struct command * t)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   *
Dfix1(Char * cp)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
Dfix2(Char ** v)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 *
Dpack(Char * wbuf,Char * wp)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
Dword(void)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
DgetC(int flag)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
dolerror(Char * s)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
Dgetdol(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 	    free(dolbang);
412 	    setDolp(dolbang = putn(backpid));
413 	}
414 	goto eatbrac;
415 
416     case '$':
417 	if (dimen || bitset)
418 	    stderror(ERR_SYNTAX);
419 	setDolp(doldol);
420 	goto eatbrac;
421 
422     case '<' | QUOTE:
423 	if (bitset)
424 	    stderror(ERR_NOTALLOWED, "$?<");
425 	if (dimen)
426 	    stderror(ERR_NOTALLOWED, "$?#");
427 	for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) {
428 	    *np = (unsigned char) tnp;
429 	    if (np >= &wbuf[BUFSIZ - 1])
430 		stderror(ERR_LTOOLONG);
431 	    if (tnp == '\n')
432 		break;
433 	}
434 	*np = 0;
435 	/*
436 	 * KLUDGE: dolmod is set here because it will cause setDolp to call
437 	 * domod and thus to copy wbuf. Otherwise setDolp would use it
438 	 * directly. If we saved it ourselves, no one would know when to free
439 	 * it. The actual function of the 'q' causes filename expansion not to
440 	 * be done on the interpolated value.
441 	 */
442 	dolmod[dolnmod++] = 'q';
443 	dolmcnt = 10000;
444 	setDolp(wbuf);
445 	goto eatbrac;
446 
447     case DEOF:
448     case '\n':
449 	stderror(ERR_SYNTAX);
450 	/* NOTREACHED */
451 	break;
452 
453     case '*':
454 	(void) Strlcpy(name, STRargv, sizeof name/sizeof(Char));
455 	vp = adrof(STRargv);
456 	subscr = -1;		/* Prevent eating [...] */
457 	break;
458 
459     default:
460 	np = name;
461 	if (Isdigit(c)) {
462 	    if (dimen)
463 		stderror(ERR_NOTALLOWED, "$#<num>");
464 	    subscr = 0;
465 	    do {
466 		subscr = subscr * 10 + c - '0';
467 		c = DgetC(0);
468 	    } while (Isdigit(c));
469 	    unDredc(c);
470 	    if (subscr < 0)
471 		stderror(ERR_RANGE);
472 	    if (subscr == 0) {
473 		if (bitset) {
474 		    dolp = ffile ? STR1 : STR0;
475 		    goto eatbrac;
476 		}
477 		if (ffile == 0)
478 		    stderror(ERR_DOLZERO);
479 		fixDolMod();
480 		setDolp(ffile);
481 		goto eatbrac;
482 	    }
483 	    if (bitset)
484 		stderror(ERR_DOLQUEST);
485 	    vp = adrof(STRargv);
486 	    if (vp == 0) {
487 		vp = &nulargv;
488 		goto eatmod;
489 	    }
490 	    break;
491 	}
492 	if (!alnum(c))
493 	    stderror(ERR_VARALNUM);
494 	for (;;) {
495 	    *np++ = c;
496 	    c = DgetC(0);
497 	    if (!alnum(c))
498 		break;
499 	    if (np >= &name[MAXVARLEN])
500 		stderror(ERR_VARTOOLONG);
501 	}
502 	*np++ = 0;
503 	unDredc(c);
504 	vp = adrof(name);
505     }
506     if (bitset) {
507 	dolp = (vp || getenv(short2str(name))) ? STR1 : STR0;
508 	goto eatbrac;
509     }
510     if (vp == 0) {
511 	np = str2short(getenv(short2str(name)));
512 	if (np) {
513 	    fixDolMod();
514 	    setDolp(np);
515 	    goto eatbrac;
516 	}
517 	udvar(name);
518 	/* NOTREACHED */
519     }
520     c = DgetC(0);
521     upb = blklen(vp->vec);
522     if (dimen == 0 && subscr == 0 && c == '[') {
523 	np = name;
524 	for (;;) {
525 	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
526 	    if (c == ']')
527 		break;
528 	    if (c == '\n' || c == DEOF)
529 		stderror(ERR_INCBR);
530 	    if (np >= &name[sizeof(name) / sizeof(Char) - 2])
531 		stderror(ERR_VARTOOLONG);
532 	    *np++ = c;
533 	}
534 	*np = 0, np = name;
535 	if (dolp || dolcnt)	/* $ exp must end before ] */
536 	    stderror(ERR_EXPORD);
537 	if (!*np)
538 	    stderror(ERR_SYNTAX);
539 	if (Isdigit(*np)) {
540 	    int     i;
541 
542 	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
543 		continue;
544 	    if ((i < 0 || i > upb) && !any("-*", *np)) {
545 		dolerror(vp->v_name);
546 		return;
547 	    }
548 	    lwb = i;
549 	    if (!*np)
550 		upb = lwb, np = STRstar;
551 	}
552 	if (*np == '*')
553 	    np++;
554 	else if (*np != '-')
555 	    stderror(ERR_MISSING, '-');
556 	else {
557 	    int i = upb;
558 
559 	    np++;
560 	    if (Isdigit(*np)) {
561 		i = 0;
562 		while (Isdigit(*np))
563 		    i = i * 10 + *np++ - '0';
564 		if (i < 0 || i > upb) {
565 		    dolerror(vp->v_name);
566 		    return;
567 		}
568 	    }
569 	    if (i < lwb)
570 		upb = lwb - 1;
571 	    else
572 		upb = i;
573 	}
574 	if (lwb == 0) {
575 	    if (upb != 0) {
576 		dolerror(vp->v_name);
577 		return;
578 	    }
579 	    upb = -1;
580 	}
581 	if (*np)
582 	    stderror(ERR_SYNTAX);
583     }
584     else {
585 	if (subscr > 0) {
586 	    if (subscr > upb)
587 		lwb = 1, upb = 0;
588 	    else
589 		lwb = upb = subscr;
590 	}
591 	unDredc(c);
592     }
593     if (dimen) {
594 	Char   *cp = putn(upb - lwb + 1);
595 
596 	addla(cp);
597 	free(cp);
598     }
599     else {
600 eatmod:
601 	fixDolMod();
602 	dolnxt = &vp->vec[lwb - 1];
603 	dolcnt = upb - lwb + 1;
604     }
605 eatbrac:
606     if (sc == '{') {
607 	c = Dredc();
608 	if (c != '}')
609 	    stderror(ERR_MISSING, '}');
610     }
611 }
612 
613 static void
fixDolMod(void)614 fixDolMod(void)
615 {
616     int c;
617 
618     c = DgetC(0);
619     if (c == ':') {
620 	do {
621 	    c = DgetC(0), dolmcnt = 1, dolwcnt = 1;
622 	    if (c == 'g' || c == 'a') {
623 		if (c == 'g')
624 		    dolmcnt = 10000;
625 		else
626 		    dolwcnt = 10000;
627 		c = DgetC(0);
628 	    }
629 	    if ((c == 'g' && dolmcnt != 10000) ||
630 		(c == 'a' && dolwcnt != 10000)) {
631 		if (c == 'g')
632 		    dolmcnt = 10000;
633 		else
634 		    dolwcnt = 10000;
635 		c = DgetC(0);
636 	    }
637 
638 	    if (c == 's') {	/* [eichin:19910926.0755EST] */
639 		int delimcnt = 2;
640 		int delim = DgetC(0);
641 		dolmod[dolnmod++] = c;
642 		dolmod[dolnmod++] = delim;
643 
644 		if (!delim || letter(delim)
645 		    || Isdigit(delim) || any(" \t\n", delim)) {
646 		    seterror(ERR_BADSUBST);
647 		    break;
648 		}
649 		while ((c = DgetC(0)) != (-1)) {
650 		    dolmod[dolnmod++] = c;
651 		    if(c == delim) delimcnt--;
652 		    if(!delimcnt) break;
653 		}
654 		if(delimcnt) {
655 		    seterror(ERR_BADSUBST);
656 		    break;
657 		}
658 		continue;
659 	    }
660 	    if (!any("htrqxes", c))
661 		stderror(ERR_BADMOD, c);
662 	    dolmod[dolnmod++] = c;
663 	    if (c == 'q')
664 		dolmcnt = 10000;
665 	}
666 	while ((c = DgetC(0)) == ':');
667 	unDredc(c);
668     }
669     else
670 	unDredc(c);
671 }
672 
673 static void
setDolp(Char * cp)674 setDolp(Char *cp)
675 {
676     Char *dp;
677     int i;
678 
679     if (dolnmod == 0 || dolmcnt == 0) {
680 	dolp = cp;
681 	return;
682     }
683     dp = cp = Strsave(cp);
684     for (i = 0; i < dolnmod; i++) {
685 	/* handle s// [eichin:19910926.0510EST] */
686 	if(dolmod[i] == 's') {
687 	    int delim;
688 	    Char *lhsub, *rhsub, *np;
689 	    size_t lhlen = 0, rhlen = 0;
690 	    int didmod = 0;
691 
692 	    delim = dolmod[++i];
693 	    if (!delim || letter(delim)
694 		|| Isdigit(delim) || any(" \t\n", delim)) {
695 		seterror(ERR_BADSUBST);
696 		break;
697 	    }
698 	    lhsub = &dolmod[++i];
699 	    while(dolmod[i] != delim && dolmod[++i]) {
700 		lhlen++;
701 	    }
702 	    dolmod[i] = 0;
703 	    rhsub = &dolmod[++i];
704 	    while(dolmod[i] != delim && dolmod[++i]) {
705 		rhlen++;
706 	    }
707 	    dolmod[i] = 0;
708 
709 	    do {
710 		dp = Strstr(cp, lhsub);
711 		if (dp) {
712 		    size_t len = Strlen(cp) + 1 - lhlen + rhlen;
713 
714 		    np = xreallocarray(NULL, len, sizeof(Char));
715 		    *dp = 0;
716 		    (void) Strlcpy(np, cp, len);
717 		    (void) Strlcat(np, rhsub, len);
718 		    (void) Strlcat(np, dp + lhlen, len);
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] = 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     addla(cp);
766     free(cp);
767 
768     dolp = STRNULL;
769     if (seterr)
770 	stderror(ERR_OLD);
771 }
772 
773 static void
unDredc(int c)774 unDredc(int c)
775 {
776 
777     Dpeekrd = c;
778 }
779 
780 static int
Dredc(void)781 Dredc(void)
782 {
783     int c;
784 
785     if ((c = Dpeekrd) != '\0') {
786 	Dpeekrd = 0;
787 	return (c);
788     }
789     if (Dcp && (c = *Dcp++))
790 	return (c & (QUOTE | TRIM));
791     if (*Dvp == 0) {
792 	Dcp = 0;
793 	return (DEOF);
794     }
795     Dcp = *Dvp++;
796     return (' ');
797 }
798 
799 static void
Dtestq(int c)800 Dtestq(int c)
801 {
802 
803     if (cmap(c, QUOTES))
804 	gflag = 1;
805 }
806 
807 /*
808  * Form a shell temporary file (in unit 0) from the words
809  * of the shell input up to EOF or a line the same as "term".
810  * Unit 0 should have been closed before this call.
811  */
812 void
heredoc(Char * term)813 heredoc(Char *term)
814 {
815     int c;
816     Char   *Dv[2];
817     Char    obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ];
818     int     ocnt, lcnt, mcnt;
819     Char *lbp, *obp, *mbp;
820     Char  **vp;
821     bool    quoted;
822     char   tmp[] = "/tmp/sh.XXXXXXXX";
823 
824     if (mkstemp(tmp) == -1)
825 	stderror(ERR_SYSTEM, tmp, strerror(errno));
826     (void) unlink(tmp);		/* 0 0 inode! */
827     Dv[0] = term;
828     Dv[1] = NULL;
829     gflag = 0;
830     trim(Dv);
831     rscan(Dv, Dtestq);
832     quoted = gflag;
833     ocnt = BUFSIZ;
834     obp = obuf;
835     for (;;) {
836 	/*
837 	 * Read up a line
838 	 */
839 	lbp = lbuf;
840 	lcnt = BUFSIZ - 4;
841 	for (;;) {
842 	    c = readc(1);	/* 1 -> Want EOF returns */
843 	    if (c < 0 || c == '\n')
844 		break;
845 	    if ((c &= TRIM) != '\0') {
846 		*lbp++ = c;
847 		if (--lcnt < 0) {
848 		    setname("<<");
849 		    stderror(ERR_NAME | ERR_OVERFLOW);
850 		}
851 	    }
852 	}
853 	*lbp = 0;
854 
855 	/*
856 	 * Check for EOF or compare to terminator -- before expansion
857 	 */
858 	if (c < 0 || eq(lbuf, term)) {
859 	    (void) write(STDIN_FILENO, short2str(obuf),
860 	        (size_t) (BUFSIZ - ocnt));
861 	    (void) lseek(STDIN_FILENO, (off_t) 0, SEEK_SET);
862 	    return;
863 	}
864 
865 	/*
866 	 * If term was quoted or -n just pass it on
867 	 */
868 	if (quoted || noexec) {
869 	    *lbp++ = '\n';
870 	    *lbp = 0;
871 	    for (lbp = lbuf; (c = *lbp++) != '\0';) {
872 		*obp++ = c;
873 		if (--ocnt == 0) {
874 		    (void) write(STDIN_FILENO, short2str(obuf), BUFSIZ);
875 		    obp = obuf;
876 		    ocnt = BUFSIZ;
877 		}
878 	    }
879 	    continue;
880 	}
881 
882 	/*
883 	 * Term wasn't quoted so variable and then command expand the input
884 	 * line
885 	 */
886 	Dcp = lbuf;
887 	Dvp = Dv + 1;
888 	mbp = mbuf;
889 	mcnt = BUFSIZ - 4;
890 	for (;;) {
891 	    c = DgetC(DODOL);
892 	    if (c == DEOF)
893 		break;
894 	    if ((c &= TRIM) == 0)
895 		continue;
896 	    /* \ quotes \ $ ` here */
897 	    if (c == '\\') {
898 		c = DgetC(0);
899 		if (!any("$\\`", c))
900 		    unDgetC(c | QUOTE), c = '\\';
901 		else
902 		    c |= QUOTE;
903 	    }
904 	    *mbp++ = c;
905 	    if (--mcnt == 0) {
906 		setname("<<");
907 		stderror(ERR_NAME | ERR_OVERFLOW);
908 	    }
909 	}
910 	*mbp++ = 0;
911 
912 	/*
913 	 * If any ` in line do command substitution
914 	 */
915 	mbp = mbuf;
916 	if (any(short2str(mbp), '`')) {
917 	    /*
918 	     * 1 arg to dobackp causes substitution to be literal. Words are
919 	     * broken only at newlines so that all blanks and tabs are
920 	     * preserved.  Blank lines (null words) are not discarded.
921 	     */
922 	    vp = dobackp(mbuf, 1);
923 	}
924 	else
925 	    /* Setup trivial vector similar to return of dobackp */
926 	    Dv[0] = mbp, Dv[1] = NULL, vp = Dv;
927 
928 	/*
929 	 * Resurrect the words from the command substitution each separated by
930 	 * a newline.  Note that the last newline of a command substitution
931 	 * will have been discarded, but we put a newline after the last word
932 	 * because this represents the newline after the last input line!
933 	 */
934 	for (; *vp; vp++) {
935 	    for (mbp = *vp; *mbp; mbp++) {
936 		*obp++ = *mbp & TRIM;
937 		if (--ocnt == 0) {
938 		    (void) write(STDIN_FILENO, short2str(obuf), BUFSIZ);
939 		    obp = obuf;
940 		    ocnt = BUFSIZ;
941 		}
942 	    }
943 	    *obp++ = '\n';
944 	    if (--ocnt == 0) {
945 		(void) write(STDIN_FILENO, short2str(obuf), BUFSIZ);
946 		obp = obuf;
947 		ocnt = BUFSIZ;
948 	    }
949 	}
950 	blkfree(pargv);
951 	pargv = NULL;
952     }
953 }
954