1 /* $OpenBSD: syn.c,v 1.39 2018/04/24 08:25:16 kn 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 if (t->str) { 369 t->str = str_save(t->str, ATEMP); 370 } else { 371 t->str = alloc(2, ATEMP); 372 t->str[0] = '\0'; /* TF_* flags */ 373 t->str[1] = '\0'; 374 } 375 } 376 t = block(TTIME, t, NULL, NULL); 377 break; 378 379 case FUNCTION: 380 musthave(LWORD, 0); 381 t = function_body(yylval.cp, true); 382 break; 383 } 384 385 while ((iop = synio(syniocf)) != NULL) { 386 if (iopn >= NUFILE) 387 yyerror("too many redirections\n"); 388 iops[iopn++] = iop; 389 } 390 391 if (iopn == 0) { 392 afree(iops, ATEMP); 393 t->ioact = NULL; 394 } else { 395 iops[iopn++] = NULL; 396 iops = areallocarray(iops, iopn, 397 sizeof(struct ioword *), ATEMP); 398 t->ioact = iops; 399 } 400 401 if (t->type == TCOM || t->type == TDBRACKET) { 402 XPput(args, NULL); 403 t->args = (char **) XPclose(args); 404 XPput(vars, NULL); 405 t->vars = (char **) XPclose(vars); 406 } else { 407 XPfree(args); 408 XPfree(vars); 409 } 410 411 return t; 412 } 413 414 static struct op * 415 dogroup(void) 416 { 417 int c; 418 struct op *list; 419 420 c = token(CONTIN|KEYWORD|ALIAS); 421 /* A {...} can be used instead of do...done for for/select loops 422 * but not for while/until loops - we don't need to check if it 423 * is a while loop because it would have been parsed as part of 424 * the conditional command list... 425 */ 426 if (c == DO) 427 c = DONE; 428 else if (c == '{') 429 c = '}'; 430 else 431 syntaxerr(NULL); 432 list = c_list(true); 433 musthave(c, KEYWORD|ALIAS); 434 return list; 435 } 436 437 static struct op * 438 thenpart(void) 439 { 440 struct op *t; 441 442 musthave(THEN, KEYWORD|ALIAS); 443 t = newtp(0); 444 t->left = c_list(true); 445 if (t->left == NULL) 446 syntaxerr(NULL); 447 t->right = elsepart(); 448 return (t); 449 } 450 451 static struct op * 452 elsepart(void) 453 { 454 struct op *t; 455 456 switch (token(KEYWORD|ALIAS|VARASN)) { 457 case ELSE: 458 if ((t = c_list(true)) == NULL) 459 syntaxerr(NULL); 460 return (t); 461 462 case ELIF: 463 t = newtp(TELIF); 464 t->left = c_list(true); 465 t->right = thenpart(); 466 return (t); 467 468 default: 469 reject = true; 470 } 471 return NULL; 472 } 473 474 static struct op * 475 caselist(void) 476 { 477 struct op *t, *tl; 478 int c; 479 480 c = token(CONTIN|KEYWORD|ALIAS); 481 /* A {...} can be used instead of in...esac for case statements */ 482 if (c == IN) 483 c = ESAC; 484 else if (c == '{') 485 c = '}'; 486 else 487 syntaxerr(NULL); 488 t = tl = NULL; 489 while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */ 490 struct op *tc = casepart(c); 491 if (tl == NULL) 492 t = tl = tc, tl->right = NULL; 493 else 494 tl->right = tc, tl = tc; 495 } 496 musthave(c, KEYWORD|ALIAS); 497 return (t); 498 } 499 500 static struct op * 501 casepart(int endtok) 502 { 503 struct op *t; 504 int c; 505 XPtrV ptns; 506 507 XPinit(ptns, 16); 508 t = newtp(TPAT); 509 c = token(CONTIN|KEYWORD); /* no ALIAS here */ 510 if (c != '(') 511 reject = true; 512 do { 513 musthave(LWORD, 0); 514 XPput(ptns, yylval.cp); 515 } while ((c = token(0)) == '|'); 516 reject = true; 517 XPput(ptns, NULL); 518 t->vars = (char **) XPclose(ptns); 519 musthave(')', 0); 520 521 t->left = c_list(true); 522 /* Note: Posix requires the ;; */ 523 if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok) 524 musthave(BREAK, CONTIN|KEYWORD|ALIAS); 525 return (t); 526 } 527 528 static struct op * 529 function_body(char *name, 530 int ksh_func) /* function foo { ... } vs foo() { .. } */ 531 { 532 char *sname, *p; 533 struct op *t; 534 int old_func_parse; 535 536 sname = wdstrip(name); 537 /* Check for valid characters in name. posix and ksh93 say only 538 * allow [a-zA-Z_0-9] but this allows more as old pdksh's have 539 * allowed more (the following were never allowed: 540 * nul space nl tab $ ' " \ ` ( ) & | ; = < > 541 * C_QUOTE covers all but = and adds # [ ? *) 542 */ 543 for (p = sname; *p; p++) 544 if (ctype(*p, C_QUOTE) || *p == '=') 545 yyerror("%s: invalid function name\n", sname); 546 547 t = newtp(TFUNCT); 548 t->str = sname; 549 t->u.ksh_func = ksh_func; 550 t->lineno = source->line; 551 552 /* Note that POSIX allows only compound statements after foo(), sh and 553 * at&t ksh allow any command, go with the later since it shouldn't 554 * break anything. However, for function foo, at&t ksh only accepts 555 * an open-brace. 556 */ 557 if (ksh_func) { 558 musthave('{', CONTIN|KEYWORD|ALIAS); /* } */ 559 reject = true; 560 } 561 562 old_func_parse = genv->flags & EF_FUNC_PARSE; 563 genv->flags |= EF_FUNC_PARSE; 564 if ((t->left = get_command(CONTIN)) == NULL) { 565 /* 566 * Probably something like foo() followed by eof or ;. 567 * This is accepted by sh and ksh88. 568 * To make "typeset -f foo" work reliably (so its output can 569 * be used as input), we pretend there is a colon here. 570 */ 571 t->left = newtp(TCOM); 572 t->left->args = areallocarray(NULL, 2, sizeof(char *), ATEMP); 573 t->left->args[0] = alloc(3, ATEMP); 574 t->left->args[0][0] = CHAR; 575 t->left->args[0][1] = ':'; 576 t->left->args[0][2] = EOS; 577 t->left->args[1] = NULL; 578 t->left->vars = alloc(sizeof(char *), ATEMP); 579 t->left->vars[0] = NULL; 580 t->left->lineno = 1; 581 } 582 if (!old_func_parse) 583 genv->flags &= ~EF_FUNC_PARSE; 584 585 return t; 586 } 587 588 static char ** 589 wordlist(void) 590 { 591 int c; 592 XPtrV args; 593 594 XPinit(args, 16); 595 /* Posix does not do alias expansion here... */ 596 if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) { 597 if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */ 598 reject = true; 599 return NULL; 600 } 601 while ((c = token(0)) == LWORD) 602 XPput(args, yylval.cp); 603 if (c != '\n' && c != ';') 604 syntaxerr(NULL); 605 XPput(args, NULL); 606 return (char **) XPclose(args); 607 } 608 609 /* 610 * supporting functions 611 */ 612 613 static struct op * 614 block(int type, struct op *t1, struct op *t2, char **wp) 615 { 616 struct op *t; 617 618 t = newtp(type); 619 t->left = t1; 620 t->right = t2; 621 t->vars = wp; 622 return (t); 623 } 624 625 const struct tokeninfo { 626 const char *name; 627 short val; 628 short reserved; 629 } tokentab[] = { 630 /* Reserved words */ 631 { "if", IF, true }, 632 { "then", THEN, true }, 633 { "else", ELSE, true }, 634 { "elif", ELIF, true }, 635 { "fi", FI, true }, 636 { "case", CASE, true }, 637 { "esac", ESAC, true }, 638 { "for", FOR, true }, 639 { "select", SELECT, true }, 640 { "while", WHILE, true }, 641 { "until", UNTIL, true }, 642 { "do", DO, true }, 643 { "done", DONE, true }, 644 { "in", IN, true }, 645 { "function", FUNCTION, true }, 646 { "time", TIME, true }, 647 { "{", '{', true }, 648 { "}", '}', true }, 649 { "!", BANG, true }, 650 { "[[", DBRACKET, true }, 651 /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */ 652 { "&&", LOGAND, false }, 653 { "||", LOGOR, false }, 654 { ";;", BREAK, false }, 655 { "((", MDPAREN, false }, 656 { "|&", COPROC, false }, 657 /* and some special cases... */ 658 { "newline", '\n', false }, 659 { 0 } 660 }; 661 662 void 663 initkeywords(void) 664 { 665 struct tokeninfo const *tt; 666 struct tbl *p; 667 668 ktinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */ 669 for (tt = tokentab; tt->name; tt++) { 670 if (tt->reserved) { 671 p = ktenter(&keywords, tt->name, hash(tt->name)); 672 p->flag |= DEFINED|ISSET; 673 p->type = CKEYWD; 674 p->val.i = tt->val; 675 } 676 } 677 } 678 679 static void 680 syntaxerr(const char *what) 681 { 682 char redir[6]; /* 2<<- is the longest redirection, I think */ 683 const char *s; 684 struct tokeninfo const *tt; 685 int c; 686 687 if (!what) 688 what = "unexpected"; 689 reject = true; 690 c = token(0); 691 Again: 692 switch (c) { 693 case 0: 694 if (nesting.start_token) { 695 c = nesting.start_token; 696 source->errline = nesting.start_line; 697 what = "unmatched"; 698 goto Again; 699 } 700 /* don't quote the EOF */ 701 yyerror("syntax error: unexpected EOF\n"); 702 /* NOTREACHED */ 703 704 case LWORD: 705 s = snptreef(NULL, 32, "%S", yylval.cp); 706 break; 707 708 case REDIR: 709 s = snptreef(redir, sizeof(redir), "%R", yylval.iop); 710 break; 711 712 default: 713 for (tt = tokentab; tt->name; tt++) 714 if (tt->val == c) 715 break; 716 if (tt->name) 717 s = tt->name; 718 else { 719 if (c > 0 && c < 256) { 720 redir[0] = c; 721 redir[1] = '\0'; 722 } else 723 shf_snprintf(redir, sizeof(redir), 724 "?%d", c); 725 s = redir; 726 } 727 } 728 yyerror("syntax error: `%s' %s\n", s, what); 729 } 730 731 static void 732 nesting_push(struct nesting_state *save, int tok) 733 { 734 *save = nesting; 735 nesting.start_token = tok; 736 nesting.start_line = source->line; 737 } 738 739 static void 740 nesting_pop(struct nesting_state *saved) 741 { 742 nesting = *saved; 743 } 744 745 static struct op * 746 newtp(int type) 747 { 748 struct op *t; 749 750 t = alloc(sizeof(*t), ATEMP); 751 t->type = type; 752 t->u.evalflags = 0; 753 t->args = t->vars = NULL; 754 t->ioact = NULL; 755 t->left = t->right = NULL; 756 t->str = NULL; 757 return (t); 758 } 759 760 struct op * 761 compile(Source *s) 762 { 763 nesting.start_token = 0; 764 nesting.start_line = 0; 765 herep = heres; 766 source = s; 767 yyparse(); 768 return outtree; 769 } 770 771 /* This kludge exists to take care of sh/at&t ksh oddity in which 772 * the arguments of alias/export/readonly/typeset have no field 773 * splitting, file globbing, or (normal) tilde expansion done. 774 * at&t ksh seems to do something similar to this since 775 * $ touch a=a; typeset a=[ab]; echo "$a" 776 * a=[ab] 777 * $ x=typeset; $x a=[ab]; echo "$a" 778 * a=a 779 * $ 780 */ 781 static int 782 assign_command(char *s) 783 { 784 if (Flag(FPOSIX) || !*s) 785 return 0; 786 return (strcmp(s, "alias") == 0) || 787 (strcmp(s, "export") == 0) || 788 (strcmp(s, "readonly") == 0) || 789 (strcmp(s, "typeset") == 0); 790 } 791 792 /* Check if we are in the middle of reading an alias */ 793 static int 794 inalias(struct source *s) 795 { 796 for (; s && s->type == SALIAS; s = s->next) 797 if (!(s->flags & SF_ALIASEND)) 798 return 1; 799 return 0; 800 } 801 802 803 /* Order important - indexed by Test_meta values 804 * Note that ||, &&, ( and ) can't appear in as unquoted strings 805 * in normal shell input, so these can be interpreted unambiguously 806 * in the evaluation pass. 807 */ 808 static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS }; 809 static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS }; 810 static const char dbtest_not[] = { CHAR, '!', EOS }; 811 static const char dbtest_oparen[] = { CHAR, '(', EOS }; 812 static const char dbtest_cparen[] = { CHAR, ')', EOS }; 813 const char *const dbtest_tokens[] = { 814 dbtest_or, dbtest_and, dbtest_not, 815 dbtest_oparen, dbtest_cparen 816 }; 817 const char db_close[] = { CHAR, ']', CHAR, ']', EOS }; 818 const char db_lthan[] = { CHAR, '<', EOS }; 819 const char db_gthan[] = { CHAR, '>', EOS }; 820 821 /* Test if the current token is a whatever. Accepts the current token if 822 * it is. Returns 0 if it is not, non-zero if it is (in the case of 823 * TM_UNOP and TM_BINOP, the returned value is a Test_op). 824 */ 825 static int 826 dbtestp_isa(Test_env *te, Test_meta meta) 827 { 828 int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); 829 int uqword = 0; 830 char *save = NULL; 831 int ret = 0; 832 833 /* unquoted word? */ 834 uqword = c == LWORD && *ident; 835 836 if (meta == TM_OR) 837 ret = c == LOGOR; 838 else if (meta == TM_AND) 839 ret = c == LOGAND; 840 else if (meta == TM_NOT) 841 ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0; 842 else if (meta == TM_OPAREN) 843 ret = c == '(' /*)*/; 844 else if (meta == TM_CPAREN) 845 ret = c == /*(*/ ')'; 846 else if (meta == TM_UNOP || meta == TM_BINOP) { 847 if (meta == TM_BINOP && c == REDIR && 848 (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { 849 ret = 1; 850 save = wdcopy(yylval.iop->flag == IOREAD ? 851 db_lthan : db_gthan, ATEMP); 852 } else if (uqword && (ret = (int) test_isop(te, meta, ident))) 853 save = yylval.cp; 854 } else /* meta == TM_END */ 855 ret = uqword && strcmp(yylval.cp, db_close) == 0; 856 if (ret) { 857 reject = false; 858 if (meta != TM_END) { 859 if (!save) 860 save = wdcopy(dbtest_tokens[(int) meta], ATEMP); 861 XPput(*te->pos.av, save); 862 } 863 } 864 return ret; 865 } 866 867 static const char * 868 dbtestp_getopnd(Test_env *te, Test_op op, int do_eval) 869 { 870 int c = tpeek(ARRAYVAR); 871 872 if (c != LWORD) 873 return NULL; 874 875 reject = false; 876 XPput(*te->pos.av, yylval.cp); 877 878 return null; 879 } 880 881 static int 882 dbtestp_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, 883 int do_eval) 884 { 885 return 1; 886 } 887 888 static void 889 dbtestp_error(Test_env *te, int offset, const char *msg) 890 { 891 te->flags |= TEF_ERROR; 892 893 if (offset < 0) { 894 reject = true; 895 /* Kludgy to say the least... */ 896 symbol = LWORD; 897 yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) + 898 offset); 899 } 900 syntaxerr(msg); 901 } 902