Back to index

enigmail  1.4.3
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        if (rightval == 0)
00300          return CALLFUNC(g, handle_error) (g, cp, "0");
00301        *valp = (*valp / rightval);
00302        break;
00303 
00304       case '%':
00305        DO (cp = parse_product (g, cp + 1, &rightval));
00306        *valp = (*valp % rightval);
00307        break;
00308     }
00309     return cp;
00310 }
00311 
00312 
00313 static const char *
00314 parse_sum (IfParser *g, const char *cp, long *valp)
00315 {
00316     long rightval;
00317 
00318     DO (cp = parse_product (g, cp, valp));
00319     SKIPSPACE (cp);
00320 
00321     switch (*cp) {
00322       case '+':
00323        DO (cp = parse_sum (g, cp + 1, &rightval));
00324        *valp = (*valp + rightval);
00325        break;
00326 
00327       case '-':
00328        DO (cp = parse_sum (g, cp + 1, &rightval));
00329        *valp = (*valp - rightval);
00330        break;
00331     }
00332     return cp;
00333 }
00334 
00335 
00336 static const char *
00337 parse_shift (IfParser *g, const char *cp, long *valp)
00338 {
00339     long rightval;
00340 
00341     DO (cp = parse_sum (g, cp, valp));
00342     SKIPSPACE (cp);
00343 
00344     switch (*cp) {
00345       case '<':
00346        if (cp[1] == '<') {
00347            DO (cp = parse_shift (g, cp + 2, &rightval));
00348            *valp = (*valp << rightval);
00349        }
00350        break;
00351 
00352       case '>':
00353        if (cp[1] == '>') {
00354            DO (cp = parse_shift (g, cp + 2, &rightval));
00355            *valp = (*valp >> rightval);
00356        }
00357        break;
00358     }
00359     return cp;
00360 }
00361 
00362 
00363 static const char *
00364 parse_inequality (IfParser *g, const char *cp, long *valp)
00365 {
00366     long rightval;
00367 
00368     DO (cp = parse_shift (g, cp, valp));
00369     SKIPSPACE (cp);
00370 
00371     switch (*cp) {
00372       case '<':
00373        if (cp[1] == '=') {
00374            DO (cp = parse_inequality (g, cp + 2, &rightval));
00375            *valp = (*valp <= rightval);
00376        } else {
00377            DO (cp = parse_inequality (g, cp + 1, &rightval));
00378            *valp = (*valp < rightval);
00379        }
00380        break;
00381 
00382       case '>':
00383        if (cp[1] == '=') {
00384            DO (cp = parse_inequality (g, cp + 2, &rightval));
00385            *valp = (*valp >= rightval);
00386        } else {
00387            DO (cp = parse_inequality (g, cp + 1, &rightval));
00388            *valp = (*valp > rightval);
00389        }
00390        break;
00391     }
00392     return cp;
00393 }
00394 
00395 
00396 static const char *
00397 parse_equality (IfParser *g, const char *cp, long *valp)
00398 {
00399     long rightval;
00400 
00401     DO (cp = parse_inequality (g, cp, valp));
00402     SKIPSPACE (cp);
00403 
00404     switch (*cp) {
00405       case '=':
00406        if (cp[1] == '=')
00407            cp++;
00408        DO (cp = parse_equality (g, cp + 1, &rightval));
00409        *valp = (*valp == rightval);
00410        break;
00411 
00412       case '!':
00413        if (cp[1] != '=')
00414            break;
00415        DO (cp = parse_equality (g, cp + 2, &rightval));
00416        *valp = (*valp != rightval);
00417        break;
00418     }
00419     return cp;
00420 }
00421 
00422 
00423 static const char *
00424 parse_band (IfParser *g, const char *cp, long *valp)
00425 {
00426     long rightval;
00427 
00428     DO (cp = parse_equality (g, cp, valp));
00429     SKIPSPACE (cp);
00430 
00431     switch (*cp) {
00432       case '&':
00433        if (cp[1] != '&') {
00434            DO (cp = parse_band (g, cp + 1, &rightval));
00435            *valp = (*valp & rightval);
00436        }
00437        break;
00438     }
00439     return cp;
00440 }
00441 
00442 
00443 static const char *
00444 parse_bxor (IfParser *g, const char *cp, long *valp)
00445 {
00446     long rightval;
00447 
00448     DO (cp = parse_band (g, cp, valp));
00449     SKIPSPACE (cp);
00450 
00451     switch (*cp) {
00452       case '^':
00453        DO (cp = parse_bxor (g, cp + 1, &rightval));
00454        *valp = (*valp ^ rightval);
00455        break;
00456     }
00457     return cp;
00458 }
00459 
00460 
00461 static const char *
00462 parse_bor (IfParser *g, const char *cp, long *valp)
00463 {
00464     long rightval;
00465 
00466     DO (cp = parse_bxor (g, cp, valp));
00467     SKIPSPACE (cp);
00468 
00469     switch (*cp) {
00470       case '|':
00471        if (cp[1] != '|') {
00472            DO (cp = parse_bor (g, cp + 1, &rightval));
00473            *valp = (*valp | rightval);
00474        }
00475        break;
00476     }
00477     return cp;
00478 }
00479 
00480 
00481 static const char *
00482 parse_land (IfParser *g, const char *cp, long *valp)
00483 {
00484     long rightval;
00485 
00486     DO (cp = parse_bor (g, cp, valp));
00487     SKIPSPACE (cp);
00488 
00489     switch (*cp) {
00490       case '&':
00491        if (cp[1] != '&')
00492            return CALLFUNC(g, handle_error) (g, cp, "&&");
00493        DO (cp = parse_land (g, cp + 2, &rightval));
00494        *valp = (*valp && rightval);
00495        break;
00496     }
00497     return cp;
00498 }
00499 
00500 
00501 static const char *
00502 parse_lor (IfParser *g, const char *cp, long *valp)
00503 {
00504     long rightval;
00505 
00506     DO (cp = parse_land (g, cp, valp));
00507     SKIPSPACE (cp);
00508 
00509     switch (*cp) {
00510       case '|':
00511        if (cp[1] != '|')
00512            return CALLFUNC(g, handle_error) (g, cp, "||");
00513        DO (cp = parse_lor (g, cp + 2, &rightval));
00514        *valp = (*valp || rightval);
00515        break;
00516     }
00517     return cp;
00518 }
00519 
00520 
00521 static const char *
00522 parse_cond(IfParser *g, const char *cp, long *valp)
00523 {
00524     long trueval, falseval;
00525 
00526     DO (cp = parse_lor (g, cp, valp));
00527     SKIPSPACE (cp);
00528 
00529     switch (*cp) {
00530       case '?':
00531        DO (cp = parse_cond (g, cp + 1, &trueval));
00532        SKIPSPACE (cp);
00533        if (*cp != ':')
00534            return CALLFUNC(g, handle_error) (g, cp, ":");
00535        DO (cp = parse_cond (g, cp + 1, &falseval));
00536        *valp = (*valp ? trueval : falseval);
00537        break;
00538     }
00539     return cp;
00540 }
00541 
00542 
00543 /****************************************************************************
00544                           External Entry Points
00545  ****************************************************************************/
00546 
00547 const char *
00548 ParseIfExpression (IfParser *g, const char *cp, long *valp)
00549 {
00550     return parse_cond (g, cp, valp);
00551 }