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