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