1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char sccsid[] = "@(#)expand.c 8.2 (Berkeley) 10/22/93"; 13 #endif /* not lint */ 14 15 /* 16 * Routines to expand arguments to commands. We have to deal with 17 * backquotes, shell variables, and file metacharacters. 18 */ 19 20 #include "shell.h" 21 #include "main.h" 22 #include "nodes.h" 23 #include "eval.h" 24 #include "expand.h" 25 #include "syntax.h" 26 #include "parser.h" 27 #include "jobs.h" 28 #include "options.h" 29 #include "var.h" 30 #include "input.h" 31 #include "output.h" 32 #include "memalloc.h" 33 #include "error.h" 34 #include "mystring.h" 35 #include <sys/types.h> 36 #include <sys/time.h> 37 #include <sys/stat.h> 38 #include <errno.h> 39 #include <dirent.h> 40 #include <pwd.h> 41 42 /* 43 * Structure specifying which parts of the string should be searched 44 * for IFS characters. 45 */ 46 47 struct ifsregion { 48 struct ifsregion *next; /* next region in list */ 49 int begoff; /* offset of start of region */ 50 int endoff; /* offset of end of region */ 51 int nulonly; /* search for nul bytes only */ 52 }; 53 54 55 char *expdest; /* output of current string */ 56 struct nodelist *argbackq; /* list of back quote expressions */ 57 struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 58 struct ifsregion *ifslastp; /* last struct in list */ 59 struct arglist exparg; /* holds expanded arg list */ 60 61 #ifdef __STDC__ 62 STATIC void argstr(char *, int); 63 STATIC void expbackq(union node *, int, int); 64 STATIC char *evalvar(char *, int); 65 STATIC int varisset(int); 66 STATIC void varvalue(int, int, int); 67 STATIC void recordregion(int, int, int); 68 STATIC void ifsbreakup(char *, struct arglist *); 69 STATIC void expandmeta(struct strlist *, int); 70 STATIC void expmeta(char *, char *); 71 STATIC void expari(int); 72 STATIC void addfname(char *); 73 STATIC struct strlist *expsort(struct strlist *); 74 STATIC struct strlist *msort(struct strlist *, int); 75 STATIC int pmatch(char *, char *); 76 STATIC char *exptilde(char *, int); 77 #else 78 STATIC void argstr(); 79 STATIC void expbackq(); 80 STATIC char *evalvar(); 81 STATIC int varisset(); 82 STATIC void varvalue(); 83 STATIC void recordregion(); 84 STATIC void ifsbreakup(); 85 STATIC void expandmeta(); 86 STATIC void expmeta(); 87 STATIC void expari(); 88 STATIC void addfname(); 89 STATIC struct strlist *expsort(); 90 STATIC struct strlist *msort(); 91 STATIC int pmatch(); 92 STATIC char *exptilde(); 93 #endif 94 95 /* 96 * Expand shell variables and backquotes inside a here document. 97 */ 98 99 void 100 expandhere(arg, fd) 101 union node *arg; /* the document */ 102 int fd; /* where to write the expanded version */ 103 { 104 herefd = fd; 105 expandarg(arg, (struct arglist *)NULL, 0); 106 xwrite(fd, stackblock(), expdest - stackblock()); 107 } 108 109 110 /* 111 * Perform variable substitution and command substitution on an argument, 112 * placing the resulting list of arguments in arglist. If EXP_FULL is true, 113 * perform splitting and file name expansion. When arglist is NULL, perform 114 * here document expansion. 115 */ 116 117 void 118 expandarg(arg, arglist, flag) 119 union node *arg; 120 struct arglist *arglist; 121 { 122 struct strlist *sp; 123 char *p; 124 125 argbackq = arg->narg.backquote; 126 STARTSTACKSTR(expdest); 127 ifsfirst.next = NULL; 128 ifslastp = NULL; 129 argstr(arg->narg.text, flag); 130 if (arglist == NULL) { 131 return; /* here document expanded */ 132 } 133 STPUTC('\0', expdest); 134 p = grabstackstr(expdest); 135 exparg.lastp = &exparg.list; 136 /* 137 * TODO - EXP_REDIR 138 */ 139 if (flag & EXP_FULL) { 140 ifsbreakup(p, &exparg); 141 *exparg.lastp = NULL; 142 exparg.lastp = &exparg.list; 143 expandmeta(exparg.list, flag); 144 } else { 145 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 146 rmescapes(p); 147 sp = (struct strlist *)stalloc(sizeof (struct strlist)); 148 sp->text = p; 149 *exparg.lastp = sp; 150 exparg.lastp = &sp->next; 151 } 152 while (ifsfirst.next != NULL) { 153 struct ifsregion *ifsp; 154 INTOFF; 155 ifsp = ifsfirst.next->next; 156 ckfree(ifsfirst.next); 157 ifsfirst.next = ifsp; 158 INTON; 159 } 160 *exparg.lastp = NULL; 161 if (exparg.list) { 162 *arglist->lastp = exparg.list; 163 arglist->lastp = exparg.lastp; 164 } 165 } 166 167 168 169 /* 170 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 171 * characters to allow for further processing. Otherwise treat 172 * $@ like $* since no splitting will be performed. 173 */ 174 175 STATIC void 176 argstr(p, flag) 177 register char *p; 178 { 179 register char c; 180 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 181 int firsteq = 1; 182 183 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 184 p = exptilde(p, flag); 185 for (;;) { 186 switch (c = *p++) { 187 case '\0': 188 case CTLENDVAR: /* ??? */ 189 goto breakloop; 190 case CTLESC: 191 if (quotes) 192 STPUTC(c, expdest); 193 c = *p++; 194 STPUTC(c, expdest); 195 break; 196 case CTLVAR: 197 p = evalvar(p, flag); 198 break; 199 case CTLBACKQ: 200 case CTLBACKQ|CTLQUOTE: 201 expbackq(argbackq->n, c & CTLQUOTE, flag); 202 argbackq = argbackq->next; 203 break; 204 case CTLENDARI: 205 expari(flag); 206 break; 207 case ':': 208 case '=': 209 /* 210 * sort of a hack - expand tildes in variable 211 * assignments (after the first '=' and after ':'s). 212 */ 213 STPUTC(c, expdest); 214 if (flag & EXP_VARTILDE && *p == '~') { 215 if (c == '=') { 216 if (firsteq) 217 firsteq = 0; 218 else 219 break; 220 } 221 p = exptilde(p, flag); 222 } 223 break; 224 default: 225 STPUTC(c, expdest); 226 } 227 } 228 breakloop:; 229 } 230 231 STATIC char * 232 exptilde(p, flag) 233 char *p; 234 { 235 char c, *startp = p; 236 struct passwd *pw; 237 char *home; 238 int quotes = flag & (EXP_FULL | EXP_CASE); 239 240 while (c = *p) { 241 switch(c) { 242 case CTLESC: 243 return (startp); 244 case ':': 245 if (flag & EXP_VARTILDE) 246 goto done; 247 break; 248 case '/': 249 goto done; 250 } 251 p++; 252 } 253 done: 254 *p = '\0'; 255 if (*(startp+1) == '\0') { 256 if ((home = lookupvar("HOME")) == NULL) 257 goto lose; 258 } else { 259 if ((pw = getpwnam(startp+1)) == NULL) 260 goto lose; 261 home = pw->pw_dir; 262 } 263 if (*home == '\0') 264 goto lose; 265 *p = c; 266 while (c = *home++) { 267 if (quotes && SQSYNTAX[c] == CCTL) 268 STPUTC(CTLESC, expdest); 269 STPUTC(c, expdest); 270 } 271 return (p); 272 lose: 273 *p = c; 274 return (startp); 275 } 276 277 278 /* 279 * Expand arithmetic expression. Backup to start of expression, 280 * evaluate, place result in (backed up) result, adjust string position. 281 */ 282 void 283 expari(flag) 284 { 285 char *p, *start; 286 int result; 287 int quotes = flag & (EXP_FULL | EXP_CASE); 288 289 /* 290 * This routine is slightly over-compilcated for 291 * efficiency. First we make sure there is 292 * enough space for the result, which may be bigger 293 * than the expression if we add exponentation. Next we 294 * scan backwards looking for the start of arithmetic. If the 295 * next previous character is a CTLESC character, then we 296 * have to rescan starting from the beginning since CTLESC 297 * characters have to be processed left to right. 298 */ 299 CHECKSTRSPACE(8, expdest); 300 USTPUTC('\0', expdest); 301 start = stackblock(); 302 p = expdest; 303 while (*p != CTLARI && p >= start) 304 --p; 305 if (*p != CTLARI) 306 error("missing CTLARI (shouldn't happen)"); 307 if (p > start && *(p-1) == CTLESC) 308 for (p = start; *p != CTLARI; p++) 309 if (*p == CTLESC) 310 p++; 311 if (quotes) 312 rmescapes(p+1); 313 result = arith(p+1); 314 fmtstr(p, 10, "%d", result); 315 while (*p++) 316 ; 317 result = expdest - p + 1; 318 STADJUST(-result, expdest); 319 } 320 321 322 /* 323 * Expand stuff in backwards quotes. 324 */ 325 326 STATIC void 327 expbackq(cmd, quoted, flag) 328 union node *cmd; 329 { 330 struct backcmd in; 331 int i; 332 char buf[128]; 333 char *p; 334 char *dest = expdest; 335 struct ifsregion saveifs, *savelastp; 336 struct nodelist *saveargbackq; 337 char lastc; 338 int startloc = dest - stackblock(); 339 char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 340 int saveherefd; 341 int quotes = flag & (EXP_FULL | EXP_CASE); 342 343 INTOFF; 344 saveifs = ifsfirst; 345 savelastp = ifslastp; 346 saveargbackq = argbackq; 347 saveherefd = herefd; 348 herefd = -1; 349 p = grabstackstr(dest); 350 evalbackcmd(cmd, &in); 351 ungrabstackstr(p, dest); 352 ifsfirst = saveifs; 353 ifslastp = savelastp; 354 argbackq = saveargbackq; 355 herefd = saveherefd; 356 357 p = in.buf; 358 lastc = '\0'; 359 for (;;) { 360 if (--in.nleft < 0) { 361 if (in.fd < 0) 362 break; 363 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 364 TRACE(("expbackq: read returns %d\n", i)); 365 if (i <= 0) 366 break; 367 p = buf; 368 in.nleft = i - 1; 369 } 370 lastc = *p++; 371 if (lastc != '\0') { 372 if (quotes && syntax[lastc] == CCTL) 373 STPUTC(CTLESC, dest); 374 STPUTC(lastc, dest); 375 } 376 } 377 if (lastc == '\n') { 378 STUNPUTC(dest); 379 } 380 if (in.fd >= 0) 381 close(in.fd); 382 if (in.buf) 383 ckfree(in.buf); 384 if (in.jp) 385 exitstatus = waitforjob(in.jp); 386 if (quoted == 0) 387 recordregion(startloc, dest - stackblock(), 0); 388 TRACE(("evalbackq: size=%d: \"%.*s\"\n", 389 (dest - stackblock()) - startloc, 390 (dest - stackblock()) - startloc, 391 stackblock() + startloc)); 392 expdest = dest; 393 INTON; 394 } 395 396 397 398 /* 399 * Expand a variable, and return a pointer to the next character in the 400 * input string. 401 */ 402 403 STATIC char * 404 evalvar(p, flag) 405 char *p; 406 { 407 int subtype; 408 int varflags; 409 char *var; 410 char *val; 411 int c; 412 int set; 413 int special; 414 int startloc; 415 int quotes = flag & (EXP_FULL | EXP_CASE); 416 417 varflags = *p++; 418 subtype = varflags & VSTYPE; 419 var = p; 420 special = 0; 421 if (! is_name(*p)) 422 special = 1; 423 p = strchr(p, '=') + 1; 424 again: /* jump here after setting a variable with ${var=text} */ 425 if (special) { 426 set = varisset(*var); 427 val = NULL; 428 } else { 429 val = lookupvar(var); 430 if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 431 val = NULL; 432 set = 0; 433 } else 434 set = 1; 435 } 436 startloc = expdest - stackblock(); 437 if (set && subtype != VSPLUS) { 438 /* insert the value of the variable */ 439 if (special) { 440 varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 441 } else { 442 char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; 443 444 while (*val) { 445 if (quotes && syntax[*val] == CCTL) 446 STPUTC(CTLESC, expdest); 447 STPUTC(*val++, expdest); 448 } 449 } 450 } 451 if (subtype == VSPLUS) 452 set = ! set; 453 if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) 454 && (set || subtype == VSNORMAL)) 455 recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); 456 if (! set && subtype != VSNORMAL) { 457 if (subtype == VSPLUS || subtype == VSMINUS) { 458 argstr(p, flag); 459 } else { 460 char *startp; 461 int saveherefd = herefd; 462 struct nodelist *saveargbackq = argbackq; 463 herefd = -1; 464 argstr(p, 0); 465 STACKSTRNUL(expdest); 466 herefd = saveherefd; 467 argbackq = saveargbackq; 468 startp = stackblock() + startloc; 469 if (subtype == VSASSIGN) { 470 setvar(var, startp, 0); 471 STADJUST(startp - expdest, expdest); 472 varflags &=~ VSNUL; 473 goto again; 474 } 475 /* subtype == VSQUESTION */ 476 if (*p != CTLENDVAR) { 477 outfmt(&errout, "%s\n", startp); 478 error((char *)NULL); 479 } 480 error("%.*s: parameter %snot set", p - var - 1, 481 var, (varflags & VSNUL)? "null or " : nullstr); 482 } 483 } 484 if (subtype != VSNORMAL) { /* skip to end of alternative */ 485 int nesting = 1; 486 for (;;) { 487 if ((c = *p++) == CTLESC) 488 p++; 489 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 490 if (set) 491 argbackq = argbackq->next; 492 } else if (c == CTLVAR) { 493 if ((*p++ & VSTYPE) != VSNORMAL) 494 nesting++; 495 } else if (c == CTLENDVAR) { 496 if (--nesting == 0) 497 break; 498 } 499 } 500 } 501 return p; 502 } 503 504 505 506 /* 507 * Test whether a specialized variable is set. 508 */ 509 510 STATIC int 511 varisset(name) 512 char name; 513 { 514 char **ap; 515 516 if (name == '!') { 517 if (backgndpid == -1) 518 return 0; 519 } else if (name == '@' || name == '*') { 520 if (*shellparam.p == NULL) 521 return 0; 522 } else if ((unsigned)(name -= '1') <= '9' - '1') { 523 ap = shellparam.p; 524 do { 525 if (*ap++ == NULL) 526 return 0; 527 } while (--name >= 0); 528 } 529 return 1; 530 } 531 532 533 534 /* 535 * Add the value of a specialized variable to the stack string. 536 */ 537 538 STATIC void 539 varvalue(name, quoted, allow_split) 540 char name; 541 { 542 int num; 543 char temp[32]; 544 char *p; 545 int i; 546 extern int exitstatus; 547 char sep; 548 char **ap; 549 char const *syntax; 550 551 #define STRTODEST(p) \ 552 do {\ 553 if (allow_split) { \ 554 syntax = quoted? DQSYNTAX : BASESYNTAX; \ 555 while (*p) { \ 556 if (syntax[*p] == CCTL) \ 557 STPUTC(CTLESC, expdest); \ 558 STPUTC(*p++, expdest); \ 559 } \ 560 } else \ 561 while (*p) \ 562 STPUTC(*p++, expdest); \ 563 } while (0) 564 565 566 switch (name) { 567 case '$': 568 num = rootpid; 569 goto numvar; 570 case '?': 571 num = exitstatus; 572 goto numvar; 573 case '#': 574 num = shellparam.nparam; 575 goto numvar; 576 case '!': 577 num = backgndpid; 578 numvar: 579 p = temp + 31; 580 temp[31] = '\0'; 581 do { 582 *--p = num % 10 + '0'; 583 } while ((num /= 10) != 0); 584 while (*p) 585 STPUTC(*p++, expdest); 586 break; 587 case '-': 588 for (i = 0 ; i < NOPTS ; i++) { 589 if (optlist[i].val) 590 STPUTC(optlist[i].letter, expdest); 591 } 592 break; 593 case '@': 594 if (allow_split) { 595 sep = '\0'; 596 goto allargs; 597 } 598 /* fall through */ 599 case '*': 600 sep = ' '; 601 allargs: 602 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 603 STRTODEST(p); 604 if (*ap) 605 STPUTC(sep, expdest); 606 } 607 break; 608 case '0': 609 p = arg0; 610 STRTODEST(p); 611 break; 612 default: 613 if ((unsigned)(name -= '1') <= '9' - '1') { 614 p = shellparam.p[name]; 615 STRTODEST(p); 616 } 617 break; 618 } 619 } 620 621 622 623 /* 624 * Record the the fact that we have to scan this region of the 625 * string for IFS characters. 626 */ 627 628 STATIC void 629 recordregion(start, end, nulonly) { 630 register struct ifsregion *ifsp; 631 632 if (ifslastp == NULL) { 633 ifsp = &ifsfirst; 634 } else { 635 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 636 ifslastp->next = ifsp; 637 } 638 ifslastp = ifsp; 639 ifslastp->next = NULL; 640 ifslastp->begoff = start; 641 ifslastp->endoff = end; 642 ifslastp->nulonly = nulonly; 643 } 644 645 646 647 /* 648 * Break the argument string into pieces based upon IFS and add the 649 * strings to the argument list. The regions of the string to be 650 * searched for IFS characters have been stored by recordregion. 651 */ 652 653 STATIC void 654 ifsbreakup(string, arglist) 655 char *string; 656 struct arglist *arglist; 657 { 658 struct ifsregion *ifsp; 659 struct strlist *sp; 660 char *start; 661 register char *p; 662 char *q; 663 char *ifs; 664 665 start = string; 666 if (ifslastp != NULL) { 667 ifsp = &ifsfirst; 668 do { 669 p = string + ifsp->begoff; 670 ifs = ifsp->nulonly? nullstr : ifsval(); 671 while (p < string + ifsp->endoff) { 672 q = p; 673 if (*p == CTLESC) 674 p++; 675 if (strchr(ifs, *p++)) { 676 if (q > start || *ifs != ' ') { 677 *q = '\0'; 678 sp = (struct strlist *)stalloc(sizeof *sp); 679 sp->text = start; 680 *arglist->lastp = sp; 681 arglist->lastp = &sp->next; 682 } 683 if (*ifs == ' ') { 684 for (;;) { 685 if (p >= string + ifsp->endoff) 686 break; 687 q = p; 688 if (*p == CTLESC) 689 p++; 690 if (strchr(ifs, *p++) == NULL) { 691 p = q; 692 break; 693 } 694 } 695 } 696 start = p; 697 } 698 } 699 } while ((ifsp = ifsp->next) != NULL); 700 if (*start || (*ifs != ' ' && start > string)) { 701 sp = (struct strlist *)stalloc(sizeof *sp); 702 sp->text = start; 703 *arglist->lastp = sp; 704 arglist->lastp = &sp->next; 705 } 706 } else { 707 sp = (struct strlist *)stalloc(sizeof *sp); 708 sp->text = start; 709 *arglist->lastp = sp; 710 arglist->lastp = &sp->next; 711 } 712 } 713 714 715 716 /* 717 * Expand shell metacharacters. At this point, the only control characters 718 * should be escapes. The results are stored in the list exparg. 719 */ 720 721 char *expdir; 722 723 724 STATIC void 725 expandmeta(str, flag) 726 struct strlist *str; 727 { 728 char *p; 729 struct strlist **savelastp; 730 struct strlist *sp; 731 char c; 732 /* TODO - EXP_REDIR */ 733 734 while (str) { 735 if (fflag) 736 goto nometa; 737 p = str->text; 738 for (;;) { /* fast check for meta chars */ 739 if ((c = *p++) == '\0') 740 goto nometa; 741 if (c == '*' || c == '?' || c == '[' || c == '!') 742 break; 743 } 744 savelastp = exparg.lastp; 745 INTOFF; 746 if (expdir == NULL) { 747 int i = strlen(str->text); 748 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 749 } 750 751 expmeta(expdir, str->text); 752 ckfree(expdir); 753 expdir = NULL; 754 INTON; 755 if (exparg.lastp == savelastp) { 756 /* 757 * no matches 758 */ 759 nometa: 760 *exparg.lastp = str; 761 rmescapes(str->text); 762 exparg.lastp = &str->next; 763 } else { 764 *exparg.lastp = NULL; 765 *savelastp = sp = expsort(*savelastp); 766 while (sp->next != NULL) 767 sp = sp->next; 768 exparg.lastp = &sp->next; 769 } 770 str = str->next; 771 } 772 } 773 774 775 /* 776 * Do metacharacter (i.e. *, ?, [...]) expansion. 777 */ 778 779 STATIC void 780 expmeta(enddir, name) 781 char *enddir; 782 char *name; 783 { 784 register char *p; 785 char *q; 786 char *start; 787 char *endname; 788 int metaflag; 789 struct stat statb; 790 DIR *dirp; 791 struct dirent *dp; 792 int atend; 793 int matchdot; 794 795 metaflag = 0; 796 start = name; 797 for (p = name ; ; p++) { 798 if (*p == '*' || *p == '?') 799 metaflag = 1; 800 else if (*p == '[') { 801 q = p + 1; 802 if (*q == '!') 803 q++; 804 for (;;) { 805 if (*q == CTLESC) 806 q++; 807 if (*q == '/' || *q == '\0') 808 break; 809 if (*++q == ']') { 810 metaflag = 1; 811 break; 812 } 813 } 814 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 815 metaflag = 1; 816 } else if (*p == '\0') 817 break; 818 else if (*p == CTLESC) 819 p++; 820 if (*p == '/') { 821 if (metaflag) 822 break; 823 start = p + 1; 824 } 825 } 826 if (metaflag == 0) { /* we've reached the end of the file name */ 827 if (enddir != expdir) 828 metaflag++; 829 for (p = name ; ; p++) { 830 if (*p == CTLESC) 831 p++; 832 *enddir++ = *p; 833 if (*p == '\0') 834 break; 835 } 836 if (metaflag == 0 || stat(expdir, &statb) >= 0) 837 addfname(expdir); 838 return; 839 } 840 endname = p; 841 if (start != name) { 842 p = name; 843 while (p < start) { 844 if (*p == CTLESC) 845 p++; 846 *enddir++ = *p++; 847 } 848 } 849 if (enddir == expdir) { 850 p = "."; 851 } else if (enddir == expdir + 1 && *expdir == '/') { 852 p = "/"; 853 } else { 854 p = expdir; 855 enddir[-1] = '\0'; 856 } 857 if ((dirp = opendir(p)) == NULL) 858 return; 859 if (enddir != expdir) 860 enddir[-1] = '/'; 861 if (*endname == 0) { 862 atend = 1; 863 } else { 864 atend = 0; 865 *endname++ = '\0'; 866 } 867 matchdot = 0; 868 if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 869 matchdot++; 870 while (! int_pending() && (dp = readdir(dirp)) != NULL) { 871 if (dp->d_name[0] == '.' && ! matchdot) 872 continue; 873 if (patmatch(start, dp->d_name)) { 874 if (atend) { 875 scopy(dp->d_name, enddir); 876 addfname(expdir); 877 } else { 878 char *q; 879 for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 880 p[-1] = '/'; 881 expmeta(p, endname); 882 } 883 } 884 } 885 closedir(dirp); 886 if (! atend) 887 endname[-1] = '/'; 888 } 889 890 891 /* 892 * Add a file name to the list. 893 */ 894 895 STATIC void 896 addfname(name) 897 char *name; 898 { 899 char *p; 900 struct strlist *sp; 901 902 p = stalloc(strlen(name) + 1); 903 scopy(name, p); 904 sp = (struct strlist *)stalloc(sizeof *sp); 905 sp->text = p; 906 *exparg.lastp = sp; 907 exparg.lastp = &sp->next; 908 } 909 910 911 /* 912 * Sort the results of file name expansion. It calculates the number of 913 * strings to sort and then calls msort (short for merge sort) to do the 914 * work. 915 */ 916 917 STATIC struct strlist * 918 expsort(str) 919 struct strlist *str; 920 { 921 int len; 922 struct strlist *sp; 923 924 len = 0; 925 for (sp = str ; sp ; sp = sp->next) 926 len++; 927 return msort(str, len); 928 } 929 930 931 STATIC struct strlist * 932 msort(list, len) 933 struct strlist *list; 934 { 935 struct strlist *p, *q; 936 struct strlist **lpp; 937 int half; 938 int n; 939 940 if (len <= 1) 941 return list; 942 half = len >> 1; 943 p = list; 944 for (n = half ; --n >= 0 ; ) { 945 q = p; 946 p = p->next; 947 } 948 q->next = NULL; /* terminate first half of list */ 949 q = msort(list, half); /* sort first half of list */ 950 p = msort(p, len - half); /* sort second half */ 951 lpp = &list; 952 for (;;) { 953 if (strcmp(p->text, q->text) < 0) { 954 *lpp = p; 955 lpp = &p->next; 956 if ((p = *lpp) == NULL) { 957 *lpp = q; 958 break; 959 } 960 } else { 961 *lpp = q; 962 lpp = &q->next; 963 if ((q = *lpp) == NULL) { 964 *lpp = p; 965 break; 966 } 967 } 968 } 969 return list; 970 } 971 972 973 974 /* 975 * Returns true if the pattern matches the string. 976 */ 977 978 int 979 patmatch(pattern, string) 980 char *pattern; 981 char *string; 982 { 983 #ifdef notdef 984 if (pattern[0] == '!' && pattern[1] == '!') 985 return 1 - pmatch(pattern + 2, string); 986 else 987 #endif 988 return pmatch(pattern, string); 989 } 990 991 992 STATIC int 993 pmatch(pattern, string) 994 char *pattern; 995 char *string; 996 { 997 register char *p, *q; 998 register char c; 999 1000 p = pattern; 1001 q = string; 1002 for (;;) { 1003 switch (c = *p++) { 1004 case '\0': 1005 goto breakloop; 1006 case CTLESC: 1007 if (*q++ != *p++) 1008 return 0; 1009 break; 1010 case '?': 1011 if (*q++ == '\0') 1012 return 0; 1013 break; 1014 case '*': 1015 c = *p; 1016 if (c != CTLESC && c != '?' && c != '*' && c != '[') { 1017 while (*q != c) { 1018 if (*q == '\0') 1019 return 0; 1020 q++; 1021 } 1022 } 1023 do { 1024 if (pmatch(p, q)) 1025 return 1; 1026 } while (*q++ != '\0'); 1027 return 0; 1028 case '[': { 1029 char *endp; 1030 int invert, found; 1031 char chr; 1032 1033 endp = p; 1034 if (*endp == '!') 1035 endp++; 1036 for (;;) { 1037 if (*endp == '\0') 1038 goto dft; /* no matching ] */ 1039 if (*endp == CTLESC) 1040 endp++; 1041 if (*++endp == ']') 1042 break; 1043 } 1044 invert = 0; 1045 if (*p == '!') { 1046 invert++; 1047 p++; 1048 } 1049 found = 0; 1050 chr = *q++; 1051 c = *p++; 1052 do { 1053 if (c == CTLESC) 1054 c = *p++; 1055 if (*p == '-' && p[1] != ']') { 1056 p++; 1057 if (*p == CTLESC) 1058 p++; 1059 if (chr >= c && chr <= *p) 1060 found = 1; 1061 p++; 1062 } else { 1063 if (chr == c) 1064 found = 1; 1065 } 1066 } while ((c = *p++) != ']'); 1067 if (found == invert) 1068 return 0; 1069 break; 1070 } 1071 dft: default: 1072 if (*q++ != c) 1073 return 0; 1074 break; 1075 } 1076 } 1077 breakloop: 1078 if (*q != '\0') 1079 return 0; 1080 return 1; 1081 } 1082 1083 1084 1085 /* 1086 * Remove any CTLESC characters from a string. 1087 */ 1088 1089 void 1090 rmescapes(str) 1091 char *str; 1092 { 1093 register char *p, *q; 1094 1095 p = str; 1096 while (*p != CTLESC) { 1097 if (*p++ == '\0') 1098 return; 1099 } 1100 q = p; 1101 while (*p) { 1102 if (*p == CTLESC) 1103 p++; 1104 *q++ = *p++; 1105 } 1106 *q = '\0'; 1107 } 1108 1109 1110 1111 /* 1112 * See if a pattern matches in a case statement. 1113 */ 1114 1115 int 1116 casematch(pattern, val) 1117 union node *pattern; 1118 char *val; 1119 { 1120 struct stackmark smark; 1121 int result; 1122 char *p; 1123 1124 setstackmark(&smark); 1125 argbackq = pattern->narg.backquote; 1126 STARTSTACKSTR(expdest); 1127 ifslastp = NULL; 1128 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1129 STPUTC('\0', expdest); 1130 p = grabstackstr(expdest); 1131 result = patmatch(p, val); 1132 popstackmark(&smark); 1133 return result; 1134 } 1135