xref: /csrg-svn/bin/csh/dol.c (revision 69126)
147823Sbostic /*-
260765Sbostic  * Copyright (c) 1980, 1991, 1993
360765Sbostic  *	The Regents of the University of California.  All rights reserved.
447823Sbostic  *
547823Sbostic  * %sccs.include.redist.c%
621928Sdist  */
721928Sdist 
817515Sedward #ifndef lint
9*69126Schristos static char sccsid[] = "@(#)dol.c	8.2 (Berkeley) 04/29/95";
1047410Sbostic #endif /* not lint */
111290Sbill 
1250028Sbostic #include <sys/types.h>
1350028Sbostic #include <fcntl.h>
1450028Sbostic #include <errno.h>
1550028Sbostic #include <stdlib.h>
1650028Sbostic #include <string.h>
1750028Sbostic #include <unistd.h>
1850033Schristos #if __STDC__
1950033Schristos # include <stdarg.h>
2050033Schristos #else
2150033Schristos # include <varargs.h>
2250033Schristos #endif
2350033Schristos 
2450023Sbostic #include "csh.h"
2550023Sbostic #include "extern.h"
261290Sbill 
271290Sbill /*
281290Sbill  * These routines perform variable substitution and quoting via ' and ".
291290Sbill  * To this point these constructs have been preserved in the divided
301290Sbill  * input words.  Here we expand variables and turn quoting via ' and " into
311290Sbill  * QUOTE bits on characters (which prevent further interpretation).
321290Sbill  * If the `:q' modifier was applied during history expansion, then
3317515Sedward  * some QUOTEing may have occurred already, so we dont "trim()" here.
341290Sbill  */
351290Sbill 
3649992Sbostic static int Dpeekc, Dpeekrd;	/* Peeks for DgetC and Dreadc */
3749992Sbostic static Char *Dcp, **Dvp;	/* Input vector for Dreadc */
381290Sbill 
391290Sbill #define	DEOF	-1
401290Sbill 
411290Sbill #define	unDgetC(c)	Dpeekc = c
421290Sbill 
4360488Schristos #define QUOTES		(_QF|_QB|_ESC)	/* \ ' " ` */
441290Sbill 
451290Sbill /*
461290Sbill  * The following variables give the information about the current
471290Sbill  * $ expansion, recording the current word position, the remaining
481290Sbill  * words within this expansion, the count of remaining words, and the
491290Sbill  * information about any : modifier which is being applied.
501290Sbill  */
5150462Schristos #define MAXWLEN (BUFSIZ - 4)
5250462Schristos #define MAXMOD MAXWLEN		/* This cannot overflow	*/
5349992Sbostic static Char *dolp;		/* Remaining chars from this word */
5449992Sbostic static Char **dolnxt;		/* Further words */
5549992Sbostic static int dolcnt;		/* Count of further words */
5650462Schristos static Char dolmod[MAXMOD];	/* : modifier character */
5750462Schristos static int dolnmod;		/* Number of modifiers */
5849992Sbostic static int dolmcnt;		/* :gx -> 10000, else 1 */
5951526Schristos static int dolwcnt;		/* :wx -> 10000, else 1 */
601290Sbill 
6150024Schristos static void	 Dfix2 __P((Char **));
6251437Sleres static Char	*Dpack __P((Char *, Char *));
6350024Schristos static int	 Dword __P((void));
6450024Schristos static void	 dolerror __P((Char *));
6550024Schristos static int	 DgetC __P((int));
6650024Schristos static void	 Dgetdol __P((void));
6750024Schristos static void	 fixDolMod __P((void));
6850024Schristos static void	 setDolp __P((Char *));
6950024Schristos static void	 unDredc __P((int));
7050024Schristos static int	 Dredc __P((void));
7150024Schristos static void	 Dtestq __P((int));
7249992Sbostic 
7349992Sbostic 
741290Sbill /*
751290Sbill  * Fix up the $ expansions and quotations in the
761290Sbill  * argument list to command t.
771290Sbill  */
7849992Sbostic void
Dfix(t)791290Sbill Dfix(t)
8049992Sbostic     register struct command *t;
811290Sbill {
8249992Sbostic     register Char **pp;
8349992Sbostic     register Char *p;
841290Sbill 
8549992Sbostic     if (noexec)
8649992Sbostic 	return;
8749992Sbostic     /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
8860237Schristos     for (pp = t->t_dcom; (p = *pp++) != NULL;)
8949992Sbostic 	for (; *p; p++) {
9049992Sbostic 	    if (cmap(*p, _DOL | QUOTES)) {	/* $, \, ', ", ` */
9149992Sbostic 		Dfix2(t->t_dcom);	/* found one */
9249992Sbostic 		blkfree(t->t_dcom);
9349992Sbostic 		t->t_dcom = gargv;
9449992Sbostic 		gargv = 0;
951290Sbill 		return;
9649992Sbostic 	    }
9749992Sbostic 	}
981290Sbill }
991290Sbill 
1001290Sbill /*
1011290Sbill  * $ substitute one word, for i/o redirection
1021290Sbill  */
10349992Sbostic Char   *
Dfix1(cp)1041290Sbill Dfix1(cp)
10549992Sbostic     register Char *cp;
1061290Sbill {
10749992Sbostic     Char   *Dv[2];
1081290Sbill 
10949992Sbostic     if (noexec)
11049992Sbostic 	return (0);
11149992Sbostic     Dv[0] = cp;
11249992Sbostic     Dv[1] = NULL;
11349992Sbostic     Dfix2(Dv);
11449992Sbostic     if (gargc != 1) {
11551589Schristos 	setname(vis_str(cp));
11649992Sbostic 	stderror(ERR_NAME | ERR_AMBIG);
11749992Sbostic     }
11849992Sbostic     cp = Strsave(gargv[0]);
11949992Sbostic     blkfree(gargv), gargv = 0;
12049992Sbostic     return (cp);
1211290Sbill }
1221290Sbill 
1231290Sbill /*
1241290Sbill  * Subroutine to do actual fixing after state initialization.
1251290Sbill  */
12649992Sbostic static void
Dfix2(v)1271290Sbill Dfix2(v)
12849992Sbostic     Char  **v;
1291290Sbill {
13049992Sbostic     ginit();			/* Initialize glob's area pointers */
13149992Sbostic     Dvp = v;
13249992Sbostic     Dcp = STRNULL;		/* Setup input vector for Dreadc */
13349992Sbostic     unDgetC(0);
13449992Sbostic     unDredc(0);			/* Clear out any old peeks (at error) */
13549992Sbostic     dolp = 0;
13649992Sbostic     dolcnt = 0;			/* Clear out residual $ expands (...) */
13749992Sbostic     while (Dword())
13849992Sbostic 	continue;
1391290Sbill }
1401290Sbill 
1411290Sbill /*
14249992Sbostic  * Pack up more characters in this word
14349992Sbostic  */
14449992Sbostic static Char *
Dpack(wbuf,wp)14549992Sbostic Dpack(wbuf, wp)
14649992Sbostic     Char   *wbuf, *wp;
14749992Sbostic {
14849992Sbostic     register int c;
14949992Sbostic     register int i = MAXWLEN - (wp - wbuf);
15049992Sbostic 
15149992Sbostic     for (;;) {
15249992Sbostic 	c = DgetC(DODOL);
15349992Sbostic 	if (c == '\\') {
15449992Sbostic 	    c = DgetC(0);
15549992Sbostic 	    if (c == DEOF) {
15649992Sbostic 		unDredc(c);
15749992Sbostic 		*wp = 0;
15849992Sbostic 		Gcat(STRNULL, wbuf);
15949992Sbostic 		return (NULL);
16049992Sbostic 	    }
16149992Sbostic 	    if (c == '\n')
16249992Sbostic 		c = ' ';
16349992Sbostic 	    else
16449992Sbostic 		c |= QUOTE;
16549992Sbostic 	}
16649992Sbostic 	if (c == DEOF) {
16749992Sbostic 	    unDredc(c);
16849992Sbostic 	    *wp = 0;
16949992Sbostic 	    Gcat(STRNULL, wbuf);
17049992Sbostic 	    return (NULL);
17149992Sbostic 	}
17260488Schristos 	if (cmap(c, _SP | _NL | _QF | _QB)) {	/* sp \t\n'"` */
17349992Sbostic 	    unDgetC(c);
17449992Sbostic 	    if (cmap(c, QUOTES))
17549992Sbostic 		return (wp);
17649992Sbostic 	    *wp++ = 0;
17749992Sbostic 	    Gcat(STRNULL, wbuf);
17849992Sbostic 	    return (NULL);
17949992Sbostic 	}
18049992Sbostic 	if (--i <= 0)
18149992Sbostic 	    stderror(ERR_WTOOLONG);
18249992Sbostic 	*wp++ = c;
18349992Sbostic     }
18449992Sbostic }
18549992Sbostic 
18649992Sbostic /*
1871290Sbill  * Get a word.  This routine is analogous to the routine
1881290Sbill  * word() in sh.lex.c for the main lexical input.  One difference
1891290Sbill  * here is that we don't get a newline to terminate our expansion.
1901290Sbill  * Rather, DgetC will return a DEOF when we hit the end-of-input.
1911290Sbill  */
19249992Sbostic static int
Dword()1931290Sbill Dword()
1941290Sbill {
19549992Sbostic     register int c, c1;
19649992Sbostic     Char    wbuf[BUFSIZ];
19749992Sbostic     register Char *wp = wbuf;
19849992Sbostic     register int i = MAXWLEN;
19949992Sbostic     register bool dolflg;
20049992Sbostic     bool    sofar = 0, done = 0;
2011290Sbill 
20249992Sbostic     while (!done) {
20349992Sbostic 	done = 1;
2041290Sbill 	c = DgetC(DODOL);
2051290Sbill 	switch (c) {
2061290Sbill 
2071290Sbill 	case DEOF:
20849992Sbostic 	    if (sofar == 0)
20949992Sbostic 		return (0);
21049992Sbostic 	    /* finish this word and catch the code above the next time */
21149992Sbostic 	    unDredc(c);
21249992Sbostic 	    /* fall into ... */
2131290Sbill 
2141290Sbill 	case '\n':
21549992Sbostic 	    *wp = 0;
21649992Sbostic 	    Gcat(STRNULL, wbuf);
21749992Sbostic 	    return (1);
2181290Sbill 
2191290Sbill 	case ' ':
2201290Sbill 	case '\t':
22149992Sbostic 	    done = 0;
22249992Sbostic 	    break;
2231290Sbill 
2241290Sbill 	case '`':
22549992Sbostic 	    /* We preserve ` quotations which are done yet later */
22649992Sbostic 	    *wp++ = c, --i;
2271290Sbill 	case '\'':
2281290Sbill 	case '"':
22949992Sbostic 	    /*
23049992Sbostic 	     * Note that DgetC never returns a QUOTES character from an
23149992Sbostic 	     * expansion, so only true input quotes will get us here or out.
23249992Sbostic 	     */
23349992Sbostic 	    c1 = c;
23449992Sbostic 	    dolflg = c1 == '"' ? DODOL : 0;
23549992Sbostic 	    for (;;) {
23649992Sbostic 		c = DgetC(dolflg);
23749992Sbostic 		if (c == c1)
23849992Sbostic 		    break;
23949992Sbostic 		if (c == '\n' || c == DEOF)
24049992Sbostic 		    stderror(ERR_UNMATCHED, c1);
24149992Sbostic 		if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE))
24249992Sbostic 		    --wp, ++i;
24349992Sbostic 		if (--i <= 0)
24449992Sbostic 		    stderror(ERR_WTOOLONG);
24549992Sbostic 		switch (c1) {
2461290Sbill 
24749992Sbostic 		case '"':
24849992Sbostic 		    /*
24949992Sbostic 		     * Leave any `s alone for later. Other chars are all
25049992Sbostic 		     * quoted, thus `...` can tell it was within "...".
25149992Sbostic 		     */
25249992Sbostic 		    *wp++ = c == '`' ? '`' : c | QUOTE;
25349992Sbostic 		    break;
2541290Sbill 
25549992Sbostic 		case '\'':
25649992Sbostic 		    /* Prevent all further interpretation */
25749992Sbostic 		    *wp++ = c | QUOTE;
25849992Sbostic 		    break;
2591290Sbill 
26049992Sbostic 		case '`':
26149992Sbostic 		    /* Leave all text alone for later */
26249992Sbostic 		    *wp++ = c;
26349992Sbostic 		    break;
26451526Schristos 
26551526Schristos 		default:
26651526Schristos 		    break;
2671290Sbill 		}
26849992Sbostic 	    }
26949992Sbostic 	    if (c1 == '`')
27051526Schristos 		*wp++ = '`' /* i--; eliminated */;
27149992Sbostic 	    sofar = 1;
27249992Sbostic 	    if ((wp = Dpack(wbuf, wp)) == NULL)
27349992Sbostic 		return (1);
27449992Sbostic 	    else {
27549992Sbostic 		i = MAXWLEN - (wp - wbuf);
27649992Sbostic 		done = 0;
27749992Sbostic 	    }
27849992Sbostic 	    break;
2791290Sbill 
2801290Sbill 	case '\\':
28149992Sbostic 	    c = DgetC(0);	/* No $ subst! */
28249992Sbostic 	    if (c == '\n' || c == DEOF) {
28349992Sbostic 		done = 0;
2841290Sbill 		break;
28549992Sbostic 	    }
28649992Sbostic 	    c |= QUOTE;
28749992Sbostic 	    break;
28851526Schristos 
28951526Schristos 	default:
29051526Schristos 	    break;
2911290Sbill 	}
29249992Sbostic 	if (done) {
29349992Sbostic 	    unDgetC(c);
29449992Sbostic 	    sofar = 1;
29549992Sbostic 	    if ((wp = Dpack(wbuf, wp)) == NULL)
29649992Sbostic 		return (1);
29749992Sbostic 	    else {
29849992Sbostic 		i = MAXWLEN - (wp - wbuf);
29949992Sbostic 		done = 0;
30049992Sbostic 	    }
3011290Sbill 	}
30249992Sbostic     }
30349992Sbostic     /* Really NOTREACHED */
30449992Sbostic     return (0);
3051290Sbill }
3061290Sbill 
30749992Sbostic 
3081290Sbill /*
3091290Sbill  * Get a character, performing $ substitution unless flag is 0.
31012210Ssam  * Any QUOTES character which is returned from a $ expansion is
31112210Ssam  * QUOTEd so that it will not be recognized above.
3121290Sbill  */
31349992Sbostic static int
DgetC(flag)3141290Sbill DgetC(flag)
31549992Sbostic     register int flag;
3161290Sbill {
31749992Sbostic     register int c;
3181290Sbill 
3191290Sbill top:
32060237Schristos     if ((c = Dpeekc) != '\0') {
32149992Sbostic 	Dpeekc = 0;
32249992Sbostic 	return (c);
32349992Sbostic     }
32449992Sbostic     if (lap) {
32549992Sbostic 	c = *lap++ & (QUOTE | TRIM);
32649992Sbostic 	if (c == 0) {
32749992Sbostic 	    lap = 0;
32849992Sbostic 	    goto top;
3291290Sbill 	}
3301290Sbill quotspec:
33149992Sbostic 	if (cmap(c, QUOTES))
33249992Sbostic 	    return (c | QUOTE);
33349992Sbostic 	return (c);
33449992Sbostic     }
33549992Sbostic     if (dolp) {
33660237Schristos 	if ((c = *dolp++ & (QUOTE | TRIM)) != '\0')
33749992Sbostic 	    goto quotspec;
3381290Sbill 	if (dolcnt > 0) {
33949992Sbostic 	    setDolp(*dolnxt++);
34049992Sbostic 	    --dolcnt;
34149992Sbostic 	    return (' ');
3421290Sbill 	}
34349992Sbostic 	dolp = 0;
34449992Sbostic     }
34549992Sbostic     if (dolcnt > 0) {
34649992Sbostic 	setDolp(*dolnxt++);
34749992Sbostic 	--dolcnt;
34849992Sbostic 	goto top;
34949992Sbostic     }
35049992Sbostic     c = Dredc();
35149992Sbostic     if (c == '$' && flag) {
35249992Sbostic 	Dgetdol();
35349992Sbostic 	goto top;
35449992Sbostic     }
35549992Sbostic     return (c);
3561290Sbill }
3571290Sbill 
35849992Sbostic static Char *nulvec[] = {0};
35960237Schristos static struct varent nulargv = {nulvec, STRargv, { NULL, NULL, NULL }, 0};
3601290Sbill 
36149992Sbostic static void
dolerror(s)36249992Sbostic dolerror(s)
36349992Sbostic     Char   *s;
36449992Sbostic {
36551589Schristos     setname(vis_str(s));
36649992Sbostic     stderror(ERR_NAME | ERR_RANGE);
36749992Sbostic }
36849992Sbostic 
3691290Sbill /*
3701290Sbill  * Handle the multitudinous $ expansion forms.
3711290Sbill  * Ugh.
3721290Sbill  */
37349992Sbostic static void
Dgetdol()3741290Sbill Dgetdol()
3751290Sbill {
37649992Sbostic     register Char *np;
37749992Sbostic     register struct varent *vp = NULL;
37849992Sbostic     Char    name[4 * MAXVARLEN + 1];
37949992Sbostic     int     c, sc;
38049992Sbostic     int     subscr = 0, lwb = 1, upb = 0;
38149992Sbostic     bool    dimen = 0, bitset = 0;
38249992Sbostic     char    tnp;
38349992Sbostic     Char    wbuf[BUFSIZ];
38451526Schristos     static Char *dolbang = NULL;
3851290Sbill 
38651526Schristos     dolnmod = dolmcnt = dolwcnt = 0;
38749992Sbostic     c = sc = DgetC(0);
38849992Sbostic     if (c == '{')
38949992Sbostic 	c = DgetC(0);		/* sc is { to take } later */
39049992Sbostic     if ((c & TRIM) == '#')
39149992Sbostic 	dimen++, c = DgetC(0);	/* $# takes dimension */
39249992Sbostic     else if (c == '?')
39349992Sbostic 	bitset++, c = DgetC(0);	/* $? tests existence */
39449992Sbostic     switch (c) {
3951290Sbill 
39651526Schristos     case '!':
39751526Schristos 	if (dimen || bitset)
39851526Schristos 	    stderror(ERR_SYNTAX);
39951526Schristos 	if (backpid != 0) {
40051526Schristos 	    if (dolbang)
40151526Schristos 		xfree((ptr_t) dolbang);
40251526Schristos 	    setDolp(dolbang = putn(backpid));
40351526Schristos 	}
40451526Schristos 	goto eatbrac;
40551526Schristos 
40649992Sbostic     case '$':
40749992Sbostic 	if (dimen || bitset)
40849992Sbostic 	    stderror(ERR_SYNTAX);
40949992Sbostic 	setDolp(doldol);
41049992Sbostic 	goto eatbrac;
4111290Sbill 
41249992Sbostic     case '<' | QUOTE:
41349992Sbostic 	if (bitset)
41449992Sbostic 	    stderror(ERR_NOTALLOWED, "$?<");
41549992Sbostic 	if (dimen)
41649992Sbostic 	    stderror(ERR_NOTALLOWED, "$?#");
41749992Sbostic 	for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) {
41853569Schristos 	    *np = (unsigned char) tnp;
41949992Sbostic 	    if (np >= &wbuf[BUFSIZ - 1])
42049992Sbostic 		stderror(ERR_LTOOLONG);
42153569Schristos 	    if (tnp == '\n')
4221290Sbill 		break;
42349992Sbostic 	}
42449992Sbostic 	*np = 0;
42549992Sbostic 	/*
42649992Sbostic 	 * KLUDGE: dolmod is set here because it will cause setDolp to call
42749992Sbostic 	 * domod and thus to copy wbuf. Otherwise setDolp would use it
42849992Sbostic 	 * directly. If we saved it ourselves, no one would know when to free
42949992Sbostic 	 * it. The actual function of the 'q' causes filename expansion not to
43049992Sbostic 	 * be done on the interpolated value.
43149992Sbostic 	 */
43250462Schristos 	dolmod[dolnmod++] = 'q';
43349992Sbostic 	dolmcnt = 10000;
43449992Sbostic 	setDolp(wbuf);
43549992Sbostic 	goto eatbrac;
4361290Sbill 
43749992Sbostic     case DEOF:
43849992Sbostic     case '\n':
43949992Sbostic 	stderror(ERR_SYNTAX);
44049992Sbostic 	/* NOTREACHED */
44149992Sbostic 	break;
44249992Sbostic 
44349992Sbostic     case '*':
44449992Sbostic 	(void) Strcpy(name, STRargv);
44549992Sbostic 	vp = adrof(STRargv);
44649992Sbostic 	subscr = -1;		/* Prevent eating [...] */
44749992Sbostic 	break;
44849992Sbostic 
44949992Sbostic     default:
45049992Sbostic 	np = name;
45149992Sbostic 	if (Isdigit(c)) {
45249992Sbostic 	    if (dimen)
45349992Sbostic 		stderror(ERR_NOTALLOWED, "$#<num>");
45449992Sbostic 	    subscr = 0;
45549992Sbostic 	    do {
45649992Sbostic 		subscr = subscr * 10 + c - '0';
45749992Sbostic 		c = DgetC(0);
45849992Sbostic 	    } while (Isdigit(c));
45949992Sbostic 	    unDredc(c);
46049992Sbostic 	    if (subscr < 0) {
46149992Sbostic 		dolerror(vp->v_name);
46249992Sbostic 		return;
46349992Sbostic 	    }
46449992Sbostic 	    if (subscr == 0) {
46549992Sbostic 		if (bitset) {
46649992Sbostic 		    dolp = ffile ? STR1 : STR0;
46749992Sbostic 		    goto eatbrac;
4681290Sbill 		}
46949992Sbostic 		if (ffile == 0)
47049992Sbostic 		    stderror(ERR_DOLZERO);
47149992Sbostic 		fixDolMod();
47249992Sbostic 		setDolp(ffile);
4731290Sbill 		goto eatbrac;
47449992Sbostic 	    }
47549992Sbostic 	    if (bitset)
47649992Sbostic 		stderror(ERR_DOLQUEST);
47749992Sbostic 	    vp = adrof(STRargv);
47849992Sbostic 	    if (vp == 0) {
47949992Sbostic 		vp = &nulargv;
48049992Sbostic 		goto eatmod;
48149992Sbostic 	    }
48249992Sbostic 	    break;
4831290Sbill 	}
48449992Sbostic 	if (!alnum(c))
48549992Sbostic 	    stderror(ERR_VARALNUM);
48649992Sbostic 	for (;;) {
48749992Sbostic 	    *np++ = c;
48849992Sbostic 	    c = DgetC(0);
48949992Sbostic 	    if (!alnum(c))
49049992Sbostic 		break;
49149992Sbostic 	    if (np >= &name[MAXVARLEN])
49249992Sbostic 		stderror(ERR_VARTOOLONG);
4931290Sbill 	}
49449992Sbostic 	*np++ = 0;
49549992Sbostic 	unDredc(c);
49649992Sbostic 	vp = adrof(name);
49749992Sbostic     }
49849992Sbostic     if (bitset) {
49949992Sbostic 	dolp = (vp || getenv(short2str(name))) ? STR1 : STR0;
50049992Sbostic 	goto eatbrac;
50149992Sbostic     }
50249992Sbostic     if (vp == 0) {
50349992Sbostic 	np = str2short(getenv(short2str(name)));
50449992Sbostic 	if (np) {
50549992Sbostic 	    fixDolMod();
50649992Sbostic 	    setDolp(np);
50749992Sbostic 	    goto eatbrac;
50849992Sbostic 	}
50949992Sbostic 	udvar(name);
51049992Sbostic 	/* NOTREACHED */
51149992Sbostic     }
51249992Sbostic     c = DgetC(0);
51349992Sbostic     upb = blklen(vp->vec);
51449992Sbostic     if (dimen == 0 && subscr == 0 && c == '[') {
51549992Sbostic 	np = name;
51649992Sbostic 	for (;;) {
51749992Sbostic 	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
51849992Sbostic 	    if (c == ']')
51949992Sbostic 		break;
52049992Sbostic 	    if (c == '\n' || c == DEOF)
52149992Sbostic 		stderror(ERR_INCBR);
52249992Sbostic 	    if (np >= &name[sizeof(name) / sizeof(Char) - 2])
52349992Sbostic 		stderror(ERR_VARTOOLONG);
52449992Sbostic 	    *np++ = c;
52549992Sbostic 	}
52649992Sbostic 	*np = 0, np = name;
52749992Sbostic 	if (dolp || dolcnt)	/* $ exp must end before ] */
52849992Sbostic 	    stderror(ERR_EXPORD);
52949992Sbostic 	if (!*np)
53049992Sbostic 	    stderror(ERR_SYNTAX);
53149992Sbostic 	if (Isdigit(*np)) {
53249992Sbostic 	    int     i;
5331290Sbill 
53451437Sleres 	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
53551437Sleres 		continue;
53649992Sbostic 	    if ((i < 0 || i > upb) && !any("-*", *np)) {
53749992Sbostic 		dolerror(vp->v_name);
53849992Sbostic 		return;
53949992Sbostic 	    }
54049992Sbostic 	    lwb = i;
54149992Sbostic 	    if (!*np)
54249992Sbostic 		upb = lwb, np = STRstar;
54349992Sbostic 	}
54449992Sbostic 	if (*np == '*')
54549992Sbostic 	    np++;
54649992Sbostic 	else if (*np != '-')
54749992Sbostic 	    stderror(ERR_MISSING, '-');
54849992Sbostic 	else {
54949992Sbostic 	    register int i = upb;
5501290Sbill 
55149992Sbostic 	    np++;
55249992Sbostic 	    if (Isdigit(*np)) {
55349992Sbostic 		i = 0;
55449992Sbostic 		while (Isdigit(*np))
55549992Sbostic 		    i = i * 10 + *np++ - '0';
55649992Sbostic 		if (i < 0 || i > upb) {
55749992Sbostic 		    dolerror(vp->v_name);
55849992Sbostic 		    return;
5591290Sbill 		}
56049992Sbostic 	    }
56149992Sbostic 	    if (i < lwb)
56249992Sbostic 		upb = lwb - 1;
56349992Sbostic 	    else
56449992Sbostic 		upb = i;
5651290Sbill 	}
56649992Sbostic 	if (lwb == 0) {
56749992Sbostic 	    if (upb != 0) {
56849992Sbostic 		dolerror(vp->v_name);
56949992Sbostic 		return;
57049992Sbostic 	    }
57149992Sbostic 	    upb = -1;
57249992Sbostic 	}
57349992Sbostic 	if (*np)
57449992Sbostic 	    stderror(ERR_SYNTAX);
57549992Sbostic     }
57649992Sbostic     else {
57749992Sbostic 	if (subscr > 0)
57849992Sbostic 	    if (subscr > upb)
57949992Sbostic 		lwb = 1, upb = 0;
58049992Sbostic 	    else
58149992Sbostic 		lwb = upb = subscr;
58249992Sbostic 	unDredc(c);
58349992Sbostic     }
58449992Sbostic     if (dimen) {
58549992Sbostic 	Char   *cp = putn(upb - lwb + 1);
5861290Sbill 
58749992Sbostic 	addla(cp);
58849992Sbostic 	xfree((ptr_t) cp);
58949992Sbostic     }
59049992Sbostic     else {
5911290Sbill eatmod:
59249992Sbostic 	fixDolMod();
59349992Sbostic 	dolnxt = &vp->vec[lwb - 1];
59449992Sbostic 	dolcnt = upb - lwb + 1;
59549992Sbostic     }
5961290Sbill eatbrac:
59749992Sbostic     if (sc == '{') {
59849992Sbostic 	c = Dredc();
59949992Sbostic 	if (c != '}')
60049992Sbostic 	    stderror(ERR_MISSING, '}');
60149992Sbostic     }
6021290Sbill }
6031290Sbill 
60449992Sbostic static void
fixDolMod()60549992Sbostic fixDolMod()
60649992Sbostic {
60749992Sbostic     register int c;
60849992Sbostic 
60949992Sbostic     c = DgetC(0);
61049992Sbostic     if (c == ':') {
61150462Schristos 	do {
61251526Schristos 	    c = DgetC(0), dolmcnt = 1, dolwcnt = 1;
61351526Schristos 	    if (c == 'g' || c == 'a') {
61451526Schristos 		if (c == 'g')
61551526Schristos 		    dolmcnt = 10000;
61651526Schristos 		else
61751526Schristos 		    dolwcnt = 10000;
61851526Schristos 		c = DgetC(0);
61951526Schristos 	    }
62051526Schristos 	    if ((c == 'g' && dolmcnt != 10000) ||
62151526Schristos 		(c == 'a' && dolwcnt != 10000)) {
62251526Schristos 		if (c == 'g')
62351526Schristos 		    dolmcnt = 10000;
62451526Schristos 		else
62551526Schristos 		    dolwcnt = 10000;
62651526Schristos 		c = DgetC(0);
62751526Schristos 	    }
62851526Schristos 
62951526Schristos 	    if (c == 's') {	/* [eichin:19910926.0755EST] */
63051526Schristos 		int delimcnt = 2;
63151526Schristos 		int delim = DgetC(0);
63251526Schristos 		dolmod[dolnmod++] = c;
63351526Schristos 		dolmod[dolnmod++] = delim;
63451526Schristos 
63551526Schristos 		if (!delim || letter(delim)
63651526Schristos 		    || Isdigit(delim) || any(" \t\n", delim)) {
63751526Schristos 		    seterror(ERR_BADSUBST);
63851526Schristos 		    break;
63951526Schristos 		}
64051526Schristos 		while ((c = DgetC(0)) != (-1)) {
64151526Schristos 		    dolmod[dolnmod++] = c;
64251526Schristos 		    if(c == delim) delimcnt--;
64351526Schristos 		    if(!delimcnt) break;
64451526Schristos 		}
64551526Schristos 		if(delimcnt) {
64651526Schristos 		    seterror(ERR_BADSUBST);
64751526Schristos 		    break;
64851526Schristos 		}
64951526Schristos 		continue;
65051526Schristos 	    }
65151526Schristos 	    if (!any("htrqxes", c))
65250462Schristos 		stderror(ERR_BADMOD, c);
65350462Schristos 	    dolmod[dolnmod++] = c;
65450462Schristos 	    if (c == 'q')
65550462Schristos 		dolmcnt = 10000;
65650462Schristos 	}
65750462Schristos 	while ((c = DgetC(0)) == ':');
65850462Schristos 	unDredc(c);
65949992Sbostic     }
66049992Sbostic     else
66149992Sbostic 	unDredc(c);
66249992Sbostic }
66349992Sbostic 
66449992Sbostic static void
setDolp(cp)6651290Sbill setDolp(cp)
66649992Sbostic     register Char *cp;
6671290Sbill {
66849992Sbostic     register Char *dp;
66950462Schristos     int i;
6701290Sbill 
67150462Schristos     if (dolnmod == 0 || dolmcnt == 0) {
67249992Sbostic 	dolp = cp;
67349992Sbostic 	return;
67449992Sbostic     }
67550462Schristos     dp = cp = Strsave(cp);
67651526Schristos     for (i = 0; i < dolnmod; i++) {
67751526Schristos 	/* handle s// [eichin:19910926.0510EST] */
67851526Schristos 	if(dolmod[i] == 's') {
67951526Schristos 	    int delim;
68051526Schristos 	    Char *lhsub, *rhsub, *np;
68151526Schristos 	    size_t lhlen = 0, rhlen = 0;
68251526Schristos 	    int didmod = 0;
68351526Schristos 
68451526Schristos 	    delim = dolmod[++i];
68551526Schristos 	    if (!delim || letter(delim)
68651526Schristos 		|| Isdigit(delim) || any(" \t\n", delim)) {
68751526Schristos 		seterror(ERR_BADSUBST);
68851526Schristos 		break;
68951526Schristos 	    }
69051526Schristos 	    lhsub = &dolmod[++i];
69151526Schristos 	    while(dolmod[i] != delim && dolmod[++i]) {
69251526Schristos 		lhlen++;
69351526Schristos 	    }
69451526Schristos 	    dolmod[i] = 0;
69551526Schristos 	    rhsub = &dolmod[++i];
69651526Schristos 	    while(dolmod[i] != delim && dolmod[++i]) {
69751526Schristos 		rhlen++;
69851526Schristos 	    }
69951526Schristos 	    dolmod[i] = 0;
70051526Schristos 
70151526Schristos 	    do {
70251526Schristos 		dp = Strstr(cp, lhsub);
70351526Schristos 		if (dp) {
70451526Schristos 		    np = (Char *) xmalloc((size_t)
70551526Schristos 					  ((Strlen(cp) + 1 - lhlen + rhlen) *
70651526Schristos 					  sizeof(Char)));
70751526Schristos 		    (void) Strncpy(np, cp, dp - cp);
70851526Schristos 		    (void) Strcpy(np + (dp - cp), rhsub);
70951526Schristos 		    (void) Strcpy(np + (dp - cp) + rhlen, dp + lhlen);
71051526Schristos 
71151526Schristos 		    xfree((ptr_t) cp);
71251526Schristos 		    dp = cp = np;
71351526Schristos 		    didmod = 1;
71451526Schristos 		} else {
71551526Schristos 		    /* should this do a seterror? */
71651526Schristos 		    break;
71751526Schristos 		}
71851526Schristos 	    }
71951526Schristos 	    while (dolwcnt == 10000);
72051526Schristos 	    /*
72151526Schristos 	     * restore dolmod for additional words
72251526Schristos 	     */
72351526Schristos 	    dolmod[i] = rhsub[-1] = delim;
72451526Schristos 	    if (didmod)
72551526Schristos 		dolmcnt--;
72651526Schristos 	    else
72751526Schristos 		break;
72851526Schristos         } else {
72951526Schristos 	    int didmod = 0;
73051526Schristos 
73151526Schristos 	    do {
73251526Schristos 		if ((dp = domod(cp, dolmod[i]))) {
73351526Schristos 		    didmod = 1;
73451526Schristos 		    if (Strcmp(cp, dp) == 0) {
73551526Schristos 			xfree((ptr_t) cp);
73651526Schristos 			cp = dp;
73751526Schristos 			break;
73851526Schristos 		    }
73951526Schristos 		    else {
74051526Schristos 			xfree((ptr_t) cp);
74151526Schristos 			cp = dp;
74251526Schristos 		    }
74351526Schristos 		}
74451526Schristos 		else
74551526Schristos 		    break;
74651526Schristos 	    }
74751526Schristos 	    while (dolwcnt == 10000);
74850462Schristos 	    dp = cp;
74951526Schristos 	    if (didmod)
75051526Schristos 		dolmcnt--;
75151526Schristos 	    else
75251526Schristos 		break;
75350462Schristos 	}
75451526Schristos     }
75550462Schristos 
75649992Sbostic     if (dp) {
75749992Sbostic 	addla(dp);
75849992Sbostic 	xfree((ptr_t) dp);
75949992Sbostic     }
76051526Schristos     else
76151526Schristos 	addla(cp);
76251526Schristos 
76349992Sbostic     dolp = STRNULL;
76449992Sbostic     if (seterr)
76549992Sbostic 	stderror(ERR_OLD);
7661290Sbill }
7671290Sbill 
76849992Sbostic static void
unDredc(c)7691290Sbill unDredc(c)
77049992Sbostic     int     c;
7711290Sbill {
7721290Sbill 
77349992Sbostic     Dpeekrd = c;
7741290Sbill }
7751290Sbill 
77649992Sbostic static int
Dredc()7771290Sbill Dredc()
7781290Sbill {
77949992Sbostic     register int c;
7801290Sbill 
78160237Schristos     if ((c = Dpeekrd) != '\0') {
78249992Sbostic 	Dpeekrd = 0;
78349992Sbostic 	return (c);
78449992Sbostic     }
78549992Sbostic     if (Dcp && (c = *Dcp++))
78649992Sbostic 	return (c & (QUOTE | TRIM));
78749992Sbostic     if (*Dvp == 0) {
78849992Sbostic 	Dcp = 0;
78949992Sbostic 	return (DEOF);
79049992Sbostic     }
79149992Sbostic     Dcp = *Dvp++;
79249992Sbostic     return (' ');
7931290Sbill }
7941290Sbill 
79549992Sbostic static void
Dtestq(c)7961290Sbill Dtestq(c)
79749992Sbostic     register int c;
7981290Sbill {
7991290Sbill 
80049992Sbostic     if (cmap(c, QUOTES))
80149992Sbostic 	gflag = 1;
8021290Sbill }
8031290Sbill 
8041290Sbill /*
8051290Sbill  * Form a shell temporary file (in unit 0) from the words
80630306Sbostic  * of the shell input up to EOF or a line the same as "term".
8071290Sbill  * Unit 0 should have been closed before this call.
8081290Sbill  */
80949992Sbostic void
81050439Schristos /*ARGSUSED*/
heredoc(term)8111290Sbill heredoc(term)
81250439Schristos     Char *term;
8131290Sbill {
81449992Sbostic     register int c;
81549992Sbostic     Char   *Dv[2];
81649992Sbostic     Char    obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ];
81749992Sbostic     int     ocnt, lcnt, mcnt;
81849992Sbostic     register Char *lbp, *obp, *mbp;
81949992Sbostic     Char  **vp;
82049992Sbostic     bool    quoted;
82149992Sbostic     char   *tmp;
8221290Sbill 
823*69126Schristos     tmp = short2str(shtemp);
824*69126Schristos     if (open(tmp, O_RDWR | O_CREAT | O_TRUNC, 0600) < 0)
82549992Sbostic 	stderror(ERR_SYSTEM, tmp, strerror(errno));
82649992Sbostic     (void) unlink(tmp);		/* 0 0 inode! */
82749992Sbostic     Dv[0] = term;
82849992Sbostic     Dv[1] = NULL;
82949992Sbostic     gflag = 0;
83049992Sbostic     trim(Dv);
83149992Sbostic     rscan(Dv, Dtestq);
83249992Sbostic     quoted = gflag;
83349992Sbostic     ocnt = BUFSIZ;
83449992Sbostic     obp = obuf;
83549992Sbostic     for (;;) {
83649992Sbostic 	/*
83749992Sbostic 	 * Read up a line
83849992Sbostic 	 */
83949992Sbostic 	lbp = lbuf;
84049992Sbostic 	lcnt = BUFSIZ - 4;
8411290Sbill 	for (;;) {
84249992Sbostic 	    c = readc(1);	/* 1 -> Want EOF returns */
84349992Sbostic 	    if (c < 0 || c == '\n')
84449992Sbostic 		break;
84560237Schristos 	    if ((c &= TRIM) != '\0') {
84649992Sbostic 		*lbp++ = c;
84749992Sbostic 		if (--lcnt < 0) {
84849992Sbostic 		    setname("<<");
84949992Sbostic 		    stderror(ERR_NAME | ERR_OVERFLOW);
8501290Sbill 		}
85149992Sbostic 	    }
85249992Sbostic 	}
85349992Sbostic 	*lbp = 0;
8541290Sbill 
85549992Sbostic 	/*
85649992Sbostic 	 * Check for EOF or compare to terminator -- before expansion
85749992Sbostic 	 */
85849992Sbostic 	if (c < 0 || eq(lbuf, term)) {
85949992Sbostic 	    (void) write(0, short2str(obuf), (size_t) (BUFSIZ - ocnt));
86049992Sbostic 	    (void) lseek(0, 0l, L_SET);
86149992Sbostic 	    return;
86249992Sbostic 	}
8631290Sbill 
86449992Sbostic 	/*
86549992Sbostic 	 * If term was quoted or -n just pass it on
86649992Sbostic 	 */
86749992Sbostic 	if (quoted || noexec) {
86849992Sbostic 	    *lbp++ = '\n';
86949992Sbostic 	    *lbp = 0;
87060237Schristos 	    for (lbp = lbuf; (c = *lbp++) != '\0';) {
87149992Sbostic 		*obp++ = c;
87249992Sbostic 		if (--ocnt == 0) {
87349992Sbostic 		    (void) write(0, short2str(obuf), BUFSIZ);
87449992Sbostic 		    obp = obuf;
87549992Sbostic 		    ocnt = BUFSIZ;
8761290Sbill 		}
87749992Sbostic 	    }
87849992Sbostic 	    continue;
87949992Sbostic 	}
8801290Sbill 
88149992Sbostic 	/*
88249992Sbostic 	 * Term wasn't quoted so variable and then command expand the input
88349992Sbostic 	 * line
88449992Sbostic 	 */
88549992Sbostic 	Dcp = lbuf;
88649992Sbostic 	Dvp = Dv + 1;
88749992Sbostic 	mbp = mbuf;
88849992Sbostic 	mcnt = BUFSIZ - 4;
88949992Sbostic 	for (;;) {
89049992Sbostic 	    c = DgetC(DODOL);
89149992Sbostic 	    if (c == DEOF)
89249992Sbostic 		break;
89349992Sbostic 	    if ((c &= TRIM) == 0)
89449992Sbostic 		continue;
89549992Sbostic 	    /* \ quotes \ $ ` here */
89649992Sbostic 	    if (c == '\\') {
89749992Sbostic 		c = DgetC(0);
89849992Sbostic 		if (!any("$\\`", c))
89949992Sbostic 		    unDgetC(c | QUOTE), c = '\\';
90049992Sbostic 		else
90149992Sbostic 		    c |= QUOTE;
90249992Sbostic 	    }
90349992Sbostic 	    *mbp++ = c;
90449992Sbostic 	    if (--mcnt == 0) {
90549992Sbostic 		setname("<<");
90649992Sbostic 		stderror(ERR_NAME | ERR_OVERFLOW);
90749992Sbostic 	    }
90849992Sbostic 	}
90949992Sbostic 	*mbp++ = 0;
9101290Sbill 
91149992Sbostic 	/*
91249992Sbostic 	 * If any ` in line do command substitution
91349992Sbostic 	 */
91449992Sbostic 	mbp = mbuf;
91549992Sbostic 	if (any(short2str(mbp), '`')) {
91649992Sbostic 	    /*
91749992Sbostic 	     * 1 arg to dobackp causes substitution to be literal. Words are
91849992Sbostic 	     * broken only at newlines so that all blanks and tabs are
91949992Sbostic 	     * preserved.  Blank lines (null words) are not discarded.
92049992Sbostic 	     */
92149992Sbostic 	    vp = dobackp(mbuf, 1);
92249992Sbostic 	}
92349992Sbostic 	else
92449992Sbostic 	    /* Setup trivial vector similar to return of dobackp */
92549992Sbostic 	    Dv[0] = mbp, Dv[1] = NULL, vp = Dv;
9261290Sbill 
92749992Sbostic 	/*
92849992Sbostic 	 * Resurrect the words from the command substitution each separated by
92949992Sbostic 	 * a newline.  Note that the last newline of a command substitution
93049992Sbostic 	 * will have been discarded, but we put a newline after the last word
93149992Sbostic 	 * because this represents the newline after the last input line!
93249992Sbostic 	 */
93349992Sbostic 	for (; *vp; vp++) {
93449992Sbostic 	    for (mbp = *vp; *mbp; mbp++) {
93549992Sbostic 		*obp++ = *mbp & TRIM;
93649992Sbostic 		if (--ocnt == 0) {
93749992Sbostic 		    (void) write(0, short2str(obuf), BUFSIZ);
93849992Sbostic 		    obp = obuf;
93949992Sbostic 		    ocnt = BUFSIZ;
9401290Sbill 		}
94149992Sbostic 	    }
94249992Sbostic 	    *obp++ = '\n';
94349992Sbostic 	    if (--ocnt == 0) {
94449992Sbostic 		(void) write(0, short2str(obuf), BUFSIZ);
94549992Sbostic 		obp = obuf;
94649992Sbostic 		ocnt = BUFSIZ;
94749992Sbostic 	    }
9481290Sbill 	}
94949992Sbostic 	if (pargv)
95049992Sbostic 	    blkfree(pargv), pargv = 0;
95149992Sbostic     }
9521290Sbill }
953