PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/usr/src/lib/libpp/common/ppexpr.c

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