ifparser.c revision 63165362
1/*
2 *
3 * Copyright 1992 Network Computing Devices, Inc.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation for any purpose and without fee is hereby granted, provided
7 * that the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Network Computing Devices may not be
10 * used in advertising or publicity pertaining to distribution of the software
11 * without specific, written prior permission.  Network Computing Devices makes
12 * no representations about the suitability of this software for any purpose.
13 * It is provided ``as is'' without express or implied warranty.
14 *
15 * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
17 * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
18 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
20 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21 * PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author:  Jim Fulton
24 *          Network Computing Devices, Inc.
25 *
26 * Simple if statement processor
27 *
28 * This module can be used to evaluate string representations of C language
29 * if constructs.  It accepts the following grammar:
30 *
31 *     EXPRESSION	:=	VALUE
32 * 			 |	VALUE  BINOP	EXPRESSION
33 *			 |	VALUE	'?'	EXPRESSION ':'	EXPRESSION
34 *
35 *     VALUE		:=	'('  EXPRESSION  ')'
36 * 			 |	'!'  VALUE
37 * 			 |	'-'  VALUE
38 * 			 |	'+'  VALUE
39 *			 |	'~'  VALUE
40 * 			 |	'defined'  '('  variable  ')'
41 * 			 |	'defined'  variable
42 *			 |	# variable '(' variable-list ')'
43 * 			 |	variable
44 * 			 |	number
45 *
46 *     BINOP		:=	'*'	|  '/'	|  '%'
47 * 			 |	'+'	|  '-'
48 * 			 |	'<<'	|  '>>'
49 * 			 |	'<'	|  '>'	|  '<='  |  '>='
50 * 			 |	'=='	|  '!='
51 * 			 |	'&'	|  '^'  |  '|'
52 * 			 |	'&&'	|  '||'
53 *
54 * The normal C order of precedence is supported.
55 *
56 *
57 * External Entry Points:
58 *
59 *     ParseIfExpression		parse a string for #if
60 */
61
62#include "ifparser.h"
63#include <ctype.h>
64#include <stdlib.h>
65#include <string.h>
66#include <limits.h>
67
68/****************************************************************************
69		   Internal Macros and Utilities for Parser
70 ****************************************************************************/
71
72#define DO(val) if (!(val)) return NULL
73#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
74#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
75#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
76
77
78static const char *
79parse_variable (IfParser *g, const char *cp, const char **varp)
80{
81    SKIPSPACE (cp);
82
83    if (!isvarfirstletter (*cp))
84	return CALLFUNC(g, handle_error) (g, cp, "variable name");
85
86    *varp = cp;
87    /* EMPTY */
88    for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
89    return cp;
90}
91
92
93static const char *
94parse_number (IfParser *g, const char *cp, long *valp)
95{
96    long base = 10;
97    SKIPSPACE (cp);
98
99    if (!isdigit(*cp))
100	return CALLFUNC(g, handle_error) (g, cp, "number");
101
102    *valp = 0;
103
104    if (*cp == '0') {
105	cp++;
106	if ((*cp == 'x') || (*cp == 'X')) {
107	    base = 16;
108	    cp++;
109	} else {
110	    base = 8;
111	}
112    }
113
114    /* Ignore overflows and assume ASCII, what source is usually written in */
115    while (1) {
116	int increment = -1;
117	if (base == 8) {
118	    if ((*cp >= '0') && (*cp <= '7'))
119		increment = *cp++ - '0';
120	} else if (base == 16) {
121	    if ((*cp >= '0') && (*cp <= '9'))
122		increment = *cp++ - '0';
123	    else if ((*cp >= 'A') &&  (*cp <= 'F'))
124		increment = *cp++ - ('A' - 10);
125	    else if ((*cp >= 'a') && (*cp <= 'f'))
126		increment = *cp++ - ('a' - 10);
127	} else {	/* Decimal */
128	    if ((*cp >= '0') && (*cp <= '9'))
129		increment = *cp++ - '0';
130	}
131	if (increment < 0)
132	    break;
133	*valp = (*valp * base) + increment;
134    }
135
136    /* Skip trailing qualifiers */
137    while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++;
138    return cp;
139}
140
141static const char *
142parse_character (IfParser *g, const char *cp, long *valp)
143{
144    char val;
145
146    SKIPSPACE (cp);
147    if (*cp == '\\')
148	switch (cp[1]) {
149	case 'n': val = '\n'; break;
150	case 't': val = '\t'; break;
151	case 'v': val = '\v'; break;
152	case 'b': val = '\b'; break;
153	case 'r': val = '\r'; break;
154	case 'f': val = '\f'; break;
155	case 'a': val = '\a'; break;
156	case '\\': val = '\\'; break;
157	case '?': val = '\?'; break;
158	case '\'': val = '\''; break;
159	case '\"': val = '\"'; break;
160	case 'x': val = (char) strtol (cp + 2, NULL, 16); break;
161	default: val = (char) strtol (cp + 1, NULL, 8); break;
162	}
163    else
164	val = *cp;
165    while (*cp != '\'') cp++;
166    *valp = (long) val;
167    return cp;
168}
169
170static const char *
171parse_value (IfParser *g, const char *cp, long *valp)
172{
173    const char *var, *varend;
174
175    *valp = 0;
176
177    SKIPSPACE (cp);
178    if (!*cp)
179	return cp;
180
181    switch (*cp) {
182      case '(':
183	DO (cp = ParseIfExpression (g, cp + 1, valp));
184	SKIPSPACE (cp);
185	if (*cp != ')')
186	    return CALLFUNC(g, handle_error) (g, cp, ")");
187
188	return cp + 1;			/* skip the right paren */
189
190      case '!':
191	DO (cp = parse_value (g, cp + 1, valp));
192	*valp = !(*valp);
193	return cp;
194
195      case '-':
196	DO (cp = parse_value (g, cp + 1, valp));
197	*valp = -(*valp);
198	return cp;
199
200      case '+':
201	DO (cp = parse_value (g, cp + 1, valp));
202	return cp;
203
204      case '~':
205	DO (cp = parse_value (g, cp + 1, valp));
206	*valp = ~(*valp);
207	return cp;
208
209      case '#':
210	DO (cp = parse_variable (g, cp + 1, &var));
211	SKIPSPACE (cp);
212	if (*cp != '(')
213	    return CALLFUNC(g, handle_error) (g, cp, "(");
214	do {
215	    DO (cp = parse_variable (g, cp + 1, &var));
216	    SKIPSPACE (cp);
217	} while (*cp && *cp != ')');
218	if (*cp != ')')
219	    return CALLFUNC(g, handle_error) (g, cp, ")");
220	*valp = 1; /* XXX */
221	return cp + 1;
222
223      case '\'':
224	DO (cp = parse_character (g, cp + 1, valp));
225	if (*cp != '\'')
226	    return CALLFUNC(g, handle_error) (g, cp, "'");
227	return cp + 1;
228
229      case 'd':
230	if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
231	    int paren = 0;
232	    int len;
233
234	    cp += 7;
235	    SKIPSPACE (cp);
236	    if (*cp == '(') {
237		paren = 1;
238		cp++;
239	    }
240	    DO (cp = parse_variable (g, cp, &var));
241	    len = cp - var;
242	    SKIPSPACE (cp);
243	    if (paren && *cp != ')')
244		return CALLFUNC(g, handle_error) (g, cp, ")");
245	    *valp = (*(g->funcs.eval_defined)) (g, var, len);
246	    return cp + paren;		/* skip the right paren */
247	}
248	/* fall out */
249    }
250
251    if (isdigit(*cp)) {
252	DO (cp = parse_number (g, cp, valp));
253    } else if (!isvarfirstletter(*cp))
254	return CALLFUNC(g, handle_error) (g, cp, "variable or number");
255    else {
256	DO (cp = parse_variable (g, cp, &var));
257	varend = cp;
258	SKIPSPACE(cp);
259	if (*cp != '(') {
260	    *valp = (*(g->funcs.eval_variable)) (g, var, varend - var);
261	} else {
262	    do {
263		long dummy;
264		DO (cp = ParseIfExpression (g, cp + 1, &dummy));
265		SKIPSPACE(cp);
266		if (*cp == ')')
267		    break;
268		if (*cp != ',')
269		    return CALLFUNC(g, handle_error) (g, cp, ",");
270	    } while (1);
271
272	    *valp = 1;	/* XXX */
273	    cp++;
274	}
275    }
276
277    return cp;
278}
279
280
281
282static const char *
283parse_product (IfParser *g, const char *cp, long *valp)
284{
285    long rightval;
286
287    DO (cp = parse_value (g, cp, valp));
288    SKIPSPACE (cp);
289
290    switch (*cp) {
291      case '*':
292	DO (cp = parse_product (g, cp + 1, &rightval));
293	*valp = (*valp * rightval);
294	break;
295
296      case '/':
297	DO (cp = parse_product (g, cp + 1, &rightval));
298    if (rightval)
299	    *valp = (*valp / rightval);
300    else
301        *valp = LONG_MAX;
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
313static const char *
314parse_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
336static const char *
337parse_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
363static const char *
364parse_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
396static const char *
397parse_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
423static const char *
424parse_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
443static const char *
444parse_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
461static const char *
462parse_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
481static const char *
482parse_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
501static const char *
502parse_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
521static const char *
522parse_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
547const char *
548ParseIfExpression (IfParser *g, const char *cp, long *valp)
549{
550    return parse_cond (g, cp, valp);
551}
552