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