|
|
1.1 ! root 1: /* ! 2: * sh/eval.c ! 3: * Bourne shell. ! 4: * Evaluation of parameter substitution, command substitution, ! 5: * blank interpretation, and file name generation. ! 6: */ ! 7: ! 8: #include <sys/compat.h> ! 9: #include <sys/stat.h> ! 10: #include <fcntl.h> ! 11: #include "sh.h" ! 12: ! 13: struct eval_context { ! 14: char * arcp; /* character position in argument */ ! 15: int argf; /* First character of argument flag */ ! 16: int argg; /* Glob seek, escape quoted glob chars */ ! 17: int argq; /* Quotation flag, no blanks or glob */ ! 18: } _eval [1]; ! 19: ! 20: LOCAL void variable (); ! 21: LOCAL void special (); ! 22: LOCAL void graves (); ! 23: ! 24: LOCAL void add_char (); ! 25: LOCAL void add_quoted (); ! 26: LOCAL void add_arg (); ! 27: LOCAL void end_arg (); ! 28: ! 29: #define EVAL_NO_QUOTE 0 ! 30: #define EVAL_QUOTED 1 ! 31: #define EVAL_FORCE_QUOTE 2 ! 32: ! 33: #define EVAL_INPUT_SOURCE(eval,cp) (void) ((eval)->arcp = (cp)) ! 34: #define EVAL_NEXT_CHAR(eval) (* (eval)->arcp ++) ! 35: #define EVAL_UNGET_CHAR(eval,ch) (void) ((eval)->arcp --) ! 36: ! 37: #define EVAL_SET_QUOTING(eval,q) (void) ((eval)->argq = (q)) ! 38: #define EVAL_ADD_QUOTE(eval,q) \ ! 39: (void) ((eval)->argq = ((eval)->argq & ~ EVAL_QUOTED) | (q)) ! 40: #define EVAL_IS_QUOTED(eval) ((eval)->argq) ! 41: ! 42: #define EVAL_SET_GLOB(eval,g) (void) ((eval)->argg = (g)) ! 43: #define EVAL_IS_GLOBSEEK(eval) ((eval)->argg != 0) ! 44: ! 45: #define EVAL_MADE_ARGUMENT(eval) (void) ((eval)->argf = 0) ! 46: #define EVAL_ARGUMENT_IF_QUOTED(eval) (void) ((eval)->argf &= \ ! 47: ~ (eval)->argq) ! 48: #define EVAL_NO_ARGUMENT(eval) (void) ((eval)->argf = 1, strp = strt) ! 49: #define EVAL_IS_NO_ARGUMENT(eval) ((eval)->argf != 0) ! 50: ! 51: ! 52: /* ! 53: * Evaluate a string. ! 54: */ ! 55: ! 56: void ! 57: eval(cp, f) ! 58: char *cp; ! 59: { ! 60: register int m, c; ! 61: ! 62: EVAL_INPUT_SOURCE (_eval, cp); ! 63: EVAL_NO_ARGUMENT (_eval); ! 64: ! 65: if (f == EHERE) { ! 66: m = MHERE; ! 67: EVAL_SET_QUOTING (_eval, EVAL_FORCE_QUOTE); ! 68: } else if (f == EWORD) { ! 69: m = MNQUO; ! 70: EVAL_SET_QUOTING (_eval, EVAL_FORCE_QUOTE); ! 71: } else { ! 72: m = MNQUO; ! 73: EVAL_SET_QUOTING (_eval, EVAL_NO_QUOTE); ! 74: } ! 75: EVAL_SET_GLOB (_eval, f == EARGS || f == EPATT); ! 76: ! 77: while ((c = EVAL_NEXT_CHAR (_eval)) != '\0') { ! 78: if (! class (c, m)) { ! 79: add_char (c); ! 80: continue; ! 81: } ! 82: switch (c) { ! 83: case '"': /* m == MNQUO || m == MDQUO */ ! 84: if ((m ^= (MDQUO ^ MNQUO)) == MDQUO) { ! 85: ! 86: EVAL_ADD_QUOTE (_eval, EVAL_QUOTED); ! 87: ! 88: if ((c = EVAL_NEXT_CHAR (_eval)) == '\"') ! 89: EVAL_MADE_ARGUMENT (_eval); ! 90: ! 91: EVAL_UNGET_CHAR (_eval, c); ! 92: } else ! 93: EVAL_ADD_QUOTE (_eval, 0); ! 94: continue; ! 95: ! 96: case '\'': /* m == MNQUO */ ! 97: while ((c = EVAL_NEXT_CHAR (_eval)) != '\'' && c != 0) ! 98: add_quoted (c); ! 99: EVAL_MADE_ARGUMENT (_eval); ! 100: continue; ! 101: ! 102: case '\\': /* m == MDQUO || m == MNQUO */ ! 103: c = EVAL_NEXT_CHAR (_eval); ! 104: if (m != MNQUO && ! class (c, m)) { ! 105: add_char ('\\'); ! 106: add_char (c); ! 107: } else ! 108: add_quoted (c); ! 109: EVAL_MADE_ARGUMENT (_eval); ! 110: continue; ! 111: ! 112: case '$': /* m == MNQUO || m = MDQUO */ ! 113: variable (); ! 114: continue; ! 115: ! 116: case '`': /* m == MNQUO || m = MDQUO */ ! 117: graves (m); ! 118: continue; ! 119: ! 120: default: ! 121: add_char (c); ! 122: continue; ! 123: } ! 124: } ! 125: ! 126: if (f == EARGS) ! 127: end_arg (); ! 128: else ! 129: * strp ++ = '\0'; ! 130: } ! 131: ! 132: ! 133: /* ! 134: * Read the name of a shell variable and perform the appropriate substitution. ! 135: */ ! 136: ! 137: LOCAL void ! 138: variable() ! 139: { ! 140: VAR *vp; ! 141: int s; ! 142: char *sav; ! 143: register int c, count, quote; ! 144: register char * pp; ! 145: char * name_start = strp; ! 146: char * alternate_value; ! 147: int colon_test = 0; ! 148: ! 149: s = '\0'; ! 150: c = EVAL_NEXT_CHAR (_eval); ! 151: ! 152: if (strchr (SPECIAL_VAR_CHARS, c) != 0) { ! 153: special (c); ! 154: return; ! 155: } else if (class (c, MRVAR)) { ! 156: do { ! 157: add_arg (c); ! 158: c = EVAL_NEXT_CHAR (_eval); ! 159: } while (class (c, MRVAR)); ! 160: ! 161: EVAL_UNGET_CHAR (_eval, c); ! 162: } else if (c != '{') { ! 163: /* Not a legal variable name, put it back. */ ! 164: add_char ('$'); ! 165: add_char (c); ! 166: return; ! 167: } else { ! 168: /* c == '{' */ ! 169: ! 170: c = EVAL_NEXT_CHAR (_eval); ! 171: ! 172: if (strchr (SPECIAL_VAR_CHARS, c) != NULL) { ! 173: int peek; ! 174: ! 175: if ((peek = EVAL_NEXT_CHAR (_eval)) == '}') { ! 176: ! 177: /* Allow specials of the form "${?}" etc. */ ! 178: special (c); ! 179: return; ! 180: } ! 181: EVAL_UNGET_CHAR (_eval, peek); ! 182: } ! 183: ! 184: for (;;) { ! 185: if (! class (c, MRVAR)) ! 186: break; ! 187: add_arg (c); ! 188: c = EVAL_NEXT_CHAR (_eval); ! 189: } ! 190: ! 191: if ((colon_test = c == ':') != 0) ! 192: c = EVAL_NEXT_CHAR (_eval); ! 193: ! 194: if (strchr ("-=?+", c) != NULL) { ! 195: /* ! 196: * ${VAR [-=?+] word} ! 197: * Stash away the value of the intemediate character ! 198: * and store a '=' character to stroke the variable- ! 199: * storage machinery. ! 200: */ ! 201: ! 202: s = c; ! 203: add_arg ('='); ! 204: alternate_value = strp; ! 205: if ((quote = EVAL_NEXT_CHAR (_eval)) != '"' && ! 206: quote != '\'') { ! 207: ! 208: EVAL_UNGET_CHAR (_eval, quote); ! 209: quote = 0; ! 210: } ! 211: ! 212: for (count = 1; ; ) { ! 213: c = EVAL_NEXT_CHAR (_eval); ! 214: if (c == '}' && count -- == 1) ! 215: break; ! 216: else if (c == '$' && quote != '\'') { ! 217: /* ! 218: * steve 6/24/92 ! 219: * This truly sleazy hack handles e.g. "${V1-$V2}", oy. ! 220: * It doesn't do it very well, paying no attention to quotes (for example). ! 221: */ ! 222: variable (); ! 223: continue; ! 224: } else if (c == '{') ! 225: ++ count; ! 226: else if (quote != 0 && c == quote) { ! 227: quote = 0; ! 228: continue; ! 229: } ! 230: add_arg (c); ! 231: } ! 232: } else if (colon_test || c != '}') { ! 233: eillvar (strp = name_start); ! 234: return; ! 235: } ! 236: } ! 237: add_arg ('\0'); ! 238: ! 239: c = * name_start; ! 240: ! 241: if (class (c, MDIGI)) { ! 242: if ((c -= '1') >= sargc) ! 243: pp = NULL; ! 244: else ! 245: pp = sargp [c]; ! 246: } else if (namevar (name_start) == 0) { ! 247: eillvar (strp = name_start); ! 248: return; ! 249: } else { ! 250: pp = NULL; ! 251: if ((vp = findvar (name_start)) != NULL) { ! 252: pp = convvar (vp); ! 253: if (* pp == '\0' && colon_test) ! 254: pp = NULL; /* regard value "" as not set */ ! 255: } ! 256: } ! 257: ! 258: switch (s) { ! 259: case '\0': ! 260: if (uflag != 0 && pp == NULL) ! 261: enotdef (name_start); ! 262: break; ! 263: ! 264: case '-': ! 265: if (pp == NULL) ! 266: pp = alternate_value; ! 267: break; ! 268: ! 269: case '=': ! 270: if (pp == NULL) { ! 271: if (class (* name_start, MDIGI) || ! 272: strchr (SPECIAL_VAR_CHARS, ! 273: * name_start) != NULL) { ! 274: ! 275: printe ("Illegal substitution"); ! 276: strp = name_start; ! 277: return; ! 278: } ! 279: setsvar (name_start); ! 280: pp = alternate_value; ! 281: } ! 282: break; ! 283: ! 284: case '?': ! 285: if (pp != NULL) ! 286: break; ! 287: if (* alternate_value != '\0') ! 288: prints ("%s\n", alternate_value); ! 289: else { ! 290: alternate_value [-1] = '\0'; ! 291: enotdef (name_start); ! 292: } ! 293: reset (RUABORT); ! 294: NOTREACHED; ! 295: ! 296: case '+': ! 297: if (pp != NULL) ! 298: pp = alternate_value; ! 299: break; ! 300: } ! 301: ! 302: strp = name_start; ! 303: ! 304: if (pp != NULL) ! 305: while ((c = * pp ++) != '\0') ! 306: add_char (c); ! 307: ! 308: EVAL_ARGUMENT_IF_QUOTED (_eval); ! 309: } ! 310: ! 311: ! 312: /* ! 313: * Return the value of the special shell variables. ! 314: * No check for end of buffer. ! 315: */ ! 316: ! 317: LOCAL void ! 318: special(n) ! 319: int n; ! 320: { ! 321: int flag; ! 322: char * sp; ! 323: ! 324: switch (n) { ! 325: case '#': ! 326: n = sargc; ! 327: goto maked; ! 328: ! 329: case '?': ! 330: n = slret; ! 331: goto maked; ! 332: ! 333: case '$': ! 334: n = shpid; ! 335: goto maked; ! 336: ! 337: case '!': ! 338: n = sback; ! 339: goto maked; ! 340: ! 341: maked: ! 342: sprintf (strp, "%d", n); ! 343: sp = strp; ! 344: break; ! 345: ! 346: case '-': ! 347: ! 348: for (sp = & eflag; sp <= & xflag ; sp ++) ! 349: if (* sp) ! 350: add_char (* sp); ! 351: ! 352: EVAL_ARGUMENT_IF_QUOTED (_eval); ! 353: return; ! 354: ! 355: case '@': ! 356: case '*': ! 357: flag = EVAL_IS_QUOTED (_eval) == EVAL_QUOTED && n == '@'; ! 358: for (n = 0; n < sargc; n++) { ! 359: if (n) { ! 360: if (flag) ! 361: end_arg (); ! 362: else ! 363: add_char (' '); ! 364: } ! 365: ! 366: sp = sargp [n]; ! 367: while (* sp) ! 368: add_char (* sp ++); ! 369: ! 370: /* ! 371: * Make sure that arguments like "" get handled ! 372: * properly when expanding "$@" ! 373: */ ! 374: ! 375: if (flag) ! 376: EVAL_ARGUMENT_IF_QUOTED (_eval); ! 377: } ! 378: return; ! 379: ! 380: case '0': ! 381: sp = sarg0; ! 382: break; ! 383: ! 384: default: ! 385: if (n - '1' >= sargc) { ! 386: if (uflag) ! 387: printe ("Unset parameter: %c", n); ! 388: EVAL_ARGUMENT_IF_QUOTED (_eval); ! 389: return; ! 390: } ! 391: sp = sargp [n - '1']; ! 392: break; ! 393: } ! 394: ! 395: while (* sp) ! 396: add_char (* sp ++); ! 397: ! 398: EVAL_ARGUMENT_IF_QUOTED (_eval); ! 399: } ! 400: ! 401: ! 402: /* ! 403: * Read and evaluate a command found between graves. ! 404: * ! 405: * NB : Backslash-quoting inside graves was not supported properly before, ! 406: * and I'm not sure that I've got it right. The idea is that before passing ! 407: * things off to session (), we process the appropriate backslash-escapes. ! 408: * What consists of an "appropriate" escape depends on whether the graves ! 409: * appeared within a double-quoted section (in which case we recognise the ! 410: * specials appropriate to that) or not (in which case we recognised the ! 411: * characters $`\ as specials. We also process '$'-expansions in graves now, ! 412: * but not globs. ! 413: * ! 414: * Since we are removing some bashslashes here, we should be building into ! 415: * a temporary buffer. We append our work onto the global "strp" buffer and ! 416: * cut it back once we have finished. ! 417: */ ! 418: ! 419: LOCAL void ! 420: graves (quotemode) ! 421: int quotemode; ! 422: { ! 423: int pipev[2], f, oslret; ! 424: char * ostrp; ! 425: register FILE *fp; ! 426: register int c; ! 427: register int nnl; ! 428: ! 429: ostrp = strp; ! 430: oslret = slret; ! 431: ! 432: while ((c = EVAL_NEXT_CHAR (_eval)) != '`') { ! 433: if (c != '\\') { ! 434: add_arg (c); ! 435: continue; ! 436: } ! 437: c = EVAL_NEXT_CHAR (_eval); ! 438: ! 439: if (! (quotemode == MDQUO && class (c, MDQUO)) && ! 440: (c != '$' && c != '\\' && c != '`')) ! 441: add_arg ('\\'); ! 442: ! 443: add_arg (c); ! 444: } ! 445: * strp = 0; ! 446: ! 447: if ((f = pipeline (pipev)) == 0) { ! 448: slret = oslret; /* in case grave command uses $? */ ! 449: dup2 (pipev [1], 1); ! 450: close (pipev [0]); ! 451: close (pipev [1]); ! 452: exit (session (SARGS, ostrp)); ! 453: NOTREACHED; ! 454: } ! 455: ! 456: close (pipev [1]); ! 457: if ((fp = fdopen (pipev [0], "r")) == NULL) { ! 458: close (pipev [0]); ! 459: ecantfdop (); ! 460: return; ! 461: } ! 462: strp = ostrp; ! 463: nnl = 0; ! 464: ! 465: while ((c = getc (fp)) != EOF) { ! 466: if (! recover (IEVAL)) { ! 467: #ifdef VERBOSE ! 468: if (xflag) prints ("Interrupt in eval\n"); ! 469: #endif ! 470: errflag ++; ! 471: break; ! 472: } ! 473: if (c == '\n') ! 474: ++ nnl; ! 475: else { ! 476: while (nnl) { ! 477: nnl --; ! 478: add_char ('\n'); ! 479: } ! 480: add_char (c); ! 481: } ! 482: } ! 483: ! 484: /* ! 485: * If we expanded to something, we have an arg. If we are in double- ! 486: * quotes, we have an arg. Otherwise, we have an arg if we had an arg ! 487: * before. ! 488: */ ! 489: ! 490: EVAL_ARGUMENT_IF_QUOTED (_eval); ! 491: fclose (fp); ! 492: waitc (f); ! 493: } ! 494: ! 495: ! 496: /* ! 497: * Add a character to the current argument. ! 498: * If no quotation is set, pick off blanks and globs. ! 499: */ ! 500: ! 501: LOCAL void ! 502: add_char(c) ! 503: register int c; ! 504: { ! 505: if (EVAL_IS_QUOTED (_eval) == 0) { ! 506: if (strchr (vifs, c) != NULL) { ! 507: end_arg (); ! 508: return; ! 509: } ! 510: if (EVAL_IS_GLOBSEEK (_eval) && class (c, MGLOB)) { ! 511: add_arg (c); ! 512: EVAL_MADE_ARGUMENT (_eval); ! 513: return; ! 514: } ! 515: } ! 516: add_quoted (c); ! 517: } ! 518: ! 519: ! 520: /* ! 521: * Add a quoted character to the current argument. ! 522: * if argg is set, then glob characters are quoted with a \, ! 523: * as well as \ itself. ! 524: */ ! 525: ! 526: LOCAL void ! 527: add_quoted(c) register int c; ! 528: { ! 529: if (EVAL_IS_GLOBSEEK (_eval) && (class (c, MGLOB) || c == '\\')) ! 530: add_arg ('\\'); ! 531: ! 532: add_arg (c); ! 533: EVAL_MADE_ARGUMENT (_eval); ! 534: } ! 535: ! 536: ! 537: /* ! 538: * Add a character to the current argument ! 539: * and check for end of buffer. ! 540: */ ! 541: ! 542: LOCAL void ! 543: add_arg (c) register int c; ! 544: { ! 545: if (strp >= strt + STRSIZE) /* Should do more */ ! 546: etoolong ("in add_arg ()"); ! 547: else ! 548: * strp ++ = c; ! 549: } ! 550: ! 551: ! 552: /* ! 553: * Terminate the current argument if it is non-empty. ! 554: * If argg is set, then glob the argument to expand globs ! 555: * or to simply remove any quotes. ! 556: */ ! 557: ! 558: LOCAL void ! 559: end_arg () ! 560: { ! 561: if (EVAL_IS_NO_ARGUMENT (_eval)) ! 562: return; ! 563: ! 564: * strp ++ = '\0'; ! 565: if (EVAL_IS_GLOBSEEK (_eval)) ! 566: glob1 (duplstr (strt, 0)); ! 567: else { ! 568: nargv = addargl (nargv, duplstr (strt, 0)); ! 569: nargc += 1; ! 570: } ! 571: ! 572: EVAL_NO_ARGUMENT (_eval); ! 573: } ! 574: ! 575: ! 576: /* ! 577: * Evaluate a here document. ! 578: */ ! 579: ! 580: int ! 581: evalhere(u2) ! 582: { ! 583: register int u1; ! 584: register FILE *f2; ! 585: char buf[128]; ! 586: char *tmp; ! 587: ! 588: tmp = shtmp (); ! 589: if ((u1 = creat (tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { ! 590: ecantmake (tmp); ! 591: return -1; ! 592: } ! 593: if ((f2 = fdopen (u2, "r")) == NULL) { ! 594: ecantfdop (); ! 595: close (u1); ! 596: close (u2); ! 597: return -1; ! 598: } ! 599: while (fgets (buf, sizeof (buf), f2) != NULL) { ! 600: eval (buf, EHERE); ! 601: write (u1, strt, strp - 1 - strt); ! 602: } ! 603: close (u1); ! 604: fclose (f2); ! 605: if ((u2 = open (tmp, O_RDONLY)) < 0) { ! 606: ecantopen (tmp); ! 607: u2 = -1; ! 608: } ! 609: unlink (tmp); ! 610: return u2; ! 611: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.