michael@0: /* michael@0: * $Xorg: ifparser.c,v 1.3 2000/08/17 19:41:50 cpqbld Exp $ michael@0: * michael@0: * Copyright 1992 Network Computing Devices, Inc. michael@0: * michael@0: * Permission to use, copy, modify, and distribute this software and its michael@0: * documentation for any purpose and without fee is hereby granted, provided michael@0: * that the above copyright notice appear in all copies and that both that michael@0: * copyright notice and this permission notice appear in supporting michael@0: * documentation, and that the name of Network Computing Devices may not be michael@0: * used in advertising or publicity pertaining to distribution of the software michael@0: * without specific, written prior permission. Network Computing Devices makes michael@0: * no representations about the suitability of this software for any purpose. michael@0: * It is provided ``as is'' without express or implied warranty. michael@0: * michael@0: * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS michael@0: * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, michael@0: * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, michael@0: * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM michael@0: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE michael@0: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR michael@0: * PERFORMANCE OF THIS SOFTWARE. michael@0: * michael@0: * Author: Jim Fulton michael@0: * Network Computing Devices, Inc. michael@0: * michael@0: * Simple if statement processor michael@0: * michael@0: * This module can be used to evaluate string representations of C language michael@0: * if constructs. It accepts the following grammar: michael@0: * michael@0: * EXPRESSION := VALUE michael@0: * | VALUE BINOP EXPRESSION michael@0: * | VALUE '?' EXPRESSION ':' EXPRESSION michael@0: * michael@0: * VALUE := '(' EXPRESSION ')' michael@0: * | '!' VALUE michael@0: * | '-' VALUE michael@0: * | '+' VALUE michael@0: * | '~' VALUE michael@0: * | 'defined' '(' variable ')' michael@0: * | 'defined' variable michael@0: * | # variable '(' variable-list ')' michael@0: * | variable michael@0: * | number michael@0: * michael@0: * BINOP := '*' | '/' | '%' michael@0: * | '+' | '-' michael@0: * | '<<' | '>>' michael@0: * | '<' | '>' | '<=' | '>=' michael@0: * | '==' | '!=' michael@0: * | '&' | '^' | '|' michael@0: * | '&&' | '||' michael@0: * michael@0: * The normal C order of precedence is supported. michael@0: * michael@0: * michael@0: * External Entry Points: michael@0: * michael@0: * ParseIfExpression parse a string for #if michael@0: */ michael@0: /* $XFree86: xc/config/makedepend/ifparser.c,v 3.11 2002/09/23 01:48:08 tsi Exp $ */ michael@0: michael@0: #include "ifparser.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: /**************************************************************************** michael@0: Internal Macros and Utilities for Parser michael@0: ****************************************************************************/ michael@0: michael@0: #define DO(val) if (!(val)) return NULL michael@0: #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff)) michael@0: #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++ michael@0: #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') michael@0: michael@0: michael@0: static const char * michael@0: parse_variable (IfParser *g, const char *cp, const char **varp) michael@0: { michael@0: SKIPSPACE (cp); michael@0: michael@0: if (!isvarfirstletter (*cp)) michael@0: return CALLFUNC(g, handle_error) (g, cp, "variable name"); michael@0: michael@0: *varp = cp; michael@0: /* EMPTY */ michael@0: for (cp++; isalnum(*cp) || *cp == '_'; cp++) ; michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_number (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long base = 10; michael@0: SKIPSPACE (cp); michael@0: michael@0: if (!isdigit(*cp)) michael@0: return CALLFUNC(g, handle_error) (g, cp, "number"); michael@0: michael@0: *valp = 0; michael@0: michael@0: if (*cp == '0') { michael@0: cp++; michael@0: if ((*cp == 'x') || (*cp == 'X')) { michael@0: base = 16; michael@0: cp++; michael@0: } else { michael@0: base = 8; michael@0: } michael@0: } michael@0: michael@0: /* Ignore overflows and assume ASCII, what source is usually written in */ michael@0: while (1) { michael@0: int increment = -1; michael@0: if (base == 8) { michael@0: if ((*cp >= '0') && (*cp <= '7')) michael@0: increment = *cp++ - '0'; michael@0: } else if (base == 16) { michael@0: if ((*cp >= '0') && (*cp <= '9')) michael@0: increment = *cp++ - '0'; michael@0: else if ((*cp >= 'A') && (*cp <= 'F')) michael@0: increment = *cp++ - ('A' - 10); michael@0: else if ((*cp >= 'a') && (*cp <= 'f')) michael@0: increment = *cp++ - ('a' - 10); michael@0: } else { /* Decimal */ michael@0: if ((*cp >= '0') && (*cp <= '9')) michael@0: increment = *cp++ - '0'; michael@0: } michael@0: if (increment < 0) michael@0: break; michael@0: *valp = (*valp * base) + increment; michael@0: } michael@0: michael@0: /* Skip trailing qualifiers */ michael@0: while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++; michael@0: return cp; michael@0: } michael@0: michael@0: static const char * michael@0: parse_character (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: char val; michael@0: michael@0: SKIPSPACE (cp); michael@0: if (*cp == '\\') michael@0: switch (cp[1]) { michael@0: case 'n': val = '\n'; break; michael@0: case 't': val = '\t'; break; michael@0: case 'v': val = '\v'; break; michael@0: case 'b': val = '\b'; break; michael@0: case 'r': val = '\r'; break; michael@0: case 'f': val = '\f'; break; michael@0: case 'a': val = '\a'; break; michael@0: case '\\': val = '\\'; break; michael@0: case '?': val = '\?'; break; michael@0: case '\'': val = '\''; break; michael@0: case '\"': val = '\"'; break; michael@0: case 'x': val = (char) strtol (cp + 2, NULL, 16); break; michael@0: default: val = (char) strtol (cp + 1, NULL, 8); break; michael@0: } michael@0: else michael@0: val = *cp; michael@0: while (*cp != '\'') cp++; michael@0: *valp = (long) val; michael@0: return cp; michael@0: } michael@0: michael@0: static const char * michael@0: parse_value (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: const char *var, *varend; michael@0: michael@0: *valp = 0; michael@0: michael@0: SKIPSPACE (cp); michael@0: if (!*cp) michael@0: return cp; michael@0: michael@0: switch (*cp) { michael@0: case '(': michael@0: DO (cp = ParseIfExpression (g, cp + 1, valp)); michael@0: SKIPSPACE (cp); michael@0: if (*cp != ')') michael@0: return CALLFUNC(g, handle_error) (g, cp, ")"); michael@0: michael@0: return cp + 1; /* skip the right paren */ michael@0: michael@0: case '!': michael@0: DO (cp = parse_value (g, cp + 1, valp)); michael@0: *valp = !(*valp); michael@0: return cp; michael@0: michael@0: case '-': michael@0: DO (cp = parse_value (g, cp + 1, valp)); michael@0: *valp = -(*valp); michael@0: return cp; michael@0: michael@0: case '+': michael@0: DO (cp = parse_value (g, cp + 1, valp)); michael@0: return cp; michael@0: michael@0: case '~': michael@0: DO (cp = parse_value (g, cp + 1, valp)); michael@0: *valp = ~(*valp); michael@0: return cp; michael@0: michael@0: case '#': michael@0: DO (cp = parse_variable (g, cp + 1, &var)); michael@0: SKIPSPACE (cp); michael@0: if (*cp != '(') michael@0: return CALLFUNC(g, handle_error) (g, cp, "("); michael@0: do { michael@0: DO (cp = parse_variable (g, cp + 1, &var)); michael@0: SKIPSPACE (cp); michael@0: } while (*cp && *cp != ')'); michael@0: if (*cp != ')') michael@0: return CALLFUNC(g, handle_error) (g, cp, ")"); michael@0: *valp = 1; /* XXX */ michael@0: return cp + 1; michael@0: michael@0: case '\'': michael@0: DO (cp = parse_character (g, cp + 1, valp)); michael@0: if (*cp != '\'') michael@0: return CALLFUNC(g, handle_error) (g, cp, "'"); michael@0: return cp + 1; michael@0: michael@0: case 'd': michael@0: if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) { michael@0: int paren = 0; michael@0: int len; michael@0: michael@0: cp += 7; michael@0: SKIPSPACE (cp); michael@0: if (*cp == '(') { michael@0: paren = 1; michael@0: cp++; michael@0: } michael@0: DO (cp = parse_variable (g, cp, &var)); michael@0: len = cp - var; michael@0: SKIPSPACE (cp); michael@0: if (paren && *cp != ')') michael@0: return CALLFUNC(g, handle_error) (g, cp, ")"); michael@0: *valp = (*(g->funcs.eval_defined)) (g, var, len); michael@0: return cp + paren; /* skip the right paren */ michael@0: } michael@0: /* fall out */ michael@0: } michael@0: michael@0: if (isdigit(*cp)) { michael@0: DO (cp = parse_number (g, cp, valp)); michael@0: } else if (!isvarfirstletter(*cp)) michael@0: return CALLFUNC(g, handle_error) (g, cp, "variable or number"); michael@0: else { michael@0: DO (cp = parse_variable (g, cp, &var)); michael@0: varend = cp; michael@0: SKIPSPACE(cp); michael@0: if (*cp != '(') { michael@0: *valp = (*(g->funcs.eval_variable)) (g, var, varend - var); michael@0: } else { michael@0: do { michael@0: long dummy; michael@0: DO (cp = ParseIfExpression (g, cp + 1, &dummy)); michael@0: SKIPSPACE(cp); michael@0: if (*cp == ')') michael@0: break; michael@0: if (*cp != ',') michael@0: return CALLFUNC(g, handle_error) (g, cp, ","); michael@0: } while (1); michael@0: michael@0: *valp = 1; /* XXX */ michael@0: cp++; michael@0: } michael@0: } michael@0: michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: michael@0: static const char * michael@0: parse_product (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_value (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '*': michael@0: DO (cp = parse_product (g, cp + 1, &rightval)); michael@0: *valp = (*valp * rightval); michael@0: break; michael@0: michael@0: case '/': michael@0: DO (cp = parse_product (g, cp + 1, &rightval)); michael@0: if (rightval == 0) michael@0: return CALLFUNC(g, handle_error) (g, cp, "0"); michael@0: *valp = (*valp / rightval); michael@0: break; michael@0: michael@0: case '%': michael@0: DO (cp = parse_product (g, cp + 1, &rightval)); michael@0: *valp = (*valp % rightval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_sum (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_product (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '+': michael@0: DO (cp = parse_sum (g, cp + 1, &rightval)); michael@0: *valp = (*valp + rightval); michael@0: break; michael@0: michael@0: case '-': michael@0: DO (cp = parse_sum (g, cp + 1, &rightval)); michael@0: *valp = (*valp - rightval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_shift (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_sum (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '<': michael@0: if (cp[1] == '<') { michael@0: DO (cp = parse_shift (g, cp + 2, &rightval)); michael@0: *valp = (*valp << rightval); michael@0: } michael@0: break; michael@0: michael@0: case '>': michael@0: if (cp[1] == '>') { michael@0: DO (cp = parse_shift (g, cp + 2, &rightval)); michael@0: *valp = (*valp >> rightval); michael@0: } michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_inequality (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_shift (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '<': michael@0: if (cp[1] == '=') { michael@0: DO (cp = parse_inequality (g, cp + 2, &rightval)); michael@0: *valp = (*valp <= rightval); michael@0: } else { michael@0: DO (cp = parse_inequality (g, cp + 1, &rightval)); michael@0: *valp = (*valp < rightval); michael@0: } michael@0: break; michael@0: michael@0: case '>': michael@0: if (cp[1] == '=') { michael@0: DO (cp = parse_inequality (g, cp + 2, &rightval)); michael@0: *valp = (*valp >= rightval); michael@0: } else { michael@0: DO (cp = parse_inequality (g, cp + 1, &rightval)); michael@0: *valp = (*valp > rightval); michael@0: } michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_equality (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_inequality (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '=': michael@0: if (cp[1] == '=') michael@0: cp++; michael@0: DO (cp = parse_equality (g, cp + 1, &rightval)); michael@0: *valp = (*valp == rightval); michael@0: break; michael@0: michael@0: case '!': michael@0: if (cp[1] != '=') michael@0: break; michael@0: DO (cp = parse_equality (g, cp + 2, &rightval)); michael@0: *valp = (*valp != rightval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_band (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_equality (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '&': michael@0: if (cp[1] != '&') { michael@0: DO (cp = parse_band (g, cp + 1, &rightval)); michael@0: *valp = (*valp & rightval); michael@0: } michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_bxor (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_band (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '^': michael@0: DO (cp = parse_bxor (g, cp + 1, &rightval)); michael@0: *valp = (*valp ^ rightval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_bor (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_bxor (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '|': michael@0: if (cp[1] != '|') { michael@0: DO (cp = parse_bor (g, cp + 1, &rightval)); michael@0: *valp = (*valp | rightval); michael@0: } michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_land (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_bor (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '&': michael@0: if (cp[1] != '&') michael@0: return CALLFUNC(g, handle_error) (g, cp, "&&"); michael@0: DO (cp = parse_land (g, cp + 2, &rightval)); michael@0: *valp = (*valp && rightval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_lor (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long rightval; michael@0: michael@0: DO (cp = parse_land (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '|': michael@0: if (cp[1] != '|') michael@0: return CALLFUNC(g, handle_error) (g, cp, "||"); michael@0: DO (cp = parse_lor (g, cp + 2, &rightval)); michael@0: *valp = (*valp || rightval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: static const char * michael@0: parse_cond(IfParser *g, const char *cp, long *valp) michael@0: { michael@0: long trueval, falseval; michael@0: michael@0: DO (cp = parse_lor (g, cp, valp)); michael@0: SKIPSPACE (cp); michael@0: michael@0: switch (*cp) { michael@0: case '?': michael@0: DO (cp = parse_cond (g, cp + 1, &trueval)); michael@0: SKIPSPACE (cp); michael@0: if (*cp != ':') michael@0: return CALLFUNC(g, handle_error) (g, cp, ":"); michael@0: DO (cp = parse_cond (g, cp + 1, &falseval)); michael@0: *valp = (*valp ? trueval : falseval); michael@0: break; michael@0: } michael@0: return cp; michael@0: } michael@0: michael@0: michael@0: /**************************************************************************** michael@0: External Entry Points michael@0: ****************************************************************************/ michael@0: michael@0: const char * michael@0: ParseIfExpression (IfParser *g, const char *cp, long *valp) michael@0: { michael@0: return parse_cond (g, cp, valp); michael@0: }