1/* $OpenBSD: eval.c,v 1.37 2011/10/11 14:32:43 otto Exp $ */ 2 3/*- 4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 5 * 2011, 2012, 2013 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 26__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.136 2013/02/10 23:43:59 tg Exp $"); 27 28/* 29 * string expansion 30 * 31 * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution. 32 * second pass: alternation ({,}), filename expansion (*?[]). 33 */ 34 35/* expansion generator state */ 36typedef struct Expand { 37 /* int type; */ /* see expand() */ 38 const char *str; /* string */ 39 union { 40 const char **strv; /* string[] */ 41 struct shf *shf; /* file */ 42 } u; /* source */ 43 struct tbl *var; /* variable in ${var..} */ 44 bool split; /* split "$@" / call waitlast $() */ 45} Expand; 46 47#define XBASE 0 /* scanning original */ 48#define XSUB 1 /* expanding ${} string */ 49#define XARGSEP 2 /* ifs0 between "$*" */ 50#define XARG 3 /* expanding $*, $@ */ 51#define XCOM 4 /* expanding $() */ 52#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */ 53#define XSUBMID 6 /* middle of expanding ${} */ 54 55/* States used for field splitting */ 56#define IFS_WORD 0 /* word has chars (or quotes) */ 57#define IFS_WS 1 /* have seen IFS white-space */ 58#define IFS_NWS 2 /* have seen IFS non-white-space */ 59 60static int varsub(Expand *, const char *, const char *, int *, int *); 61static int comsub(Expand *, const char *, int); 62static void funsub(struct op *); 63static char *trimsub(char *, char *, int); 64static void glob(char *, XPtrV *, bool); 65static void globit(XString *, char **, char *, XPtrV *, int); 66static const char *maybe_expand_tilde(const char *, XString *, char **, int); 67#ifndef MKSH_NOPWNAM 68static char *homedir(char *); 69#endif 70static void alt_expand(XPtrV *, char *, char *, char *, int); 71static int utflen(const char *); 72static void utfincptr(const char *, mksh_ari_t *); 73 74/* UTFMODE functions */ 75static int 76utflen(const char *s) 77{ 78 size_t n; 79 80 if (UTFMODE) { 81 n = 0; 82 while (*s) { 83 s += utf_ptradj(s); 84 ++n; 85 } 86 } else 87 n = strlen(s); 88 89 if (n > 2147483647) 90 n = 2147483647; 91 return ((int)n); 92} 93 94static void 95utfincptr(const char *s, mksh_ari_t *lp) 96{ 97 const char *cp = s; 98 99 while ((*lp)--) 100 cp += utf_ptradj(cp); 101 *lp = cp - s; 102} 103 104/* compile and expand word */ 105char * 106substitute(const char *cp, int f) 107{ 108 struct source *s, *sold; 109 110 sold = source; 111 s = pushs(SWSTR, ATEMP); 112 s->start = s->str = cp; 113 source = s; 114 if (yylex(ONEWORD) != LWORD) 115 internal_errorf("bad substitution"); 116 source = sold; 117 afree(s, ATEMP); 118 return (evalstr(yylval.cp, f)); 119} 120 121/* 122 * expand arg-list 123 */ 124char ** 125eval(const char **ap, int f) 126{ 127 XPtrV w; 128 129 if (*ap == NULL) { 130 union mksh_ccphack vap; 131 132 vap.ro = ap; 133 return (vap.rw); 134 } 135 XPinit(w, 32); 136 /* space for shell name */ 137 XPput(w, NULL); 138 while (*ap != NULL) 139 expand(*ap++, &w, f); 140 XPput(w, NULL); 141 return ((char **)XPclose(w) + 1); 142} 143 144/* 145 * expand string 146 */ 147char * 148evalstr(const char *cp, int f) 149{ 150 XPtrV w; 151 char *dp = null; 152 153 XPinit(w, 1); 154 expand(cp, &w, f); 155 if (XPsize(w)) 156 dp = *XPptrv(w); 157 XPfree(w); 158 return (dp); 159} 160 161/* 162 * expand string - return only one component 163 * used from iosetup to expand redirection files 164 */ 165char * 166evalonestr(const char *cp, int f) 167{ 168 XPtrV w; 169 char *rv; 170 171 XPinit(w, 1); 172 expand(cp, &w, f); 173 switch (XPsize(w)) { 174 case 0: 175 rv = null; 176 break; 177 case 1: 178 rv = (char *) *XPptrv(w); 179 break; 180 default: 181 rv = evalstr(cp, f&~DOGLOB); 182 break; 183 } 184 XPfree(w); 185 return (rv); 186} 187 188/* for nested substitution: ${var:=$var2} */ 189typedef struct SubType { 190 struct tbl *var; /* variable for ${var..} */ 191 struct SubType *prev; /* old type */ 192 struct SubType *next; /* poped type (to avoid re-allocating) */ 193 size_t base; /* begin position of expanded word */ 194 short stype; /* [=+-?%#] action after expanded word */ 195 short f; /* saved value of f (DOPAT, etc) */ 196 uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */ 197 uint8_t quotew; /* saved value of quote (for ${..[+-=]..}) */ 198} SubType; 199 200void 201expand(const char *cp, /* input word */ 202 XPtrV *wp, /* output words */ 203 int f) /* DO* flags */ 204{ 205 int c = 0; 206 int type; /* expansion type */ 207 int quote = 0; /* quoted */ 208 XString ds; /* destination string */ 209 char *dp; /* destination */ 210 const char *sp; /* source */ 211 int fdo, word; /* second pass flags; have word */ 212 int doblank; /* field splitting of parameter/command subst */ 213 Expand x = { 214 /* expansion variables */ 215 NULL, { NULL }, NULL, 0 216 }; 217 SubType st_head, *st; 218 /* For trailing newlines in COMSUB */ 219 int newlines = 0; 220 bool saw_eq, make_magic; 221 int tilde_ok; 222 size_t len; 223 224 if (cp == NULL) 225 internal_errorf("expand(NULL)"); 226 /* for alias, readonly, set, typeset commands */ 227 if ((f & DOVACHECK) && is_wdvarassign(cp)) { 228 f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); 229 f |= DOASNTILDE; 230 } 231 if (Flag(FNOGLOB)) 232 f &= ~DOGLOB; 233 if (Flag(FMARKDIRS)) 234 f |= DOMARKDIRS; 235 if (Flag(FBRACEEXPAND) && (f & DOGLOB)) 236 f |= DOBRACE; 237 238 /* init destination string */ 239 Xinit(ds, dp, 128, ATEMP); 240 type = XBASE; 241 sp = cp; 242 fdo = 0; 243 saw_eq = false; 244 /* must be 1/0 */ 245 tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; 246 doblank = 0; 247 make_magic = false; 248 word = (f&DOBLANK) ? IFS_WS : IFS_WORD; 249 /* clang doesn't know OSUBST comes before CSUBST */ 250 memset(&st_head, 0, sizeof(st_head)); 251 st = &st_head; 252 253 while (/* CONSTCOND */ 1) { 254 Xcheck(ds, dp); 255 256 switch (type) { 257 case XBASE: 258 /* original prefixed string */ 259 c = *sp++; 260 switch (c) { 261 case EOS: 262 c = 0; 263 break; 264 case CHAR: 265 c = *sp++; 266 break; 267 case QCHAR: 268 /* temporary quote */ 269 quote |= 2; 270 c = *sp++; 271 break; 272 case OQUOTE: 273 word = IFS_WORD; 274 tilde_ok = 0; 275 quote = 1; 276 continue; 277 case CQUOTE: 278 quote = st->quotew; 279 continue; 280 case COMSUB: 281 case FUNSUB: 282 tilde_ok = 0; 283 if (f & DONTRUNCOMMAND) { 284 word = IFS_WORD; 285 *dp++ = '$'; 286 if (c == FUNSUB) { 287 *dp++ = '{'; 288 *dp++ = ' '; 289 } else 290 *dp++ = '('; 291 while (*sp != '\0') { 292 Xcheck(ds, dp); 293 *dp++ = *sp++; 294 } 295 if (c == FUNSUB) { 296 *dp++ = ';'; 297 *dp++ = '}'; 298 } else 299 *dp++ = ')'; 300 } else { 301 type = comsub(&x, sp, c); 302 if (type == XCOM && (f&DOBLANK)) 303 doblank++; 304 sp = strnul(sp) + 1; 305 newlines = 0; 306 } 307 continue; 308 case EXPRSUB: 309 word = IFS_WORD; 310 tilde_ok = 0; 311 if (f & DONTRUNCOMMAND) { 312 *dp++ = '$'; *dp++ = '('; *dp++ = '('; 313 while (*sp != '\0') { 314 Xcheck(ds, dp); 315 *dp++ = *sp++; 316 } 317 *dp++ = ')'; *dp++ = ')'; 318 } else { 319 struct tbl v; 320 char *p; 321 322 v.flag = DEFINED|ISSET|INTEGER; 323 /* not default */ 324 v.type = 10; 325 v.name[0] = '\0'; 326 v_evaluate(&v, substitute(sp, 0), 327 KSH_UNWIND_ERROR, true); 328 sp = strnul(sp) + 1; 329 for (p = str_val(&v); *p; ) { 330 Xcheck(ds, dp); 331 *dp++ = *p++; 332 } 333 } 334 continue; 335 case OSUBST: { 336 /* ${{#}var{:}[=+-?#%]word} */ 337 /*- 338 * format is: 339 * OSUBST [{x] plain-variable-part \0 340 * compiled-word-part CSUBST [}x] 341 * This is where all syntax checking gets done... 342 */ 343 /* skip the { or x (}) */ 344 const char *varname = ++sp; 345 int stype; 346 int slen = 0; 347 348 /* skip variable */ 349 sp = cstrchr(sp, '\0') + 1; 350 type = varsub(&x, varname, sp, &stype, &slen); 351 if (type < 0) { 352 char *beg, *end, *str; 353 unwind_substsyn: 354 /* restore sp */ 355 sp = varname - 2; 356 end = (beg = wdcopy(sp, ATEMP)) + 357 (wdscan(sp, CSUBST) - sp); 358 /* ({) the } or x is already skipped */ 359 if (end < wdscan(beg, EOS)) 360 *end = EOS; 361 str = snptreef(NULL, 64, "%S", beg); 362 afree(beg, ATEMP); 363 errorf("%s: %s", str, "bad substitution"); 364 } 365 if (f & DOBLANK) 366 doblank++; 367 tilde_ok = 0; 368 if (type == XBASE) { 369 /* expand? */ 370 if (!st->next) { 371 SubType *newst; 372 373 newst = alloc(sizeof(SubType), ATEMP); 374 newst->next = NULL; 375 newst->prev = st; 376 st->next = newst; 377 } 378 st = st->next; 379 st->stype = stype; 380 st->base = Xsavepos(ds, dp); 381 st->f = f; 382 if (x.var == &vtemp) { 383 st->var = tempvar(); 384 st->var->flag &= ~INTEGER; 385 /* can't fail here */ 386 setstr(st->var, 387 str_val(x.var), 388 KSH_RETURN_ERROR | 0x4); 389 } else 390 st->var = x.var; 391 392 st->quotew = st->quotep = quote; 393 /* skip qualifier(s) */ 394 if (stype) 395 sp += slen; 396 switch (stype & 0x17F) { 397 case 0x100 | '#': 398 { 399 char *beg, *end; 400 mksh_ari_t seed; 401 register uint32_t h; 402 403 beg = wdcopy(sp, ATEMP); 404 end = beg + (wdscan(sp, CSUBST) - sp); 405 end[-2] = EOS; 406 end = wdstrip(beg, 0); 407 afree(beg, ATEMP); 408 evaluate(substitute(end, 0), 409 &seed, KSH_UNWIND_ERROR, true); 410 /* hash with seed, for now */ 411 h = seed; 412 NZATUpdateString(h, 413 str_val(st->var)); 414 NZAATFinish(h); 415 x.str = shf_smprintf("%08X", 416 (unsigned int)h); 417 break; 418 } 419 case 0x100 | 'Q': 420 { 421 struct shf shf; 422 423 shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf); 424 print_value_quoted(&shf, str_val(st->var)); 425 x.str = shf_sclose(&shf); 426 break; 427 } 428 case '0': { 429 char *beg, *mid, *end, *stg; 430 mksh_ari_t from = 0, num = -1, flen, finc = 0; 431 432 beg = wdcopy(sp, ATEMP); 433 mid = beg + (wdscan(sp, ADELIM) - sp); 434 stg = beg + (wdscan(sp, CSUBST) - sp); 435 if (mid >= stg) 436 goto unwind_substsyn; 437 mid[-2] = EOS; 438 if (mid[-1] == /*{*/'}') { 439 sp += mid - beg - 1; 440 end = NULL; 441 } else { 442 end = mid + 443 (wdscan(mid, ADELIM) - mid); 444 if (end >= stg || 445 /* more than max delimiters */ 446 end[-1] != /*{*/ '}') 447 goto unwind_substsyn; 448 end[-2] = EOS; 449 sp += end - beg - 1; 450 } 451 evaluate(substitute(stg = wdstrip(beg, 0), 0), 452 &from, KSH_UNWIND_ERROR, true); 453 afree(stg, ATEMP); 454 if (end) { 455 evaluate(substitute(stg = wdstrip(mid, 0), 0), 456 &num, KSH_UNWIND_ERROR, true); 457 afree(stg, ATEMP); 458 } 459 afree(beg, ATEMP); 460 beg = str_val(st->var); 461 flen = utflen(beg); 462 if (from < 0) { 463 if (-from < flen) 464 finc = flen + from; 465 } else 466 finc = from < flen ? from : flen; 467 if (UTFMODE) 468 utfincptr(beg, &finc); 469 beg += finc; 470 flen = utflen(beg); 471 if (num < 0 || num > flen) 472 num = flen; 473 if (UTFMODE) 474 utfincptr(beg, &num); 475 strndupx(x.str, beg, num, ATEMP); 476 goto do_CSUBST; 477 } 478 case '/': { 479 char *s, *p, *d, *sbeg, *end; 480 char *pat, *rrep; 481 char *tpat0, *tpat1, *tpat2; 482 483 s = wdcopy(sp, ATEMP); 484 p = s + (wdscan(sp, ADELIM) - sp); 485 d = s + (wdscan(sp, CSUBST) - sp); 486 if (p >= d) 487 goto unwind_substsyn; 488 p[-2] = EOS; 489 if (p[-1] == /*{*/'}') 490 d = NULL; 491 else 492 d[-2] = EOS; 493 sp += (d ? d : p) - s - 1; 494 tpat0 = wdstrip(s, 495 WDS_KEEPQ | WDS_MAGIC); 496 pat = substitute(tpat0, 0); 497 if (d) { 498 d = wdstrip(p, WDS_KEEPQ); 499 rrep = substitute(d, 0); 500 afree(d, ATEMP); 501 } else 502 rrep = null; 503 afree(s, ATEMP); 504 s = d = pat; 505 while (*s) 506 if (*s != '\\' || 507 s[1] == '%' || 508 s[1] == '#' || 509 s[1] == '\0' || 510 /* XXX really? */ s[1] == '\\' || 511 s[1] == '/') 512 *d++ = *s++; 513 else 514 s++; 515 *d = '\0'; 516 afree(tpat0, ATEMP); 517 518 /* check for special cases */ 519 d = str_val(st->var); 520 mkssert(d != NULL); 521 switch (*pat) { 522 case '#': 523 /* anchor at begin */ 524 tpat0 = pat + 1; 525 tpat1 = rrep; 526 tpat2 = d; 527 break; 528 case '%': 529 /* anchor at end */ 530 tpat0 = pat + 1; 531 tpat1 = d; 532 tpat2 = rrep; 533 break; 534 case '\0': 535 /* empty pattern */ 536 goto no_repl; 537 default: 538 tpat0 = pat; 539 /* silence gcc */ 540 tpat1 = tpat2 = NULL; 541 } 542 if (gmatchx(null, tpat0, false)) { 543 /* 544 * pattern matches 545 * the empty string 546 */ 547 if (tpat0 == pat) 548 goto no_repl; 549 /* but is anchored */ 550 s = shf_smprintf("%s%s", 551 tpat1, tpat2); 552 goto do_repl; 553 } 554 555 /* prepare string on which to work */ 556 strdupx(s, d, ATEMP); 557 sbeg = s; 558 559 /* first see if we have any match at all */ 560 tpat0 = pat; 561 if (*pat == '#') { 562 /* anchor at the beginning */ 563 tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC); 564 tpat2 = tpat1; 565 } else if (*pat == '%') { 566 /* anchor at the end */ 567 tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0); 568 tpat2 = tpat0; 569 } else { 570 /* float */ 571 tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC); 572 tpat2 = tpat1 + 2; 573 } 574 again_repl: 575 /* 576 * this would not be necessary if gmatchx would return 577 * the start and end values of a match found, like re* 578 */ 579 if (!gmatchx(sbeg, tpat1, false)) 580 goto end_repl; 581 end = strnul(s); 582 /* now anchor the beginning of the match */ 583 if (*pat != '#') 584 while (sbeg <= end) { 585 if (gmatchx(sbeg, tpat2, false)) 586 break; 587 else 588 sbeg++; 589 } 590 /* now anchor the end of the match */ 591 p = end; 592 if (*pat != '%') 593 while (p >= sbeg) { 594 bool gotmatch; 595 596 c = *p; 597 *p = '\0'; 598 gotmatch = tobool(gmatchx(sbeg, tpat0, false)); 599 *p = c; 600 if (gotmatch) 601 break; 602 p--; 603 } 604 strndupx(end, s, sbeg - s, ATEMP); 605 d = shf_smprintf("%s%s%s", end, rrep, p); 606 afree(end, ATEMP); 607 sbeg = d + (sbeg - s) + strlen(rrep); 608 afree(s, ATEMP); 609 s = d; 610 if (stype & 0x80) 611 goto again_repl; 612 end_repl: 613 afree(tpat1, ATEMP); 614 do_repl: 615 x.str = s; 616 no_repl: 617 afree(pat, ATEMP); 618 if (rrep != null) 619 afree(rrep, ATEMP); 620 goto do_CSUBST; 621 } 622 case '#': 623 case '%': 624 /* ! DOBLANK,DOBRACE,DOTILDE */ 625 f = (f & DONTRUNCOMMAND) | 626 DOPAT | DOTEMP; 627 st->quotew = quote = 0; 628 /* 629 * Prepend open pattern (so | 630 * in a trim will work as 631 * expected) 632 */ 633 if (!Flag(FSH)) { 634 *dp++ = MAGIC; 635 *dp++ = '@' | 0x80; 636 } 637 break; 638 case '=': 639 /* 640 * Enabling tilde expansion 641 * after :s here is 642 * non-standard ksh, but is 643 * consistent with rules for 644 * other assignments. Not 645 * sure what POSIX thinks of 646 * this. 647 * Not doing tilde expansion 648 * for integer variables is a 649 * non-POSIX thing - makes 650 * sense though, since ~ is 651 * a arithmetic operator. 652 */ 653 if (!(x.var->flag & INTEGER)) 654 f |= DOASNTILDE|DOTILDE; 655 f |= DOTEMP; 656 /* 657 * These will be done after the 658 * value has been assigned. 659 */ 660 f &= ~(DOBLANK|DOGLOB|DOBRACE); 661 tilde_ok = 1; 662 break; 663 case '?': 664 f &= ~DOBLANK; 665 f |= DOTEMP; 666 /* FALLTHROUGH */ 667 default: 668 /* Enable tilde expansion */ 669 tilde_ok = 1; 670 f |= DOTILDE; 671 } 672 } else 673 /* skip word */ 674 sp += wdscan(sp, CSUBST) - sp; 675 continue; 676 } 677 case CSUBST: 678 /* only get here if expanding word */ 679 do_CSUBST: 680 /* ({) skip the } or x */ 681 sp++; 682 /* in case of ${unset:-} */ 683 tilde_ok = 0; 684 *dp = '\0'; 685 quote = st->quotep; 686 f = st->f; 687 if (f&DOBLANK) 688 doblank--; 689 switch (st->stype & 0x17F) { 690 case '#': 691 case '%': 692 if (!Flag(FSH)) { 693 /* Append end-pattern */ 694 *dp++ = MAGIC; 695 *dp++ = ')'; 696 } 697 *dp = '\0'; 698 dp = Xrestpos(ds, dp, st->base); 699 /* 700 * Must use st->var since calling 701 * global would break things 702 * like x[i+=1]. 703 */ 704 x.str = trimsub(str_val(st->var), 705 dp, st->stype); 706 if (x.str[0] != '\0' || st->quotep) 707 type = XSUB; 708 else 709 type = XNULLSUB; 710 if (f&DOBLANK) 711 doblank++; 712 st = st->prev; 713 continue; 714 case '=': 715 /* 716 * Restore our position and substitute 717 * the value of st->var (may not be 718 * the assigned value in the presence 719 * of integer/right-adj/etc attributes). 720 */ 721 dp = Xrestpos(ds, dp, st->base); 722 /* 723 * Must use st->var since calling 724 * global would cause with things 725 * like x[i+=1] to be evaluated twice. 726 */ 727 /* 728 * Note: not exported by FEXPORT 729 * in AT&T ksh. 730 */ 731 /* 732 * XXX POSIX says readonly is only 733 * fatal for special builtins (setstr 734 * does readonly check). 735 */ 736 len = strlen(dp) + 1; 737 setstr(st->var, 738 debunk(alloc(len, ATEMP), 739 dp, len), KSH_UNWIND_ERROR); 740 x.str = str_val(st->var); 741 type = XSUB; 742 if (f&DOBLANK) 743 doblank++; 744 st = st->prev; 745 continue; 746 case '?': { 747 char *s = Xrestpos(ds, dp, st->base); 748 749 errorf("%s: %s", st->var->name, 750 dp == s ? 751 "parameter null or not set" : 752 (debunk(s, s, strlen(s) + 1), s)); 753 } 754 case '0': 755 case '/': 756 case 0x100 | '#': 757 case 0x100 | 'Q': 758 dp = Xrestpos(ds, dp, st->base); 759 type = XSUB; 760 if (f&DOBLANK) 761 doblank++; 762 st = st->prev; 763 continue; 764 } 765 st = st->prev; 766 type = XBASE; 767 continue; 768 769 case OPAT: 770 /* open pattern: *(foo|bar) */ 771 /* Next char is the type of pattern */ 772 make_magic = true; 773 c = *sp++ | 0x80; 774 break; 775 776 case SPAT: 777 /* pattern separator (|) */ 778 make_magic = true; 779 c = '|'; 780 break; 781 782 case CPAT: 783 /* close pattern */ 784 make_magic = true; 785 c = /*(*/ ')'; 786 break; 787 } 788 break; 789 790 case XNULLSUB: 791 /* 792 * Special case for "$@" (and "${foo[@]}") - no 793 * word is generated if $# is 0 (unless there is 794 * other stuff inside the quotes). 795 */ 796 type = XBASE; 797 if (f&DOBLANK) { 798 doblank--; 799 /* 800 * not really correct: x=; "$x$@" should 801 * generate a null argument and 802 * set A; "${@:+}" shouldn't. 803 */ 804 if (dp == Xstring(ds, dp)) 805 word = IFS_WS; 806 } 807 continue; 808 809 case XSUB: 810 case XSUBMID: 811 if ((c = *x.str++) == 0) { 812 type = XBASE; 813 if (f&DOBLANK) 814 doblank--; 815 continue; 816 } 817 break; 818 819 case XARGSEP: 820 type = XARG; 821 quote = 1; 822 /* FALLTHROUGH */ 823 case XARG: 824 if ((c = *x.str++) == '\0') { 825 /* 826 * force null words to be created so 827 * set -- '' 2 ''; foo "$@" will do 828 * the right thing 829 */ 830 if (quote && x.split) 831 word = IFS_WORD; 832 if ((x.str = *x.u.strv++) == NULL) { 833 type = XBASE; 834 if (f&DOBLANK) 835 doblank--; 836 continue; 837 } 838 c = ifs0; 839 if (c == 0) { 840 if (quote && !x.split) 841 continue; 842 c = ' '; 843 } 844 if (quote && x.split) { 845 /* terminate word for "$@" */ 846 type = XARGSEP; 847 quote = 0; 848 } 849 } 850 break; 851 852 case XCOM: 853 if (newlines) { 854 /* Spit out saved NLs */ 855 c = '\n'; 856 --newlines; 857 } else { 858 while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') 859 if (c == '\n') 860 /* Save newlines */ 861 newlines++; 862 if (newlines && c != EOF) { 863 shf_ungetc(c, x.u.shf); 864 c = '\n'; 865 --newlines; 866 } 867 } 868 if (c == EOF) { 869 newlines = 0; 870 shf_close(x.u.shf); 871 if (x.split) 872 subst_exstat = waitlast(); 873 type = XBASE; 874 if (f&DOBLANK) 875 doblank--; 876 continue; 877 } 878 break; 879 } 880 881 /* check for end of word or IFS separation */ 882 if (c == 0 || (!quote && (f & DOBLANK) && doblank && 883 !make_magic && ctype(c, C_IFS))) { 884 /*- 885 * How words are broken up: 886 * | value of c 887 * word | ws nws 0 888 * ----------------------------------- 889 * IFS_WORD w/WS w/NWS w 890 * IFS_WS -/WS w/NWS - 891 * IFS_NWS -/NWS w/NWS w 892 * (w means generate a word) 893 * Note that IFS_NWS/0 generates a word (AT&T ksh 894 * doesn't do this, but POSIX does). 895 */ 896 if (word == IFS_WORD || 897 (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) { 898 char *p; 899 900 *dp++ = '\0'; 901 p = Xclose(ds, dp); 902 if (fdo & DOBRACE) 903 /* also does globbing */ 904 alt_expand(wp, p, p, 905 p + Xlength(ds, (dp - 1)), 906 fdo | (f & DOMARKDIRS)); 907 else if (fdo & DOGLOB) 908 glob(p, wp, tobool(f & DOMARKDIRS)); 909 else if ((f & DOPAT) || !(fdo & DOMAGIC)) 910 XPput(*wp, p); 911 else 912 XPput(*wp, debunk(p, p, strlen(p) + 1)); 913 fdo = 0; 914 saw_eq = false; 915 tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; 916 if (c == 0) 917 return; 918 Xinit(ds, dp, 128, ATEMP); 919 } else if (c == 0) { 920 return; 921 } else if (type == XSUB && ctype(c, C_IFS) && 922 !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) { 923 char *p; 924 925 *(p = alloc(1, ATEMP)) = '\0'; 926 XPput(*wp, p); 927 type = XSUBMID; 928 } 929 if (word != IFS_NWS) 930 word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS; 931 } else { 932 if (type == XSUB) { 933 if (word == IFS_NWS && 934 Xlength(ds, dp) == 0) { 935 char *p; 936 937 *(p = alloc(1, ATEMP)) = '\0'; 938 XPput(*wp, p); 939 } 940 type = XSUBMID; 941 } 942 943 /* age tilde_ok info - ~ code tests second bit */ 944 tilde_ok <<= 1; 945 /* mark any special second pass chars */ 946 if (!quote) 947 switch (c) { 948 case '[': 949 case '!': 950 case '-': 951 case ']': 952 /* 953 * For character classes - doesn't hurt 954 * to have magic !,-,]s outside of 955 * [...] expressions. 956 */ 957 if (f & (DOPAT | DOGLOB)) { 958 fdo |= DOMAGIC; 959 if (c == '[') 960 fdo |= f & DOGLOB; 961 *dp++ = MAGIC; 962 } 963 break; 964 case '*': 965 case '?': 966 if (f & (DOPAT | DOGLOB)) { 967 fdo |= DOMAGIC | (f & DOGLOB); 968 *dp++ = MAGIC; 969 } 970 break; 971 case '{': 972 case '}': 973 case ',': 974 if ((f & DOBRACE) && (c == '{' /*}*/ || 975 (fdo & DOBRACE))) { 976 fdo |= DOBRACE|DOMAGIC; 977 *dp++ = MAGIC; 978 } 979 break; 980 case '=': 981 /* Note first unquoted = for ~ */ 982 if (!(f & DOTEMP) && !saw_eq && 983 (Flag(FBRACEEXPAND) || 984 (f & DOASNTILDE))) { 985 saw_eq = true; 986 tilde_ok = 1; 987 } 988 break; 989 case ':': 990 /* : */ 991 /* Note unquoted : for ~ */ 992 if (!(f & DOTEMP) && (f & DOASNTILDE)) 993 tilde_ok = 1; 994 break; 995 case '~': 996 /* 997 * tilde_ok is reset whenever 998 * any of ' " $( $(( ${ } are seen. 999 * Note that tilde_ok must be preserved 1000 * through the sequence ${A=a=}~ 1001 */ 1002 if (type == XBASE && 1003 (f & (DOTILDE|DOASNTILDE)) && 1004 (tilde_ok & 2)) { 1005 const char *p; 1006 char *dp_x; 1007 1008 dp_x = dp; 1009 p = maybe_expand_tilde(sp, 1010 &ds, &dp_x, 1011 f & DOASNTILDE); 1012 if (p) { 1013 if (dp != dp_x) 1014 word = IFS_WORD; 1015 dp = dp_x; 1016 sp = p; 1017 continue; 1018 } 1019 } 1020 break; 1021 } 1022 else 1023 /* undo temporary */ 1024 quote &= ~2; 1025 1026 if (make_magic) { 1027 make_magic = false; 1028 fdo |= DOMAGIC | (f & DOGLOB); 1029 *dp++ = MAGIC; 1030 } else if (ISMAGIC(c)) { 1031 fdo |= DOMAGIC; 1032 *dp++ = MAGIC; 1033 } 1034 /* save output char */ 1035 *dp++ = c; 1036 word = IFS_WORD; 1037 } 1038 } 1039} 1040 1041/* 1042 * Prepare to generate the string returned by ${} substitution. 1043 */ 1044static int 1045varsub(Expand *xp, const char *sp, const char *word, 1046 int *stypep, /* becomes qualifier type */ 1047 int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */ 1048{ 1049 int c; 1050 int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */ 1051 int stype; /* substitution type */ 1052 int slen; 1053 const char *p; 1054 struct tbl *vp; 1055 bool zero_ok = false; 1056 1057 if ((stype = sp[0]) == '\0') 1058 /* Bad variable name */ 1059 return (-1); 1060 1061 xp->var = NULL; 1062 1063 /*- 1064 * ${#var}, string length (-U: characters, +U: octets) or array size 1065 * ${%var}, string width (-U: screen columns, +U: octets) 1066 */ 1067 c = sp[1]; 1068 if (stype == '%' && c == '\0') 1069 return (-1); 1070 if ((stype == '#' || stype == '%') && c != '\0') { 1071 /* Can't have any modifiers for ${#...} or ${%...} */ 1072 if (*word != CSUBST) 1073 return (-1); 1074 sp++; 1075 /* Check for size of array */ 1076 if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') && 1077 p[2] == ']') { 1078 int n = 0; 1079 1080 if (stype != '#') 1081 return (-1); 1082 vp = global(arrayname(sp)); 1083 if (vp->flag & (ISSET|ARRAY)) 1084 zero_ok = true; 1085 for (; vp; vp = vp->u.array) 1086 if (vp->flag & ISSET) 1087 n++; 1088 c = n; 1089 } else if (c == '*' || c == '@') { 1090 if (stype != '#') 1091 return (-1); 1092 c = e->loc->argc; 1093 } else { 1094 p = str_val(global(sp)); 1095 zero_ok = p != null; 1096 if (stype == '#') 1097 c = utflen(p); 1098 else { 1099 /* partial utf_mbswidth reimplementation */ 1100 const char *s = p; 1101 unsigned int wc; 1102 size_t len; 1103 int cw; 1104 1105 c = 0; 1106 while (*s) { 1107 if (!UTFMODE || (len = utf_mbtowc(&wc, 1108 s)) == (size_t)-1) 1109 /* not UTFMODE or not UTF-8 */ 1110 wc = (unsigned char)(*s++); 1111 else 1112 /* UTFMODE and UTF-8 */ 1113 s += len; 1114 /* wc == char or wchar at s++ */ 1115 if ((cw = utf_wcwidth(wc)) == -1) { 1116 /* 646, 8859-1, 10646 C0/C1 */ 1117 c = -1; 1118 break; 1119 } 1120 c += cw; 1121 } 1122 } 1123 } 1124 if (Flag(FNOUNSET) && c == 0 && !zero_ok) 1125 errorf("%s: %s", sp, "parameter not set"); 1126 /* unqualified variable/string substitution */ 1127 *stypep = 0; 1128 xp->str = shf_smprintf("%d", c); 1129 return (XSUB); 1130 } 1131 1132 /* Check for qualifiers in word part */ 1133 stype = 0; 1134 c = word[slen = 0] == CHAR ? word[1] : 0; 1135 if (c == ':') { 1136 slen += 2; 1137 stype = 0x80; 1138 c = word[slen + 0] == CHAR ? word[slen + 1] : 0; 1139 } 1140 if (!stype && c == '/') { 1141 slen += 2; 1142 stype = c; 1143 if (word[slen] == ADELIM) { 1144 slen += 2; 1145 stype |= 0x80; 1146 } 1147 } else if (stype == 0x80 && (c == ' ' || c == '0')) { 1148 stype |= '0'; 1149 } else if (ctype(c, C_SUBOP1)) { 1150 slen += 2; 1151 stype |= c; 1152 } else if (ctype(c, C_SUBOP2)) { 1153 /* Note: ksh88 allows :%, :%%, etc */ 1154 slen += 2; 1155 stype = c; 1156 if (word[slen + 0] == CHAR && c == word[slen + 1]) { 1157 stype |= 0x80; 1158 slen += 2; 1159 } 1160 } else if (c == '@') { 1161 /* @x where x is command char */ 1162 slen += 2; 1163 stype |= 0x100; 1164 if (word[slen] == CHAR) { 1165 stype |= word[slen + 1]; 1166 slen += 2; 1167 } 1168 } else if (stype) 1169 /* : is not ok */ 1170 return (-1); 1171 if (!stype && *word != CSUBST) 1172 return (-1); 1173 *stypep = stype; 1174 *slenp = slen; 1175 1176 c = sp[0]; 1177 if (c == '*' || c == '@') { 1178 switch (stype & 0x17F) { 1179 case '=': /* can't assign to a vector */ 1180 case '%': /* can't trim a vector (yet) */ 1181 case '#': 1182 case '0': 1183 case '/': 1184 case 0x100 | '#': 1185 case 0x100 | 'Q': 1186 return (-1); 1187 } 1188 if (e->loc->argc == 0) { 1189 xp->str = null; 1190 xp->var = global(sp); 1191 state = c == '@' ? XNULLSUB : XSUB; 1192 } else { 1193 xp->u.strv = (const char **)e->loc->argv + 1; 1194 xp->str = *xp->u.strv++; 1195 /* $@ */ 1196 xp->split = tobool(c == '@'); 1197 state = XARG; 1198 } 1199 /* POSIX 2009? */ 1200 zero_ok = true; 1201 } else { 1202 if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') && 1203 p[2] == ']') { 1204 XPtrV wv; 1205 1206 switch (stype & 0x17F) { 1207 case '=': /* can't assign to a vector */ 1208 case '%': /* can't trim a vector (yet) */ 1209 case '#': 1210 case '?': 1211 case '0': 1212 case '/': 1213 case 0x100 | '#': 1214 case 0x100 | 'Q': 1215 return (-1); 1216 } 1217 XPinit(wv, 32); 1218 if ((c = sp[0]) == '!') 1219 ++sp; 1220 vp = global(arrayname(sp)); 1221 for (; vp; vp = vp->u.array) { 1222 if (!(vp->flag&ISSET)) 1223 continue; 1224 XPput(wv, c == '!' ? shf_smprintf("%lu", 1225 arrayindex(vp)) : 1226 str_val(vp)); 1227 } 1228 if (XPsize(wv) == 0) { 1229 xp->str = null; 1230 state = p[1] == '@' ? XNULLSUB : XSUB; 1231 XPfree(wv); 1232 } else { 1233 XPput(wv, 0); 1234 xp->u.strv = (const char **)XPptrv(wv); 1235 xp->str = *xp->u.strv++; 1236 /* ${foo[@]} */ 1237 xp->split = tobool(p[1] == '@'); 1238 state = XARG; 1239 } 1240 } else { 1241 /* Can't assign things like $! or $1 */ 1242 if ((stype & 0x17F) == '=' && 1243 ctype(*sp, C_VAR1 | C_DIGIT)) 1244 return (-1); 1245 if (*sp == '!' && sp[1]) { 1246 ++sp; 1247 xp->var = global(sp); 1248 if (vstrchr(sp, '[')) { 1249 if (xp->var->flag & ISSET) 1250 xp->str = shf_smprintf("%lu", 1251 arrayindex(xp->var)); 1252 else 1253 xp->str = null; 1254 } else if (xp->var->flag & ISSET) 1255 xp->str = xp->var->name; 1256 else 1257 /* ksh93 compat */ 1258 xp->str = "0"; 1259 } else { 1260 xp->var = global(sp); 1261 xp->str = str_val(xp->var); 1262 } 1263 state = XSUB; 1264 } 1265 } 1266 1267 c = stype & 0x7F; 1268 /* test the compiler's code generator */ 1269 if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' || 1270 (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ 1271 c == '=' || c == '-' || c == '?' : c == '+'))) || 1272 stype == (0x80 | '0') || stype == (0x100 | '#') || 1273 stype == (0x100 | 'Q')) 1274 /* expand word instead of variable value */ 1275 state = XBASE; 1276 if (Flag(FNOUNSET) && xp->str == null && !zero_ok && 1277 (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) 1278 errorf("%s: %s", sp, "parameter not set"); 1279 return (state); 1280} 1281 1282/* 1283 * Run the command in $(...) and read its output. 1284 */ 1285static int 1286comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED) 1287{ 1288 Source *s, *sold; 1289 struct op *t; 1290 struct shf *shf; 1291 uint8_t old_utfmode = UTFMODE; 1292 1293 s = pushs(SSTRING, ATEMP); 1294 s->start = s->str = cp; 1295 sold = source; 1296 t = compile(s, true); 1297 afree(s, ATEMP); 1298 source = sold; 1299 1300 UTFMODE = old_utfmode; 1301 1302 if (t == NULL) 1303 return (XBASE); 1304 1305 /* no waitlast() unless specifically enabled later */ 1306 xp->split = false; 1307 1308 if (t->type == TCOM && 1309 *t->args == NULL && *t->vars == NULL && t->ioact != NULL) { 1310 /* $(<file) */ 1311 struct ioword *io = *t->ioact; 1312 char *name; 1313 1314 if ((io->flag & IOTYPE) != IOREAD) 1315 errorf("%s: %s", "funny $() command", 1316 snptreef(NULL, 32, "%R", io)); 1317 shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, 1318 SHF_MAPHI|SHF_CLEXEC); 1319 if (shf == NULL) 1320 errorf("%s: %s %s", name, "can't open", "$() input"); 1321 } else if (fn == FUNSUB) { 1322 int ofd1; 1323 struct temp *tf = NULL; 1324 1325 /* create a temporary file, open for writing */ 1326 maketemp(ATEMP, TT_FUNSUB, &tf); 1327 if (!tf->shf) { 1328 errorf("can't %s temporary file %s: %s", 1329 "create", tf->tffn, cstrerror(errno)); 1330 } 1331 /* save stdout and make the temporary file it */ 1332 ofd1 = savefd(1); 1333 ksh_dup2(shf_fileno(tf->shf), 1, false); 1334 /* 1335 * run tree, with output thrown into the tempfile, 1336 * in a new function block 1337 */ 1338 funsub(t); 1339 subst_exstat = exstat & 0xFF; 1340 /* close the tempfile and restore regular stdout */ 1341 shf_close(tf->shf); 1342 restfd(1, ofd1); 1343 /* now open, unlink and free the tempfile for reading */ 1344 shf = shf_open(tf->tffn, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC); 1345 unlink(tf->tffn); 1346 afree(tf, ATEMP); 1347 } else { 1348 int ofd1, pv[2]; 1349 1350 openpipe(pv); 1351 shf = shf_fdopen(pv[0], SHF_RD, NULL); 1352 ofd1 = savefd(1); 1353 if (pv[1] != 1) { 1354 ksh_dup2(pv[1], 1, false); 1355 close(pv[1]); 1356 } 1357 execute(t, XXCOM | XPIPEO | XFORK, NULL); 1358 restfd(1, ofd1); 1359 startlast(); 1360 /* waitlast() */ 1361 xp->split = true; 1362 } 1363 1364 xp->u.shf = shf; 1365 return (XCOM); 1366} 1367 1368/* 1369 * perform #pattern and %pattern substitution in ${} 1370 */ 1371static char * 1372trimsub(char *str, char *pat, int how) 1373{ 1374 char *end = strnul(str); 1375 char *p, c; 1376 1377 switch (how & 0xFF) { 1378 case '#': 1379 /* shortest match at beginning */ 1380 for (p = str; p <= end; p += utf_ptradj(p)) { 1381 c = *p; *p = '\0'; 1382 if (gmatchx(str, pat, false)) { 1383 *p = c; 1384 return (p); 1385 } 1386 *p = c; 1387 } 1388 break; 1389 case '#'|0x80: 1390 /* longest match at beginning */ 1391 for (p = end; p >= str; p--) { 1392 c = *p; *p = '\0'; 1393 if (gmatchx(str, pat, false)) { 1394 *p = c; 1395 return (p); 1396 } 1397 *p = c; 1398 } 1399 break; 1400 case '%': 1401 /* shortest match at end */ 1402 p = end; 1403 while (p >= str) { 1404 if (gmatchx(p, pat, false)) 1405 goto trimsub_match; 1406 if (UTFMODE) { 1407 char *op = p; 1408 while ((p-- > str) && ((*p & 0xC0) == 0x80)) 1409 ; 1410 if ((p < str) || (p + utf_ptradj(p) != op)) 1411 p = op - 1; 1412 } else 1413 --p; 1414 } 1415 break; 1416 case '%'|0x80: 1417 /* longest match at end */ 1418 for (p = str; p <= end; p++) 1419 if (gmatchx(p, pat, false)) { 1420 trimsub_match: 1421 strndupx(end, str, p - str, ATEMP); 1422 return (end); 1423 } 1424 break; 1425 } 1426 1427 /* no match, return string */ 1428 return (str); 1429} 1430 1431/* 1432 * glob 1433 * Name derived from V6's /etc/glob, the program that expanded filenames. 1434 */ 1435 1436/* XXX cp not const 'cause slashes are temporarily replaced with NULs... */ 1437static void 1438glob(char *cp, XPtrV *wp, bool markdirs) 1439{ 1440 int oldsize = XPsize(*wp); 1441 1442 if (glob_str(cp, wp, markdirs) == 0) 1443 XPput(*wp, debunk(cp, cp, strlen(cp) + 1)); 1444 else 1445 qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize, 1446 sizeof(void *), xstrcmp); 1447} 1448 1449#define GF_NONE 0 1450#define GF_EXCHECK BIT(0) /* do existence check on file */ 1451#define GF_GLOBBED BIT(1) /* some globbing has been done */ 1452#define GF_MARKDIR BIT(2) /* add trailing / to directories */ 1453 1454/* 1455 * Apply file globbing to cp and store the matching files in wp. Returns 1456 * the number of matches found. 1457 */ 1458int 1459glob_str(char *cp, XPtrV *wp, bool markdirs) 1460{ 1461 int oldsize = XPsize(*wp); 1462 XString xs; 1463 char *xp; 1464 1465 Xinit(xs, xp, 256, ATEMP); 1466 globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE); 1467 Xfree(xs, xp); 1468 1469 return (XPsize(*wp) - oldsize); 1470} 1471 1472static void 1473globit(XString *xs, /* dest string */ 1474 char **xpp, /* ptr to dest end */ 1475 char *sp, /* source path */ 1476 XPtrV *wp, /* output list */ 1477 int check) /* GF_* flags */ 1478{ 1479 char *np; /* next source component */ 1480 char *xp = *xpp; 1481 char *se; 1482 char odirsep; 1483 1484 /* This to allow long expansions to be interrupted */ 1485 intrcheck(); 1486 1487 if (sp == NULL) { 1488 /* end of source path */ 1489 /* 1490 * We only need to check if the file exists if a pattern 1491 * is followed by a non-pattern (eg, foo*x/bar; no check 1492 * is needed for foo* since the match must exist) or if 1493 * any patterns were expanded and the markdirs option is set. 1494 * Symlinks make things a bit tricky... 1495 */ 1496 if ((check & GF_EXCHECK) || 1497 ((check & GF_MARKDIR) && (check & GF_GLOBBED))) { 1498#define stat_check() (stat_done ? stat_done : \ 1499 (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \ 1500 ? -1 : 1)) 1501 struct stat lstatb, statb; 1502 int stat_done = 0; /* -1: failed, 1 ok */ 1503 1504 if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0) 1505 return; 1506 /* 1507 * special case for systems which strip trailing 1508 * slashes from regular files (eg, /etc/passwd/). 1509 * SunOS 4.1.3 does this... 1510 */ 1511 if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) && 1512 xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) && 1513 (!S_ISLNK(lstatb.st_mode) || 1514 stat_check() < 0 || !S_ISDIR(statb.st_mode))) 1515 return; 1516 /* 1517 * Possibly tack on a trailing / if there isn't already 1518 * one and if the file is a directory or a symlink to a 1519 * directory 1520 */ 1521 if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) && 1522 xp > Xstring(*xs, xp) && xp[-1] != '/' && 1523 (S_ISDIR(lstatb.st_mode) || 1524 (S_ISLNK(lstatb.st_mode) && stat_check() > 0 && 1525 S_ISDIR(statb.st_mode)))) { 1526 *xp++ = '/'; 1527 *xp = '\0'; 1528 } 1529 } 1530 strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP); 1531 XPput(*wp, np); 1532 return; 1533 } 1534 1535 if (xp > Xstring(*xs, xp)) 1536 *xp++ = '/'; 1537 while (*sp == '/') { 1538 Xcheck(*xs, xp); 1539 *xp++ = *sp++; 1540 } 1541 np = strchr(sp, '/'); 1542 if (np != NULL) { 1543 se = np; 1544 /* don't assume '/', can be multiple kinds */ 1545 odirsep = *np; 1546 *np++ = '\0'; 1547 } else { 1548 odirsep = '\0'; /* keep gcc quiet */ 1549 se = sp + strlen(sp); 1550 } 1551 1552 1553 /* 1554 * Check if sp needs globbing - done to avoid pattern checks for strings 1555 * containing MAGIC characters, open [s without the matching close ], 1556 * etc. (otherwise opendir() will be called which may fail because the 1557 * directory isn't readable - if no globbing is needed, only execute 1558 * permission should be required (as per POSIX)). 1559 */ 1560 if (!has_globbing(sp, se)) { 1561 XcheckN(*xs, xp, se - sp + 1); 1562 debunk(xp, sp, Xnleft(*xs, xp)); 1563 xp += strlen(xp); 1564 *xpp = xp; 1565 globit(xs, xpp, np, wp, check); 1566 } else { 1567 DIR *dirp; 1568 struct dirent *d; 1569 char *name; 1570 size_t len, prefix_len; 1571 1572 /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */ 1573 *xp = '\0'; 1574 prefix_len = Xlength(*xs, xp); 1575 dirp = opendir(prefix_len ? Xstring(*xs, xp) : "."); 1576 if (dirp == NULL) 1577 goto Nodir; 1578 while ((d = readdir(dirp)) != NULL) { 1579 name = d->d_name; 1580 if (name[0] == '.' && 1581 (name[1] == 0 || (name[1] == '.' && name[2] == 0))) 1582 /* always ignore . and .. */ 1583 continue; 1584 if ((*name == '.' && *sp != '.') || 1585 !gmatchx(name, sp, true)) 1586 continue; 1587 1588 len = strlen(d->d_name) + 1; 1589 XcheckN(*xs, xp, len); 1590 memcpy(xp, name, len); 1591 *xpp = xp + len - 1; 1592 globit(xs, xpp, np, wp, 1593 (check & GF_MARKDIR) | GF_GLOBBED 1594 | (np ? GF_EXCHECK : GF_NONE)); 1595 xp = Xstring(*xs, xp) + prefix_len; 1596 } 1597 closedir(dirp); 1598 Nodir: 1599 ; 1600 } 1601 1602 if (np != NULL) 1603 *--np = odirsep; 1604} 1605 1606/* remove MAGIC from string */ 1607char * 1608debunk(char *dp, const char *sp, size_t dlen) 1609{ 1610 char *d; 1611 const char *s; 1612 1613 if ((s = cstrchr(sp, MAGIC))) { 1614 if (s - sp >= (ssize_t)dlen) 1615 return (dp); 1616 memmove(dp, sp, s - sp); 1617 for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++) 1618 if (!ISMAGIC(*s) || !(*++s & 0x80) || 1619 !vstrchr("*+?@! ", *s & 0x7f)) 1620 *d++ = *s; 1621 else { 1622 /* extended pattern operators: *+?@! */ 1623 if ((*s & 0x7f) != ' ') 1624 *d++ = *s & 0x7f; 1625 if (d - dp < (ssize_t)dlen) 1626 *d++ = '('; 1627 } 1628 *d = '\0'; 1629 } else if (dp != sp) 1630 strlcpy(dp, sp, dlen); 1631 return (dp); 1632} 1633 1634/* 1635 * Check if p is an unquoted name, possibly followed by a / or :. If so 1636 * puts the expanded version in *dcp,dp and returns a pointer in p just 1637 * past the name, otherwise returns 0. 1638 */ 1639static const char * 1640maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign) 1641{ 1642 XString ts; 1643 char *dp = *dpp; 1644 char *tp; 1645 const char *r; 1646 1647 Xinit(ts, tp, 16, ATEMP); 1648 /* : only for DOASNTILDE form */ 1649 while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':')) 1650 { 1651 Xcheck(ts, tp); 1652 *tp++ = p[1]; 1653 p += 2; 1654 } 1655 *tp = '\0'; 1656 r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? 1657 tilde(Xstring(ts, tp)) : NULL; 1658 Xfree(ts, tp); 1659 if (r) { 1660 while (*r) { 1661 Xcheck(*dsp, dp); 1662 if (ISMAGIC(*r)) 1663 *dp++ = MAGIC; 1664 *dp++ = *r++; 1665 } 1666 *dpp = dp; 1667 r = p; 1668 } 1669 return (r); 1670} 1671 1672/* 1673 * tilde expansion 1674 * 1675 * based on a version by Arnold Robbins 1676 */ 1677 1678char * 1679tilde(char *cp) 1680{ 1681 char *dp = null; 1682 1683 if (cp[0] == '\0') 1684 dp = str_val(global("HOME")); 1685 else if (cp[0] == '+' && cp[1] == '\0') 1686 dp = str_val(global("PWD")); 1687 else if (cp[0] == '-' && cp[1] == '\0') 1688 dp = str_val(global("OLDPWD")); 1689#ifndef MKSH_NOPWNAM 1690 else 1691 dp = homedir(cp); 1692#endif 1693 /* If HOME, PWD or OLDPWD are not set, don't expand ~ */ 1694 return (dp == null ? NULL : dp); 1695} 1696 1697#ifndef MKSH_NOPWNAM 1698/* 1699 * map userid to user's home directory. 1700 * note that 4.3's getpw adds more than 6K to the shell, 1701 * and the YP version probably adds much more. 1702 * we might consider our own version of getpwnam() to keep the size down. 1703 */ 1704static char * 1705homedir(char *name) 1706{ 1707 struct tbl *ap; 1708 1709 ap = ktenter(&homedirs, name, hash(name)); 1710 if (!(ap->flag & ISSET)) { 1711 struct passwd *pw; 1712 1713 pw = getpwnam(name); 1714 if (pw == NULL) 1715 return (NULL); 1716 strdupx(ap->val.s, pw->pw_dir, APERM); 1717 ap->flag |= DEFINED|ISSET|ALLOC; 1718 } 1719 return (ap->val.s); 1720} 1721#endif 1722 1723static void 1724alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo) 1725{ 1726 int count = 0; 1727 char *brace_start, *brace_end, *comma = NULL; 1728 char *field_start; 1729 char *p; 1730 1731 /* search for open brace */ 1732 for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != '{' /*}*/; p += 2) 1733 ; 1734 brace_start = p; 1735 1736 /* find matching close brace, if any */ 1737 if (p) { 1738 comma = NULL; 1739 count = 1; 1740 for (p += 2; *p && count; p++) { 1741 if (ISMAGIC(*p)) { 1742 if (*++p == '{' /*}*/) 1743 count++; 1744 else if (*p == /*{*/ '}') 1745 --count; 1746 else if (*p == ',' && count == 1) 1747 comma = p; 1748 } 1749 } 1750 } 1751 /* no valid expansions... */ 1752 if (!p || count != 0) { 1753 /* 1754 * Note that given a{{b,c} we do not expand anything (this is 1755 * what AT&T ksh does. This may be changed to do the {b,c} 1756 * expansion. } 1757 */ 1758 if (fdo & DOGLOB) 1759 glob(start, wp, tobool(fdo & DOMARKDIRS)); 1760 else 1761 XPput(*wp, debunk(start, start, end - start)); 1762 return; 1763 } 1764 brace_end = p; 1765 if (!comma) { 1766 alt_expand(wp, start, brace_end, end, fdo); 1767 return; 1768 } 1769 1770 /* expand expression */ 1771 field_start = brace_start + 2; 1772 count = 1; 1773 for (p = brace_start + 2; p != brace_end; p++) { 1774 if (ISMAGIC(*p)) { 1775 if (*++p == '{' /*}*/) 1776 count++; 1777 else if ((*p == /*{*/ '}' && --count == 0) || 1778 (*p == ',' && count == 1)) { 1779 char *news; 1780 int l1, l2, l3; 1781 1782 /* 1783 * addition safe since these operate on 1784 * one string (separate substrings) 1785 */ 1786 l1 = brace_start - start; 1787 l2 = (p - 1) - field_start; 1788 l3 = end - brace_end; 1789 news = alloc(l1 + l2 + l3 + 1, ATEMP); 1790 memcpy(news, start, l1); 1791 memcpy(news + l1, field_start, l2); 1792 memcpy(news + l1 + l2, brace_end, l3); 1793 news[l1 + l2 + l3] = '\0'; 1794 alt_expand(wp, news, news + l1, 1795 news + l1 + l2 + l3, fdo); 1796 field_start = p + 1; 1797 } 1798 } 1799 } 1800 return; 1801} 1802 1803/* helper function due to setjmp/longjmp woes */ 1804static void 1805funsub(struct op *t) 1806{ 1807 newblock(); 1808 e->type = E_FUNC; 1809 if (!kshsetjmp(e->jbuf)) 1810 execute(t, XXCOM | XERROK, NULL); 1811 popblock(); 1812} 1813