1 /* $NetBSD: expand.c,v 1.29 1997/03/18 18:54:40 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.29 1997/03/18 18:54:40 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((char *, int)); 104 STATIC void varvalue __P((char *, 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 char *p; 199 int flag; 200 { 201 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 while (ifsfirst.next != NULL) { 314 struct ifsregion *ifsp; 315 INTOFF; 316 ifsp = ifsfirst.next->next; 317 ckfree(ifsfirst.next); 318 ifsfirst.next = ifsp; 319 INTON; 320 } 321 ifslastp = NULL; 322 323 /* 324 * This routine is slightly over-compilcated for 325 * efficiency. First we make sure there is 326 * enough space for the result, which may be bigger 327 * than the expression if we add exponentation. Next we 328 * scan backwards looking for the start of arithmetic. If the 329 * next previous character is a CTLESC character, then we 330 * have to rescan starting from the beginning since CTLESC 331 * characters have to be processed left to right. 332 */ 333 CHECKSTRSPACE(8, expdest); 334 USTPUTC('\0', expdest); 335 start = stackblock(); 336 p = expdest; 337 while (*p != CTLARI && p >= start) 338 --p; 339 if (*p != CTLARI) 340 error("missing CTLARI (shouldn't happen)"); 341 if (p > start && *(p-1) == CTLESC) 342 for (p = start; *p != CTLARI; p++) 343 if (*p == CTLESC) 344 p++; 345 if (quotes) 346 rmescapes(p+1); 347 result = arith(p+1); 348 fmtstr(p, 10, "%d", result); 349 350 while (*p++) 351 ; 352 353 result = expdest - p + 1; 354 STADJUST(-result, expdest); 355 } 356 357 358 /* 359 * Expand stuff in backwards quotes. 360 */ 361 362 STATIC void 363 expbackq(cmd, quoted, flag) 364 union node *cmd; 365 int quoted; 366 int flag; 367 { 368 struct backcmd in; 369 int i; 370 char buf[128]; 371 char *p; 372 char *dest = expdest; 373 struct ifsregion saveifs, *savelastp; 374 struct nodelist *saveargbackq; 375 char lastc; 376 int startloc = dest - stackblock(); 377 char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 378 int saveherefd; 379 int quotes = flag & (EXP_FULL | EXP_CASE); 380 381 INTOFF; 382 saveifs = ifsfirst; 383 savelastp = ifslastp; 384 saveargbackq = argbackq; 385 saveherefd = herefd; 386 herefd = -1; 387 p = grabstackstr(dest); 388 evalbackcmd(cmd, &in); 389 ungrabstackstr(p, dest); 390 ifsfirst = saveifs; 391 ifslastp = savelastp; 392 argbackq = saveargbackq; 393 herefd = saveherefd; 394 395 p = in.buf; 396 lastc = '\0'; 397 for (;;) { 398 if (--in.nleft < 0) { 399 if (in.fd < 0) 400 break; 401 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 402 TRACE(("expbackq: read returns %d\n", i)); 403 if (i <= 0) 404 break; 405 p = buf; 406 in.nleft = i - 1; 407 } 408 lastc = *p++; 409 if (lastc != '\0') { 410 if (quotes && syntax[lastc] == CCTL) 411 STPUTC(CTLESC, dest); 412 STPUTC(lastc, dest); 413 } 414 } 415 416 /* Eat all trailing newlines */ 417 for (p--; lastc == '\n'; lastc = *--p) 418 STUNPUTC(dest); 419 420 if (in.fd >= 0) 421 close(in.fd); 422 if (in.buf) 423 ckfree(in.buf); 424 if (in.jp) 425 exitstatus = waitforjob(in.jp); 426 if (quoted == 0) 427 recordregion(startloc, dest - stackblock(), 0); 428 TRACE(("evalbackq: size=%d: \"%.*s\"\n", 429 (dest - stackblock()) - startloc, 430 (dest - stackblock()) - startloc, 431 stackblock() + startloc)); 432 expdest = dest; 433 INTON; 434 } 435 436 437 438 STATIC int 439 subevalvar(p, str, strloc, subtype, startloc, varflags) 440 char *p; 441 char *str; 442 int strloc; 443 int subtype; 444 int startloc; 445 int varflags; 446 { 447 char *startp; 448 char *loc = NULL; 449 int c = 0; 450 int saveherefd = herefd; 451 struct nodelist *saveargbackq = argbackq; 452 int amount; 453 454 herefd = -1; 455 argstr(p, 0); 456 STACKSTRNUL(expdest); 457 herefd = saveherefd; 458 argbackq = saveargbackq; 459 startp = stackblock() + startloc; 460 if (str == NULL) 461 str = stackblock() + strloc; 462 463 switch (subtype) { 464 case VSASSIGN: 465 setvar(str, startp, 0); 466 amount = startp - expdest; 467 STADJUST(amount, expdest); 468 varflags &= ~VSNUL; 469 if (c != 0) 470 *loc = c; 471 return 1; 472 473 case VSQUESTION: 474 if (*p != CTLENDVAR) { 475 outfmt(&errout, "%s\n", startp); 476 error((char *)NULL); 477 } 478 error("%.*s: parameter %snot set", p - str - 1, 479 str, (varflags & VSNUL) ? "null or " 480 : nullstr); 481 return 0; 482 483 case VSTRIMLEFT: 484 for (loc = startp; loc < str; 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 VSTRIMLEFTMAX: 496 for (loc = str - 1; loc >= startp; loc--) { 497 c = *loc; 498 *loc = '\0'; 499 if (patmatch(str, startp)) { 500 *loc = c; 501 goto recordleft; 502 } 503 *loc = c; 504 } 505 return 0; 506 507 case VSTRIMRIGHT: 508 for (loc = str - 1; loc >= startp; loc--) { 509 if (patmatch(str, loc)) { 510 amount = loc - expdest; 511 STADJUST(amount, expdest); 512 return 1; 513 } 514 } 515 return 0; 516 517 case VSTRIMRIGHTMAX: 518 for (loc = startp; loc < str - 1; loc++) { 519 if (patmatch(str, loc)) { 520 amount = loc - expdest; 521 STADJUST(amount, expdest); 522 return 1; 523 } 524 } 525 return 0; 526 527 528 default: 529 abort(); 530 } 531 532 recordleft: 533 amount = ((str - 1) - (loc - startp)) - expdest; 534 STADJUST(amount, expdest); 535 while (loc != str - 1) 536 *startp++ = *loc++; 537 return 1; 538 } 539 540 541 /* 542 * Expand a variable, and return a pointer to the next character in the 543 * input string. 544 */ 545 546 STATIC char * 547 evalvar(p, flag) 548 char *p; 549 int flag; 550 { 551 int subtype; 552 int varflags; 553 char *var; 554 char *val; 555 char *pat; 556 int c; 557 int set; 558 int special; 559 int startloc; 560 int varlen; 561 int easy; 562 int quotes = flag & (EXP_FULL | EXP_CASE); 563 564 varflags = *p++; 565 subtype = varflags & VSTYPE; 566 var = p; 567 special = 0; 568 if (! is_name(*p)) 569 special = 1; 570 p = strchr(p, '=') + 1; 571 again: /* jump here after setting a variable with ${var=text} */ 572 if (special) { 573 set = varisset(var, varflags & VSNUL); 574 val = NULL; 575 } else { 576 val = lookupvar(var); 577 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 578 val = NULL; 579 set = 0; 580 } else 581 set = 1; 582 } 583 varlen = 0; 584 startloc = expdest - stackblock(); 585 if (set && subtype != VSPLUS) { 586 /* insert the value of the variable */ 587 if (special) { 588 varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); 589 if (subtype == VSLENGTH) { 590 varlen = expdest - stackblock() - startloc; 591 STADJUST(-varlen, expdest); 592 } 593 } else { 594 char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 595 : BASESYNTAX; 596 597 if (subtype == VSLENGTH) { 598 for (;*val; val++) 599 varlen++; 600 } 601 else { 602 while (*val) { 603 if (quotes && syntax[*val] == CCTL) 604 STPUTC(CTLESC, expdest); 605 STPUTC(*val++, expdest); 606 } 607 608 } 609 } 610 } 611 612 if (subtype == VSPLUS) 613 set = ! set; 614 615 easy = ((varflags & VSQUOTE) == 0 || 616 (*var == '@' && shellparam.nparam != 1)); 617 618 619 switch (subtype) { 620 case VSLENGTH: 621 expdest = cvtnum(varlen, expdest); 622 goto record; 623 624 case VSNORMAL: 625 if (!easy) 626 break; 627 record: 628 recordregion(startloc, expdest - stackblock(), 629 varflags & VSQUOTE); 630 break; 631 632 case VSPLUS: 633 case VSMINUS: 634 if (!set) { 635 argstr(p, flag); 636 break; 637 } 638 if (easy) 639 goto record; 640 break; 641 642 case VSTRIMLEFT: 643 case VSTRIMLEFTMAX: 644 case VSTRIMRIGHT: 645 case VSTRIMRIGHTMAX: 646 if (!set) 647 break; 648 /* 649 * Terminate the string and start recording the pattern 650 * right after it 651 */ 652 STPUTC('\0', expdest); 653 pat = expdest; 654 if (subevalvar(p, NULL, expdest - stackblock(), subtype, 655 startloc, varflags)) 656 goto record; 657 else { 658 int amount = (expdest - pat) + 1; 659 STADJUST(-amount, expdest); 660 } 661 break; 662 663 case VSASSIGN: 664 case VSQUESTION: 665 if (!set) { 666 if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 667 varflags &= ~VSNUL; 668 goto again; 669 } 670 break; 671 } 672 if (easy) 673 goto record; 674 break; 675 676 default: 677 abort(); 678 } 679 680 if (subtype != VSNORMAL) { /* skip to end of alternative */ 681 int nesting = 1; 682 for (;;) { 683 if ((c = *p++) == CTLESC) 684 p++; 685 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 686 if (set) 687 argbackq = argbackq->next; 688 } else if (c == CTLVAR) { 689 if ((*p++ & VSTYPE) != VSNORMAL) 690 nesting++; 691 } else if (c == CTLENDVAR) { 692 if (--nesting == 0) 693 break; 694 } 695 } 696 } 697 return p; 698 } 699 700 701 702 /* 703 * Test whether a specialized variable is set. 704 */ 705 706 STATIC int 707 varisset(name, nulok) 708 char *name; 709 int nulok; 710 { 711 if (*name == '!') 712 return backgndpid != -1; 713 else if (*name == '@' || *name == '*') { 714 if (*shellparam.p == NULL) 715 return 0; 716 717 if (nulok) { 718 char **av; 719 720 for (av = shellparam.p; *av; av++) 721 if (**av != '\0') 722 return 1; 723 return 0; 724 } 725 } else if (is_digit(*name)) { 726 char *ap; 727 int num = atoi(name); 728 729 if (num > shellparam.nparam) 730 return 0; 731 732 if (num == 0) 733 ap = arg0; 734 else 735 ap = shellparam.p[num - 1]; 736 737 if (nulok && (ap == NULL || *ap == '\0')) 738 return 0; 739 } 740 return 1; 741 } 742 743 744 745 /* 746 * Add the value of a specialized variable to the stack string. 747 */ 748 749 STATIC void 750 varvalue(name, quoted, allow_split) 751 char *name; 752 int quoted; 753 int allow_split; 754 { 755 int num; 756 char *p; 757 int i; 758 extern int oexitstatus; 759 char sep; 760 char **ap; 761 char const *syntax; 762 763 #define STRTODEST(p) \ 764 do {\ 765 if (allow_split) { \ 766 syntax = quoted? DQSYNTAX : BASESYNTAX; \ 767 while (*p) { \ 768 if (syntax[*p] == CCTL) \ 769 STPUTC(CTLESC, expdest); \ 770 STPUTC(*p++, expdest); \ 771 } \ 772 } else \ 773 while (*p) \ 774 STPUTC(*p++, expdest); \ 775 } while (0) 776 777 778 switch (*name) { 779 case '$': 780 num = rootpid; 781 goto numvar; 782 case '?': 783 num = oexitstatus; 784 goto numvar; 785 case '#': 786 num = shellparam.nparam; 787 goto numvar; 788 case '!': 789 num = backgndpid; 790 numvar: 791 expdest = cvtnum(num, expdest); 792 break; 793 case '-': 794 for (i = 0 ; i < NOPTS ; i++) { 795 if (optlist[i].val) 796 STPUTC(optlist[i].letter, expdest); 797 } 798 break; 799 case '@': 800 if (allow_split) { 801 sep = '\0'; 802 goto allargs; 803 } 804 /* fall through */ 805 case '*': 806 sep = ' '; 807 allargs: 808 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 809 STRTODEST(p); 810 if (*ap) 811 STPUTC(sep, expdest); 812 } 813 break; 814 case '0': 815 p = arg0; 816 STRTODEST(p); 817 break; 818 default: 819 if (is_digit(*name)) { 820 num = atoi(name); 821 if (num > 0 && num <= shellparam.nparam) { 822 p = shellparam.p[num - 1]; 823 STRTODEST(p); 824 } 825 } 826 break; 827 } 828 } 829 830 831 832 /* 833 * Record the the fact that we have to scan this region of the 834 * string for IFS characters. 835 */ 836 837 STATIC void 838 recordregion(start, end, nulonly) 839 int start; 840 int end; 841 int nulonly; 842 { 843 struct ifsregion *ifsp; 844 845 if (ifslastp == NULL) { 846 ifsp = &ifsfirst; 847 } else { 848 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 849 ifslastp->next = ifsp; 850 } 851 ifslastp = ifsp; 852 ifslastp->next = NULL; 853 ifslastp->begoff = start; 854 ifslastp->endoff = end; 855 ifslastp->nulonly = nulonly; 856 } 857 858 859 860 /* 861 * Break the argument string into pieces based upon IFS and add the 862 * strings to the argument list. The regions of the string to be 863 * searched for IFS characters have been stored by recordregion. 864 */ 865 STATIC void 866 ifsbreakup(string, arglist) 867 char *string; 868 struct arglist *arglist; 869 { 870 struct ifsregion *ifsp; 871 struct strlist *sp; 872 char *start; 873 char *p; 874 char *q; 875 char *ifs; 876 int ifsspc; 877 878 879 start = string; 880 if (ifslastp != NULL) { 881 ifsp = &ifsfirst; 882 do { 883 p = string + ifsp->begoff; 884 ifs = ifsp->nulonly? nullstr : ifsval(); 885 ifsspc = strchr(ifs, ' ') != NULL; 886 while (p < string + ifsp->endoff) { 887 q = p; 888 if (*p == CTLESC) 889 p++; 890 if (strchr(ifs, *p++)) { 891 if (q > start || !ifsspc) { 892 *q = '\0'; 893 sp = (struct strlist *)stalloc(sizeof *sp); 894 sp->text = start; 895 *arglist->lastp = sp; 896 arglist->lastp = &sp->next; 897 } 898 if (ifsspc) { 899 for (;;) { 900 if (p >= string + ifsp->endoff) 901 break; 902 q = p; 903 if (*p == CTLESC) 904 p++; 905 if (strchr(ifs, *p++) == NULL) { 906 p = q; 907 break; 908 } 909 } 910 } 911 start = p; 912 } 913 } 914 } while ((ifsp = ifsp->next) != NULL); 915 if (*start || (!ifsspc && start > string)) { 916 sp = (struct strlist *)stalloc(sizeof *sp); 917 sp->text = start; 918 *arglist->lastp = sp; 919 arglist->lastp = &sp->next; 920 } 921 } else { 922 sp = (struct strlist *)stalloc(sizeof *sp); 923 sp->text = start; 924 *arglist->lastp = sp; 925 arglist->lastp = &sp->next; 926 } 927 } 928 929 930 931 /* 932 * Expand shell metacharacters. At this point, the only control characters 933 * should be escapes. The results are stored in the list exparg. 934 */ 935 936 char *expdir; 937 938 939 STATIC void 940 expandmeta(str, flag) 941 struct strlist *str; 942 int flag; 943 { 944 char *p; 945 struct strlist **savelastp; 946 struct strlist *sp; 947 char c; 948 /* TODO - EXP_REDIR */ 949 950 while (str) { 951 if (fflag) 952 goto nometa; 953 p = str->text; 954 for (;;) { /* fast check for meta chars */ 955 if ((c = *p++) == '\0') 956 goto nometa; 957 if (c == '*' || c == '?' || c == '[' || c == '!') 958 break; 959 } 960 savelastp = exparg.lastp; 961 INTOFF; 962 if (expdir == NULL) { 963 int i = strlen(str->text); 964 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 965 } 966 967 expmeta(expdir, str->text); 968 ckfree(expdir); 969 expdir = NULL; 970 INTON; 971 if (exparg.lastp == savelastp) { 972 /* 973 * no matches 974 */ 975 nometa: 976 *exparg.lastp = str; 977 rmescapes(str->text); 978 exparg.lastp = &str->next; 979 } else { 980 *exparg.lastp = NULL; 981 *savelastp = sp = expsort(*savelastp); 982 while (sp->next != NULL) 983 sp = sp->next; 984 exparg.lastp = &sp->next; 985 } 986 str = str->next; 987 } 988 } 989 990 991 /* 992 * Do metacharacter (i.e. *, ?, [...]) expansion. 993 */ 994 995 STATIC void 996 expmeta(enddir, name) 997 char *enddir; 998 char *name; 999 { 1000 char *p; 1001 char *q; 1002 char *start; 1003 char *endname; 1004 int metaflag; 1005 struct stat statb; 1006 DIR *dirp; 1007 struct dirent *dp; 1008 int atend; 1009 int matchdot; 1010 1011 metaflag = 0; 1012 start = name; 1013 for (p = name ; ; p++) { 1014 if (*p == '*' || *p == '?') 1015 metaflag = 1; 1016 else if (*p == '[') { 1017 q = p + 1; 1018 if (*q == '!') 1019 q++; 1020 for (;;) { 1021 if (*q == CTLESC) 1022 q++; 1023 if (*q == '/' || *q == '\0') 1024 break; 1025 if (*++q == ']') { 1026 metaflag = 1; 1027 break; 1028 } 1029 } 1030 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 1031 metaflag = 1; 1032 } else if (*p == '\0') 1033 break; 1034 else if (*p == CTLESC) 1035 p++; 1036 if (*p == '/') { 1037 if (metaflag) 1038 break; 1039 start = p + 1; 1040 } 1041 } 1042 if (metaflag == 0) { /* we've reached the end of the file name */ 1043 if (enddir != expdir) 1044 metaflag++; 1045 for (p = name ; ; p++) { 1046 if (*p == CTLESC) 1047 p++; 1048 *enddir++ = *p; 1049 if (*p == '\0') 1050 break; 1051 } 1052 if (metaflag == 0 || stat(expdir, &statb) >= 0) 1053 addfname(expdir); 1054 return; 1055 } 1056 endname = p; 1057 if (start != name) { 1058 p = name; 1059 while (p < start) { 1060 if (*p == CTLESC) 1061 p++; 1062 *enddir++ = *p++; 1063 } 1064 } 1065 if (enddir == expdir) { 1066 p = "."; 1067 } else if (enddir == expdir + 1 && *expdir == '/') { 1068 p = "/"; 1069 } else { 1070 p = expdir; 1071 enddir[-1] = '\0'; 1072 } 1073 if ((dirp = opendir(p)) == NULL) 1074 return; 1075 if (enddir != expdir) 1076 enddir[-1] = '/'; 1077 if (*endname == 0) { 1078 atend = 1; 1079 } else { 1080 atend = 0; 1081 *endname++ = '\0'; 1082 } 1083 matchdot = 0; 1084 if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.')) 1085 matchdot++; 1086 while (! int_pending() && (dp = readdir(dirp)) != NULL) { 1087 if (dp->d_name[0] == '.' && ! matchdot) 1088 continue; 1089 if (patmatch(start, dp->d_name)) { 1090 if (atend) { 1091 scopy(dp->d_name, enddir); 1092 addfname(expdir); 1093 } else { 1094 char *q; 1095 for (p = enddir, q = dp->d_name; 1096 (*p++ = *q++) != '\0';) 1097 continue; 1098 p[-1] = '/'; 1099 expmeta(p, endname); 1100 } 1101 } 1102 } 1103 closedir(dirp); 1104 if (! atend) 1105 endname[-1] = '/'; 1106 } 1107 1108 1109 /* 1110 * Add a file name to the list. 1111 */ 1112 1113 STATIC void 1114 addfname(name) 1115 char *name; 1116 { 1117 char *p; 1118 struct strlist *sp; 1119 1120 p = stalloc(strlen(name) + 1); 1121 scopy(name, p); 1122 sp = (struct strlist *)stalloc(sizeof *sp); 1123 sp->text = p; 1124 *exparg.lastp = sp; 1125 exparg.lastp = &sp->next; 1126 } 1127 1128 1129 /* 1130 * Sort the results of file name expansion. It calculates the number of 1131 * strings to sort and then calls msort (short for merge sort) to do the 1132 * work. 1133 */ 1134 1135 STATIC struct strlist * 1136 expsort(str) 1137 struct strlist *str; 1138 { 1139 int len; 1140 struct strlist *sp; 1141 1142 len = 0; 1143 for (sp = str ; sp ; sp = sp->next) 1144 len++; 1145 return msort(str, len); 1146 } 1147 1148 1149 STATIC struct strlist * 1150 msort(list, len) 1151 struct strlist *list; 1152 int len; 1153 { 1154 struct strlist *p, *q = NULL; 1155 struct strlist **lpp; 1156 int half; 1157 int n; 1158 1159 if (len <= 1) 1160 return list; 1161 half = len >> 1; 1162 p = list; 1163 for (n = half ; --n >= 0 ; ) { 1164 q = p; 1165 p = p->next; 1166 } 1167 q->next = NULL; /* terminate first half of list */ 1168 q = msort(list, half); /* sort first half of list */ 1169 p = msort(p, len - half); /* sort second half */ 1170 lpp = &list; 1171 for (;;) { 1172 if (strcmp(p->text, q->text) < 0) { 1173 *lpp = p; 1174 lpp = &p->next; 1175 if ((p = *lpp) == NULL) { 1176 *lpp = q; 1177 break; 1178 } 1179 } else { 1180 *lpp = q; 1181 lpp = &q->next; 1182 if ((q = *lpp) == NULL) { 1183 *lpp = p; 1184 break; 1185 } 1186 } 1187 } 1188 return list; 1189 } 1190 1191 1192 1193 /* 1194 * Returns true if the pattern matches the string. 1195 */ 1196 1197 int 1198 patmatch(pattern, string) 1199 char *pattern; 1200 char *string; 1201 { 1202 #ifdef notdef 1203 if (pattern[0] == '!' && pattern[1] == '!') 1204 return 1 - pmatch(pattern + 2, string); 1205 else 1206 #endif 1207 return pmatch(pattern, string); 1208 } 1209 1210 1211 STATIC int 1212 pmatch(pattern, string) 1213 char *pattern; 1214 char *string; 1215 { 1216 char *p, *q; 1217 char c; 1218 1219 p = pattern; 1220 q = string; 1221 for (;;) { 1222 switch (c = *p++) { 1223 case '\0': 1224 goto breakloop; 1225 case CTLESC: 1226 if (*q++ != *p++) 1227 return 0; 1228 break; 1229 case '?': 1230 if (*q++ == '\0') 1231 return 0; 1232 break; 1233 case '*': 1234 c = *p; 1235 if (c != CTLESC && c != '?' && c != '*' && c != '[') { 1236 while (*q != c) { 1237 if (*q == '\0') 1238 return 0; 1239 q++; 1240 } 1241 } 1242 do { 1243 if (pmatch(p, q)) 1244 return 1; 1245 } while (*q++ != '\0'); 1246 return 0; 1247 case '[': { 1248 char *endp; 1249 int invert, found; 1250 char chr; 1251 1252 endp = p; 1253 if (*endp == '!') 1254 endp++; 1255 for (;;) { 1256 if (*endp == '\0') 1257 goto dft; /* no matching ] */ 1258 if (*endp == CTLESC) 1259 endp++; 1260 if (*++endp == ']') 1261 break; 1262 } 1263 invert = 0; 1264 if (*p == '!') { 1265 invert++; 1266 p++; 1267 } 1268 found = 0; 1269 chr = *q++; 1270 if (chr == '\0') 1271 return 0; 1272 c = *p++; 1273 do { 1274 if (c == CTLESC) 1275 c = *p++; 1276 if (*p == '-' && p[1] != ']') { 1277 p++; 1278 if (*p == CTLESC) 1279 p++; 1280 if (chr >= c && chr <= *p) 1281 found = 1; 1282 p++; 1283 } else { 1284 if (chr == c) 1285 found = 1; 1286 } 1287 } while ((c = *p++) != ']'); 1288 if (found == invert) 1289 return 0; 1290 break; 1291 } 1292 dft: default: 1293 if (*q++ != c) 1294 return 0; 1295 break; 1296 } 1297 } 1298 breakloop: 1299 if (*q != '\0') 1300 return 0; 1301 return 1; 1302 } 1303 1304 1305 1306 /* 1307 * Remove any CTLESC characters from a string. 1308 */ 1309 1310 void 1311 rmescapes(str) 1312 char *str; 1313 { 1314 char *p, *q; 1315 1316 p = str; 1317 while (*p != CTLESC) { 1318 if (*p++ == '\0') 1319 return; 1320 } 1321 q = p; 1322 while (*p) { 1323 if (*p == CTLESC) 1324 p++; 1325 *q++ = *p++; 1326 } 1327 *q = '\0'; 1328 } 1329 1330 1331 1332 /* 1333 * See if a pattern matches in a case statement. 1334 */ 1335 1336 int 1337 casematch(pattern, val) 1338 union node *pattern; 1339 char *val; 1340 { 1341 struct stackmark smark; 1342 int result; 1343 char *p; 1344 1345 setstackmark(&smark); 1346 argbackq = pattern->narg.backquote; 1347 STARTSTACKSTR(expdest); 1348 ifslastp = NULL; 1349 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1350 STPUTC('\0', expdest); 1351 p = grabstackstr(expdest); 1352 result = patmatch(p, val); 1353 popstackmark(&smark); 1354 return result; 1355 } 1356 1357 /* 1358 * Our own itoa(). 1359 */ 1360 1361 STATIC char * 1362 cvtnum(num, buf) 1363 int num; 1364 char *buf; 1365 { 1366 char temp[32]; 1367 int neg = num < 0; 1368 char *p = temp + 31; 1369 1370 temp[31] = '\0'; 1371 1372 do { 1373 *--p = num % 10 + '0'; 1374 } while ((num /= 10) != 0); 1375 1376 if (neg) 1377 *--p = '-'; 1378 1379 while (*p) 1380 STPUTC(*p++, buf); 1381 return buf; 1382 } 1383