OSDN Git Service

Merge "Upgrade to mksh R52b."
[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
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.197 2016/01/14 22:49:33 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 char *formatstr(struct tbl *, const char *);
49 static void exportprep(struct tbl *, const char *);
50 static int special(const char *);
51 static void unspecial(const char *);
52 static void getspec(struct tbl *);
53 static void setspec(struct tbl *);
54 static void unsetspec(struct tbl *);
55 static int getint(struct tbl *, mksh_ari_u *, bool);
56 static const char *array_index_calc(const char *, bool *, uint32_t *);
57
58 /*
59  * create a new block for function calls and simple commands
60  * assume caller has allocated and set up e->loc
61  */
62 void
63 newblock(void)
64 {
65         struct block *l;
66         static const char *empty[] = { null };
67
68         l = alloc(sizeof(struct block), ATEMP);
69         l->flags = 0;
70         /* TODO: could use e->area (l->area => l->areap) */
71         ainit(&l->area);
72         if (!e->loc) {
73                 l->argc = 0;
74                 l->argv = empty;
75         } else {
76                 l->argc = e->loc->argc;
77                 l->argv = e->loc->argv;
78         }
79         l->exit = l->error = NULL;
80         ktinit(&l->area, &l->vars, 0);
81         ktinit(&l->area, &l->funs, 0);
82         l->next = e->loc;
83         e->loc = l;
84 }
85
86 /*
87  * pop a block handling special variables
88  */
89 void
90 popblock(void)
91 {
92         ssize_t i;
93         struct block *l = e->loc;
94         struct tbl *vp, **vpp = l->vars.tbls, *vq;
95
96         /* pop block */
97         e->loc = l->next;
98
99         i = 1 << (l->vars.tshift);
100         while (--i >= 0)
101                 if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
102                         if ((vq = global(vp->name))->flag & ISSET)
103                                 setspec(vq);
104                         else
105                                 unsetspec(vq);
106                 }
107         if (l->flags & BF_DOGETOPTS)
108                 user_opt = l->getopts_state;
109         afreeall(&l->area);
110         afree(l, ATEMP);
111 }
112
113 /* called by main() to initialise variable data structures */
114 #define VARSPEC_DEFNS
115 #include "var_spec.h"
116
117 enum var_specs {
118 #define VARSPEC_ENUMS
119 #include "var_spec.h"
120         V_MAX
121 };
122
123 /* this is biased with -1 relative to VARSPEC_ENUMS */
124 static const char * const initvar_names[] = {
125 #define VARSPEC_ITEMS
126 #include "var_spec.h"
127 };
128
129 void
130 initvar(void)
131 {
132         int i = 0;
133         struct tbl *tp;
134
135         ktinit(APERM, &specials,
136             /* currently 14 specials: 75% of 32 = 2^5 */
137             5);
138         while (i < V_MAX - 1) {
139                 tp = ktenter(&specials, initvar_names[i],
140                     hash(initvar_names[i]));
141                 tp->flag = DEFINED|ISSET;
142                 tp->type = ++i;
143         }
144 }
145
146 /* common code for several functions below and c_typeset() */
147 struct block *
148 varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
149 {
150         register struct tbl *vp;
151
152         if (l) {
153  varsearch_loop:
154                 if ((vp = ktsearch(&l->vars, vn, h)) != NULL)
155                         goto varsearch_out;
156                 if (l->next != NULL) {
157                         l = l->next;
158                         goto varsearch_loop;
159                 }
160         }
161         vp = NULL;
162  varsearch_out:
163         *vpp = vp;
164         return (l);
165 }
166
167 /*
168  * Used to calculate an array index for global()/local(). Sets *arrayp
169  * to true if this is an array, sets *valp to the array index, returns
170  * the basename of the array. May only be called from global()/local()
171  * and must be their first callee.
172  */
173 static const char *
174 array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
175 {
176         const char *p;
177         size_t len;
178         char *ap = NULL;
179
180         *arrayp = false;
181  redo_from_ref:
182         p = skip_varname(n, false);
183         if (innermost_refflag == SRF_NOP && (p != n) && ksh_isalphx(n[0])) {
184                 struct tbl *vp;
185                 char *vn;
186
187                 strndupx(vn, n, p - n, ATEMP);
188                 /* check if this is a reference */
189                 varsearch(e->loc, &vp, vn, hash(vn));
190                 afree(vn, ATEMP);
191                 if (vp && (vp->flag & (DEFINED | ASSOC | ARRAY)) ==
192                     (DEFINED | ASSOC)) {
193                         char *cp;
194
195                         /* gotcha! */
196                         cp = shf_smprintf("%s%s", str_val(vp), p);
197                         afree(ap, ATEMP);
198                         n = ap = cp;
199                         goto redo_from_ref;
200                 }
201         }
202         innermost_refflag = SRF_NOP;
203
204         if (p != n && *p == '[' && (len = array_ref_len(p))) {
205                 char *sub, *tmp;
206                 mksh_ari_t rval;
207
208                 /* calculate the value of the subscript */
209                 *arrayp = true;
210                 strndupx(tmp, p + 1, len - 2, ATEMP);
211                 sub = substitute(tmp, 0);
212                 afree(tmp, ATEMP);
213                 strndupx(n, n, p - n, ATEMP);
214                 evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
215                 *valp = (uint32_t)rval;
216                 afree(sub, ATEMP);
217         }
218         return (n);
219 }
220
221 #define vn vname.ro
222 /*
223  * Search for variable, if not found create globally.
224  */
225 struct tbl *
226 global(const char *n)
227 {
228         struct tbl *vp;
229         union mksh_cchack vname;
230         struct block *l = e->loc;
231         int c;
232         bool array;
233         uint32_t h, val;
234
235         /*
236          * check to see if this is an array;
237          * dereference namerefs; must come first
238          */
239         vn = array_index_calc(n, &array, &val);
240         h = hash(vn);
241         c = (unsigned char)vn[0];
242         if (!ksh_isalphx(c)) {
243                 if (array)
244                         errorf("bad substitution");
245                 vp = &vtemp;
246                 vp->flag = DEFINED;
247                 vp->type = 0;
248                 vp->areap = ATEMP;
249                 *vp->name = c;
250                 if (ksh_isdigit(c)) {
251                         if (getn(vn, &c) && (c <= l->argc))
252                                 /* setstr can't fail here */
253                                 setstr(vp, l->argv[c], KSH_RETURN_ERROR);
254                         vp->flag |= RDONLY;
255                         goto out;
256                 }
257                 vp->flag |= RDONLY;
258                 if (vn[1] != '\0')
259                         goto out;
260                 vp->flag |= ISSET|INTEGER;
261                 switch (c) {
262                 case '$':
263                         vp->val.i = kshpid;
264                         break;
265                 case '!':
266                         /* if no job, expand to nothing */
267                         if ((vp->val.i = j_async()) == 0)
268                                 vp->flag &= ~(ISSET|INTEGER);
269                         break;
270                 case '?':
271                         vp->val.i = exstat & 0xFF;
272                         break;
273                 case '#':
274                         vp->val.i = l->argc;
275                         break;
276                 case '-':
277                         vp->flag &= ~INTEGER;
278                         vp->val.s = getoptions();
279                         break;
280                 default:
281                         vp->flag &= ~(ISSET|INTEGER);
282                 }
283                 goto out;
284         }
285         l = varsearch(e->loc, &vp, vn, h);
286         if (vp != NULL) {
287                 if (array)
288                         vp = arraysearch(vp, val);
289                 goto out;
290         }
291         vp = ktenter(&l->vars, vn, h);
292         if (array)
293                 vp = arraysearch(vp, val);
294         vp->flag |= DEFINED;
295         if (special(vn))
296                 vp->flag |= SPECIAL;
297  out:
298         last_lookup_was_array = array;
299         if (vn != n)
300                 afree(vname.rw, ATEMP);
301         return (vp);
302 }
303
304 /*
305  * Search for local variable, if not found create locally.
306  */
307 struct tbl *
308 local(const char *n, bool copy)
309 {
310         struct tbl *vp;
311         union mksh_cchack vname;
312         struct block *l = e->loc;
313         bool array;
314         uint32_t h, val;
315
316         /*
317          * check to see if this is an array;
318          * dereference namerefs; must come first
319          */
320         vn = array_index_calc(n, &array, &val);
321         h = hash(vn);
322         if (!ksh_isalphx(*vn)) {
323                 vp = &vtemp;
324                 vp->flag = DEFINED|RDONLY;
325                 vp->type = 0;
326                 vp->areap = ATEMP;
327                 goto out;
328         }
329         vp = ktenter(&l->vars, vn, h);
330         if (copy && !(vp->flag & DEFINED)) {
331                 struct tbl *vq;
332
333                 varsearch(l->next, &vq, vn, h);
334                 if (vq != NULL) {
335                         vp->flag |= vq->flag &
336                             (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
337                             ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
338                         if (vq->flag & INTEGER)
339                                 vp->type = vq->type;
340                         vp->u2.field = vq->u2.field;
341                 }
342         }
343         if (array)
344                 vp = arraysearch(vp, val);
345         vp->flag |= DEFINED;
346         if (special(vn))
347                 vp->flag |= SPECIAL;
348  out:
349         last_lookup_was_array = array;
350         if (vn != n)
351                 afree(vname.rw, ATEMP);
352         return (vp);
353 }
354 #undef vn
355
356 /* get variable string value */
357 char *
358 str_val(struct tbl *vp)
359 {
360         char *s;
361
362         if ((vp->flag&SPECIAL))
363                 getspec(vp);
364         if (!(vp->flag&ISSET))
365                 /* special to dollar() */
366                 s = null;
367         else if (!(vp->flag&INTEGER))
368                 /* string source */
369                 s = vp->val.s + vp->type;
370         else {
371                 /* integer source */
372                 mksh_uari_t n;
373                 unsigned int base;
374                 /**
375                  * worst case number length is when base == 2:
376                  *      1 (minus) + 2 (base, up to 36) + 1 ('#') +
377                  *      number of bits in the mksh_uari_t + 1 (NUL)
378                  */
379                 char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1];
380                 const char *digits = (vp->flag & UCASEV_AL) ?
381                     digits_uc : digits_lc;
382
383                 s = strbuf + sizeof(strbuf);
384                 if (vp->flag & INT_U)
385                         n = vp->val.u;
386                 else
387                         n = (vp->val.i < 0) ? -vp->val.u : vp->val.u;
388                 base = (vp->type == 0) ? 10U : (unsigned int)vp->type;
389
390                 if (base == 1 && n == 0)
391                         base = 2;
392                 if (base == 1) {
393                         size_t sz = 1;
394
395                         *(s = strbuf) = '1';
396                         s[1] = '#';
397                         if (!UTFMODE || ((n & 0xFF80) == 0xEF80))
398                                 /* OPTU-16 -> raw octet */
399                                 s[2] = n & 0xFF;
400                         else
401                                 sz = utf_wctomb(s + 2, n);
402                         s[2 + sz] = '\0';
403                 } else {
404                         *--s = '\0';
405                         do {
406                                 *--s = digits[n % base];
407                                 n /= base;
408                         } while (n != 0);
409                         if (base != 10) {
410                                 *--s = '#';
411                                 *--s = digits[base % 10];
412                                 if (base >= 10)
413                                         *--s = digits[base / 10];
414                         }
415                         if (!(vp->flag & INT_U) && vp->val.i < 0)
416                                 *--s = '-';
417                 }
418                 if (vp->flag & (RJUST|LJUST))
419                         /* case already dealt with */
420                         s = formatstr(vp, s);
421                 else
422                         strdupx(s, s, ATEMP);
423         }
424         return (s);
425 }
426
427 /* set variable to string value */
428 int
429 setstr(struct tbl *vq, const char *s, int error_ok)
430 {
431         char *salloc = NULL;
432         bool no_ro_check = tobool(error_ok & 0x4);
433
434         error_ok &= ~0x4;
435         if ((vq->flag & RDONLY) && !no_ro_check) {
436                 warningf(true, "read-only: %s", vq->name);
437                 if (!error_ok)
438                         errorfxz(2);
439                 return (0);
440         }
441         if (!(vq->flag&INTEGER)) {
442                 /* string dest */
443                 if ((vq->flag&ALLOC)) {
444 #ifndef MKSH_SMALL
445                         /* debugging */
446                         if (s >= vq->val.s &&
447                             s <= vq->val.s + strlen(vq->val.s)) {
448                                 internal_errorf(
449                                     "setstr: %s=%s: assigning to self",
450                                     vq->name, s);
451                         }
452 #endif
453                         afree(vq->val.s, vq->areap);
454                 }
455                 vq->flag &= ~(ISSET|ALLOC);
456                 vq->type = 0;
457                 if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
458                         s = salloc = formatstr(vq, s);
459                 if ((vq->flag&EXPORT))
460                         exportprep(vq, s);
461                 else {
462                         strdupx(vq->val.s, s, vq->areap);
463                         vq->flag |= ALLOC;
464                 }
465         } else {
466                 /* integer dest */
467                 if (!v_evaluate(vq, s, error_ok, true))
468                         return (0);
469         }
470         vq->flag |= ISSET;
471         if ((vq->flag&SPECIAL))
472                 setspec(vq);
473         afree(salloc, ATEMP);
474         return (1);
475 }
476
477 /* set variable to integer */
478 void
479 setint(struct tbl *vq, mksh_ari_t n)
480 {
481         if (!(vq->flag&INTEGER)) {
482                 struct tbl *vp = &vtemp;
483                 vp->flag = (ISSET|INTEGER);
484                 vp->type = 0;
485                 vp->areap = ATEMP;
486                 vp->val.i = n;
487                 /* setstr can't fail here */
488                 setstr(vq, str_val(vp), KSH_RETURN_ERROR);
489         } else
490                 vq->val.i = n;
491         vq->flag |= ISSET;
492         if ((vq->flag&SPECIAL))
493                 setspec(vq);
494 }
495
496 static int
497 getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
498 {
499         mksh_uari_t c, num = 0, base = 10;
500         const char *s;
501         bool have_base = false, neg = false;
502
503         if (vp->flag & SPECIAL)
504                 getspec(vp);
505         /* XXX is it possible for ISSET to be set and val.s to be NULL? */
506         if (!(vp->flag & ISSET) || (!(vp->flag & INTEGER) && vp->val.s == NULL))
507                 return (-1);
508         if (vp->flag & INTEGER) {
509                 nump->i = vp->val.i;
510                 return (vp->type);
511         }
512         s = vp->val.s + vp->type;
513
514         do {
515                 c = (unsigned char)*s++;
516         } while (ksh_isspace(c));
517
518         switch (c) {
519         case '-':
520                 neg = true;
521                 /* FALLTHROUGH */
522         case '+':
523                 c = (unsigned char)*s++;
524                 break;
525         }
526
527         if (c == '0' && arith) {
528                 if (ksh_eq(s[0], 'X', 'x')) {
529                         /* interpret as hexadecimal */
530                         base = 16;
531                         ++s;
532                         goto getint_c_style_base;
533                 } else if (Flag(FPOSIX) && ksh_isdigit(s[0]) &&
534                     !(vp->flag & ZEROFIL)) {
535                         /* interpret as octal (deprecated) */
536                         base = 8;
537  getint_c_style_base:
538                         have_base = true;
539                         c = (unsigned char)*s++;
540                 }
541         }
542
543         do {
544                 if (c == '#') {
545                         /* ksh-style base determination */
546                         if (have_base || num < 1)
547                                 return (-1);
548                         if ((base = num) == 1) {
549                                 /* mksh-specific extension */
550                                 unsigned int wc;
551
552                                 if (!UTFMODE)
553                                         wc = *(const unsigned char *)s;
554                                 else if (utf_mbtowc(&wc, s) == (size_t)-1)
555                                         /* OPTU-8 -> OPTU-16 */
556                                         /*
557                                          * (with a twist: 1#\uEF80 converts
558                                          * the same as 1#\x80 does, thus is
559                                          * not round-tripping correctly XXX)
560                                          */
561                                         wc = 0xEF00 + *(const unsigned char *)s;
562                                 nump->u = (mksh_uari_t)wc;
563                                 return (1);
564                         } else if (base > 36)
565                                 base = 10;
566                         num = 0;
567                         have_base = true;
568                         continue;
569                 }
570                 if (ksh_isdigit(c))
571                         c = ksh_numdig(c);
572                 else if (ksh_isupper(c))
573                         c = ksh_numuc(c) + 10;
574                 else if (ksh_islower(c))
575                         c = ksh_numlc(c) + 10;
576                 else
577                         return (-1);
578                 if (c >= base)
579                         return (-1);
580                 /* handle overflow as truncation */
581                 num = num * base + c;
582         } while ((c = (unsigned char)*s++));
583
584         if (neg)
585                 num = -num;
586         nump->u = num;
587         return (base);
588 }
589
590 /*
591  * convert variable vq to integer variable, setting its value from vp
592  * (vq and vp may be the same)
593  */
594 struct tbl *
595 setint_v(struct tbl *vq, struct tbl *vp, bool arith)
596 {
597         int base;
598         mksh_ari_u num;
599
600         if ((base = getint(vp, &num, arith)) == -1)
601                 return (NULL);
602         setint_n(vq, num.i, 0);
603         if (vq->type == 0)
604                 /* default base */
605                 vq->type = base;
606         return (vq);
607 }
608
609 /* convert variable vq to integer variable, setting its value to num */
610 void
611 setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
612 {
613         if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
614                 vq->flag &= ~ALLOC;
615                 vq->type = 0;
616                 afree(vq->val.s, vq->areap);
617         }
618         vq->val.i = num;
619         if (newbase != 0)
620                 vq->type = newbase;
621         vq->flag |= ISSET|INTEGER;
622         if (vq->flag&SPECIAL)
623                 setspec(vq);
624 }
625
626 static char *
627 formatstr(struct tbl *vp, const char *s)
628 {
629         int olen, nlen;
630         char *p, *q;
631         size_t psiz;
632
633         olen = (int)utf_mbswidth(s);
634
635         if (vp->flag & (RJUST|LJUST)) {
636                 if (!vp->u2.field)
637                         /* default field width */
638                         vp->u2.field = olen;
639                 nlen = vp->u2.field;
640         } else
641                 nlen = olen;
642
643         p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
644         if (vp->flag & (RJUST|LJUST)) {
645                 int slen = olen, i = 0;
646
647                 if (vp->flag & RJUST) {
648                         const char *qq = s;
649                         int n = 0;
650
651                         while (i < slen)
652                                 i += utf_widthadj(qq, &qq);
653                         /* strip trailing spaces (AT&T uses qq[-1] == ' ') */
654                         while (qq > s && ksh_isspace(qq[-1])) {
655                                 --qq;
656                                 --slen;
657                         }
658                         if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
659                                 if (!s[0] || !s[1])
660                                         goto uhm_no;
661                                 if (s[1] == '#')
662                                         n = 2;
663                                 else if (s[2] == '#')
664                                         n = 3;
665  uhm_no:
666                                 if (vp->u2.field <= n)
667                                         n = 0;
668                         }
669                         if (n) {
670                                 memcpy(p, s, n);
671                                 s += n;
672                         }
673                         while (slen > vp->u2.field)
674                                 slen -= utf_widthadj(s, &s);
675                         if (vp->u2.field - slen)
676                                 memset(p + n, (vp->flag & ZEROFIL) ? '0' : ' ',
677                                     vp->u2.field - slen);
678                         slen -= n;
679                         shf_snprintf(p + vp->u2.field - slen,
680                             psiz - (vp->u2.field - slen),
681                             "%.*s", slen, s);
682                 } else {
683                         /* strip leading spaces/zeros */
684                         while (ksh_isspace(*s))
685                                 s++;
686                         if (vp->flag & ZEROFIL)
687                                 while (*s == '0')
688                                         s++;
689                         shf_snprintf(p, nlen + 1, "%-*.*s",
690                                 vp->u2.field, vp->u2.field, s);
691                 }
692         } else
693                 memcpy(p, s, strlen(s) + 1);
694
695         if (vp->flag & UCASEV_AL) {
696                 for (q = p; *q; q++)
697                         *q = ksh_toupper(*q);
698         } else if (vp->flag & LCASEV) {
699                 for (q = p; *q; q++)
700                         *q = ksh_tolower(*q);
701         }
702
703         return (p);
704 }
705
706 /*
707  * make vp->val.s be "name=value" for quick exporting.
708  */
709 static void
710 exportprep(struct tbl *vp, const char *val)
711 {
712         char *xp;
713         char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
714         size_t namelen, vallen;
715
716         namelen = strlen(vp->name);
717         vallen = strlen(val) + 1;
718
719         vp->flag |= ALLOC;
720         /* since name+val are both in memory this can go unchecked */
721         xp = alloc(namelen + 1 + vallen, vp->areap);
722         memcpy(vp->val.s = xp, vp->name, namelen);
723         xp += namelen;
724         *xp++ = '=';
725         /* offset to value */
726         vp->type = xp - vp->val.s;
727         memcpy(xp, val, vallen);
728         afree(op, vp->areap);
729 }
730
731 /*
732  * lookup variable (according to (set&LOCAL)), set its attributes
733  * (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, LCASEV,
734  * UCASEV_AL), and optionally set its value if an assignment.
735  */
736 struct tbl *
737 typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
738 {
739         struct tbl *vp;
740         struct tbl *vpbase, *t;
741         char *tvar;
742         const char *val;
743         size_t len;
744         bool vappend = false;
745         enum namerefflag new_refflag = SRF_NOP;
746
747         if ((set & (ARRAY | ASSOC)) == ASSOC) {
748                 new_refflag = SRF_ENABLE;
749                 set &= ~(ARRAY | ASSOC);
750         }
751         if ((clr & (ARRAY | ASSOC)) == ASSOC) {
752                 new_refflag = SRF_DISABLE;
753                 clr &= ~(ARRAY | ASSOC);
754         }
755
756         /* check for valid variable name, search for value */
757         val = skip_varname(var, false);
758         if (val == var) {
759                 /* no variable name given */
760                 return (NULL);
761         }
762         if (*val == '[') {
763                 if (new_refflag != SRF_NOP)
764                         errorf("%s: %s", var,
765                             "reference variable can't be an array");
766                 len = array_ref_len(val);
767                 if (len == 0)
768                         return (NULL);
769                 /*
770                  * IMPORT is only used when the shell starts up and is
771                  * setting up its environment. Allow only simple array
772                  * references at this time since parameter/command
773                  * substitution is performed on the [expression] which
774                  * would be a major security hole.
775                  */
776                 if (set & IMPORT) {
777                         size_t i;
778
779                         for (i = 1; i < len - 1; i++)
780                                 if (!ksh_isdigit(val[i]))
781                                         return (NULL);
782                 }
783                 val += len;
784         }
785         if (val[0] == '=') {
786                 strndupx(tvar, var, val - var, ATEMP);
787                 ++val;
788         } else if (set & IMPORT) {
789                 /* environment invalid variable name or no assignment */
790                 return (NULL);
791         } else if (val[0] == '+' && val[1] == '=') {
792                 strndupx(tvar, var, val - var, ATEMP);
793                 val += 2;
794                 vappend = true;
795         } else if (val[0] != '\0') {
796                 /* other invalid variable names (not from environment) */
797                 return (NULL);
798         } else {
799                 /* just varname with no value part nor equals sign */
800                 strdupx(tvar, var, ATEMP);
801                 val = NULL;
802                 /* handle foo[*] => foo (whole array) mapping for R39b */
803                 len = strlen(tvar);
804                 if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' &&
805                     tvar[len - 1] == ']')
806                         tvar[len - 3] = '\0';
807         }
808
809         if (new_refflag == SRF_ENABLE) {
810                 const char *qval, *ccp;
811
812                 /* bail out on 'nameref foo+=bar' */
813                 if (vappend)
814                         errorf("appending not allowed for nameref");
815                 /* find value if variable already exists */
816                 if ((qval = val) == NULL) {
817                         varsearch(e->loc, &vp, tvar, hash(tvar));
818                         if (vp == NULL)
819                                 goto nameref_empty;
820                         qval = str_val(vp);
821                 }
822                 /* check target value for being a valid variable name */
823                 ccp = skip_varname(qval, false);
824                 if (ccp == qval) {
825                         if (ksh_isdigit(qval[0])) {
826                                 int c;
827
828                                 if (getn(qval, &c))
829                                         goto nameref_rhs_checked;
830                         } else if (qval[1] == '\0') switch (qval[0]) {
831                         case '$':
832                         case '!':
833                         case '?':
834                         case '#':
835                         case '-':
836                                 goto nameref_rhs_checked;
837                         }
838  nameref_empty:
839                         errorf("%s: %s", var, "empty nameref target");
840                 }
841                 len = (*ccp == '[') ? array_ref_len(ccp) : 0;
842                 if (ccp[len]) {
843                         /*
844                          * works for cases "no array", "valid array with
845                          * junk after it" and "invalid array"; in the
846                          * latter case, len is also 0 and points to '['
847                          */
848                         errorf("%s: %s", qval,
849                             "nameref target not a valid parameter name");
850                 }
851  nameref_rhs_checked:
852                 /* prevent nameref loops */
853                 while (qval) {
854                         if (!strcmp(qval, tvar))
855                                 errorf("%s: %s", qval,
856                                     "expression recurses on parameter");
857                         varsearch(e->loc, &vp, qval, hash(qval));
858                         qval = NULL;
859                         if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
860                                 qval = str_val(vp);
861                 }
862         }
863
864         /* prevent typeset from creating a local PATH/ENV/SHELL */
865         if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
866             strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
867                 errorf("%s: %s", tvar, "restricted");
868
869         innermost_refflag = new_refflag;
870         vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
871             global(tvar);
872         if (new_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
873                 vp->flag &= ~ASSOC;
874         else if (new_refflag == SRF_ENABLE) {
875                 if (vp->flag & ARRAY) {
876                         struct tbl *a, *tmp;
877
878                         /* free up entire array */
879                         for (a = vp->u.array; a; ) {
880                                 tmp = a;
881                                 a = a->u.array;
882                                 if (tmp->flag & ALLOC)
883                                         afree(tmp->val.s, tmp->areap);
884                                 afree(tmp, tmp->areap);
885                         }
886                         vp->u.array = NULL;
887                         vp->flag &= ~ARRAY;
888                 }
889                 vp->flag |= ASSOC;
890         }
891
892         set &= ~(LOCAL|LOCAL_COPY);
893
894         vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
895
896         /*
897          * only allow export flag to be set; AT&T ksh allows any
898          * attribute to be changed which means it can be truncated or
899          * modified (-L/-R/-Z/-i)
900          */
901         if ((vpbase->flag & RDONLY) &&
902             (val || clr || (set & ~EXPORT)))
903                 /* XXX check calls - is error here ok by POSIX? */
904                 errorfx(2, "read-only: %s", tvar);
905         afree(tvar, ATEMP);
906
907         /* most calls are with set/clr == 0 */
908         if (set | clr) {
909                 bool ok = true;
910
911                 /*
912                  * XXX if x[0] isn't set, there will be problems: need
913                  * to have one copy of attributes for arrays...
914                  */
915                 for (t = vpbase; t; t = t->u.array) {
916                         bool fake_assign;
917                         char *s = NULL;
918                         char *free_me = NULL;
919
920                         fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
921                             ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
922                             ((t->flag & INTEGER) && (clr & INTEGER)) ||
923                             (!(t->flag & INTEGER) && (set & INTEGER)));
924                         if (fake_assign) {
925                                 if (t->flag & INTEGER) {
926                                         s = str_val(t);
927                                         free_me = NULL;
928                                 } else {
929                                         s = t->val.s + t->type;
930                                         free_me = (t->flag & ALLOC) ? t->val.s :
931                                             NULL;
932                                 }
933                                 t->flag &= ~ALLOC;
934                         }
935                         if (!(t->flag & INTEGER) && (set & INTEGER)) {
936                                 t->type = 0;
937                                 t->flag &= ~ALLOC;
938                         }
939                         t->flag = (t->flag | set) & ~clr;
940                         /*
941                          * Don't change base if assignment is to be
942                          * done, in case assignment fails.
943                          */
944                         if ((set & INTEGER) && base > 0 && (!val || t != vp))
945                                 t->type = base;
946                         if (set & (LJUST|RJUST|ZEROFIL))
947                                 t->u2.field = field;
948                         if (fake_assign) {
949                                 if (!setstr(t, s, KSH_RETURN_ERROR)) {
950                                         /*
951                                          * Somewhat arbitrary action
952                                          * here: zap contents of
953                                          * variable, but keep the flag
954                                          * settings.
955                                          */
956                                         ok = false;
957                                         if (t->flag & INTEGER)
958                                                 t->flag &= ~ISSET;
959                                         else {
960                                                 if (t->flag & ALLOC)
961                                                         afree(t->val.s, t->areap);
962                                                 t->flag &= ~(ISSET|ALLOC);
963                                                 t->type = 0;
964                                         }
965                                 }
966                                 afree(free_me, t->areap);
967                         }
968                 }
969                 if (!ok)
970                         errorfz();
971         }
972
973         if (val != NULL) {
974                 char *tval;
975
976                 if (vappend) {
977                         tval = shf_smprintf("%s%s", str_val(vp), val);
978                         val = tval;
979                 } else
980                         tval = NULL;
981
982                 if (vp->flag&INTEGER) {
983                         /* do not zero base before assignment */
984                         setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
985                         /* done after assignment to override default */
986                         if (base > 0)
987                                 vp->type = base;
988                 } else
989                         /* setstr can't fail (readonly check already done) */
990                         setstr(vp, val, KSH_RETURN_ERROR | 0x4);
991
992                 afree(tval, ATEMP);
993         }
994
995         /* only x[0] is ever exported, so use vpbase */
996         if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
997             vpbase->type == 0)
998                 exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
999
1000         return (vp);
1001 }
1002
1003 /**
1004  * Unset a variable. The flags can be:
1005  * |1   = tear down entire array
1006  * |2   = keep attributes, only unset content
1007  */
1008 void
1009 unset(struct tbl *vp, int flags)
1010 {
1011         if (vp->flag & ALLOC)
1012                 afree(vp->val.s, vp->areap);
1013         if ((vp->flag & ARRAY) && (flags & 1)) {
1014                 struct tbl *a, *tmp;
1015
1016                 /* free up entire array */
1017                 for (a = vp->u.array; a; ) {
1018                         tmp = a;
1019                         a = a->u.array;
1020                         if (tmp->flag & ALLOC)
1021                                 afree(tmp->val.s, tmp->areap);
1022                         afree(tmp, tmp->areap);
1023                 }
1024                 vp->u.array = NULL;
1025         }
1026         if (flags & 2) {
1027                 vp->flag &= ~(ALLOC|ISSET);
1028                 return;
1029         }
1030         /* if foo[0] is being unset, the remainder of the array is kept... */
1031         vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
1032         if (vp->flag & SPECIAL)
1033                 /* responsible for 'unspecial'ing var */
1034                 unsetspec(vp);
1035 }
1036
1037 /*
1038  * Return a pointer to the first char past a legal variable name
1039  * (returns the argument if there is no legal name, returns a pointer to
1040  * the terminating NUL if whole string is legal).
1041  */
1042 const char *
1043 skip_varname(const char *s, bool aok)
1044 {
1045         size_t alen;
1046
1047         if (s && ksh_isalphx(*s)) {
1048                 while (*++s && ksh_isalnux(*s))
1049                         ;
1050                 if (aok && *s == '[' && (alen = array_ref_len(s)))
1051                         s += alen;
1052         }
1053         return (s);
1054 }
1055
1056 /* Return a pointer to the first character past any legal variable name */
1057 const char *
1058 skip_wdvarname(const char *s,
1059     /* skip array de-reference? */
1060     bool aok)
1061 {
1062         if (s[0] == CHAR && ksh_isalphx(s[1])) {
1063                 do {
1064                         s += 2;
1065                 } while (s[0] == CHAR && ksh_isalnux(s[1]));
1066                 if (aok && s[0] == CHAR && s[1] == '[') {
1067                         /* skip possible array de-reference */
1068                         const char *p = s;
1069                         char c;
1070                         int depth = 0;
1071
1072                         while (/* CONSTCOND */ 1) {
1073                                 if (p[0] != CHAR)
1074                                         break;
1075                                 c = p[1];
1076                                 p += 2;
1077                                 if (c == '[')
1078                                         depth++;
1079                                 else if (c == ']' && --depth == 0) {
1080                                         s = p;
1081                                         break;
1082                                 }
1083                         }
1084                 }
1085         }
1086         return (s);
1087 }
1088
1089 /* Check if coded string s is a variable name */
1090 int
1091 is_wdvarname(const char *s, bool aok)
1092 {
1093         const char *p = skip_wdvarname(s, aok);
1094
1095         return (p != s && p[0] == EOS);
1096 }
1097
1098 /* Check if coded string s is a variable assignment */
1099 int
1100 is_wdvarassign(const char *s)
1101 {
1102         const char *p = skip_wdvarname(s, true);
1103
1104         return (p != s && p[0] == CHAR &&
1105             (p[1] == '=' || (p[1] == '+' && p[2] == CHAR && p[3] == '=')));
1106 }
1107
1108 /*
1109  * Make the exported environment from the exported names in the dictionary.
1110  */
1111 char **
1112 makenv(void)
1113 {
1114         ssize_t i;
1115         struct block *l;
1116         XPtrV denv;
1117         struct tbl *vp, **vpp;
1118
1119         XPinit(denv, 64);
1120         for (l = e->loc; l != NULL; l = l->next) {
1121                 vpp = l->vars.tbls;
1122                 i = 1 << (l->vars.tshift);
1123                 while (--i >= 0)
1124                         if ((vp = *vpp++) != NULL &&
1125                             (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
1126                                 struct block *l2;
1127                                 struct tbl *vp2;
1128                                 uint32_t h = hash(vp->name);
1129
1130                                 /* unexport any redefined instances */
1131                                 for (l2 = l->next; l2 != NULL; l2 = l2->next) {
1132                                         vp2 = ktsearch(&l2->vars, vp->name, h);
1133                                         if (vp2 != NULL)
1134                                                 vp2->flag &= ~EXPORT;
1135                                 }
1136                                 if ((vp->flag&INTEGER)) {
1137                                         /* integer to string */
1138                                         char *val;
1139                                         val = str_val(vp);
1140                                         vp->flag &= ~(INTEGER|RDONLY|SPECIAL);
1141                                         /* setstr can't fail here */
1142                                         setstr(vp, val, KSH_RETURN_ERROR);
1143                                 }
1144                                 XPput(denv, vp->val.s);
1145                         }
1146                 if (l->flags & BF_STOPENV)
1147                         break;
1148         }
1149         XPput(denv, NULL);
1150         return ((char **)XPclose(denv));
1151 }
1152
1153 /*
1154  * handle special variables with side effects - PATH, SECONDS.
1155  */
1156
1157 /* Test if name is a special parameter */
1158 static int
1159 special(const char *name)
1160 {
1161         struct tbl *tp;
1162
1163         tp = ktsearch(&specials, name, hash(name));
1164         return (tp && (tp->flag & ISSET) ? tp->type : V_NONE);
1165 }
1166
1167 /* Make a variable non-special */
1168 static void
1169 unspecial(const char *name)
1170 {
1171         struct tbl *tp;
1172
1173         tp = ktsearch(&specials, name, hash(name));
1174         if (tp)
1175                 ktdelete(tp);
1176 }
1177
1178 static time_t seconds;          /* time SECONDS last set */
1179 static mksh_uari_t user_lineno; /* what user set $LINENO to */
1180
1181 static void
1182 getspec(struct tbl *vp)
1183 {
1184         mksh_ari_u num;
1185         int st;
1186         struct timeval tv;
1187
1188         switch ((st = special(vp->name))) {
1189         case V_COLUMNS:
1190         case V_LINES:
1191                 /*
1192                  * Do NOT export COLUMNS/LINES. Many applications
1193                  * check COLUMNS/LINES before checking ws.ws_col/row,
1194                  * so if the app is started with C/L in the environ
1195                  * and the window is then resized, the app won't
1196                  * see the change cause the environ doesn't change.
1197                  */
1198                 if (got_winch)
1199                         change_winsz();
1200                 break;
1201         }
1202         switch (st) {
1203         case V_BASHPID:
1204                 num.u = (mksh_uari_t)procpid;
1205                 break;
1206         case V_COLUMNS:
1207                 num.i = x_cols;
1208                 break;
1209         case V_HISTSIZE:
1210                 num.i = histsize;
1211                 break;
1212         case V_LINENO:
1213                 num.u = (mksh_uari_t)current_lineno + user_lineno;
1214                 break;
1215         case V_LINES:
1216                 num.i = x_lins;
1217                 break;
1218         case V_EPOCHREALTIME: {
1219                 /* 10(%u) + 1(.) + 6 + NUL */
1220                 char buf[18];
1221
1222                 vp->flag &= ~SPECIAL;
1223                 mksh_TIME(tv);
1224                 shf_snprintf(buf, sizeof(buf), "%u.%06u",
1225                     (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
1226                 setstr(vp, buf, KSH_RETURN_ERROR | 0x4);
1227                 vp->flag |= SPECIAL;
1228                 return;
1229         }
1230         case V_OPTIND:
1231                 num.i = user_opt.uoptind;
1232                 break;
1233         case V_RANDOM:
1234                 num.i = rndget();
1235                 break;
1236         case V_SECONDS:
1237                 /*
1238                  * On start up the value of SECONDS is used before
1239                  * it has been set - don't do anything in this case
1240                  * (see initcoms[] in main.c).
1241                  */
1242                 if (vp->flag & ISSET) {
1243                         mksh_TIME(tv);
1244                         num.i = tv.tv_sec - seconds;
1245                 } else
1246                         return;
1247                 break;
1248         default:
1249                 /* do nothing, do not touch vp at all */
1250                 return;
1251         }
1252         vp->flag &= ~SPECIAL;
1253         setint_n(vp, num.i, 0);
1254         vp->flag |= SPECIAL;
1255 }
1256
1257 static void
1258 setspec(struct tbl *vp)
1259 {
1260         mksh_ari_u num;
1261         char *s;
1262         int st;
1263
1264         switch ((st = special(vp->name))) {
1265 #if HAVE_PERSISTENT_HISTORY
1266         case V_HISTFILE:
1267                 sethistfile(str_val(vp));
1268                 return;
1269 #endif
1270         case V_IFS:
1271                 setctypes(s = str_val(vp), C_IFS);
1272                 ifs0 = *s;
1273                 return;
1274         case V_PATH:
1275                 afree(path, APERM);
1276                 s = str_val(vp);
1277                 strdupx(path, s, APERM);
1278                 /* clear tracked aliases */
1279                 flushcom(true);
1280                 return;
1281         case V_TMPDIR:
1282                 afree(tmpdir, APERM);
1283                 tmpdir = NULL;
1284                 /*
1285                  * Use tmpdir iff it is an absolute path, is writable
1286                  * and searchable and is a directory...
1287                  */
1288                 {
1289                         struct stat statb;
1290
1291                         s = str_val(vp);
1292                         /* LINTED use of access */
1293                         if (mksh_abspath(s) && access(s, W_OK|X_OK) == 0 &&
1294                             stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
1295                                 strdupx(tmpdir, s, APERM);
1296                 }
1297                 return;
1298         /* common sub-cases */
1299         case V_COLUMNS:
1300         case V_LINES:
1301                 if (vp->flag & IMPORT) {
1302                         /* do not touch */
1303                         unspecial(vp->name);
1304                         vp->flag &= ~SPECIAL;
1305                         return;
1306                 }
1307                 /* FALLTHROUGH */
1308         case V_HISTSIZE:
1309         case V_LINENO:
1310         case V_OPTIND:
1311         case V_RANDOM:
1312         case V_SECONDS:
1313         case V_TMOUT:
1314                 vp->flag &= ~SPECIAL;
1315                 if (getint(vp, &num, false) == -1) {
1316                         s = str_val(vp);
1317                         if (st != V_RANDOM)
1318                                 errorf("%s: %s: %s", vp->name, "bad number", s);
1319                         num.u = hash(s);
1320                 }
1321                 vp->flag |= SPECIAL;
1322                 break;
1323         default:
1324                 /* do nothing, do not touch vp at all */
1325                 return;
1326         }
1327
1328         /* process the singular parts of the common cases */
1329
1330         switch (st) {
1331         case V_COLUMNS:
1332                 if (num.i >= MIN_COLS)
1333                         x_cols = num.i;
1334                 break;
1335         case V_HISTSIZE:
1336                 sethistsize(num.i);
1337                 break;
1338         case V_LINENO:
1339                 /* The -1 is because line numbering starts at 1. */
1340                 user_lineno = num.u - (mksh_uari_t)current_lineno - 1;
1341                 break;
1342         case V_LINES:
1343                 if (num.i >= MIN_LINS)
1344                         x_lins = num.i;
1345                 break;
1346         case V_OPTIND:
1347                 getopts_reset((int)num.i);
1348                 break;
1349         case V_RANDOM:
1350                 /*
1351                  * mksh R39d+ no longer has the traditional repeatability
1352                  * of $RANDOM sequences, but always retains state
1353                  */
1354                 rndset((unsigned long)num.u);
1355                 break;
1356         case V_SECONDS:
1357                 {
1358                         struct timeval tv;
1359
1360                         mksh_TIME(tv);
1361                         seconds = tv.tv_sec - num.i;
1362                 }
1363                 break;
1364         case V_TMOUT:
1365                 ksh_tmout = num.i >= 0 ? num.i : 0;
1366                 break;
1367         }
1368 }
1369
1370 static void
1371 unsetspec(struct tbl *vp)
1372 {
1373         /*
1374          * AT&T ksh man page says OPTIND, OPTARG and _ lose special
1375          * meaning, but OPTARG does not (still set by getopts) and _ is
1376          * also still set in various places. Don't know what AT&T does
1377          * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
1378          * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
1379          */
1380
1381         switch (special(vp->name)) {
1382 #if HAVE_PERSISTENT_HISTORY
1383         case V_HISTFILE:
1384                 sethistfile(NULL);
1385                 return;
1386 #endif
1387         case V_IFS:
1388                 setctypes(TC_IFSWS, C_IFS);
1389                 ifs0 = ' ';
1390                 break;
1391         case V_PATH:
1392                 afree(path, APERM);
1393                 strdupx(path, def_path, APERM);
1394                 /* clear tracked aliases */
1395                 flushcom(true);
1396                 break;
1397         case V_TMPDIR:
1398                 /* should not become unspecial */
1399                 if (tmpdir) {
1400                         afree(tmpdir, APERM);
1401                         tmpdir = NULL;
1402                 }
1403                 break;
1404         case V_LINENO:
1405         case V_RANDOM:
1406         case V_SECONDS:
1407         case V_TMOUT:
1408                 /* AT&T ksh leaves previous value in place */
1409                 unspecial(vp->name);
1410                 break;
1411         }
1412 }
1413
1414 /*
1415  * Search for (and possibly create) a table entry starting with
1416  * vp, indexed by val.
1417  */
1418 struct tbl *
1419 arraysearch(struct tbl *vp, uint32_t val)
1420 {
1421         struct tbl *prev, *curr, *news;
1422         size_t len;
1423
1424         vp->flag = (vp->flag | (ARRAY | DEFINED)) & ~ASSOC;
1425         /* the table entry is always [0] */
1426         if (val == 0)
1427                 return (vp);
1428         prev = vp;
1429         curr = vp->u.array;
1430         while (curr && curr->ua.index < val) {
1431                 prev = curr;
1432                 curr = curr->u.array;
1433         }
1434         if (curr && curr->ua.index == val) {
1435                 if (curr->flag&ISSET)
1436                         return (curr);
1437                 news = curr;
1438         } else
1439                 news = NULL;
1440         if (!news) {
1441                 len = strlen(vp->name);
1442                 checkoktoadd(len, 1 + offsetof(struct tbl, name[0]));
1443                 news = alloc(offsetof(struct tbl, name[0]) + ++len, vp->areap);
1444                 memcpy(news->name, vp->name, len);
1445         }
1446         news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
1447         news->type = vp->type;
1448         news->areap = vp->areap;
1449         news->u2.field = vp->u2.field;
1450         news->ua.index = val;
1451
1452         if (curr != news) {
1453                 /* not reusing old array entry */
1454                 prev->u.array = news;
1455                 news->u.array = curr;
1456         }
1457         return (news);
1458 }
1459
1460 /*
1461  * Return the length of an array reference (eg, [1+2]) - cp is assumed
1462  * to point to the open bracket. Returns 0 if there is no matching
1463  * closing bracket.
1464  *
1465  * XXX this should parse the actual arithmetic syntax
1466  */
1467 size_t
1468 array_ref_len(const char *cp)
1469 {
1470         const char *s = cp;
1471         char c;
1472         int depth = 0;
1473
1474         while ((c = *s++) && (c != ']' || --depth))
1475                 if (c == '[')
1476                         depth++;
1477         if (!c)
1478                 return (0);
1479         return (s - cp);
1480 }
1481
1482 /*
1483  * Make a copy of the base of an array name
1484  */
1485 char *
1486 arrayname(const char *str)
1487 {
1488         const char *p;
1489         char *rv;
1490
1491         if (!(p = cstrchr(str, '[')))
1492                 /* Shouldn't happen, but why worry? */
1493                 strdupx(rv, str, ATEMP);
1494         else
1495                 strndupx(rv, str, p - str, ATEMP);
1496
1497         return (rv);
1498 }
1499
1500 /* set (or overwrite, if reset) the array variable var to the values in vals */
1501 mksh_uari_t
1502 set_array(const char *var, bool reset, const char **vals)
1503 {
1504         struct tbl *vp, *vq;
1505         mksh_uari_t i = 0, j = 0;
1506         const char *ccp = var;
1507         char *cp = NULL;
1508         size_t n;
1509
1510         /* to get local array, use "local foo; set -A foo" */
1511         n = strlen(var);
1512         if (n > 0 && var[n - 1] == '+') {
1513                 /* append mode */
1514                 reset = false;
1515                 strndupx(cp, var, n - 1, ATEMP);
1516                 ccp = cp;
1517         }
1518         vp = global(ccp);
1519
1520         /* Note: AT&T ksh allows set -A but not set +A of a read-only var */
1521         if ((vp->flag&RDONLY))
1522                 errorfx(2, "read-only: %s", ccp);
1523         /* This code is quite non-optimal */
1524         if (reset) {
1525                 /* trash existing values and attributes */
1526                 unset(vp, 1);
1527                 /* allocate-by-access the [0] element to keep in scope */
1528                 arraysearch(vp, 0);
1529         }
1530         /*
1531          * TODO: would be nice for assignment to completely succeed or
1532          * completely fail. Only really effects integer arrays:
1533          * evaluation of some of vals[] may fail...
1534          */
1535         if (cp != NULL) {
1536                 /* find out where to set when appending */
1537                 for (vq = vp; vq; vq = vq->u.array) {
1538                         if (!(vq->flag & ISSET))
1539                                 continue;
1540                         if (arrayindex(vq) >= j)
1541                                 j = arrayindex(vq) + 1;
1542                 }
1543                 afree(cp, ATEMP);
1544         }
1545         while ((ccp = vals[i])) {
1546 #if 0 /* temporarily taken out due to regression */
1547                 if (*ccp == '[') {
1548                         int level = 0;
1549
1550                         while (*ccp) {
1551                                 if (*ccp == ']' && --level == 0)
1552                                         break;
1553                                 if (*ccp == '[')
1554                                         ++level;
1555                                 ++ccp;
1556                         }
1557                         if (*ccp == ']' && level == 0 && ccp[1] == '=') {
1558                                 strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
1559                                     ATEMP);
1560                                 evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
1561                                     KSH_UNWIND_ERROR, true);
1562                                 afree(cp, ATEMP);
1563                                 ccp += 2;
1564                         } else
1565                                 ccp = vals[i];
1566                 }
1567 #endif
1568
1569                 vq = arraysearch(vp, j);
1570                 /* would be nice to deal with errors here... (see above) */
1571                 setstr(vq, ccp, KSH_RETURN_ERROR);
1572                 i++;
1573                 j++;
1574         }
1575
1576         return (i);
1577 }
1578
1579 void
1580 change_winsz(void)
1581 {
1582         struct timeval tv;
1583
1584         mksh_TIME(tv);
1585         BAFHUpdateMem_mem(qh_state, &tv, sizeof(tv));
1586
1587 #ifdef TIOCGWINSZ
1588         /* check if window size has changed */
1589         if (tty_init_fd() < 2) {
1590                 struct winsize ws;
1591
1592                 if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
1593                         if (ws.ws_col)
1594                                 x_cols = ws.ws_col;
1595                         if (ws.ws_row)
1596                                 x_lins = ws.ws_row;
1597                 }
1598         }
1599 #endif
1600
1601         /* bounds check for sane values, use defaults otherwise */
1602         if (x_cols < MIN_COLS)
1603                 x_cols = 80;
1604         if (x_lins < MIN_LINS)
1605                 x_lins = 24;
1606
1607 #ifdef SIGWINCH
1608         got_winch = 0;
1609 #endif
1610 }
1611
1612 uint32_t
1613 hash(const void *s)
1614 {
1615         register uint32_t h;
1616
1617         BAFHInit(h);
1618         BAFHUpdateStr_reg(h, s);
1619         BAFHFinish_reg(h);
1620         return (h);
1621 }
1622
1623 uint32_t
1624 chvt_rndsetup(const void *bp, size_t sz)
1625 {
1626         register uint32_t h;
1627
1628         /* use LCG as seed but try to get them to deviate immediately */
1629         h = lcg_state;
1630         (void)rndget();
1631         BAFHFinish_reg(h);
1632         /* variation through pid, ppid, and the works */
1633         BAFHUpdateMem_reg(h, &rndsetupstate, sizeof(rndsetupstate));
1634         /* some variation, some possibly entropy, depending on OE */
1635         BAFHUpdateMem_reg(h, bp, sz);
1636         /* mix them all up */
1637         BAFHFinish_reg(h);
1638
1639         return (h);
1640 }
1641
1642 mksh_ari_t
1643 rndget(void)
1644 {
1645         /*
1646          * this is the same Linear Congruential PRNG as Borland
1647          * C/C++ allegedly uses in its built-in rand() function
1648          */
1649         return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
1650 }
1651
1652 void
1653 rndset(unsigned long v)
1654 {
1655         register uint32_t h;
1656 #if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
1657         register uint32_t t;
1658 #endif
1659         struct {
1660                 struct timeval tv;
1661                 void *sp;
1662                 uint32_t qh;
1663                 pid_t pp;
1664                 short r;
1665         } z;
1666
1667 #ifdef DEBUG
1668         /* clear the allocated space, for valgrind */
1669         memset(&z, 0, sizeof(z));
1670 #endif
1671
1672         h = lcg_state;
1673         BAFHFinish_reg(h);
1674         BAFHUpdateMem_reg(h, &v, sizeof(v));
1675
1676         mksh_TIME(z.tv);
1677         z.sp = &lcg_state;
1678         z.pp = procpid;
1679         z.r = (short)rndget();
1680
1681 #if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
1682         t = qh_state;
1683         BAFHFinish_reg(t);
1684         z.qh = (t & 0xFFFF8000) | rndget();
1685         lcg_state = (t << 15) | rndget();
1686         /*
1687          * either we have very chap entropy get and push available,
1688          * with malloc() pulling in this code already anyway, or the
1689          * user requested us to use the old functions
1690          */
1691         t = h;
1692         BAFHUpdateMem_reg(t, &lcg_state, sizeof(lcg_state));
1693         BAFHFinish_reg(t);
1694         lcg_state = t;
1695 #if defined(arc4random_pushb_fast)
1696         arc4random_pushb_fast(&lcg_state, sizeof(lcg_state));
1697         lcg_state = arc4random();
1698 #else
1699         lcg_state = arc4random_pushb(&lcg_state, sizeof(lcg_state));
1700 #endif
1701         BAFHUpdateMem_reg(h, &lcg_state, sizeof(lcg_state));
1702 #else
1703         z.qh = qh_state;
1704 #endif
1705
1706         BAFHUpdateMem_reg(h, &z, sizeof(z));
1707         BAFHFinish_reg(h);
1708         lcg_state = h;
1709 }
1710
1711 void
1712 rndpush(const void *s)
1713 {
1714         register uint32_t h = qh_state;
1715
1716         BAFHUpdateStr_reg(h, s);
1717         BAFHUpdateOctet_reg(h, 0);
1718         qh_state = h;
1719 }