1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $ */
259243Sobrien/*
359243Sobrien * sh.dol.c: Variable substitutions
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35232633SmpRCSID("$tcsh: sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $")
3659243Sobrien
3759243Sobrien/*
3859243Sobrien * C shell
3959243Sobrien */
4059243Sobrien
4159243Sobrien/*
4259243Sobrien * These routines perform variable substitution and quoting via ' and ".
4359243Sobrien * To this point these constructs have been preserved in the divided
4459243Sobrien * input words.  Here we expand variables and turn quoting via ' and " into
4559243Sobrien * QUOTE bits on characters (which prevent further interpretation).
4659243Sobrien * If the `:q' modifier was applied during history expansion, then
4759243Sobrien * some QUOTEing may have occurred already, so we dont "trim()" here.
4859243Sobrien */
4959243Sobrien
50232633Smpstatic eChar Dpeekc;		/* Peek for DgetC */
51145479Smpstatic eChar Dpeekrd;		/* Peek for Dreadc */
52167465Smpstatic Char *Dcp, *const *Dvp;	/* Input vector for Dreadc */
5359243Sobrien
54145479Smp#define	DEOF	CHAR_ERR
5559243Sobrien
5659243Sobrien#define	unDgetC(c)	Dpeekc = c
5759243Sobrien
5859243Sobrien#define QUOTES		(_QF|_QB|_ESC)	/* \ ' " ` */
5959243Sobrien
6059243Sobrien/*
6159243Sobrien * The following variables give the information about the current
6259243Sobrien * $ expansion, recording the current word position, the remaining
6359243Sobrien * words within this expansion, the count of remaining words, and the
6459243Sobrien * information about any : modifier which is being applied.
6559243Sobrien */
6659243Sobrienstatic Char *dolp;		/* Remaining chars from this word */
6759243Sobrienstatic Char **dolnxt;		/* Further words */
6859243Sobrienstatic int dolcnt;		/* Count of further words */
69167465Smpstatic struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */
70167465Smpstatic int dolmcnt;		/* :gx -> INT_MAX, else 1 */
71167465Smpstatic int dol_flag_a;		/* :ax -> 1, else 0 */
7259243Sobrien
73167465Smpstatic	Char	 **Dfix2	(Char *const *);
74167465Smpstatic	int 	 Dpack		(struct Strbuf *);
75167465Smpstatic	int	 Dword		(struct blk_buf *);
76167465Smpstatic	void	 dolerror	(Char *);
77167465Smpstatic	eChar	 DgetC		(int);
78167465Smpstatic	void	 Dgetdol	(void);
79167465Smpstatic	void	 fixDolMod	(void);
80167465Smpstatic	void	 setDolp	(Char *);
81167465Smpstatic	void	 unDredc	(eChar);
82167465Smpstatic	eChar	 Dredc		(void);
83167465Smpstatic	void	 Dtestq		(Char);
8459243Sobrien
8559243Sobrien/*
8659243Sobrien * Fix up the $ expansions and quotations in the
8759243Sobrien * argument list to command t.
8859243Sobrien */
8959243Sobrienvoid
90167465SmpDfix(struct command *t)
9159243Sobrien{
92145479Smp    Char **pp;
93145479Smp    Char *p;
9459243Sobrien
9559243Sobrien    if (noexec)
9659243Sobrien	return;
9759243Sobrien    /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
9859243Sobrien    for (pp = t->t_dcom; (p = *pp++) != NULL;) {
9959243Sobrien	for (; *p; p++) {
10059243Sobrien	    if (cmap(*p, _DOL | QUOTES)) {	/* $, \, ', ", ` */
101167465Smp		Char **expanded;
102167465Smp
103167465Smp		expanded = Dfix2(t->t_dcom);	/* found one */
10459243Sobrien		blkfree(t->t_dcom);
105167465Smp		t->t_dcom = expanded;
10659243Sobrien		return;
10759243Sobrien	    }
10859243Sobrien	}
10959243Sobrien    }
11059243Sobrien}
11159243Sobrien
11259243Sobrien/*
11359243Sobrien * $ substitute one word, for i/o redirection
11459243Sobrien */
11559243SobrienChar   *
116167465SmpDfix1(Char *cp)
11759243Sobrien{
118167465Smp    Char *Dv[2], **expanded;
11959243Sobrien
12059243Sobrien    if (noexec)
12159243Sobrien	return (0);
12259243Sobrien    Dv[0] = cp;
12359243Sobrien    Dv[1] = NULL;
124167465Smp    expanded = Dfix2(Dv);
125167465Smp    if (expanded[0] == NULL || expanded[1] != NULL) {
126167465Smp	blkfree(expanded);
12759243Sobrien	setname(short2str(cp));
12859243Sobrien	stderror(ERR_NAME | ERR_AMBIG);
12959243Sobrien    }
130167465Smp    cp = Strsave(expanded[0]);
131167465Smp    blkfree(expanded);
13259243Sobrien    return (cp);
13359243Sobrien}
13459243Sobrien
13559243Sobrien/*
13659243Sobrien * Subroutine to do actual fixing after state initialization.
13759243Sobrien */
138167465Smpstatic Char **
139167465SmpDfix2(Char *const *v)
14059243Sobrien{
141195609Smp    struct blk_buf *bb = bb_alloc();
142195609Smp    Char **vec;
143167465Smp
14459243Sobrien    Dvp = v;
14559243Sobrien    Dcp = STRNULL;		/* Setup input vector for Dreadc */
14659243Sobrien    unDgetC(0);
14759243Sobrien    unDredc(0);			/* Clear out any old peeks (at error) */
14859243Sobrien    dolp = 0;
14959243Sobrien    dolcnt = 0;			/* Clear out residual $ expands (...) */
150195609Smp    cleanup_push(bb, bb_free);
151195609Smp    while (Dword(bb))
15259243Sobrien	continue;
153195609Smp    cleanup_ignore(bb);
154195609Smp    cleanup_until(bb);
155195609Smp    vec = bb_finish(bb);
156195609Smp    xfree(bb);
157195609Smp    return vec;
15859243Sobrien}
15959243Sobrien
16059243Sobrien/*
16159243Sobrien * Pack up more characters in this word
16259243Sobrien */
163167465Smpstatic int
164167465SmpDpack(struct Strbuf *wbuf)
16559243Sobrien{
166145479Smp    eChar c;
16759243Sobrien
16859243Sobrien    for (;;) {
16959243Sobrien	c = DgetC(DODOL);
17059243Sobrien	if (c == '\\') {
17159243Sobrien	    c = DgetC(0);
17259243Sobrien	    if (c == DEOF) {
17359243Sobrien		unDredc(c);
174167465Smp		return 1;
17559243Sobrien	    }
17659243Sobrien	    if (c == '\n')
17759243Sobrien		c = ' ';
17859243Sobrien	    else
17959243Sobrien		c |= QUOTE;
18059243Sobrien	}
18159243Sobrien	if (c == DEOF) {
18259243Sobrien	    unDredc(c);
183167465Smp	    return 1;
18459243Sobrien	}
18559243Sobrien	if (cmap(c, _SP | _NL | _QF | _QB)) {	/* sp \t\n'"` */
18659243Sobrien	    unDgetC(c);
18759243Sobrien	    if (cmap(c, QUOTES))
188167465Smp		return 0;
189167465Smp	    return 1;
19059243Sobrien	}
191167465Smp	Strbuf_append1(wbuf, (Char) c);
19259243Sobrien    }
19359243Sobrien}
19459243Sobrien
19559243Sobrien/*
19659243Sobrien * Get a word.  This routine is analogous to the routine
19759243Sobrien * word() in sh.lex.c for the main lexical input.  One difference
19859243Sobrien * here is that we don't get a newline to terminate our expansion.
19959243Sobrien * Rather, DgetC will return a DEOF when we hit the end-of-input.
20059243Sobrien */
20159243Sobrienstatic int
202167465SmpDword(struct blk_buf *bb)
20359243Sobrien{
204145479Smp    eChar c, c1;
205195609Smp    struct Strbuf *wbuf = Strbuf_alloc();
206145479Smp    int dolflg;
207167465Smp    int    sofar = 0;
208195609Smp    Char *str;
20959243Sobrien
210195609Smp    cleanup_push(wbuf, Strbuf_free);
211167465Smp    for (;;) {
21259243Sobrien	c = DgetC(DODOL);
21359243Sobrien	switch (c) {
21459243Sobrien
21559243Sobrien	case DEOF:
216167465Smp	    if (sofar == 0) {
217195609Smp		cleanup_until(wbuf);
21859243Sobrien		return (0);
219167465Smp	    }
22059243Sobrien	    /* finish this word and catch the code above the next time */
22159243Sobrien	    unDredc(c);
22259243Sobrien	    /*FALLTHROUGH*/
22359243Sobrien
22459243Sobrien	case '\n':
225167465Smp	    goto end;
22659243Sobrien
22759243Sobrien	case ' ':
22859243Sobrien	case '\t':
229167465Smp	    continue;
23059243Sobrien
23159243Sobrien	case '`':
23259243Sobrien	    /* We preserve ` quotations which are done yet later */
233195609Smp	    Strbuf_append1(wbuf, (Char) c);
23459243Sobrien	    /*FALLTHROUGH*/
23559243Sobrien	case '\'':
23659243Sobrien	case '"':
23759243Sobrien	    /*
23859243Sobrien	     * Note that DgetC never returns a QUOTES character from an
23959243Sobrien	     * expansion, so only true input quotes will get us here or out.
24059243Sobrien	     */
24159243Sobrien	    c1 = c;
24259243Sobrien	    dolflg = c1 == '"' ? DODOL : 0;
24359243Sobrien	    for (;;) {
24459243Sobrien		c = DgetC(dolflg);
24559243Sobrien		if (c == c1)
24659243Sobrien		    break;
247195609Smp		if (c == '\n' || c == DEOF) {
248195609Smp		    cleanup_until(bb);
249145479Smp		    stderror(ERR_UNMATCHED, (int)c1);
250195609Smp		}
25159243Sobrien		if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
252195609Smp		    if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\')
253195609Smp			wbuf->len--;
25459243Sobrien		}
25559243Sobrien		switch (c1) {
25659243Sobrien
25759243Sobrien		case '"':
25859243Sobrien		    /*
25959243Sobrien		     * Leave any `s alone for later. Other chars are all
26059243Sobrien		     * quoted, thus `...` can tell it was within "...".
26159243Sobrien		     */
262195609Smp		    Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE);
26359243Sobrien		    break;
26459243Sobrien
26559243Sobrien		case '\'':
26659243Sobrien		    /* Prevent all further interpretation */
267195609Smp		    Strbuf_append1(wbuf, c | QUOTE);
26859243Sobrien		    break;
26959243Sobrien
27059243Sobrien		case '`':
27159243Sobrien		    /* Leave all text alone for later */
272195609Smp		    Strbuf_append1(wbuf, (Char) c);
27359243Sobrien		    break;
27459243Sobrien
27559243Sobrien		default:
27659243Sobrien		    break;
27759243Sobrien		}
27859243Sobrien	    }
27959243Sobrien	    if (c1 == '`')
280195609Smp		Strbuf_append1(wbuf, '`');
28159243Sobrien	    sofar = 1;
282195609Smp	    if (Dpack(wbuf) != 0)
283167465Smp		goto end;
284167465Smp	    continue;
28559243Sobrien
28659243Sobrien	case '\\':
28759243Sobrien	    c = DgetC(0);	/* No $ subst! */
288167465Smp	    if (c == '\n' || c == DEOF)
289167465Smp		continue;
29059243Sobrien	    c |= QUOTE;
29159243Sobrien	    break;
29259243Sobrien
29359243Sobrien	default:
29459243Sobrien	    break;
29559243Sobrien	}
296167465Smp	unDgetC(c);
297167465Smp	sofar = 1;
298195609Smp	if (Dpack(wbuf) != 0)
299167465Smp	    goto end;
30059243Sobrien    }
301167465Smp
302167465Smp end:
303195609Smp    cleanup_ignore(wbuf);
304195609Smp    cleanup_until(wbuf);
305195609Smp    str = Strbuf_finish(wbuf);
306195609Smp    bb_append(bb, str);
307195609Smp    xfree(wbuf);
308167465Smp    return 1;
30959243Sobrien}
31059243Sobrien
31159243Sobrien
31259243Sobrien/*
31359243Sobrien * Get a character, performing $ substitution unless flag is 0.
31459243Sobrien * Any QUOTES character which is returned from a $ expansion is
31559243Sobrien * QUOTEd so that it will not be recognized above.
31659243Sobrien */
317145479Smpstatic eChar
318167465SmpDgetC(int flag)
31959243Sobrien{
320232633Smp    eChar c;
32159243Sobrien
32259243Sobrientop:
32359243Sobrien    if ((c = Dpeekc) != 0) {
32459243Sobrien	Dpeekc = 0;
32559243Sobrien	return (c);
32659243Sobrien    }
327167465Smp    if (lap < labuf.len) {
328167465Smp	c = labuf.s[lap++] & (QUOTE | TRIM);
32959243Sobrienquotspec:
33059243Sobrien	if (cmap(c, QUOTES))
33159243Sobrien	    return (c | QUOTE);
33259243Sobrien	return (c);
33359243Sobrien    }
33459243Sobrien    if (dolp) {
33559243Sobrien	if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
33659243Sobrien	    goto quotspec;
33759243Sobrien	if (dolcnt > 0) {
33859243Sobrien	    setDolp(*dolnxt++);
33959243Sobrien	    --dolcnt;
34059243Sobrien	    return (' ');
34159243Sobrien	}
34259243Sobrien	dolp = 0;
34359243Sobrien    }
34459243Sobrien    if (dolcnt > 0) {
34559243Sobrien	setDolp(*dolnxt++);
34659243Sobrien	--dolcnt;
34759243Sobrien	goto top;
34859243Sobrien    }
34959243Sobrien    c = Dredc();
35059243Sobrien    if (c == '$' && flag) {
35159243Sobrien	Dgetdol();
35259243Sobrien	goto top;
35359243Sobrien    }
35459243Sobrien    return (c);
35559243Sobrien}
35659243Sobrien
35759243Sobrienstatic Char *nulvec[] = { NULL };
35859243Sobrienstatic struct varent nulargv = {nulvec, STRargv, VAR_READWRITE,
35959243Sobrien				{ NULL, NULL, NULL }, 0 };
36059243Sobrien
36159243Sobrienstatic void
362167465Smpdolerror(Char *s)
36359243Sobrien{
36459243Sobrien    setname(short2str(s));
36559243Sobrien    stderror(ERR_NAME | ERR_RANGE);
36659243Sobrien}
36759243Sobrien
36859243Sobrien/*
36959243Sobrien * Handle the multitudinous $ expansion forms.
37059243Sobrien * Ugh.
37159243Sobrien */
37259243Sobrienstatic void
373167465SmpDgetdol(void)
37459243Sobrien{
375145479Smp    Char *np;
376145479Smp    struct varent *vp = NULL;
377195609Smp    struct Strbuf *name = Strbuf_alloc();
378145479Smp    eChar   c, sc;
37959243Sobrien    int     subscr = 0, lwb = 1, upb = 0;
380145479Smp    int    dimen = 0, bitset = 0, length = 0;
38159243Sobrien    static Char *dolbang = NULL;
38259243Sobrien
383195609Smp    cleanup_push(name, Strbuf_free);
384167465Smp    dolmod.len = dolmcnt = dol_flag_a = 0;
38559243Sobrien    c = sc = DgetC(0);
386167465Smp    if (c == DEOF) {
387167465Smp      stderror(ERR_SYNTAX);
388167465Smp      return;
389167465Smp    }
39059243Sobrien    if (c == '{')
39159243Sobrien	c = DgetC(0);		/* sc is { to take } later */
39259243Sobrien    if ((c & TRIM) == '#')
39359243Sobrien	dimen++, c = DgetC(0);	/* $# takes dimension */
39459243Sobrien    else if (c == '?')
39559243Sobrien	bitset++, c = DgetC(0);	/* $? tests existence */
39659243Sobrien    else if (c == '%')
39759243Sobrien	length++, c = DgetC(0); /* $% returns length in chars */
39859243Sobrien    switch (c) {
39959243Sobrien
40059243Sobrien    case '!':
40159243Sobrien	if (dimen || bitset || length)
40259243Sobrien	    stderror(ERR_SYNTAX);
40359243Sobrien	if (backpid != 0) {
404167465Smp	    xfree(dolbang);
405232633Smp	    setDolp(dolbang = putn((tcsh_number_t)backpid));
40659243Sobrien	}
407195609Smp	cleanup_until(name);
40859243Sobrien	goto eatbrac;
40959243Sobrien
41059243Sobrien    case '$':
41159243Sobrien	if (dimen || bitset || length)
41259243Sobrien	    stderror(ERR_SYNTAX);
41359243Sobrien	setDolp(doldol);
414195609Smp	cleanup_until(name);
41559243Sobrien	goto eatbrac;
41659243Sobrien
417167465Smp    case '<'|QUOTE: {
418167465Smp	static struct Strbuf wbuf; /* = Strbuf_INIT; */
419167465Smp
42059243Sobrien	if (bitset)
42159243Sobrien	    stderror(ERR_NOTALLOWED, "$?<");
42259243Sobrien	if (dimen)
42359243Sobrien	    stderror(ERR_NOTALLOWED, "$#<");
42459243Sobrien	if (length)
42559243Sobrien	    stderror(ERR_NOTALLOWED, "$%<");
426167465Smp	wbuf.len = 0;
42759243Sobrien	{
428145479Smp	    char cbuf[MB_LEN_MAX];
429145479Smp	    size_t cbp = 0;
430167465Smp	    int old_pintr_disabled;
431145479Smp
432167465Smp	    for (;;) {
433145479Smp	        int len;
434167465Smp		ssize_t res;
435167465Smp		Char wc;
436145479Smp
437167465Smp		pintr_push_enable(&old_pintr_disabled);
438167465Smp		res = force_read(OLDSTD, cbuf + cbp, 1);
439167465Smp		cleanup_until(&old_pintr_disabled);
440167465Smp		if (res != 1)
441167465Smp		    break;
442167465Smp		cbp++;
443167465Smp		len = normal_mbtowc(&wc, cbuf, cbp);
444145479Smp		if (len == -1) {
445145479Smp		    reset_mbtowc();
446145479Smp		    if (cbp < MB_LEN_MAX)
447145479Smp		        continue; /* Maybe a partial character */
448167465Smp		    wc = (unsigned char)*cbuf | INVALID_BYTE;
449145479Smp		}
450145479Smp		if (len <= 0)
451145479Smp		    len = 1;
452145479Smp		if (cbp != (size_t)len)
453145479Smp		    memmove(cbuf, cbuf + len, cbp - len);
454145479Smp		cbp -= len;
455167465Smp		if (wc == '\n')
45659243Sobrien		    break;
457167465Smp		Strbuf_append1(&wbuf, wc);
45859243Sobrien	    }
459145479Smp	    while (cbp != 0) {
460167465Smp		int len;
461167465Smp		Char wc;
462167465Smp
463167465Smp		len = normal_mbtowc(&wc, cbuf, cbp);
464167465Smp		if (len == -1) {
465167465Smp		    reset_mbtowc();
466167465Smp		    wc = (unsigned char)*cbuf | INVALID_BYTE;
467167465Smp		}
468167465Smp		if (len <= 0)
469167465Smp		    len = 1;
470167465Smp		if (cbp != (size_t)len)
471167465Smp		    memmove(cbuf, cbuf + len, cbp - len);
472167465Smp		cbp -= len;
473167465Smp		if (wc == '\n')
474145479Smp		    break;
475167465Smp		Strbuf_append1(&wbuf, wc);
476145479Smp	    }
477167465Smp	    Strbuf_terminate(&wbuf);
47859243Sobrien	}
47959243Sobrien
48059243Sobrien	fixDolMod();
481167465Smp	setDolp(wbuf.s); /* Kept allocated until next $< expansion */
482195609Smp	cleanup_until(name);
48359243Sobrien	goto eatbrac;
484167465Smp    }
48559243Sobrien
48659243Sobrien    case '*':
487195609Smp	Strbuf_append(name, STRargv);
488195609Smp	Strbuf_terminate(name);
48959243Sobrien	vp = adrof(STRargv);
49059243Sobrien	subscr = -1;		/* Prevent eating [...] */
49159243Sobrien	break;
49259243Sobrien
49359243Sobrien    case DEOF:
49459243Sobrien    case '\n':
49559243Sobrien	np = dimen ? STRargv : (bitset ? STRstatus : NULL);
49659243Sobrien	if (np) {
49759243Sobrien	    bitset = 0;
498195609Smp	    Strbuf_append(name, np);
499195609Smp	    Strbuf_terminate(name);
50059243Sobrien	    vp = adrof(np);
50159243Sobrien	    subscr = -1;		/* Prevent eating [...] */
50259243Sobrien	    unDredc(c);
50359243Sobrien	    break;
50459243Sobrien	}
50559243Sobrien	else
50659243Sobrien	    stderror(ERR_SYNTAX);
50759243Sobrien	/*NOTREACHED*/
50859243Sobrien
50959243Sobrien    default:
51059243Sobrien	if (Isdigit(c)) {
51159243Sobrien	    if (dimen)
51259243Sobrien		stderror(ERR_NOTALLOWED, "$#<num>");
51359243Sobrien	    subscr = 0;
51459243Sobrien	    do {
51559243Sobrien		subscr = subscr * 10 + c - '0';
51659243Sobrien		c = DgetC(0);
517167465Smp	    } while (c != DEOF && Isdigit(c));
51859243Sobrien	    unDredc(c);
519100616Smp	    if (subscr < 0)
520100616Smp		stderror(ERR_RANGE);
52159243Sobrien	    if (subscr == 0) {
52259243Sobrien		if (bitset) {
52359243Sobrien		    dolp = dolzero ? STR1 : STR0;
524195609Smp		    cleanup_until(name);
52559243Sobrien		    goto eatbrac;
52659243Sobrien		}
52759243Sobrien		if (ffile == 0)
52859243Sobrien		    stderror(ERR_DOLZERO);
52959243Sobrien		if (length) {
530167465Smp		    length = Strlen(ffile);
531232633Smp		    addla(putn((tcsh_number_t)length));
53259243Sobrien		}
53359243Sobrien		else {
53459243Sobrien		    fixDolMod();
53559243Sobrien		    setDolp(ffile);
53659243Sobrien		}
537195609Smp		cleanup_until(name);
53859243Sobrien		goto eatbrac;
53959243Sobrien	    }
54059243Sobrien#if 0
54159243Sobrien	    if (bitset)
54259243Sobrien		stderror(ERR_NOTALLOWED, "$?<num>");
54359243Sobrien	    if (length)
54459243Sobrien		stderror(ERR_NOTALLOWED, "$%<num>");
54559243Sobrien#endif
54659243Sobrien	    vp = adrof(STRargv);
54759243Sobrien	    if (vp == 0) {
54859243Sobrien		vp = &nulargv;
549195609Smp		cleanup_until(name);
55059243Sobrien		goto eatmod;
55159243Sobrien	    }
55259243Sobrien	    break;
55359243Sobrien	}
554167465Smp	if (c == DEOF || !alnum(c)) {
55559243Sobrien	    np = dimen ? STRargv : (bitset ? STRstatus : NULL);
55659243Sobrien	    if (np) {
55759243Sobrien		bitset = 0;
558195609Smp		Strbuf_append(name, np);
559195609Smp		Strbuf_terminate(name);
56059243Sobrien		vp = adrof(np);
56159243Sobrien		subscr = -1;		/* Prevent eating [...] */
56259243Sobrien		unDredc(c);
56359243Sobrien		break;
56459243Sobrien	    }
56559243Sobrien	    else
56659243Sobrien		stderror(ERR_VARALNUM);
56759243Sobrien	}
56859243Sobrien	for (;;) {
569195609Smp	    Strbuf_append1(name, (Char) c);
57059243Sobrien	    c = DgetC(0);
571167465Smp	    if (c == DEOF || !alnum(c))
57259243Sobrien		break;
57359243Sobrien	}
574195609Smp	Strbuf_terminate(name);
57559243Sobrien	unDredc(c);
576195609Smp	vp = adrof(name->s);
57759243Sobrien    }
57859243Sobrien    if (bitset) {
579195609Smp	dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0;
580195609Smp	cleanup_until(name);
58159243Sobrien	goto eatbrac;
58259243Sobrien    }
583100616Smp    if (vp == NULL || vp->vec == NULL) {
584195609Smp	np = str2short(getenv(short2str(name->s)));
58559243Sobrien	if (np) {
586167465Smp	    static Char *env_val; /* = NULL; */
587167465Smp
588195609Smp	    cleanup_until(name);
58959243Sobrien	    fixDolMod();
590195609Smp	    if (length) {
591232633Smp		    addla(putn((tcsh_number_t)Strlen(np)));
592195609Smp	    } else {
593195609Smp		    xfree(env_val);
594195609Smp		    env_val = Strsave(np);
595195609Smp		    setDolp(env_val);
596195609Smp	    }
59759243Sobrien	    goto eatbrac;
59859243Sobrien	}
599195609Smp	udvar(name->s);
60059243Sobrien	/* NOTREACHED */
60159243Sobrien    }
602195609Smp    cleanup_until(name);
60359243Sobrien    c = DgetC(0);
60459243Sobrien    upb = blklen(vp->vec);
60559243Sobrien    if (dimen == 0 && subscr == 0 && c == '[') {
606195609Smp	name = Strbuf_alloc();
607195609Smp	cleanup_push(name, Strbuf_free);
608195609Smp	np = name->s;
60959243Sobrien	for (;;) {
61059243Sobrien	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
61159243Sobrien	    if (c == ']')
61259243Sobrien		break;
61359243Sobrien	    if (c == '\n' || c == DEOF)
61459243Sobrien		stderror(ERR_INCBR);
615195609Smp	    Strbuf_append1(name, (Char) c);
61659243Sobrien	}
617195609Smp	Strbuf_terminate(name);
618195609Smp	np = name->s;
61959243Sobrien	if (dolp || dolcnt)	/* $ exp must end before ] */
62059243Sobrien	    stderror(ERR_EXPORD);
62159243Sobrien	if (!*np)
62259243Sobrien	    stderror(ERR_SYNTAX);
62359243Sobrien	if (Isdigit(*np)) {
62459243Sobrien	    int     i;
62559243Sobrien
62659243Sobrien	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
62759243Sobrien		continue;
628232633Smp	    if (i < 0 || (i > upb && !any("-*", *np))) {
629195609Smp		cleanup_until(name);
63059243Sobrien		dolerror(vp->v_name);
63159243Sobrien		return;
63259243Sobrien	    }
63359243Sobrien	    lwb = i;
63459243Sobrien	    if (!*np)
63559243Sobrien		upb = lwb, np = STRstar;
63659243Sobrien	}
63759243Sobrien	if (*np == '*')
63859243Sobrien	    np++;
63959243Sobrien	else if (*np != '-')
64059243Sobrien	    stderror(ERR_MISSING, '-');
64159243Sobrien	else {
642145479Smp	    int i = upb;
64359243Sobrien
64459243Sobrien	    np++;
64559243Sobrien	    if (Isdigit(*np)) {
64659243Sobrien		i = 0;
64759243Sobrien		while (Isdigit(*np))
64859243Sobrien		    i = i * 10 + *np++ - '0';
64959243Sobrien		if (i < 0 || i > upb) {
650195609Smp		    cleanup_until(name);
65159243Sobrien		    dolerror(vp->v_name);
65259243Sobrien		    return;
65359243Sobrien		}
65459243Sobrien	    }
65559243Sobrien	    if (i < lwb)
65659243Sobrien		upb = lwb - 1;
65759243Sobrien	    else
65859243Sobrien		upb = i;
65959243Sobrien	}
66059243Sobrien	if (lwb == 0) {
66159243Sobrien	    if (upb != 0) {
662195609Smp		cleanup_until(name);
66359243Sobrien		dolerror(vp->v_name);
66459243Sobrien		return;
66559243Sobrien	    }
66659243Sobrien	    upb = -1;
66759243Sobrien	}
66859243Sobrien	if (*np)
66959243Sobrien	    stderror(ERR_SYNTAX);
670195609Smp	cleanup_until(name);
67159243Sobrien    }
67259243Sobrien    else {
67359243Sobrien	if (subscr > 0) {
67459243Sobrien	    if (subscr > upb)
67559243Sobrien		lwb = 1, upb = 0;
67659243Sobrien	    else
67759243Sobrien		lwb = upb = subscr;
67859243Sobrien	}
67959243Sobrien	unDredc(c);
68059243Sobrien    }
68159243Sobrien    if (dimen) {
68259243Sobrien	/* this is a kludge. It prevents Dgetdol() from */
68359243Sobrien	/* pushing erroneous ${#<error> values into the labuf. */
68459243Sobrien	if (sc == '{') {
68559243Sobrien	    c = Dredc();
68659243Sobrien	    if (c != '}')
68759243Sobrien		stderror(ERR_MISSING, '}');
68859243Sobrien	    unDredc(c);
68959243Sobrien	}
690232633Smp	addla(putn((tcsh_number_t)(upb - lwb + 1)));
69159243Sobrien    }
69259243Sobrien    else if (length) {
69359243Sobrien	int i;
694167465Smp
69559243Sobrien	for (i = lwb - 1, length = 0; i < upb; i++)
696167465Smp	    length += Strlen(vp->vec[i]);
69759243Sobrien#ifdef notdef
69859243Sobrien	/* We don't want that, since we can always compute it by adding $#xxx */
69959243Sobrien	length += i - 1;	/* Add the number of spaces in */
70059243Sobrien#endif
701232633Smp	addla(putn((tcsh_number_t)length));
70259243Sobrien    }
70359243Sobrien    else {
70459243Sobrieneatmod:
70559243Sobrien	fixDolMod();
70659243Sobrien	dolnxt = &vp->vec[lwb - 1];
70759243Sobrien	dolcnt = upb - lwb + 1;
70859243Sobrien    }
70959243Sobrieneatbrac:
71059243Sobrien    if (sc == '{') {
71159243Sobrien	c = Dredc();
71259243Sobrien	if (c != '}')
71359243Sobrien	    stderror(ERR_MISSING, '}');
71459243Sobrien    }
71559243Sobrien}
71659243Sobrien
71759243Sobrienstatic void
718167465SmpfixDolMod(void)
71959243Sobrien{
720145479Smp    eChar c;
72159243Sobrien
72259243Sobrien    c = DgetC(0);
72359243Sobrien    if (c == ':') {
72459243Sobrien	do {
725167465Smp	    c = DgetC(0), dolmcnt = 1, dol_flag_a = 0;
72659243Sobrien	    if (c == 'g' || c == 'a') {
72759243Sobrien		if (c == 'g')
728167465Smp		    dolmcnt = INT_MAX;
72959243Sobrien		else
730167465Smp		    dol_flag_a = 1;
73159243Sobrien		c = DgetC(0);
73259243Sobrien	    }
733167465Smp	    if ((c == 'g' && dolmcnt != INT_MAX) ||
734167465Smp		(c == 'a' && dol_flag_a == 0)) {
73559243Sobrien		if (c == 'g')
736167465Smp		    dolmcnt = INT_MAX;
73759243Sobrien		else
738167465Smp		    dol_flag_a = 1;
739167465Smp		c = DgetC(0);
74059243Sobrien	    }
74159243Sobrien
74259243Sobrien	    if (c == 's') {	/* [eichin:19910926.0755EST] */
74359243Sobrien		int delimcnt = 2;
744145479Smp		eChar delim = DgetC(0);
745167465Smp		Strbuf_append1(&dolmod, (Char) c);
746167465Smp		Strbuf_append1(&dolmod, (Char) delim);
747167465Smp
748167465Smp		if (delim == DEOF || !delim || letter(delim)
74959243Sobrien		    || Isdigit(delim) || any(" \t\n", delim)) {
75059243Sobrien		    seterror(ERR_BADSUBST);
75159243Sobrien		    break;
75259243Sobrien		}
753145479Smp		while ((c = DgetC(0)) != DEOF) {
754167465Smp		    Strbuf_append1(&dolmod, (Char) c);
75559243Sobrien		    if(c == delim) delimcnt--;
75659243Sobrien		    if(!delimcnt) break;
75759243Sobrien		}
75859243Sobrien		if(delimcnt) {
75959243Sobrien		    seterror(ERR_BADSUBST);
76059243Sobrien		    break;
76159243Sobrien		}
76259243Sobrien		continue;
76359243Sobrien	    }
76459243Sobrien	    if (!any("luhtrqxes", c))
765145479Smp		stderror(ERR_BADMOD, (int)c);
766167465Smp	    Strbuf_append1(&dolmod, (Char) c);
76759243Sobrien	    if (c == 'q')
768167465Smp		dolmcnt = INT_MAX;
76959243Sobrien	}
77059243Sobrien	while ((c = DgetC(0)) == ':');
77159243Sobrien	unDredc(c);
77259243Sobrien    }
77359243Sobrien    else
77459243Sobrien	unDredc(c);
77559243Sobrien}
77659243Sobrien
77759243Sobrienstatic void
778167465SmpsetDolp(Char *cp)
77959243Sobrien{
780145479Smp    Char *dp;
781167465Smp    size_t i;
78259243Sobrien
783167465Smp    if (dolmod.len == 0 || dolmcnt == 0) {
78459243Sobrien	dolp = cp;
78559243Sobrien	return;
78659243Sobrien    }
787167465Smp    cp = Strsave(cp);
788167465Smp    for (i = 0; i < dolmod.len; i++) {
789167465Smp	int didmod = 0;
790167465Smp
79159243Sobrien	/* handle s// [eichin:19910926.0510EST] */
792167465Smp	if(dolmod.s[i] == 's') {
793145479Smp	    Char delim;
79459243Sobrien	    Char *lhsub, *rhsub, *np;
79559243Sobrien	    size_t lhlen = 0, rhlen = 0;
796167465Smp
797167465Smp	    delim = dolmod.s[++i];
79859243Sobrien	    if (!delim || letter(delim)
79959243Sobrien		|| Isdigit(delim) || any(" \t\n", delim)) {
80059243Sobrien		seterror(ERR_BADSUBST);
80159243Sobrien		break;
80259243Sobrien	    }
803167465Smp	    lhsub = &dolmod.s[++i];
804167465Smp	    while(dolmod.s[i] != delim && dolmod.s[++i]) {
80559243Sobrien		lhlen++;
80659243Sobrien	    }
807167465Smp	    dolmod.s[i] = 0;
808167465Smp	    rhsub = &dolmod.s[++i];
809167465Smp	    while(dolmod.s[i] != delim && dolmod.s[++i]) {
81059243Sobrien		rhlen++;
81159243Sobrien	    }
812167465Smp	    dolmod.s[i] = 0;
81359243Sobrien
814167465Smp	    strip(lhsub);
815195609Smp	    strip(rhsub);
816167465Smp	    strip(cp);
817167465Smp	    dp = cp;
81859243Sobrien	    do {
819167465Smp		dp = Strstr(dp, lhsub);
82059243Sobrien		if (dp) {
821167465Smp		    ptrdiff_t diff = dp - cp;
822195609Smp		    size_t len = (Strlen(cp) + 1 - lhlen + rhlen);
823195609Smp		    np = xmalloc(len * sizeof(Char));
824167465Smp		    (void) Strncpy(np, cp, diff);
825167465Smp		    (void) Strcpy(np + diff, rhsub);
826167465Smp		    (void) Strcpy(np + diff + rhlen, dp + lhlen);
82759243Sobrien
828167465Smp		    dp = np + diff + 1;
829167465Smp		    xfree(cp);
830167465Smp		    cp = np;
831195609Smp		    cp[--len] = '\0';
83259243Sobrien		    didmod = 1;
833232633Smp		    if (diff >= (ssize_t)len)
834195609Smp			break;
83559243Sobrien		} else {
83659243Sobrien		    /* should this do a seterror? */
83759243Sobrien		    break;
83859243Sobrien		}
83959243Sobrien	    }
840167465Smp	    while (dol_flag_a != 0);
84159243Sobrien	    /*
84259243Sobrien	     * restore dolmod for additional words
84359243Sobrien	     */
844167465Smp	    dolmod.s[i] = rhsub[-1] = (Char) delim;
84559243Sobrien        } else {
84659243Sobrien
84759243Sobrien	    do {
848167465Smp		if ((dp = domod(cp, dolmod.s[i])) != NULL) {
84959243Sobrien		    didmod = 1;
85059243Sobrien		    if (Strcmp(cp, dp) == 0) {
851167465Smp			xfree(cp);
85259243Sobrien			cp = dp;
85359243Sobrien			break;
85459243Sobrien		    }
85559243Sobrien		    else {
856167465Smp			xfree(cp);
85759243Sobrien			cp = dp;
85859243Sobrien		    }
85959243Sobrien		}
86059243Sobrien		else
86159243Sobrien		    break;
86259243Sobrien	    }
863167465Smp	    while (dol_flag_a != 0);
864167465Smp	}
865167465Smp	if (didmod && dolmcnt != INT_MAX)
866167465Smp	    dolmcnt--;
86759243Sobrien#ifdef notdef
868167465Smp	else
869167465Smp	    break;
87059243Sobrien#endif
87159243Sobrien    }
87259243Sobrien
873167465Smp    addla(cp);
87459243Sobrien
87559243Sobrien    dolp = STRNULL;
87659243Sobrien    if (seterr)
87759243Sobrien	stderror(ERR_OLD);
87859243Sobrien}
87959243Sobrien
88059243Sobrienstatic void
881167465SmpunDredc(eChar c)
88259243Sobrien{
88359243Sobrien
88459243Sobrien    Dpeekrd = c;
88559243Sobrien}
88659243Sobrien
887145479Smpstatic eChar
888167465SmpDredc(void)
88959243Sobrien{
890232633Smp    eChar c;
89159243Sobrien
89259243Sobrien    if ((c = Dpeekrd) != 0) {
89359243Sobrien	Dpeekrd = 0;
89459243Sobrien	return (c);
89559243Sobrien    }
89659243Sobrien    if (Dcp && (c = *Dcp++))
89759243Sobrien	return (c & (QUOTE | TRIM));
89859243Sobrien    if (*Dvp == 0) {
89959243Sobrien	Dcp = 0;
90059243Sobrien	return (DEOF);
90159243Sobrien    }
90259243Sobrien    Dcp = *Dvp++;
90359243Sobrien    return (' ');
90459243Sobrien}
90559243Sobrien
906167465Smpstatic int gflag;
907167465Smp
90859243Sobrienstatic void
909167465SmpDtestq(Char c)
91059243Sobrien{
91159243Sobrien
91259243Sobrien    if (cmap(c, QUOTES))
91359243Sobrien	gflag = 1;
91459243Sobrien}
91559243Sobrien
916167465Smpstatic void
917167465Smpinheredoc_cleanup(void *dummy)
918167465Smp{
919167465Smp    USE(dummy);
920167465Smp    inheredoc = 0;
921167465Smp}
922167465Smp
92359243Sobrien/*
92459243Sobrien * Form a shell temporary file (in unit 0) from the words
92559243Sobrien * of the shell input up to EOF or a line the same as "term".
92659243Sobrien * Unit 0 should have been closed before this call.
92759243Sobrien */
92859243Sobrienvoid
929167465Smpheredoc(Char *term)
93059243Sobrien{
931145479Smp    eChar  c;
93259243Sobrien    Char   *Dv[2];
933167465Smp    struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
934167465Smp    Char    obuf[BUFSIZE + 1];
935167465Smp#define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
936145479Smp    Char *lbp, *obp, *mbp;
93759243Sobrien    Char  **vp;
938145479Smp    int    quoted;
939232633Smp#ifdef HAVE_MKSTEMP
940232633Smp    char   *tmp = short2str(shtemp);
941232633Smp    char   *dot = strrchr(tmp, '.');
942232633Smp
943232633Smp    if (!dot)
944232633Smp	stderror(ERR_NAME | ERR_NOMATCH);
945232633Smp    strcpy(dot, TMP_TEMPLATE);
946232633Smp
947232633Smp    xclose(0);
948232633Smp    if (mkstemp(tmp) == -1)
949232633Smp	stderror(ERR_SYSTEM, tmp, strerror(errno));
950232633Smp#else /* !HAVE_MKSTEMP */
95159243Sobrien    char   *tmp;
952232633Smp# ifndef WINNT_NATIVE
95368332Skris    struct timeval tv;
95459243Sobrien
95568332Skrisagain:
956232633Smp# endif /* WINNT_NATIVE */
95759243Sobrien    tmp = short2str(shtemp);
958232633Smp# if O_CREAT == 0
959167465Smp    if (xcreat(tmp, 0600) < 0)
96059243Sobrien	stderror(ERR_SYSTEM, tmp, strerror(errno));
961232633Smp# endif
962167465Smp    xclose(0);
963167465Smp    if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
964167465Smp	-1) {
96568332Skris	int oerrno = errno;
966232633Smp# ifndef WINNT_NATIVE
96768332Skris	if (errno == EEXIST) {
96868332Skris	    if (unlink(tmp) == -1) {
96968332Skris		(void) gettimeofday(&tv, NULL);
970167465Smp		xfree(shtemp);
971232633Smp		mbp = putn((((tcsh_number_t)tv.tv_sec) ^
972232633Smp		    ((tcsh_number_t)tv.tv_usec) ^
973232633Smp		    ((tcsh_number_t)getpid())) & 0x00ffffff);
974167465Smp		shtemp = Strspl(STRtmpsh, mbp);
975167465Smp		xfree(mbp);
97668332Skris	    }
97768332Skris	    goto again;
97868332Skris	}
979232633Smp# endif /* WINNT_NATIVE */
98059243Sobrien	(void) unlink(tmp);
98159243Sobrien	errno = oerrno;
98268332Skris 	stderror(ERR_SYSTEM, tmp, strerror(errno));
98359243Sobrien    }
984232633Smp#endif /* HAVE_MKSTEMP */
98559243Sobrien    (void) unlink(tmp);		/* 0 0 inode! */
98659243Sobrien    Dv[0] = term;
98759243Sobrien    Dv[1] = NULL;
98859243Sobrien    gflag = 0;
98959243Sobrien    trim(Dv);
99059243Sobrien    rscan(Dv, Dtestq);
99159243Sobrien    quoted = gflag;
99259243Sobrien    obp = obuf;
993145479Smp    obuf[BUFSIZE] = 0;
99459243Sobrien    inheredoc = 1;
995167465Smp    cleanup_push(&inheredoc, inheredoc_cleanup);
99669408Sache#ifdef WINNT_NATIVE
99759243Sobrien    __dup_stdin = 1;
99869408Sache#endif /* WINNT_NATIVE */
999167465Smp    cleanup_push(&lbuf, Strbuf_cleanup);
1000167465Smp    cleanup_push(&mbuf, Strbuf_cleanup);
100159243Sobrien    for (;;) {
1002167465Smp	Char **words;
1003167465Smp
100459243Sobrien	/*
100559243Sobrien	 * Read up a line
100659243Sobrien	 */
1007167465Smp	lbuf.len = 0;
100859243Sobrien	for (;;) {
100959243Sobrien	    c = readc(1);	/* 1 -> Want EOF returns */
1010145479Smp	    if (c == CHAR_ERR || c == '\n')
101159243Sobrien		break;
1012167465Smp	    if ((c &= TRIM) != 0)
1013167465Smp		Strbuf_append1(&lbuf, (Char) c);
101459243Sobrien	}
1015167465Smp	Strbuf_terminate(&lbuf);
101659243Sobrien
1017232633Smp	/* Catch EOF in the middle of a line. */
1018232633Smp	if (c == CHAR_ERR && lbuf.len != 0)
1019232633Smp	    c = '\n';
1020232633Smp
102159243Sobrien	/*
102259243Sobrien	 * Check for EOF or compare to terminator -- before expansion
102359243Sobrien	 */
1024167465Smp	if (c == CHAR_ERR || eq(lbuf.s, term))
1025167465Smp	    break;
102659243Sobrien
102759243Sobrien	/*
102859243Sobrien	 * If term was quoted or -n just pass it on
102959243Sobrien	 */
103059243Sobrien	if (quoted || noexec) {
1031167465Smp	    Strbuf_append1(&lbuf, '\n');
1032167465Smp	    Strbuf_terminate(&lbuf);
1033167465Smp	    for (lbp = lbuf.s; (c = *lbp++) != 0;) {
103459243Sobrien		*obp++ = (Char) c;
1035167465Smp		if (obp == OBUF_END) {
1036145479Smp		    tmp = short2str(obuf);
1037167465Smp		    (void) xwrite(0, tmp, strlen (tmp));
103859243Sobrien		    obp = obuf;
103959243Sobrien		}
104059243Sobrien	    }
104159243Sobrien	    continue;
104259243Sobrien	}
104359243Sobrien
104459243Sobrien	/*
104559243Sobrien	 * Term wasn't quoted so variable and then command expand the input
104659243Sobrien	 * line
104759243Sobrien	 */
1048167465Smp	Dcp = lbuf.s;
104959243Sobrien	Dvp = Dv + 1;
1050167465Smp	mbuf.len = 0;
105159243Sobrien	for (;;) {
105259243Sobrien	    c = DgetC(DODOL);
105359243Sobrien	    if (c == DEOF)
105459243Sobrien		break;
105559243Sobrien	    if ((c &= TRIM) == 0)
105659243Sobrien		continue;
105759243Sobrien	    /* \ quotes \ $ ` here */
105859243Sobrien	    if (c == '\\') {
105959243Sobrien		c = DgetC(0);
106059243Sobrien		if (!any("$\\`", c))
106159243Sobrien		    unDgetC(c | QUOTE), c = '\\';
106259243Sobrien		else
106359243Sobrien		    c |= QUOTE;
106459243Sobrien	    }
1065167465Smp	    Strbuf_append1(&mbuf, (Char) c);
106659243Sobrien	}
1067167465Smp	Strbuf_terminate(&mbuf);
106859243Sobrien
106959243Sobrien	/*
107059243Sobrien	 * If any ` in line do command substitution
107159243Sobrien	 */
1072167465Smp	mbp = mbuf.s;
107359243Sobrien	if (Strchr(mbp, '`') != NULL) {
107459243Sobrien	    /*
107559243Sobrien	     * 1 arg to dobackp causes substitution to be literal. Words are
107659243Sobrien	     * broken only at newlines so that all blanks and tabs are
107759243Sobrien	     * preserved.  Blank lines (null words) are not discarded.
107859243Sobrien	     */
1079167465Smp	    words = dobackp(mbp, 1);
108059243Sobrien	}
108159243Sobrien	else
108259243Sobrien	    /* Setup trivial vector similar to return of dobackp */
1083167465Smp	    Dv[0] = mbp, Dv[1] = NULL, words = Dv;
108459243Sobrien
108559243Sobrien	/*
108659243Sobrien	 * Resurrect the words from the command substitution each separated by
108759243Sobrien	 * a newline.  Note that the last newline of a command substitution
108859243Sobrien	 * will have been discarded, but we put a newline after the last word
108959243Sobrien	 * because this represents the newline after the last input line!
109059243Sobrien	 */
1091167465Smp	for (vp= words; *vp; vp++) {
109259243Sobrien	    for (mbp = *vp; *mbp; mbp++) {
109359243Sobrien		*obp++ = *mbp & TRIM;
1094167465Smp		if (obp == OBUF_END) {
1095145479Smp		    tmp = short2str(obuf);
1096167465Smp		    (void) xwrite(0, tmp, strlen (tmp));
109759243Sobrien		    obp = obuf;
109859243Sobrien		}
109959243Sobrien	    }
110059243Sobrien	    *obp++ = '\n';
1101167465Smp	    if (obp == OBUF_END) {
1102145479Smp	        tmp = short2str(obuf);
1103167465Smp		(void) xwrite(0, tmp, strlen (tmp));
110459243Sobrien		obp = obuf;
110559243Sobrien	    }
110659243Sobrien	}
1107167465Smp	if (words != Dv)
1108167465Smp	    blkfree(words);
110959243Sobrien    }
1110167465Smp    *obp = 0;
1111167465Smp    tmp = short2str(obuf);
1112167465Smp    (void) xwrite(0, tmp, strlen (tmp));
1113167465Smp    (void) lseek(0, (off_t) 0, L_SET);
1114167465Smp    cleanup_until(&inheredoc);
111559243Sobrien}
1116