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