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