1 /*
2  * $Xorg: ifparser.c,v 1.3 2000/08/17 19:41:50 cpqbld Exp $
3  *
4  * Copyright 1992 Network Computing Devices, Inc.
5  *
6  * Permission to use, copy, modify, and distribute this software and its
7  * documentation for any purpose and without fee is hereby granted, provided
8  * that the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Network Computing Devices may not be
11  * used in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission.  Network Computing Devices makes
13  * no representations about the suitability of this software for any purpose.
14  * It is provided ``as is'' without express or implied warranty.
15  *
16  * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
18  * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
19  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
21  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author:  Jim Fulton
25  *          Network Computing Devices, Inc.
26  *
27  * Simple if statement processor
28  *
29  * This module can be used to evaluate string representations of C language
30  * if constructs.  It accepts the following grammar:
31  *
32  *     EXPRESSION	:=	VALUE
33  * 			 |	VALUE  BINOP	EXPRESSION
34  *			 |	VALUE	'?'	EXPRESSION ':'	EXPRESSION
35  *
36  *     VALUE		:=	'('  EXPRESSION  ')'
37  * 			 |	'!'  VALUE
38  * 			 |	'-'  VALUE
39  * 			 |	'+'  VALUE
40  *			 |	'~'  VALUE
41  * 			 |	'defined'  '('  variable  ')'
42  * 			 |	'defined'  variable
43  *			 |	# variable '(' variable-list ')'
44  * 			 |	variable
45  * 			 |	number
46  *
47  *     BINOP		:=	'*'	|  '/'	|  '%'
48  * 			 |	'+'	|  '-'
49  * 			 |	'<<'	|  '>>'
50  * 			 |	'<'	|  '>'	|  '<='  |  '>='
51  * 			 |	'=='	|  '!='
52  * 			 |	'&'	|  '^'  |  '|'
53  * 			 |	'&&'	|  '||'
54  *
55  * The normal C order of precedence is supported.
56  *
57  *
58  * External Entry Points:
59  *
60  *     ParseIfExpression		parse a string for #if
61  */
62 /* $XFree86: xc/config/makedepend/ifparser.c,v 3.11 2002/09/23 01:48:08 tsi Exp $ */
63 
64 #include "ifparser.h"
65 #include <ctype.h>
66 #include <stdlib.h>
67 #include <string.h>
68 
69 /****************************************************************************
70 		   Internal Macros and Utilities for Parser
71  ****************************************************************************/
72 
73 #define DO(val) if (!(val)) return NULL
74 #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
75 #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
76 #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
77 
78 
79 static const char *
parse_variable(IfParser * g,const char * cp,const char ** varp)80 parse_variable (IfParser *g, const char *cp, const char **varp)
81 {
82     SKIPSPACE (cp);
83 
84     if (!isvarfirstletter (*cp))
85 	return CALLFUNC(g, handle_error) (g, cp, "variable name");
86 
87     *varp = cp;
88     /* EMPTY */
89     for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
90     return cp;
91 }
92 
93 
94 static const char *
parse_number(IfParser * g,const char * cp,long * valp)95 parse_number (IfParser *g, const char *cp, long *valp)
96 {
97     long base = 10;
98     SKIPSPACE (cp);
99 
100     if (!isdigit(*cp))
101 	return CALLFUNC(g, handle_error) (g, cp, "number");
102 
103     *valp = 0;
104 
105     if (*cp == '0') {
106 	cp++;
107 	if ((*cp == 'x') || (*cp == 'X')) {
108 	    base = 16;
109 	    cp++;
110 	} else {
111 	    base = 8;
112 	}
113     }
114 
115     /* Ignore overflows and assume ASCII, what source is usually written in */
116     while (1) {
117 	int increment = -1;
118 	if (base == 8) {
119 	    if ((*cp >= '0') && (*cp <= '7'))
120 		increment = *cp++ - '0';
121 	} else if (base == 16) {
122 	    if ((*cp >= '0') && (*cp <= '9'))
123 		increment = *cp++ - '0';
124 	    else if ((*cp >= 'A') &&  (*cp <= 'F'))
125 		increment = *cp++ - ('A' - 10);
126 	    else if ((*cp >= 'a') && (*cp <= 'f'))
127 		increment = *cp++ - ('a' - 10);
128 	} else {	/* Decimal */
129 	    if ((*cp >= '0') && (*cp <= '9'))
130 		increment = *cp++ - '0';
131 	}
132 	if (increment < 0)
133 	    break;
134 	*valp = (*valp * base) + increment;
135     }
136 
137     /* Skip trailing qualifiers */
138     while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++;
139     return cp;
140 }
141 
142 static const char *
parse_character(IfParser * g,const char * cp,long * valp)143 parse_character (IfParser *g, const char *cp, long *valp)
144 {
145     char val;
146 
147     SKIPSPACE (cp);
148     if (*cp == '\\')
149 	switch (cp[1]) {
150 	case 'n': val = '\n'; break;
151 	case 't': val = '\t'; break;
152 	case 'v': val = '\v'; break;
153 	case 'b': val = '\b'; break;
154 	case 'r': val = '\r'; break;
155 	case 'f': val = '\f'; break;
156 	case 'a': val = '\a'; break;
157 	case '\\': val = '\\'; break;
158 	case '?': val = '\?'; break;
159 	case '\'': val = '\''; break;
160 	case '\"': val = '\"'; break;
161 	case 'x': val = (char) strtol (cp + 2, NULL, 16); break;
162 	default: val = (char) strtol (cp + 1, NULL, 8); break;
163 	}
164     else
165 	val = *cp;
166     while (*cp != '\'') cp++;
167     *valp = (long) val;
168     return cp;
169 }
170 
171 static const char *
parse_value(IfParser * g,const char * cp,long * valp)172 parse_value (IfParser *g, const char *cp, long *valp)
173 {
174     const char *var, *varend;
175 
176     *valp = 0;
177 
178     SKIPSPACE (cp);
179     if (!*cp)
180 	return cp;
181 
182     switch (*cp) {
183       case '(':
184 	DO (cp = ParseIfExpression (g, cp + 1, valp));
185 	SKIPSPACE (cp);
186 	if (*cp != ')')
187 	    return CALLFUNC(g, handle_error) (g, cp, ")");
188 
189 	return cp + 1;			/* skip the right paren */
190 
191       case '!':
192 	DO (cp = parse_value (g, cp + 1, valp));
193 	*valp = !(*valp);
194 	return cp;
195 
196       case '-':
197 	DO (cp = parse_value (g, cp + 1, valp));
198 	*valp = -(*valp);
199 	return cp;
200 
201       case '+':
202 	DO (cp = parse_value (g, cp + 1, valp));
203 	return cp;
204 
205       case '~':
206 	DO (cp = parse_value (g, cp + 1, valp));
207 	*valp = ~(*valp);
208 	return cp;
209 
210       case '#':
211 	DO (cp = parse_variable (g, cp + 1, &var));
212 	SKIPSPACE (cp);
213 	if (*cp != '(')
214 	    return CALLFUNC(g, handle_error) (g, cp, "(");
215 	do {
216 	    DO (cp = parse_variable (g, cp + 1, &var));
217 	    SKIPSPACE (cp);
218 	} while (*cp && *cp != ')');
219 	if (*cp != ')')
220 	    return CALLFUNC(g, handle_error) (g, cp, ")");
221 	*valp = 1; /* XXX */
222 	return cp + 1;
223 
224       case '\'':
225 	DO (cp = parse_character (g, cp + 1, valp));
226 	if (*cp != '\'')
227 	    return CALLFUNC(g, handle_error) (g, cp, "'");
228 	return cp + 1;
229 
230       case 'd':
231 	if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
232 	    int paren = 0;
233 	    int len;
234 
235 	    cp += 7;
236 	    SKIPSPACE (cp);
237 	    if (*cp == '(') {
238 		paren = 1;
239 		cp++;
240 	    }
241 	    DO (cp = parse_variable (g, cp, &var));
242 	    len = cp - var;
243 	    SKIPSPACE (cp);
244 	    if (paren && *cp != ')')
245 		return CALLFUNC(g, handle_error) (g, cp, ")");
246 	    *valp = (*(g->funcs.eval_defined)) (g, var, len);
247 	    return cp + paren;		/* skip the right paren */
248 	}
249 	/* fall out */
250     }
251 
252     if (isdigit(*cp)) {
253 	DO (cp = parse_number (g, cp, valp));
254     } else if (!isvarfirstletter(*cp))
255 	return CALLFUNC(g, handle_error) (g, cp, "variable or number");
256     else {
257 	DO (cp = parse_variable (g, cp, &var));
258 	varend = cp;
259 	SKIPSPACE(cp);
260 	if (*cp != '(') {
261 	    *valp = (*(g->funcs.eval_variable)) (g, var, varend - var);
262 	} else {
263 	    do {
264 		long dummy;
265 		DO (cp = ParseIfExpression (g, cp + 1, &dummy));
266 		SKIPSPACE(cp);
267 		if (*cp == ')')
268 		    break;
269 		if (*cp != ',')
270 		    return CALLFUNC(g, handle_error) (g, cp, ",");
271 	    } while (1);
272 
273 	    *valp = 1;	/* XXX */
274 	    cp++;
275 	}
276     }
277 
278     return cp;
279 }
280 
281 
282 
283 static const char *
parse_product(IfParser * g,const char * cp,long * valp)284 parse_product (IfParser *g, const char *cp, long *valp)
285 {
286     long rightval;
287 
288     DO (cp = parse_value (g, cp, valp));
289     SKIPSPACE (cp);
290 
291     switch (*cp) {
292       case '*':
293 	DO (cp = parse_product (g, cp + 1, &rightval));
294 	*valp = (*valp * rightval);
295 	break;
296 
297       case '/':
298 	DO (cp = parse_product (g, cp + 1, &rightval));
299 	if (rightval == 0)
300 	  return CALLFUNC(g, handle_error) (g, cp, "0");
301 	*valp = (*valp / rightval);
302 	break;
303 
304       case '%':
305 	DO (cp = parse_product (g, cp + 1, &rightval));
306 	*valp = (*valp % rightval);
307 	break;
308     }
309     return cp;
310 }
311 
312 
313 static const char *
parse_sum(IfParser * g,const char * cp,long * valp)314 parse_sum (IfParser *g, const char *cp, long *valp)
315 {
316     long rightval;
317 
318     DO (cp = parse_product (g, cp, valp));
319     SKIPSPACE (cp);
320 
321     switch (*cp) {
322       case '+':
323 	DO (cp = parse_sum (g, cp + 1, &rightval));
324 	*valp = (*valp + rightval);
325 	break;
326 
327       case '-':
328 	DO (cp = parse_sum (g, cp + 1, &rightval));
329 	*valp = (*valp - rightval);
330 	break;
331     }
332     return cp;
333 }
334 
335 
336 static const char *
parse_shift(IfParser * g,const char * cp,long * valp)337 parse_shift (IfParser *g, const char *cp, long *valp)
338 {
339     long rightval;
340 
341     DO (cp = parse_sum (g, cp, valp));
342     SKIPSPACE (cp);
343 
344     switch (*cp) {
345       case '<':
346 	if (cp[1] == '<') {
347 	    DO (cp = parse_shift (g, cp + 2, &rightval));
348 	    *valp = (*valp << rightval);
349 	}
350 	break;
351 
352       case '>':
353 	if (cp[1] == '>') {
354 	    DO (cp = parse_shift (g, cp + 2, &rightval));
355 	    *valp = (*valp >> rightval);
356 	}
357 	break;
358     }
359     return cp;
360 }
361 
362 
363 static const char *
parse_inequality(IfParser * g,const char * cp,long * valp)364 parse_inequality (IfParser *g, const char *cp, long *valp)
365 {
366     long rightval;
367 
368     DO (cp = parse_shift (g, cp, valp));
369     SKIPSPACE (cp);
370 
371     switch (*cp) {
372       case '<':
373 	if (cp[1] == '=') {
374 	    DO (cp = parse_inequality (g, cp + 2, &rightval));
375 	    *valp = (*valp <= rightval);
376 	} else {
377 	    DO (cp = parse_inequality (g, cp + 1, &rightval));
378 	    *valp = (*valp < rightval);
379 	}
380 	break;
381 
382       case '>':
383 	if (cp[1] == '=') {
384 	    DO (cp = parse_inequality (g, cp + 2, &rightval));
385 	    *valp = (*valp >= rightval);
386 	} else {
387 	    DO (cp = parse_inequality (g, cp + 1, &rightval));
388 	    *valp = (*valp > rightval);
389 	}
390 	break;
391     }
392     return cp;
393 }
394 
395 
396 static const char *
parse_equality(IfParser * g,const char * cp,long * valp)397 parse_equality (IfParser *g, const char *cp, long *valp)
398 {
399     long rightval;
400 
401     DO (cp = parse_inequality (g, cp, valp));
402     SKIPSPACE (cp);
403 
404     switch (*cp) {
405       case '=':
406 	if (cp[1] == '=')
407 	    cp++;
408 	DO (cp = parse_equality (g, cp + 1, &rightval));
409 	*valp = (*valp == rightval);
410 	break;
411 
412       case '!':
413 	if (cp[1] != '=')
414 	    break;
415 	DO (cp = parse_equality (g, cp + 2, &rightval));
416 	*valp = (*valp != rightval);
417 	break;
418     }
419     return cp;
420 }
421 
422 
423 static const char *
parse_band(IfParser * g,const char * cp,long * valp)424 parse_band (IfParser *g, const char *cp, long *valp)
425 {
426     long rightval;
427 
428     DO (cp = parse_equality (g, cp, valp));
429     SKIPSPACE (cp);
430 
431     switch (*cp) {
432       case '&':
433 	if (cp[1] != '&') {
434 	    DO (cp = parse_band (g, cp + 1, &rightval));
435 	    *valp = (*valp & rightval);
436 	}
437 	break;
438     }
439     return cp;
440 }
441 
442 
443 static const char *
parse_bxor(IfParser * g,const char * cp,long * valp)444 parse_bxor (IfParser *g, const char *cp, long *valp)
445 {
446     long rightval;
447 
448     DO (cp = parse_band (g, cp, valp));
449     SKIPSPACE (cp);
450 
451     switch (*cp) {
452       case '^':
453 	DO (cp = parse_bxor (g, cp + 1, &rightval));
454 	*valp = (*valp ^ rightval);
455 	break;
456     }
457     return cp;
458 }
459 
460 
461 static const char *
parse_bor(IfParser * g,const char * cp,long * valp)462 parse_bor (IfParser *g, const char *cp, long *valp)
463 {
464     long rightval;
465 
466     DO (cp = parse_bxor (g, cp, valp));
467     SKIPSPACE (cp);
468 
469     switch (*cp) {
470       case '|':
471 	if (cp[1] != '|') {
472 	    DO (cp = parse_bor (g, cp + 1, &rightval));
473 	    *valp = (*valp | rightval);
474 	}
475 	break;
476     }
477     return cp;
478 }
479 
480 
481 static const char *
parse_land(IfParser * g,const char * cp,long * valp)482 parse_land (IfParser *g, const char *cp, long *valp)
483 {
484     long rightval;
485 
486     DO (cp = parse_bor (g, cp, valp));
487     SKIPSPACE (cp);
488 
489     switch (*cp) {
490       case '&':
491 	if (cp[1] != '&')
492 	    return CALLFUNC(g, handle_error) (g, cp, "&&");
493 	DO (cp = parse_land (g, cp + 2, &rightval));
494 	*valp = (*valp && rightval);
495 	break;
496     }
497     return cp;
498 }
499 
500 
501 static const char *
parse_lor(IfParser * g,const char * cp,long * valp)502 parse_lor (IfParser *g, const char *cp, long *valp)
503 {
504     long rightval;
505 
506     DO (cp = parse_land (g, cp, valp));
507     SKIPSPACE (cp);
508 
509     switch (*cp) {
510       case '|':
511 	if (cp[1] != '|')
512 	    return CALLFUNC(g, handle_error) (g, cp, "||");
513 	DO (cp = parse_lor (g, cp + 2, &rightval));
514 	*valp = (*valp || rightval);
515 	break;
516     }
517     return cp;
518 }
519 
520 
521 static const char *
parse_cond(IfParser * g,const char * cp,long * valp)522 parse_cond(IfParser *g, const char *cp, long *valp)
523 {
524     long trueval, falseval;
525 
526     DO (cp = parse_lor (g, cp, valp));
527     SKIPSPACE (cp);
528 
529     switch (*cp) {
530       case '?':
531 	DO (cp = parse_cond (g, cp + 1, &trueval));
532 	SKIPSPACE (cp);
533 	if (*cp != ':')
534 	    return CALLFUNC(g, handle_error) (g, cp, ":");
535 	DO (cp = parse_cond (g, cp + 1, &falseval));
536 	*valp = (*valp ? trueval : falseval);
537 	break;
538     }
539     return cp;
540 }
541 
542 
543 /****************************************************************************
544 			     External Entry Points
545  ****************************************************************************/
546 
547 const char *
ParseIfExpression(IfParser * g,const char * cp,long * valp)548 ParseIfExpression (IfParser *g, const char *cp, long *valp)
549 {
550     return parse_cond (g, cp, valp);
551 }
552