1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1986-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 /* 22 * Glenn Fowler 23 * AT&T Research 24 * 25 * preprocessor expression evaluation support 26 */ 27 28 #include "pplib.h" 29 30 #include <regex.h> 31 32 #define lex(c) ((((c)=peektoken)>=0?(peektoken=(-1)):((c)=pplex())),(c)) 33 #define unlex(c) (peektoken=(c)) 34 35 static int peektoken; /* expression lookahead token */ 36 static char* errmsg; /* subexpr() error message */ 37 38 /* 39 * exists predicate evaluation 40 */ 41 42 static int 43 exists(int op, char* pred, register char* args) 44 { 45 register int c; 46 register int type; 47 char* pptoken; 48 long state; 49 char file[MAXTOKEN + 1]; 50 51 state = (pp.state & ~DISABLE); 52 PUSH_STRING(args); 53 pptoken = pp.token; 54 pp.token = file; 55 pp.state |= HEADER|PASSEOF; 56 type = pplex(); 57 pp.state &= ~HEADER; 58 pp.token = pptoken; 59 switch (type) 60 { 61 case T_STRING: 62 case T_HEADER: 63 break; 64 default: 65 error(1, "%s: \"...\" or <...> argument expected", pred); 66 c = 0; 67 goto done; 68 } 69 if (op == X_EXISTS) 70 { 71 if ((c = pplex()) == ',') 72 { 73 while ((c = pplex()) == T_STRING) 74 { 75 if (pathaccess(pp.path, pp.token, file, NiL, 0)) 76 { 77 pathcanon(pp.path, 0); 78 message((-2, "%s: %s found", pred, pp.path)); 79 c = 1; 80 goto done; 81 } 82 if ((c = pplex()) != ',') break; 83 } 84 if (c) error(1, "%s: \"...\" arguments expected", pred); 85 strcpy(pp.path, file); 86 message((-2, "%s: %s not found", pred, file)); 87 c = 0; 88 } 89 else c = ppsearch(file, type, SEARCH_EXISTS) >= 0; 90 } 91 else 92 { 93 register struct ppfile* fp; 94 95 fp = ppsetfile(file); 96 c = fp->flags || fp->guard == INC_IGNORE; 97 } 98 done: 99 while (pplex()); 100 pp.state = state; 101 return c; 102 } 103 104 /* 105 * strcmp/match predicate evaluation 106 */ 107 108 static int 109 compare(char* pred, char* args, int match) 110 { 111 register int c; 112 char* pptoken; 113 long state; 114 regex_t re; 115 char tmp[MAXTOKEN + 1]; 116 117 state = (pp.state & ~DISABLE); 118 PUSH_STRING(args); 119 pp.state |= PASSEOF; 120 pptoken = pp.token; 121 pp.token = tmp; 122 if (!pplex()) 123 goto bad; 124 pp.token = pptoken; 125 if (pplex() != ',' || !pplex()) 126 goto bad; 127 if (!match) 128 c = strcmp(tmp, pp.token); 129 else if ((c = regcomp(&re, pp.token, REG_AUGMENTED|REG_LENIENT|REG_NULL)) || (c = regexec(&re, tmp, NiL, 0, 0)) && c != REG_NOMATCH) 130 regfatal(&re, 3, c); 131 else 132 { 133 c = !c; 134 regfree(&re); 135 } 136 if ((pp.state & PASSEOF) && pplex()) 137 goto bad; 138 pp.state = state; 139 return c; 140 bad: 141 pp.token = pptoken; 142 error(2, "%s: 2 arguments expected", pred); 143 while (pplex()); 144 pp.state = state; 145 return 0; 146 } 147 148 /* 149 * #if predicate parse and evaluation 150 */ 151 152 static int 153 predicate(int warn) 154 { 155 register char* args; 156 register struct pplist* p; 157 register struct ppsymbol* sym; 158 register int type; 159 int index; 160 161 static char pred[MAXID + 1]; 162 163 /* 164 * first gather the args 165 */ 166 167 index = (int)hashref(pp.strtab, pp.token); 168 if (warn && peekchr() != '(') switch (index) 169 { 170 case X_DEFINED: 171 case X_EXISTS: 172 case X_INCLUDED: 173 case X_MATCH: 174 case X_NOTICED: 175 case X_OPTION: 176 case X_SIZEOF: 177 case X_STRCMP: 178 break; 179 default: 180 if (pp.macref) pprefmac(pp.token, REF_IF); 181 return 0; 182 } 183 strcpy(pred, pp.token); 184 pp.state |= DISABLE; 185 type = pppredargs(); 186 pp.state &= ~DISABLE; 187 switch (type) 188 { 189 case T_ID: 190 case T_STRING: 191 break; 192 default: 193 unlex(type); 194 /*FALLTHROUGH*/ 195 case 0: 196 if (index && !(pp.state & STRICT)) 197 error(1, "%s: predicate argument expected", pred); 198 if (pp.macref) pprefmac(pred, REF_IF); 199 return 0; 200 } 201 args = pp.args; 202 203 /* 204 * now evaluate 205 */ 206 207 debug((-6, "pred=%s args=%s", pred, args)); 208 if ((pp.state & STRICT) && !(pp.mode & HOSTED)) switch (index) 209 { 210 case X_DEFINED: 211 case X_SIZEOF: 212 break; 213 default: 214 error(1, "%s(%s): non-standard predicate test", pred, args); 215 return 0; 216 } 217 switch (index) 218 { 219 case X_DEFINED: 220 if (type != T_ID) error(1, "%s: identifier argument expected", pred); 221 else if ((sym = pprefmac(args, REF_IF)) && sym->macro) return 1; 222 else if (args[0] == '_' && args[1] == '_' && !strncmp(args, "__STDPP__", 9)) 223 { 224 if (pp.hosted == 1 && pp.in->prev->type == IN_FILE) 225 { 226 pp.mode |= HOSTED; 227 pp.flags |= PP_hosted; 228 } 229 return *(args + 9) ? (int)hashref(pp.strtab, args + 9) : 1; 230 } 231 break; 232 case X_EXISTS: 233 case X_INCLUDED: 234 return exists(index, pred, args); 235 case X_MATCH: 236 case X_STRCMP: 237 return compare(pred, args, index == X_MATCH); 238 case X_NOTICED: 239 if (type != T_ID) error(1, "%s: identifier argument expected", pred); 240 else if (((sym = pprefmac(args, REF_IF)) || (sym = ppsymref(pp.symtab, args))) && (sym->flags & SYM_NOTICED)) return 1; 241 break; 242 case X_OPTION: 243 return ppoption(args); 244 case X_SIZEOF: 245 error(2, "%s invalid in #%s expressions", pred, dirname(IF)); 246 break; 247 default: 248 if (warn && !(pp.mode & HOSTED) && (sym = ppsymref(pp.symtab, pred)) && (sym->flags & SYM_PREDICATE)) 249 error(1, "use #%s(%s) to disambiguate", pred, args); 250 if (p = (struct pplist*)hashget(pp.prdtab, pred)) 251 { 252 if (!*args) return 1; 253 while (p) 254 { 255 if (streq(p->value, args)) return 1; 256 p = p->next; 257 } 258 } 259 break; 260 } 261 return 0; 262 } 263 264 /* 265 * evaluate a long integer subexpression with precedence 266 * taken from the library routine streval() 267 * may be called recursively 268 * 269 * NOTE: all operands are evaluated as both the parse 270 * and evaluation are done on the fly 271 */ 272 273 static long 274 subexpr(register int precedence, int* pun) 275 { 276 register int c; 277 register long n; 278 register long x; 279 register int operand = 1; 280 int un = 0; 281 int xn; 282 283 switch (lex(c)) 284 { 285 case 0: 286 case '\n': 287 unlex(c); 288 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "more tokens expected"; 289 return 0; 290 case '-': 291 n = -subexpr(13, &un); 292 break; 293 case '+': 294 n = subexpr(13, &un); 295 break; 296 case '!': 297 n = !subexpr(13, &un); 298 break; 299 case '~': 300 n = ~subexpr(13, &un); 301 break; 302 default: 303 unlex(c); 304 n = 0; 305 operand = 0; 306 break; 307 } 308 un <<= 1; 309 for (;;) 310 { 311 switch (lex(c)) 312 { 313 case 0: 314 case '\n': 315 goto done; 316 case ')': 317 if (!precedence) 318 { 319 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "too many )'s"; 320 return 0; 321 } 322 goto done; 323 case '(': 324 n = subexpr(1, &un); 325 if (lex(c) != ')') 326 { 327 unlex(c); 328 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "closing ) expected"; 329 return 0; 330 } 331 gotoperand: 332 if (operand) 333 { 334 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operator expected"; 335 return 0; 336 } 337 operand = 1; 338 un <<= 1; 339 continue; 340 case '?': 341 if (precedence > 1) goto done; 342 un = 0; 343 if (lex(c) == ':') 344 { 345 if (!n) n = subexpr(2, &un); 346 else 347 { 348 x = pp.mode; 349 pp.mode |= INACTIVE; 350 subexpr(2, &xn); 351 pp.mode = x; 352 } 353 } 354 else 355 { 356 unlex(c); 357 x = subexpr(2, &xn); 358 if (lex(c) != ':') 359 { 360 unlex(c); 361 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = ": expected for ? operator"; 362 return 0; 363 } 364 if (n) 365 { 366 n = x; 367 un = xn; 368 subexpr(2, &xn); 369 } 370 else n = subexpr(2, &un); 371 } 372 break; 373 case ':': 374 goto done; 375 case T_ANDAND: 376 case T_OROR: 377 xn = (c == T_ANDAND) ? 4 : 3; 378 if (precedence >= xn) goto done; 379 if ((n != 0) == (c == T_ANDAND)) n = subexpr(xn, &un) != 0; 380 else 381 { 382 x = pp.mode; 383 pp.mode |= INACTIVE; 384 subexpr(xn, &un); 385 pp.mode = x; 386 } 387 un = 0; 388 break; 389 case '|': 390 if (precedence > 4) goto done; 391 n |= subexpr(5, &un); 392 break; 393 case '^': 394 if (precedence > 5) goto done; 395 n ^= subexpr(6, &un); 396 break; 397 case '&': 398 if (precedence > 6) goto done; 399 n &= subexpr(7, &un); 400 break; 401 case T_EQ: 402 case T_NE: 403 if (precedence > 7) goto done; 404 n = (n == subexpr(8, &un)) == (c == T_EQ); 405 un = 0; 406 break; 407 case '<': 408 case T_LE: 409 case T_GE: 410 case '>': 411 if (precedence > 8) goto done; 412 x = subexpr(9, &un); 413 switch (c) 414 { 415 case '<': 416 switch (un) 417 { 418 case 01: 419 n = n < (unsigned long)x; 420 break; 421 case 02: 422 n = (unsigned long)n < x; 423 break; 424 case 03: 425 n = (unsigned long)n < (unsigned long)x; 426 break; 427 default: 428 n = n < x; 429 break; 430 } 431 break; 432 case T_LE: 433 switch (un) 434 { 435 case 01: 436 n = n <= (unsigned long)x; 437 break; 438 case 02: 439 n = (unsigned long)n <= x; 440 break; 441 case 03: 442 n = (unsigned long)n <= (unsigned long)x; 443 break; 444 default: 445 n = n <= x; 446 break; 447 } 448 break; 449 case T_GE: 450 switch (un) 451 { 452 case 01: 453 n = n >= (unsigned long)x; 454 break; 455 case 02: 456 n = (unsigned long)n >= x; 457 break; 458 case 03: 459 n = (unsigned long)n >= (unsigned long)x; 460 break; 461 default: 462 n = n >= x; 463 break; 464 } 465 break; 466 case '>': 467 switch (un) 468 { 469 case 01: 470 n = n > (unsigned long)x; 471 break; 472 case 02: 473 n = (unsigned long)n > x; 474 break; 475 case 03: 476 n = (unsigned long)n > (unsigned long)x; 477 break; 478 default: 479 n = n > x; 480 break; 481 } 482 break; 483 } 484 un = 0; 485 break; 486 case T_LSHIFT: 487 case T_RSHIFT: 488 if (precedence > 9) goto done; 489 x = subexpr(10, &un); 490 if (c == T_LSHIFT) n <<= x; 491 else n >>= x; 492 un >>= 1; 493 break; 494 case '+': 495 case '-': 496 if (precedence > 10) goto done; 497 x = subexpr(11, &un); 498 if (c == '+') n += x; 499 else n -= x; 500 break; 501 case '*': 502 case '/': 503 case '%': 504 if (precedence > 11) goto done; 505 x = subexpr(12, &un); 506 if (c == '*') n *= x; 507 else if (x == 0) 508 { 509 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "divide by zero"; 510 return 0; 511 } 512 else if (c == '/') n /= x; 513 else n %= x; 514 break; 515 case '#': 516 pp.state |= DISABLE; 517 c = pplex(); 518 pp.state &= ~DISABLE; 519 if (c != T_ID) 520 { 521 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "# must precede a predicate identifier"; 522 return 0; 523 } 524 n = predicate(0); 525 goto gotoperand; 526 case T_ID: 527 n = predicate(1); 528 goto gotoperand; 529 case T_CHARCONST: 530 c = *(pp.toknxt - 1); 531 *(pp.toknxt - 1) = 0; 532 n = chrtoi(pp.token + 1); 533 *(pp.toknxt - 1) = c; 534 if (n & ~((1<<CHAR_BIT)-1)) 535 { 536 if (!(pp.mode & HOSTED)) 537 error(1, "'%s': multi-character character constants are not portable", pp.token); 538 } 539 #if CHAR_MIN < 0 540 else n = (char)n; 541 #endif 542 goto gotoperand; 543 case T_DECIMAL_U: 544 case T_DECIMAL_UL: 545 case T_OCTAL_U: 546 case T_OCTAL_UL: 547 case T_HEXADECIMAL_U: 548 case T_HEXADECIMAL_UL: 549 un |= 01; 550 /*FALLTHROUGH*/ 551 case T_DECIMAL: 552 case T_DECIMAL_L: 553 case T_OCTAL: 554 case T_OCTAL_L: 555 case T_HEXADECIMAL: 556 case T_HEXADECIMAL_L: 557 n = strtoul(pp.token, NiL, 0); 558 if ((unsigned long)n > LONG_MAX) un |= 01; 559 goto gotoperand; 560 case T_WCHARCONST: 561 n = chrtoi(pp.token); 562 goto gotoperand; 563 default: 564 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid token"; 565 return 0; 566 } 567 if (errmsg) return 0; 568 if (!operand) goto nooperand; 569 } 570 done: 571 unlex(c); 572 if (!operand) 573 { 574 nooperand: 575 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operand expected"; 576 return 0; 577 } 578 if (un) *pun |= 01; 579 return n; 580 } 581 582 /* 583 * preprocessor expression evaluator using modified streval(3) 584 * *pun!=0 if result is unsigned 585 */ 586 587 long 588 ppexpr(int* pun) 589 { 590 long n; 591 int opeektoken; 592 long ppstate; 593 594 ppstate = (pp.state & (CONDITIONAL|DISABLE|NOSPACE|STRIP)); 595 pp.state &= ~(DISABLE|STRIP); 596 pp.state |= CONDITIONAL|NOSPACE; 597 opeektoken = peektoken; 598 peektoken = -1; 599 *pun = 0; 600 n = subexpr(0, pun); 601 if (peektoken == ':' && !errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid use of :"; 602 if (errmsg) 603 { 604 error(2, "%s in expression", errmsg); 605 errmsg = 0; 606 n = 0; 607 } 608 peektoken = opeektoken; 609 pp.state &= ~(CONDITIONAL|NOSPACE); 610 pp.state |= ppstate; 611 if (*pun) debug((-4, "ppexpr() = %luU", n)); 612 else debug((-4, "ppexpr() = %ld", n)); 613 return n; 614 } 615 616 /* 617 * return non-zero if option s is set 618 */ 619 620 int 621 ppoption(char* s) 622 { 623 switch ((int)hashget(pp.strtab, s)) 624 { 625 case X_ALLMULTIPLE: 626 return pp.mode & ALLMULTIPLE; 627 case X_BUILTIN: 628 return pp.mode & BUILTIN; 629 case X_CATLITERAL: 630 return pp.mode & CATLITERAL; 631 case X_COMPATIBILITY: 632 return pp.state & COMPATIBILITY; 633 case X_DEBUG: 634 return -error_info.trace; 635 case X_ELSEIF: 636 return pp.option & ELSEIF; 637 case X_FINAL: 638 return pp.option & FINAL; 639 case X_HOSTDIR: 640 return pp.mode & HOSTED; 641 case X_HOSTED: 642 return pp.flags & PP_hosted; 643 case X_INITIAL: 644 return pp.option & INITIAL; 645 case X_KEYARGS: 646 return pp.option & KEYARGS; 647 case X_LINEBASE: 648 return pp.flags & PP_linebase; 649 case X_LINEFILE: 650 return pp.flags & PP_linefile; 651 case X_LINETYPE: 652 return pp.flags & PP_linetype; 653 case X_PLUSCOMMENT: 654 return pp.option & PLUSCOMMENT; 655 case X_PLUSPLUS: 656 return pp.option & PLUSPLUS; 657 case X_PLUSSPLICE: 658 return pp.option & PLUSSPLICE; 659 case X_PRAGMAEXPAND: 660 return pp.option & PRAGMAEXPAND; 661 case X_PREDEFINED: 662 return pp.option & PREDEFINED; 663 case X_PREFIX: 664 return pp.option & PREFIX; 665 case X_PROTOTYPED: 666 return pp.option & PROTOTYPED; 667 case X_READONLY: 668 return pp.mode & READONLY; 669 case X_REGUARD: 670 return pp.option & REGUARD; 671 case X_SPACEOUT: 672 return pp.state & SPACEOUT; 673 case X_SPLICECAT: 674 return pp.option & SPLICECAT; 675 case X_SPLICESPACE: 676 return pp.option & SPLICESPACE; 677 case X_STRICT: 678 return pp.state & STRICT; 679 case X_STRINGSPAN: 680 return pp.option & STRINGSPAN; 681 case X_STRINGSPLIT: 682 return pp.option & STRINGSPLIT; 683 case X_TEST: 684 return pp.test; 685 case X_TEXT: 686 return !(pp.state & NOTEXT); 687 case X_TRANSITION: 688 return pp.state & TRANSITION; 689 case X_TRUNCATE: 690 return pp.truncate; 691 case X_WARN: 692 return pp.state & WARN; 693 default: 694 if (pp.state & WARN) error(1, "%s: unknown option name", s); 695 return 0; 696 } 697 } 698