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