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