loksh-noxz

[fork] a Linux port of OpenBSD's ksh
git clone https://noxz.tech/git/loksh-noxz.git
Log | Files | README

var.c
1/*	$OpenBSD: var.c,v 1.72 2021/03/05 15:22:03 zhuk Exp $	*/
2
3#include <sys/stat.h>
4#include <sys/time.h>
5
6#include <ctype.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <limits.h>
10#include <stdlib.h>
11#include <string.h>
12#include <time.h>
13#include <unistd.h>
14#ifndef SMALL
15# include <term.h>
16# include <curses.h>
17#endif
18
19#include "sh.h"
20
21/*
22 * Variables
23 *
24 * WARNING: unreadable code, needs a rewrite
25 *
26 * if (flag&INTEGER), val.i contains integer value, and type contains base.
27 * otherwise, (val.s + type) contains string value.
28 * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
29 */
30static	struct tbl vtemp;
31static	struct table specials;
32static char	*formatstr(struct tbl *, const char *);
33static void	export(struct tbl *, const char *);
34static int	special(const char *);
35static void	unspecial(const char *);
36static void	getspec(struct tbl *);
37static void	setspec(struct tbl *);
38static void	unsetspec(struct tbl *);
39static struct tbl *arraysearch(struct tbl *, int);
40
41/*
42 * create a new block for function calls and simple commands
43 * assume caller has allocated and set up genv->loc
44 */
45void
46newblock(void)
47{
48	struct block *l;
49	static char *const empty[] = {null};
50
51	l = alloc(sizeof(struct block), ATEMP);
52	l->flags = 0;
53	ainit(&l->area); /* todo: could use genv->area (l->area => l->areap) */
54	if (!genv->loc) {
55		l->argc = 0;
56		l->argv = (char **) empty;
57	} else {
58		l->argc = genv->loc->argc;
59		l->argv = genv->loc->argv;
60	}
61	l->exit = l->error = NULL;
62	ktinit(&l->vars, &l->area, 0);
63	ktinit(&l->funs, &l->area, 0);
64	l->next = genv->loc;
65	genv->loc = l;
66}
67
68/*
69 * pop a block handling special variables
70 */
71void
72popblock(void)
73{
74	struct block *l = genv->loc;
75	struct tbl *vp, **vpp = l->vars.tbls, *vq;
76	int i;
77
78	genv->loc = l->next;	/* pop block */
79	for (i = l->vars.size; --i >= 0; )
80		if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
81			if ((vq = global(vp->name))->flag & ISSET)
82				setspec(vq);
83			else
84				unsetspec(vq);
85		}
86	if (l->flags & BF_DOGETOPTS)
87		user_opt = l->getopts_state;
88	afreeall(&l->area);
89	afree(l, ATEMP);
90}
91
92/* called by main() to initialize variable data structures */
93void
94initvar(void)
95{
96	static const struct {
97		const char *name;
98		int v;
99	} names[] = {
100		{ "COLUMNS",		V_COLUMNS },
101		{ "IFS",		V_IFS },
102		{ "OPTIND",		V_OPTIND },
103		{ "PATH",		V_PATH },
104		{ "POSIXLY_CORRECT",	V_POSIXLY_CORRECT },
105		{ "TMPDIR",		V_TMPDIR },
106		{ "HISTCONTROL",	V_HISTCONTROL },
107		{ "HISTFILE",		V_HISTFILE },
108		{ "HISTSIZE",		V_HISTSIZE },
109		{ "EDITOR",		V_EDITOR },
110		{ "VISUAL",		V_VISUAL },
111		{ "MAIL",		V_MAIL },
112		{ "MAILCHECK",		V_MAILCHECK },
113		{ "MAILPATH",		V_MAILPATH },
114		{ "RANDOM",		V_RANDOM },
115		{ "SECONDS",		V_SECONDS },
116		{ "TMOUT",		V_TMOUT },
117		{ "LINENO",		V_LINENO },
118		{ "TERM",		V_TERM },
119		{ NULL,	0 }
120	};
121	int i;
122	struct tbl *tp;
123
124	ktinit(&specials, APERM, 32); /* must be 2^n (currently 19 specials) */
125	for (i = 0; names[i].name; i++) {
126		tp = ktenter(&specials, names[i].name, hash(names[i].name));
127		tp->flag = DEFINED|ISSET;
128		tp->type = names[i].v;
129	}
130}
131
132/* Used to calculate an array index for global()/local().  Sets *arrayp to
133 * non-zero if this is an array, sets *valp to the array index, returns
134 * the basename of the array.
135 */
136static const char *
137array_index_calc(const char *n, bool *arrayp, int *valp)
138{
139	const char *p;
140	int len;
141
142	*arrayp = false;
143	p = skip_varname(n, false);
144	if (p != n && *p == '[' && (len = array_ref_len(p))) {
145		char *sub, *tmp;
146		int64_t rval;
147
148		/* Calculate the value of the subscript */
149		*arrayp = true;
150		tmp = str_nsave(p+1, len-2, ATEMP);
151		sub = substitute(tmp, 0);
152		afree(tmp, ATEMP);
153		n = str_nsave(n, p - n, ATEMP);
154		evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
155		if (rval < 0 || rval > INT_MAX)
156			errorf("%s: subscript %" PRIi64 " out of range",
157			    n, rval);
158		*valp = rval;
159		afree(sub, ATEMP);
160	}
161	return n;
162}
163
164/*
165 * Search for variable, if not found create globally.
166 */
167struct tbl *
168global(const char *n)
169{
170	struct block *l = genv->loc;
171	struct tbl *vp;
172	long	 num;
173	int c;
174	unsigned int h;
175	bool	 array;
176	int	 val;
177
178	/* Check to see if this is an array */
179	n = array_index_calc(n, &array, &val);
180	h = hash(n);
181	c = (unsigned char)n[0];
182	if (!letter(c)) {
183		if (array)
184			errorf("bad substitution");
185		vp = &vtemp;
186		vp->flag = DEFINED;
187		vp->type = 0;
188		vp->areap = ATEMP;
189		*vp->name = c;
190		if (digit(c)) {
191			errno = 0;
192			num = strtol(n, NULL, 10);
193			if (errno == 0 && num <= l->argc)
194				/* setstr can't fail here */
195				setstr(vp, l->argv[num], KSH_RETURN_ERROR);
196			vp->flag |= RDONLY;
197			return vp;
198		}
199		vp->flag |= RDONLY;
200		if (n[1] != '\0')
201			return vp;
202		vp->flag |= ISSET|INTEGER;
203		switch (c) {
204		case '$':
205			vp->val.i = kshpid;
206			break;
207		case '!':
208			/* If no job, expand to nothing */
209			if ((vp->val.i = j_async()) == 0)
210				vp->flag &= ~(ISSET|INTEGER);
211			break;
212		case '?':
213			vp->val.i = exstat;
214			break;
215		case '#':
216			vp->val.i = l->argc;
217			break;
218		case '-':
219			vp->flag &= ~INTEGER;
220			vp->val.s = getoptions();
221			break;
222		default:
223			vp->flag &= ~(ISSET|INTEGER);
224		}
225		return vp;
226	}
227	for (l = genv->loc; ; l = l->next) {
228		vp = ktsearch(&l->vars, n, h);
229		if (vp != NULL) {
230			if (array)
231				return arraysearch(vp, val);
232			else
233				return vp;
234		}
235		if (l->next == NULL)
236			break;
237	}
238	vp = ktenter(&l->vars, n, h);
239	if (array)
240		vp = arraysearch(vp, val);
241	vp->flag |= DEFINED;
242	if (special(n))
243		vp->flag |= SPECIAL;
244	return vp;
245}
246
247/*
248 * Search for local variable, if not found create locally.
249 */
250struct tbl *
251local(const char *n, bool copy)
252{
253	struct block *l = genv->loc;
254	struct tbl *vp;
255	unsigned int h;
256	bool	 array;
257	int	 val;
258
259	/* Check to see if this is an array */
260	n = array_index_calc(n, &array, &val);
261	h = hash(n);
262	if (!letter(*n)) {
263		vp = &vtemp;
264		vp->flag = DEFINED|RDONLY;
265		vp->type = 0;
266		vp->areap = ATEMP;
267		return vp;
268	}
269	vp = ktenter(&l->vars, n, h);
270	if (copy && !(vp->flag & DEFINED)) {
271		struct block *ll = l;
272		struct tbl *vq = NULL;
273
274		while ((ll = ll->next) && !(vq = ktsearch(&ll->vars, n, h)))
275			;
276		if (vq) {
277			vp->flag |= vq->flag &
278			    (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
279			    ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
280			if (vq->flag & INTEGER)
281				vp->type = vq->type;
282			vp->u2.field = vq->u2.field;
283		}
284	}
285	if (array)
286		vp = arraysearch(vp, val);
287	vp->flag |= DEFINED;
288	if (special(n))
289		vp->flag |= SPECIAL;
290	return vp;
291}
292
293/* get variable string value */
294char *
295str_val(struct tbl *vp)
296{
297	char *s;
298
299	if ((vp->flag&SPECIAL))
300		getspec(vp);
301	if (!(vp->flag&ISSET))
302		s = null;		/* special to dollar() */
303	else if (!(vp->flag&INTEGER))	/* string source */
304		s = vp->val.s + vp->type;
305	else {				/* integer source */
306		/* worst case number length is when base=2, so use
307		 * minus base # number BITS(int64_t) NUL */
308		char strbuf[1 + 2 + 1 + BITS(int64_t) + 1];
309		const char *digits = (vp->flag & UCASEV_AL) ?
310		    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" :
311		    "0123456789abcdefghijklmnopqrstuvwxyz";
312		uint64_t n;
313		unsigned int base;
314
315		s = strbuf + sizeof(strbuf);
316		if (vp->flag & INT_U)
317			n = (uint64_t) vp->val.i;
318		else
319			n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;
320		base = (vp->type == 0) ? 10 : vp->type;
321		if (base < 2 || base > strlen(digits))
322			base = 10;
323
324		*--s = '\0';
325		do {
326			*--s = digits[n % base];
327			n /= base;
328		} while (n != 0);
329		if (base != 10) {
330			*--s = '#';
331			*--s = digits[base % 10];
332			if (base >= 10)
333				*--s = digits[base / 10];
334		}
335		if (!(vp->flag & INT_U) && vp->val.i < 0)
336			*--s = '-';
337		if (vp->flag & (RJUST|LJUST)) /* case already dealt with */
338			s = formatstr(vp, s);
339		else
340			s = str_save(s, ATEMP);
341	}
342	return s;
343}
344
345/* get variable integer value, with error checking */
346int64_t
347intval(struct tbl *vp)
348{
349	int64_t num;
350	int base;
351
352	base = getint(vp, &num, false);
353	if (base == -1)
354		/* XXX check calls - is error here ok by POSIX? */
355		errorf("%s: bad number", str_val(vp));
356	return num;
357}
358
359/* set variable to string value */
360int
361setstr(struct tbl *vq, const char *s, int error_ok)
362{
363	const char *fs = NULL;
364	int no_ro_check = error_ok & KSH_IGNORE_RDONLY;
365	error_ok &= ~KSH_IGNORE_RDONLY;
366	if ((vq->flag & RDONLY) && !no_ro_check) {
367		warningf(true, "%s: is read only", vq->name);
368		if (!error_ok)
369			errorf(NULL);
370		return 0;
371	}
372	if (!(vq->flag&INTEGER)) { /* string dest */
373		if ((vq->flag&ALLOC)) {
374			/* debugging */
375			if (s >= vq->val.s &&
376			    s <= vq->val.s + strlen(vq->val.s))
377				internal_errorf("%s: %s=%s: assigning to self",
378				    __func__, vq->name, s);
379			afree(vq->val.s, vq->areap);
380		}
381		vq->flag &= ~(ISSET|ALLOC);
382		vq->type = 0;
383		if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
384			fs = s = formatstr(vq, s);
385		if ((vq->flag&EXPORT))
386			export(vq, s);
387		else {
388			vq->val.s = str_save(s, vq->areap);
389			vq->flag |= ALLOC;
390		}
391	} else {		/* integer dest */
392		if (!v_evaluate(vq, s, error_ok, true))
393			return 0;
394	}
395	vq->flag |= ISSET;
396	if ((vq->flag&SPECIAL))
397		setspec(vq);
398	afree((void *)fs, ATEMP);
399	return 1;
400}
401
402/* set variable to integer */
403void
404setint(struct tbl *vq, int64_t n)
405{
406	if (!(vq->flag&INTEGER)) {
407		struct tbl *vp = &vtemp;
408		vp->flag = (ISSET|INTEGER);
409		vp->type = 0;
410		vp->areap = ATEMP;
411		vp->val.i = n;
412		/* setstr can't fail here */
413		setstr(vq, str_val(vp), KSH_RETURN_ERROR);
414	} else
415		vq->val.i = n;
416	vq->flag |= ISSET;
417	if ((vq->flag&SPECIAL))
418		setspec(vq);
419}
420
421int
422getint(struct tbl *vp, int64_t *nump, bool arith)
423{
424	char *s;
425	int c;
426	int base, neg;
427	int have_base = 0;
428	int64_t num;
429
430	if (vp->flag&SPECIAL)
431		getspec(vp);
432	/* XXX is it possible for ISSET to be set and val.s to be 0? */
433	if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
434		return -1;
435	if (vp->flag&INTEGER) {
436		*nump = vp->val.i;
437		return vp->type;
438	}
439	s = vp->val.s + vp->type;
440	if (s == NULL)	/* redundant given initial test */
441		s = null;
442	base = 10;
443	num = 0;
444	neg = 0;
445	if (arith && *s == '0' && *(s+1)) {
446		s++;
447		if (*s == 'x' || *s == 'X') {
448			s++;
449			base = 16;
450		} else if (vp->flag & ZEROFIL) {
451			while (*s == '0')
452				s++;
453		} else
454			base = 8;
455		have_base++;
456	}
457	for (c = (unsigned char)*s++; c ; c = (unsigned char)*s++) {
458		if (c == '-') {
459			neg++;
460		} else if (c == '#') {
461			base = (int) num;
462			if (have_base || base < 2 || base > 36)
463				return -1;
464			num = 0;
465			have_base = 1;
466		} else if (letnum(c)) {
467			if (isdigit(c))
468				c -= '0';
469			else if (islower(c))
470				c -= 'a' - 10; /* todo: assumes ascii */
471			else if (isupper(c))
472				c -= 'A' - 10; /* todo: assumes ascii */
473			else
474				c = -1; /* _: force error */
475			if (c < 0 || c >= base)
476				return -1;
477			num = num * base + c;
478		} else
479			return -1;
480	}
481	if (neg)
482		num = -num;
483	*nump = num;
484	return base;
485}
486
487/* convert variable vq to integer variable, setting its value from vp
488 * (vq and vp may be the same)
489 */
490struct tbl *
491setint_v(struct tbl *vq, struct tbl *vp, bool arith)
492{
493	int base;
494	int64_t num;
495
496	if ((base = getint(vp, &num, arith)) == -1)
497		return NULL;
498	if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
499		vq->flag &= ~ALLOC;
500		afree(vq->val.s, vq->areap);
501	}
502	vq->val.i = num;
503	if (vq->type == 0) /* default base */
504		vq->type = base;
505	vq->flag |= ISSET|INTEGER;
506	if (vq->flag&SPECIAL)
507		setspec(vq);
508	return vq;
509}
510
511static char *
512formatstr(struct tbl *vp, const char *s)
513{
514	int olen, nlen;
515	char *p, *q;
516
517	olen = strlen(s);
518
519	if (vp->flag & (RJUST|LJUST)) {
520		if (!vp->u2.field)	/* default field width */
521			vp->u2.field = olen;
522		nlen = vp->u2.field;
523	} else
524		nlen = olen;
525
526	p = alloc(nlen + 1, ATEMP);
527	if (vp->flag & (RJUST|LJUST)) {
528		int slen;
529
530		if (vp->flag & RJUST) {
531			const char *r = s + olen;
532			/* strip trailing spaces (at&t ksh uses r[-1] == ' ') */
533			while (r > s && isspace((unsigned char)r[-1]))
534				--r;
535			slen = r - s;
536			if (slen > vp->u2.field) {
537				s += slen - vp->u2.field;
538				slen = vp->u2.field;
539			}
540			shf_snprintf(p, nlen + 1,
541				((vp->flag & ZEROFIL) && digit(*s)) ?
542					  "%0*s%.*s" : "%*s%.*s",
543				vp->u2.field - slen, null, slen, s);
544		} else {
545			/* strip leading spaces/zeros */
546			while (isspace((unsigned char)*s))
547				s++;
548			if (vp->flag & ZEROFIL)
549				while (*s == '0')
550					s++;
551			shf_snprintf(p, nlen + 1, "%-*.*s",
552				vp->u2.field, vp->u2.field, s);
553		}
554	} else
555		memcpy(p, s, olen + 1);
556
557	if (vp->flag & UCASEV_AL) {
558		for (q = p; *q; q++)
559			if (islower((unsigned char)*q))
560				*q = toupper((unsigned char)*q);
561	} else if (vp->flag & LCASEV) {
562		for (q = p; *q; q++)
563			if (isupper((unsigned char)*q))
564				*q = tolower((unsigned char)*q);
565	}
566
567	return p;
568}
569
570/*
571 * make vp->val.s be "name=value" for quick exporting.
572 */
573static void
574export(struct tbl *vp, const char *val)
575{
576	char *xp;
577	char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
578	int namelen = strlen(vp->name);
579	int vallen = strlen(val) + 1;
580
581	vp->flag |= ALLOC;
582	xp = alloc(namelen + 1 + vallen, vp->areap);
583	memcpy(vp->val.s = xp, vp->name, namelen);
584	xp += namelen;
585	*xp++ = '=';
586	vp->type = xp - vp->val.s; /* offset to value */
587	memcpy(xp, val, vallen);
588	afree(op, vp->areap);
589}
590
591/*
592 * lookup variable (according to (set&LOCAL)),
593 * set its attributes (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL,
594 * LCASEV, UCASEV_AL), and optionally set its value if an assignment.
595 */
596struct tbl *
597typeset(const char *var, int set, int clr, int field, int base)
598{
599	struct tbl *vp;
600	struct tbl *vpbase, *t;
601	char *tvar;
602	const char *val;
603
604	/* check for valid variable name, search for value */
605	val = skip_varname(var, false);
606	if (val == var)
607		return NULL;
608	if (*val == '[') {
609		int len;
610
611		len = array_ref_len(val);
612		if (len == 0)
613			return NULL;
614		/* IMPORT is only used when the shell starts up and is
615		 * setting up its environment.  Allow only simple array
616		 * references at this time since parameter/command substitution
617		 * is preformed on the [expression], which would be a major
618		 * security hole.
619		 */
620		if (set & IMPORT) {
621			int i;
622			for (i = 1; i < len - 1; i++)
623				if (!digit(val[i]))
624					return NULL;
625		}
626		val += len;
627	}
628	if (*val == '=')
629		tvar = str_nsave(var, val++ - var, ATEMP);
630	else {
631		/* Importing from original environment: must have an = */
632		if (set & IMPORT)
633			return NULL;
634		tvar = (char *) var;
635		val = NULL;
636	}
637
638	/* Prevent typeset from creating a local PATH/ENV/SHELL */
639	if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
640	    strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
641		errorf("%s: restricted", tvar);
642
643	vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? true : false) :
644	    global(tvar);
645	set &= ~(LOCAL|LOCAL_COPY);
646
647	vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
648
649	/* only allow export flag to be set.  at&t ksh allows any attribute to
650	 * be changed, which means it can be truncated or modified
651	 * (-L/-R/-Z/-i).
652	 */
653	if ((vpbase->flag&RDONLY) &&
654	    (val || clr || (set & ~EXPORT)))
655		/* XXX check calls - is error here ok by POSIX? */
656		errorf("%s: is read only", tvar);
657	if (val)
658		afree(tvar, ATEMP);
659
660	/* most calls are with set/clr == 0 */
661	if (set | clr) {
662		int ok = 1;
663		/* XXX if x[0] isn't set, there will be problems: need to have
664		 * one copy of attributes for arrays...
665		 */
666		for (t = vpbase; t; t = t->u.array) {
667			int fake_assign;
668			int error_ok = KSH_RETURN_ERROR;
669			char *s = NULL;
670			char *free_me = NULL;
671
672			fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
673			    ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
674			    ((t->flag & INTEGER) && (clr & INTEGER)) ||
675			    (!(t->flag & INTEGER) && (set & INTEGER)));
676			if (fake_assign) {
677				if (t->flag & INTEGER) {
678					s = str_val(t);
679					free_me = NULL;
680				} else {
681					s = t->val.s + t->type;
682					free_me = (t->flag & ALLOC) ? t->val.s :
683					    NULL;
684				}
685				t->flag &= ~ALLOC;
686			}
687			if (!(t->flag & INTEGER) && (set & INTEGER)) {
688				t->type = 0;
689				t->flag &= ~ALLOC;
690			}
691			if (!(t->flag & RDONLY) && (set & RDONLY)) {
692				/* allow var to be initialized read-only */
693				error_ok |= KSH_IGNORE_RDONLY;
694			}
695			t->flag = (t->flag | set) & ~clr;
696			/* Don't change base if assignment is to be done,
697			 * in case assignment fails.
698			 */
699			if ((set & INTEGER) && base > 0 && (!val || t != vp))
700				t->type = base;
701			if (set & (LJUST|RJUST|ZEROFIL))
702				t->u2.field = field;
703			if (fake_assign) {
704				if (!setstr(t, s, error_ok)) {
705					/* Somewhat arbitrary action here:
706					 * zap contents of variable, but keep
707					 * the flag settings.
708					 */
709					ok = 0;
710					if (t->flag & INTEGER)
711						t->flag &= ~ISSET;
712					else {
713						if (t->flag & ALLOC)
714							afree(t->val.s, t->areap);
715						t->flag &= ~(ISSET|ALLOC);
716						t->type = 0;
717					}
718				}
719				afree(free_me, t->areap);
720			}
721		}
722		if (!ok)
723		    errorf(NULL);
724	}
725
726	if (val != NULL) {
727		if (vp->flag&INTEGER) {
728			/* do not zero base before assignment */
729			setstr(vp, val, KSH_UNWIND_ERROR | KSH_IGNORE_RDONLY);
730			/* Done after assignment to override default */
731			if (base > 0)
732				vp->type = base;
733		} else
734			/* setstr can't fail (readonly check already done) */
735			setstr(vp, val, KSH_RETURN_ERROR | KSH_IGNORE_RDONLY);
736	}
737
738	/* only x[0] is ever exported, so use vpbase */
739	if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
740	    vpbase->type == 0)
741		export(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
742
743	return vp;
744}
745
746/* Unset a variable.  array_ref is set if there was an array reference in
747 * the name lookup (eg, x[2]).
748 */
749void
750unset(struct tbl *vp, int array_ref)
751{
752	if (vp->flag & ALLOC)
753		afree(vp->val.s, vp->areap);
754	if ((vp->flag & ARRAY) && !array_ref) {
755		struct tbl *a, *tmp;
756
757		/* Free up entire array */
758		for (a = vp->u.array; a; ) {
759			tmp = a;
760			a = a->u.array;
761			if (tmp->flag & ALLOC)
762				afree(tmp->val.s, tmp->areap);
763			afree(tmp, tmp->areap);
764		}
765		vp->u.array = NULL;
766	}
767	/* If foo[0] is being unset, the remainder of the array is kept... */
768	vp->flag &= SPECIAL | (array_ref ? ARRAY|DEFINED : 0);
769	if (vp->flag & SPECIAL)
770		unsetspec(vp);	/* responsible for `unspecial'ing var */
771}
772
773/* return a pointer to the first char past a legal variable name (returns the
774 * argument if there is no legal name, returns * a pointer to the terminating
775 * null if whole string is legal).
776 */
777char *
778skip_varname(const char *s, int aok)
779{
780	int alen;
781
782	if (s && letter(*s)) {
783		while (*++s && letnum(*s))
784			;
785		if (aok && *s == '[' && (alen = array_ref_len(s)))
786			s += alen;
787	}
788	return (char *) s;
789}
790
791/* Return a pointer to the first character past any legal variable name.  */
792char *
793skip_wdvarname(const char *s,
794    int aok)				/* skip array de-reference? */
795{
796	if (s[0] == CHAR && letter(s[1])) {
797		do {
798			s += 2;
799		} while (s[0] == CHAR && letnum(s[1]));
800		if (aok && s[0] == CHAR && s[1] == '[') {
801			/* skip possible array de-reference */
802			const char *p = s;
803			char c;
804			int depth = 0;
805
806			while (1) {
807				if (p[0] != CHAR)
808					break;
809				c = p[1];
810				p += 2;
811				if (c == '[')
812					depth++;
813				else if (c == ']' && --depth == 0) {
814					s = p;
815					break;
816				}
817			}
818		}
819	}
820	return (char *) s;
821}
822
823/* Check if coded string s is a variable name */
824int
825is_wdvarname(const char *s, int aok)
826{
827	char *p = skip_wdvarname(s, aok);
828
829	return p != s && p[0] == EOS;
830}
831
832/* Check if coded string s is a variable assignment */
833int
834is_wdvarassign(const char *s)
835{
836	char *p = skip_wdvarname(s, true);
837
838	return p != s && p[0] == CHAR && p[1] == '=';
839}
840
841/*
842 * Make the exported environment from the exported names in the dictionary.
843 */
844char **
845makenv(void)
846{
847	struct block *l;
848	XPtrV env;
849	struct tbl *vp, **vpp;
850	int i;
851
852	XPinit(env, 64);
853	for (l = genv->loc; l != NULL; l = l->next)
854		for (vpp = l->vars.tbls, i = l->vars.size; --i >= 0; )
855			if ((vp = *vpp++) != NULL &&
856			    (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
857				struct block *l2;
858				struct tbl *vp2;
859				unsigned int h = hash(vp->name);
860
861				/* unexport any redefined instances */
862				for (l2 = l->next; l2 != NULL; l2 = l2->next) {
863					vp2 = ktsearch(&l2->vars, vp->name, h);
864					if (vp2 != NULL)
865						vp2->flag &= ~EXPORT;
866				}
867				if ((vp->flag&INTEGER)) {
868					/* integer to string */
869					char *val;
870					val = str_val(vp);
871					vp->flag &= ~(INTEGER|RDONLY);
872					/* setstr can't fail here */
873					setstr(vp, val, KSH_RETURN_ERROR);
874				}
875				XPput(env, vp->val.s);
876			}
877	XPput(env, NULL);
878	return (char **) XPclose(env);
879}
880
881/*
882 * Called after a fork in parent to bump the random number generator.
883 * Done to ensure children will not get the same random number sequence
884 * if the parent doesn't use $RANDOM.
885 */
886void
887change_random(void)
888{
889	rand();
890}
891
892/*
893 * handle special variables with side effects - PATH, SECONDS.
894 */
895
896/* Test if name is a special parameter */
897static int
898special(const char *name)
899{
900	struct tbl *tp;
901
902	tp = ktsearch(&specials, name, hash(name));
903	return tp && (tp->flag & ISSET) ? tp->type : V_NONE;
904}
905
906/* Make a variable non-special */
907static void
908unspecial(const char *name)
909{
910	struct tbl *tp;
911
912	tp = ktsearch(&specials, name, hash(name));
913	if (tp)
914		ktdelete(tp);
915}
916
917static	struct	timespec seconds;	/* time SECONDS last set */
918static	int	user_lineno;		/* what user set $LINENO to */
919
920static void
921getspec(struct tbl *vp)
922{
923	switch (special(vp->name)) {
924	case V_SECONDS:
925		vp->flag &= ~SPECIAL;
926		/* On start up the value of SECONDS is used before seconds
927		 * has been set - don't do anything in this case
928		 * (see initcoms[] in main.c).
929		 */
930		if (vp->flag & ISSET) {
931			struct timespec difference, now;
932
933			clock_gettime(CLOCK_MONOTONIC, &now);
934			timespecsub(&now, &seconds, &difference);
935			setint(vp, (int64_t)difference.tv_sec);
936		}
937		vp->flag |= SPECIAL;
938		break;
939	case V_RANDOM:
940		vp->flag &= ~SPECIAL;
941		setint(vp, (int64_t) (rand() & 0x7fff));
942		vp->flag |= SPECIAL;
943		break;
944	case V_HISTSIZE:
945		vp->flag &= ~SPECIAL;
946		setint(vp, (int64_t) histsize);
947		vp->flag |= SPECIAL;
948		break;
949	case V_OPTIND:
950		vp->flag &= ~SPECIAL;
951		setint(vp, (int64_t) user_opt.uoptind);
952		vp->flag |= SPECIAL;
953		break;
954	case V_LINENO:
955		vp->flag &= ~SPECIAL;
956		setint(vp, (int64_t) current_lineno + user_lineno);
957		vp->flag |= SPECIAL;
958		break;
959	}
960}
961
962static void
963setspec(struct tbl *vp)
964{
965	char *s;
966
967	switch (special(vp->name)) {
968	case V_PATH:
969		afree(search_path, APERM);
970		search_path = str_save(str_val(vp), APERM);
971		flushcom(1);	/* clear tracked aliases */
972		break;
973	case V_IFS:
974		setctypes(s = str_val(vp), C_IFS);
975		ifs0 = *s;
976		break;
977	case V_OPTIND:
978		vp->flag &= ~SPECIAL;
979		getopts_reset((int) intval(vp));
980		vp->flag |= SPECIAL;
981		break;
982	case V_POSIXLY_CORRECT:
983		change_flag(FPOSIX, OF_SPECIAL, 1);
984		break;
985	case V_TMPDIR:
986		afree(tmpdir, APERM);
987		tmpdir = NULL;
988		/* Use tmpdir iff it is an absolute path, is writable and
989		 * searchable and is a directory...
990		 */
991		{
992			struct stat statb;
993
994			s = str_val(vp);
995			if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&
996			    stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
997				tmpdir = str_save(s, APERM);
998		}
999		break;
1000	case V_HISTCONTROL:
1001		sethistcontrol(str_val(vp));
1002		break;
1003	case V_HISTSIZE:
1004		vp->flag &= ~SPECIAL;
1005		sethistsize((int) intval(vp));
1006		vp->flag |= SPECIAL;
1007		break;
1008	case V_HISTFILE:
1009		sethistfile(str_val(vp));
1010		break;
1011	case V_VISUAL:
1012		set_editmode(str_val(vp));
1013		break;
1014	case V_EDITOR:
1015		if (!(global("VISUAL")->flag & ISSET))
1016			set_editmode(str_val(vp));
1017		break;
1018	case V_COLUMNS:
1019		{
1020			int64_t l;
1021
1022			if (getint(vp, &l, false) == -1) {
1023				x_cols = MIN_COLS;
1024				break;
1025			}
1026			if (l <= MIN_COLS || l > INT_MAX)
1027				x_cols = MIN_COLS;
1028			else
1029				x_cols = l;
1030		}
1031		break;
1032	case V_MAIL:
1033		mbset(str_val(vp));
1034		break;
1035	case V_MAILPATH:
1036		mpset(str_val(vp));
1037		break;
1038	case V_MAILCHECK:
1039		vp->flag &= ~SPECIAL;
1040		mcset(intval(vp));
1041		vp->flag |= SPECIAL;
1042		break;
1043	case V_RANDOM:
1044		vp->flag &= ~SPECIAL;
1045		srand_deterministic((unsigned int)intval(vp));
1046		vp->flag |= SPECIAL;
1047		break;
1048	case V_SECONDS:
1049		vp->flag &= ~SPECIAL;
1050		clock_gettime(CLOCK_MONOTONIC, &seconds);
1051		seconds.tv_sec -= intval(vp);
1052		vp->flag |= SPECIAL;
1053		break;
1054	case V_TMOUT:
1055		/* Enforce integer to avoid command execution from initcoms[] */
1056		vp->flag &= ~SPECIAL;
1057		intval(vp);
1058		vp->flag |= SPECIAL;
1059		/* at&t ksh seems to do this (only listen if integer) */
1060		if (vp->flag & INTEGER)
1061			ksh_tmout = vp->val.i >= 0 ? vp->val.i : 0;
1062		break;
1063	case V_LINENO:
1064		vp->flag &= ~SPECIAL;
1065		/* The -1 is because line numbering starts at 1. */
1066		user_lineno = (unsigned int) intval(vp) - current_lineno - 1;
1067		vp->flag |= SPECIAL;
1068		break;
1069	case V_TERM:
1070#ifndef SMALL
1071		{
1072			int ret;
1073
1074			vp->flag &= ~SPECIAL;
1075			if (setupterm(str_val(vp), shl_out->fd, &ret) == ERR)
1076				del_curterm(cur_term);
1077			vp->flag |= SPECIAL;
1078		}
1079#endif
1080		break;
1081	}
1082}
1083
1084static void
1085unsetspec(struct tbl *vp)
1086{
1087	switch (special(vp->name)) {
1088	case V_PATH:
1089		afree(search_path, APERM);
1090		search_path = str_save(def_path, APERM);
1091		flushcom(1);	/* clear tracked aliases */
1092		break;
1093	case V_IFS:
1094		setctypes(" \t\n", C_IFS);
1095		ifs0 = ' ';
1096		break;
1097	case V_TMPDIR:
1098		/* should not become unspecial */
1099		afree(tmpdir, APERM);
1100		tmpdir = NULL;
1101		break;
1102	case V_MAIL:
1103		mbset(NULL);
1104		break;
1105	case V_MAILPATH:
1106		mpset(NULL);
1107		break;
1108	case V_HISTCONTROL:
1109		sethistcontrol(NULL);
1110		break;
1111	case V_LINENO:
1112	case V_MAILCHECK:	/* at&t ksh leaves previous value in place */
1113	case V_RANDOM:
1114	case V_SECONDS:
1115	case V_TMOUT:		/* at&t ksh leaves previous value in place */
1116		unspecial(vp->name);
1117		break;
1118
1119	  /* at&t ksh man page says OPTIND, OPTARG and _ lose special meaning,
1120	   * but OPTARG does not (still set by getopts) and _ is also still
1121	   * set in various places.
1122	   * Don't know what at&t does for:
1123	   *		MAIL, MAILPATH, HISTSIZE, HISTFILE,
1124	   * Unsetting these in at&t ksh does not loose the `specialness':
1125	   *    no effect: IFS, COLUMNS, PATH, TMPDIR,
1126	   *		VISUAL, EDITOR,
1127	   * pdkshisms: no effect:
1128	   *		POSIXLY_CORRECT (use set +o posix instead)
1129	   */
1130	}
1131}
1132
1133/*
1134 * Search for (and possibly create) a table entry starting with
1135 * vp, indexed by val.
1136 */
1137static struct tbl *
1138arraysearch(struct tbl *vp, int val)
1139{
1140	struct tbl *prev, *curr, *new;
1141	size_t namelen = strlen(vp->name) + 1;
1142
1143	vp->flag |= ARRAY|DEFINED;
1144	vp->index = 0;
1145	/* The table entry is always [0] */
1146	if (val == 0)
1147		return vp;
1148	prev = vp;
1149	curr = vp->u.array;
1150	while (curr && curr->index < val) {
1151		prev = curr;
1152		curr = curr->u.array;
1153	}
1154	if (curr && curr->index == val) {
1155		if (curr->flag&ISSET)
1156			return curr;
1157		else
1158			new = curr;
1159	} else
1160		new = alloc(sizeof(struct tbl) + namelen,
1161		    vp->areap);
1162	strlcpy(new->name, vp->name, namelen);
1163	new->flag = vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL);
1164	new->type = vp->type;
1165	new->areap = vp->areap;
1166	new->u2.field = vp->u2.field;
1167	new->index = val;
1168	if (curr != new) {		/* not reusing old array entry */
1169		prev->u.array = new;
1170		new->u.array = curr;
1171	}
1172	return new;
1173}
1174
1175/* Return the length of an array reference (eg, [1+2]) - cp is assumed
1176 * to point to the open bracket.  Returns 0 if there is no matching closing
1177 * bracket.
1178 */
1179int
1180array_ref_len(const char *cp)
1181{
1182	const char *s = cp;
1183	int c;
1184	int depth = 0;
1185
1186	while ((c = *s++) && (c != ']' || --depth))
1187		if (c == '[')
1188			depth++;
1189	if (!c)
1190		return 0;
1191	return s - cp;
1192}
1193
1194/*
1195 * Make a copy of the base of an array name
1196 */
1197char *
1198arrayname(const char *str)
1199{
1200	const char *p;
1201
1202	if ((p = strchr(str, '[')) == 0)
1203		/* Shouldn't happen, but why worry? */
1204		return (char *) str;
1205
1206	return str_nsave(str, p - str, ATEMP);
1207}
1208
1209/* Set (or overwrite, if !reset) the array variable var to the values in vals.
1210 */
1211void
1212set_array(const char *var, int reset, char **vals)
1213{
1214	struct tbl *vp, *vq;
1215	int i;
1216
1217	/* to get local array, use "typeset foo; set -A foo" */
1218	vp = global(var);
1219
1220	/* Note: at&t ksh allows set -A but not set +A of a read-only var */
1221	if ((vp->flag&RDONLY))
1222		errorf("%s: is read only", var);
1223	/* This code is quite non-optimal */
1224	if (reset > 0)
1225		/* trash existing values and attributes */
1226		unset(vp, 0);
1227	/* todo: would be nice for assignment to completely succeed or
1228	 * completely fail.  Only really effects integer arrays:
1229	 * evaluation of some of vals[] may fail...
1230	 */
1231	for (i = 0; vals[i]; i++) {
1232		vq = arraysearch(vp, i);
1233		/* would be nice to deal with errors here... (see above) */
1234		setstr(vq, vals[i], KSH_RETURN_ERROR);
1235	}
1236}