OSDN Git Service

Upgrade to mksh R56b.
[android-x86/external-mksh.git] / src / var.c
1 /*      $OpenBSD: var.c,v 1.44 2015/09/10 11:37:42 jca Exp $    */
2
3 /*-
4  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
5  *               2011, 2012, 2013, 2014, 2015, 2016, 2017
6  *      mirabilos <m@mirbsd.org>
7  *
8  * Provided that these terms and disclaimer and all copyright notices
9  * are retained or reproduced in an accompanying document, permission
10  * is granted to deal in this work without restriction, including un-
11  * limited rights to use, publicly perform, distribute, sell, modify,
12  * merge, give away, or sublicence.
13  *
14  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15  * the utmost extent permitted by applicable law, neither express nor
16  * implied; without malicious intent or gross negligence. In no event
17  * may a licensor, author or contributor be held liable for indirect,
18  * direct, other damage, loss, or other issues arising in any way out
19  * of dealing in the work, even if advised of the possibility of such
20  * damage or existence of a defect, except proven that it results out
21  * of said person's immediate fault when using the work as intended.
22  */
23
24 #include "sh.h"
25 #include "mirhash.h"
26
27 #if defined(__OpenBSD__)
28 #include <sys/sysctl.h>
29 #endif
30
31 __RCSID("$MirOS: src/bin/mksh/var.c,v 1.220 2017/07/26 23:02:28 tg Exp $");
32
33 /*-
34  * Variables
35  *
36  * WARNING: unreadable code, needs a rewrite
37  *
38  * if (flag&INTEGER), val.i contains integer value, and type contains base.
39  * otherwise, (val.s + type) contains string value.
40  * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
41  */
42
43 static struct table specials;
44 static uint32_t lcg_state = 5381, qh_state = 4711;
45 /* may only be set by typeset() just before call to array_index_calc() */
46 static enum namerefflag innermost_refflag = SRF_NOP;
47
48 static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
49 static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
50     bool);
51 static char *formatstr(struct tbl *, const char *);
52 static void exportprep(struct tbl *, const char *);
53 static int special(const char *);
54 static void unspecial(const char *);
55 static void getspec(struct tbl *);
56 static void setspec(struct tbl *);
57 static void unsetspec(struct tbl *);
58 static int getint(struct tbl *, mksh_ari_u *, bool);
59 static const char *array_index_calc(const char *, bool *, uint32_t *);
60
61 /*
62  * create a new block for function calls and simple commands
63  * assume caller has allocated and set up e->loc
64  */
65 void
66 newblock(void)
67 {
68         struct block *l;
69         static const char *empty[] = { null };
70
71         l = alloc(sizeof(struct block), ATEMP);
72         l->flags = 0;
73         /* TODO: could use e->area (l->area => l->areap) */
74         ainit(&l->area);
75         if (!e->loc) {
76                 l->argc = 0;
77                 l->argv = empty;
78         } else {
79                 l->argc = e->loc->argc;
80                 l->argv = e->loc->argv;
81         }
82         l->exit = l->error = NULL;
83         ktinit(&l->area, &l->vars, 0);
84         ktinit(&l->area, &l->funs, 0);
85         l->next = e->loc;
86         e->loc = l;
87 }
88
89 /*
90  * pop a block handling special variables
91  */
92 void
93 popblock(void)
94 {
95         ssize_t i;
96         struct block *l = e->loc;
97         struct tbl *vp, **vpp = l->vars.tbls, *vq;
98
99         /* pop block */
100         e->loc = l->next;
101
102         i = 1 << (l->vars.tshift);
103         while (--i >= 0)
104                 if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
105                         if ((vq = global(vp->name))->flag & ISSET)
106                                 setspec(vq);
107                         else
108                                 unsetspec(vq);
109                 }
110         if (l->flags & BF_DOGETOPTS)
111                 user_opt = l->getopts_state;
112         afreeall(&l->area);
113         afree(l, ATEMP);
114 }
115
116 /* called by main() to initialise variable data structures */
117 #define VARSPEC_DEFNS
118 #include "var_spec.h"
119
120 enum var_specs {
121 #define VARSPEC_ENUMS
122 #include "var_spec.h"
123         V_MAX
124 };
125
126 /* this is biased with -1 relative to VARSPEC_ENUMS */
127 static const char * const initvar_names[] = {
128 #define VARSPEC_ITEMS
129 #include "var_spec.h"
130 };
131
132 void
133 initvar(void)
134 {
135         int i = 0;
136         struct tbl *tp;
137
138         ktinit(APERM, &specials,
139             /* currently 18 specials: 75% of 32 = 2^5 */
140             5);
141         while (i < V_MAX - 1) {
142                 tp = ktenter(&specials, initvar_names[i],
143                     hash(initvar_names[i]));
144                 tp->flag = DEFINED|ISSET;
145                 tp->type = ++i;
146         }
147 }
148
149 /* common code for several functions below and c_typeset() */
150 struct block *
151 varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
152 {
153         register struct tbl *vp;
154
155         if (l) {
156  varsearch_loop:
157                 if ((vp = ktsearch(&l->vars, vn, h)) != NULL)
158                         goto varsearch_out;
159                 if (l->next != NULL) {
160                         l = l->next;
161                         goto varsearch_loop;
162                 }
163         }
164         vp = NULL;
165  varsearch_out:
166         *vpp = vp;
167         return (l);
168 }
169
170 /*
171  * Used to calculate an array index for global()/local(). Sets *arrayp
172  * to true if this is an array, sets *valp to the array index, returns
173  * the basename of the array. May only be called from global()/local()
174  * and must be their first callee.
175  */
176 static const char *
177 array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
178 {
179         const char *p;
180         size_t len;
181         char *ap = NULL;
182
183         *arrayp = false;
184  redo_from_ref:
185         p = skip_varname(n, false);
186         if (innermost_refflag == SRF_NOP && (p != n) && ctype(n[0], C_ALPHX)) {
187                 struct tbl *vp;
188                 char *vn;
189
190                 strndupx(vn, n, p - n, ATEMP);
191                 /* check if this is a reference */
192                 varsearch(e->loc, &vp, vn, hash(vn));
193                 afree(vn, ATEMP);
194                 if (vp && (vp->flag & (DEFINED | ASSOC | ARRAY)) ==
195                     (DEFINED | ASSOC)) {
196                         char *cp;
197
198                         /* gotcha! */
199                         cp = shf_smprintf(Tf_ss, str_val(vp), p);
200                         afree(ap, ATEMP);
201                         n = ap = cp;
202                         goto redo_from_ref;
203                 }
204         }
205         innermost_refflag = SRF_NOP;
206
207         if (p != n && ord(*p) == ord('[') && (len = array_ref_len(p))) {
208                 char *sub, *tmp;
209                 mksh_ari_t rval;
210
211                 /* calculate the value of the subscript */
212                 *arrayp = true;
213                 strndupx(tmp, p + 1, len - 2, ATEMP);
214                 sub = substitute(tmp, 0);
215                 afree(tmp, ATEMP);
216                 strndupx(n, n, p - n, ATEMP);
217                 evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
218                 *valp = (uint32_t)rval;
219                 afree(sub, ATEMP);
220         }
221         return (n);
222 }
223
224 #define vn vname.ro
225 /*
226  * Search for variable, if not found create globally.
227  */
228 struct tbl *
229 global(const char *n)
230 {
231         return (isglobal(n, true));
232 }
233
234 /* search for variable; if not found, return NULL or create globally */
235 struct tbl *
236 isglobal(const char *n, bool docreate)
237 {
238         struct tbl *vp;
239         union mksh_cchack vname;
240         struct block *l = e->loc;
241         int c;
242         bool array;
243         uint32_t h, val;
244
245         /*
246          * check to see if this is an array;
247          * dereference namerefs; must come first
248          */
249         vn = array_index_calc(n, &array, &val);
250         h = hash(vn);
251         c = (unsigned char)vn[0];
252         if (!ctype(c, C_ALPHX)) {
253                 if (array)
254                         errorf(Tbadsubst);
255                 vp = vtemp;
256                 vp->flag = DEFINED;
257                 vp->type = 0;
258                 vp->areap = ATEMP;
259                 if (ctype(c, C_DIGIT)) {
260                         if (getn(vn, &c)) {
261                                 /* main.c:main_init() says 12 */
262                                 shf_snprintf(vp->name, 12, Tf_d, c);
263                                 if (c <= l->argc) {
264                                         /* setstr can't fail here */
265                                         setstr(vp, l->argv[c],
266                                             KSH_RETURN_ERROR);
267                                 }
268                         } else
269                                 vp->name[0] = '\0';
270                         vp->flag |= RDONLY;
271                         goto out;
272                 }
273                 vp->name[0] = c;
274                 vp->name[1] = '\0';
275                 vp->flag |= RDONLY;
276                 if (vn[1] != '\0')
277                         goto out;
278                 vp->flag |= ISSET|INTEGER;
279                 switch (c) {
280                 case '$':
281                         vp->val.i = kshpid;
282                         break;
283                 case '!':
284                         /* if no job, expand to nothing */
285                         if ((vp->val.i = j_async()) == 0)
286                                 vp->flag &= ~(ISSET|INTEGER);
287                         break;
288                 case '?':
289                         vp->val.i = exstat & 0xFF;
290                         break;
291                 case '#':
292                         vp->val.i = l->argc;
293                         break;
294                 case '-':
295                         vp->flag &= ~INTEGER;
296                         vp->val.s = getoptions();
297                         break;
298                 default:
299                         vp->flag &= ~(ISSET|INTEGER);
300                 }
301                 goto out;
302         }
303         l = varsearch(e->loc, &vp, vn, h);
304         if (vp == NULL && docreate)
305                 vp = ktenter(&l->vars, vn, h);
306         else
307                 docreate = false;
308         if (vp != NULL) {
309                 if (array)
310                         vp = arraysearch(vp, val);
311                 if (docreate) {
312                         vp->flag |= DEFINED;
313                         if (special(vn))
314                                 vp->flag |= SPECIAL;
315                 }
316         }
317  out:
318         last_lookup_was_array = array;
319         if (vn != n)
320                 afree(vname.rw, ATEMP);
321         return (vp);
322 }
323
324 /*
325  * Search for local variable, if not found create locally.
326  */
327 struct tbl *
328 local(const char *n, bool copy)
329 {
330         struct tbl *vp;
331         union mksh_cchack vname;
332         struct block *l = e->loc;
333         bool array;
334         uint32_t h, val;
335
336         /*
337          * check to see if this is an array;
338          * dereference namerefs; must come first
339          */
340         vn = array_index_calc(n, &array, &val);
341         h = hash(vn);
342         if (!ctype(*vn, C_ALPHX)) {
343                 vp = vtemp;
344                 vp->flag = DEFINED|RDONLY;
345                 vp->type = 0;
346                 vp->areap = ATEMP;
347                 goto out;
348         }
349         vp = ktenter(&l->vars, vn, h);
350         if (copy && !(vp->flag & DEFINED)) {
351                 struct tbl *vq;
352
353                 varsearch(l->next, &vq, vn, h);
354                 if (vq != NULL) {
355                         vp->flag |= vq->flag &
356                             (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
357                             ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
358                         if (vq->flag & INTEGER)
359                                 vp->type = vq->type;
360                         vp->u2.field = vq->u2.field;
361                 }
362         }
363         if (array)
364                 vp = arraysearch(vp, val);
365         vp->flag |= DEFINED;
366         if (special(vn))
367                 vp->flag |= SPECIAL;
368  out:
369         last_lookup_was_array = array;
370         if (vn != n)
371                 afree(vname.rw, ATEMP);
372         return (vp);
373 }
374 #undef vn
375
376 /* get variable string value */
377 char *
378 str_val(struct tbl *vp)
379 {
380         char *s;
381
382         if ((vp->flag&SPECIAL))
383                 getspec(vp);
384         if (!(vp->flag&ISSET))
385                 /* special to dollar() */
386                 s = null;
387         else if (!(vp->flag&INTEGER))
388                 /* string source */
389                 s = vp->val.s + vp->type;
390         else {
391                 /* integer source */
392                 mksh_uari_t n;
393                 unsigned int base;
394                 /**
395                  * worst case number length is when base == 2:
396                  *      1 (minus) + 2 (base, up to 36) + 1 ('#') +
397                  *      number of bits in the mksh_uari_t + 1 (NUL)
398                  */
399                 char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1];
400                 const char *digits = (vp->flag & UCASEV_AL) ?
401                     digits_uc : digits_lc;
402
403                 s = strbuf + sizeof(strbuf);
404                 if (vp->flag & INT_U)
405                         n = vp->val.u;
406                 else
407                         n = (vp->val.i < 0) ? -vp->val.u : vp->val.u;
408                 base = (vp->type == 0) ? 10U : (unsigned int)vp->type;
409
410                 if (base == 1 && n == 0)
411                         base = 2;
412                 if (base == 1) {
413                         size_t sz = 1;
414
415                         *(s = strbuf) = '1';
416                         s[1] = '#';
417                         if (!UTFMODE)
418                                 s[2] = (unsigned char)n;
419                         else if ((n & 0xFF80) == 0xEF80)
420                                 /* OPTU-16 -> raw octet */
421                                 s[2] = asc2rtt(n & 0xFF);
422                         else
423                                 sz = utf_wctomb(s + 2, n);
424                         s[2 + sz] = '\0';
425                 } else {
426                         *--s = '\0';
427                         do {
428                                 *--s = digits[n % base];
429                                 n /= base;
430                         } while (n != 0);
431                         if (base != 10) {
432                                 *--s = '#';
433                                 *--s = digits[base % 10];
434                                 if (base >= 10)
435                                         *--s = digits[base / 10];
436                         }
437                         if (!(vp->flag & INT_U) && vp->val.i < 0)
438                                 *--s = '-';
439                 }
440                 if (vp->flag & (RJUST|LJUST))
441                         /* case already dealt with */
442                         s = formatstr(vp, s);
443                 else
444                         strdupx(s, s, ATEMP);
445         }
446         return (s);
447 }
448
449 /* set variable to string value */
450 int
451 setstr(struct tbl *vq, const char *s, int error_ok)
452 {
453         char *salloc = NULL;
454         bool no_ro_check = tobool(error_ok & 0x4);
455
456         error_ok &= ~0x4;
457         if ((vq->flag & RDONLY) && !no_ro_check) {
458                 warningf(true, Tf_ro, vq->name);
459                 if (!error_ok)
460                         errorfxz(2);
461                 return (0);
462         }
463         if (!(vq->flag&INTEGER)) {
464                 /* string dest */
465                 if ((vq->flag&ALLOC)) {
466 #ifndef MKSH_SMALL
467                         /* debugging */
468                         if (s >= vq->val.s &&
469                             s <= strnul(vq->val.s)) {
470                                 internal_errorf(
471                                     "setstr: %s=%s: assigning to self",
472                                     vq->name, s);
473                         }
474 #endif
475                         afree(vq->val.s, vq->areap);
476                 }
477                 vq->flag &= ~(ISSET|ALLOC);
478                 vq->type = 0;
479                 if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
480                         s = salloc = formatstr(vq, s);
481                 if ((vq->flag&EXPORT))
482                         exportprep(vq, s);
483                 else {
484                         strdupx(vq->val.s, s, vq->areap);
485                         vq->flag |= ALLOC;
486                 }
487         } else {
488                 /* integer dest */
489                 if (!v_evaluate(vq, s, error_ok, true))
490                         return (0);
491         }
492         vq->flag |= ISSET;
493         if ((vq->flag&SPECIAL))
494                 setspec(vq);
495         afree(salloc, ATEMP);
496         return (1);
497 }
498
499 /* set variable to integer */
500 void
501 setint(struct tbl *vq, mksh_ari_t n)
502 {
503         if (!(vq->flag&INTEGER)) {
504                 vtemp->flag = (ISSET|INTEGER);
505                 vtemp->type = 0;
506                 vtemp->areap = ATEMP;
507                 vtemp->val.i = n;
508                 /* setstr can't fail here */
509                 setstr(vq, str_val(vtemp), KSH_RETURN_ERROR);
510         } else
511                 vq->val.i = n;
512         vq->flag |= ISSET;
513         if ((vq->flag&SPECIAL))
514                 setspec(vq);
515 }
516
517 static int
518 getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
519 {
520         mksh_uari_t c, num = 0, base = 10;
521         const char *s;
522         bool have_base = false, neg = false;
523
524         if (vp->flag & SPECIAL)
525                 getspec(vp);
526         /* XXX is it possible for ISSET to be set and val.s to be NULL? */
527         if (!(vp->flag & ISSET) || (!(vp->flag & INTEGER) && vp->val.s == NULL))
528                 return (-1);
529         if (vp->flag & INTEGER) {
530                 nump->i = vp->val.i;
531                 return (vp->type);
532         }
533         s = vp->val.s + vp->type;
534
535         do {
536                 c = (unsigned char)*s++;
537         } while (ctype(c, C_SPACE));
538
539         switch (c) {
540         case '-':
541                 neg = true;
542                 /* FALLTHROUGH */
543         case '+':
544                 c = (unsigned char)*s++;
545                 break;
546         }
547
548         if (c == '0' && arith) {
549                 if (ksh_eq(s[0], 'X', 'x')) {
550                         /* interpret as hexadecimal */
551                         base = 16;
552                         ++s;
553                         goto getint_c_style_base;
554                 } else if (Flag(FPOSIX) && ctype(s[0], C_DIGIT) &&
555                     !(vp->flag & ZEROFIL)) {
556                         /* interpret as octal (deprecated) */
557                         base = 8;
558  getint_c_style_base:
559                         have_base = true;
560                         c = (unsigned char)*s++;
561                 }
562         }
563
564         do {
565                 if (c == '#') {
566                         /* ksh-style base determination */
567                         if (have_base || num < 1)
568                                 return (-1);
569                         if ((base = num) == 1) {
570                                 /* mksh-specific extension */
571                                 unsigned int wc;
572
573                                 if (!UTFMODE)
574                                         wc = *(const unsigned char *)s;
575                                 else if (utf_mbtowc(&wc, s) == (size_t)-1)
576                                         /* OPTU-8 -> OPTU-16 */
577                                         /*
578                                          * (with a twist: 1#\uEF80 converts
579                                          * the same as 1#\x80 does, thus is
580                                          * not round-tripping correctly XXX)
581                                          */
582                                         wc = 0xEF00 + rtt2asc(*s);
583                                 nump->u = (mksh_uari_t)wc;
584                                 return (1);
585                         } else if (base > 36)
586                                 base = 10;
587                         num = 0;
588                         have_base = true;
589                         continue;
590                 }
591                 if (ctype(c, C_DIGIT))
592                         c = ksh_numdig(c);
593                 else if (ctype(c, C_UPPER))
594                         c = ksh_numuc(c) + 10;
595                 else if (ctype(c, C_LOWER))
596                         c = ksh_numlc(c) + 10;
597                 else
598                         return (-1);
599                 if (c >= base)
600                         return (-1);
601                 /* handle overflow as truncation */
602                 num = num * base + c;
603         } while ((c = (unsigned char)*s++));
604
605         if (neg)
606                 num = -num;
607         nump->u = num;
608         return (base);
609 }
610
611 /*
612  * convert variable vq to integer variable, setting its value from vp
613  * (vq and vp may be the same)
614  */
615 struct tbl *
616 setint_v(struct tbl *vq, struct tbl *vp, bool arith)
617 {
618         int base;
619         mksh_ari_u num;
620
621         if ((base = getint(vp, &num, arith)) == -1)
622                 return (NULL);
623         setint_n(vq, num.i, 0);
624         if (vq->type == 0)
625                 /* default base */
626                 vq->type = base;
627         return (vq);
628 }
629
630 /* convert variable vq to integer variable, setting its value to num */
631 void
632 setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
633 {
634         if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
635                 vq->flag &= ~ALLOC;
636                 vq->type = 0;
637                 afree(vq->val.s, vq->areap);
638         }
639         vq->val.i = num;
640         if (newbase != 0)
641                 vq->type = newbase;
642         vq->flag |= ISSET|INTEGER;
643         if (vq->flag&SPECIAL)
644                 setspec(vq);
645 }
646
647 static char *
648 formatstr(struct tbl *vp, const char *s)
649 {
650         int olen, nlen;
651         char *p, *q;
652         size_t psiz;
653
654         olen = (int)utf_mbswidth(s);
655
656         if (vp->flag & (RJUST|LJUST)) {
657                 if (!vp->u2.field)
658                         /* default field width */
659                         vp->u2.field = olen;
660                 nlen = vp->u2.field;
661         } else
662                 nlen = olen;
663
664         p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
665         if (vp->flag & (RJUST|LJUST)) {
666                 int slen = olen;
667
668                 if (vp->flag & RJUST) {
669                         const char *qq;
670                         int n = 0;
671
672                         qq = utf_skipcols(s, slen, &slen);
673
674                         /* strip trailing spaces (AT&T uses qq[-1] == ' ') */
675                         while (qq > s && ctype(qq[-1], C_SPACE)) {
676                                 --qq;
677                                 --slen;
678                         }
679                         if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
680                                 if (!s[0] || !s[1])
681                                         goto uhm_no;
682                                 if (s[1] == '#')
683                                         n = 2;
684                                 else if (s[2] == '#')
685                                         n = 3;
686  uhm_no:
687                                 if (vp->u2.field <= n)
688                                         n = 0;
689                         }
690                         if (n) {
691                                 memcpy(p, s, n);
692                                 s += n;
693                         }
694                         while (slen > vp->u2.field)
695                                 slen -= utf_widthadj(s, &s);
696                         if (vp->u2.field - slen)
697                                 memset(p + n, (vp->flag & ZEROFIL) ? '0' : ' ',
698                                     vp->u2.field - slen);
699                         slen -= n;
700                         shf_snprintf(p + vp->u2.field - slen,
701                             psiz - (vp->u2.field - slen),
702                             "%.*s", slen, s);
703                 } else {
704                         /* strip leading spaces/zeros */
705                         while (ctype(*s, C_SPACE))
706                                 s++;
707                         if (vp->flag & ZEROFIL)
708                                 while (*s == '0')
709                                         s++;
710                         shf_snprintf(p, nlen + 1, "%-*.*s",
711                                 vp->u2.field, vp->u2.field, s);
712                 }
713         } else
714                 memcpy(p, s, strlen(s) + 1);
715
716         if (vp->flag & UCASEV_AL) {
717                 for (q = p; *q; q++)
718                         *q = ksh_toupper(*q);
719         } else if (vp->flag & LCASEV) {
720                 for (q = p; *q; q++)
721                         *q = ksh_tolower(*q);
722         }
723
724         return (p);
725 }
726
727 /*
728  * make vp->val.s be "name=value" for quick exporting.
729  */
730 static void
731 exportprep(struct tbl *vp, const char *val)
732 {
733         char *xp;
734         char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
735         size_t namelen, vallen;
736
737         namelen = strlen(vp->name);
738         vallen = strlen(val) + 1;
739
740         vp->flag |= ALLOC;
741         /* since name+val are both in memory this can go unchecked */
742         xp = alloc(namelen + 1 + vallen, vp->areap);
743         memcpy(vp->val.s = xp, vp->name, namelen);
744         xp += namelen;
745         *xp++ = '=';
746         /* offset to value */
747         vp->type = xp - vp->val.s;
748         memcpy(xp, val, vallen);
749         afree(op, vp->areap);
750 }
751
752 /*
753  * lookup variable (according to (set&LOCAL)), set its attributes
754  * (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, LCASEV,
755  * UCASEV_AL), and optionally set its value if an assignment.
756  */
757 struct tbl *
758 typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
759 {
760         struct tbl *vp;
761         struct tbl *vpbase, *t;
762         char *tvar;
763         const char *val;
764         size_t len;
765         bool vappend = false;
766         enum namerefflag new_refflag = SRF_NOP;
767
768         if ((set & (ARRAY | ASSOC)) == ASSOC) {
769                 new_refflag = SRF_ENABLE;
770                 set &= ~(ARRAY | ASSOC);
771         }
772         if ((clr & (ARRAY | ASSOC)) == ASSOC) {
773                 new_refflag = SRF_DISABLE;
774                 clr &= ~(ARRAY | ASSOC);
775         }
776
777         /* check for valid variable name, search for value */
778         val = skip_varname(var, false);
779         if (val == var) {
780                 /* no variable name given */
781                 return (NULL);
782         }
783         if (ord(*val) == ord('[')) {
784                 if (new_refflag != SRF_NOP)
785                         errorf(Tf_sD_s, var,
786                             "reference variable can't be an array");
787                 len = array_ref_len(val);
788                 if (len == 0)
789                         return (NULL);
790                 /*
791                  * IMPORT is only used when the shell starts up and is
792                  * setting up its environment. Allow only simple array
793                  * references at this time since parameter/command
794                  * substitution is performed on the [expression] which
795                  * would be a major security hole.
796                  */
797                 if (set & IMPORT) {
798                         size_t i;
799
800                         for (i = 1; i < len - 1; i++)
801                                 if (!ctype(val[i], C_DIGIT))
802                                         return (NULL);
803                 }
804                 val += len;
805         }
806         if (ord(val[0]) == ord('=')) {
807                 strndupx(tvar, var, val - var, ATEMP);
808                 ++val;
809         } else if (set & IMPORT) {
810                 /* environment invalid variable name or no assignment */
811                 return (NULL);
812         } else if (ord(val[0]) == ord('+') && ord(val[1]) == ord('=')) {
813                 strndupx(tvar, var, val - var, ATEMP);
814                 val += 2;
815                 vappend = true;
816         } else if (val[0] != '\0') {
817                 /* other invalid variable names (not from environment) */
818                 return (NULL);
819         } else {
820                 /* just varname with no value part nor equals sign */
821                 strdupx(tvar, var, ATEMP);
822                 val = NULL;
823                 /* handle foo[*] => foo (whole array) mapping for R39b */
824                 len = strlen(tvar);
825                 if (len > 3 && ord(tvar[len - 3]) == ord('[') &&
826                     ord(tvar[len - 2]) == ord('*') &&
827                     ord(tvar[len - 1]) == ord(']'))
828                         tvar[len - 3] = '\0';
829         }
830
831         if (new_refflag == SRF_ENABLE) {
832                 const char *qval, *ccp;
833
834                 /* bail out on 'nameref foo+=bar' */
835                 if (vappend)
836                         errorf("appending not allowed for nameref");
837                 /* find value if variable already exists */
838                 if ((qval = val) == NULL) {
839                         varsearch(e->loc, &vp, tvar, hash(tvar));
840                         if (vp == NULL)
841                                 goto nameref_empty;
842                         qval = str_val(vp);
843                 }
844                 /* check target value for being a valid variable name */
845                 ccp = skip_varname(qval, false);
846                 if (ccp == qval) {
847                         int c;
848
849                         if (!(c = (unsigned char)qval[0]))
850                                 goto nameref_empty;
851                         else if (ctype(c, C_DIGIT) && getn(qval, &c))
852                                 goto nameref_rhs_checked;
853                         else if (qval[1] == '\0') switch (c) {
854                         case '$':
855                         case '!':
856                         case '?':
857                         case '#':
858                         case '-':
859                                 goto nameref_rhs_checked;
860                         }
861  nameref_empty:
862                         errorf(Tf_sD_s, var, "empty nameref target");
863                 }
864                 len = (ord(*ccp) == ord('[')) ? array_ref_len(ccp) : 0;
865                 if (ccp[len]) {
866                         /*
867                          * works for cases "no array", "valid array with
868                          * junk after it" and "invalid array"; in the
869                          * latter case, len is also 0 and points to '['
870                          */
871                         errorf(Tf_sD_s, qval,
872                             "nameref target not a valid parameter name");
873                 }
874  nameref_rhs_checked:
875                 /* prevent nameref loops */
876                 while (qval) {
877                         if (!strcmp(qval, tvar))
878                                 errorf(Tf_sD_s, qval,
879                                     "expression recurses on parameter");
880                         varsearch(e->loc, &vp, qval, hash(qval));
881                         qval = NULL;
882                         if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
883                                 qval = str_val(vp);
884                 }
885         }
886
887         /* prevent typeset from creating a local PATH/ENV/SHELL */
888         if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
889             strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
890                 errorf(Tf_sD_s, tvar, "restricted");
891
892         innermost_refflag = new_refflag;
893         vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
894             global(tvar);
895         if (new_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
896                 vp->flag &= ~ASSOC;
897         else if (new_refflag == SRF_ENABLE) {
898                 if (vp->flag & ARRAY) {
899                         struct tbl *a, *tmp;
900
901                         /* free up entire array */
902                         for (a = vp->u.array; a; ) {
903                                 tmp = a;
904                                 a = a->u.array;
905                                 if (tmp->flag & ALLOC)
906                                         afree(tmp->val.s, tmp->areap);
907                                 afree(tmp, tmp->areap);
908                         }
909                         vp->u.array = NULL;
910                         vp->flag &= ~ARRAY;
911                 }
912                 vp->flag |= ASSOC;
913         }
914
915         set &= ~(LOCAL|LOCAL_COPY);
916
917         vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
918
919         /*
920          * only allow export and readonly flag to be set; AT&T ksh
921          * allows any attribute to be changed which means it can be
922          * truncated or modified (-L/-R/-Z/-i)
923          */
924         if ((vpbase->flag & RDONLY) &&
925             (val || clr || (set & ~(EXPORT | RDONLY))))
926                 /* XXX check calls - is error here ok by POSIX? */
927                 errorfx(2, Tf_ro, tvar);
928         afree(tvar, ATEMP);
929
930         /* most calls are with set/clr == 0 */
931         if (set | clr) {
932                 bool ok = true;
933
934                 /*
935                  * XXX if x[0] isn't set, there will be problems: need
936                  * to have one copy of attributes for arrays...
937                  */
938                 for (t = vpbase; t; t = t->u.array) {
939                         bool fake_assign;
940                         char *s = NULL;
941                         char *free_me = NULL;
942
943                         fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
944                             ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
945                             ((t->flag & INTEGER) && (clr & INTEGER)) ||
946                             (!(t->flag & INTEGER) && (set & INTEGER)));
947                         if (fake_assign) {
948                                 if (t->flag & INTEGER) {
949                                         s = str_val(t);
950                                         free_me = NULL;
951                                 } else {
952                                         s = t->val.s + t->type;
953                                         free_me = (t->flag & ALLOC) ? t->val.s :
954                                             NULL;
955                                 }
956                                 t->flag &= ~ALLOC;
957                         }
958                         if (!(t->flag & INTEGER) && (set & INTEGER)) {
959                                 t->type = 0;
960                                 t->flag &= ~ALLOC;
961                         }
962                         t->flag = (t->flag | set) & ~clr;
963                         /*
964                          * Don't change base if assignment is to be
965                          * done, in case assignment fails.
966                          */
967                         if ((set & INTEGER) && base > 0 && (!val || t != vp))
968                                 t->type = base;
969                         if (set & (LJUST|RJUST|ZEROFIL))
970                                 t->u2.field = field;
971                         if (fake_assign) {
972                                 if (!setstr(t, s, KSH_RETURN_ERROR)) {
973                                         /*
974                                          * Somewhat arbitrary action
975                                          * here: zap contents of
976                                          * variable, but keep the flag
977                                          * settings.
978                                          */
979                                         ok = false;
980                                         if (t->flag & INTEGER)
981                                                 t->flag &= ~ISSET;
982                                         else {
983                                                 if (t->flag & ALLOC)
984                                                         afree(t->val.s, t->areap);
985                                                 t->flag &= ~(ISSET|ALLOC);
986                                                 t->type = 0;
987                                         }
988                                 }
989                                 afree(free_me, t->areap);
990                         }
991                 }
992                 if (!ok)
993                         errorfz();
994         }
995
996         if (val != NULL) {
997                 char *tval;
998
999                 if (vappend) {
1000                         tval = shf_smprintf(Tf_ss, str_val(vp), val);
1001                         val = tval;
1002                 } else
1003                         tval = NULL;
1004
1005                 if (vp->flag&INTEGER) {
1006                         /* do not zero base before assignment */
1007                         setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
1008                         /* done after assignment to override default */
1009                         if (base > 0)
1010                                 vp->type = base;
1011                 } else
1012                         /* setstr can't fail (readonly check already done) */
1013                         setstr(vp, val, KSH_RETURN_ERROR | 0x4);
1014
1015                 afree(tval, ATEMP);
1016         }
1017
1018         /* only x[0] is ever exported, so use vpbase */
1019         if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
1020             vpbase->type == 0)
1021                 exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
1022
1023         return (vp);
1024 }
1025
1026 /**
1027  * Unset a variable. The flags can be:
1028  * |1   = tear down entire array
1029  * |2   = keep attributes, only unset content
1030  */
1031 void
1032 unset(struct tbl *vp, int flags)
1033 {
1034         if (vp->flag & ALLOC)
1035                 afree(vp->val.s, vp->areap);
1036         if ((vp->flag & ARRAY) && (flags & 1)) {
1037                 struct tbl *a, *tmp;
1038
1039                 /* free up entire array */
1040                 for (a = vp->u.array; a; ) {
1041                         tmp = a;
1042                         a = a->u.array;
1043                         if (tmp->flag & ALLOC)
1044                                 afree(tmp->val.s, tmp->areap);
1045                         afree(tmp, tmp->areap);
1046                 }
1047                 vp->u.array = NULL;
1048         }
1049         if (flags & 2) {
1050                 vp->flag &= ~(ALLOC|ISSET);
1051                 return;
1052         }
1053         /* if foo[0] is being unset, the remainder of the array is kept... */
1054         vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
1055         if (vp->flag & SPECIAL)
1056                 /* responsible for 'unspecial'ing var */
1057                 unsetspec(vp);
1058 }
1059
1060 /*
1061  * Return a pointer to the first char past a legal variable name
1062  * (returns the argument if there is no legal name, returns a pointer to
1063  * the terminating NUL if whole string is legal).
1064  */
1065 const char *
1066 skip_varname(const char *s, bool aok)
1067 {
1068         size_t alen;
1069
1070         if (s && ctype(*s, C_ALPHX)) {
1071                 do {
1072                         ++s;
1073                 } while (ctype(*s, C_ALNUX));
1074                 if (aok && ord(*s) == ord('[') && (alen = array_ref_len(s)))
1075                         s += alen;
1076         }
1077         return (s);
1078 }
1079
1080 /* Return a pointer to the first character past any legal variable name */
1081 const char *
1082 skip_wdvarname(const char *s,
1083     /* skip array de-reference? */
1084     bool aok)
1085 {
1086         if (s[0] == CHAR && ctype(s[1], C_ALPHX)) {
1087                 do {
1088                         s += 2;
1089                 } while (s[0] == CHAR && ctype(s[1], C_ALNUX));
1090                 if (aok && s[0] == CHAR && ord(s[1]) == ord('[')) {
1091                         /* skip possible array de-reference */
1092                         const char *p = s;
1093                         char c;
1094                         int depth = 0;
1095
1096                         while (/* CONSTCOND */ 1) {
1097                                 if (p[0] != CHAR)
1098                                         break;
1099                                 c = p[1];
1100                                 p += 2;
1101                                 if (ord(c) == ord('['))
1102                                         depth++;
1103                                 else if (ord(c) == ord(']') && --depth == 0) {
1104                                         s = p;
1105                                         break;
1106                                 }
1107                         }
1108                 }
1109         }
1110         return (s);
1111 }
1112
1113 /* Check if coded string s is a variable name */
1114 int
1115 is_wdvarname(const char *s, bool aok)
1116 {
1117         const char *p = skip_wdvarname(s, aok);
1118
1119         return (p != s && p[0] == EOS);
1120 }
1121
1122 /* Check if coded string s is a variable assignment */
1123 int
1124 is_wdvarassign(const char *s)
1125 {
1126         const char *p = skip_wdvarname(s, true);
1127
1128         return (p != s && p[0] == CHAR &&
1129             (p[1] == '=' || (p[1] == '+' && p[2] == CHAR && p[3] == '=')));
1130 }
1131
1132 /*
1133  * Make the exported environment from the exported names in the dictionary.
1134  */
1135 char **
1136 makenv(void)
1137 {
1138         ssize_t i;
1139         struct block *l;
1140         XPtrV denv;
1141         struct tbl *vp, **vpp;
1142
1143         XPinit(denv, 64);
1144         for (l = e->loc; l != NULL; l = l->next) {
1145                 vpp = l->vars.tbls;
1146                 i = 1 << (l->vars.tshift);
1147                 while (--i >= 0)
1148                         if ((vp = *vpp++) != NULL &&
1149                             (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
1150                                 struct block *l2;
1151                                 struct tbl *vp2;
1152                                 uint32_t h = hash(vp->name);
1153
1154                                 /* unexport any redefined instances */
1155                                 for (l2 = l->next; l2 != NULL; l2 = l2->next) {
1156                                         vp2 = ktsearch(&l2->vars, vp->name, h);
1157                                         if (vp2 != NULL)
1158                                                 vp2->flag &= ~EXPORT;
1159                                 }
1160                                 if ((vp->flag&INTEGER)) {
1161                                         /* integer to string */
1162                                         char *val;
1163                                         val = str_val(vp);
1164                                         vp->flag &= ~(INTEGER|RDONLY|SPECIAL);
1165                                         /* setstr can't fail here */
1166                                         setstr(vp, val, KSH_RETURN_ERROR);
1167                                 }
1168 #ifdef __OS2__
1169                                 /* these special variables are not exported */
1170                                 if (!strcmp(vp->name, "BEGINLIBPATH") ||
1171                                     !strcmp(vp->name, "ENDLIBPATH") ||
1172                                     !strcmp(vp->name, "LIBPATHSTRICT"))
1173                                         continue;
1174 #endif
1175                                 XPput(denv, vp->val.s);
1176                         }
1177                 if (l->flags & BF_STOPENV)
1178                         break;
1179         }
1180         XPput(denv, NULL);
1181         return ((char **)XPclose(denv));
1182 }
1183
1184 /*
1185  * handle special variables with side effects - PATH, SECONDS.
1186  */
1187
1188 /* Test if name is a special parameter */
1189 static int
1190 special(const char *name)
1191 {
1192         struct tbl *tp;
1193
1194         tp = ktsearch(&specials, name, hash(name));
1195         return (tp && (tp->flag & ISSET) ? tp->type : V_NONE);
1196 }
1197
1198 /* Make a variable non-special */
1199 static void
1200 unspecial(const char *name)
1201 {
1202         struct tbl *tp;
1203
1204         tp = ktsearch(&specials, name, hash(name));
1205         if (tp)
1206                 ktdelete(tp);
1207 }
1208
1209 static time_t seconds;          /* time SECONDS last set */
1210 static mksh_uari_t user_lineno; /* what user set $LINENO to */
1211
1212 /* minimum values from the OS we consider sane, lowered for R53 */
1213 #define MIN_COLS        4
1214 #define MIN_LINS        2
1215
1216 static void
1217 getspec(struct tbl *vp)
1218 {
1219         mksh_ari_u num;
1220         int st;
1221         struct timeval tv;
1222
1223         switch ((st = special(vp->name))) {
1224         case V_COLUMNS:
1225         case V_LINES:
1226                 /*
1227                  * Do NOT export COLUMNS/LINES. Many applications
1228                  * check COLUMNS/LINES before checking ws.ws_col/row,
1229                  * so if the app is started with C/L in the environ
1230                  * and the window is then resized, the app won't
1231                  * see the change cause the environ doesn't change.
1232                  */
1233                 if (got_winch)
1234                         change_winsz();
1235                 break;
1236         }
1237         switch (st) {
1238         case V_BASHPID:
1239                 num.u = (mksh_uari_t)procpid;
1240                 break;
1241         case V_COLUMNS:
1242                 num.i = x_cols;
1243                 break;
1244         case V_HISTSIZE:
1245                 num.i = histsize;
1246                 break;
1247         case V_LINENO:
1248                 num.u = (mksh_uari_t)current_lineno + user_lineno;
1249                 break;
1250         case V_LINES:
1251                 num.i = x_lins;
1252                 break;
1253         case V_EPOCHREALTIME: {
1254                 /* 10(%u) + 1(.) + 6 + NUL */
1255                 char buf[18];
1256
1257                 vp->flag &= ~SPECIAL;
1258                 mksh_TIME(tv);
1259                 shf_snprintf(buf, sizeof(buf), "%u.%06u",
1260                     (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
1261                 setstr(vp, buf, KSH_RETURN_ERROR | 0x4);
1262                 vp->flag |= SPECIAL;
1263                 return;
1264         }
1265         case V_OPTIND:
1266                 num.i = user_opt.uoptind;
1267                 break;
1268         case V_RANDOM:
1269                 num.i = rndget();
1270                 break;
1271         case V_SECONDS:
1272                 /*
1273                  * On start up the value of SECONDS is used before
1274                  * it has been set - don't do anything in this case
1275                  * (see initcoms[] in main.c).
1276                  */
1277                 if (vp->flag & ISSET) {
1278                         mksh_TIME(tv);
1279                         num.i = tv.tv_sec - seconds;
1280                 } else
1281                         return;
1282                 break;
1283         default:
1284                 /* do nothing, do not touch vp at all */
1285                 return;
1286         }
1287         vp->flag &= ~SPECIAL;
1288         setint_n(vp, num.i, 0);
1289         vp->flag |= SPECIAL;
1290 }
1291
1292 static void
1293 setspec(struct tbl *vp)
1294 {
1295         mksh_ari_u num;
1296         char *s;
1297         int st;
1298
1299         switch ((st = special(vp->name))) {
1300 #ifdef __OS2__
1301         case V_BEGINLIBPATH:
1302         case V_ENDLIBPATH:
1303         case V_LIBPATHSTRICT:
1304                 setextlibpath(vp->name, str_val(vp));
1305                 return;
1306 #endif
1307 #if HAVE_PERSISTENT_HISTORY
1308         case V_HISTFILE:
1309                 sethistfile(str_val(vp));
1310                 return;
1311 #endif
1312         case V_IFS:
1313                 set_ifs(str_val(vp));
1314                 return;
1315         case V_PATH:
1316                 afree(path, APERM);
1317                 s = str_val(vp);
1318                 strdupx(path, s, APERM);
1319                 /* clear tracked aliases */
1320                 flushcom(true);
1321                 return;
1322 #ifndef MKSH_NO_CMDLINE_EDITING
1323         case V_TERM:
1324                 x_initterm(str_val(vp));
1325                 return;
1326 #endif
1327         case V_TMPDIR:
1328                 afree(tmpdir, APERM);
1329                 tmpdir = NULL;
1330                 /*
1331                  * Use tmpdir iff it is an absolute path, is writable
1332                  * and searchable and is a directory...
1333                  */
1334                 {
1335                         struct stat statb;
1336
1337                         s = str_val(vp);
1338                         /* LINTED use of access */
1339                         if (mksh_abspath(s) && access(s, W_OK|X_OK) == 0 &&
1340                             stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
1341                                 strdupx(tmpdir, s, APERM);
1342                 }
1343                 return;
1344         /* common sub-cases */
1345         case V_COLUMNS:
1346         case V_LINES:
1347                 if (vp->flag & IMPORT) {
1348                         /* do not touch */
1349                         unspecial(vp->name);
1350                         vp->flag &= ~SPECIAL;
1351                         return;
1352                 }
1353                 /* FALLTHROUGH */
1354         case V_HISTSIZE:
1355         case V_LINENO:
1356         case V_OPTIND:
1357         case V_RANDOM:
1358         case V_SECONDS:
1359         case V_TMOUT:
1360                 vp->flag &= ~SPECIAL;
1361                 if (getint(vp, &num, false) == -1) {
1362                         s = str_val(vp);
1363                         if (st != V_RANDOM)
1364                                 errorf(Tf_sD_sD_s, vp->name, Tbadnum, s);
1365                         num.u = hash(s);
1366                 }
1367                 vp->flag |= SPECIAL;
1368                 break;
1369         default:
1370                 /* do nothing, do not touch vp at all */
1371                 return;
1372         }
1373
1374         /* process the singular parts of the common cases */
1375
1376         switch (st) {
1377         case V_COLUMNS:
1378                 if (num.i >= MIN_COLS)
1379                         x_cols = num.i;
1380                 break;
1381         case V_HISTSIZE:
1382                 sethistsize(num.i);
1383                 break;
1384         case V_LINENO:
1385                 /* The -1 is because line numbering starts at 1. */
1386                 user_lineno = num.u - (mksh_uari_t)current_lineno - 1;
1387                 break;
1388         case V_LINES:
1389                 if (num.i >= MIN_LINS)
1390                         x_lins = num.i;
1391                 break;
1392         case V_OPTIND:
1393                 getopts_reset((int)num.i);
1394                 break;
1395         case V_RANDOM:
1396                 /*
1397                  * mksh R39d+ no longer has the traditional repeatability
1398                  * of $RANDOM sequences, but always retains state
1399                  */
1400                 rndset((unsigned long)num.u);
1401                 break;
1402         case V_SECONDS:
1403                 {
1404                         struct timeval tv;
1405
1406                         mksh_TIME(tv);
1407                         seconds = tv.tv_sec - num.i;
1408                 }
1409                 break;
1410         case V_TMOUT:
1411                 ksh_tmout = num.i >= 0 ? num.i : 0;
1412                 break;
1413         }
1414 }
1415
1416 static void
1417 unsetspec(struct tbl *vp)
1418 {
1419         /*
1420          * AT&T ksh man page says OPTIND, OPTARG and _ lose special
1421          * meaning, but OPTARG does not (still set by getopts) and _ is
1422          * also still set in various places. Don't know what AT&T does
1423          * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
1424          * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
1425          */
1426
1427         switch (special(vp->name)) {
1428 #ifdef __OS2__
1429         case V_BEGINLIBPATH:
1430         case V_ENDLIBPATH:
1431         case V_LIBPATHSTRICT:
1432                 setextlibpath(vp->name, "");
1433                 return;
1434 #endif
1435 #if HAVE_PERSISTENT_HISTORY
1436         case V_HISTFILE:
1437                 sethistfile(NULL);
1438                 return;
1439 #endif
1440         case V_IFS:
1441                 set_ifs(TC_IFSWS);
1442                 break;
1443         case V_PATH:
1444                 afree(path, APERM);
1445                 strdupx(path, def_path, APERM);
1446                 /* clear tracked aliases */
1447                 flushcom(true);
1448                 break;
1449 #ifndef MKSH_NO_CMDLINE_EDITING
1450         case V_TERM:
1451                 x_initterm(null);
1452                 return;
1453 #endif
1454         case V_TMPDIR:
1455                 /* should not become unspecial */
1456                 if (tmpdir) {
1457                         afree(tmpdir, APERM);
1458                         tmpdir = NULL;
1459                 }
1460                 break;
1461         case V_LINENO:
1462         case V_RANDOM:
1463         case V_SECONDS:
1464         case V_TMOUT:
1465                 /* AT&T ksh leaves previous value in place */
1466                 unspecial(vp->name);
1467                 break;
1468         }
1469 }
1470
1471 /*
1472  * Search for (and possibly create) a table entry starting with
1473  * vp, indexed by val.
1474  */
1475 struct tbl *
1476 arraysearch(struct tbl *vp, uint32_t val)
1477 {
1478         struct tbl *prev, *curr, *news;
1479         size_t len;
1480
1481         vp->flag = (vp->flag | (ARRAY | DEFINED)) & ~ASSOC;
1482         /* the table entry is always [0] */
1483         if (val == 0)
1484                 return (vp);
1485         prev = vp;
1486         curr = vp->u.array;
1487         while (curr && curr->ua.index < val) {
1488                 prev = curr;
1489                 curr = curr->u.array;
1490         }
1491         if (curr && curr->ua.index == val) {
1492                 if (curr->flag&ISSET)
1493                         return (curr);
1494                 news = curr;
1495         } else
1496                 news = NULL;
1497         if (!news) {
1498                 len = strlen(vp->name);
1499                 checkoktoadd(len, 1 + offsetof(struct tbl, name[0]));
1500                 news = alloc(offsetof(struct tbl, name[0]) + ++len, vp->areap);
1501                 memcpy(news->name, vp->name, len);
1502         }
1503         news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
1504         news->type = vp->type;
1505         news->areap = vp->areap;
1506         news->u2.field = vp->u2.field;
1507         news->ua.index = val;
1508
1509         if (curr != news) {
1510                 /* not reusing old array entry */
1511                 prev->u.array = news;
1512                 news->u.array = curr;
1513         }
1514         return (news);
1515 }
1516
1517 /*
1518  * Return the length of an array reference (eg, [1+2]) - cp is assumed
1519  * to point to the open bracket. Returns 0 if there is no matching
1520  * closing bracket.
1521  *
1522  * XXX this should parse the actual arithmetic syntax
1523  */
1524 size_t
1525 array_ref_len(const char *cp)
1526 {
1527         const char *s = cp;
1528         char c;
1529         int depth = 0;
1530
1531         while ((c = *s++) && (ord(c) != ord(']') || --depth))
1532                 if (ord(c) == ord('['))
1533                         depth++;
1534         if (!c)
1535                 return (0);
1536         return (s - cp);
1537 }
1538
1539 /*
1540  * Make a copy of the base of an array name
1541  */
1542 char *
1543 arrayname(const char *str)
1544 {
1545         const char *p;
1546         char *rv;
1547
1548         if (!(p = cstrchr(str, '[')))
1549                 /* Shouldn't happen, but why worry? */
1550                 strdupx(rv, str, ATEMP);
1551         else
1552                 strndupx(rv, str, p - str, ATEMP);
1553
1554         return (rv);
1555 }
1556
1557 /* set (or overwrite, if reset) the array variable var to the values in vals */
1558 mksh_uari_t
1559 set_array(const char *var, bool reset, const char **vals)
1560 {
1561         struct tbl *vp, *vq;
1562         mksh_uari_t i = 0, j = 0;
1563         const char *ccp = var;
1564         char *cp = NULL;
1565         size_t n;
1566
1567         /* to get local array, use "local foo; set -A foo" */
1568         n = strlen(var);
1569         if (n > 0 && var[n - 1] == '+') {
1570                 /* append mode */
1571                 reset = false;
1572                 strndupx(cp, var, n - 1, ATEMP);
1573                 ccp = cp;
1574         }
1575         vp = global(ccp);
1576
1577         /* Note: AT&T ksh allows set -A but not set +A of a read-only var */
1578         if ((vp->flag&RDONLY))
1579                 errorfx(2, Tf_ro, ccp);
1580         /* This code is quite non-optimal */
1581         if (reset) {
1582                 /* trash existing values and attributes */
1583                 unset(vp, 1);
1584                 /* allocate-by-access the [0] element to keep in scope */
1585                 arraysearch(vp, 0);
1586         }
1587         /*
1588          * TODO: would be nice for assignment to completely succeed or
1589          * completely fail. Only really effects integer arrays:
1590          * evaluation of some of vals[] may fail...
1591          */
1592         if (cp != NULL) {
1593                 /* find out where to set when appending */
1594                 for (vq = vp; vq; vq = vq->u.array) {
1595                         if (!(vq->flag & ISSET))
1596                                 continue;
1597                         if (arrayindex(vq) >= j)
1598                                 j = arrayindex(vq) + 1;
1599                 }
1600                 afree(cp, ATEMP);
1601         }
1602         while ((ccp = vals[i])) {
1603 #if 0 /* temporarily taken out due to regression */
1604                 if (ord(*ccp) == ord('[')) {
1605                         int level = 0;
1606
1607                         while (*ccp) {
1608                                 if (ord(*ccp) == ord(']') && --level == 0)
1609                                         break;
1610                                 if (ord(*ccp) == ord('['))
1611                                         ++level;
1612                                 ++ccp;
1613                         }
1614                         if (ord(*ccp) == ord(']') && level == 0 &&
1615                             ord(ccp[1]) == ord('=')) {
1616                                 strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
1617                                     ATEMP);
1618                                 evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
1619                                     KSH_UNWIND_ERROR, true);
1620                                 afree(cp, ATEMP);
1621                                 ccp += 2;
1622                         } else
1623                                 ccp = vals[i];
1624                 }
1625 #endif
1626
1627                 vq = arraysearch(vp, j);
1628                 /* would be nice to deal with errors here... (see above) */
1629                 setstr(vq, ccp, KSH_RETURN_ERROR);
1630                 i++;
1631                 j++;
1632         }
1633
1634         return (i);
1635 }
1636
1637 void
1638 change_winsz(void)
1639 {
1640         struct timeval tv;
1641
1642         mksh_TIME(tv);
1643         BAFHUpdateMem_mem(qh_state, &tv, sizeof(tv));
1644
1645 #ifdef TIOCGWINSZ
1646         /* check if window size has changed */
1647         if (tty_init_fd() < 2) {
1648                 struct winsize ws;
1649
1650                 if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
1651                         if (ws.ws_col)
1652                                 x_cols = ws.ws_col;
1653                         if (ws.ws_row)
1654                                 x_lins = ws.ws_row;
1655                 }
1656         }
1657 #endif
1658
1659         /* bounds check for sane values, use defaults otherwise */
1660         if (x_cols < MIN_COLS)
1661                 x_cols = 80;
1662         if (x_lins < MIN_LINS)
1663                 x_lins = 24;
1664
1665 #ifdef SIGWINCH
1666         got_winch = 0;
1667 #endif
1668 }
1669
1670 uint32_t
1671 hash(const void *s)
1672 {
1673         register uint32_t h;
1674
1675         BAFHInit(h);
1676         BAFHUpdateStr_reg(h, s);
1677         BAFHFinish_reg(h);
1678         return (h);
1679 }
1680
1681 uint32_t
1682 chvt_rndsetup(const void *bp, size_t sz)
1683 {
1684         register uint32_t h;
1685
1686         /* use LCG as seed but try to get them to deviate immediately */
1687         h = lcg_state;
1688         (void)rndget();
1689         BAFHFinish_reg(h);
1690         /* variation through pid, ppid, and the works */
1691         BAFHUpdateMem_reg(h, &rndsetupstate, sizeof(rndsetupstate));
1692         /* some variation, some possibly entropy, depending on OE */
1693         BAFHUpdateMem_reg(h, bp, sz);
1694         /* mix them all up */
1695         BAFHFinish_reg(h);
1696
1697         return (h);
1698 }
1699
1700 mksh_ari_t
1701 rndget(void)
1702 {
1703         /*
1704          * this is the same Linear Congruential PRNG as Borland
1705          * C/C++ allegedly uses in its built-in rand() function
1706          */
1707         return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
1708 }
1709
1710 void
1711 rndset(unsigned long v)
1712 {
1713         register uint32_t h;
1714 #if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
1715         register uint32_t t;
1716 #endif
1717         struct {
1718                 struct timeval tv;
1719                 void *sp;
1720                 uint32_t qh;
1721                 pid_t pp;
1722                 short r;
1723         } z;
1724
1725         /* clear the allocated space, for valgrind and to avoid UB */
1726         memset(&z, 0, sizeof(z));
1727
1728         h = lcg_state;
1729         BAFHFinish_reg(h);
1730         BAFHUpdateMem_reg(h, &v, sizeof(v));
1731
1732         mksh_TIME(z.tv);
1733         z.sp = &lcg_state;
1734         z.pp = procpid;
1735         z.r = (short)rndget();
1736
1737 #if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
1738         t = qh_state;
1739         BAFHFinish_reg(t);
1740         z.qh = (t & 0xFFFF8000) | rndget();
1741         lcg_state = (t << 15) | rndget();
1742         /*
1743          * either we have very chap entropy get and push available,
1744          * with malloc() pulling in this code already anyway, or the
1745          * user requested us to use the old functions
1746          */
1747         t = h;
1748         BAFHUpdateMem_reg(t, &lcg_state, sizeof(lcg_state));
1749         BAFHFinish_reg(t);
1750         lcg_state = t;
1751 #if defined(arc4random_pushb_fast)
1752         arc4random_pushb_fast(&lcg_state, sizeof(lcg_state));
1753         lcg_state = arc4random();
1754 #else
1755         lcg_state = arc4random_pushb(&lcg_state, sizeof(lcg_state));
1756 #endif
1757         BAFHUpdateMem_reg(h, &lcg_state, sizeof(lcg_state));
1758 #else
1759         z.qh = qh_state;
1760 #endif
1761
1762         BAFHUpdateMem_reg(h, &z, sizeof(z));
1763         BAFHFinish_reg(h);
1764         lcg_state = h;
1765 }
1766
1767 void
1768 rndpush(const void *s)
1769 {
1770         register uint32_t h = qh_state;
1771
1772         BAFHUpdateStr_reg(h, s);
1773         BAFHUpdateOctet_reg(h, 0);
1774         qh_state = h;
1775 }
1776
1777 /* record last glob match */
1778 void
1779 record_match(const char *istr)
1780 {
1781         struct tbl *vp;
1782
1783         vp = local("KSH_MATCH", false);
1784         unset(vp, 1);
1785         vp->flag = DEFINED | RDONLY;
1786         setstr(vp, istr, 0x4);
1787 }
1788
1789 /* typeset, global(deprecated), export, and readonly */
1790 int
1791 c_typeset(const char **wp)
1792 {
1793         struct tbl *vp, **p;
1794         uint32_t fset = 0, fclr = 0, flag;
1795         int thing = 0, field = 0, base = 0, i;
1796         struct block *l;
1797         const char *opts;
1798         const char *fieldstr = NULL, *basestr = NULL;
1799         bool localv = false, func = false, pflag = false, istset = true;
1800         enum namerefflag new_refflag = SRF_NOP;
1801
1802         switch (**wp) {
1803
1804         /* export */
1805         case 'e':
1806                 fset |= EXPORT;
1807                 istset = false;
1808                 break;
1809
1810         /* readonly */
1811         case 'r':
1812                 fset |= RDONLY;
1813                 istset = false;
1814                 break;
1815
1816         /* set */
1817         case 's':
1818                 /* called with 'typeset -' */
1819                 break;
1820
1821         /* typeset */
1822         case 't':
1823                 localv = true;
1824                 break;
1825         }
1826
1827         /* see comment below regarding possible opions */
1828         opts = istset ? "L#R#UZ#afgi#lnprtux" : "p";
1829
1830         builtin_opt.flags |= GF_PLUSOPT;
1831         /*
1832          * AT&T ksh seems to have 0-9 as options which are multiplied
1833          * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
1834          * sets right justify in a field of 12). This allows options
1835          * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
1836          * does not allow the number to be specified as a separate argument
1837          * Here, the number must follow the RLZi option, but is optional
1838          * (see the # kludge in ksh_getopt()).
1839          */
1840         while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
1841                 flag = 0;
1842                 switch (i) {
1843                 case 'L':
1844                         flag = LJUST;
1845                         fieldstr = builtin_opt.optarg;
1846                         break;
1847                 case 'R':
1848                         flag = RJUST;
1849                         fieldstr = builtin_opt.optarg;
1850                         break;
1851                 case 'U':
1852                         /*
1853                          * AT&T ksh uses u, but this conflicts with
1854                          * upper/lower case. If this option is changed,
1855                          * need to change the -U below as well
1856                          */
1857                         flag = INT_U;
1858                         break;
1859                 case 'Z':
1860                         flag = ZEROFIL;
1861                         fieldstr = builtin_opt.optarg;
1862                         break;
1863                 case 'a':
1864                         /*
1865                          * this is supposed to set (-a) or unset (+a) the
1866                          * indexed array attribute; it does nothing on an
1867                          * existing regular string or indexed array though
1868                          */
1869                         break;
1870                 case 'f':
1871                         func = true;
1872                         break;
1873                 case 'g':
1874                         localv = (builtin_opt.info & GI_PLUS) ? true : false;
1875                         break;
1876                 case 'i':
1877                         flag = INTEGER;
1878                         basestr = builtin_opt.optarg;
1879                         break;
1880                 case 'l':
1881                         flag = LCASEV;
1882                         break;
1883                 case 'n':
1884                         new_refflag = (builtin_opt.info & GI_PLUS) ?
1885                             SRF_DISABLE : SRF_ENABLE;
1886                         break;
1887                 /* export, readonly: POSIX -p flag */
1888                 case 'p':
1889                         /* typeset: show values as well */
1890                         pflag = true;
1891                         if (istset)
1892                                 continue;
1893                         break;
1894                 case 'r':
1895                         flag = RDONLY;
1896                         break;
1897                 case 't':
1898                         flag = TRACE;
1899                         break;
1900                 case 'u':
1901                         /* upper case / autoload */
1902                         flag = UCASEV_AL;
1903                         break;
1904                 case 'x':
1905                         flag = EXPORT;
1906                         break;
1907                 case '?':
1908                         return (1);
1909                 }
1910                 if (builtin_opt.info & GI_PLUS) {
1911                         fclr |= flag;
1912                         fset &= ~flag;
1913                         thing = '+';
1914                 } else {
1915                         fset |= flag;
1916                         fclr &= ~flag;
1917                         thing = '-';
1918                 }
1919         }
1920
1921         if (fieldstr && !getn(fieldstr, &field)) {
1922                 bi_errorf(Tf_sD_s, Tbadnum, fieldstr);
1923                 return (1);
1924         }
1925         if (basestr) {
1926                 if (!getn(basestr, &base)) {
1927                         bi_errorf(Tf_sD_s, "bad integer base", basestr);
1928                         return (1);
1929                 }
1930                 if (base < 1 || base > 36)
1931                         base = 10;
1932         }
1933
1934         if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
1935             (wp[builtin_opt.optind][0] == '-' ||
1936             wp[builtin_opt.optind][0] == '+') &&
1937             wp[builtin_opt.optind][1] == '\0') {
1938                 thing = wp[builtin_opt.optind][0];
1939                 builtin_opt.optind++;
1940         }
1941
1942         if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
1943             new_refflag != SRF_NOP)) {
1944                 bi_errorf("only -t, -u and -x options may be used with -f");
1945                 return (1);
1946         }
1947         if (wp[builtin_opt.optind]) {
1948                 /*
1949                  * Take care of exclusions.
1950                  * At this point, flags in fset are cleared in fclr and vice
1951                  * versa. This property should be preserved.
1952                  */
1953                 if (fset & LCASEV)
1954                         /* LCASEV has priority over UCASEV_AL */
1955                         fset &= ~UCASEV_AL;
1956                 if (fset & LJUST)
1957                         /* LJUST has priority over RJUST */
1958                         fset &= ~RJUST;
1959                 if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
1960                         /* -Z implies -ZR */
1961                         fset |= RJUST;
1962                         fclr &= ~RJUST;
1963                 }
1964                 /*
1965                  * Setting these attributes clears the others, unless they
1966                  * are also set in this command
1967                  */
1968                 if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
1969                     INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
1970                         fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
1971                             LCASEV | INTEGER | INT_U | INT_L);
1972         }
1973         if (new_refflag != SRF_NOP) {
1974                 fclr &= ~(ARRAY | ASSOC);
1975                 fset &= ~(ARRAY | ASSOC);
1976                 fclr |= EXPORT;
1977                 fset |= ASSOC;
1978                 if (new_refflag == SRF_DISABLE)
1979                         fclr |= ASSOC;
1980         }
1981
1982         /* set variables and attributes */
1983         if (wp[builtin_opt.optind] &&
1984             /* not "typeset -p varname" */
1985             !(!func && pflag && !(fset | fclr))) {
1986                 int rv = 0;
1987                 struct tbl *f;
1988
1989                 if (localv && !func)
1990                         fset |= LOCAL;
1991                 for (i = builtin_opt.optind; wp[i]; i++) {
1992                         if (func) {
1993                                 f = findfunc(wp[i], hash(wp[i]),
1994                                     tobool(fset & UCASEV_AL));
1995                                 if (!f) {
1996                                         /* AT&T ksh does ++rv: bogus */
1997                                         rv = 1;
1998                                         continue;
1999                                 }
2000                                 if (fset | fclr) {
2001                                         f->flag |= fset;
2002                                         f->flag &= ~fclr;
2003                                 } else {
2004                                         fpFUNCTf(shl_stdout, 0,
2005                                             tobool(f->flag & FKSH),
2006                                             wp[i], f->val.t);
2007                                         shf_putc('\n', shl_stdout);
2008                                 }
2009                         } else if (!typeset(wp[i], fset, fclr, field, base)) {
2010                                 bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
2011                                 return (1);
2012                         }
2013                 }
2014                 return (rv);
2015         }
2016
2017         /* list variables and attributes */
2018
2019         /* no difference at this point.. */
2020         flag = fset | fclr;
2021         if (func) {
2022                 for (l = e->loc; l; l = l->next) {
2023                         for (p = ktsort(&l->funs); (vp = *p++); ) {
2024                                 if (flag && (vp->flag & flag) == 0)
2025                                         continue;
2026                                 if (thing == '-')
2027                                         fpFUNCTf(shl_stdout, 0,
2028                                             tobool(vp->flag & FKSH),
2029                                             vp->name, vp->val.t);
2030                                 else
2031                                         shf_puts(vp->name, shl_stdout);
2032                                 shf_putc('\n', shl_stdout);
2033                         }
2034                 }
2035         } else if (wp[builtin_opt.optind]) {
2036                 for (i = builtin_opt.optind; wp[i]; i++) {
2037                         vp = isglobal(wp[i], false);
2038                         c_typeset_vardump(vp, flag, thing,
2039                             last_lookup_was_array ? 4 : 0, pflag, istset);
2040                 }
2041         } else
2042                 c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
2043         return (0);
2044 }
2045
2046 static void
2047 c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
2048     bool pflag, bool istset)
2049 {
2050         struct tbl **blockvars, *vp;
2051
2052         if (l->next)
2053                 c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
2054         blockvars = ktsort(&l->vars);
2055         while ((vp = *blockvars++))
2056                 c_typeset_vardump(vp, flag, thing, 0, pflag, istset);
2057         /*XXX doesn’t this leak? */
2058 }
2059
2060 static void
2061 c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set,
2062     bool pflag, bool istset)
2063 {
2064         struct tbl *tvp;
2065         char *s;
2066
2067         if (!vp)
2068                 return;
2069
2070         /*
2071          * See if the parameter is set (for arrays, if any
2072          * element is set).
2073          */
2074         for (tvp = vp; tvp; tvp = tvp->u.array)
2075                 if (tvp->flag & ISSET) {
2076                         any_set |= 1;
2077                         break;
2078                 }
2079
2080         /*
2081          * Check attributes - note that all array elements
2082          * have (should have?) the same attributes, so checking
2083          * the first is sufficient.
2084          *
2085          * Report an unset param only if the user has
2086          * explicitly given it some attribute (like export);
2087          * otherwise, after "echo $FOO", we would report FOO...
2088          */
2089         if (!any_set && !(vp->flag & USERATTRIB))
2090                 return;
2091         if (flag && (vp->flag & flag) == 0)
2092                 return;
2093         if (!(vp->flag & ARRAY))
2094                 /* optimise later conditionals */
2095                 any_set = 0;
2096         do {
2097                 /*
2098                  * Ignore array elements that aren't set unless there
2099                  * are no set elements, in which case the first is
2100                  * reported on
2101                  */
2102                 if (any_set && !(vp->flag & ISSET))
2103                         continue;
2104                 /* no arguments */
2105                 if (!thing && !flag) {
2106                         if (any_set == 1) {
2107                                 shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
2108                                 any_set = 2;
2109                         }
2110                         /*
2111                          * AT&T ksh prints things like export, integer,
2112                          * leftadj, zerofill, etc., but POSIX says must
2113                          * be suitable for re-entry...
2114                          */
2115                         shprintf(Tf_s_s, Ttypeset, "");
2116                         if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
2117                                 shprintf(Tf__c_, 'n');
2118                         if ((vp->flag & INTEGER))
2119                                 shprintf(Tf__c_, 'i');
2120                         if ((vp->flag & EXPORT))
2121                                 shprintf(Tf__c_, 'x');
2122                         if ((vp->flag & RDONLY))
2123                                 shprintf(Tf__c_, 'r');
2124                         if ((vp->flag & TRACE))
2125                                 shprintf(Tf__c_, 't');
2126                         if ((vp->flag & LJUST))
2127                                 shprintf("-L%d ", vp->u2.field);
2128                         if ((vp->flag & RJUST))
2129                                 shprintf("-R%d ", vp->u2.field);
2130                         if ((vp->flag & ZEROFIL))
2131                                 shprintf(Tf__c_, 'Z');
2132                         if ((vp->flag & LCASEV))
2133                                 shprintf(Tf__c_, 'l');
2134                         if ((vp->flag & UCASEV_AL))
2135                                 shprintf(Tf__c_, 'u');
2136                         if ((vp->flag & INT_U))
2137                                 shprintf(Tf__c_, 'U');
2138                 } else if (pflag) {
2139                         shprintf(Tf_s_s, istset ? Ttypeset :
2140                             (flag & EXPORT) ? Texport : Treadonly, "");
2141                 }
2142                 if (any_set)
2143                         shprintf("%s[%lu]", vp->name, arrayindex(vp));
2144                 else
2145                         shf_puts(vp->name, shl_stdout);
2146                 if ((!thing && !flag && pflag) ||
2147                     (thing == '-' && (vp->flag & ISSET))) {
2148                         s = str_val(vp);
2149                         shf_putc('=', shl_stdout);
2150                         /* AT&T ksh can't have justified integers... */
2151                         if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
2152                                 shf_puts(s, shl_stdout);
2153                         else
2154                                 print_value_quoted(shl_stdout, s);
2155                 }
2156                 shf_putc('\n', shl_stdout);
2157
2158                 /*
2159                  * Only report first 'element' of an array with
2160                  * no set elements.
2161                  */
2162                 if (!any_set)
2163                         return;
2164         } while (!(any_set & 4) && (vp = vp->u.array));
2165 }