Back to index

lightning-sunbird  0.9+nobinonly
ifparser.c
Go to the documentation of this file.
00001 /*
00002  * $Xorg: ifparser.c,v 1.3 2000/08/17 19:41:50 cpqbld Exp $
00003  *
00004  * Copyright 1992 Network Computing Devices, Inc.
00005  * 
00006  * Permission to use, copy, modify, and distribute this software and its
00007  * documentation for any purpose and without fee is hereby granted, provided
00008  * that the above copyright notice appear in all copies and that both that
00009  * copyright notice and this permission notice appear in supporting
00010  * documentation, and that the name of Network Computing Devices may not be
00011  * used in advertising or publicity pertaining to distribution of the software
00012  * without specific, written prior permission.  Network Computing Devices makes
00013  * no representations about the suitability of this software for any purpose.
00014  * It is provided ``as is'' without express or implied warranty.
00015  * 
00016  * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
00017  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
00018  * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
00019  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00020  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00021  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00022  * PERFORMANCE OF THIS SOFTWARE.
00023  * 
00024  * Author:  Jim Fulton
00025  *          Network Computing Devices, Inc.
00026  * 
00027  * Simple if statement processor
00028  *
00029  * This module can be used to evaluate string representations of C language
00030  * if constructs.  It accepts the following grammar:
00031  * 
00032  *     EXPRESSION    :=     VALUE
00033  *                    |     VALUE  BINOP  EXPRESSION
00034  *                    |     VALUE  '?'    EXPRESSION ':'       EXPRESSION
00035  * 
00036  *     VALUE         :=     '('  EXPRESSION  ')'
00037  *                    |     '!'  VALUE
00038  *                    |     '-'  VALUE
00039  *                    |     '+'  VALUE
00040  *                    |     '~'  VALUE
00041  *                    |     'defined'  '('  variable  ')'
00042  *                    |     'defined'  variable
00043  *                    |     # variable '(' variable-list ')'
00044  *                    |     variable
00045  *                    |     number
00046  * 
00047  *     BINOP         :=     '*'    |  '/' |  '%'
00048  *                    |     '+'    |  '-'
00049  *                    |     '<<'   |  '>>'
00050  *                    |     '<'    |  '>' |  '<='  |  '>='
00051  *                    |     '=='   |  '!='
00052  *                    |     '&'    |  '^'  |  '|'
00053  *                    |     '&&'   |  '||'
00054  * 
00055  * The normal C order of precedence is supported.
00056  * 
00057  * 
00058  * External Entry Points:
00059  * 
00060  *     ParseIfExpression           parse a string for #if
00061  */
00062 /* $XFree86: xc/config/makedepend/ifparser.c,v 3.11 2002/09/23 01:48:08 tsi Exp $ */
00063 
00064 #include "ifparser.h"
00065 #include <ctype.h>
00066 #include <stdlib.h>
00067 #include <string.h>
00068 
00069 /****************************************************************************
00070                  Internal Macros and Utilities for Parser
00071  ****************************************************************************/
00072 
00073 #define DO(val) if (!(val)) return NULL
00074 #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
00075 #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
00076 #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
00077 
00078 
00079 static const char *
00080 parse_variable (IfParser *g, const char *cp, const char **varp)
00081 {
00082     SKIPSPACE (cp);
00083 
00084     if (!isvarfirstletter (*cp))
00085        return CALLFUNC(g, handle_error) (g, cp, "variable name");
00086 
00087     *varp = cp;
00088     /* EMPTY */
00089     for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
00090     return cp;
00091 }
00092 
00093 
00094 static const char *
00095 parse_number (IfParser *g, const char *cp, long *valp)
00096 {
00097     long base = 10;
00098     SKIPSPACE (cp);
00099 
00100     if (!isdigit(*cp))
00101        return CALLFUNC(g, handle_error) (g, cp, "number");
00102 
00103     *valp = 0;
00104 
00105     if (*cp == '0') {
00106        cp++;
00107        if ((*cp == 'x') || (*cp == 'X')) {
00108            base = 16;
00109            cp++;
00110        } else {
00111            base = 8;
00112        }
00113     }
00114 
00115     /* Ignore overflows and assume ASCII, what source is usually written in */
00116     while (1) {
00117        int increment = -1;
00118        if (base == 8) {
00119            if ((*cp >= '0') && (*cp <= '7'))
00120               increment = *cp++ - '0';
00121        } else if (base == 16) {
00122            if ((*cp >= '0') && (*cp <= '9'))
00123               increment = *cp++ - '0';
00124            else if ((*cp >= 'A') &&  (*cp <= 'F'))
00125               increment = *cp++ - ('A' - 10);
00126            else if ((*cp >= 'a') && (*cp <= 'f'))
00127               increment = *cp++ - ('a' - 10);
00128        } else {      /* Decimal */
00129            if ((*cp >= '0') && (*cp <= '9'))
00130               increment = *cp++ - '0';
00131        }
00132        if (increment < 0)
00133            break;
00134        *valp = (*valp * base) + increment;
00135     }
00136 
00137     /* Skip trailing qualifiers */
00138     while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++;
00139     return cp;
00140 }
00141 
00142 static const char *
00143 parse_character (IfParser *g, const char *cp, long *valp)
00144 {
00145     char val;
00146 
00147     SKIPSPACE (cp);
00148     if (*cp == '\\')
00149        switch (cp[1]) {
00150        case 'n': val = '\n'; break;
00151        case 't': val = '\t'; break;
00152        case 'v': val = '\v'; break;
00153        case 'b': val = '\b'; break;
00154        case 'r': val = '\r'; break;
00155        case 'f': val = '\f'; break;
00156        case 'a': val = '\a'; break;
00157        case '\\': val = '\\'; break;
00158        case '?': val = '\?'; break;
00159        case '\'': val = '\''; break;
00160        case '\"': val = '\"'; break;
00161        case 'x': val = (char) strtol (cp + 2, NULL, 16); break;
00162        default: val = (char) strtol (cp + 1, NULL, 8); break;
00163        }
00164     else
00165        val = *cp;
00166     while (*cp != '\'') cp++;
00167     *valp = (long) val;
00168     return cp;
00169 }
00170 
00171 static const char *
00172 parse_value (IfParser *g, const char *cp, long *valp)
00173 {
00174     const char *var, *varend;
00175 
00176     *valp = 0;
00177 
00178     SKIPSPACE (cp);
00179     if (!*cp)
00180        return cp;
00181 
00182     switch (*cp) {
00183       case '(':
00184        DO (cp = ParseIfExpression (g, cp + 1, valp));
00185        SKIPSPACE (cp);
00186        if (*cp != ')') 
00187            return CALLFUNC(g, handle_error) (g, cp, ")");
00188 
00189        return cp + 1;                     /* skip the right paren */
00190 
00191       case '!':
00192        DO (cp = parse_value (g, cp + 1, valp));
00193        *valp = !(*valp);
00194        return cp;
00195 
00196       case '-':
00197        DO (cp = parse_value (g, cp + 1, valp));
00198        *valp = -(*valp);
00199        return cp;
00200 
00201       case '+':
00202        DO (cp = parse_value (g, cp + 1, valp));
00203        return cp;
00204 
00205       case '~':
00206        DO (cp = parse_value (g, cp + 1, valp));
00207        *valp = ~(*valp);
00208        return cp;
00209 
00210       case '#':
00211        DO (cp = parse_variable (g, cp + 1, &var));
00212        SKIPSPACE (cp);
00213        if (*cp != '(')
00214            return CALLFUNC(g, handle_error) (g, cp, "(");
00215        do {
00216            DO (cp = parse_variable (g, cp + 1, &var));
00217            SKIPSPACE (cp);
00218        } while (*cp && *cp != ')');
00219        if (*cp != ')')
00220            return CALLFUNC(g, handle_error) (g, cp, ")");
00221        *valp = 1; /* XXX */
00222        return cp + 1;
00223 
00224       case '\'':
00225        DO (cp = parse_character (g, cp + 1, valp));
00226        if (*cp != '\'')
00227            return CALLFUNC(g, handle_error) (g, cp, "'");
00228        return cp + 1;
00229 
00230       case 'd':
00231        if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
00232            int paren = 0;
00233            int len;
00234 
00235            cp += 7;
00236            SKIPSPACE (cp);
00237            if (*cp == '(') {
00238               paren = 1;
00239               cp++;
00240            }
00241            DO (cp = parse_variable (g, cp, &var));
00242            len = cp - var;
00243            SKIPSPACE (cp);
00244            if (paren && *cp != ')')
00245               return CALLFUNC(g, handle_error) (g, cp, ")");
00246            *valp = (*(g->funcs.eval_defined)) (g, var, len);
00247            return cp + paren;             /* skip the right paren */
00248        }
00249        /* fall out */
00250     }
00251 
00252     if (isdigit(*cp)) {
00253        DO (cp = parse_number (g, cp, valp));
00254     } else if (!isvarfirstletter(*cp))
00255        return CALLFUNC(g, handle_error) (g, cp, "variable or number");
00256     else {
00257        DO (cp = parse_variable (g, cp, &var));
00258        varend = cp;
00259        SKIPSPACE(cp);
00260        if (*cp != '(') {
00261            *valp = (*(g->funcs.eval_variable)) (g, var, varend - var);
00262        } else {
00263            do {
00264               long dummy;
00265               DO (cp = ParseIfExpression (g, cp + 1, &dummy));
00266               SKIPSPACE(cp);
00267               if (*cp == ')')
00268                   break;
00269               if (*cp != ',')
00270                   return CALLFUNC(g, handle_error) (g, cp, ",");
00271            } while (1);
00272 
00273            *valp = 1;       /* XXX */
00274            cp++;
00275        }
00276     }
00277     
00278     return cp;
00279 }
00280 
00281 
00282 
00283 static const char *
00284 parse_product (IfParser *g, const char *cp, long *valp)
00285 {
00286     long rightval;
00287 
00288     DO (cp = parse_value (g, cp, valp));
00289     SKIPSPACE (cp);
00290 
00291     switch (*cp) {
00292       case '*':
00293        DO (cp = parse_product (g, cp + 1, &rightval));
00294        *valp = (*valp * rightval);
00295        break;
00296 
00297       case '/':
00298        DO (cp = parse_product (g, cp + 1, &rightval));
00299        *valp = (*valp / rightval);
00300        break;
00301 
00302       case '%':
00303        DO (cp = parse_product (g, cp + 1, &rightval));
00304        *valp = (*valp % rightval);
00305        break;
00306     }
00307     return cp;
00308 }
00309 
00310 
00311 static const char *
00312 parse_sum (IfParser *g, const char *cp, long *valp)
00313 {
00314     long rightval;
00315 
00316     DO (cp = parse_product (g, cp, valp));
00317     SKIPSPACE (cp);
00318 
00319     switch (*cp) {
00320       case '+':
00321        DO (cp = parse_sum (g, cp + 1, &rightval));
00322        *valp = (*valp + rightval);
00323        break;
00324 
00325       case '-':
00326        DO (cp = parse_sum (g, cp + 1, &rightval));
00327        *valp = (*valp - rightval);
00328        break;
00329     }
00330     return cp;
00331 }
00332 
00333 
00334 static const char *
00335 parse_shift (IfParser *g, const char *cp, long *valp)
00336 {
00337     long rightval;
00338 
00339     DO (cp = parse_sum (g, cp, valp));
00340     SKIPSPACE (cp);
00341 
00342     switch (*cp) {
00343       case '<':
00344        if (cp[1] == '<') {
00345            DO (cp = parse_shift (g, cp + 2, &rightval));
00346            *valp = (*valp << rightval);
00347        }
00348        break;
00349 
00350       case '>':
00351        if (cp[1] == '>') {
00352            DO (cp = parse_shift (g, cp + 2, &rightval));
00353            *valp = (*valp >> rightval);
00354        }
00355        break;
00356     }
00357     return cp;
00358 }
00359 
00360 
00361 static const char *
00362 parse_inequality (IfParser *g, const char *cp, long *valp)
00363 {
00364     long rightval;
00365 
00366     DO (cp = parse_shift (g, cp, valp));
00367     SKIPSPACE (cp);
00368 
00369     switch (*cp) {
00370       case '<':
00371        if (cp[1] == '=') {
00372            DO (cp = parse_inequality (g, cp + 2, &rightval));
00373            *valp = (*valp <= rightval);
00374        } else {
00375            DO (cp = parse_inequality (g, cp + 1, &rightval));
00376            *valp = (*valp < rightval);
00377        }
00378        break;
00379 
00380       case '>':
00381        if (cp[1] == '=') {
00382            DO (cp = parse_inequality (g, cp + 2, &rightval));
00383            *valp = (*valp >= rightval);
00384        } else {
00385            DO (cp = parse_inequality (g, cp + 1, &rightval));
00386            *valp = (*valp > rightval);
00387        }
00388        break;
00389     }
00390     return cp;
00391 }
00392 
00393 
00394 static const char *
00395 parse_equality (IfParser *g, const char *cp, long *valp)
00396 {
00397     long rightval;
00398 
00399     DO (cp = parse_inequality (g, cp, valp));
00400     SKIPSPACE (cp);
00401 
00402     switch (*cp) {
00403       case '=':
00404        if (cp[1] == '=')
00405            cp++;
00406        DO (cp = parse_equality (g, cp + 1, &rightval));
00407        *valp = (*valp == rightval);
00408        break;
00409 
00410       case '!':
00411        if (cp[1] != '=')
00412            break;
00413        DO (cp = parse_equality (g, cp + 2, &rightval));
00414        *valp = (*valp != rightval);
00415        break;
00416     }
00417     return cp;
00418 }
00419 
00420 
00421 static const char *
00422 parse_band (IfParser *g, const char *cp, long *valp)
00423 {
00424     long rightval;
00425 
00426     DO (cp = parse_equality (g, cp, valp));
00427     SKIPSPACE (cp);
00428 
00429     switch (*cp) {
00430       case '&':
00431        if (cp[1] != '&') {
00432            DO (cp = parse_band (g, cp + 1, &rightval));
00433            *valp = (*valp & rightval);
00434        }
00435        break;
00436     }
00437     return cp;
00438 }
00439 
00440 
00441 static const char *
00442 parse_bxor (IfParser *g, const char *cp, long *valp)
00443 {
00444     long rightval;
00445 
00446     DO (cp = parse_band (g, cp, valp));
00447     SKIPSPACE (cp);
00448 
00449     switch (*cp) {
00450       case '^':
00451        DO (cp = parse_bxor (g, cp + 1, &rightval));
00452        *valp = (*valp ^ rightval);
00453        break;
00454     }
00455     return cp;
00456 }
00457 
00458 
00459 static const char *
00460 parse_bor (IfParser *g, const char *cp, long *valp)
00461 {
00462     long rightval;
00463 
00464     DO (cp = parse_bxor (g, cp, valp));
00465     SKIPSPACE (cp);
00466 
00467     switch (*cp) {
00468       case '|':
00469        if (cp[1] != '|') {
00470            DO (cp = parse_bor (g, cp + 1, &rightval));
00471            *valp = (*valp | rightval);
00472        }
00473        break;
00474     }
00475     return cp;
00476 }
00477 
00478 
00479 static const char *
00480 parse_land (IfParser *g, const char *cp, long *valp)
00481 {
00482     long rightval;
00483 
00484     DO (cp = parse_bor (g, cp, valp));
00485     SKIPSPACE (cp);
00486 
00487     switch (*cp) {
00488       case '&':
00489        if (cp[1] != '&')
00490            return CALLFUNC(g, handle_error) (g, cp, "&&");
00491        DO (cp = parse_land (g, cp + 2, &rightval));
00492        *valp = (*valp && rightval);
00493        break;
00494     }
00495     return cp;
00496 }
00497 
00498 
00499 static const char *
00500 parse_lor (IfParser *g, const char *cp, long *valp)
00501 {
00502     long rightval;
00503 
00504     DO (cp = parse_land (g, cp, valp));
00505     SKIPSPACE (cp);
00506 
00507     switch (*cp) {
00508       case '|':
00509        if (cp[1] != '|')
00510            return CALLFUNC(g, handle_error) (g, cp, "||");
00511        DO (cp = parse_lor (g, cp + 2, &rightval));
00512        *valp = (*valp || rightval);
00513        break;
00514     }
00515     return cp;
00516 }
00517 
00518 
00519 static const char *
00520 parse_cond(IfParser *g, const char *cp, long *valp)
00521 {
00522     long trueval, falseval;
00523 
00524     DO (cp = parse_lor (g, cp, valp));
00525     SKIPSPACE (cp);
00526 
00527     switch (*cp) {
00528       case '?':
00529        DO (cp = parse_cond (g, cp + 1, &trueval));
00530        SKIPSPACE (cp);
00531        if (*cp != ':')
00532            return CALLFUNC(g, handle_error) (g, cp, ":");
00533        DO (cp = parse_cond (g, cp + 1, &falseval));
00534        *valp = (*valp ? trueval : falseval);
00535        break;
00536     }
00537     return cp;
00538 }
00539 
00540 
00541 /****************************************************************************
00542                           External Entry Points
00543  ****************************************************************************/
00544 
00545 const char *
00546 ParseIfExpression (IfParser *g, const char *cp, long *valp)
00547 {
00548     return parse_cond (g, cp, valp);
00549 }