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