1 /* $OpenBSD: syn.c,v 1.28 2008/07/23 16:34:38 jaredy Exp $ */ 2 3 /* 4 * shell parser (C version) 5 */ 6 7 #include "sh.h" 8 #include "c_test.h" 9 10 struct nesting_state { 11 int start_token; /* token than began nesting (eg, FOR) */ 12 int start_line; /* line nesting began on */ 13 }; 14 15 static void yyparse(void); 16 static struct op *pipeline(int); 17 static struct op *andor(void); 18 static struct op *c_list(int); 19 static struct ioword *synio(int); 20 static void musthave(int, int); 21 static struct op *nested(int, int, int); 22 static struct op *get_command(int); 23 static struct op *dogroup(void); 24 static struct op *thenpart(void); 25 static struct op *elsepart(void); 26 static struct op *caselist(void); 27 static struct op *casepart(int); 28 static struct op *function_body(char *, int); 29 static char ** wordlist(void); 30 static struct op *block(int, struct op *, struct op *, char **); 31 static struct op *newtp(int); 32 static void syntaxerr(const char *) __attribute__((__noreturn__)); 33 static void nesting_push(struct nesting_state *, int); 34 static void nesting_pop(struct nesting_state *); 35 static int assign_command(char *); 36 static int inalias(struct source *); 37 static int dbtestp_isa(Test_env *, Test_meta); 38 static const char *dbtestp_getopnd(Test_env *, Test_op, int); 39 static int dbtestp_eval(Test_env *, Test_op, const char *, const char *, 40 int); 41 static void dbtestp_error(Test_env *, int, const char *); 42 43 static struct op *outtree; /* yyparse output */ 44 45 static struct nesting_state nesting; /* \n changed to ; */ 46 47 static int reject; /* token(cf) gets symbol again */ 48 static int symbol; /* yylex value */ 49 50 #define REJECT (reject = 1) 51 #define ACCEPT (reject = 0) 52 #define token(cf) \ 53 ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf))) 54 #define tpeek(cf) \ 55 ((reject) ? (symbol) : (REJECT, symbol = yylex(cf))) 56 57 static void 58 yyparse(void) 59 { 60 int c; 61 62 ACCEPT; 63 64 outtree = c_list(source->type == SSTRING); 65 c = tpeek(0); 66 if (c == 0 && !outtree) 67 outtree = newtp(TEOF); 68 else if (c != '\n' && c != 0) 69 syntaxerr((char *) 0); 70 } 71 72 static struct op * 73 pipeline(int cf) 74 { 75 struct op *t, *p, *tl = NULL; 76 77 t = get_command(cf); 78 if (t != NULL) { 79 while (token(0) == '|') { 80 if ((p = get_command(CONTIN)) == NULL) 81 syntaxerr((char *) 0); 82 if (tl == NULL) 83 t = tl = block(TPIPE, t, p, NOWORDS); 84 else 85 tl = tl->right = block(TPIPE, tl->right, p, NOWORDS); 86 } 87 REJECT; 88 } 89 return (t); 90 } 91 92 static struct op * 93 andor(void) 94 { 95 struct op *t, *p; 96 int c; 97 98 t = pipeline(0); 99 if (t != NULL) { 100 while ((c = token(0)) == LOGAND || c == LOGOR) { 101 if ((p = pipeline(CONTIN)) == NULL) 102 syntaxerr((char *) 0); 103 t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); 104 } 105 REJECT; 106 } 107 return (t); 108 } 109 110 static struct op * 111 c_list(int multi) 112 { 113 struct op *t = NULL, *p, *tl = NULL; 114 int c; 115 int have_sep; 116 117 while (1) { 118 p = andor(); 119 /* Token has always been read/rejected at this point, so 120 * we don't worry about what flags to pass token() 121 */ 122 c = token(0); 123 have_sep = 1; 124 if (c == '\n' && (multi || inalias(source))) { 125 if (!p) /* ignore blank lines */ 126 continue; 127 } else if (!p) 128 break; 129 else if (c == '&' || c == COPROC) 130 p = block(c == '&' ? TASYNC : TCOPROC, 131 p, NOBLOCK, NOWORDS); 132 else if (c != ';') 133 have_sep = 0; 134 if (!t) 135 t = p; 136 else if (!tl) 137 t = tl = block(TLIST, t, p, NOWORDS); 138 else 139 tl = tl->right = block(TLIST, tl->right, p, NOWORDS); 140 if (!have_sep) 141 break; 142 } 143 REJECT; 144 return t; 145 } 146 147 static struct ioword * 148 synio(int cf) 149 { 150 struct ioword *iop; 151 int ishere; 152 153 if (tpeek(cf) != REDIR) 154 return NULL; 155 ACCEPT; 156 iop = yylval.iop; 157 ishere = (iop->flag&IOTYPE) == IOHERE; 158 musthave(LWORD, ishere ? HEREDELIM : 0); 159 if (ishere) { 160 iop->delim = yylval.cp; 161 if (*ident != 0) /* unquoted */ 162 iop->flag |= IOEVAL; 163 if (herep >= &heres[HERES]) 164 yyerror("too many <<'s\n"); 165 *herep++ = iop; 166 } else 167 iop->name = yylval.cp; 168 return iop; 169 } 170 171 static void 172 musthave(int c, int cf) 173 { 174 if ((token(cf)) != c) 175 syntaxerr((char *) 0); 176 } 177 178 static struct op * 179 nested(int type, int smark, int emark) 180 { 181 struct op *t; 182 struct nesting_state old_nesting; 183 184 nesting_push(&old_nesting, smark); 185 t = c_list(true); 186 musthave(emark, KEYWORD|ALIAS); 187 nesting_pop(&old_nesting); 188 return (block(type, t, NOBLOCK, NOWORDS)); 189 } 190 191 static struct op * 192 get_command(int cf) 193 { 194 struct op *t; 195 int c, iopn = 0, syniocf; 196 struct ioword *iop, **iops; 197 XPtrV args, vars; 198 struct nesting_state old_nesting; 199 200 iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1), 201 ATEMP); 202 XPinit(args, 16); 203 XPinit(vars, 16); 204 205 syniocf = KEYWORD|ALIAS; 206 switch (c = token(cf|KEYWORD|ALIAS|VARASN)) { 207 default: 208 REJECT; 209 afree((void*) iops, ATEMP); 210 XPfree(args); 211 XPfree(vars); 212 return NULL; /* empty line */ 213 214 case LWORD: 215 case REDIR: 216 REJECT; 217 syniocf &= ~(KEYWORD|ALIAS); 218 t = newtp(TCOM); 219 t->lineno = source->line; 220 while (1) { 221 cf = (t->u.evalflags ? ARRAYVAR : 0) | 222 (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD); 223 switch (tpeek(cf)) { 224 case REDIR: 225 if (iopn >= NUFILE) 226 yyerror("too many redirections\n"); 227 iops[iopn++] = synio(cf); 228 break; 229 230 case LWORD: 231 ACCEPT; 232 /* the iopn == 0 and XPsize(vars) == 0 are 233 * dubious but at&t ksh acts this way 234 */ 235 if (iopn == 0 && XPsize(vars) == 0 && 236 XPsize(args) == 0 && 237 assign_command(ident)) 238 t->u.evalflags = DOVACHECK; 239 if ((XPsize(args) == 0 || Flag(FKEYWORD)) && 240 is_wdvarassign(yylval.cp)) 241 XPput(vars, yylval.cp); 242 else 243 XPput(args, yylval.cp); 244 break; 245 246 case '(': 247 /* Check for "> foo (echo hi)", which at&t ksh 248 * allows (not POSIX, but not disallowed) 249 */ 250 afree(t, ATEMP); 251 if (XPsize(args) == 0 && XPsize(vars) == 0) { 252 ACCEPT; 253 goto Subshell; 254 } 255 /* Must be a function */ 256 if (iopn != 0 || XPsize(args) != 1 || 257 XPsize(vars) != 0) 258 syntaxerr((char *) 0); 259 ACCEPT; 260 /*(*/ 261 musthave(')', 0); 262 t = function_body(XPptrv(args)[0], false); 263 goto Leave; 264 265 default: 266 goto Leave; 267 } 268 } 269 Leave: 270 break; 271 272 Subshell: 273 case '(': 274 t = nested(TPAREN, '(', ')'); 275 break; 276 277 case '{': /*}*/ 278 t = nested(TBRACE, '{', '}'); 279 break; 280 281 case MDPAREN: 282 { 283 static const char let_cmd[] = { 284 CHAR, 'l', CHAR, 'e', 285 CHAR, 't', EOS 286 }; 287 /* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */ 288 t = newtp(TCOM); 289 t->lineno = source->line; 290 ACCEPT; 291 XPput(args, wdcopy(let_cmd, ATEMP)); 292 musthave(LWORD,LETEXPR); 293 XPput(args, yylval.cp); 294 break; 295 } 296 297 case DBRACKET: /* [[ .. ]] */ 298 /* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */ 299 t = newtp(TDBRACKET); 300 ACCEPT; 301 { 302 Test_env te; 303 304 te.flags = TEF_DBRACKET; 305 te.pos.av = &args; 306 te.isa = dbtestp_isa; 307 te.getopnd = dbtestp_getopnd; 308 te.eval = dbtestp_eval; 309 te.error = dbtestp_error; 310 311 test_parse(&te); 312 } 313 break; 314 315 case FOR: 316 case SELECT: 317 t = newtp((c == FOR) ? TFOR : TSELECT); 318 musthave(LWORD, ARRAYVAR); 319 if (!is_wdvarname(yylval.cp, true)) 320 yyerror("%s: bad identifier\n", 321 c == FOR ? "for" : "select"); 322 t->str = str_save(ident, ATEMP); 323 nesting_push(&old_nesting, c); 324 t->vars = wordlist(); 325 t->left = dogroup(); 326 nesting_pop(&old_nesting); 327 break; 328 329 case WHILE: 330 case UNTIL: 331 nesting_push(&old_nesting, c); 332 t = newtp((c == WHILE) ? TWHILE : TUNTIL); 333 t->left = c_list(true); 334 t->right = dogroup(); 335 nesting_pop(&old_nesting); 336 break; 337 338 case CASE: 339 t = newtp(TCASE); 340 musthave(LWORD, 0); 341 t->str = yylval.cp; 342 nesting_push(&old_nesting, c); 343 t->left = caselist(); 344 nesting_pop(&old_nesting); 345 break; 346 347 case IF: 348 nesting_push(&old_nesting, c); 349 t = newtp(TIF); 350 t->left = c_list(true); 351 t->right = thenpart(); 352 musthave(FI, KEYWORD|ALIAS); 353 nesting_pop(&old_nesting); 354 break; 355 356 case BANG: 357 syniocf &= ~(KEYWORD|ALIAS); 358 t = pipeline(0); 359 if (t == (struct op *) 0) 360 syntaxerr((char *) 0); 361 t = block(TBANG, NOBLOCK, t, NOWORDS); 362 break; 363 364 case TIME: 365 syniocf &= ~(KEYWORD|ALIAS); 366 t = pipeline(0); 367 if (t) { 368 t->str = alloc(2, ATEMP); 369 t->str[0] = '\0'; /* TF_* flags */ 370 t->str[1] = '\0'; 371 } 372 t = block(TTIME, t, NOBLOCK, NOWORDS); 373 break; 374 375 case FUNCTION: 376 musthave(LWORD, 0); 377 t = function_body(yylval.cp, true); 378 break; 379 } 380 381 while ((iop = synio(syniocf)) != NULL) { 382 if (iopn >= NUFILE) 383 yyerror("too many redirections\n"); 384 iops[iopn++] = iop; 385 } 386 387 if (iopn == 0) { 388 afree((void*) iops, ATEMP); 389 t->ioact = NULL; 390 } else { 391 iops[iopn++] = NULL; 392 iops = (struct ioword **) aresize((void*) iops, 393 sizeofN(struct ioword *, iopn), ATEMP); 394 t->ioact = iops; 395 } 396 397 if (t->type == TCOM || t->type == TDBRACKET) { 398 XPput(args, NULL); 399 t->args = (char **) XPclose(args); 400 XPput(vars, NULL); 401 t->vars = (char **) XPclose(vars); 402 } else { 403 XPfree(args); 404 XPfree(vars); 405 } 406 407 return t; 408 } 409 410 static struct op * 411 dogroup(void) 412 { 413 int c; 414 struct op *list; 415 416 c = token(CONTIN|KEYWORD|ALIAS); 417 /* A {...} can be used instead of do...done for for/select loops 418 * but not for while/until loops - we don't need to check if it 419 * is a while loop because it would have been parsed as part of 420 * the conditional command list... 421 */ 422 if (c == DO) 423 c = DONE; 424 else if (c == '{') 425 c = '}'; 426 else 427 syntaxerr((char *) 0); 428 list = c_list(true); 429 musthave(c, KEYWORD|ALIAS); 430 return list; 431 } 432 433 static struct op * 434 thenpart(void) 435 { 436 struct op *t; 437 438 musthave(THEN, KEYWORD|ALIAS); 439 t = newtp(0); 440 t->left = c_list(true); 441 if (t->left == NULL) 442 syntaxerr((char *) 0); 443 t->right = elsepart(); 444 return (t); 445 } 446 447 static struct op * 448 elsepart(void) 449 { 450 struct op *t; 451 452 switch (token(KEYWORD|ALIAS|VARASN)) { 453 case ELSE: 454 if ((t = c_list(true)) == NULL) 455 syntaxerr((char *) 0); 456 return (t); 457 458 case ELIF: 459 t = newtp(TELIF); 460 t->left = c_list(true); 461 t->right = thenpart(); 462 return (t); 463 464 default: 465 REJECT; 466 } 467 return NULL; 468 } 469 470 static struct op * 471 caselist(void) 472 { 473 struct op *t, *tl; 474 int c; 475 476 c = token(CONTIN|KEYWORD|ALIAS); 477 /* A {...} can be used instead of in...esac for case statements */ 478 if (c == IN) 479 c = ESAC; 480 else if (c == '{') 481 c = '}'; 482 else 483 syntaxerr((char *) 0); 484 t = tl = NULL; 485 while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */ 486 struct op *tc = casepart(c); 487 if (tl == NULL) 488 t = tl = tc, tl->right = NULL; 489 else 490 tl->right = tc, tl = tc; 491 } 492 musthave(c, KEYWORD|ALIAS); 493 return (t); 494 } 495 496 static struct op * 497 casepart(int endtok) 498 { 499 struct op *t; 500 int c; 501 XPtrV ptns; 502 503 XPinit(ptns, 16); 504 t = newtp(TPAT); 505 c = token(CONTIN|KEYWORD); /* no ALIAS here */ 506 if (c != '(') 507 REJECT; 508 do { 509 musthave(LWORD, 0); 510 XPput(ptns, yylval.cp); 511 } while ((c = token(0)) == '|'); 512 REJECT; 513 XPput(ptns, NULL); 514 t->vars = (char **) XPclose(ptns); 515 musthave(')', 0); 516 517 t->left = c_list(true); 518 /* Note: Posix requires the ;; */ 519 if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok) 520 musthave(BREAK, CONTIN|KEYWORD|ALIAS); 521 return (t); 522 } 523 524 static struct op * 525 function_body(char *name, 526 int ksh_func) /* function foo { ... } vs foo() { .. } */ 527 { 528 char *sname, *p; 529 struct op *t; 530 int old_func_parse; 531 532 sname = wdstrip(name); 533 /* Check for valid characters in name. posix and ksh93 say only 534 * allow [a-zA-Z_0-9] but this allows more as old pdksh's have 535 * allowed more (the following were never allowed: 536 * nul space nl tab $ ' " \ ` ( ) & | ; = < > 537 * C_QUOTE covers all but = and adds # [ ? *) 538 */ 539 for (p = sname; *p; p++) 540 if (ctype(*p, C_QUOTE) || *p == '=') 541 yyerror("%s: invalid function name\n", sname); 542 543 t = newtp(TFUNCT); 544 t->str = sname; 545 t->u.ksh_func = ksh_func; 546 t->lineno = source->line; 547 548 /* Note that POSIX allows only compound statements after foo(), sh and 549 * at&t ksh allow any command, go with the later since it shouldn't 550 * break anything. However, for function foo, at&t ksh only accepts 551 * an open-brace. 552 */ 553 if (ksh_func) { 554 musthave('{', CONTIN|KEYWORD|ALIAS); /* } */ 555 REJECT; 556 } 557 558 old_func_parse = e->flags & EF_FUNC_PARSE; 559 e->flags |= EF_FUNC_PARSE; 560 if ((t->left = get_command(CONTIN)) == (struct op *) 0) { 561 /* 562 * Probably something like foo() followed by eof or ;. 563 * This is accepted by sh and ksh88. 564 * To make "typeset -f foo" work reliably (so its output can 565 * be used as input), we pretend there is a colon here. 566 */ 567 t->left = newtp(TCOM); 568 t->left->args = (char **) alloc(sizeof(char *) * 2, ATEMP); 569 t->left->args[0] = alloc(sizeof(char) * 3, ATEMP); 570 t->left->args[0][0] = CHAR; 571 t->left->args[0][1] = ':'; 572 t->left->args[0][2] = EOS; 573 t->left->args[1] = (char *) 0; 574 t->left->vars = (char **) alloc(sizeof(char *), ATEMP); 575 t->left->vars[0] = (char *) 0; 576 t->left->lineno = 1; 577 } 578 if (!old_func_parse) 579 e->flags &= ~EF_FUNC_PARSE; 580 581 return t; 582 } 583 584 static char ** 585 wordlist(void) 586 { 587 int c; 588 XPtrV args; 589 590 XPinit(args, 16); 591 /* Posix does not do alias expansion here... */ 592 if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) { 593 if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */ 594 REJECT; 595 return NULL; 596 } 597 while ((c = token(0)) == LWORD) 598 XPput(args, yylval.cp); 599 if (c != '\n' && c != ';') 600 syntaxerr((char *) 0); 601 if (XPsize(args) == 0) { 602 XPfree(args); 603 return NULL; 604 } else { 605 XPput(args, NULL); 606 return (char **) XPclose(args); 607 } 608 } 609 610 /* 611 * supporting functions 612 */ 613 614 static struct op * 615 block(int type, struct op *t1, struct op *t2, char **wp) 616 { 617 struct op *t; 618 619 t = newtp(type); 620 t->left = t1; 621 t->right = t2; 622 t->vars = wp; 623 return (t); 624 } 625 626 const struct tokeninfo { 627 const char *name; 628 short val; 629 short reserved; 630 } tokentab[] = { 631 /* Reserved words */ 632 { "if", IF, true }, 633 { "then", THEN, true }, 634 { "else", ELSE, true }, 635 { "elif", ELIF, true }, 636 { "fi", FI, true }, 637 { "case", CASE, true }, 638 { "esac", ESAC, true }, 639 { "for", FOR, true }, 640 { "select", SELECT, true }, 641 { "while", WHILE, true }, 642 { "until", UNTIL, true }, 643 { "do", DO, true }, 644 { "done", DONE, true }, 645 { "in", IN, true }, 646 { "function", FUNCTION, true }, 647 { "time", TIME, true }, 648 { "{", '{', true }, 649 { "}", '}', true }, 650 { "!", BANG, true }, 651 { "[[", DBRACKET, true }, 652 /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */ 653 { "&&", LOGAND, false }, 654 { "||", LOGOR, false }, 655 { ";;", BREAK, false }, 656 { "((", MDPAREN, false }, 657 { "|&", COPROC, false }, 658 /* and some special cases... */ 659 { "newline", '\n', false }, 660 { 0 } 661 }; 662 663 void 664 initkeywords(void) 665 { 666 struct tokeninfo const *tt; 667 struct tbl *p; 668 669 ktinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */ 670 for (tt = tokentab; tt->name; tt++) { 671 if (tt->reserved) { 672 p = ktenter(&keywords, tt->name, hash(tt->name)); 673 p->flag |= DEFINED|ISSET; 674 p->type = CKEYWD; 675 p->val.i = tt->val; 676 } 677 } 678 } 679 680 static void 681 syntaxerr(const char *what) 682 { 683 char redir[6]; /* 2<<- is the longest redirection, I think */ 684 const char *s; 685 struct tokeninfo const *tt; 686 int c; 687 688 if (!what) 689 what = "unexpected"; 690 REJECT; 691 c = token(0); 692 Again: 693 switch (c) { 694 case 0: 695 if (nesting.start_token) { 696 c = nesting.start_token; 697 source->errline = nesting.start_line; 698 what = "unmatched"; 699 goto Again; 700 } 701 /* don't quote the EOF */ 702 yyerror("syntax error: unexpected EOF\n"); 703 /* NOTREACHED */ 704 705 case LWORD: 706 s = snptreef((char *) 0, 32, "%S", yylval.cp); 707 break; 708 709 case REDIR: 710 s = snptreef(redir, sizeof(redir), "%R", yylval.iop); 711 break; 712 713 default: 714 for (tt = tokentab; tt->name; tt++) 715 if (tt->val == c) 716 break; 717 if (tt->name) 718 s = tt->name; 719 else { 720 if (c > 0 && c < 256) { 721 redir[0] = c; 722 redir[1] = '\0'; 723 } else 724 shf_snprintf(redir, sizeof(redir), 725 "?%d", c); 726 s = redir; 727 } 728 } 729 yyerror("syntax error: `%s' %s\n", s, what); 730 } 731 732 static void 733 nesting_push(struct nesting_state *save, int tok) 734 { 735 *save = nesting; 736 nesting.start_token = tok; 737 nesting.start_line = source->line; 738 } 739 740 static void 741 nesting_pop(struct nesting_state *saved) 742 { 743 nesting = *saved; 744 } 745 746 static struct op * 747 newtp(int type) 748 { 749 struct op *t; 750 751 t = (struct op *) alloc(sizeof(*t), ATEMP); 752 t->type = type; 753 t->u.evalflags = 0; 754 t->args = t->vars = NULL; 755 t->ioact = NULL; 756 t->left = t->right = NULL; 757 t->str = NULL; 758 return (t); 759 } 760 761 struct op * 762 compile(Source *s) 763 { 764 nesting.start_token = 0; 765 nesting.start_line = 0; 766 herep = heres; 767 source = s; 768 yyparse(); 769 return outtree; 770 } 771 772 /* This kludge exists to take care of sh/at&t ksh oddity in which 773 * the arguments of alias/export/readonly/typeset have no field 774 * splitting, file globbing, or (normal) tilde expansion done. 775 * at&t ksh seems to do something similar to this since 776 * $ touch a=a; typeset a=[ab]; echo "$a" 777 * a=[ab] 778 * $ x=typeset; $x a=[ab]; echo "$a" 779 * a=a 780 * $ 781 */ 782 static int 783 assign_command(char *s) 784 { 785 if (Flag(FPOSIX) || !*s) 786 return 0; 787 return (strcmp(s, "alias") == 0) || 788 (strcmp(s, "export") == 0) || 789 (strcmp(s, "readonly") == 0) || 790 (strcmp(s, "typeset") == 0); 791 } 792 793 /* Check if we are in the middle of reading an alias */ 794 static int 795 inalias(struct source *s) 796 { 797 for (; s && s->type == SALIAS; s = s->next) 798 if (!(s->flags & SF_ALIASEND)) 799 return 1; 800 return 0; 801 } 802 803 804 /* Order important - indexed by Test_meta values 805 * Note that ||, &&, ( and ) can't appear in as unquoted strings 806 * in normal shell input, so these can be interpreted unambiguously 807 * in the evaluation pass. 808 */ 809 static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS }; 810 static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS }; 811 static const char dbtest_not[] = { CHAR, '!', EOS }; 812 static const char dbtest_oparen[] = { CHAR, '(', EOS }; 813 static const char dbtest_cparen[] = { CHAR, ')', EOS }; 814 const char *const dbtest_tokens[] = { 815 dbtest_or, dbtest_and, dbtest_not, 816 dbtest_oparen, dbtest_cparen 817 }; 818 const char db_close[] = { CHAR, ']', CHAR, ']', EOS }; 819 const char db_lthan[] = { CHAR, '<', EOS }; 820 const char db_gthan[] = { CHAR, '>', EOS }; 821 822 /* Test if the current token is a whatever. Accepts the current token if 823 * it is. Returns 0 if it is not, non-zero if it is (in the case of 824 * TM_UNOP and TM_BINOP, the returned value is a Test_op). 825 */ 826 static int 827 dbtestp_isa(Test_env *te, Test_meta meta) 828 { 829 int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); 830 int uqword = 0; 831 char *save = (char *) 0; 832 int ret = 0; 833 834 /* unquoted word? */ 835 uqword = c == LWORD && *ident; 836 837 if (meta == TM_OR) 838 ret = c == LOGOR; 839 else if (meta == TM_AND) 840 ret = c == LOGAND; 841 else if (meta == TM_NOT) 842 ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0; 843 else if (meta == TM_OPAREN) 844 ret = c == '(' /*)*/; 845 else if (meta == TM_CPAREN) 846 ret = c == /*(*/ ')'; 847 else if (meta == TM_UNOP || meta == TM_BINOP) { 848 if (meta == TM_BINOP && c == REDIR && 849 (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { 850 ret = 1; 851 save = wdcopy(yylval.iop->flag == IOREAD ? 852 db_lthan : db_gthan, ATEMP); 853 } else if (uqword && (ret = (int) test_isop(te, meta, ident))) 854 save = yylval.cp; 855 } else /* meta == TM_END */ 856 ret = uqword && strcmp(yylval.cp, db_close) == 0; 857 if (ret) { 858 ACCEPT; 859 if (meta != TM_END) { 860 if (!save) 861 save = wdcopy(dbtest_tokens[(int) meta], ATEMP); 862 XPput(*te->pos.av, save); 863 } 864 } 865 return ret; 866 } 867 868 static const char * 869 dbtestp_getopnd(Test_env *te, Test_op op, int do_eval) 870 { 871 int c = tpeek(ARRAYVAR); 872 873 if (c != LWORD) 874 return (const char *) 0; 875 876 ACCEPT; 877 XPput(*te->pos.av, yylval.cp); 878 879 return null; 880 } 881 882 static int 883 dbtestp_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, 884 int do_eval) 885 { 886 return 1; 887 } 888 889 static void 890 dbtestp_error(Test_env *te, int offset, const char *msg) 891 { 892 te->flags |= TEF_ERROR; 893 894 if (offset < 0) { 895 REJECT; 896 /* Kludgy to say the least... */ 897 symbol = LWORD; 898 yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) + 899 offset); 900 } 901 syntaxerr(msg); 902 } 903