Home | History | Annotate | Download | only in csh
      1    0   stevel /*
      2  356   muffin  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
      3    0   stevel  * Use is subject to license terms.
      4    0   stevel  */
      5    0   stevel 
      6    0   stevel /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7    0   stevel /*	  All Rights Reserved  	*/
      8    0   stevel 
      9    0   stevel /*
     10    0   stevel  * Copyright (c) 1980 Regents of the University of California.
     11    0   stevel  * All rights reserved.  The Berkeley Software License Agreement
     12    0   stevel  * specifies the terms and conditions for redistribution.
     13    0   stevel  */
     14    0   stevel 
     15    0   stevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16    0   stevel 
     17    0   stevel #include <unistd.h>		/* for lseek prototype */
     18    0   stevel #include "sh.h"
     19    0   stevel #include "sh.tconst.h"
     20    0   stevel 
     21    0   stevel /*
     22    0   stevel  * C shell
     23    0   stevel  */
     24    0   stevel 
     25    0   stevel /*
     26    0   stevel  * These routines perform variable substitution and quoting via ' and ".
     27    0   stevel  * To this point these constructs have been preserved in the divided
     28    0   stevel  * input words.  Here we expand variables and turn quoting via ' and " into
     29    0   stevel  * QUOTE bits on characters (which prevent further interpretation).
     30    0   stevel  * If the `:q' modifier was applied during history expansion, then
     31    0   stevel  * some QUOTEing may have occurred already, so we dont "trim()" here.
     32    0   stevel  */
     33    0   stevel 
     34    0   stevel int	Dpeekc, Dpeekrd;		/* Peeks for DgetC and Dreadc */
     35    0   stevel tchar	*Dcp, **Dvp;			/* Input vector for Dreadc */
     36    0   stevel 
     37    0   stevel #define	DEOF	-1
     38    0   stevel 
     39    0   stevel #define	unDgetC(c)	Dpeekc = c
     40    0   stevel 
     41  559  nakanon #define	QUOTES		(_Q|_Q1|_ESC)	/* \ ' " ` */
     42    0   stevel 
     43    0   stevel /*
     44    0   stevel  * The following variables give the information about the current
     45    0   stevel  * $ expansion, recording the current word position, the remaining
     46    0   stevel  * words within this expansion, the count of remaining words, and the
     47    0   stevel  * information about any : modifier which is being applied.
     48    0   stevel  */
     49    0   stevel tchar	*dolp;			/* Remaining chars from this word */
     50    0   stevel tchar	**dolnxt;		/* Further words */
     51    0   stevel int	dolcnt;			/* Count of further words */
     52    0   stevel tchar	dolmod;			/* : modifier character */
     53    0   stevel int	dolmcnt;		/* :gx -> 10000, else 1 */
     54    0   stevel 
     55  356   muffin void	Dfix2(tchar **);
     56  356   muffin void	Dgetdol(void);
     57  356   muffin void	setDolp(tchar *);
     58  356   muffin void	unDredc(int);
     59  356   muffin 
     60    0   stevel /*
     61    0   stevel  * Fix up the $ expansions and quotations in the
     62    0   stevel  * argument list to command t.
     63    0   stevel  */
     64  356   muffin void
     65  356   muffin Dfix(struct command *t)
     66    0   stevel {
     67  356   muffin 	tchar **pp;
     68  356   muffin 	tchar *p;
     69    0   stevel 
     70    0   stevel #ifdef TRACE
     71    0   stevel 	tprintf("TRACE- Dfix()\n");
     72    0   stevel #endif
     73    0   stevel 	if (noexec)
     74    0   stevel 		return;
     75    0   stevel 	/* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
     76  559  nakanon 	for (pp = t->t_dcom; p = *pp++; )
     77    0   stevel 		while (*p)
     78    0   stevel 			if (cmap(*p++, _DOL|QUOTES)) {	/* $, \, ', ", ` */
     79    0   stevel 				Dfix2(t->t_dcom);	/* found one */
     80    0   stevel 				blkfree(t->t_dcom);
     81    0   stevel 				t->t_dcom = gargv;
     82    0   stevel 				gargv = 0;
     83    0   stevel 				return;
     84    0   stevel 			}
     85    0   stevel }
     86    0   stevel 
     87    0   stevel /*
     88    0   stevel  * $ substitute one word, for i/o redirection
     89    0   stevel  */
     90    0   stevel tchar *
     91  356   muffin Dfix1(tchar *cp)
     92    0   stevel {
     93    0   stevel 	tchar *Dv[2];
     94    0   stevel 
     95    0   stevel #ifdef TRACE
     96    0   stevel 	tprintf("TRACE- Dfix1()\n");
     97    0   stevel #endif
     98    0   stevel 	if (noexec)
     99    0   stevel 		return (0);
    100    0   stevel 	Dv[0] = cp; Dv[1] = NOSTR;
    101    0   stevel 	Dfix2(Dv);
    102    0   stevel 	if (gargc != 1) {
    103    0   stevel 		setname(cp);
    104    0   stevel 		bferr("Ambiguous");
    105    0   stevel 	}
    106    0   stevel 	cp = savestr(gargv[0]);
    107    0   stevel 	blkfree(gargv), gargv = 0;
    108    0   stevel 	return (cp);
    109    0   stevel }
    110    0   stevel 
    111    0   stevel /*
    112    0   stevel  * Subroutine to do actual fixing after state initialization.
    113    0   stevel  */
    114  356   muffin void
    115  356   muffin Dfix2(tchar **v)
    116    0   stevel {
    117    0   stevel 	tchar *agargv[GAVSIZ];
    118    0   stevel 
    119    0   stevel #ifdef TRACE
    120    0   stevel 	tprintf("TRACE- Dfix2()\n");
    121    0   stevel #endif
    122    0   stevel 	ginit(agargv);			/* Initialize glob's area pointers */
    123  559  nakanon 	Dvp = v; Dcp = S_ /* "" */;	/* Setup input vector for Dreadc */
    124    0   stevel 	unDgetC(0); unDredc(0);		/* Clear out any old peeks (at error) */
    125    0   stevel 	dolp = 0; dolcnt = 0;		/* Clear out residual $ expands (...) */
    126    0   stevel 	while (Dword())
    127    0   stevel 		continue;
    128    0   stevel 	gargv = copyblk(gargv);
    129    0   stevel }
    130    0   stevel 
    131    0   stevel /*
    132    0   stevel  * Get a word.  This routine is analogous to the routine
    133    0   stevel  * word() in sh.lex.c for the main lexical input.  One difference
    134    0   stevel  * here is that we don't get a newline to terminate our expansion.
    135    0   stevel  * Rather, DgetC will return a DEOF when we hit the end-of-input.
    136    0   stevel  */
    137  356   muffin int
    138  356   muffin Dword(void)
    139    0   stevel {
    140  356   muffin 	int c, c1;
    141    0   stevel 	static tchar *wbuf = NULL;
    142    0   stevel 	static int wbufsiz = BUFSIZ;
    143  356   muffin 	int wp = 0;
    144  356   muffin 	bool dolflg;
    145    0   stevel 	bool sofar = 0;
    146    0   stevel #define	DYNAMICBUFFER() \
    147    0   stevel 	do { \
    148    0   stevel 		if (wp >= wbufsiz) { \
    149    0   stevel 			wbufsiz += BUFSIZ; \
    150    0   stevel 			wbuf = xrealloc(wbuf, (wbufsiz+1) * sizeof (tchar)); \
    151    0   stevel 		} \
    152    0   stevel 	} while (0)
    153    0   stevel 
    154    0   stevel #ifdef TRACE
    155    0   stevel 	tprintf("TRACE- Dword()\n");
    156    0   stevel #endif
    157    0   stevel 	if (wbuf == NULL)
    158    0   stevel 		wbuf = xalloc((wbufsiz+1) * sizeof (tchar));
    159    0   stevel loop:
    160    0   stevel 	c = DgetC(DODOL);
    161    0   stevel 	switch (c) {
    162    0   stevel 
    163    0   stevel 	case DEOF:
    164    0   stevel deof:
    165    0   stevel 		if (sofar == 0)
    166    0   stevel 			return (0);
    167    0   stevel 		/* finish this word and catch the code above the next time */
    168    0   stevel 		unDredc(c);
    169    0   stevel 		/* fall into ... */
    170    0   stevel 
    171    0   stevel 	case '\n':
    172    0   stevel 		wbuf[wp] = 0;
    173    0   stevel 		goto ret;
    174    0   stevel 
    175    0   stevel 	case ' ':
    176    0   stevel 	case '\t':
    177    0   stevel 		goto loop;
    178    0   stevel 
    179    0   stevel 	case '`':
    180    0   stevel 		/* We preserve ` quotations which are done yet later */
    181    0   stevel 		wbuf[wp++] = c;
    182    0   stevel 	case '\'':
    183    0   stevel 	case '"':
    184    0   stevel 		/*
    185    0   stevel 		 * Note that DgetC never returns a QUOTES character
    186    0   stevel 		 * from an expansion, so only true input quotes will
    187    0   stevel 		 * get us here or out.
    188    0   stevel 		 */
    189    0   stevel 		c1 = c;
    190    0   stevel 		dolflg = c1 == '"' ? DODOL : 0;
    191    0   stevel 		for (;;) {
    192    0   stevel 			c = DgetC(dolflg);
    193    0   stevel 			if (c == c1)
    194    0   stevel 				break;
    195    0   stevel 			if (c == '\n' || c == DEOF)
    196    0   stevel 				error("Unmatched %c", (tchar) c1);
    197    0   stevel 			if ((c & (QUOTE|TRIM)) == ('\n' | QUOTE))
    198    0   stevel 				--wp;
    199    0   stevel 			DYNAMICBUFFER();
    200    0   stevel 			switch (c1) {
    201    0   stevel 
    202    0   stevel 			case '"':
    203    0   stevel 				/*
    204    0   stevel 				 * Leave any `s alone for later.
    205    0   stevel 				 * Other chars are all quoted, thus `...`
    206    0   stevel 				 * can tell it was within "...".
    207    0   stevel 				 */
    208    0   stevel 				wbuf[wp++] = c == '`' ? '`' : c | QUOTE;
    209    0   stevel 				break;
    210    0   stevel 
    211    0   stevel 			case '\'':
    212    0   stevel 				/* Prevent all further interpretation */
    213    0   stevel 				wbuf[wp++] = c | QUOTE;
    214    0   stevel 				break;
    215    0   stevel 
    216    0   stevel 			case '`':
    217    0   stevel 				/* Leave all text alone for later */
    218    0   stevel 				wbuf[wp++] = c;
    219    0   stevel 				break;
    220    0   stevel 			}
    221    0   stevel 		}
    222    0   stevel 		if (c1 == '`') {
    223    0   stevel 			DYNAMICBUFFER();
    224    0   stevel 			wbuf[wp++] = '`';
    225    0   stevel 		}
    226    0   stevel 		goto pack;		/* continue the word */
    227    0   stevel 
    228    0   stevel 	case '\\':
    229    0   stevel 		c = DgetC(0);		/* No $ subst! */
    230    0   stevel 		if (c == '\n' || c == DEOF)
    231    0   stevel 			goto loop;
    232    0   stevel 		c |= QUOTE;
    233    0   stevel 		break;
    234    0   stevel #ifdef MBCHAR /* Could be a space char from aux. codeset. */
    235    0   stevel 	default:
    236    0   stevel 		if (isauxsp(c)) goto loop;
    237    0   stevel #endif /* MBCHAR */
    238    0   stevel 	}
    239    0   stevel 	unDgetC(c);
    240    0   stevel pack:
    241    0   stevel 	sofar = 1;
    242    0   stevel 	/* pack up more characters in this word */
    243    0   stevel 	for (;;) {
    244    0   stevel 		c = DgetC(DODOL);
    245    0   stevel 		if (c == '\\') {
    246    0   stevel 			c = DgetC(0);
    247    0   stevel 			if (c == DEOF)
    248    0   stevel 				goto deof;
    249    0   stevel 			if (c == '\n')
    250    0   stevel 				c = ' ';
    251    0   stevel 			else
    252    0   stevel 				c |= QUOTE;
    253    0   stevel 		}
    254    0   stevel 		if (c == DEOF)
    255    0   stevel 			goto deof;
    256    0   stevel 		if (cmap(c, _SP|_NL|_Q|_Q1) ||
    257    0   stevel 		    isauxsp(c)) {		/* sp \t\n'"` or aux. sp */
    258    0   stevel 			unDgetC(c);
    259    0   stevel 			if (cmap(c, QUOTES))
    260    0   stevel 				goto loop;
    261    0   stevel 			DYNAMICBUFFER();
    262    0   stevel 			wbuf[wp++] = 0;
    263    0   stevel 			goto ret;
    264    0   stevel 		}
    265    0   stevel 		DYNAMICBUFFER();
    266    0   stevel 		wbuf[wp++] = c;
    267    0   stevel 	}
    268    0   stevel ret:
    269    0   stevel 	Gcat(S_ /* "" */, wbuf);
    270    0   stevel 	return (1);
    271    0   stevel }
    272    0   stevel 
    273    0   stevel /*
    274    0   stevel  * Get a character, performing $ substitution unless flag is 0.
    275    0   stevel  * Any QUOTES character which is returned from a $ expansion is
    276    0   stevel  * QUOTEd so that it will not be recognized above.
    277    0   stevel  */
    278  356   muffin int
    279  356   muffin DgetC(int flag)
    280    0   stevel {
    281  356   muffin 	int c;
    282    0   stevel 
    283    0   stevel top:
    284    0   stevel 	if (c = Dpeekc) {
    285    0   stevel 		Dpeekc = 0;
    286    0   stevel 		return (c);
    287    0   stevel 	}
    288    0   stevel 	if (lap) {
    289    0   stevel 		c = *lap++ & (QUOTE|TRIM);
    290    0   stevel 		if (c == 0) {
    291    0   stevel 			lap = 0;
    292    0   stevel 			goto top;
    293    0   stevel 		}
    294    0   stevel quotspec:
    295    0   stevel 		/*
    296    0   stevel 		 *	don't quote things if there was an error (err!=0)
    297    0   stevel 		 * 	the input is original, not from a substitution and
    298    0   stevel 		 *	therefore should not be quoted
    299    0   stevel 		 */
    300    0   stevel 		if (!err && cmap(c, QUOTES))
    301    0   stevel 			return (c | QUOTE);
    302    0   stevel 		return (c);
    303    0   stevel 	}
    304    0   stevel 	if (dolp) {
    305    0   stevel 		if (c = *dolp++ & (QUOTE|TRIM))
    306    0   stevel 			goto quotspec;
    307    0   stevel 		if (dolcnt > 0) {
    308    0   stevel 			setDolp(*dolnxt++);
    309    0   stevel 			--dolcnt;
    310    0   stevel 			return (' ');
    311    0   stevel 		}
    312    0   stevel 		dolp = 0;
    313    0   stevel 	}
    314    0   stevel 	if (dolcnt > 0) {
    315    0   stevel 		setDolp(*dolnxt++);
    316    0   stevel 		--dolcnt;
    317    0   stevel 		goto top;
    318    0   stevel 	}
    319    0   stevel 	c = Dredc();
    320    0   stevel 	if (c == '$' && flag) {
    321    0   stevel 		Dgetdol();
    322    0   stevel 		goto top;
    323    0   stevel 	}
    324    0   stevel 	return (c);
    325    0   stevel }
    326    0   stevel 
    327    0   stevel tchar *nulvec[] = { 0 };
    328    0   stevel struct	varent nulargv = { nulvec, S_argv, 0 };
    329    0   stevel 
    330    0   stevel /*
    331    0   stevel  * Handle the multitudinous $ expansion forms.
    332    0   stevel  * Ugh.
    333    0   stevel  */
    334  356   muffin void
    335  356   muffin Dgetdol(void)
    336    0   stevel {
    337  356   muffin 	tchar *np;
    338  356   muffin 	struct varent *vp;
    339    0   stevel 	tchar name[MAX_VREF_LEN];
    340    0   stevel 	int c, sc;
    341    0   stevel 	int subscr = 0, lwb = 1, upb = 0;
    342    0   stevel 	bool dimen = 0, bitset = 0;
    343  559  nakanon 	tchar wbuf[BUFSIZ + MB_LEN_MAX]; /* read_ may return extra bytes */
    344    0   stevel 
    345    0   stevel #ifdef TRACE
    346    0   stevel 	tprintf("TRACE- Dgetdol()\n");
    347    0   stevel #endif
    348    0   stevel 	dolmod = dolmcnt = 0;
    349    0   stevel 	c = sc = DgetC(0);
    350    0   stevel 	if (c == '{')
    351    0   stevel 		c = DgetC(0);		/* sc is { to take } later */
    352    0   stevel 	if ((c & TRIM) == '#')
    353    0   stevel 		dimen++, c = DgetC(0);		/* $# takes dimension */
    354    0   stevel 	else if (c == '?')
    355    0   stevel 		bitset++, c = DgetC(0);		/* $? tests existence */
    356    0   stevel 	switch (c) {
    357  559  nakanon 
    358    0   stevel 	case '$':
    359    0   stevel 		if (dimen || bitset)
    360    0   stevel syntax:
    361    0   stevel 		error("Variable syntax");  /* No $?$, $#$ */
    362    0   stevel 		setDolp(doldol);
    363    0   stevel 		goto eatbrac;
    364    0   stevel 
    365    0   stevel 	case '<'|QUOTE:
    366    0   stevel 		if (dimen || bitset)
    367    0   stevel 			goto syntax;		/* No $?<, $#< */
    368    0   stevel 		for (np = wbuf; read_(OLDSTD, np, 1) == 1; np++) {
    369    0   stevel 			if (np >= &wbuf[BUFSIZ-1])
    370    0   stevel 				error("$< line too long");
    371    0   stevel 			if (*np <= 0 || *np == '\n')
    372    0   stevel 				break;
    373    0   stevel 		}
    374    0   stevel 		*np = 0;
    375    0   stevel 		/*
    376    0   stevel 		 * KLUDGE: dolmod is set here because it will
    377    0   stevel 		 * cause setDolp to call domod and thus to copy wbuf.
    378    0   stevel 		 * Otherwise setDolp would use it directly. If we saved
    379    0   stevel 		 * it ourselves, no one would know when to free it.
    380    0   stevel 		 * The actual function of the 'q' causes filename
    381    0   stevel 		 * expansion not to be done on the interpolated value.
    382    0   stevel 		 */
    383    0   stevel 		dolmod = 'q';
    384    0   stevel 		dolmcnt = 10000;
    385    0   stevel 		setDolp(wbuf);
    386    0   stevel 		goto eatbrac;
    387    0   stevel 
    388    0   stevel 	case DEOF:
    389    0   stevel 	case '\n':
    390    0   stevel 		goto syntax;
    391    0   stevel 
    392    0   stevel 	case '*':
    393    0   stevel 		(void) strcpy_(name, S_argv);
    394    0   stevel 		vp = adrof(S_argv);
    395    0   stevel 		subscr = -1;			/* Prevent eating [...] */
    396    0   stevel 		break;
    397    0   stevel 
    398    0   stevel 	default:
    399    0   stevel 		np = name;
    400    0   stevel 		if (digit(c)) {
    401    0   stevel 			if (dimen)
    402    0   stevel 				goto syntax;	/* No $#1, e.g. */
    403    0   stevel 			subscr = 0;
    404    0   stevel 			do {
    405    0   stevel 				subscr = subscr * 10 + c - '0';
    406    0   stevel 				c = DgetC(0);
    407    0   stevel 			} while (digit(c));
    408    0   stevel 			unDredc(c);
    409    0   stevel 			if (subscr < 0)
    410    0   stevel 				error("Subscript out of range");
    411    0   stevel 			if (subscr == 0) {
    412    0   stevel 				if (bitset) {
    413  559  nakanon 					dolp = file ? S_1 /* "1" */ : S_0 /* "0" */;
    414    0   stevel 					goto eatbrac;
    415    0   stevel 				}
    416    0   stevel 				if (file == 0)
    417    0   stevel 					error("No file for $0");
    418    0   stevel 				setDolp(file);
    419    0   stevel 				goto eatbrac;
    420    0   stevel 			}
    421    0   stevel 			if (bitset)
    422    0   stevel 				goto syntax;
    423    0   stevel 			vp = adrof(S_argv);
    424    0   stevel 			if (vp == 0) {
    425    0   stevel 				vp = &nulargv;
    426    0   stevel 				goto eatmod;
    427    0   stevel 			}
    428    0   stevel 			break;
    429    0   stevel 		}
    430    0   stevel 		if (!alnum(c))
    431    0   stevel 			goto syntax;
    432    0   stevel 		for (;;) {
    433    0   stevel 			*np++ = c;
    434    0   stevel 			c = DgetC(0);
    435    0   stevel 			if (!alnum(c))
    436    0   stevel 				break;
    437    0   stevel 			/* if variable name is > 20, complain */
    438    0   stevel 			if (np >= &name[MAX_VAR_LEN])
    439    0   stevel 				error("Variable name too long");
    440    0   stevel 
    441    0   stevel 		}
    442    0   stevel 		*np++ = 0;
    443    0   stevel 		unDredc(c);
    444    0   stevel 		vp = adrof(name);
    445    0   stevel 	}
    446    0   stevel 	if (bitset) {
    447  559  nakanon 		/*
    448  559  nakanon 		 * getenv() to getenv_(), because 'name''s type is now tchar *
    449  559  nakanon 		 * no need to xalloc
    450  559  nakanon 		 */
    451  559  nakanon 		dolp = (vp || getenv_(name)) ? S_1 /* "1" */ : S_0 /* "0" */;
    452    0   stevel 		goto eatbrac;
    453    0   stevel 	}
    454    0   stevel 	if (vp == 0) {
    455  559  nakanon 		/*
    456  559  nakanon 		 * getenv() to getenv_(), because 'name''s type is now tchar *
    457  559  nakanon 		 * no need to xalloc
    458  559  nakanon 		 */
    459    0   stevel 		np = getenv_(name);
    460    0   stevel 		if (np) {
    461    0   stevel 			addla(np);
    462    0   stevel 			goto eatbrac;
    463    0   stevel 		}
    464    0   stevel 		udvar(name);
    465    0   stevel 		/*NOTREACHED*/
    466    0   stevel 	}
    467    0   stevel 	c = DgetC(0);
    468    0   stevel 	upb = blklen(vp->vec);
    469    0   stevel 	if (dimen == 0 && subscr == 0 && c == '[') {
    470    0   stevel 		np = name;
    471    0   stevel 		for (;;) {
    472    0   stevel 			c = DgetC(DODOL);	/* Allow $ expand within [ ] */
    473    0   stevel 			if (c == ']')
    474    0   stevel 				break;
    475    0   stevel 			if (c == '\n' || c == DEOF)
    476    0   stevel 				goto syntax;
    477    0   stevel 			if (np >= &name[MAX_VREF_LEN])
    478    0   stevel 				error("Variable reference too long");
    479    0   stevel 			*np++ = c;
    480    0   stevel 		}
    481    0   stevel 		*np = 0, np = name;
    482    0   stevel 		if (dolp || dolcnt)		/* $ exp must end before ] */
    483    0   stevel 			goto syntax;
    484    0   stevel 		if (!*np)
    485    0   stevel 			goto syntax;
    486    0   stevel 		if (digit(*np)) {
    487  356   muffin 			int i = 0;
    488    0   stevel 
    489    0   stevel 			while (digit(*np))
    490    0   stevel 				i = i * 10 + *np++ - '0';
    491  559  nakanon /*			if ((i < 0 || i > upb) && !any(*np, "-*")) { */
    492  559  nakanon 			if ((i < 0 || i > upb) && (*np != '-') && (*np != '*')) {
    493    0   stevel oob:
    494    0   stevel 				setname(vp->v_name);
    495    0   stevel 				error("Subscript out of range");
    496    0   stevel 			}
    497    0   stevel 			lwb = i;
    498    0   stevel 			if (!*np)
    499  559  nakanon 				upb = lwb, np = S_AST /* "*" */;
    500    0   stevel 		}
    501    0   stevel 		if (*np == '*')
    502    0   stevel 			np++;
    503    0   stevel 		else if (*np != '-')
    504    0   stevel 			goto syntax;
    505    0   stevel 		else {
    506  356   muffin 			int i = upb;
    507    0   stevel 
    508    0   stevel 			np++;
    509    0   stevel 			if (digit(*np)) {
    510    0   stevel 				i = 0;
    511    0   stevel 				while (digit(*np))
    512    0   stevel 					i = i * 10 + *np++ - '0';
    513    0   stevel 				if (i < 0 || i > upb)
    514    0   stevel 					goto oob;
    515    0   stevel 			}
    516    0   stevel 			if (i < lwb)
    517    0   stevel 				upb = lwb - 1;
    518    0   stevel 			else
    519    0   stevel 				upb = i;
    520    0   stevel 		}
    521    0   stevel 		if (lwb == 0) {
    522    0   stevel 			if (upb != 0)
    523    0   stevel 				goto oob;
    524    0   stevel 			upb = -1;
    525    0   stevel 		}
    526    0   stevel 		if (*np)
    527    0   stevel 			goto syntax;
    528    0   stevel 	} else {
    529    0   stevel 		if (subscr > 0)
    530    0   stevel 			if (subscr > upb)
    531    0   stevel 				lwb = 1, upb = 0;
    532    0   stevel 			else
    533    0   stevel 				lwb = upb = subscr;
    534    0   stevel 		unDredc(c);
    535    0   stevel 	}
    536    0   stevel 	if (dimen) {
    537    0   stevel 		tchar *cp = putn(upb - lwb + 1);
    538    0   stevel 
    539    0   stevel 		addla(cp);
    540    0   stevel 		xfree(cp);
    541    0   stevel 	} else {
    542    0   stevel eatmod:
    543    0   stevel 		c = DgetC(0);
    544    0   stevel 		if (c == ':') {
    545    0   stevel 			c = DgetC(0), dolmcnt = 1;
    546    0   stevel 			if (c == 'g')
    547    0   stevel 				c = DgetC(0), dolmcnt = 10000;
    548    0   stevel 			if (!any(c, S_htrqxe))
    549    0   stevel 				error("Bad : mod in $");
    550    0   stevel 			dolmod = c;
    551    0   stevel 			if (c == 'q')
    552    0   stevel 				dolmcnt = 10000;
    553    0   stevel 		} else
    554    0   stevel 			unDredc(c);
    555    0   stevel 		dolnxt = &vp->vec[lwb - 1];
    556    0   stevel 		dolcnt = upb - lwb + 1;
    557    0   stevel 	}
    558    0   stevel eatbrac:
    559    0   stevel 	if (sc == '{') {
    560    0   stevel 		c = Dredc();
    561    0   stevel 		if (c != '}')
    562    0   stevel 			goto syntax;
    563    0   stevel 	}
    564    0   stevel }
    565    0   stevel 
    566  356   muffin void
    567  356   muffin setDolp(tchar *cp)
    568    0   stevel {
    569  356   muffin 	tchar *dp;
    570    0   stevel 
    571    0   stevel #ifdef TRACE
    572    0   stevel 	tprintf("TRACE- setDolp()\n");
    573    0   stevel #endif
    574    0   stevel 	if (dolmod == 0 || dolmcnt == 0) {
    575    0   stevel 		dolp = cp;
    576    0   stevel 		return;
    577    0   stevel 	}
    578    0   stevel 	dp = domod(cp, dolmod);
    579    0   stevel 	if (dp) {
    580    0   stevel 		dolmcnt--;
    581    0   stevel 		addla(dp);
    582    0   stevel 		xfree(dp);
    583    0   stevel 	} else
    584    0   stevel 		addla(cp);
    585  559  nakanon 	dolp = S_ /* "" */;
    586    0   stevel }
    587    0   stevel 
    588  356   muffin void
    589  356   muffin unDredc(int c)
    590    0   stevel {
    591    0   stevel 
    592    0   stevel 	Dpeekrd = c;
    593    0   stevel }
    594    0   stevel 
    595  356   muffin int
    596    0   stevel Dredc()
    597    0   stevel {
    598  356   muffin 	int c;
    599    0   stevel 
    600    0   stevel 	if (c = Dpeekrd) {
    601    0   stevel 		Dpeekrd = 0;
    602    0   stevel 		return (c);
    603    0   stevel 	}
    604    0   stevel 	if (Dcp && (c = *Dcp++))
    605    0   stevel 		return (c&(QUOTE|TRIM));
    606    0   stevel 	if (*Dvp == 0) {
    607    0   stevel 		Dcp = 0;
    608    0   stevel 		return (DEOF);
    609    0   stevel 	}
    610    0   stevel 	Dcp = *Dvp++;
    611    0   stevel 	return (' ');
    612    0   stevel }
    613    0   stevel 
    614  356   muffin void
    615  356   muffin Dtestq(int c)
    616    0   stevel {
    617    0   stevel 
    618    0   stevel 	if (cmap(c, QUOTES))
    619    0   stevel 		gflag = 1;
    620    0   stevel }
    621    0   stevel 
    622    0   stevel /*
    623    0   stevel  * Form a shell temporary file (in unit 0) from the words
    624    0   stevel  * of the shell input up to a line the same as "term".
    625    0   stevel  * Unit 0 should have been closed before this call.
    626    0   stevel  */
    627  356   muffin void
    628  356   muffin heredoc(tchar *term)
    629    0   stevel {
    630  356   muffin 	int c;
    631    0   stevel 	tchar *Dv[2];
    632    0   stevel 	tchar obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ];
    633    0   stevel 	int ocnt, lcnt, mcnt;
    634  356   muffin 	tchar *lbp, *obp, *mbp;
    635    0   stevel 	tchar **vp;
    636    0   stevel 	bool quoted;
    637    0   stevel 	tchar shtemp[] = {'/', 't', 'm', 'p', '/', 's', 'h', 'X', 'X', 'X',
    638    0   stevel 'X', 'X', 'X', 0};
    639    0   stevel 	int fd1;
    640    0   stevel 
    641    0   stevel #ifdef TRACE
    642    0   stevel 	tprintf("TRACE- heredoc()\n");
    643    0   stevel #endif
    644    0   stevel 	if ((fd1 = mkstemp_(shtemp)) < 0)
    645    0   stevel 		Perror(shtemp);
    646    0   stevel 	(void) unlink_(shtemp);			/* 0 0 inode! */
    647    0   stevel 	unsetfd(fd1);
    648    0   stevel 	Dv[0] = term; Dv[1] = NOSTR; gflag = 0;
    649    0   stevel 	trim(Dv); rscan(Dv, Dtestq); quoted = gflag;
    650    0   stevel 	ocnt = BUFSIZ; obp = obuf;
    651    0   stevel 	for (;;) {
    652    0   stevel 		/*
    653    0   stevel 		 * Read up a line
    654    0   stevel 		 */
    655    0   stevel 		lbp = lbuf; lcnt = BUFSIZ - 4;
    656    0   stevel 		for (;;) {
    657    0   stevel 			c = readc(1);		/* 1 -> Want EOF returns */
    658    0   stevel 			if (c < 0) {
    659    0   stevel 				setname(term);
    660    0   stevel 				bferr("<< terminator not found");
    661    0   stevel 			}
    662    0   stevel 			if (c == '\n')
    663    0   stevel 				break;
    664    0   stevel 			if (c &= TRIM) {
    665    0   stevel 				*lbp++ = c;
    666    0   stevel 				if (--lcnt < 0) {
    667  559  nakanon 					setname(S_LESLES /* "<<" */);
    668    0   stevel 					error("Line overflow");
    669  559  nakanon 				}
    670    0   stevel 			}
    671    0   stevel 		}
    672    0   stevel 		*lbp = 0;
    673    0   stevel 
    674    0   stevel 		/*
    675    0   stevel 		 * Compare to terminator -- before expansion
    676    0   stevel 		 */
    677    0   stevel 		if (eq(lbuf, term)) {
    678    0   stevel 			(void) write_(0, obuf, BUFSIZ - ocnt);
    679    0   stevel 			(void) lseek(0, (off_t)0, 0);
    680    0   stevel 			return;
    681    0   stevel 		}
    682    0   stevel 
    683    0   stevel 		/*
    684    0   stevel 		 * If term was quoted or -n just pass it on
    685    0   stevel 		 */
    686    0   stevel 		if (quoted || noexec) {
    687    0   stevel 			*lbp++ = '\n'; *lbp = 0;
    688  559  nakanon 			for (lbp = lbuf; c = *lbp++; ) {
    689    0   stevel 				*obp++ = c;
    690    0   stevel 				if (--ocnt == 0) {
    691    0   stevel 					(void) write_(0, obuf, BUFSIZ);
    692    0   stevel 					obp = obuf; ocnt = BUFSIZ;
    693    0   stevel 				}
    694    0   stevel 			}
    695    0   stevel 			continue;
    696    0   stevel 		}
    697    0   stevel 
    698    0   stevel 		/*
    699    0   stevel 		 * Term wasn't quoted so variable and then command
    700    0   stevel 		 * expand the input line
    701    0   stevel 		 */
    702    0   stevel 		Dcp = lbuf; Dvp = Dv + 1; mbp = mbuf; mcnt = BUFSIZ - 4;
    703    0   stevel 		for (;;) {
    704    0   stevel 			c = DgetC(DODOL);
    705    0   stevel 			if (c == DEOF)
    706    0   stevel 				break;
    707    0   stevel 			if ((c &= TRIM) == 0)
    708    0   stevel 				continue;
    709    0   stevel 			/* \ quotes \ $ ` here */
    710  559  nakanon 			if (c == '\\') {
    711    0   stevel 				c = DgetC(0);
    712  559  nakanon /*				if (!any(c, "$\\`")) */
    713  559  nakanon 				if ((c != '$') && (c != '\\') && (c != '`'))
    714    0   stevel 					unDgetC(c | QUOTE), c = '\\';
    715    0   stevel 				else
    716    0   stevel 					c |= QUOTE;
    717    0   stevel 			}
    718    0   stevel 			*mbp++ = c;
    719    0   stevel 			if (--mcnt == 0) {
    720  559  nakanon 				setname(S_LESLES /* "<<" */);
    721    0   stevel 				bferr("Line overflow");
    722    0   stevel 			}
    723    0   stevel 		}
    724    0   stevel 		*mbp++ = 0;
    725    0   stevel 
    726    0   stevel 		/*
    727    0   stevel 		 * If any ` in line do command substitution
    728    0   stevel 		 */
    729    0   stevel 		mbp = mbuf;
    730    0   stevel 		if (any('`', mbp)) {
    731    0   stevel 			/*
    732    0   stevel 			 * 1 arg to dobackp causes substitution to be literal.
    733    0   stevel 			 * Words are broken only at newlines so that all blanks
    734    0   stevel 			 * and tabs are preserved.  Blank lines (null words)
    735    0   stevel 			 * are not discarded.
    736    0   stevel 			 */
    737    0   stevel 			vp = dobackp(mbuf, 1);
    738    0   stevel 		} else
    739    0   stevel 			/* Setup trivial vector similar to return of dobackp */
    740    0   stevel 			Dv[0] = mbp, Dv[1] = NOSTR, vp = Dv;
    741    0   stevel 
    742    0   stevel 		/*
    743    0   stevel 		 * Resurrect the words from the command substitution
    744    0   stevel 		 * each separated by a newline.  Note that the last
    745    0   stevel 		 * newline of a command substitution will have been
    746    0   stevel 		 * discarded, but we put a newline after the last word
    747    0   stevel 		 * because this represents the newline after the last
    748    0   stevel 		 * input line!
    749    0   stevel 		 */
    750    0   stevel 		for (; *vp; vp++) {
    751    0   stevel 			for (mbp = *vp; *mbp; mbp++) {
    752    0   stevel 				*obp++ = *mbp & TRIM;
    753    0   stevel 				if (--ocnt == 0) {
    754    0   stevel 					(void) write_(0, obuf, BUFSIZ);
    755    0   stevel 					obp = obuf; ocnt = BUFSIZ;
    756    0   stevel 				}
    757    0   stevel 			}
    758    0   stevel 			*obp++ = '\n';
    759    0   stevel 			if (--ocnt == 0) {
    760    0   stevel 				(void) write_(0, obuf, BUFSIZ);
    761    0   stevel 				obp = obuf; ocnt = BUFSIZ;
    762    0   stevel 			}
    763    0   stevel 		}
    764    0   stevel 		if (pargv)
    765    0   stevel 			blkfree(pargv), pargv = 0;
    766    0   stevel 	}
    767    0   stevel }
    768