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