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