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
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
35static int		peektoken;	/* expression lookahead token	*/
36static char*		errmsg;		/* subexpr() error message	*/
37
38/*
39 * exists predicate evaluation
40 */
41
42static int
43exists(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
108static int
109compare(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
152static int
153predicate(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
273static long
274subexpr(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
587long
588ppexpr(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
620int
621ppoption(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