1 /* $NetBSD: expand.c,v 1.93 2015/08/27 07:46:47 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 39 #else 40 __RCSID("$NetBSD: expand.c,v 1.93 2015/08/27 07:46:47 christos Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <sys/types.h> 45 #include <sys/time.h> 46 #include <sys/stat.h> 47 #include <errno.h> 48 #include <dirent.h> 49 #include <unistd.h> 50 #include <pwd.h> 51 #include <limits.h> 52 #include <stdlib.h> 53 #include <stdio.h> 54 #include <wctype.h> 55 56 /* 57 * Routines to expand arguments to commands. We have to deal with 58 * backquotes, shell variables, and file metacharacters. 59 */ 60 61 #include "shell.h" 62 #include "main.h" 63 #include "nodes.h" 64 #include "eval.h" 65 #include "expand.h" 66 #include "syntax.h" 67 #include "parser.h" 68 #include "jobs.h" 69 #include "options.h" 70 #include "builtins.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 "show.h" 78 79 /* 80 * Structure specifying which parts of the string should be searched 81 * for IFS characters. 82 */ 83 84 struct ifsregion { 85 struct ifsregion *next; /* next region in list */ 86 int begoff; /* offset of start of region */ 87 int endoff; /* offset of end of region */ 88 int inquotes; /* search for nul bytes only */ 89 }; 90 91 92 char *expdest; /* output of current string */ 93 struct nodelist *argbackq; /* list of back quote expressions */ 94 struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 95 struct ifsregion *ifslastp; /* last struct in list */ 96 struct arglist exparg; /* holds expanded arg list */ 97 98 STATIC void argstr(char *, int); 99 STATIC char *exptilde(char *, int); 100 STATIC void expbackq(union node *, int, int); 101 STATIC int subevalvar(char *, char *, int, int, int, int, int); 102 STATIC char *evalvar(char *, int); 103 STATIC int varisset(char *, int); 104 STATIC void varvalue(char *, int, int, int); 105 STATIC void recordregion(int, int, int); 106 STATIC void removerecordregions(int); 107 STATIC void ifsbreakup(char *, struct arglist *); 108 STATIC void ifsfree(void); 109 STATIC void expandmeta(struct strlist *, int); 110 STATIC void expmeta(char *, char *); 111 STATIC void addfname(char *); 112 STATIC struct strlist *expsort(struct strlist *); 113 STATIC struct strlist *msort(struct strlist *, int); 114 STATIC int pmatch(char *, char *, int); 115 STATIC char *cvtnum(int, char *); 116 117 /* 118 * Expand shell variables and backquotes inside a here document. 119 */ 120 121 void 122 expandhere(union node *arg, int fd) 123 { 124 herefd = fd; 125 expandarg(arg, NULL, 0); 126 xwrite(fd, stackblock(), expdest - stackblock()); 127 } 128 129 130 /* 131 * Perform variable substitution and command substitution on an argument, 132 * placing the resulting list of arguments in arglist. If EXP_FULL is true, 133 * perform splitting and file name expansion. When arglist is NULL, perform 134 * here document expansion. 135 */ 136 137 void 138 expandarg(union node *arg, struct arglist *arglist, int flag) 139 { 140 struct strlist *sp; 141 char *p; 142 143 argbackq = arg->narg.backquote; 144 STARTSTACKSTR(expdest); 145 ifsfirst.next = NULL; 146 ifslastp = NULL; 147 argstr(arg->narg.text, flag); 148 if (arglist == NULL) { 149 return; /* here document expanded */ 150 } 151 STPUTC('\0', expdest); 152 p = grabstackstr(expdest); 153 exparg.lastp = &exparg.list; 154 /* 155 * TODO - EXP_REDIR 156 */ 157 if (flag & EXP_FULL) { 158 ifsbreakup(p, &exparg); 159 *exparg.lastp = NULL; 160 exparg.lastp = &exparg.list; 161 expandmeta(exparg.list, flag); 162 } else { 163 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 164 rmescapes(p); 165 sp = (struct strlist *)stalloc(sizeof (struct strlist)); 166 sp->text = p; 167 *exparg.lastp = sp; 168 exparg.lastp = &sp->next; 169 } 170 ifsfree(); 171 *exparg.lastp = NULL; 172 if (exparg.list) { 173 *arglist->lastp = exparg.list; 174 arglist->lastp = exparg.lastp; 175 } 176 } 177 178 179 180 /* 181 * Perform variable and command substitution. 182 * If EXP_FULL is set, output CTLESC characters to allow for further processing. 183 * Otherwise treat $@ like $* since no splitting will be performed. 184 */ 185 186 STATIC void 187 argstr(char *p, int flag) 188 { 189 char c; 190 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 191 int firsteq = 1; 192 const char *ifs = NULL; 193 int ifs_split = EXP_IFS_SPLIT; 194 195 if (flag & EXP_IFS_SPLIT) 196 ifs = ifsset() ? ifsval() : " \t\n"; 197 198 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 199 p = exptilde(p, flag); 200 for (;;) { 201 switch (c = *p++) { 202 case '\0': 203 case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ 204 return; 205 case CTLQUOTEMARK: 206 /* "$@" syntax adherence hack */ 207 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 208 break; 209 if ((flag & EXP_FULL) != 0) 210 STPUTC(c, expdest); 211 ifs_split = 0; 212 break; 213 case CTLQUOTEEND: 214 ifs_split = EXP_IFS_SPLIT; 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 & ~EXP_IFS_SPLIT) | (flag & ifs_split)); 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 if (flag & ifs_split && strchr(ifs, c) != NULL) { 253 /* We need to get the output split here... */ 254 recordregion(expdest - stackblock() - 1, 255 expdest - stackblock(), 0); 256 } 257 break; 258 } 259 } 260 } 261 262 STATIC char * 263 exptilde(char *p, int flag) 264 { 265 char c, *startp = p; 266 struct passwd *pw; 267 const char *home; 268 int quotes = flag & (EXP_FULL | EXP_CASE); 269 270 while ((c = *p) != '\0') { 271 switch(c) { 272 case CTLESC: 273 return (startp); 274 case CTLQUOTEMARK: 275 return (startp); 276 case ':': 277 if (flag & EXP_VARTILDE) 278 goto done; 279 break; 280 case '/': 281 goto done; 282 } 283 p++; 284 } 285 done: 286 *p = '\0'; 287 if (*(startp+1) == '\0') { 288 if ((home = lookupvar("HOME")) == NULL) 289 goto lose; 290 } else { 291 if ((pw = getpwnam(startp+1)) == NULL) 292 goto lose; 293 home = pw->pw_dir; 294 } 295 if (*home == '\0') 296 goto lose; 297 *p = c; 298 while ((c = *home++) != '\0') { 299 if (quotes && SQSYNTAX[(int)c] == CCTL) 300 STPUTC(CTLESC, expdest); 301 STPUTC(c, expdest); 302 } 303 return (p); 304 lose: 305 *p = c; 306 return (startp); 307 } 308 309 310 STATIC void 311 removerecordregions(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(int flag) 356 { 357 char *p, *start; 358 intmax_t result; 359 int adjustment; 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 /* SPACE_NEEDED is enough for all digits, plus possible "-", plus 2 (why?) */ 377 #define SPACE_NEEDED ((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 1 + 2) 378 CHECKSTRSPACE((int)(SPACE_NEEDED - 2), expdest); 379 USTPUTC('\0', expdest); 380 start = stackblock(); 381 p = expdest - 1; 382 while (*p != CTLARI && p >= start) 383 --p; 384 if (*p != CTLARI) 385 error("missing CTLARI (shouldn't happen)"); 386 if (p > start && *(p-1) == CTLESC) 387 for (p = start; *p != CTLARI; p++) 388 if (*p == CTLESC) 389 p++; 390 391 if (p[1] == '"') 392 quoted=1; 393 else 394 quoted=0; 395 begoff = p - start; 396 removerecordregions(begoff); 397 if (quotes) 398 rmescapes(p+2); 399 result = arith(p+2); 400 fmtstr(p, SPACE_NEEDED, "%"PRIdMAX, result); 401 402 while (*p++) 403 ; 404 405 if (quoted == 0) 406 recordregion(begoff, p - 1 - start, 0); 407 adjustment = expdest - p + 1; 408 STADJUST(-adjustment, expdest); 409 } 410 411 412 /* 413 * Expand stuff in backwards quotes. 414 */ 415 416 STATIC void 417 expbackq(union node *cmd, int quoted, 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 int nnl; 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 nnl = 0; 450 for (;;) { 451 if (--in.nleft < 0) { 452 if (in.fd < 0) 453 break; 454 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) 455 continue; 456 TRACE(("expbackq: read returns %d\n", i)); 457 if (i <= 0) 458 break; 459 p = buf; 460 in.nleft = i - 1; 461 } 462 lastc = *p++; 463 if (lastc != '\0') { 464 if (lastc == '\n') 465 nnl++; 466 else { 467 CHECKSTRSPACE(nnl + 2, dest); 468 while (nnl > 0) { 469 nnl--; 470 USTPUTC('\n', dest); 471 } 472 if (quotes && syntax[(int)lastc] == CCTL) 473 USTPUTC(CTLESC, dest); 474 USTPUTC(lastc, dest); 475 } 476 } 477 } 478 479 if (in.fd >= 0) 480 close(in.fd); 481 if (in.buf) 482 ckfree(in.buf); 483 if (in.jp) 484 back_exitstatus = waitforjob(in.jp); 485 if (quoted == 0) 486 recordregion(startloc, dest - stackblock(), 0); 487 TRACE(("evalbackq: size=%d: \"%.*s\"\n", 488 (int)((dest - stackblock()) - startloc), 489 (int)((dest - stackblock()) - startloc), 490 stackblock() + startloc)); 491 expdest = dest; 492 INTON; 493 } 494 495 496 497 STATIC int 498 subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) 499 { 500 char *startp; 501 char *loc = NULL; 502 char *q; 503 int c = 0; 504 int saveherefd = herefd; 505 struct nodelist *saveargbackq = argbackq; 506 int amount, how; 507 508 herefd = -1; 509 switch (subtype) { 510 case VSTRIMLEFT: 511 case VSTRIMLEFTMAX: 512 case VSTRIMRIGHT: 513 case VSTRIMRIGHTMAX: 514 how = (varflags & VSQUOTE) ? 0 : EXP_CASE; 515 break; 516 default: 517 how = 0; 518 break; 519 } 520 argstr(p, how); 521 STACKSTRNUL(expdest); 522 herefd = saveherefd; 523 argbackq = saveargbackq; 524 startp = stackblock() + startloc; 525 if (str == NULL) 526 str = stackblock() + strloc; 527 528 switch (subtype) { 529 case VSASSIGN: 530 setvar(str, startp, 0); 531 amount = startp - expdest; 532 STADJUST(amount, expdest); 533 varflags &= ~VSNUL; 534 return 1; 535 536 case VSQUESTION: 537 if (*p != CTLENDVAR) { 538 outfmt(&errout, "%s\n", startp); 539 error(NULL); 540 } 541 error("%.*s: parameter %snot set", 542 (int)(p - str - 1), 543 str, (varflags & VSNUL) ? "null or " 544 : nullstr); 545 /* NOTREACHED */ 546 547 case VSTRIMLEFT: 548 for (loc = startp; loc < str; loc++) { 549 c = *loc; 550 *loc = '\0'; 551 if (patmatch(str, startp, quotes)) 552 goto recordleft; 553 *loc = c; 554 if (quotes && *loc == CTLESC) 555 loc++; 556 } 557 return 0; 558 559 case VSTRIMLEFTMAX: 560 for (loc = str - 1; loc >= startp;) { 561 c = *loc; 562 *loc = '\0'; 563 if (patmatch(str, startp, quotes)) 564 goto recordleft; 565 *loc = c; 566 loc--; 567 if (quotes && loc > startp && 568 *(loc - 1) == CTLESC) { 569 for (q = startp; q < loc; q++) 570 if (*q == CTLESC) 571 q++; 572 if (q > loc) 573 loc--; 574 } 575 } 576 return 0; 577 578 case VSTRIMRIGHT: 579 for (loc = str - 1; loc >= startp;) { 580 if (patmatch(str, loc, quotes)) 581 goto recordright; 582 loc--; 583 if (quotes && loc > startp && 584 *(loc - 1) == CTLESC) { 585 for (q = startp; q < loc; q++) 586 if (*q == CTLESC) 587 q++; 588 if (q > loc) 589 loc--; 590 } 591 } 592 return 0; 593 594 case VSTRIMRIGHTMAX: 595 for (loc = startp; loc < str - 1; loc++) { 596 if (patmatch(str, loc, quotes)) 597 goto recordright; 598 if (quotes && *loc == CTLESC) 599 loc++; 600 } 601 return 0; 602 603 default: 604 abort(); 605 } 606 607 recordleft: 608 *loc = c; 609 amount = ((str - 1) - (loc - startp)) - expdest; 610 STADJUST(amount, expdest); 611 while (loc != str - 1) 612 *startp++ = *loc++; 613 return 1; 614 615 recordright: 616 amount = loc - expdest; 617 STADJUST(amount, expdest); 618 STPUTC('\0', expdest); 619 STADJUST(-1, expdest); 620 return 1; 621 } 622 623 624 /* 625 * Expand a variable, and return a pointer to the next character in the 626 * input string. 627 */ 628 629 STATIC char * 630 evalvar(char *p, int flag) 631 { 632 int subtype; 633 int varflags; 634 char *var; 635 char *val; 636 int patloc; 637 int c; 638 int set; 639 int special; 640 int startloc; 641 int varlen; 642 int apply_ifs; 643 int quotes = flag & (EXP_FULL | EXP_CASE); 644 645 varflags = (unsigned char)*p++; 646 subtype = varflags & VSTYPE; 647 var = p; 648 special = !is_name(*p); 649 p = strchr(p, '=') + 1; 650 651 again: /* jump here after setting a variable with ${var=text} */ 652 if (varflags & VSLINENO) { 653 set = 1; 654 special = 0; 655 val = var; 656 p[-1] = '\0'; 657 } else if (special) { 658 set = varisset(var, varflags & VSNUL); 659 val = NULL; 660 } else { 661 val = lookupvar(var); 662 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 663 val = NULL; 664 set = 0; 665 } else 666 set = 1; 667 } 668 669 varlen = 0; 670 startloc = expdest - stackblock(); 671 672 if (!set && uflag && *var != '@' && *var != '*') { 673 switch (subtype) { 674 case VSNORMAL: 675 case VSTRIMLEFT: 676 case VSTRIMLEFTMAX: 677 case VSTRIMRIGHT: 678 case VSTRIMRIGHTMAX: 679 case VSLENGTH: 680 error("%.*s: parameter not set", 681 (int)(p - var - 1), var); 682 /* NOTREACHED */ 683 } 684 } 685 686 if (set && subtype != VSPLUS) { 687 /* insert the value of the variable */ 688 if (special) { 689 varvalue(var, varflags & VSQUOTE, subtype, flag); 690 if (subtype == VSLENGTH) { 691 varlen = expdest - stackblock() - startloc; 692 STADJUST(-varlen, expdest); 693 } 694 } else { 695 char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 696 : BASESYNTAX; 697 698 if (subtype == VSLENGTH) { 699 for (;*val; val++) 700 varlen++; 701 } else { 702 while (*val) { 703 if (quotes && syntax[(int)*val] == CCTL) 704 STPUTC(CTLESC, expdest); 705 STPUTC(*val++, expdest); 706 } 707 708 } 709 } 710 } 711 712 713 if (flag & EXP_IN_QUOTES) 714 apply_ifs = 0; 715 else if (varflags & VSQUOTE) { 716 if (*var == '@' && shellparam.nparam != 1) 717 apply_ifs = 1; 718 else { 719 /* 720 * Mark so that we don't apply IFS if we recurse through 721 * here expanding $bar from "${foo-$bar}". 722 */ 723 flag |= EXP_IN_QUOTES; 724 apply_ifs = 0; 725 } 726 } else 727 apply_ifs = 1; 728 729 switch (subtype) { 730 case VSLENGTH: 731 expdest = cvtnum(varlen, expdest); 732 break; 733 734 case VSNORMAL: 735 break; 736 737 case VSPLUS: 738 set = !set; 739 /* FALLTHROUGH */ 740 case VSMINUS: 741 if (!set) { 742 argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); 743 /* 744 * ${x-a b c} doesn't get split, but removing the 745 * 'apply_ifs = 0' apparently breaks ${1+"$@"}.. 746 * ${x-'a b' c} should generate 2 args. 747 */ 748 /* We should have marked stuff already */ 749 apply_ifs = 0; 750 } 751 break; 752 753 case VSTRIMLEFT: 754 case VSTRIMLEFTMAX: 755 case VSTRIMRIGHT: 756 case VSTRIMRIGHTMAX: 757 if (!set) 758 break; 759 /* 760 * Terminate the string and start recording the pattern 761 * right after it 762 */ 763 STPUTC('\0', expdest); 764 patloc = expdest - stackblock(); 765 if (subevalvar(p, NULL, patloc, subtype, 766 startloc, varflags, quotes) == 0) { 767 int amount = (expdest - stackblock() - patloc) + 1; 768 STADJUST(-amount, expdest); 769 } 770 /* Remove any recorded regions beyond start of variable */ 771 removerecordregions(startloc); 772 apply_ifs = 1; 773 break; 774 775 case VSASSIGN: 776 case VSQUESTION: 777 if (set) 778 break; 779 if (subevalvar(p, var, 0, subtype, startloc, varflags, quotes)) { 780 varflags &= ~VSNUL; 781 /* 782 * Remove any recorded regions beyond 783 * start of variable 784 */ 785 removerecordregions(startloc); 786 goto again; 787 } 788 apply_ifs = 0; 789 break; 790 791 default: 792 abort(); 793 } 794 p[-1] = '='; /* recover overwritten '=' */ 795 796 if (apply_ifs) 797 recordregion(startloc, expdest - stackblock(), 798 varflags & VSQUOTE); 799 800 if (subtype != VSNORMAL) { /* skip to end of alternative */ 801 int nesting = 1; 802 for (;;) { 803 if ((c = *p++) == CTLESC) 804 p++; 805 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 806 if (set) 807 argbackq = argbackq->next; 808 } else if (c == CTLVAR) { 809 if ((*p++ & VSTYPE) != VSNORMAL) 810 nesting++; 811 } else if (c == CTLENDVAR) { 812 if (--nesting == 0) 813 break; 814 } 815 } 816 } 817 return p; 818 } 819 820 821 822 /* 823 * Test whether a specialized variable is set. 824 */ 825 826 STATIC int 827 varisset(char *name, int nulok) 828 { 829 if (*name == '!') 830 return backgndpid != -1; 831 else if (*name == '@' || *name == '*') { 832 if (*shellparam.p == NULL) 833 return 0; 834 835 if (nulok) { 836 char **av; 837 838 for (av = shellparam.p; *av; av++) 839 if (**av != '\0') 840 return 1; 841 return 0; 842 } 843 } else if (is_digit(*name)) { 844 char *ap; 845 int num = atoi(name); 846 847 if (num > shellparam.nparam) 848 return 0; 849 850 if (num == 0) 851 ap = arg0; 852 else 853 ap = shellparam.p[num - 1]; 854 855 if (nulok && (ap == NULL || *ap == '\0')) 856 return 0; 857 } 858 return 1; 859 } 860 861 862 863 /* 864 * Add the value of a specialized variable to the stack string. 865 */ 866 867 STATIC void 868 varvalue(char *name, int quoted, int subtype, int flag) 869 { 870 int num; 871 char *p; 872 int i; 873 char sep; 874 char **ap; 875 char const *syntax; 876 877 #define STRTODEST(p) \ 878 do {\ 879 if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ 880 syntax = quoted? DQSYNTAX : BASESYNTAX; \ 881 while (*p) { \ 882 if (syntax[(int)*p] == CCTL) \ 883 STPUTC(CTLESC, expdest); \ 884 STPUTC(*p++, expdest); \ 885 } \ 886 } else \ 887 while (*p) \ 888 STPUTC(*p++, expdest); \ 889 } while (0) 890 891 892 switch (*name) { 893 case '$': 894 num = rootpid; 895 goto numvar; 896 case '?': 897 num = exitstatus; 898 goto numvar; 899 case '#': 900 num = shellparam.nparam; 901 goto numvar; 902 case '!': 903 num = backgndpid; 904 numvar: 905 expdest = cvtnum(num, expdest); 906 break; 907 case '-': 908 for (i = 0; optlist[i].name; i++) { 909 if (optlist[i].val && optlist[i].letter) 910 STPUTC(optlist[i].letter, expdest); 911 } 912 break; 913 case '@': 914 if (flag & EXP_FULL && quoted) { 915 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 916 STRTODEST(p); 917 if (*ap) 918 /* A NUL separates args inside "" */ 919 STPUTC('\0', expdest); 920 } 921 break; 922 } 923 /* fall through */ 924 case '*': 925 if (ifsset() != 0) 926 sep = ifsval()[0]; 927 else 928 sep = ' '; 929 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 930 STRTODEST(p); 931 if (*ap && sep) 932 STPUTC(sep, expdest); 933 } 934 break; 935 case '0': 936 p = arg0; 937 STRTODEST(p); 938 break; 939 default: 940 if (is_digit(*name)) { 941 num = atoi(name); 942 if (num > 0 && num <= shellparam.nparam) { 943 p = shellparam.p[num - 1]; 944 STRTODEST(p); 945 } 946 } 947 break; 948 } 949 } 950 951 952 953 /* 954 * Record the fact that we have to scan this region of the 955 * string for IFS characters. 956 */ 957 958 STATIC void 959 recordregion(int start, int end, int inquotes) 960 { 961 struct ifsregion *ifsp; 962 963 if (ifslastp == NULL) { 964 ifsp = &ifsfirst; 965 } else { 966 if (ifslastp->endoff == start 967 && ifslastp->inquotes == inquotes) { 968 /* extend previous area */ 969 ifslastp->endoff = end; 970 return; 971 } 972 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 973 ifslastp->next = ifsp; 974 } 975 ifslastp = ifsp; 976 ifslastp->next = NULL; 977 ifslastp->begoff = start; 978 ifslastp->endoff = end; 979 ifslastp->inquotes = inquotes; 980 } 981 982 983 984 /* 985 * Break the argument string into pieces based upon IFS and add the 986 * strings to the argument list. The regions of the string to be 987 * searched for IFS characters have been stored by recordregion. 988 */ 989 STATIC void 990 ifsbreakup(char *string, struct arglist *arglist) 991 { 992 struct ifsregion *ifsp; 993 struct strlist *sp; 994 char *start; 995 char *p; 996 char *q; 997 const char *ifs; 998 const char *ifsspc; 999 int had_param_ch = 0; 1000 1001 start = string; 1002 1003 if (ifslastp == NULL) { 1004 /* Return entire argument, IFS doesn't apply to any of it */ 1005 sp = (struct strlist *)stalloc(sizeof *sp); 1006 sp->text = start; 1007 *arglist->lastp = sp; 1008 arglist->lastp = &sp->next; 1009 return; 1010 } 1011 1012 ifs = ifsset() ? ifsval() : " \t\n"; 1013 1014 for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 1015 p = string + ifsp->begoff; 1016 while (p < string + ifsp->endoff) { 1017 had_param_ch = 1; 1018 q = p; 1019 if (*p == CTLESC) 1020 p++; 1021 if (ifsp->inquotes) { 1022 /* Only NULs (should be from "$@") end args */ 1023 if (*p != 0) { 1024 p++; 1025 continue; 1026 } 1027 ifsspc = NULL; 1028 } else { 1029 if (!strchr(ifs, *p)) { 1030 p++; 1031 continue; 1032 } 1033 had_param_ch = 0; 1034 ifsspc = strchr(" \t\n", *p); 1035 1036 /* Ignore IFS whitespace at start */ 1037 if (q == start && ifsspc != NULL) { 1038 p++; 1039 start = p; 1040 continue; 1041 } 1042 } 1043 1044 /* Save this argument... */ 1045 *q = '\0'; 1046 sp = (struct strlist *)stalloc(sizeof *sp); 1047 sp->text = start; 1048 *arglist->lastp = sp; 1049 arglist->lastp = &sp->next; 1050 p++; 1051 1052 if (ifsspc != NULL) { 1053 /* Ignore further trailing IFS whitespace */ 1054 for (; p < string + ifsp->endoff; p++) { 1055 q = p; 1056 if (*p == CTLESC) 1057 p++; 1058 if (strchr(ifs, *p) == NULL) { 1059 p = q; 1060 break; 1061 } 1062 if (strchr(" \t\n", *p) == NULL) { 1063 p++; 1064 break; 1065 } 1066 } 1067 } 1068 start = p; 1069 } 1070 } 1071 1072 /* 1073 * Save anything left as an argument. 1074 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1075 * generating 2 arguments, the second of which is empty. 1076 * Some recent clarification of the Posix spec say that it 1077 * should only generate one.... 1078 */ 1079 if (had_param_ch || *start != 0) { 1080 sp = (struct strlist *)stalloc(sizeof *sp); 1081 sp->text = start; 1082 *arglist->lastp = sp; 1083 arglist->lastp = &sp->next; 1084 } 1085 } 1086 1087 STATIC void 1088 ifsfree(void) 1089 { 1090 while (ifsfirst.next != NULL) { 1091 struct ifsregion *ifsp; 1092 INTOFF; 1093 ifsp = ifsfirst.next->next; 1094 ckfree(ifsfirst.next); 1095 ifsfirst.next = ifsp; 1096 INTON; 1097 } 1098 ifslastp = NULL; 1099 ifsfirst.next = NULL; 1100 } 1101 1102 1103 1104 /* 1105 * Expand shell metacharacters. At this point, the only control characters 1106 * should be escapes. The results are stored in the list exparg. 1107 */ 1108 1109 char *expdir; 1110 1111 1112 STATIC void 1113 expandmeta(struct strlist *str, int flag) 1114 { 1115 char *p; 1116 struct strlist **savelastp; 1117 struct strlist *sp; 1118 char c; 1119 /* TODO - EXP_REDIR */ 1120 1121 while (str) { 1122 if (fflag) 1123 goto nometa; 1124 p = str->text; 1125 for (;;) { /* fast check for meta chars */ 1126 if ((c = *p++) == '\0') 1127 goto nometa; 1128 if (c == '*' || c == '?' || c == '[' || c == '!') 1129 break; 1130 } 1131 savelastp = exparg.lastp; 1132 INTOFF; 1133 if (expdir == NULL) { 1134 int i = strlen(str->text); 1135 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 1136 } 1137 1138 expmeta(expdir, str->text); 1139 ckfree(expdir); 1140 expdir = NULL; 1141 INTON; 1142 if (exparg.lastp == savelastp) { 1143 /* 1144 * no matches 1145 */ 1146 nometa: 1147 *exparg.lastp = str; 1148 rmescapes(str->text); 1149 exparg.lastp = &str->next; 1150 } else { 1151 *exparg.lastp = NULL; 1152 *savelastp = sp = expsort(*savelastp); 1153 while (sp->next != NULL) 1154 sp = sp->next; 1155 exparg.lastp = &sp->next; 1156 } 1157 str = str->next; 1158 } 1159 } 1160 1161 1162 /* 1163 * Do metacharacter (i.e. *, ?, [...]) expansion. 1164 */ 1165 1166 STATIC void 1167 expmeta(char *enddir, char *name) 1168 { 1169 char *p; 1170 const char *cp; 1171 char *q; 1172 char *start; 1173 char *endname; 1174 int metaflag; 1175 struct stat statb; 1176 DIR *dirp; 1177 struct dirent *dp; 1178 int atend; 1179 int matchdot; 1180 1181 metaflag = 0; 1182 start = name; 1183 for (p = name ; ; p++) { 1184 if (*p == '*' || *p == '?') 1185 metaflag = 1; 1186 else if (*p == '[') { 1187 q = p + 1; 1188 if (*q == '!') 1189 q++; 1190 for (;;) { 1191 while (*q == CTLQUOTEMARK) 1192 q++; 1193 if (*q == CTLESC) 1194 q++; 1195 if (*q == '/' || *q == '\0') 1196 break; 1197 if (*++q == ']') { 1198 metaflag = 1; 1199 break; 1200 } 1201 } 1202 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 1203 metaflag = 1; 1204 } else if (*p == '\0') 1205 break; 1206 else if (*p == CTLQUOTEMARK) 1207 continue; 1208 else if (*p == CTLESC) 1209 p++; 1210 if (*p == '/') { 1211 if (metaflag) 1212 break; 1213 start = p + 1; 1214 } 1215 } 1216 if (metaflag == 0) { /* we've reached the end of the file name */ 1217 if (enddir != expdir) 1218 metaflag++; 1219 for (p = name ; ; p++) { 1220 if (*p == CTLQUOTEMARK) 1221 continue; 1222 if (*p == CTLESC) 1223 p++; 1224 *enddir++ = *p; 1225 if (*p == '\0') 1226 break; 1227 } 1228 if (metaflag == 0 || lstat(expdir, &statb) >= 0) 1229 addfname(expdir); 1230 return; 1231 } 1232 endname = p; 1233 if (start != name) { 1234 p = name; 1235 while (p < start) { 1236 while (*p == CTLQUOTEMARK) 1237 p++; 1238 if (*p == CTLESC) 1239 p++; 1240 *enddir++ = *p++; 1241 } 1242 } 1243 if (enddir == expdir) { 1244 cp = "."; 1245 } else if (enddir == expdir + 1 && *expdir == '/') { 1246 cp = "/"; 1247 } else { 1248 cp = expdir; 1249 enddir[-1] = '\0'; 1250 } 1251 if ((dirp = opendir(cp)) == NULL) 1252 return; 1253 if (enddir != expdir) 1254 enddir[-1] = '/'; 1255 if (*endname == 0) { 1256 atend = 1; 1257 } else { 1258 atend = 0; 1259 *endname++ = '\0'; 1260 } 1261 matchdot = 0; 1262 p = start; 1263 while (*p == CTLQUOTEMARK) 1264 p++; 1265 if (*p == CTLESC) 1266 p++; 1267 if (*p == '.') 1268 matchdot++; 1269 while (! int_pending() && (dp = readdir(dirp)) != NULL) { 1270 if (dp->d_name[0] == '.' && ! matchdot) 1271 continue; 1272 if (patmatch(start, dp->d_name, 0)) { 1273 if (atend) { 1274 scopy(dp->d_name, enddir); 1275 addfname(expdir); 1276 } else { 1277 for (p = enddir, cp = dp->d_name; 1278 (*p++ = *cp++) != '\0';) 1279 continue; 1280 p[-1] = '/'; 1281 expmeta(p, endname); 1282 } 1283 } 1284 } 1285 closedir(dirp); 1286 if (! atend) 1287 endname[-1] = '/'; 1288 } 1289 1290 1291 /* 1292 * Add a file name to the list. 1293 */ 1294 1295 STATIC void 1296 addfname(char *name) 1297 { 1298 char *p; 1299 struct strlist *sp; 1300 1301 p = stalloc(strlen(name) + 1); 1302 scopy(name, p); 1303 sp = (struct strlist *)stalloc(sizeof *sp); 1304 sp->text = p; 1305 *exparg.lastp = sp; 1306 exparg.lastp = &sp->next; 1307 } 1308 1309 1310 /* 1311 * Sort the results of file name expansion. It calculates the number of 1312 * strings to sort and then calls msort (short for merge sort) to do the 1313 * work. 1314 */ 1315 1316 STATIC struct strlist * 1317 expsort(struct strlist *str) 1318 { 1319 int len; 1320 struct strlist *sp; 1321 1322 len = 0; 1323 for (sp = str ; sp ; sp = sp->next) 1324 len++; 1325 return msort(str, len); 1326 } 1327 1328 1329 STATIC struct strlist * 1330 msort(struct strlist *list, int len) 1331 { 1332 struct strlist *p, *q = NULL; 1333 struct strlist **lpp; 1334 int half; 1335 int n; 1336 1337 if (len <= 1) 1338 return list; 1339 half = len >> 1; 1340 p = list; 1341 for (n = half ; --n >= 0 ; ) { 1342 q = p; 1343 p = p->next; 1344 } 1345 q->next = NULL; /* terminate first half of list */ 1346 q = msort(list, half); /* sort first half of list */ 1347 p = msort(p, len - half); /* sort second half */ 1348 lpp = &list; 1349 for (;;) { 1350 if (strcmp(p->text, q->text) < 0) { 1351 *lpp = p; 1352 lpp = &p->next; 1353 if ((p = *lpp) == NULL) { 1354 *lpp = q; 1355 break; 1356 } 1357 } else { 1358 *lpp = q; 1359 lpp = &q->next; 1360 if ((q = *lpp) == NULL) { 1361 *lpp = p; 1362 break; 1363 } 1364 } 1365 } 1366 return list; 1367 } 1368 1369 1370 /* 1371 * See if a character matches a character class, starting at the first colon 1372 * of "[:class:]". 1373 * If a valid character class is recognized, a pointer to the next character 1374 * after the final closing bracket is stored into *end, otherwise a null 1375 * pointer is stored into *end. 1376 */ 1377 static int 1378 match_charclass(char *p, wchar_t chr, char **end) 1379 { 1380 char name[20]; 1381 char *nameend; 1382 wctype_t cclass; 1383 1384 *end = NULL; 1385 p++; 1386 nameend = strstr(p, ":]"); 1387 if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || 1388 nameend == p) 1389 return 0; 1390 memcpy(name, p, nameend - p); 1391 name[nameend - p] = '\0'; 1392 *end = nameend + 2; 1393 cclass = wctype(name); 1394 /* An unknown class matches nothing but is valid nevertheless. */ 1395 if (cclass == 0) 1396 return 0; 1397 return iswctype(chr, cclass); 1398 } 1399 1400 1401 1402 /* 1403 * Returns true if the pattern matches the string. 1404 */ 1405 1406 int 1407 patmatch(char *pattern, char *string, int squoted) 1408 { 1409 #ifdef notdef 1410 if (pattern[0] == '!' && pattern[1] == '!') 1411 return 1 - pmatch(pattern + 2, string); 1412 else 1413 #endif 1414 return pmatch(pattern, string, squoted); 1415 } 1416 1417 1418 STATIC int 1419 pmatch(char *pattern, char *string, int squoted) 1420 { 1421 char *p, *q, *end; 1422 char c; 1423 1424 p = pattern; 1425 q = string; 1426 for (;;) { 1427 switch (c = *p++) { 1428 case '\0': 1429 goto breakloop; 1430 case CTLESC: 1431 if (squoted && *q == CTLESC) 1432 q++; 1433 if (*q++ != *p++) 1434 return 0; 1435 break; 1436 case CTLQUOTEMARK: 1437 continue; 1438 case '?': 1439 if (squoted && *q == CTLESC) 1440 q++; 1441 if (*q++ == '\0') 1442 return 0; 1443 break; 1444 case '*': 1445 c = *p; 1446 while (c == CTLQUOTEMARK || c == '*') 1447 c = *++p; 1448 if (c != CTLESC && c != CTLQUOTEMARK && 1449 c != '?' && c != '*' && c != '[') { 1450 while (*q != c) { 1451 if (squoted && *q == CTLESC && 1452 q[1] == c) 1453 break; 1454 if (*q == '\0') 1455 return 0; 1456 if (squoted && *q == CTLESC) 1457 q++; 1458 q++; 1459 } 1460 } 1461 do { 1462 if (pmatch(p, q, squoted)) 1463 return 1; 1464 if (squoted && *q == CTLESC) 1465 q++; 1466 } while (*q++ != '\0'); 1467 return 0; 1468 case '[': { 1469 char *endp; 1470 int invert, found; 1471 char chr; 1472 1473 endp = p; 1474 if (*endp == '!') 1475 endp++; 1476 for (;;) { 1477 while (*endp == CTLQUOTEMARK) 1478 endp++; 1479 if (*endp == '\0') 1480 goto dft; /* no matching ] */ 1481 if (*endp == CTLESC) 1482 endp++; 1483 if (*++endp == ']') 1484 break; 1485 } 1486 invert = 0; 1487 if (*p == '!') { 1488 invert++; 1489 p++; 1490 } 1491 found = 0; 1492 chr = *q++; 1493 if (squoted && chr == CTLESC) 1494 chr = *q++; 1495 if (chr == '\0') 1496 return 0; 1497 c = *p++; 1498 do { 1499 if (c == CTLQUOTEMARK) 1500 continue; 1501 if (c == '[' && *p == ':') { 1502 found |= match_charclass(p, chr, &end); 1503 if (end != NULL) 1504 p = end; 1505 } 1506 if (c == CTLESC) 1507 c = *p++; 1508 if (*p == '-' && p[1] != ']') { 1509 p++; 1510 while (*p == CTLQUOTEMARK) 1511 p++; 1512 if (*p == CTLESC) 1513 p++; 1514 if (chr >= c && chr <= *p) 1515 found = 1; 1516 p++; 1517 } else { 1518 if (chr == c) 1519 found = 1; 1520 } 1521 } while ((c = *p++) != ']'); 1522 if (found == invert) 1523 return 0; 1524 break; 1525 } 1526 dft: default: 1527 if (squoted && *q == CTLESC) 1528 q++; 1529 if (*q++ != c) 1530 return 0; 1531 break; 1532 } 1533 } 1534 breakloop: 1535 if (*q != '\0') 1536 return 0; 1537 return 1; 1538 } 1539 1540 1541 1542 /* 1543 * Remove any CTLESC characters from a string. 1544 */ 1545 1546 void 1547 rmescapes(char *str) 1548 { 1549 char *p, *q; 1550 1551 p = str; 1552 while (*p != CTLESC && *p != CTLQUOTEMARK) { 1553 if (*p++ == '\0') 1554 return; 1555 } 1556 q = p; 1557 while (*p) { 1558 if (*p == CTLQUOTEMARK) { 1559 p++; 1560 continue; 1561 } 1562 if (*p == CTLESC) 1563 p++; 1564 *q++ = *p++; 1565 } 1566 *q = '\0'; 1567 } 1568 1569 1570 1571 /* 1572 * See if a pattern matches in a case statement. 1573 */ 1574 1575 int 1576 casematch(union node *pattern, char *val) 1577 { 1578 struct stackmark smark; 1579 int result; 1580 char *p; 1581 1582 setstackmark(&smark); 1583 argbackq = pattern->narg.backquote; 1584 STARTSTACKSTR(expdest); 1585 ifslastp = NULL; 1586 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1587 STPUTC('\0', expdest); 1588 p = grabstackstr(expdest); 1589 result = patmatch(p, val, 0); 1590 popstackmark(&smark); 1591 return result; 1592 } 1593 1594 /* 1595 * Our own itoa(). 1596 */ 1597 1598 STATIC char * 1599 cvtnum(int num, char *buf) 1600 { 1601 char temp[32]; 1602 int neg = num < 0; 1603 char *p = temp + 31; 1604 1605 temp[31] = '\0'; 1606 1607 do { 1608 *--p = num % 10 + '0'; 1609 } while ((num /= 10) != 0); 1610 1611 if (neg) 1612 *--p = '-'; 1613 1614 while (*p) 1615 STPUTC(*p++, buf); 1616 return buf; 1617 } 1618 1619 /* 1620 * Do most of the work for wordexp(3). 1621 */ 1622 1623 int 1624 wordexpcmd(int argc, char **argv) 1625 { 1626 size_t len; 1627 int i; 1628 1629 out1fmt("%d", argc - 1); 1630 out1c('\0'); 1631 for (i = 1, len = 0; i < argc; i++) 1632 len += strlen(argv[i]); 1633 out1fmt("%zu", len); 1634 out1c('\0'); 1635 for (i = 1; i < argc; i++) { 1636 out1str(argv[i]); 1637 out1c('\0'); 1638 } 1639 return (0); 1640 } 1641