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