Merge commit '1f1540205fa6366266184180654434272c425ac2'
[unleashed.git] / usr / src / lib / libpp / common / ppexpr.c
blob225034b97df4a71d401cb6967a7f2867df3cd20d
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
22 * Glenn Fowler
23 * AT&T Research
25 * preprocessor expression evaluation support
28 #include "pplib.h"
30 #include <regex.h>
32 #define lex(c) ((((c)=peektoken)>=0?(peektoken=(-1)):((c)=pplex())),(c))
33 #define unlex(c) (peektoken=(c))
35 static int peektoken; /* expression lookahead token */
36 static char* errmsg; /* subexpr() error message */
39 * exists predicate evaluation
42 static int
43 exists(int op, char* pred, register char* args)
45 register int c;
46 register int type;
47 char* pptoken;
48 long state;
49 char file[MAXTOKEN + 1];
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)
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;
69 if (op == X_EXISTS)
71 if ((c = pplex()) == ',')
73 while ((c = pplex()) == T_STRING)
75 if (pathaccess(pp.path, pp.token, file, NiL, 0))
77 pathcanon(pp.path, 0);
78 message((-2, "%s: %s found", pred, pp.path));
79 c = 1;
80 goto done;
82 if ((c = pplex()) != ',') break;
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;
89 else c = ppsearch(file, type, SEARCH_EXISTS) >= 0;
91 else
93 register struct ppfile* fp;
95 fp = ppsetfile(file);
96 c = fp->flags || fp->guard == INC_IGNORE;
98 done:
99 while (pplex());
100 pp.state = state;
101 return c;
105 * strcmp/match predicate evaluation
108 static int
109 compare(char* pred, char* args, int match)
111 register int c;
112 char* pptoken;
113 long state;
114 regex_t re;
115 char tmp[MAXTOKEN + 1];
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
133 c = !c;
134 regfree(&re);
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;
149 * #if predicate parse and evaluation
152 static int
153 predicate(int warn)
155 register char* args;
156 register struct pplist* p;
157 register struct ppsymbol* sym;
158 register int type;
159 int index;
161 static char pred[MAXID + 1];
164 * first gather the args
167 index = (int)hashref(pp.strtab, pp.token);
168 if (warn && peekchr() != '(') switch (index)
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;
183 strcpy(pred, pp.token);
184 pp.state |= DISABLE;
185 type = pppredargs();
186 pp.state &= ~DISABLE;
187 switch (type)
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;
201 args = pp.args;
204 * now evaluate
207 debug((-6, "pred=%s args=%s", pred, args));
208 if ((pp.state & STRICT) && !(pp.mode & HOSTED)) switch (index)
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;
217 switch (index)
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))
224 if (pp.hosted == 1 && pp.in->prev->type == IN_FILE)
226 pp.mode |= HOSTED;
227 pp.flags |= PP_hosted;
229 return *(args + 9) ? (int)hashref(pp.strtab, args + 9) : 1;
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))
252 if (!*args) return 1;
253 while (p)
255 if (streq(p->value, args)) return 1;
256 p = p->next;
259 break;
261 return 0;
265 * evaluate a long integer subexpression with precedence
266 * taken from the library routine streval()
267 * may be called recursively
269 * NOTE: all operands are evaluated as both the parse
270 * and evaluation are done on the fly
273 static long
274 subexpr(register int precedence, int* pun)
276 register int c;
277 register long n;
278 register long x;
279 register int operand = 1;
280 int un = 0;
281 int xn;
283 switch (lex(c))
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;
308 un <<= 1;
309 for (;;)
311 switch (lex(c))
313 case 0:
314 case '\n':
315 goto done;
316 case ')':
317 if (!precedence)
319 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "too many )'s";
320 return 0;
322 goto done;
323 case '(':
324 n = subexpr(1, &un);
325 if (lex(c) != ')')
327 unlex(c);
328 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "closing ) expected";
329 return 0;
331 gotoperand:
332 if (operand)
334 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operator expected";
335 return 0;
337 operand = 1;
338 un <<= 1;
339 continue;
340 case '?':
341 if (precedence > 1) goto done;
342 un = 0;
343 if (lex(c) == ':')
345 if (!n) n = subexpr(2, &un);
346 else
348 x = pp.mode;
349 pp.mode |= INACTIVE;
350 subexpr(2, &xn);
351 pp.mode = x;
354 else
356 unlex(c);
357 x = subexpr(2, &xn);
358 if (lex(c) != ':')
360 unlex(c);
361 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = ": expected for ? operator";
362 return 0;
364 if (n)
366 n = x;
367 un = xn;
368 subexpr(2, &xn);
370 else n = subexpr(2, &un);
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
382 x = pp.mode;
383 pp.mode |= INACTIVE;
384 subexpr(xn, &un);
385 pp.mode = x;
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)
415 case '<':
416 switch (un)
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;
431 break;
432 case T_LE:
433 switch (un)
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;
448 break;
449 case T_GE:
450 switch (un)
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;
465 break;
466 case '>':
467 switch (un)
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;
482 break;
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)
509 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "divide by zero";
510 return 0;
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)
521 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "# must precede a predicate identifier";
522 return 0;
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))
536 if (!(pp.mode & HOSTED))
537 error(1, "'%s': multi-character character constants are not portable", pp.token);
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;
567 if (errmsg) return 0;
568 if (!operand) goto nooperand;
570 done:
571 unlex(c);
572 if (!operand)
574 nooperand:
575 if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operand expected";
576 return 0;
578 if (un) *pun |= 01;
579 return n;
583 * preprocessor expression evaluator using modified streval(3)
584 * *pun!=0 if result is unsigned
587 long
588 ppexpr(int* pun)
590 long n;
591 int opeektoken;
592 long ppstate;
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)
604 error(2, "%s in expression", errmsg);
605 errmsg = 0;
606 n = 0;
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;
617 * return non-zero if option s is set
621 ppoption(char* s)
623 switch ((int)hashget(pp.strtab, s))
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;