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