159243Sobrien/*
259243Sobrien * sh.dol.c: Variable substitutions
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien
3459243Sobrien/*
3559243Sobrien * C shell
3659243Sobrien */
3759243Sobrien
3859243Sobrien/*
3959243Sobrien * These routines perform variable substitution and quoting via ' and ".
4059243Sobrien * To this point these constructs have been preserved in the divided
4159243Sobrien * input words.  Here we expand variables and turn quoting via ' and " into
4259243Sobrien * QUOTE bits on characters (which prevent further interpretation).
4359243Sobrien * If the `:q' modifier was applied during history expansion, then
4459243Sobrien * some QUOTEing may have occurred already, so we dont "trim()" here.
4559243Sobrien */
4659243Sobrien
47231990Smpstatic eChar Dpeekc;		/* Peek for DgetC */
48145479Smpstatic eChar Dpeekrd;		/* Peek for Dreadc */
49167465Smpstatic Char *Dcp, *const *Dvp;	/* Input vector for Dreadc */
5059243Sobrien
51145479Smp#define	DEOF	CHAR_ERR
5259243Sobrien
5359243Sobrien#define	unDgetC(c)	Dpeekc = c
5459243Sobrien
5559243Sobrien#define QUOTES		(_QF|_QB|_ESC)	/* \ ' " ` */
5659243Sobrien
5759243Sobrien/*
5859243Sobrien * The following variables give the information about the current
5959243Sobrien * $ expansion, recording the current word position, the remaining
6059243Sobrien * words within this expansion, the count of remaining words, and the
6159243Sobrien * information about any : modifier which is being applied.
6259243Sobrien */
6359243Sobrienstatic Char *dolp;		/* Remaining chars from this word */
6459243Sobrienstatic Char **dolnxt;		/* Further words */
6559243Sobrienstatic int dolcnt;		/* Count of further words */
66167465Smpstatic struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */
67167465Smpstatic int dolmcnt;		/* :gx -> INT_MAX, else 1 */
68167465Smpstatic int dol_flag_a;		/* :ax -> 1, else 0 */
6959243Sobrien
70167465Smpstatic	Char	 **Dfix2	(Char *const *);
71167465Smpstatic	int 	 Dpack		(struct Strbuf *);
72167465Smpstatic	int	 Dword		(struct blk_buf *);
73167465Smpstatic	void	 dolerror	(Char *);
74167465Smpstatic	eChar	 DgetC		(int);
75167465Smpstatic	void	 Dgetdol	(void);
76167465Smpstatic	void	 fixDolMod	(void);
77167465Smpstatic	void	 setDolp	(Char *);
78167465Smpstatic	void	 unDredc	(eChar);
79167465Smpstatic	eChar	 Dredc		(void);
80167465Smpstatic	void	 Dtestq		(Char);
8159243Sobrien
8259243Sobrien/*
8359243Sobrien * Fix up the $ expansions and quotations in the
8459243Sobrien * argument list to command t.
8559243Sobrien */
8659243Sobrienvoid
87167465SmpDfix(struct command *t)
8859243Sobrien{
89145479Smp    Char **pp;
90145479Smp    Char *p;
9159243Sobrien
9259243Sobrien    if (noexec)
9359243Sobrien	return;
9459243Sobrien    /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
9559243Sobrien    for (pp = t->t_dcom; (p = *pp++) != NULL;) {
9659243Sobrien	for (; *p; p++) {
9759243Sobrien	    if (cmap(*p, _DOL | QUOTES)) {	/* $, \, ', ", ` */
98167465Smp		Char **expanded;
99167465Smp
100167465Smp		expanded = Dfix2(t->t_dcom);	/* found one */
10159243Sobrien		blkfree(t->t_dcom);
102167465Smp		t->t_dcom = expanded;
10359243Sobrien		return;
10459243Sobrien	    }
10559243Sobrien	}
10659243Sobrien    }
10759243Sobrien}
10859243Sobrien
10959243Sobrien/*
11059243Sobrien * $ substitute one word, for i/o redirection
11159243Sobrien */
11259243SobrienChar   *
113167465SmpDfix1(Char *cp)
11459243Sobrien{
115167465Smp    Char *Dv[2], **expanded;
11659243Sobrien
11759243Sobrien    if (noexec)
11859243Sobrien	return (0);
11959243Sobrien    Dv[0] = cp;
12059243Sobrien    Dv[1] = NULL;
121167465Smp    expanded = Dfix2(Dv);
122167465Smp    if (expanded[0] == NULL || expanded[1] != NULL) {
123167465Smp	blkfree(expanded);
12459243Sobrien	setname(short2str(cp));
12559243Sobrien	stderror(ERR_NAME | ERR_AMBIG);
12659243Sobrien    }
127167465Smp    cp = Strsave(expanded[0]);
128167465Smp    blkfree(expanded);
12959243Sobrien    return (cp);
13059243Sobrien}
13159243Sobrien
13259243Sobrien/*
13359243Sobrien * Subroutine to do actual fixing after state initialization.
13459243Sobrien */
135167465Smpstatic Char **
136167465SmpDfix2(Char *const *v)
13759243Sobrien{
138195609Smp    struct blk_buf *bb = bb_alloc();
139195609Smp    Char **vec;
140167465Smp
14159243Sobrien    Dvp = v;
14259243Sobrien    Dcp = STRNULL;		/* Setup input vector for Dreadc */
14359243Sobrien    unDgetC(0);
14459243Sobrien    unDredc(0);			/* Clear out any old peeks (at error) */
14559243Sobrien    dolp = 0;
14659243Sobrien    dolcnt = 0;			/* Clear out residual $ expands (...) */
147195609Smp    cleanup_push(bb, bb_free);
148195609Smp    while (Dword(bb))
14959243Sobrien	continue;
150195609Smp    cleanup_ignore(bb);
151195609Smp    cleanup_until(bb);
152195609Smp    vec = bb_finish(bb);
153195609Smp    xfree(bb);
154195609Smp    return vec;
15559243Sobrien}
15659243Sobrien
15759243Sobrien/*
15859243Sobrien * Pack up more characters in this word
15959243Sobrien */
160167465Smpstatic int
161167465SmpDpack(struct Strbuf *wbuf)
16259243Sobrien{
163145479Smp    eChar c;
16459243Sobrien
16559243Sobrien    for (;;) {
16659243Sobrien	c = DgetC(DODOL);
16759243Sobrien	if (c == '\\') {
16859243Sobrien	    c = DgetC(0);
16959243Sobrien	    if (c == DEOF) {
17059243Sobrien		unDredc(c);
171167465Smp		return 1;
17259243Sobrien	    }
17359243Sobrien	    if (c == '\n')
17459243Sobrien		c = ' ';
17559243Sobrien	    else
17659243Sobrien		c |= QUOTE;
17759243Sobrien	}
17859243Sobrien	if (c == DEOF) {
17959243Sobrien	    unDredc(c);
180167465Smp	    return 1;
18159243Sobrien	}
18259243Sobrien	if (cmap(c, _SP | _NL | _QF | _QB)) {	/* sp \t\n'"` */
18359243Sobrien	    unDgetC(c);
18459243Sobrien	    if (cmap(c, QUOTES))
185167465Smp		return 0;
186167465Smp	    return 1;
18759243Sobrien	}
188167465Smp	Strbuf_append1(wbuf, (Char) c);
18959243Sobrien    }
19059243Sobrien}
19159243Sobrien
19259243Sobrien/*
19359243Sobrien * Get a word.  This routine is analogous to the routine
19459243Sobrien * word() in sh.lex.c for the main lexical input.  One difference
19559243Sobrien * here is that we don't get a newline to terminate our expansion.
19659243Sobrien * Rather, DgetC will return a DEOF when we hit the end-of-input.
19759243Sobrien */
19859243Sobrienstatic int
199167465SmpDword(struct blk_buf *bb)
20059243Sobrien{
201145479Smp    eChar c, c1;
202195609Smp    struct Strbuf *wbuf = Strbuf_alloc();
203145479Smp    int dolflg;
204167465Smp    int    sofar = 0;
205195609Smp    Char *str;
20659243Sobrien
207195609Smp    cleanup_push(wbuf, Strbuf_free);
208167465Smp    for (;;) {
20959243Sobrien	c = DgetC(DODOL);
21059243Sobrien	switch (c) {
21159243Sobrien
21259243Sobrien	case DEOF:
213167465Smp	    if (sofar == 0) {
214195609Smp		cleanup_until(wbuf);
21559243Sobrien		return (0);
216167465Smp	    }
21759243Sobrien	    /* finish this word and catch the code above the next time */
21859243Sobrien	    unDredc(c);
21959243Sobrien	    /*FALLTHROUGH*/
22059243Sobrien
22159243Sobrien	case '\n':
222167465Smp	    goto end;
22359243Sobrien
22459243Sobrien	case ' ':
22559243Sobrien	case '\t':
226167465Smp	    continue;
22759243Sobrien
22859243Sobrien	case '`':
22959243Sobrien	    /* We preserve ` quotations which are done yet later */
230195609Smp	    Strbuf_append1(wbuf, (Char) c);
23159243Sobrien	    /*FALLTHROUGH*/
23259243Sobrien	case '\'':
23359243Sobrien	case '"':
23459243Sobrien	    /*
23559243Sobrien	     * Note that DgetC never returns a QUOTES character from an
23659243Sobrien	     * expansion, so only true input quotes will get us here or out.
23759243Sobrien	     */
23859243Sobrien	    c1 = c;
23959243Sobrien	    dolflg = c1 == '"' ? DODOL : 0;
24059243Sobrien	    for (;;) {
24159243Sobrien		c = DgetC(dolflg);
24259243Sobrien		if (c == c1)
24359243Sobrien		    break;
244195609Smp		if (c == '\n' || c == DEOF) {
245195609Smp		    cleanup_until(bb);
246145479Smp		    stderror(ERR_UNMATCHED, (int)c1);
247195609Smp		}
24859243Sobrien		if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
249195609Smp		    if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\')
250195609Smp			wbuf->len--;
25159243Sobrien		}
25259243Sobrien		switch (c1) {
25359243Sobrien
25459243Sobrien		case '"':
25559243Sobrien		    /*
25659243Sobrien		     * Leave any `s alone for later. Other chars are all
25759243Sobrien		     * quoted, thus `...` can tell it was within "...".
25859243Sobrien		     */
259195609Smp		    Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE);
26059243Sobrien		    break;
26159243Sobrien
26259243Sobrien		case '\'':
26359243Sobrien		    /* Prevent all further interpretation */
264195609Smp		    Strbuf_append1(wbuf, c | QUOTE);
26559243Sobrien		    break;
26659243Sobrien
26759243Sobrien		case '`':
26859243Sobrien		    /* Leave all text alone for later */
269195609Smp		    Strbuf_append1(wbuf, (Char) c);
27059243Sobrien		    break;
27159243Sobrien
27259243Sobrien		default:
27359243Sobrien		    break;
27459243Sobrien		}
27559243Sobrien	    }
27659243Sobrien	    if (c1 == '`')
277195609Smp		Strbuf_append1(wbuf, '`');
27859243Sobrien	    sofar = 1;
279195609Smp	    if (Dpack(wbuf) != 0)
280167465Smp		goto end;
281167465Smp	    continue;
28259243Sobrien
28359243Sobrien	case '\\':
28459243Sobrien	    c = DgetC(0);	/* No $ subst! */
285167465Smp	    if (c == '\n' || c == DEOF)
286167465Smp		continue;
28759243Sobrien	    c |= QUOTE;
28859243Sobrien	    break;
28959243Sobrien
29059243Sobrien	default:
29159243Sobrien	    break;
29259243Sobrien	}
293167465Smp	unDgetC(c);
294167465Smp	sofar = 1;
295195609Smp	if (Dpack(wbuf) != 0)
296167465Smp	    goto end;
29759243Sobrien    }
298167465Smp
299167465Smp end:
300195609Smp    cleanup_ignore(wbuf);
301195609Smp    cleanup_until(wbuf);
302195609Smp    str = Strbuf_finish(wbuf);
303195609Smp    bb_append(bb, str);
304195609Smp    xfree(wbuf);
305167465Smp    return 1;
30659243Sobrien}
30759243Sobrien
30859243Sobrien
30959243Sobrien/*
31059243Sobrien * Get a character, performing $ substitution unless flag is 0.
31159243Sobrien * Any QUOTES character which is returned from a $ expansion is
31259243Sobrien * QUOTEd so that it will not be recognized above.
31359243Sobrien */
314145479Smpstatic eChar
315167465SmpDgetC(int flag)
31659243Sobrien{
317231990Smp    eChar c;
31859243Sobrien
31959243Sobrientop:
32059243Sobrien    if ((c = Dpeekc) != 0) {
32159243Sobrien	Dpeekc = 0;
32259243Sobrien	return (c);
32359243Sobrien    }
324167465Smp    if (lap < labuf.len) {
325167465Smp	c = labuf.s[lap++] & (QUOTE | TRIM);
32659243Sobrienquotspec:
32759243Sobrien	if (cmap(c, QUOTES))
32859243Sobrien	    return (c | QUOTE);
32959243Sobrien	return (c);
33059243Sobrien    }
33159243Sobrien    if (dolp) {
33259243Sobrien	if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
33359243Sobrien	    goto quotspec;
33459243Sobrien	if (dolcnt > 0) {
33559243Sobrien	    setDolp(*dolnxt++);
33659243Sobrien	    --dolcnt;
33759243Sobrien	    return (' ');
33859243Sobrien	}
33959243Sobrien	dolp = 0;
34059243Sobrien    }
34159243Sobrien    if (dolcnt > 0) {
34259243Sobrien	setDolp(*dolnxt++);
34359243Sobrien	--dolcnt;
34459243Sobrien	goto top;
34559243Sobrien    }
34659243Sobrien    c = Dredc();
34759243Sobrien    if (c == '$' && flag) {
34859243Sobrien	Dgetdol();
34959243Sobrien	goto top;
35059243Sobrien    }
35159243Sobrien    return (c);
35259243Sobrien}
35359243Sobrien
35459243Sobrienstatic Char *nulvec[] = { NULL };
35559243Sobrienstatic struct varent nulargv = {nulvec, STRargv, VAR_READWRITE,
35659243Sobrien				{ NULL, NULL, NULL }, 0 };
35759243Sobrien
35859243Sobrienstatic void
359167465Smpdolerror(Char *s)
36059243Sobrien{
36159243Sobrien    setname(short2str(s));
36259243Sobrien    stderror(ERR_NAME | ERR_RANGE);
36359243Sobrien}
36459243Sobrien
36559243Sobrien/*
36659243Sobrien * Handle the multitudinous $ expansion forms.
36759243Sobrien * Ugh.
36859243Sobrien */
36959243Sobrienstatic void
370167465SmpDgetdol(void)
37159243Sobrien{
372145479Smp    Char *np;
373145479Smp    struct varent *vp = NULL;
374195609Smp    struct Strbuf *name = Strbuf_alloc();
375145479Smp    eChar   c, sc;
37659243Sobrien    int     subscr = 0, lwb = 1, upb = 0;
377145479Smp    int    dimen = 0, bitset = 0, length = 0;
37859243Sobrien    static Char *dolbang = NULL;
37959243Sobrien
380195609Smp    cleanup_push(name, Strbuf_free);
381167465Smp    dolmod.len = dolmcnt = dol_flag_a = 0;
38259243Sobrien    c = sc = DgetC(0);
383167465Smp    if (c == DEOF) {
384167465Smp      stderror(ERR_SYNTAX);
385167465Smp      return;
386167465Smp    }
38759243Sobrien    if (c == '{')
38859243Sobrien	c = DgetC(0);		/* sc is { to take } later */
38959243Sobrien    if ((c & TRIM) == '#')
39059243Sobrien	dimen++, c = DgetC(0);	/* $# takes dimension */
39159243Sobrien    else if (c == '?')
39259243Sobrien	bitset++, c = DgetC(0);	/* $? tests existence */
39359243Sobrien    else if (c == '%')
39459243Sobrien	length++, c = DgetC(0); /* $% returns length in chars */
39559243Sobrien    switch (c) {
39659243Sobrien
39759243Sobrien    case '!':
39859243Sobrien	if (dimen || bitset || length)
39959243Sobrien	    stderror(ERR_SYNTAX);
40059243Sobrien	if (backpid != 0) {
401167465Smp	    xfree(dolbang);
402231990Smp	    setDolp(dolbang = putn((tcsh_number_t)backpid));
40359243Sobrien	}
404195609Smp	cleanup_until(name);
40559243Sobrien	goto eatbrac;
40659243Sobrien
40759243Sobrien    case '$':
40859243Sobrien	if (dimen || bitset || length)
40959243Sobrien	    stderror(ERR_SYNTAX);
41059243Sobrien	setDolp(doldol);
411195609Smp	cleanup_until(name);
41259243Sobrien	goto eatbrac;
41359243Sobrien
414167465Smp    case '<'|QUOTE: {
415167465Smp	static struct Strbuf wbuf; /* = Strbuf_INIT; */
416167465Smp
41759243Sobrien	if (bitset)
41859243Sobrien	    stderror(ERR_NOTALLOWED, "$?<");
41959243Sobrien	if (dimen)
42059243Sobrien	    stderror(ERR_NOTALLOWED, "$#<");
42159243Sobrien	if (length)
42259243Sobrien	    stderror(ERR_NOTALLOWED, "$%<");
423167465Smp	wbuf.len = 0;
42459243Sobrien	{
425145479Smp	    char cbuf[MB_LEN_MAX];
426145479Smp	    size_t cbp = 0;
427167465Smp	    int old_pintr_disabled;
428145479Smp
429167465Smp	    for (;;) {
430145479Smp	        int len;
431167465Smp		ssize_t res;
432167465Smp		Char wc;
433145479Smp
434167465Smp		pintr_push_enable(&old_pintr_disabled);
435167465Smp		res = force_read(OLDSTD, cbuf + cbp, 1);
436167465Smp		cleanup_until(&old_pintr_disabled);
437167465Smp		if (res != 1)
438167465Smp		    break;
439167465Smp		cbp++;
440167465Smp		len = normal_mbtowc(&wc, cbuf, cbp);
441145479Smp		if (len == -1) {
442145479Smp		    reset_mbtowc();
443145479Smp		    if (cbp < MB_LEN_MAX)
444145479Smp		        continue; /* Maybe a partial character */
445167465Smp		    wc = (unsigned char)*cbuf | INVALID_BYTE;
446145479Smp		}
447145479Smp		if (len <= 0)
448145479Smp		    len = 1;
449145479Smp		if (cbp != (size_t)len)
450145479Smp		    memmove(cbuf, cbuf + len, cbp - len);
451145479Smp		cbp -= len;
452167465Smp		if (wc == '\n')
45359243Sobrien		    break;
454167465Smp		Strbuf_append1(&wbuf, wc);
45559243Sobrien	    }
456145479Smp	    while (cbp != 0) {
457167465Smp		int len;
458167465Smp		Char wc;
459167465Smp
460167465Smp		len = normal_mbtowc(&wc, cbuf, cbp);
461167465Smp		if (len == -1) {
462167465Smp		    reset_mbtowc();
463167465Smp		    wc = (unsigned char)*cbuf | INVALID_BYTE;
464167465Smp		}
465167465Smp		if (len <= 0)
466167465Smp		    len = 1;
467167465Smp		if (cbp != (size_t)len)
468167465Smp		    memmove(cbuf, cbuf + len, cbp - len);
469167465Smp		cbp -= len;
470167465Smp		if (wc == '\n')
471145479Smp		    break;
472167465Smp		Strbuf_append1(&wbuf, wc);
473145479Smp	    }
474167465Smp	    Strbuf_terminate(&wbuf);
47559243Sobrien	}
47659243Sobrien
47759243Sobrien	fixDolMod();
478167465Smp	setDolp(wbuf.s); /* Kept allocated until next $< expansion */
479195609Smp	cleanup_until(name);
48059243Sobrien	goto eatbrac;
481167465Smp    }
48259243Sobrien
48359243Sobrien    case '*':
484195609Smp	Strbuf_append(name, STRargv);
485195609Smp	Strbuf_terminate(name);
48659243Sobrien	vp = adrof(STRargv);
48759243Sobrien	subscr = -1;		/* Prevent eating [...] */
48859243Sobrien	break;
48959243Sobrien
49059243Sobrien    case DEOF:
49159243Sobrien    case '\n':
49259243Sobrien	np = dimen ? STRargv : (bitset ? STRstatus : NULL);
49359243Sobrien	if (np) {
49459243Sobrien	    bitset = 0;
495195609Smp	    Strbuf_append(name, np);
496195609Smp	    Strbuf_terminate(name);
49759243Sobrien	    vp = adrof(np);
49859243Sobrien	    subscr = -1;		/* Prevent eating [...] */
49959243Sobrien	    unDredc(c);
50059243Sobrien	    break;
50159243Sobrien	}
50259243Sobrien	else
50359243Sobrien	    stderror(ERR_SYNTAX);
50459243Sobrien	/*NOTREACHED*/
50559243Sobrien
50659243Sobrien    default:
50759243Sobrien	if (Isdigit(c)) {
50859243Sobrien	    if (dimen)
50959243Sobrien		stderror(ERR_NOTALLOWED, "$#<num>");
51059243Sobrien	    subscr = 0;
51159243Sobrien	    do {
51259243Sobrien		subscr = subscr * 10 + c - '0';
51359243Sobrien		c = DgetC(0);
514167465Smp	    } while (c != DEOF && Isdigit(c));
51559243Sobrien	    unDredc(c);
516100616Smp	    if (subscr < 0)
517100616Smp		stderror(ERR_RANGE);
51859243Sobrien	    if (subscr == 0) {
51959243Sobrien		if (bitset) {
52059243Sobrien		    dolp = dolzero ? STR1 : STR0;
521195609Smp		    cleanup_until(name);
52259243Sobrien		    goto eatbrac;
52359243Sobrien		}
52459243Sobrien		if (ffile == 0)
52559243Sobrien		    stderror(ERR_DOLZERO);
52659243Sobrien		if (length) {
527167465Smp		    length = Strlen(ffile);
528231990Smp		    addla(putn((tcsh_number_t)length));
52959243Sobrien		}
53059243Sobrien		else {
53159243Sobrien		    fixDolMod();
53259243Sobrien		    setDolp(ffile);
53359243Sobrien		}
534195609Smp		cleanup_until(name);
53559243Sobrien		goto eatbrac;
53659243Sobrien	    }
53759243Sobrien#if 0
53859243Sobrien	    if (bitset)
53959243Sobrien		stderror(ERR_NOTALLOWED, "$?<num>");
54059243Sobrien	    if (length)
54159243Sobrien		stderror(ERR_NOTALLOWED, "$%<num>");
54259243Sobrien#endif
54359243Sobrien	    vp = adrof(STRargv);
54459243Sobrien	    if (vp == 0) {
54559243Sobrien		vp = &nulargv;
546195609Smp		cleanup_until(name);
54759243Sobrien		goto eatmod;
54859243Sobrien	    }
54959243Sobrien	    break;
55059243Sobrien	}
551167465Smp	if (c == DEOF || !alnum(c)) {
55259243Sobrien	    np = dimen ? STRargv : (bitset ? STRstatus : NULL);
55359243Sobrien	    if (np) {
55459243Sobrien		bitset = 0;
555195609Smp		Strbuf_append(name, np);
556195609Smp		Strbuf_terminate(name);
55759243Sobrien		vp = adrof(np);
55859243Sobrien		subscr = -1;		/* Prevent eating [...] */
55959243Sobrien		unDredc(c);
56059243Sobrien		break;
56159243Sobrien	    }
56259243Sobrien	    else
56359243Sobrien		stderror(ERR_VARALNUM);
56459243Sobrien	}
56559243Sobrien	for (;;) {
566195609Smp	    Strbuf_append1(name, (Char) c);
56759243Sobrien	    c = DgetC(0);
568167465Smp	    if (c == DEOF || !alnum(c))
56959243Sobrien		break;
57059243Sobrien	}
571195609Smp	Strbuf_terminate(name);
57259243Sobrien	unDredc(c);
573195609Smp	vp = adrof(name->s);
57459243Sobrien    }
57559243Sobrien    if (bitset) {
576195609Smp	dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0;
577195609Smp	cleanup_until(name);
57859243Sobrien	goto eatbrac;
57959243Sobrien    }
580100616Smp    if (vp == NULL || vp->vec == NULL) {
581195609Smp	np = str2short(getenv(short2str(name->s)));
58259243Sobrien	if (np) {
583167465Smp	    static Char *env_val; /* = NULL; */
584167465Smp
585195609Smp	    cleanup_until(name);
58659243Sobrien	    fixDolMod();
587195609Smp	    if (length) {
588231990Smp		    addla(putn((tcsh_number_t)Strlen(np)));
589195609Smp	    } else {
590195609Smp		    xfree(env_val);
591195609Smp		    env_val = Strsave(np);
592195609Smp		    setDolp(env_val);
593195609Smp	    }
59459243Sobrien	    goto eatbrac;
59559243Sobrien	}
596195609Smp	udvar(name->s);
59759243Sobrien	/* NOTREACHED */
59859243Sobrien    }
599195609Smp    cleanup_until(name);
60059243Sobrien    c = DgetC(0);
60159243Sobrien    upb = blklen(vp->vec);
60259243Sobrien    if (dimen == 0 && subscr == 0 && c == '[') {
603195609Smp	name = Strbuf_alloc();
604195609Smp	cleanup_push(name, Strbuf_free);
605195609Smp	np = name->s;
60659243Sobrien	for (;;) {
60759243Sobrien	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
60859243Sobrien	    if (c == ']')
60959243Sobrien		break;
61059243Sobrien	    if (c == '\n' || c == DEOF)
61159243Sobrien		stderror(ERR_INCBR);
612195609Smp	    Strbuf_append1(name, (Char) c);
61359243Sobrien	}
614195609Smp	Strbuf_terminate(name);
615195609Smp	np = name->s;
61659243Sobrien	if (dolp || dolcnt)	/* $ exp must end before ] */
61759243Sobrien	    stderror(ERR_EXPORD);
61859243Sobrien	if (!*np)
61959243Sobrien	    stderror(ERR_SYNTAX);
62059243Sobrien	if (Isdigit(*np)) {
62159243Sobrien	    int     i;
62259243Sobrien
62359243Sobrien	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
62459243Sobrien		continue;
625231990Smp	    if (i < 0 || (i > upb && !any("-*", *np))) {
626195609Smp		cleanup_until(name);
62759243Sobrien		dolerror(vp->v_name);
62859243Sobrien		return;
62959243Sobrien	    }
63059243Sobrien	    lwb = i;
63159243Sobrien	    if (!*np)
63259243Sobrien		upb = lwb, np = STRstar;
63359243Sobrien	}
63459243Sobrien	if (*np == '*')
63559243Sobrien	    np++;
63659243Sobrien	else if (*np != '-')
63759243Sobrien	    stderror(ERR_MISSING, '-');
63859243Sobrien	else {
639145479Smp	    int i = upb;
64059243Sobrien
64159243Sobrien	    np++;
64259243Sobrien	    if (Isdigit(*np)) {
64359243Sobrien		i = 0;
64459243Sobrien		while (Isdigit(*np))
64559243Sobrien		    i = i * 10 + *np++ - '0';
64659243Sobrien		if (i < 0 || i > upb) {
647195609Smp		    cleanup_until(name);
64859243Sobrien		    dolerror(vp->v_name);
64959243Sobrien		    return;
65059243Sobrien		}
65159243Sobrien	    }
65259243Sobrien	    if (i < lwb)
65359243Sobrien		upb = lwb - 1;
65459243Sobrien	    else
65559243Sobrien		upb = i;
65659243Sobrien	}
65759243Sobrien	if (lwb == 0) {
65859243Sobrien	    if (upb != 0) {
659195609Smp		cleanup_until(name);
66059243Sobrien		dolerror(vp->v_name);
66159243Sobrien		return;
66259243Sobrien	    }
66359243Sobrien	    upb = -1;
66459243Sobrien	}
66559243Sobrien	if (*np)
66659243Sobrien	    stderror(ERR_SYNTAX);
667195609Smp	cleanup_until(name);
66859243Sobrien    }
66959243Sobrien    else {
67059243Sobrien	if (subscr > 0) {
67159243Sobrien	    if (subscr > upb)
67259243Sobrien		lwb = 1, upb = 0;
67359243Sobrien	    else
67459243Sobrien		lwb = upb = subscr;
67559243Sobrien	}
67659243Sobrien	unDredc(c);
67759243Sobrien    }
67859243Sobrien    if (dimen) {
67959243Sobrien	/* this is a kludge. It prevents Dgetdol() from */
68059243Sobrien	/* pushing erroneous ${#<error> values into the labuf. */
68159243Sobrien	if (sc == '{') {
68259243Sobrien	    c = Dredc();
68359243Sobrien	    if (c != '}')
68459243Sobrien		stderror(ERR_MISSING, '}');
68559243Sobrien	    unDredc(c);
68659243Sobrien	}
687231990Smp	addla(putn((tcsh_number_t)(upb - lwb + 1)));
68859243Sobrien    }
68959243Sobrien    else if (length) {
69059243Sobrien	int i;
691167465Smp
69259243Sobrien	for (i = lwb - 1, length = 0; i < upb; i++)
693167465Smp	    length += Strlen(vp->vec[i]);
69459243Sobrien#ifdef notdef
69559243Sobrien	/* We don't want that, since we can always compute it by adding $#xxx */
69659243Sobrien	length += i - 1;	/* Add the number of spaces in */
69759243Sobrien#endif
698231990Smp	addla(putn((tcsh_number_t)length));
69959243Sobrien    }
70059243Sobrien    else {
70159243Sobrieneatmod:
70259243Sobrien	fixDolMod();
70359243Sobrien	dolnxt = &vp->vec[lwb - 1];
70459243Sobrien	dolcnt = upb - lwb + 1;
70559243Sobrien    }
70659243Sobrieneatbrac:
70759243Sobrien    if (sc == '{') {
70859243Sobrien	c = Dredc();
70959243Sobrien	if (c != '}')
71059243Sobrien	    stderror(ERR_MISSING, '}');
71159243Sobrien    }
71259243Sobrien}
71359243Sobrien
71459243Sobrienstatic void
715167465SmpfixDolMod(void)
71659243Sobrien{
717145479Smp    eChar c;
71859243Sobrien
71959243Sobrien    c = DgetC(0);
72059243Sobrien    if (c == ':') {
72159243Sobrien	do {
722167465Smp	    c = DgetC(0), dolmcnt = 1, dol_flag_a = 0;
72359243Sobrien	    if (c == 'g' || c == 'a') {
72459243Sobrien		if (c == 'g')
725167465Smp		    dolmcnt = INT_MAX;
72659243Sobrien		else
727167465Smp		    dol_flag_a = 1;
72859243Sobrien		c = DgetC(0);
72959243Sobrien	    }
730167465Smp	    if ((c == 'g' && dolmcnt != INT_MAX) ||
731167465Smp		(c == 'a' && dol_flag_a == 0)) {
73259243Sobrien		if (c == 'g')
733167465Smp		    dolmcnt = INT_MAX;
73459243Sobrien		else
735167465Smp		    dol_flag_a = 1;
736167465Smp		c = DgetC(0);
73759243Sobrien	    }
73859243Sobrien
73959243Sobrien	    if (c == 's') {	/* [eichin:19910926.0755EST] */
74059243Sobrien		int delimcnt = 2;
741145479Smp		eChar delim = DgetC(0);
742167465Smp		Strbuf_append1(&dolmod, (Char) c);
743167465Smp		Strbuf_append1(&dolmod, (Char) delim);
744167465Smp
745167465Smp		if (delim == DEOF || !delim || letter(delim)
74659243Sobrien		    || Isdigit(delim) || any(" \t\n", delim)) {
74759243Sobrien		    seterror(ERR_BADSUBST);
74859243Sobrien		    break;
74959243Sobrien		}
750145479Smp		while ((c = DgetC(0)) != DEOF) {
751167465Smp		    Strbuf_append1(&dolmod, (Char) c);
75259243Sobrien		    if(c == delim) delimcnt--;
75359243Sobrien		    if(!delimcnt) break;
75459243Sobrien		}
75559243Sobrien		if(delimcnt) {
75659243Sobrien		    seterror(ERR_BADSUBST);
75759243Sobrien		    break;
75859243Sobrien		}
75959243Sobrien		continue;
76059243Sobrien	    }
76159243Sobrien	    if (!any("luhtrqxes", c))
762145479Smp		stderror(ERR_BADMOD, (int)c);
763167465Smp	    Strbuf_append1(&dolmod, (Char) c);
76459243Sobrien	    if (c == 'q')
765167465Smp		dolmcnt = INT_MAX;
76659243Sobrien	}
76759243Sobrien	while ((c = DgetC(0)) == ':');
76859243Sobrien	unDredc(c);
76959243Sobrien    }
77059243Sobrien    else
77159243Sobrien	unDredc(c);
77259243Sobrien}
77359243Sobrien
77459243Sobrienstatic void
775167465SmpsetDolp(Char *cp)
77659243Sobrien{
777145479Smp    Char *dp;
778167465Smp    size_t i;
77959243Sobrien
780167465Smp    if (dolmod.len == 0 || dolmcnt == 0) {
78159243Sobrien	dolp = cp;
78259243Sobrien	return;
78359243Sobrien    }
784167465Smp    cp = Strsave(cp);
785167465Smp    for (i = 0; i < dolmod.len; i++) {
786167465Smp	int didmod = 0;
787167465Smp
78859243Sobrien	/* handle s// [eichin:19910926.0510EST] */
789167465Smp	if(dolmod.s[i] == 's') {
790145479Smp	    Char delim;
79159243Sobrien	    Char *lhsub, *rhsub, *np;
79259243Sobrien	    size_t lhlen = 0, rhlen = 0;
793167465Smp
794167465Smp	    delim = dolmod.s[++i];
79559243Sobrien	    if (!delim || letter(delim)
79659243Sobrien		|| Isdigit(delim) || any(" \t\n", delim)) {
79759243Sobrien		seterror(ERR_BADSUBST);
79859243Sobrien		break;
79959243Sobrien	    }
800167465Smp	    lhsub = &dolmod.s[++i];
801167465Smp	    while(dolmod.s[i] != delim && dolmod.s[++i]) {
80259243Sobrien		lhlen++;
80359243Sobrien	    }
804167465Smp	    dolmod.s[i] = 0;
805167465Smp	    rhsub = &dolmod.s[++i];
806167465Smp	    while(dolmod.s[i] != delim && dolmod.s[++i]) {
80759243Sobrien		rhlen++;
80859243Sobrien	    }
809167465Smp	    dolmod.s[i] = 0;
81059243Sobrien
811167465Smp	    strip(lhsub);
812195609Smp	    strip(rhsub);
813167465Smp	    strip(cp);
814167465Smp	    dp = cp;
81559243Sobrien	    do {
816167465Smp		dp = Strstr(dp, lhsub);
81759243Sobrien		if (dp) {
818167465Smp		    ptrdiff_t diff = dp - cp;
819195609Smp		    size_t len = (Strlen(cp) + 1 - lhlen + rhlen);
820195609Smp		    np = xmalloc(len * sizeof(Char));
821167465Smp		    (void) Strncpy(np, cp, diff);
822167465Smp		    (void) Strcpy(np + diff, rhsub);
823167465Smp		    (void) Strcpy(np + diff + rhlen, dp + lhlen);
82459243Sobrien
825167465Smp		    xfree(cp);
826316957Sdchagin		    dp = cp = np;
827195609Smp		    cp[--len] = '\0';
82859243Sobrien		    didmod = 1;
829231990Smp		    if (diff >= (ssize_t)len)
830195609Smp			break;
83159243Sobrien		} else {
83259243Sobrien		    /* should this do a seterror? */
83359243Sobrien		    break;
83459243Sobrien		}
83559243Sobrien	    }
836167465Smp	    while (dol_flag_a != 0);
83759243Sobrien	    /*
83859243Sobrien	     * restore dolmod for additional words
83959243Sobrien	     */
840167465Smp	    dolmod.s[i] = rhsub[-1] = (Char) delim;
84159243Sobrien        } else {
84259243Sobrien
84359243Sobrien	    do {
844167465Smp		if ((dp = domod(cp, dolmod.s[i])) != NULL) {
84559243Sobrien		    didmod = 1;
84659243Sobrien		    if (Strcmp(cp, dp) == 0) {
847167465Smp			xfree(cp);
84859243Sobrien			cp = dp;
84959243Sobrien			break;
85059243Sobrien		    }
85159243Sobrien		    else {
852167465Smp			xfree(cp);
85359243Sobrien			cp = dp;
85459243Sobrien		    }
85559243Sobrien		}
85659243Sobrien		else
85759243Sobrien		    break;
85859243Sobrien	    }
859167465Smp	    while (dol_flag_a != 0);
860167465Smp	}
861167465Smp	if (didmod && dolmcnt != INT_MAX)
862167465Smp	    dolmcnt--;
86359243Sobrien#ifdef notdef
864167465Smp	else
865167465Smp	    break;
86659243Sobrien#endif
86759243Sobrien    }
86859243Sobrien
869167465Smp    addla(cp);
87059243Sobrien
87159243Sobrien    dolp = STRNULL;
87259243Sobrien    if (seterr)
87359243Sobrien	stderror(ERR_OLD);
87459243Sobrien}
87559243Sobrien
87659243Sobrienstatic void
877167465SmpunDredc(eChar c)
87859243Sobrien{
87959243Sobrien
88059243Sobrien    Dpeekrd = c;
88159243Sobrien}
88259243Sobrien
883145479Smpstatic eChar
884167465SmpDredc(void)
88559243Sobrien{
886231990Smp    eChar c;
88759243Sobrien
88859243Sobrien    if ((c = Dpeekrd) != 0) {
88959243Sobrien	Dpeekrd = 0;
89059243Sobrien	return (c);
89159243Sobrien    }
89259243Sobrien    if (Dcp && (c = *Dcp++))
89359243Sobrien	return (c & (QUOTE | TRIM));
89459243Sobrien    if (*Dvp == 0) {
89559243Sobrien	Dcp = 0;
89659243Sobrien	return (DEOF);
89759243Sobrien    }
89859243Sobrien    Dcp = *Dvp++;
89959243Sobrien    return (' ');
90059243Sobrien}
90159243Sobrien
902167465Smpstatic int gflag;
903167465Smp
90459243Sobrienstatic void
905167465SmpDtestq(Char c)
90659243Sobrien{
90759243Sobrien
90859243Sobrien    if (cmap(c, QUOTES))
90959243Sobrien	gflag = 1;
91059243Sobrien}
91159243Sobrien
912167465Smpstatic void
913167465Smpinheredoc_cleanup(void *dummy)
914167465Smp{
915167465Smp    USE(dummy);
916167465Smp    inheredoc = 0;
917167465Smp}
918167465Smp
919316957SdchaginChar *
920316957Sdchaginrandsuf(void) {
921316957Sdchagin#ifndef WINNT_NATIVE
922316957Sdchagin	struct timeval tv;
923316957Sdchagin	(void) gettimeofday(&tv, NULL);
924316957Sdchagin	return putn((((tcsh_number_t)tv.tv_sec) ^
925316957Sdchagin	    ((tcsh_number_t)tv.tv_usec) ^
926316957Sdchagin	    ((tcsh_number_t)getpid())) & 0x00ffffff);
927316957Sdchagin#else
928316957Sdchagin    return putn(getpid());
929316957Sdchagin#endif
930316957Sdchagin}
931316957Sdchagin
93259243Sobrien/*
93359243Sobrien * Form a shell temporary file (in unit 0) from the words
93459243Sobrien * of the shell input up to EOF or a line the same as "term".
93559243Sobrien * Unit 0 should have been closed before this call.
93659243Sobrien */
93759243Sobrienvoid
938167465Smpheredoc(Char *term)
93959243Sobrien{
940145479Smp    eChar  c;
94159243Sobrien    Char   *Dv[2];
942167465Smp    struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
943167465Smp    Char    obuf[BUFSIZE + 1];
944167465Smp#define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
945145479Smp    Char *lbp, *obp, *mbp;
94659243Sobrien    Char  **vp;
947145479Smp    int    quoted;
948231990Smp#ifdef HAVE_MKSTEMP
949231990Smp    char   *tmp = short2str(shtemp);
950231990Smp    char   *dot = strrchr(tmp, '.');
951231990Smp
952231990Smp    if (!dot)
953231990Smp	stderror(ERR_NAME | ERR_NOMATCH);
954231990Smp    strcpy(dot, TMP_TEMPLATE);
955231990Smp
956231990Smp    xclose(0);
957231990Smp    if (mkstemp(tmp) == -1)
958231990Smp	stderror(ERR_SYSTEM, tmp, strerror(errno));
959231990Smp#else /* !HAVE_MKSTEMP */
96059243Sobrien    char   *tmp;
961231990Smp# ifndef WINNT_NATIVE
96259243Sobrien
96368332Skrisagain:
964231990Smp# endif /* WINNT_NATIVE */
96559243Sobrien    tmp = short2str(shtemp);
966231990Smp# if O_CREAT == 0
967167465Smp    if (xcreat(tmp, 0600) < 0)
96859243Sobrien	stderror(ERR_SYSTEM, tmp, strerror(errno));
969231990Smp# endif
970167465Smp    xclose(0);
971167465Smp    if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
972167465Smp	-1) {
97368332Skris	int oerrno = errno;
974231990Smp# ifndef WINNT_NATIVE
97568332Skris	if (errno == EEXIST) {
97668332Skris	    if (unlink(tmp) == -1) {
977167465Smp		xfree(shtemp);
978316957Sdchagin		mbp = randsuf();
979167465Smp		shtemp = Strspl(STRtmpsh, mbp);
980167465Smp		xfree(mbp);
98168332Skris	    }
98268332Skris	    goto again;
98368332Skris	}
984231990Smp# endif /* WINNT_NATIVE */
98559243Sobrien	(void) unlink(tmp);
98659243Sobrien	errno = oerrno;
98768332Skris 	stderror(ERR_SYSTEM, tmp, strerror(errno));
98859243Sobrien    }
989231990Smp#endif /* HAVE_MKSTEMP */
99059243Sobrien    (void) unlink(tmp);		/* 0 0 inode! */
99159243Sobrien    Dv[0] = term;
99259243Sobrien    Dv[1] = NULL;
99359243Sobrien    gflag = 0;
99459243Sobrien    trim(Dv);
99559243Sobrien    rscan(Dv, Dtestq);
99659243Sobrien    quoted = gflag;
99759243Sobrien    obp = obuf;
998145479Smp    obuf[BUFSIZE] = 0;
99959243Sobrien    inheredoc = 1;
1000167465Smp    cleanup_push(&inheredoc, inheredoc_cleanup);
100169408Sache#ifdef WINNT_NATIVE
100259243Sobrien    __dup_stdin = 1;
100369408Sache#endif /* WINNT_NATIVE */
1004167465Smp    cleanup_push(&lbuf, Strbuf_cleanup);
1005167465Smp    cleanup_push(&mbuf, Strbuf_cleanup);
100659243Sobrien    for (;;) {
1007167465Smp	Char **words;
1008167465Smp
100959243Sobrien	/*
101059243Sobrien	 * Read up a line
101159243Sobrien	 */
1012167465Smp	lbuf.len = 0;
101359243Sobrien	for (;;) {
101459243Sobrien	    c = readc(1);	/* 1 -> Want EOF returns */
1015145479Smp	    if (c == CHAR_ERR || c == '\n')
101659243Sobrien		break;
1017167465Smp	    if ((c &= TRIM) != 0)
1018167465Smp		Strbuf_append1(&lbuf, (Char) c);
101959243Sobrien	}
1020167465Smp	Strbuf_terminate(&lbuf);
102159243Sobrien
1022231990Smp	/* Catch EOF in the middle of a line. */
1023231990Smp	if (c == CHAR_ERR && lbuf.len != 0)
1024231990Smp	    c = '\n';
1025231990Smp
102659243Sobrien	/*
102759243Sobrien	 * Check for EOF or compare to terminator -- before expansion
102859243Sobrien	 */
1029167465Smp	if (c == CHAR_ERR || eq(lbuf.s, term))
1030167465Smp	    break;
103159243Sobrien
103259243Sobrien	/*
103359243Sobrien	 * If term was quoted or -n just pass it on
103459243Sobrien	 */
103559243Sobrien	if (quoted || noexec) {
1036167465Smp	    Strbuf_append1(&lbuf, '\n');
1037167465Smp	    Strbuf_terminate(&lbuf);
1038167465Smp	    for (lbp = lbuf.s; (c = *lbp++) != 0;) {
103959243Sobrien		*obp++ = (Char) c;
1040167465Smp		if (obp == OBUF_END) {
1041145479Smp		    tmp = short2str(obuf);
1042167465Smp		    (void) xwrite(0, tmp, strlen (tmp));
104359243Sobrien		    obp = obuf;
104459243Sobrien		}
104559243Sobrien	    }
104659243Sobrien	    continue;
104759243Sobrien	}
104859243Sobrien
104959243Sobrien	/*
105059243Sobrien	 * Term wasn't quoted so variable and then command expand the input
105159243Sobrien	 * line
105259243Sobrien	 */
1053167465Smp	Dcp = lbuf.s;
105459243Sobrien	Dvp = Dv + 1;
1055167465Smp	mbuf.len = 0;
105659243Sobrien	for (;;) {
105759243Sobrien	    c = DgetC(DODOL);
105859243Sobrien	    if (c == DEOF)
105959243Sobrien		break;
106059243Sobrien	    if ((c &= TRIM) == 0)
106159243Sobrien		continue;
106259243Sobrien	    /* \ quotes \ $ ` here */
106359243Sobrien	    if (c == '\\') {
106459243Sobrien		c = DgetC(0);
106559243Sobrien		if (!any("$\\`", c))
106659243Sobrien		    unDgetC(c | QUOTE), c = '\\';
106759243Sobrien		else
106859243Sobrien		    c |= QUOTE;
106959243Sobrien	    }
1070167465Smp	    Strbuf_append1(&mbuf, (Char) c);
107159243Sobrien	}
1072167465Smp	Strbuf_terminate(&mbuf);
107359243Sobrien
107459243Sobrien	/*
107559243Sobrien	 * If any ` in line do command substitution
107659243Sobrien	 */
1077167465Smp	mbp = mbuf.s;
107859243Sobrien	if (Strchr(mbp, '`') != NULL) {
107959243Sobrien	    /*
108059243Sobrien	     * 1 arg to dobackp causes substitution to be literal. Words are
108159243Sobrien	     * broken only at newlines so that all blanks and tabs are
108259243Sobrien	     * preserved.  Blank lines (null words) are not discarded.
108359243Sobrien	     */
1084167465Smp	    words = dobackp(mbp, 1);
108559243Sobrien	}
108659243Sobrien	else
108759243Sobrien	    /* Setup trivial vector similar to return of dobackp */
1088167465Smp	    Dv[0] = mbp, Dv[1] = NULL, words = Dv;
108959243Sobrien
109059243Sobrien	/*
109159243Sobrien	 * Resurrect the words from the command substitution each separated by
109259243Sobrien	 * a newline.  Note that the last newline of a command substitution
109359243Sobrien	 * will have been discarded, but we put a newline after the last word
109459243Sobrien	 * because this represents the newline after the last input line!
109559243Sobrien	 */
1096167465Smp	for (vp= words; *vp; vp++) {
109759243Sobrien	    for (mbp = *vp; *mbp; mbp++) {
109859243Sobrien		*obp++ = *mbp & TRIM;
1099167465Smp		if (obp == OBUF_END) {
1100145479Smp		    tmp = short2str(obuf);
1101167465Smp		    (void) xwrite(0, tmp, strlen (tmp));
110259243Sobrien		    obp = obuf;
110359243Sobrien		}
110459243Sobrien	    }
110559243Sobrien	    *obp++ = '\n';
1106167465Smp	    if (obp == OBUF_END) {
1107145479Smp	        tmp = short2str(obuf);
1108167465Smp		(void) xwrite(0, tmp, strlen (tmp));
110959243Sobrien		obp = obuf;
111059243Sobrien	    }
111159243Sobrien	}
1112167465Smp	if (words != Dv)
1113167465Smp	    blkfree(words);
111459243Sobrien    }
1115167465Smp    *obp = 0;
1116167465Smp    tmp = short2str(obuf);
1117167465Smp    (void) xwrite(0, tmp, strlen (tmp));
1118167465Smp    (void) lseek(0, (off_t) 0, L_SET);
1119167465Smp    cleanup_until(&inheredoc);
112059243Sobrien}
1121