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
92static const char *
93parse_number(IfParser *g, const char *cp, long *valp)
94{
95    long base = 10;
96
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        }
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
119        if (base == 8) {
120            if ((*cp >= '0') && (*cp <= '7'))
121                increment = *cp++ - '0';
122        }
123        else if (base == 16) {
124            if ((*cp >= '0') && (*cp <= '9'))
125                increment = *cp++ - '0';
126            else if ((*cp >= 'A') && (*cp <= 'F'))
127                increment = *cp++ - ('A' - 10);
128            else if ((*cp >= 'a') && (*cp <= 'f'))
129                increment = *cp++ - ('a' - 10);
130        }
131        else {                  /* Decimal */
132            if ((*cp >= '0') && (*cp <= '9'))
133                increment = *cp++ - '0';
134        }
135        if (increment < 0)
136            break;
137        *valp = (*valp * base) + increment;
138    }
139
140    /* Skip trailing qualifiers */
141    while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l')
142        cp++;
143    return cp;
144}
145
146static const char *
147parse_character(IfParser *g, const char *cp, long *valp)
148{
149    char val;
150
151    SKIPSPACE(cp);
152    if (*cp == '\\')
153        switch (cp[1]) {
154        case 'n':
155            val = '\n';
156            break;
157        case 't':
158            val = '\t';
159            break;
160        case 'v':
161            val = '\v';
162            break;
163        case 'b':
164            val = '\b';
165            break;
166        case 'r':
167            val = '\r';
168            break;
169        case 'f':
170            val = '\f';
171            break;
172        case 'a':
173            val = '\a';
174            break;
175        case '\\':
176            val = '\\';
177            break;
178        case '?':
179            val = '\?';
180            break;
181        case '\'':
182            val = '\'';
183            break;
184        case '\"':
185            val = '\"';
186            break;
187        case 'x':
188            val = (char) strtol(cp + 2, NULL, 16);
189            break;
190        default:
191            val = (char) strtol(cp + 1, NULL, 8);
192            break;
193        }
194    else
195        val = *cp;
196    while (*cp != '\'')
197        cp++;
198    *valp = (long) val;
199    return cp;
200}
201
202static const char *
203parse_value(IfParser *g, const char *cp, long *valp)
204{
205    const char *var;
206
207    *valp = 0;
208
209    SKIPSPACE(cp);
210    if (!*cp)
211        return cp;
212
213    switch (*cp) {
214    case '(':
215        DO(cp = ParseIfExpression(g, cp + 1, valp));
216        SKIPSPACE(cp);
217        if (*cp != ')')
218            return CALLFUNC(g, handle_error) (g, cp, ")");
219
220        return cp + 1;          /* skip the right paren */
221
222    case '!':
223        DO(cp = parse_value(g, cp + 1, valp));
224        *valp = !(*valp);
225        return cp;
226
227    case '-':
228        DO(cp = parse_value(g, cp + 1, valp));
229        *valp = -(*valp);
230        return cp;
231
232    case '+':
233        DO(cp = parse_value(g, cp + 1, valp));
234        return cp;
235
236    case '~':
237        DO(cp = parse_value(g, cp + 1, valp));
238        *valp = ~(*valp);
239        return cp;
240
241    case '#':
242        DO(cp = parse_variable(g, cp + 1, &var));
243        SKIPSPACE(cp);
244        if (*cp != '(')
245            return CALLFUNC(g, handle_error) (g, cp, "(");
246        do {
247            DO(cp = parse_variable(g, cp + 1, &var));
248            SKIPSPACE(cp);
249        } while (*cp && *cp != ')');
250        if (*cp != ')')
251            return CALLFUNC(g, handle_error) (g, cp, ")");
252        *valp = 1;              /* XXX */
253        return cp + 1;
254
255    case '\'':
256        DO(cp = parse_character(g, cp + 1, valp));
257        if (*cp != '\'')
258            return CALLFUNC(g, handle_error) (g, cp, "'");
259        return cp + 1;
260
261    case 'd':
262        if (strncmp(cp, "defined", 7) == 0 && !isalnum(cp[7])) {
263            int paren = 0;
264            int len;
265
266            cp += 7;
267            SKIPSPACE(cp);
268            if (*cp == '(') {
269                paren = 1;
270                cp++;
271            }
272            DO(cp = parse_variable(g, cp, &var));
273            len = cp - var;
274            SKIPSPACE(cp);
275            if (paren && *cp != ')')
276                return CALLFUNC(g, handle_error) (g, cp, ")");
277            *valp = (*(g->funcs.eval_defined)) (g, var, len);
278            return cp + paren;  /* skip the right paren */
279        }
280        /* fall out */
281    }
282
283    if (isdigit(*cp)) {
284        DO(cp = parse_number(g, cp, valp));
285    }
286    else if (!isvarfirstletter(*cp))
287        return CALLFUNC(g, handle_error) (g, cp, "variable or number");
288    else {
289        const char *varend;
290
291        DO(cp = parse_variable(g, cp, &var));
292        varend = cp;
293        SKIPSPACE(cp);
294        if (*cp != '(') {
295            *valp = (*(g->funcs.eval_variable)) (g, var, varend - var);
296        }
297        else {
298            do {
299                long dummy;
300
301                DO(cp = ParseIfExpression(g, cp + 1, &dummy));
302                SKIPSPACE(cp);
303                if (*cp == ')')
304                    break;
305                if (*cp != ',')
306                    return CALLFUNC(g, handle_error) (g, cp, ",");
307            } while (1);
308
309            *valp = 1;          /* XXX */
310            cp++;
311        }
312    }
313
314    return cp;
315}
316
317static const char *
318parse_product(IfParser *g, const char *cp, long *valp)
319{
320    long rightval;
321
322    DO(cp = parse_value(g, cp, valp));
323    SKIPSPACE(cp);
324
325    switch (*cp) {
326    case '*':
327        DO(cp = parse_product(g, cp + 1, &rightval));
328        *valp = (*valp * rightval);
329        break;
330
331    case '/':
332        DO(cp = parse_product(g, cp + 1, &rightval));
333        if (rightval)
334            *valp = (*valp / rightval);
335        else
336            *valp = LONG_MAX;
337        break;
338
339    case '%':
340        DO(cp = parse_product(g, cp + 1, &rightval));
341        if (rightval)
342            *valp = (*valp % rightval);
343        else
344            *valp = LONG_MAX;
345        break;
346    }
347    return cp;
348}
349
350static const char *
351parse_sum(IfParser *g, const char *cp, long *valp)
352{
353    long rightval;
354
355    DO(cp = parse_product(g, cp, valp));
356    SKIPSPACE(cp);
357
358    switch (*cp) {
359    case '+':
360        DO(cp = parse_sum(g, cp + 1, &rightval));
361        *valp = (*valp + rightval);
362        break;
363
364    case '-':
365        DO(cp = parse_sum(g, cp + 1, &rightval));
366        *valp = (*valp - rightval);
367        break;
368    }
369    return cp;
370}
371
372static const char *
373parse_shift(IfParser *g, const char *cp, long *valp)
374{
375    long rightval;
376
377    DO(cp = parse_sum(g, cp, valp));
378    SKIPSPACE(cp);
379
380    switch (*cp) {
381    case '<':
382        if (cp[1] == '<') {
383            DO(cp = parse_shift(g, cp + 2, &rightval));
384            *valp = (*valp << rightval);
385        }
386        break;
387
388    case '>':
389        if (cp[1] == '>') {
390            DO(cp = parse_shift(g, cp + 2, &rightval));
391            *valp = (*valp >> rightval);
392        }
393        break;
394    }
395    return cp;
396}
397
398static const char *
399parse_inequality(IfParser *g, const char *cp, long *valp)
400{
401    long rightval;
402
403    DO(cp = parse_shift(g, cp, valp));
404    SKIPSPACE(cp);
405
406    switch (*cp) {
407    case '<':
408        if (cp[1] == '=') {
409            DO(cp = parse_inequality(g, cp + 2, &rightval));
410            *valp = (*valp <= rightval);
411        }
412        else {
413            DO(cp = parse_inequality(g, cp + 1, &rightval));
414            *valp = (*valp < rightval);
415        }
416        break;
417
418    case '>':
419        if (cp[1] == '=') {
420            DO(cp = parse_inequality(g, cp + 2, &rightval));
421            *valp = (*valp >= rightval);
422        }
423        else {
424            DO(cp = parse_inequality(g, cp + 1, &rightval));
425            *valp = (*valp > rightval);
426        }
427        break;
428    }
429    return cp;
430}
431
432static const char *
433parse_equality(IfParser *g, const char *cp, long *valp)
434{
435    long rightval;
436
437    DO(cp = parse_inequality(g, cp, valp));
438    SKIPSPACE(cp);
439
440    switch (*cp) {
441    case '=':
442        if (cp[1] == '=')
443            cp++;
444        DO(cp = parse_equality(g, cp + 1, &rightval));
445        *valp = (*valp == rightval);
446        break;
447
448    case '!':
449        if (cp[1] != '=')
450            break;
451        DO(cp = parse_equality(g, cp + 2, &rightval));
452        *valp = (*valp != rightval);
453        break;
454    }
455    return cp;
456}
457
458static const char *
459parse_band(IfParser *g, const char *cp, long *valp)
460{
461    long rightval;
462
463    DO(cp = parse_equality(g, cp, valp));
464    SKIPSPACE(cp);
465
466    switch (*cp) {
467    case '&':
468        if (cp[1] != '&') {
469            DO(cp = parse_band(g, cp + 1, &rightval));
470            *valp = (*valp & rightval);
471        }
472        break;
473    }
474    return cp;
475}
476
477static const char *
478parse_bxor(IfParser *g, const char *cp, long *valp)
479{
480    long rightval;
481
482    DO(cp = parse_band(g, cp, valp));
483    SKIPSPACE(cp);
484
485    switch (*cp) {
486    case '^':
487        DO(cp = parse_bxor(g, cp + 1, &rightval));
488        *valp = (*valp ^ rightval);
489        break;
490    }
491    return cp;
492}
493
494static const char *
495parse_bor(IfParser *g, const char *cp, long *valp)
496{
497    long rightval;
498
499    DO(cp = parse_bxor(g, cp, valp));
500    SKIPSPACE(cp);
501
502    switch (*cp) {
503    case '|':
504        if (cp[1] != '|') {
505            DO(cp = parse_bor(g, cp + 1, &rightval));
506            *valp = (*valp | rightval);
507        }
508        break;
509    }
510    return cp;
511}
512
513static const char *
514parse_land(IfParser *g, const char *cp, long *valp)
515{
516    long rightval;
517
518    DO(cp = parse_bor(g, cp, valp));
519    SKIPSPACE(cp);
520
521    switch (*cp) {
522    case '&':
523        if (cp[1] != '&')
524            return CALLFUNC(g, handle_error) (g, cp, "&&");
525        DO(cp = parse_land(g, cp + 2, &rightval));
526        *valp = (*valp && rightval);
527        break;
528    }
529    return cp;
530}
531
532static const char *
533parse_lor(IfParser *g, const char *cp, long *valp)
534{
535    long rightval;
536
537    DO(cp = parse_land(g, cp, valp));
538    SKIPSPACE(cp);
539
540    switch (*cp) {
541    case '|':
542        if (cp[1] != '|')
543            return CALLFUNC(g, handle_error) (g, cp, "||");
544        DO(cp = parse_lor(g, cp + 2, &rightval));
545        *valp = (*valp || rightval);
546        break;
547    }
548    return cp;
549}
550
551static const char *
552parse_cond(IfParser *g, const char *cp, long *valp)
553{
554    long trueval, falseval;
555
556    DO(cp = parse_lor(g, cp, valp));
557    SKIPSPACE(cp);
558
559    switch (*cp) {
560    case '?':
561        DO(cp = parse_cond(g, cp + 1, &trueval));
562        SKIPSPACE(cp);
563        if (*cp != ':')
564            return CALLFUNC(g, handle_error) (g, cp, ":");
565        DO(cp = parse_cond(g, cp + 1, &falseval));
566        *valp = (*valp ? trueval : falseval);
567        break;
568    }
569    return cp;
570}
571
572
573/****************************************************************************
574			     External Entry Points
575 ****************************************************************************/
576
577const char *
578ParseIfExpression(IfParser *g, const char *cp, long *valp)
579{
580    return parse_cond(g, cp, valp);
581}
582