1 /* $OpenBSD: syn.c,v 1.38 2015/12/30 09:07:00 tedu Exp $ */ 2 3 /* 4 * shell parser (C version) 5 */ 6 7 #include <string.h> 8 9 #include "sh.h" 10 #include "c_test.h" 11 12 struct nesting_state { 13 int start_token; /* token than began nesting (eg, FOR) */ 14 int start_line; /* line nesting began on */ 15 }; 16 17 static void yyparse(void); 18 static struct op *pipeline(int); 19 static struct op *andor(void); 20 static struct op *c_list(int); 21 static struct ioword *synio(int); 22 static void musthave(int, int); 23 static struct op *nested(int, int, int); 24 static struct op *get_command(int); 25 static struct op *dogroup(void); 26 static struct op *thenpart(void); 27 static struct op *elsepart(void); 28 static struct op *caselist(void); 29 static struct op *casepart(int); 30 static struct op *function_body(char *, int); 31 static char ** wordlist(void); 32 static struct op *block(int, struct op *, struct op *, char **); 33 static struct op *newtp(int); 34 static void syntaxerr(const char *) __attribute__((__noreturn__)); 35 static void nesting_push(struct nesting_state *, int); 36 static void nesting_pop(struct nesting_state *); 37 static int assign_command(char *); 38 static int inalias(struct source *); 39 static int dbtestp_isa(Test_env *, Test_meta); 40 static const char *dbtestp_getopnd(Test_env *, Test_op, int); 41 static int dbtestp_eval(Test_env *, Test_op, const char *, const char *, 42 int); 43 static void dbtestp_error(Test_env *, int, const char *); 44 45 static struct op *outtree; /* yyparse output */ 46 47 static struct nesting_state nesting; /* \n changed to ; */ 48 49 static int reject; /* token(cf) gets symbol again */ 50 static int symbol; /* yylex value */ 51 52 #define token(cf) \ 53 ((reject) ? (reject = false, symbol) : (symbol = yylex(cf))) 54 #define tpeek(cf) \ 55 ((reject) ? (symbol) : (reject = true, symbol = yylex(cf))) 56 57 static void 58 yyparse(void) 59 { 60 int c; 61 62 reject = false; 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(NULL); 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(NULL); 82 if (tl == NULL) 83 t = tl = block(TPIPE, t, p, NULL); 84 else 85 tl = tl->right = block(TPIPE, tl->right, p, NULL); 86 } 87 reject = true; 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(NULL); 103 t = block(c == LOGAND? TAND: TOR, t, p, NULL); 104 } 105 reject = true; 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, NULL, NULL); 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, NULL); 138 else 139 tl = tl->right = block(TLIST, tl->right, p, NULL); 140 if (!have_sep) 141 break; 142 } 143 reject = true; 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 reject = false; 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(NULL); 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, NULL, NULL)); 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 = areallocarray(NULL, NUFILE + 1, 201 sizeof(struct ioword *), 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 = true; 209 afree(iops, ATEMP); 210 XPfree(args); 211 XPfree(vars); 212 return NULL; /* empty line */ 213 214 case LWORD: 215 case REDIR: 216 reject = true; 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 reject = false; 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 reject = false; 253 goto Subshell; 254 } 255 /* Must be a function */ 256 if (iopn != 0 || XPsize(args) != 1 || 257 XPsize(vars) != 0) 258 syntaxerr(NULL); 259 reject = false; 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 reject = false; 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 reject = false; 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 == NULL) 360 syntaxerr(NULL); 361 t = block(TBANG, NULL, t, NULL); 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, NULL, NULL); 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(iops, ATEMP); 389 t->ioact = NULL; 390 } else { 391 iops[iopn++] = NULL; 392 iops = areallocarray(iops, iopn, 393 sizeof(struct ioword *), 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(NULL); 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(NULL); 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(NULL); 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 = true; 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(NULL); 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 = true; 508 do { 509 musthave(LWORD, 0); 510 XPput(ptns, yylval.cp); 511 } while ((c = token(0)) == '|'); 512 reject = true; 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 = true; 556 } 557 558 old_func_parse = genv->flags & EF_FUNC_PARSE; 559 genv->flags |= EF_FUNC_PARSE; 560 if ((t->left = get_command(CONTIN)) == NULL) { 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 = areallocarray(NULL, 2, sizeof(char *), ATEMP); 569 t->left->args[0] = alloc(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] = NULL; 574 t->left->vars = alloc(sizeof(char *), ATEMP); 575 t->left->vars[0] = NULL; 576 t->left->lineno = 1; 577 } 578 if (!old_func_parse) 579 genv->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 = true; 595 return NULL; 596 } 597 while ((c = token(0)) == LWORD) 598 XPput(args, yylval.cp); 599 if (c != '\n' && c != ';') 600 syntaxerr(NULL); 601 XPput(args, NULL); 602 return (char **) XPclose(args); 603 } 604 605 /* 606 * supporting functions 607 */ 608 609 static struct op * 610 block(int type, struct op *t1, struct op *t2, char **wp) 611 { 612 struct op *t; 613 614 t = newtp(type); 615 t->left = t1; 616 t->right = t2; 617 t->vars = wp; 618 return (t); 619 } 620 621 const struct tokeninfo { 622 const char *name; 623 short val; 624 short reserved; 625 } tokentab[] = { 626 /* Reserved words */ 627 { "if", IF, true }, 628 { "then", THEN, true }, 629 { "else", ELSE, true }, 630 { "elif", ELIF, true }, 631 { "fi", FI, true }, 632 { "case", CASE, true }, 633 { "esac", ESAC, true }, 634 { "for", FOR, true }, 635 { "select", SELECT, true }, 636 { "while", WHILE, true }, 637 { "until", UNTIL, true }, 638 { "do", DO, true }, 639 { "done", DONE, true }, 640 { "in", IN, true }, 641 { "function", FUNCTION, true }, 642 { "time", TIME, true }, 643 { "{", '{', true }, 644 { "}", '}', true }, 645 { "!", BANG, true }, 646 { "[[", DBRACKET, true }, 647 /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */ 648 { "&&", LOGAND, false }, 649 { "||", LOGOR, false }, 650 { ";;", BREAK, false }, 651 { "((", MDPAREN, false }, 652 { "|&", COPROC, false }, 653 /* and some special cases... */ 654 { "newline", '\n', false }, 655 { 0 } 656 }; 657 658 void 659 initkeywords(void) 660 { 661 struct tokeninfo const *tt; 662 struct tbl *p; 663 664 ktinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */ 665 for (tt = tokentab; tt->name; tt++) { 666 if (tt->reserved) { 667 p = ktenter(&keywords, tt->name, hash(tt->name)); 668 p->flag |= DEFINED|ISSET; 669 p->type = CKEYWD; 670 p->val.i = tt->val; 671 } 672 } 673 } 674 675 static void 676 syntaxerr(const char *what) 677 { 678 char redir[6]; /* 2<<- is the longest redirection, I think */ 679 const char *s; 680 struct tokeninfo const *tt; 681 int c; 682 683 if (!what) 684 what = "unexpected"; 685 reject = true; 686 c = token(0); 687 Again: 688 switch (c) { 689 case 0: 690 if (nesting.start_token) { 691 c = nesting.start_token; 692 source->errline = nesting.start_line; 693 what = "unmatched"; 694 goto Again; 695 } 696 /* don't quote the EOF */ 697 yyerror("syntax error: unexpected EOF\n"); 698 /* NOTREACHED */ 699 700 case LWORD: 701 s = snptreef(NULL, 32, "%S", yylval.cp); 702 break; 703 704 case REDIR: 705 s = snptreef(redir, sizeof(redir), "%R", yylval.iop); 706 break; 707 708 default: 709 for (tt = tokentab; tt->name; tt++) 710 if (tt->val == c) 711 break; 712 if (tt->name) 713 s = tt->name; 714 else { 715 if (c > 0 && c < 256) { 716 redir[0] = c; 717 redir[1] = '\0'; 718 } else 719 shf_snprintf(redir, sizeof(redir), 720 "?%d", c); 721 s = redir; 722 } 723 } 724 yyerror("syntax error: `%s' %s\n", s, what); 725 } 726 727 static void 728 nesting_push(struct nesting_state *save, int tok) 729 { 730 *save = nesting; 731 nesting.start_token = tok; 732 nesting.start_line = source->line; 733 } 734 735 static void 736 nesting_pop(struct nesting_state *saved) 737 { 738 nesting = *saved; 739 } 740 741 static struct op * 742 newtp(int type) 743 { 744 struct op *t; 745 746 t = alloc(sizeof(*t), ATEMP); 747 t->type = type; 748 t->u.evalflags = 0; 749 t->args = t->vars = NULL; 750 t->ioact = NULL; 751 t->left = t->right = NULL; 752 t->str = NULL; 753 return (t); 754 } 755 756 struct op * 757 compile(Source *s) 758 { 759 nesting.start_token = 0; 760 nesting.start_line = 0; 761 herep = heres; 762 source = s; 763 yyparse(); 764 return outtree; 765 } 766 767 /* This kludge exists to take care of sh/at&t ksh oddity in which 768 * the arguments of alias/export/readonly/typeset have no field 769 * splitting, file globbing, or (normal) tilde expansion done. 770 * at&t ksh seems to do something similar to this since 771 * $ touch a=a; typeset a=[ab]; echo "$a" 772 * a=[ab] 773 * $ x=typeset; $x a=[ab]; echo "$a" 774 * a=a 775 * $ 776 */ 777 static int 778 assign_command(char *s) 779 { 780 if (Flag(FPOSIX) || !*s) 781 return 0; 782 return (strcmp(s, "alias") == 0) || 783 (strcmp(s, "export") == 0) || 784 (strcmp(s, "readonly") == 0) || 785 (strcmp(s, "typeset") == 0); 786 } 787 788 /* Check if we are in the middle of reading an alias */ 789 static int 790 inalias(struct source *s) 791 { 792 for (; s && s->type == SALIAS; s = s->next) 793 if (!(s->flags & SF_ALIASEND)) 794 return 1; 795 return 0; 796 } 797 798 799 /* Order important - indexed by Test_meta values 800 * Note that ||, &&, ( and ) can't appear in as unquoted strings 801 * in normal shell input, so these can be interpreted unambiguously 802 * in the evaluation pass. 803 */ 804 static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS }; 805 static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS }; 806 static const char dbtest_not[] = { CHAR, '!', EOS }; 807 static const char dbtest_oparen[] = { CHAR, '(', EOS }; 808 static const char dbtest_cparen[] = { CHAR, ')', EOS }; 809 const char *const dbtest_tokens[] = { 810 dbtest_or, dbtest_and, dbtest_not, 811 dbtest_oparen, dbtest_cparen 812 }; 813 const char db_close[] = { CHAR, ']', CHAR, ']', EOS }; 814 const char db_lthan[] = { CHAR, '<', EOS }; 815 const char db_gthan[] = { CHAR, '>', EOS }; 816 817 /* Test if the current token is a whatever. Accepts the current token if 818 * it is. Returns 0 if it is not, non-zero if it is (in the case of 819 * TM_UNOP and TM_BINOP, the returned value is a Test_op). 820 */ 821 static int 822 dbtestp_isa(Test_env *te, Test_meta meta) 823 { 824 int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); 825 int uqword = 0; 826 char *save = NULL; 827 int ret = 0; 828 829 /* unquoted word? */ 830 uqword = c == LWORD && *ident; 831 832 if (meta == TM_OR) 833 ret = c == LOGOR; 834 else if (meta == TM_AND) 835 ret = c == LOGAND; 836 else if (meta == TM_NOT) 837 ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0; 838 else if (meta == TM_OPAREN) 839 ret = c == '(' /*)*/; 840 else if (meta == TM_CPAREN) 841 ret = c == /*(*/ ')'; 842 else if (meta == TM_UNOP || meta == TM_BINOP) { 843 if (meta == TM_BINOP && c == REDIR && 844 (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { 845 ret = 1; 846 save = wdcopy(yylval.iop->flag == IOREAD ? 847 db_lthan : db_gthan, ATEMP); 848 } else if (uqword && (ret = (int) test_isop(te, meta, ident))) 849 save = yylval.cp; 850 } else /* meta == TM_END */ 851 ret = uqword && strcmp(yylval.cp, db_close) == 0; 852 if (ret) { 853 reject = false; 854 if (meta != TM_END) { 855 if (!save) 856 save = wdcopy(dbtest_tokens[(int) meta], ATEMP); 857 XPput(*te->pos.av, save); 858 } 859 } 860 return ret; 861 } 862 863 static const char * 864 dbtestp_getopnd(Test_env *te, Test_op op, int do_eval) 865 { 866 int c = tpeek(ARRAYVAR); 867 868 if (c != LWORD) 869 return NULL; 870 871 reject = false; 872 XPput(*te->pos.av, yylval.cp); 873 874 return null; 875 } 876 877 static int 878 dbtestp_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, 879 int do_eval) 880 { 881 return 1; 882 } 883 884 static void 885 dbtestp_error(Test_env *te, int offset, const char *msg) 886 { 887 te->flags |= TEF_ERROR; 888 889 if (offset < 0) { 890 reject = true; 891 /* Kludgy to say the least... */ 892 symbol = LWORD; 893 yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) + 894 offset); 895 } 896 syntaxerr(msg); 897 } 898