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