xref: /illumos-gate/usr/src/contrib/ast/src/lib/libpp/ppexpr.c (revision 3aa6c13072f3d4792a18693e916aed260a496c1f)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1986-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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.token, file, NiL, 0, pp.path, MAXTOKEN + 1))
76 				{
77 					pathcanon(pp.path, 0, 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, 4, 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