|
|
1.1 ! root 1: /* ! 2: * Expansion - quoting, separation, substitution, globbing ! 3: */ ! 4: ! 5: static char *RCSid = "$Header: /newbits/usr/bin/korn/RCS/eval.c,v 1.2 91/08/01 12:39:55 bin Exp Locker: bin $"; ! 6: ! 7: #include <stddef.h> ! 8: #include <stdio.h> ! 9: #include <string.h> ! 10: #include <errno.h> ! 11: #include <setjmp.h> ! 12: #include <unistd.h> ! 13: #include <sys/types.h> ! 14: #include <dirent.h> ! 15: #include <pwd.h> ! 16: #include "sh.h" ! 17: #include "lex.h" ! 18: #include "tree.h" ! 19: #include "table.h" ! 20: #include "expand.h" ! 21: ! 22: #if COHERENT ! 23: struct dirent *readdir(); /* missing from headers */ ! 24: DIR *opendir(); ! 25: #endif ! 26: ! 27: /* ! 28: * string expansion ! 29: * ! 30: * first pass: quoting, IFS separation, ${} and $() substitution. ! 31: * second pass: filename expansion (*?[]~). ! 32: */ ! 33: ! 34: /* expansion generator state */ ! 35: typedef struct Expand { ! 36: /* int type; */ /* see expand() */ ! 37: char *str; /* string */ ! 38: union { ! 39: char **strv; /* string[] */ ! 40: FILE *file; /* file */ ! 41: } u; /* source */ ! 42: short split; /* split "$@"*/ ! 43: } Expand; ! 44: ! 45: #define XBASE 0 /* scanning original */ ! 46: #define XSUB 1 /* expanding ${} string */ ! 47: #define XARGSEP 2 /* ifs0 between "$@" */ ! 48: #define XARG 3 /* expanding $*, $@ */ ! 49: #define XCOM 4 /* expanding $() */ ! 50: ! 51: static void expand ARGS((char *, XPtrV *, int)); ! 52: static int comsub ARGS((Expand *, char *comm)); ! 53: static int varsub ARGS((Expand *, char *name, int stype)); ! 54: static void glob ARGS((char *cp, XPtrV *wp)); ! 55: static void globit ARGS((char *ds, char *dp, char *sp, XPtrV *wp, int check)); ! 56: static char *tilde ARGS((char *wp)); ! 57: static char *trimsub ARGS((char *str, char *pat, int how)); ! 58: ! 59: int ifs0 = ' '; /* todo: first char of $IFS */ ! 60: ! 61: /* compile and expand word */ ! 62: char * ! 63: substitute(cp, f) ! 64: char Const *cp; ! 65: int f; ! 66: { ! 67: struct source *s, *sold; ! 68: ! 69: sold = source; ! 70: s = pushs(SWSTR); ! 71: s->str = (char *) cp; ! 72: source = s; ! 73: if (yylex(ONEWORD) != LWORD) ! 74: errorf("eval:substitute error\n"); ! 75: source = sold; ! 76: return evalstr(yylval.cp, f); ! 77: } ! 78: ! 79: /* ! 80: * expand arg-list ! 81: */ ! 82: char ** ! 83: eval(ap, f) ! 84: register char **ap; ! 85: { ! 86: XPtrV w; ! 87: ! 88: if (*ap == NULL) ! 89: return ap; ! 90: XPinit(w, 32); ! 91: XPput(w, NULL); /* space for shell name */ ! 92: while (*ap != NULL) ! 93: expand(*ap++, &w, f); ! 94: XPput(w, NULL); ! 95: return (char **) XPclose(w) + 1; ! 96: } ! 97: ! 98: /* ! 99: * expand string ! 100: */ ! 101: char * ! 102: evalstr(cp, f) ! 103: register char *cp; ! 104: int f; ! 105: { ! 106: XPtrV w; ! 107: ! 108: XPinit(w, 1); ! 109: expand(cp, &w, f); ! 110: cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w); ! 111: XPfree(w); ! 112: return cp; ! 113: } ! 114: ! 115: /* for nested substitution: ${var:=$var2} */ ! 116: typedef struct SubType { ! 117: short type; /* [=+-?%#] action after expanded word */ ! 118: short base; /* begin position of expanded word */ ! 119: char *name; /* name for ${var=word} */ ! 120: } SubType; ! 121: ! 122: #if 1 ! 123: mypr(cp) ! 124: char *cp; ! 125: { ! 126: fprintf(stderr, "%r", &cp); ! 127: } ! 128: #endif ! 129: ! 130: static void ! 131: expand(cp, wp, f) ! 132: char *cp; /* input word */ ! 133: register XPtrV *wp; /* output words */ ! 134: int f; /* DO* flags */ ! 135: { ! 136: register int c; ! 137: register int type = XBASE; /* expansion type */ ! 138: register int quote = 0; /* quoted */ ! 139: XString ds; /* destination string */ ! 140: register char *dp, *sp; /* dest., source */ ! 141: int fdo, word, combase; /* second pass flags; have word */ ! 142: Expand x; /* expansion variables */ ! 143: SubType subtype [10]; /* substitution type stack */ ! 144: register SubType *st = subtype + 10; ! 145: ! 146: if (cp == NULL) ! 147: errorf("eval:expand(NULL)\n"); ! 148: if (flag[FNOGLOB]) { ! 149: f &= ~ DOGLOB; ! 150: } ! 151: Xinit(ds, dp, 128); /* init dest. string */ ! 152: type = XBASE; ! 153: sp = cp; ! 154: fdo = 0; ! 155: word = !(f&DOBLANK); ! 156: ! 157: while (1) { ! 158: Xcheck(ds, dp); ! 159: ! 160: switch (type) { ! 161: case XBASE: /* original prefixed string */ ! 162: c = *sp++; ! 163: switch (c) { ! 164: case EOS: ! 165: c = 0; ! 166: break; ! 167: case CHAR: ! 168: c = *sp++; ! 169: break; ! 170: case QCHAR: ! 171: quote |= 2; /* temporary quote */ ! 172: c = *sp++; ! 173: break; ! 174: case OQUOTE: ! 175: word = quote = 1; ! 176: continue; ! 177: case CQUOTE: ! 178: quote = 0; ! 179: continue; ! 180: case COMSUB: ! 181: type = comsub(&x, sp); ! 182: sp = strchr(sp, 0) + 1; ! 183: combase = Xsavepos(ds, dp); ! 184: continue; ! 185: case OSUBST: /* ${var{:}[=+-?]word} */ ! 186: cp = sp; /* variable */ ! 187: sp = strchr(sp, 0) + 1; /* skip variable */ ! 188: c = (*sp == CSUBST) ? 0 : *sp++; ! 189: if ((c&0x7F) == '#' || (c&0x7F) == '%') ! 190: f |= DOPAT; ! 191: type = varsub(&x, cp, c); ! 192: if (type == XBASE) { /* expand? */ ! 193: if (st == subtype) ! 194: errorf("ridiculous ${} nesting\n"); ! 195: --st; ! 196: st->type = c; ! 197: st->base = Xsavepos(ds, dp); ! 198: st->name = cp; ! 199: } else ! 200: /* todo: nested OSUBST/CSUBST */ ! 201: sp = wdscan(sp, CSUBST); /* skip word */ ! 202: continue; ! 203: case CSUBST: /* only get here if expanding word */ ! 204: *dp = 0; ! 205: if (f&DOGLOB) ! 206: f &= ~DOPAT; ! 207: switch (st->type&0x7F) { ! 208: case '#': ! 209: case '%': ! 210: *dp = 0; ! 211: dp = Xrestpos(ds, dp, st->base); ! 212: x.str = trimsub(x.str, dp, st->type); ! 213: type = XSUB; ! 214: continue; ! 215: case '=': ! 216: #if 0 ! 217: if ((x.u.vp->flag&RDONLY)) ! 218: errorf("cannot set readonly %s\n", cp); ! 219: #endif ! 220: setstr(global(st->name), Xrestpos(ds, dp, st->base)); ! 221: break; ! 222: case '?': ! 223: if (dp == Xrestpos(ds, dp, st->base)) ! 224: errorf("missing value for %s\n", cp); ! 225: else ! 226: errorf("%s\n", Xrestpos(ds, dp, st->base)); ! 227: } ! 228: st++; ! 229: type = XBASE; ! 230: continue; ! 231: } ! 232: break; ! 233: ! 234: case XSUB: ! 235: if ((c = *x.str++) == 0) { ! 236: type = XBASE; ! 237: continue; ! 238: } ! 239: break; ! 240: ! 241: case XARGSEP: ! 242: type = XARG; ! 243: quote = 1; ! 244: case XARG: ! 245: if ((c = *x.str++) == 0) { ! 246: if ((x.str = *x.u.strv++) == NULL) { ! 247: type = XBASE; ! 248: continue; ! 249: } else if (quote && x.split) { ! 250: /* terminate word for "$@" */ ! 251: type = XARGSEP; ! 252: quote = 0; ! 253: } ! 254: c = ifs0; ! 255: } ! 256: break; ! 257: ! 258: case XCOM: ! 259: c = getc(x.u.file); ! 260: if (quote) { ! 261: if (c == EOF) { ! 262: cp = Xrestpos(ds, sp, combase); ! 263: for (dp--; dp >= cp && *dp == '\n'; dp--) ! 264: ; ! 265: dp++; ! 266: fclose(x.u.file); ! 267: if (x.split) ! 268: waitlast(); ! 269: type = XBASE; ! 270: continue; ! 271: } ! 272: } else { /* this part is probably redundant */ ! 273: if (c == EOF || c == '\n') { ! 274: while ((c = getc(x.u.file)) == '\n') ! 275: ; ! 276: if (c == EOF) { ! 277: fclose(x.u.file); ! 278: if (x.split) ! 279: waitlast(); ! 280: type = XBASE; ! 281: continue; ! 282: } ! 283: ungetc(c, x.u.file); ! 284: c = ifs0; ! 285: } ! 286: } ! 287: break; ! 288: } ! 289: ! 290: /* check for end of word or IFS separation */ ! 291: if (c == 0 || !quote && (f&DOBLANK) && ctype(c, C_IFS)) { ! 292: if (word) { ! 293: *dp++ = 0; ! 294: cp = Xclose(ds, dp); ! 295: if (fdo&DOTILDE) ! 296: cp = tilde(cp); ! 297: if (fdo&DOGLOB) ! 298: glob(cp, wp); ! 299: else ! 300: {XPput(*wp, cp);} ! 301: fdo = word = 0; ! 302: if (c != 0) ! 303: Xinit(ds, dp, 128); ! 304: } else ! 305: ; /* ignore IFS */ ! 306: if (c == 0) ! 307: return; ! 308: } else { ! 309: /* mark any special second pass chars */ ! 310: if (!quote) ! 311: switch (c) { ! 312: case '*': ! 313: case '?': ! 314: case '[': ! 315: if (f&(DOPAT|DOGLOB)) { ! 316: fdo |= (f&DOGLOB); ! 317: *dp++ = MAGIC; ! 318: } ! 319: break; ! 320: case '~': ! 321: if ((f&DOTILDE) && dp == Xstring(ds, dp) || ! 322: !(f&DOBLANK) && ! 323: (dp[-1] == '=' || dp[-1] == ':')) { ! 324: fdo |= DOTILDE; ! 325: *dp++ = MAGIC; ! 326: } ! 327: break; ! 328: } ! 329: else ! 330: quote &= ~2; /* undo temporary */ ! 331: ! 332: word = 1; ! 333: *dp++ = c; /* save output char */ ! 334: } ! 335: } ! 336: } ! 337: ! 338: /* ! 339: * Prepare to generate the string returned by ${} substitution. ! 340: */ ! 341: static int ! 342: varsub(xp, sp, stype) ! 343: register Expand *xp; ! 344: register char *sp; ! 345: int stype; ! 346: { ! 347: register int c; ! 348: int type; ! 349: ! 350: /* ${#var}, string length or argc */ ! 351: if (sp[0] == '#' && (c = sp[1]) != 0) { ! 352: c = (c == '*' || c == '@') ? e.loc->argc : ! 353: strlen(strval(global(sp+1))); ! 354: xp->str = strsave(ulton((unsigned long)c, 10), ATEMP); ! 355: return XSUB; ! 356: } ! 357: ! 358: c = sp[0]; ! 359: if (c == '*' || c == '@') { ! 360: if (e.loc->argc == 0) { ! 361: xp->str = null; ! 362: type = XSUB; ! 363: } else { ! 364: xp->u.strv = e.loc->argv + 1; ! 365: xp->str = *xp->u.strv++; ! 366: xp->split = c == '@'; /* $@ */ ! 367: type = XARG; ! 368: } ! 369: } else { ! 370: xp->str = strval(global(sp)); ! 371: type = XSUB; ! 372: } ! 373: ! 374: c = stype&0x7F; ! 375: /* test the compiler's code generator */ ! 376: if (c == '%' || c == '#' || ! 377: (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ ! 378: c == '=' || c == '-' || c == '?' : c == '+')) ! 379: type = XBASE; /* expand word instead of variable value */ ! 380: if (type != XBASE && flag[FNOUNSET] && xp->str == null) ! 381: errorf("%s: unset variable\n", sp); ! 382: return type; ! 383: } ! 384: ! 385: /* ! 386: * Run the command in $(...) and read its output. ! 387: */ ! 388: static int ! 389: comsub(xp, cp) ! 390: register Expand *xp; ! 391: char *cp; ! 392: { ! 393: Source *s; ! 394: register struct op *t; ! 395: FILE *fi; ! 396: ! 397: s = pushs(SSTRING); ! 398: s->str = cp; ! 399: t = compile(s); ! 400: ! 401: if (t != NULL && t->type == TCOM && /* $(<file) */ ! 402: *t->args == NULL && *t->vars == NULL && t->ioact != NULL) { ! 403: register struct ioword *io = *t->ioact; ! 404: ! 405: if (io->flag != IOREAD) ! 406: errorf("funny $() command\n"); ! 407: fi = fopen(evalstr(io->name, DOTILDE), "r"); ! 408: if (fi != NULL) ! 409: fileno(fi) = savefd(fileno(fi)); ! 410: xp->split = 0; /* no waitlast() */ ! 411: } else { ! 412: int ofd1, pv[2]; ! 413: ! 414: openpipe(pv); ! 415: fi = fdopen(pv[0], "r"); ! 416: ofd1 = savefd(1); ! 417: dup2(pv[1], 1); ! 418: close(pv[1]); ! 419: #if 0 ! 420: exchild(t, XXCOM|XPIPEO); ! 421: #else ! 422: execute(t, XFORK|XXCOM|XPIPEO); ! 423: #endif ! 424: dup2(ofd1, 1); ! 425: #if COHERENT ! 426: close(ofd1); ! 427: #endif ! 428: xp->split = 1; /* waitlast() */ ! 429: } ! 430: ! 431: if (fi == NULL) ! 432: errorf("cannot open $() input\n"); ! 433: setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ); ! 434: xp->u.file = fi; ! 435: return XCOM; ! 436: } ! 437: ! 438: /* ! 439: * perform #pattern and %pattern substitution in ${} ! 440: */ ! 441: ! 442: static char * ! 443: trimsub(str, pat, how) ! 444: register char *str; ! 445: char *pat; ! 446: int how; ! 447: { ! 448: register char *end = strchr(str, 0); ! 449: register char *p, c; ! 450: ! 451: switch (how) { ! 452: case '#': /* shortest at begin */ ! 453: for (p = str; p <= end; p++) { ! 454: c = *p; *p = '\0'; ! 455: if (gmatch(str, pat)) { ! 456: *p = c; ! 457: return p; ! 458: } ! 459: *p = c; ! 460: } ! 461: break; ! 462: case '#'|0x80: /* longest match at begin */ ! 463: for (p = end; p >= str; p--) { ! 464: c = *p; *p = '\0'; ! 465: if (gmatch(str, pat)) { ! 466: *p = c; ! 467: return p; ! 468: } ! 469: *p = c; ! 470: } ! 471: break; ! 472: case '%': /* shortest match at end */ ! 473: for (p = end; p >= str; p--) { ! 474: if (gmatch(p, pat)) { ! 475: *p = '\0'; ! 476: return str; ! 477: } ! 478: } ! 479: break; ! 480: case '%'|0x80: /* longest match at end */ ! 481: for (p = str; p <= end; p++) { ! 482: if (gmatch(p, pat)) { ! 483: *p = '\0'; ! 484: return str; ! 485: } ! 486: } ! 487: break; ! 488: } ! 489: ! 490: return str; /* no match, return string */ ! 491: } ! 492: ! 493: /* ! 494: * glob ! 495: * Name derived from V6's /etc/glob, the program that expanded filenames. ! 496: */ ! 497: ! 498: static char *debunk(); ! 499: ! 500: static void ! 501: glob(cp, wp) ! 502: char *cp; ! 503: register XPtrV *wp; ! 504: { ! 505: char path [PATH]; ! 506: register char *sp = cp; ! 507: int oldsize; ! 508: ! 509: oldsize = XPsize(*wp); ! 510: globit(path, path, sp, wp, 0); ! 511: ! 512: if (XPsize(*wp) == oldsize) ! 513: {XPput(*wp, debunk(cp));} ! 514: else ! 515: qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp); ! 516: } ! 517: ! 518: static void ! 519: globit(ds, dp, sp, wp, check) ! 520: char *ds; /* dest path */ ! 521: char *dp; /* dest end */ ! 522: char *sp; /* source path */ ! 523: register XPtrV *wp; /* output list */ ! 524: int check; /* check dest existence */ ! 525: { ! 526: register char *np; /* next source component */ ! 527: register char *tsp, *tdp; ! 528: ! 529: if (sp == NULL) { /* end of source path */ ! 530: if (check && access(ds, 0) < 0) ! 531: return; ! 532: XPput(*wp, strsave(ds, ATEMP)); ! 533: return; ! 534: } ! 535: ! 536: if (dp > ds) ! 537: *dp++ = '/'; ! 538: while (*sp == '/') ! 539: *dp++ = *sp++; ! 540: np = strchr(sp, '/'); ! 541: if (np != NULL) ! 542: *np++ = 0; ! 543: ! 544: *dp = 0; ! 545: if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */ ! 546: tdp = dp; tsp = sp; ! 547: while ((*tdp++ = *tsp++) != 0) ! 548: ; ! 549: --tdp; ! 550: globit(ds, tdp, np, wp, check); ! 551: } else { ! 552: DIR *dirp; ! 553: struct dirent *d; ! 554: ! 555: dirp = opendir(ds); ! 556: if (dirp == NULL) ! 557: goto Nodir; ! 558: while ((d = readdir(dirp)) != NULL) { ! 559: tsp = d->d_name; ! 560: if (tsp[0] == '.' && ! 561: (tsp[1] == 0 || tsp[1] == '.' && tsp[2] == 0)) ! 562: continue; /* always ignore . and .. */ ! 563: if (*tsp == '.' && *sp != '.' || !gmatch(tsp, sp)) ! 564: continue; ! 565: ! 566: tdp = dp; ! 567: while ((*tdp++ = *tsp++) != 0) ! 568: ; ! 569: --tdp; ! 570: globit(ds, tdp, np, wp, np != NULL); ! 571: } ! 572: closedir(dirp); ! 573: Nodir:; ! 574: } ! 575: ! 576: if (np != NULL) ! 577: *--np = '/'; ! 578: } ! 579: ! 580: /* remove MAGIC from string */ ! 581: static char * ! 582: debunk(cp) ! 583: char *cp; ! 584: { ! 585: register unsigned char *dp, *sp; ! 586: ! 587: for (dp = sp = cp; *sp != 0; sp++) ! 588: if (*sp != MAGIC) ! 589: *dp++ = *sp; ! 590: *dp = 0; ! 591: return cp; ! 592: } ! 593: ! 594: /* ! 595: * tilde expansion ! 596: * ! 597: * based on a version by Arnold Robbins ! 598: */ ! 599: ! 600: static char *homedir(); ! 601: ! 602: static char * ! 603: tilde(acp) ! 604: char *acp; ! 605: { ! 606: register unsigned int c; ! 607: char path[PATH+1]; ! 608: register unsigned char *cp = acp, *wp = path, *dp; ! 609: char userid[16+1]; ! 610: ! 611: Again: ! 612: while (1) { ! 613: if ((c = *cp++) == 0) { ! 614: *wp = 0; ! 615: afree((Void*)acp, ATEMP); ! 616: return strsave(path, ATEMP); ! 617: } else if (c == MAGIC && *cp == '~') ! 618: break; ! 619: else ! 620: *wp++ = c; ! 621: } ! 622: ! 623: dp = NULL; /* no output substitution */ ! 624: if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */ ! 625: dp = strval(global("HOME")), cp += 1; ! 626: else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0)) ! 627: dp = strval(global("PWD")), cp += 2; ! 628: else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0)) ! 629: dp = strval(global("OLDPWD")), cp += 2; ! 630: else if (letter(cp[1])) { ! 631: char *save = cp; ! 632: for (dp = userid, cp++; letnum(*cp) && dp < userid+16; ) ! 633: *dp++ = *cp++; ! 634: *dp = 0; ! 635: dp = homedir(userid); ! 636: if (dp == NULL) ! 637: cp = save; ! 638: } ! 639: /* substitute */ ! 640: if (dp != NULL) ! 641: while (*dp != 0) ! 642: *wp++ = *dp++; ! 643: goto Again; ! 644: } ! 645: ! 646: /* ! 647: * map userid to user's home directory. ! 648: * todo: implement a cache with the "homedirs" table. ! 649: * note that 4.3's getpw adds more than 6K to the shell, ! 650: * and the YP version probably adds much more. ! 651: * we might consider our own version of getpwnam() to keep the size down. ! 652: */ ! 653: ! 654: static char * ! 655: homedir(name) ! 656: char *name; ! 657: { ! 658: register struct tbl *ap; ! 659: register struct passwd *pw; ! 660: ! 661: ap = tsearch(&homedirs, name, hash(name)); ! 662: if ((ap != NULL && (ap->flag&ISSET))) ! 663: return ap->val.s; ! 664: pw = getpwnam(name); ! 665: if (pw == NULL) ! 666: return NULL; ! 667: return pw->pw_dir; ! 668: } ! 669:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.