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