if (vq->flag & RDONLY) {
int no_ro_check = error_ok & 0x4; error_ok &= ~0x4; if ((vq->flag & RDONLY) && !no_ro_check) {
static char	*formatstr	ARGS((struct tbl *vp, const char *s));static void	export		ARGS((struct tbl *vp, const char *val));static int	special		ARGS((const char *name));static void	unspecial	ARGS((const char *name));static void	getspec		ARGS((struct tbl *vp));static void	setspec		ARGS((struct tbl *vp));static void	unsetspec	ARGS((struct tbl *vp));static struct tbl *arraysearch  ARGS((struct tbl *, int));/* * create a new block for function calls and simple commands * assume caller has allocated and set up e->loc */voidnewblock(){	register struct block *l;	static char *const empty[] = {null};	l = (struct block *) alloc(sizeof(struct block), ATEMP);	l->flags = 0;	ainit(&l->area); /* todo: could use e->area (l->area => l->areap) */	if (!e->loc) {		l->argc = 0;		l->argv = (char **) empty;	} else {		l->argc = e->loc->argc;		l->argv = e->loc->argv;	}	l->exit = l->error = NULL;	tinit(&l->vars, &l->area, 0);	tinit(&l->funs, &l->area, 0);	l->next = e->loc;	e->loc = l;}/* * pop a block handling special variables */voidpopblock(){	register struct block *l = e->loc;	register struct tbl *vp, **vpp = l->vars.tbls, *vq;	register int i;	e->loc = l->next;	/* pop block */	for (i = l->vars.size; --i >= 0; )		if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {			if ((vq = global(vp->name))->flag & ISSET)				setspec(vq);			else				unsetspec(vq);		}	if (l->flags & BF_DOGETOPTS)		user_opt = l->getopts_state;	afreeall(&l->area);	afree(l, ATEMP);}/* called by main() to initialize variable data structures */voidinitvar(){	static const struct {		const char *name;		int v;	} names[] = {			{ "COLUMNS",		V_COLUMNS },			{ "IFS",		V_IFS },			{ "OPTIND",		V_OPTIND },			{ "PATH",		V_PATH },			{ "POSIXLY_CORRECT",	V_POSIXLY_CORRECT },			{ "TMPDIR",		V_TMPDIR },#ifdef HISTORY			{ "HISTFILE",		V_HISTFILE },			{ "HISTSIZE",		V_HISTSIZE },#endif /* HISTORY */#ifdef EDIT			{ "EDITOR",		V_EDITOR },			{ "VISUAL",		V_VISUAL },#endif /* EDIT */#ifdef KSH			{ "MAIL",		V_MAIL },			{ "MAILCHECK",		V_MAILCHECK },			{ "MAILPATH",		V_MAILPATH },			{ "RANDOM",		V_RANDOM },			{ "SECONDS",		V_SECONDS },			{ "TMOUT",		V_TMOUT },#endif /* KSH */			{ "LINENO",		V_LINENO },			{ (char *) 0,	0 }		};	int i;	struct tbl *tp;	tinit(&specials, APERM, 32); /* must be 2^n (currently 17 specials) */	for (i = 0; names[i].name; i++) {		tp = tenter(&specials, names[i].name, hash(names[i].name));		tp->flag = DEFINED|ISSET;		tp->type = names[i].v;	}}/* Used to calculate an array index for global()/local().  Sets *arrayp to * non-zero if this is an array, sets *valp to the array index, returns * the basename of the array. */const char *array_index_calc(n, arrayp, valp)	const char *n;	bool_t *arrayp;	int *valp;{	const char *p;	int len;	*arrayp = FALSE;	p = skip_varname(n, FALSE);	if (p != n && *p == '[' && (len = array_ref_len(p))) {		char *sub, *tmp;		long rval;		/* Calculate the value of the subscript */		*arrayp = TRUE;		tmp = str_nsave(p+1, len-2, ATEMP);		sub = substitute(tmp, 0);		afree(tmp, ATEMP);		n = str_nsave(n, p - n, ATEMP);		evaluate(sub, &rval, KSH_UNWIND_ERROR);		if (rval < 0 || rval > ARRAYMAX)			errorf("%s: subscript out of range", n);		*valp = rval;		afree(sub, ATEMP);	}	return n;}/* * Search for variable, if not found create globally. */struct tbl *global(n)	register const char *n;{	register struct block *l = e->loc;	register struct tbl *vp;	register int c;	unsigned h;	bool_t	 array;	int	 val;	/* Check to see if this is an array */	n = array_index_calc(n, &array, &val);	h = hash(n);	c = n[0];	if (!letter(c)) {		if (array)			errorf("bad substitution");		vp = &vtemp;		vp->flag = DEFINED;		vp->type = 0;		vp->areap = ATEMP;		*vp->name = c;		if (digit(c)) {			for (c = 0; digit(*n); n++)				c = c*10 + *n-'0';			if (c <= l->argc)				/* setstr can't fail here */				setstr(vp, l->argv[c], KSH_RETURN_ERROR);			vp->flag |= RDONLY;			return vp;		}		vp->flag |= RDONLY;		if (n[1] != '\0')			return vp;		vp->flag |= ISSET|INTEGER;		switch (c) {		  case '$':			vp->val.i = kshpid;			break;		  case '!':			/* If no job, expand to nothing */			if ((vp->val.i = j_async()) == 0)				vp->flag &= ~(ISSET|INTEGER);			break;		  case '?':			vp->val.i = exstat;			break;		  case '#':			vp->val.i = l->argc;			break;		  case '-':			vp->flag &= ~INTEGER;			vp->val.s = getoptions();			break;		  default:			vp->flag &= ~(ISSET|INTEGER);		}		return vp;	}	for (l = e->loc; ; l = l->next) {		vp = tsearch(&l->vars, n, h);		if (vp != NULL) {			if (array)				return arraysearch(vp, val);			else				return vp;		}		if (l->next == NULL)			break;	}	vp = tenter(&l->vars, n, h);	if (array)		vp = arraysearch(vp, val);	vp->flag |= DEFINED;	if (special(n))		vp->flag |= SPECIAL;	return vp;}/* * Search for local variable, if not found create locally. */struct tbl *local(n, copy)	register const char *n;	bool_t copy;{	register struct block *l = e->loc;	register struct tbl *vp;	unsigned h;	bool_t	 array;	int	 val;	/* Check to see if this is an array */	n = array_index_calc(n, &array, &val);	h = hash(n);	if (!letter(*n)) {		vp = &vtemp;		vp->flag = DEFINED|RDONLY;		vp->type = 0;		vp->areap = ATEMP;		return vp;	}	vp = tenter(&l->vars, n, h);	if (copy && !(vp->flag & DEFINED)) {		struct block *ll = l;		struct tbl *vq = (struct tbl *) 0;		while ((ll = ll->next) && !(vq = tsearch(&ll->vars, n, h)))			;		if (vq) {			vp->flag |= vq->flag & (EXPORT|INTEGER|RDONLY						|LJUST|RJUST|ZEROFIL						|LCASEV|UCASEV_AL|INT_U|INT_L);			if (vq->flag & INTEGER)				vp->type = vq->type;			vp->u2.field = vq->u2.field;		}	}	if (array)		vp = arraysearch(vp, val);	vp->flag |= DEFINED;	if (special(n))		vp->flag |= SPECIAL;	return vp;}/* get variable string value */char *str_val(vp)	register struct tbl *vp;{	char *s;	if ((vp->flag&SPECIAL))		getspec(vp);	if (!(vp->flag&ISSET))		s = null;		/* special to dollar() */	else if (!(vp->flag&INTEGER))	/* string source */		s = vp->val.s + vp->type;	else {				/* integer source */		/* worst case number length is when base=2, so use BITS(long) */			     /* minus base #     number    null */		static char strbuf[1 + 2 + 1 + BITS(long) + 1];		const char *digits = (vp->flag & UCASEV_AL) ?				  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"				: "0123456789abcdefghijklmnopqrstuvwxyz";		register unsigned long n;		register int base;		s = strbuf + sizeof(strbuf);		if (vp->flag & INT_U)			n = (unsigned long) vp->val.i;		else			n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;		base = (vp->type == 0) ? 10 : vp->type;		*--s = '\0';		do {			*--s = digits[n % base];			n /= base;		} while (n != 0);		if (base != 10) {			*--s = '#';			*--s = digits[base % 10];			if (base >= 10)				*--s = digits[base / 10];		}		if (!(vp->flag & INT_U) && vp->val.i < 0)			*--s = '-';		if (vp->flag & (RJUST|LJUST)) /* case already dealt with */			s = formatstr(vp, s);	}	return s;}/* get variable integer value, with error checking */longintval(vp)	register struct tbl *vp;{	long num;	int base;	base = getint(vp, &num);	if (base == -1)		/* XXX check calls - is error here ok by POSIX? */		errorf("%s: bad number", str_val(vp));	return num;}/* set variable to string value */intsetstr(vq, s, error_ok)	register struct tbl *vq;	const char *s;	int error_ok;{	if (vq->flag & RDONLY) {		warningf(TRUE, "%s: is read only", vq->name);		if (!error_ok)			errorf(null);		return 0;	}	if (!(vq->flag&INTEGER)) { /* string dest */		if ((vq->flag&ALLOC)) {			/* debugging */			if (s >= vq->val.s			    && s <= vq->val.s + strlen(vq->val.s))				internal_errorf(TRUE,				    "setstr: %s=%s: assigning to self",				    vq->name, s);			afree((void*)vq->val.s, vq->areap);		}		vq->flag &= ~(ISSET|ALLOC);		vq->type = 0;		if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))			s = formatstr(vq, s);		if ((vq->flag&EXPORT))			export(vq, s);		else {			vq->val.s = str_save(s, vq->areap);			if (vq->val.s)		/* <sjg> don't lie */				vq->flag |= ALLOC;		}	} else			/* integer dest */		if (!v_evaluate(vq, s, error_ok))			return 0;	vq->flag |= ISSET;	if ((vq->flag&SPECIAL))		setspec(vq);	return 1;}/* set variable to integer */voidsetint(vq, n)	register struct tbl *vq;	long n;{	if (!(vq->flag&INTEGER)) {		register struct tbl *vp = &vtemp;		vp->flag = (ISSET|INTEGER);		vp->type = 0;		vp->areap = ATEMP;		vp->val.i = n;		/* setstr can't fail here */		setstr(vq, str_val(vp), KSH_RETURN_ERROR);	} else		vq->val.i = n;	vq->flag |= ISSET;	if ((vq->flag&SPECIAL))		setspec(vq);}intgetint(vp, nump)	struct tbl *vp;	long *nump;{	register char *s;	register int c;	int base, neg;	int have_base = 0;	long num;	if (vp->flag&SPECIAL)		getspec(vp);	/* XXX is it possible for ISSET to be set and val.s to be 0? */	if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))		return -1;	if (vp->flag&INTEGER) {		*nump = vp->val.i;		return vp->type;	}	s = vp->val.s + vp->type;	if (s == NULL)	/* redundent given initial test */		s = null;	base = 10;	num = 0;	neg = 0;	for (c = *s++; c ; c = *s++) {		if (c == '-') {			neg++;		} else if (c == '#') {			base = (int) num;			if (have_base || base < 2 || base > 36)				return -1;			num = 0;			have_base = 1;		} else if (letnum(c)) {			if (isdigit(c))				c -= '0';			else if (islower(c))				c -= 'a' - 10; /* todo: assumes ascii */			else if (isupper(c))				c -= 'A' - 10; /* todo: assumes ascii */			else				c = -1; /* _: force error */			if (c < 0 || c >= base)				return -1;			num = num * base + c;		} else			return -1;	}	if (neg)		num = -num;	*nump = num;	return base;}/* convert variable vq to integer variable, setting its value from vp * (vq and vp may be the same) */struct tbl *setint_v(vq, vp)	register struct tbl *vq, *vp;{	int base;	long num;	if ((base = getint(vp, &num)) == -1)		return NULL;	if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {		vq->flag &= ~ALLOC;		afree(vq->val.s, vq->areap);	}	vq->val.i = num;	if (vq->type == 0) /* default base */		vq->type = base;	vq->flag |= ISSET|INTEGER;	if (vq->flag&SPECIAL)		setspec(vq);	return vq;}static char *formatstr(vp, s)	struct tbl *vp;	const char *s;{	int olen, nlen;	char *p, *q;	olen = strlen(s);	if (vp->flag & (RJUST|LJUST)) {		if (!vp->u2.field)	/* default field width */			vp->u2.field = olen;		nlen = vp->u2.field;	} else		nlen = olen;	p = (char *) alloc(nlen + 1, ATEMP);	if (vp->flag & (RJUST|LJUST)) {		int slen;		if (vp->flag & RJUST) {			const char *q = s + olen;			/* strip trailing spaces (at&t ksh uses q[-1] == ' ') */			while (q > s && isspace(q[-1]))				--q;			slen = q - s;			if (slen > vp->u2.field) {				s += slen - vp->u2.field;				slen = vp->u2.field;			}			shf_snprintf(p, nlen + 1,				((vp->flag & ZEROFIL) && digit(*s)) ?					  "%0*s%.*s" : "%*s%.*s",				vp->u2.field - slen, null, slen, s);		} else {			/* strip leading spaces/zeros */			while (isspace(*s))				s++;			if (vp->flag & ZEROFIL)				while (*s == '0')					s++;			shf_snprintf(p, nlen + 1, "%-*.*s",				vp->u2.field, vp->u2.field, s);		}	} else		memcpy(p, s, olen + 1);	if (vp->flag & UCASEV_AL) {		for (q = p; *q; q++)			if (islower(*q))				*q = toupper(*q);	} else if (vp->flag & LCASEV) {		for (q = p; *q; q++)			if (isupper(*q))				*q = tolower(*q);	}	return p;}