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