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