OSDN Git Service

mkshrc: add back the aliases I like
[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, 2018
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.226 2018/07/15 17:21:24 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 *, bool);
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, false);
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 21 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, psiz, "%-*.*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, true);
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 = special(vp->name);
1298
1299 #ifdef MKSH_DOSPATH
1300         switch (st) {
1301         case V_PATH:
1302         case V_TMPDIR:
1303 #ifdef __OS2__
1304         case V_BEGINLIBPATH:
1305         case V_ENDLIBPATH:
1306 #endif
1307                 /* convert backslashes to slashes for convenience */
1308                 if (!(vp->flag&INTEGER)) {
1309                         s = str_val(vp);
1310                         do {
1311                                 if (*s == ORD('\\'))
1312                                         *s = '/';
1313                         } while (*s++);
1314                 }
1315                 break;
1316         }
1317 #endif
1318
1319         switch (st) {
1320 #ifdef __OS2__
1321         case V_BEGINLIBPATH:
1322         case V_ENDLIBPATH:
1323         case V_LIBPATHSTRICT:
1324                 setextlibpath(vp->name, str_val(vp));
1325                 return;
1326 #endif
1327 #if HAVE_PERSISTENT_HISTORY
1328         case V_HISTFILE:
1329                 sethistfile(str_val(vp));
1330                 return;
1331 #endif
1332         case V_IFS:
1333                 set_ifs(str_val(vp));
1334                 return;
1335         case V_PATH:
1336                 afree(path, APERM);
1337                 s = str_val(vp);
1338                 strdupx(path, s, APERM);
1339                 /* clear tracked aliases */
1340                 flushcom(true);
1341                 return;
1342 #ifndef MKSH_NO_CMDLINE_EDITING
1343         case V_TERM:
1344                 x_initterm(str_val(vp));
1345                 return;
1346 #endif
1347         case V_TMPDIR:
1348                 afree(tmpdir, APERM);
1349                 tmpdir = NULL;
1350                 /*
1351                  * Use tmpdir iff it is an absolute path, is writable
1352                  * and searchable and is a directory...
1353                  */
1354                 {
1355                         struct stat statb;
1356
1357                         s = str_val(vp);
1358                         /* LINTED use of access */
1359                         if (mksh_abspath(s) && access(s, W_OK|X_OK) == 0 &&
1360                             stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
1361                                 strdupx(tmpdir, s, APERM);
1362                 }
1363                 return;
1364         /* common sub-cases */
1365         case V_COLUMNS:
1366         case V_LINES:
1367                 if (vp->flag & IMPORT) {
1368                         /* do not touch */
1369                         unspecial(vp->name);
1370                         vp->flag &= ~SPECIAL;
1371                         return;
1372                 }
1373                 /* FALLTHROUGH */
1374         case V_HISTSIZE:
1375         case V_LINENO:
1376         case V_OPTIND:
1377         case V_RANDOM:
1378         case V_SECONDS:
1379         case V_TMOUT:
1380                 vp->flag &= ~SPECIAL;
1381                 if (getint(vp, &num, false) == -1) {
1382                         s = str_val(vp);
1383                         if (st != V_RANDOM)
1384                                 errorf(Tf_sD_sD_s, vp->name, Tbadnum, s);
1385                         num.u = hash(s);
1386                 }
1387                 vp->flag |= SPECIAL;
1388                 break;
1389 #ifdef MKSH_EARLY_LOCALE_TRACKING
1390         case V_LANG:
1391         case V_LC_ALL:
1392         case V_LC_CTYPE:
1393                 recheck_ctype();
1394                 return;
1395 #endif
1396         default:
1397                 /* do nothing, do not touch vp at all */
1398                 return;
1399         }
1400
1401         /* process the singular parts of the common cases */
1402
1403         switch (st) {
1404         case V_COLUMNS:
1405                 if (num.i >= MIN_COLS)
1406                         x_cols = num.i;
1407                 break;
1408         case V_HISTSIZE:
1409                 sethistsize(num.i);
1410                 break;
1411         case V_LINENO:
1412                 /* The -1 is because line numbering starts at 1. */
1413                 user_lineno = num.u - (mksh_uari_t)current_lineno - 1;
1414                 break;
1415         case V_LINES:
1416                 if (num.i >= MIN_LINS)
1417                         x_lins = num.i;
1418                 break;
1419         case V_OPTIND:
1420                 getopts_reset((int)num.i);
1421                 break;
1422         case V_RANDOM:
1423                 /*
1424                  * mksh R39d+ no longer has the traditional repeatability
1425                  * of $RANDOM sequences, but always retains state
1426                  */
1427                 rndset((unsigned long)num.u);
1428                 break;
1429         case V_SECONDS:
1430                 {
1431                         struct timeval tv;
1432
1433                         mksh_TIME(tv);
1434                         seconds = tv.tv_sec - num.i;
1435                 }
1436                 break;
1437         case V_TMOUT:
1438                 ksh_tmout = num.i >= 0 ? num.i : 0;
1439                 break;
1440         }
1441 }
1442
1443 static void
1444 unsetspec(struct tbl *vp, bool dounset)
1445 {
1446         /*
1447          * AT&T ksh man page says OPTIND, OPTARG and _ lose special
1448          * meaning, but OPTARG does not (still set by getopts) and _ is
1449          * also still set in various places. Don't know what AT&T does
1450          * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
1451          * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
1452          */
1453
1454         switch (special(vp->name)) {
1455 #ifdef __OS2__
1456         case V_BEGINLIBPATH:
1457         case V_ENDLIBPATH:
1458         case V_LIBPATHSTRICT:
1459                 setextlibpath(vp->name, "");
1460                 return;
1461 #endif
1462 #if HAVE_PERSISTENT_HISTORY
1463         case V_HISTFILE:
1464                 sethistfile(NULL);
1465                 return;
1466 #endif
1467         case V_IFS:
1468                 set_ifs(TC_IFSWS);
1469                 return;
1470         case V_PATH:
1471                 afree(path, APERM);
1472                 strdupx(path, def_path, APERM);
1473                 /* clear tracked aliases */
1474                 flushcom(true);
1475                 return;
1476 #ifndef MKSH_NO_CMDLINE_EDITING
1477         case V_TERM:
1478                 x_initterm(null);
1479                 return;
1480 #endif
1481         case V_TMPDIR:
1482                 /* should not become unspecial */
1483                 if (tmpdir) {
1484                         afree(tmpdir, APERM);
1485                         tmpdir = NULL;
1486                 }
1487                 return;
1488         case V_LINENO:
1489         case V_RANDOM:
1490         case V_SECONDS:
1491         case V_TMOUT:
1492                 /* AT&T ksh leaves previous value in place */
1493                 unspecial(vp->name);
1494                 return;
1495 #ifdef MKSH_EARLY_LOCALE_TRACKING
1496         case V_LANG:
1497         case V_LC_ALL:
1498         case V_LC_CTYPE:
1499                 recheck_ctype();
1500                 return;
1501 #endif
1502         /* should not become unspecial, but allow unsetting */
1503         case V_COLUMNS:
1504         case V_LINES:
1505                 if (dounset)
1506                         unspecial(vp->name);
1507                 return;
1508         }
1509 }
1510
1511 /*
1512  * Search for (and possibly create) a table entry starting with
1513  * vp, indexed by val.
1514  */
1515 struct tbl *
1516 arraysearch(struct tbl *vp, uint32_t val)
1517 {
1518         struct tbl *prev, *curr, *news;
1519         size_t len;
1520
1521         vp->flag = (vp->flag | (ARRAY | DEFINED)) & ~ASSOC;
1522         /* the table entry is always [0] */
1523         if (val == 0)
1524                 return (vp);
1525         prev = vp;
1526         curr = vp->u.array;
1527         while (curr && curr->ua.index < val) {
1528                 prev = curr;
1529                 curr = curr->u.array;
1530         }
1531         if (curr && curr->ua.index == val) {
1532                 if (curr->flag&ISSET)
1533                         return (curr);
1534                 news = curr;
1535         } else
1536                 news = NULL;
1537         if (!news) {
1538                 len = strlen(vp->name);
1539                 checkoktoadd(len, 1 + offsetof(struct tbl, name[0]));
1540                 news = alloc(offsetof(struct tbl, name[0]) + ++len, vp->areap);
1541                 memcpy(news->name, vp->name, len);
1542         }
1543         news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
1544         news->type = vp->type;
1545         news->areap = vp->areap;
1546         news->u2.field = vp->u2.field;
1547         news->ua.index = val;
1548
1549         if (curr != news) {
1550                 /* not reusing old array entry */
1551                 prev->u.array = news;
1552                 news->u.array = curr;
1553         }
1554         return (news);
1555 }
1556
1557 /*
1558  * Return the length of an array reference (eg, [1+2]) - cp is assumed
1559  * to point to the open bracket. Returns 0 if there is no matching
1560  * closing bracket.
1561  *
1562  * XXX this should parse the actual arithmetic syntax
1563  */
1564 size_t
1565 array_ref_len(const char *cp)
1566 {
1567         const char *s = cp;
1568         char c;
1569         int depth = 0;
1570
1571         while ((c = *s++) && (ord(c) != ORD(']') || --depth))
1572                 if (ord(c) == ORD('['))
1573                         depth++;
1574         if (!c)
1575                 return (0);
1576         return (s - cp);
1577 }
1578
1579 /*
1580  * Make a copy of the base of an array name
1581  */
1582 char *
1583 arrayname(const char *str)
1584 {
1585         const char *p;
1586         char *rv;
1587
1588         if (!(p = cstrchr(str, '[')))
1589                 /* Shouldn't happen, but why worry? */
1590                 strdupx(rv, str, ATEMP);
1591         else
1592                 strndupx(rv, str, p - str, ATEMP);
1593
1594         return (rv);
1595 }
1596
1597 /* set (or overwrite, if reset) the array variable var to the values in vals */
1598 mksh_uari_t
1599 set_array(const char *var, bool reset, const char **vals)
1600 {
1601         struct tbl *vp, *vq;
1602         mksh_uari_t i = 0, j = 0;
1603         const char *ccp = var;
1604         char *cp = NULL;
1605         size_t n;
1606
1607         /* to get local array, use "local foo; set -A foo" */
1608         n = strlen(var);
1609         if (n > 0 && var[n - 1] == '+') {
1610                 /* append mode */
1611                 reset = false;
1612                 strndupx(cp, var, n - 1, ATEMP);
1613                 ccp = cp;
1614         }
1615         vp = global(ccp);
1616
1617         /* Note: AT&T ksh allows set -A but not set +A of a read-only var */
1618         if ((vp->flag&RDONLY))
1619                 errorfx(2, Tf_ro, ccp);
1620         /* This code is quite non-optimal */
1621         if (reset) {
1622                 /* trash existing values and attributes */
1623                 unset(vp, 1);
1624                 /* allocate-by-access the [0] element to keep in scope */
1625                 arraysearch(vp, 0);
1626                 /* honour set -o allexport */
1627                 if (Flag(FEXPORT))
1628                         typeset(ccp, EXPORT, 0, 0, 0);
1629         }
1630         /*
1631          * TODO: would be nice for assignment to completely succeed or
1632          * completely fail. Only really effects integer arrays:
1633          * evaluation of some of vals[] may fail...
1634          */
1635         if (cp != NULL) {
1636                 /* find out where to set when appending */
1637                 for (vq = vp; vq; vq = vq->u.array) {
1638                         if (!(vq->flag & ISSET))
1639                                 continue;
1640                         if (arrayindex(vq) >= j)
1641                                 j = arrayindex(vq) + 1;
1642                 }
1643                 afree(cp, ATEMP);
1644         }
1645         while ((ccp = vals[i])) {
1646 #if 0 /* temporarily taken out due to regression */
1647                 if (ord(*ccp) == ORD('[')) {
1648                         int level = 0;
1649
1650                         while (*ccp) {
1651                                 if (ord(*ccp) == ORD(']') && --level == 0)
1652                                         break;
1653                                 if (ord(*ccp) == ORD('['))
1654                                         ++level;
1655                                 ++ccp;
1656                         }
1657                         if (ord(*ccp) == ORD(']') && level == 0 &&
1658                             ord(ccp[1]) == ORD('=')) {
1659                                 strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
1660                                     ATEMP);
1661                                 evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
1662                                     KSH_UNWIND_ERROR, true);
1663                                 afree(cp, ATEMP);
1664                                 ccp += 2;
1665                         } else
1666                                 ccp = vals[i];
1667                 }
1668 #endif
1669
1670                 vq = arraysearch(vp, j);
1671                 /* would be nice to deal with errors here... (see above) */
1672                 setstr(vq, ccp, KSH_RETURN_ERROR);
1673                 i++;
1674                 j++;
1675         }
1676
1677         return (i);
1678 }
1679
1680 void
1681 change_winsz(void)
1682 {
1683         struct timeval tv;
1684
1685         mksh_TIME(tv);
1686         BAFHUpdateMem_mem(qh_state, &tv, sizeof(tv));
1687
1688 #ifdef TIOCGWINSZ
1689         /* check if window size has changed */
1690         if (tty_init_fd() < 2) {
1691                 struct winsize ws;
1692
1693                 if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
1694                         if (ws.ws_col)
1695                                 x_cols = ws.ws_col;
1696                         if (ws.ws_row)
1697                                 x_lins = ws.ws_row;
1698                 }
1699         }
1700 #endif
1701
1702         /* bounds check for sane values, use defaults otherwise */
1703         if (x_cols < MIN_COLS)
1704                 x_cols = 80;
1705         if (x_lins < MIN_LINS)
1706                 x_lins = 24;
1707
1708 #ifdef SIGWINCH
1709         got_winch = 0;
1710 #endif
1711 }
1712
1713 uint32_t
1714 hash(const void *s)
1715 {
1716         register uint32_t h;
1717
1718         BAFHInit(h);
1719         BAFHUpdateStr_reg(h, s);
1720         BAFHFinish_reg(h);
1721         return (h);
1722 }
1723
1724 uint32_t
1725 chvt_rndsetup(const void *bp, size_t sz)
1726 {
1727         register uint32_t h;
1728
1729         /* use LCG as seed but try to get them to deviate immediately */
1730         h = lcg_state;
1731         (void)rndget();
1732         BAFHFinish_reg(h);
1733         /* variation through pid, ppid, and the works */
1734         BAFHUpdateMem_reg(h, &rndsetupstate, sizeof(rndsetupstate));
1735         /* some variation, some possibly entropy, depending on OE */
1736         BAFHUpdateMem_reg(h, bp, sz);
1737         /* mix them all up */
1738         BAFHFinish_reg(h);
1739
1740         return (h);
1741 }
1742
1743 mksh_ari_t
1744 rndget(void)
1745 {
1746         /*
1747          * this is the same Linear Congruential PRNG as Borland
1748          * C/C++ allegedly uses in its built-in rand() function
1749          */
1750         return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
1751 }
1752
1753 void
1754 rndset(unsigned long v)
1755 {
1756         register uint32_t h;
1757 #if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
1758         register uint32_t t;
1759 #endif
1760         struct {
1761                 struct timeval tv;
1762                 void *sp;
1763                 uint32_t qh;
1764                 pid_t pp;
1765                 short r;
1766         } z;
1767
1768         /* clear the allocated space, for valgrind and to avoid UB */
1769         memset(&z, 0, sizeof(z));
1770
1771         h = lcg_state;
1772         BAFHFinish_reg(h);
1773         BAFHUpdateMem_reg(h, &v, sizeof(v));
1774
1775         mksh_TIME(z.tv);
1776         z.sp = &lcg_state;
1777         z.pp = procpid;
1778         z.r = (short)rndget();
1779
1780 #if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
1781         t = qh_state;
1782         BAFHFinish_reg(t);
1783         z.qh = (t & 0xFFFF8000) | rndget();
1784         lcg_state = (t << 15) | rndget();
1785         /*
1786          * either we have very chap entropy get and push available,
1787          * with malloc() pulling in this code already anyway, or the
1788          * user requested us to use the old functions
1789          */
1790         t = h;
1791         BAFHUpdateMem_reg(t, &lcg_state, sizeof(lcg_state));
1792         BAFHFinish_reg(t);
1793         lcg_state = t;
1794 #if defined(arc4random_pushb_fast)
1795         arc4random_pushb_fast(&lcg_state, sizeof(lcg_state));
1796         lcg_state = arc4random();
1797 #else
1798         lcg_state = arc4random_pushb(&lcg_state, sizeof(lcg_state));
1799 #endif
1800         BAFHUpdateMem_reg(h, &lcg_state, sizeof(lcg_state));
1801 #else
1802         z.qh = qh_state;
1803 #endif
1804
1805         BAFHUpdateMem_reg(h, &z, sizeof(z));
1806         BAFHFinish_reg(h);
1807         lcg_state = h;
1808 }
1809
1810 void
1811 rndpush(const void *s)
1812 {
1813         register uint32_t h = qh_state;
1814
1815         BAFHUpdateStr_reg(h, s);
1816         BAFHUpdateOctet_reg(h, 0);
1817         qh_state = h;
1818 }
1819
1820 /* record last glob match */
1821 void
1822 record_match(const char *istr)
1823 {
1824         struct tbl *vp;
1825
1826         vp = local("KSH_MATCH", false);
1827         unset(vp, 1);
1828         vp->flag = DEFINED | RDONLY;
1829         setstr(vp, istr, 0x4);
1830 }
1831
1832 /* typeset, global(deprecated), export, and readonly */
1833 int
1834 c_typeset(const char **wp)
1835 {
1836         struct tbl *vp, **p;
1837         uint32_t fset = 0, fclr = 0, flag;
1838         int thing = 0, field = 0, base = 0, i;
1839         struct block *l;
1840         const char *opts;
1841         const char *fieldstr = NULL, *basestr = NULL;
1842         bool localv = false, func = false, pflag = false, istset = true;
1843         enum namerefflag new_refflag = SRF_NOP;
1844
1845         switch (**wp) {
1846
1847         /* export */
1848         case 'e':
1849                 fset |= EXPORT;
1850                 istset = false;
1851                 break;
1852
1853         /* readonly */
1854         case 'r':
1855                 fset |= RDONLY;
1856                 istset = false;
1857                 break;
1858
1859         /* set */
1860         case 's':
1861                 /* called with 'typeset -' */
1862                 break;
1863
1864         /* typeset */
1865         case 't':
1866                 localv = true;
1867                 break;
1868         }
1869
1870         /* see comment below regarding possible opions */
1871         opts = istset ? "L#R#UZ#afgi#lnprtux" : "p";
1872
1873         builtin_opt.flags |= GF_PLUSOPT;
1874         /*
1875          * AT&T ksh seems to have 0-9 as options which are multiplied
1876          * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
1877          * sets right justify in a field of 12). This allows options
1878          * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
1879          * does not allow the number to be specified as a separate argument
1880          * Here, the number must follow the RLZi option, but is optional
1881          * (see the # kludge in ksh_getopt()).
1882          */
1883         while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
1884                 flag = 0;
1885                 switch (i) {
1886                 case 'L':
1887                         flag = LJUST;
1888                         fieldstr = builtin_opt.optarg;
1889                         break;
1890                 case 'R':
1891                         flag = RJUST;
1892                         fieldstr = builtin_opt.optarg;
1893                         break;
1894                 case 'U':
1895                         /*
1896                          * AT&T ksh uses u, but this conflicts with
1897                          * upper/lower case. If this option is changed,
1898                          * need to change the -U below as well
1899                          */
1900                         flag = INT_U;
1901                         break;
1902                 case 'Z':
1903                         flag = ZEROFIL;
1904                         fieldstr = builtin_opt.optarg;
1905                         break;
1906                 case 'a':
1907                         /*
1908                          * this is supposed to set (-a) or unset (+a) the
1909                          * indexed array attribute; it does nothing on an
1910                          * existing regular string or indexed array though
1911                          */
1912                         break;
1913                 case 'f':
1914                         func = true;
1915                         break;
1916                 case 'g':
1917                         localv = (builtin_opt.info & GI_PLUS) ? true : false;
1918                         break;
1919                 case 'i':
1920                         flag = INTEGER;
1921                         basestr = builtin_opt.optarg;
1922                         break;
1923                 case 'l':
1924                         flag = LCASEV;
1925                         break;
1926                 case 'n':
1927                         new_refflag = (builtin_opt.info & GI_PLUS) ?
1928                             SRF_DISABLE : SRF_ENABLE;
1929                         break;
1930                 /* export, readonly: POSIX -p flag */
1931                 case 'p':
1932                         /* typeset: show values as well */
1933                         pflag = true;
1934                         if (istset)
1935                                 continue;
1936                         break;
1937                 case 'r':
1938                         flag = RDONLY;
1939                         break;
1940                 case 't':
1941                         flag = TRACE;
1942                         break;
1943                 case 'u':
1944                         /* upper case / autoload */
1945                         flag = UCASEV_AL;
1946                         break;
1947                 case 'x':
1948                         flag = EXPORT;
1949                         break;
1950                 case '?':
1951                         return (1);
1952                 }
1953                 if (builtin_opt.info & GI_PLUS) {
1954                         fclr |= flag;
1955                         fset &= ~flag;
1956                         thing = '+';
1957                 } else {
1958                         fset |= flag;
1959                         fclr &= ~flag;
1960                         thing = '-';
1961                 }
1962         }
1963
1964         if (fieldstr && !getn(fieldstr, &field)) {
1965                 bi_errorf(Tf_sD_s, Tbadnum, fieldstr);
1966                 return (1);
1967         }
1968         if (basestr) {
1969                 if (!getn(basestr, &base)) {
1970                         bi_errorf(Tf_sD_s, "bad integer base", basestr);
1971                         return (1);
1972                 }
1973                 if (base < 1 || base > 36)
1974                         base = 10;
1975         }
1976
1977         if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
1978             (wp[builtin_opt.optind][0] == '-' ||
1979             wp[builtin_opt.optind][0] == '+') &&
1980             wp[builtin_opt.optind][1] == '\0') {
1981                 thing = wp[builtin_opt.optind][0];
1982                 builtin_opt.optind++;
1983         }
1984
1985         if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
1986             new_refflag != SRF_NOP)) {
1987                 bi_errorf("only -t, -u and -x options may be used with -f");
1988                 return (1);
1989         }
1990         if (wp[builtin_opt.optind]) {
1991                 /*
1992                  * Take care of exclusions.
1993                  * At this point, flags in fset are cleared in fclr and vice
1994                  * versa. This property should be preserved.
1995                  */
1996                 if (fset & LCASEV)
1997                         /* LCASEV has priority over UCASEV_AL */
1998                         fset &= ~UCASEV_AL;
1999                 if (fset & LJUST)
2000                         /* LJUST has priority over RJUST */
2001                         fset &= ~RJUST;
2002                 if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
2003                         /* -Z implies -ZR */
2004                         fset |= RJUST;
2005                         fclr &= ~RJUST;
2006                 }
2007                 /*
2008                  * Setting these attributes clears the others, unless they
2009                  * are also set in this command
2010                  */
2011                 if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
2012                     INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
2013                         fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
2014                             LCASEV | INTEGER | INT_U | INT_L);
2015         }
2016         if (new_refflag != SRF_NOP) {
2017                 fclr &= ~(ARRAY | ASSOC);
2018                 fset &= ~(ARRAY | ASSOC);
2019                 fclr |= EXPORT;
2020                 fset |= ASSOC;
2021                 if (new_refflag == SRF_DISABLE)
2022                         fclr |= ASSOC;
2023         }
2024
2025         /* set variables and attributes */
2026         if (wp[builtin_opt.optind] &&
2027             /* not "typeset -p varname" */
2028             !(!func && pflag && !(fset | fclr))) {
2029                 int rv = 0;
2030                 struct tbl *f;
2031
2032                 if (localv && !func)
2033                         fset |= LOCAL;
2034                 for (i = builtin_opt.optind; wp[i]; i++) {
2035                         if (func) {
2036                                 f = findfunc(wp[i], hash(wp[i]),
2037                                     tobool(fset & UCASEV_AL));
2038                                 if (!f) {
2039                                         /* AT&T ksh does ++rv: bogus */
2040                                         rv = 1;
2041                                         continue;
2042                                 }
2043                                 if (fset | fclr) {
2044                                         f->flag |= fset;
2045                                         f->flag &= ~fclr;
2046                                 } else {
2047                                         fpFUNCTf(shl_stdout, 0,
2048                                             tobool(f->flag & FKSH),
2049                                             wp[i], f->val.t);
2050                                         shf_putc('\n', shl_stdout);
2051                                 }
2052                         } else if (!typeset(wp[i], fset, fclr, field, base)) {
2053                                 bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
2054                                 return (1);
2055                         }
2056                 }
2057                 return (rv);
2058         }
2059
2060         /* list variables and attributes */
2061
2062         /* no difference at this point.. */
2063         flag = fset | fclr;
2064         if (func) {
2065                 for (l = e->loc; l; l = l->next) {
2066                         for (p = ktsort(&l->funs); (vp = *p++); ) {
2067                                 if (flag && (vp->flag & flag) == 0)
2068                                         continue;
2069                                 if (thing == '-')
2070                                         fpFUNCTf(shl_stdout, 0,
2071                                             tobool(vp->flag & FKSH),
2072                                             vp->name, vp->val.t);
2073                                 else
2074                                         shf_puts(vp->name, shl_stdout);
2075                                 shf_putc('\n', shl_stdout);
2076                         }
2077                 }
2078         } else if (wp[builtin_opt.optind]) {
2079                 for (i = builtin_opt.optind; wp[i]; i++) {
2080                         vp = isglobal(wp[i], false);
2081                         c_typeset_vardump(vp, flag, thing,
2082                             last_lookup_was_array ? 4 : 0, pflag, istset);
2083                 }
2084         } else
2085                 c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
2086         return (0);
2087 }
2088
2089 static void
2090 c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
2091     bool pflag, bool istset)
2092 {
2093         struct tbl **blockvars, *vp;
2094
2095         if (l->next)
2096                 c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
2097         blockvars = ktsort(&l->vars);
2098         while ((vp = *blockvars++))
2099                 c_typeset_vardump(vp, flag, thing, 0, pflag, istset);
2100         /*XXX doesn’t this leak? */
2101 }
2102
2103 static void
2104 c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set,
2105     bool pflag, bool istset)
2106 {
2107         struct tbl *tvp;
2108         char *s;
2109
2110         if (!vp)
2111                 return;
2112
2113         /*
2114          * See if the parameter is set (for arrays, if any
2115          * element is set).
2116          */
2117         for (tvp = vp; tvp; tvp = tvp->u.array)
2118                 if (tvp->flag & ISSET) {
2119                         any_set |= 1;
2120                         break;
2121                 }
2122
2123         /*
2124          * Check attributes - note that all array elements
2125          * have (should have?) the same attributes, so checking
2126          * the first is sufficient.
2127          *
2128          * Report an unset param only if the user has
2129          * explicitly given it some attribute (like export);
2130          * otherwise, after "echo $FOO", we would report FOO...
2131          */
2132         if (!any_set && !(vp->flag & USERATTRIB))
2133                 return;
2134         if (flag && (vp->flag & flag) == 0)
2135                 return;
2136         if (!(vp->flag & ARRAY))
2137                 /* optimise later conditionals */
2138                 any_set = 0;
2139         do {
2140                 /*
2141                  * Ignore array elements that aren't set unless there
2142                  * are no set elements, in which case the first is
2143                  * reported on
2144                  */
2145                 if (any_set && !(vp->flag & ISSET))
2146                         continue;
2147                 /* no arguments */
2148                 if (!thing && !flag) {
2149                         if (any_set == 1) {
2150                                 shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
2151                                 any_set = 2;
2152                         }
2153                         /*
2154                          * AT&T ksh prints things like export, integer,
2155                          * leftadj, zerofill, etc., but POSIX says must
2156                          * be suitable for re-entry...
2157                          */
2158                         shprintf(Tf_s_s, Ttypeset, "");
2159                         if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
2160                                 shprintf(Tf__c_, 'n');
2161                         if ((vp->flag & INTEGER))
2162                                 shprintf(Tf__c_, 'i');
2163                         if ((vp->flag & EXPORT))
2164                                 shprintf(Tf__c_, 'x');
2165                         if ((vp->flag & RDONLY))
2166                                 shprintf(Tf__c_, 'r');
2167                         if ((vp->flag & TRACE))
2168                                 shprintf(Tf__c_, 't');
2169                         if ((vp->flag & LJUST))
2170                                 shprintf("-L%d ", vp->u2.field);
2171                         if ((vp->flag & RJUST))
2172                                 shprintf("-R%d ", vp->u2.field);
2173                         if ((vp->flag & ZEROFIL))
2174                                 shprintf(Tf__c_, 'Z');
2175                         if ((vp->flag & LCASEV))
2176                                 shprintf(Tf__c_, 'l');
2177                         if ((vp->flag & UCASEV_AL))
2178                                 shprintf(Tf__c_, 'u');
2179                         if ((vp->flag & INT_U))
2180                                 shprintf(Tf__c_, 'U');
2181                 } else if (pflag) {
2182                         shprintf(Tf_s_s, istset ? Ttypeset :
2183                             (flag & EXPORT) ? Texport : Treadonly, "");
2184                 }
2185                 if (any_set)
2186                         shprintf("%s[%lu]", vp->name, arrayindex(vp));
2187                 else
2188                         shf_puts(vp->name, shl_stdout);
2189                 if ((!thing && !flag && pflag) ||
2190                     (thing == '-' && (vp->flag & ISSET))) {
2191                         s = str_val(vp);
2192                         shf_putc('=', shl_stdout);
2193                         /* AT&T ksh can't have justified integers... */
2194                         if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
2195                                 shf_puts(s, shl_stdout);
2196                         else
2197                                 print_value_quoted(shl_stdout, s);
2198                 }
2199                 shf_putc('\n', shl_stdout);
2200
2201                 /*
2202                  * Only report first 'element' of an array with
2203                  * no set elements.
2204                  */
2205                 if (!any_set)
2206                         return;
2207         } while (!(any_set & 4) && (vp = vp->u.array));
2208 }