rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

build/expression.c
Go to the documentation of this file.

    00001
    00014 #include "system.h"
    00015
    00016 #include <rpmio.h>
    00017 #include <rpmiotypes.h>
    00018 #include <rpmlog.h>
    00019
    00020 #include <rpmbuild.h>
    00021
    00022 #include "debug.h"
    00023
    00024 /* #define DEBUG_PARSER 1 */
    00025
    00026 #ifdef DEBUG_PARSER
    00027 #include <stdio.h>
    00028 #define DEBUG(x) do { x ; } while (0)
    00029 #else
    00030 #define DEBUG(x)
    00031 #endif
    00032
    00036 typedef struct _value {
    00037     enum { VALUE_TYPE_INTEGER, VALUE_TYPE_STRING } type;
    00038     union {
    00039         const char *s;
    00040         int i;
    00041     } data;
    00042 } *Value;
    00043
    00046 static Value valueMakeInteger(int i)
    00047         /*@*/
    00048 {
    00049     Value v;
    00050
    00051     v = (Value) xmalloc(sizeof(*v));
    00052     v->type = VALUE_TYPE_INTEGER;
    00053     v->data.i = i;
    00054     return v;
    00055 }
    00056
    00059 static Value valueMakeString(/*@only@*/ const char *s)
    00060         /*@*/
    00061 {
    00062     Value v;
    00063
    00064     v = (Value) xmalloc(sizeof(*v));
    00065     v->type = VALUE_TYPE_STRING;
    00066     v->data.s = s;
    00067     return v;
    00068 }
    00069
    00072 static void valueFree( /*@only@*/ Value v)
    00073         /*@modifies v @*/
    00074 {
    00075     if (v) {
    00076         if (v->type == VALUE_TYPE_STRING)
    00077             v->data.s = _free(v->data.s);
    00078         v = _free(v);
    00079     }
    00080 }
    00081
    00082 #ifdef DEBUG_PARSER
    00083 static void valueDump(const char *msg, Value v, FILE *fp)
    00084         /*@*/
    00085 {
    00086     if (msg)
    00087         fprintf(fp, "%s ", msg);
    00088     if (v) {
    00089         if (v->type == VALUE_TYPE_INTEGER)
    00090             fprintf(fp, "INTEGER %d\n", v->data.i);
    00091         else
    00092             fprintf(fp, "STRING '%s'\n", v->data.s);
    00093     } else
    00094         fprintf(fp, "NULL\n");
    00095 }
    00096 #endif
    00097
    00098 #define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
    00099 #define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
    00100 #define valueSameType(v1,v2) ((v1)->type == (v2)->type)
    00101
    00102
    00106 typedef struct _parseState {
    00107 /*@owned@*/
    00108     char *str;
    00109 /*@dependent@*/
    00110     char *p;
    00111     int nextToken;
    00112 /*@relnull@*/
    00113     Value tokenValue;
    00114     Spec spec;
    00115 } *ParseState;
    00116
    00117
    00122 #define TOK_EOF          1
    00123 #define TOK_INTEGER      2
    00124 #define TOK_STRING       3
    00125 #define TOK_IDENTIFIER   4
    00126 #define TOK_ADD          5
    00127 #define TOK_MINUS        6
    00128 #define TOK_MULTIPLY     7
    00129 #define TOK_DIVIDE       8
    00130 #define TOK_OPEN_P       9
    00131 #define TOK_CLOSE_P     10
    00132 #define TOK_EQ          11
    00133 #define TOK_NEQ         12
    00134 #define TOK_LT          13
    00135 #define TOK_LE          14
    00136 #define TOK_GT          15
    00137 #define TOK_GE          16
    00138 #define TOK_NOT         17
    00139 #define TOK_LOGICAL_AND 18
    00140 #define TOK_LOGICAL_OR  19
    00141
    00143 #define EXPRBUFSIZ      BUFSIZ
    00144
    00145 #if defined(DEBUG_PARSER)
    00146 typedef struct exprTokTableEntry {
    00147     const char *name;
    00148     int val;
    00149 } ETTE_t;
    00150
    00151 ETTE_t exprTokTable[] = {
    00152     { "EOF",    TOK_EOF },
    00153     { "I",      TOK_INTEGER },
    00154     { "S",      TOK_STRING },
    00155     { "ID",     TOK_IDENTIFIER },
    00156     { "+",      TOK_ADD },
    00157     { "-",      TOK_MINUS },
    00158     { "*",      TOK_MULTIPLY },
    00159     { "/",      TOK_DIVIDE },
    00160     { "( ",     TOK_OPEN_P },
    00161     { " )",     TOK_CLOSE_P },
    00162     { "==",     TOK_EQ },
    00163     { "!=",     TOK_NEQ },
    00164     { "<",      TOK_LT },
    00165     { "<=",     TOK_LE },
    00166     { ">",      TOK_GT },
    00167     { ">=",     TOK_GE },
    00168     { "!",      TOK_NOT },
    00169     { "&&",     TOK_LOGICAL_AND },
    00170     { "||",     TOK_LOGICAL_OR },
    00171     { NULL, 0 }
    00172 };
    00173
    00174 static const char *prToken(int val)
    00175         /*@*/
    00176 {
    00177     ETTE_t *et;
    00178
    00179     for (et = exprTokTable; et->name != NULL; et++) {
    00180         if (val == et->val)
    00181             return et->name;
    00182     }
    00183     return "???";
    00184 }
    00185 #endif  /* DEBUG_PARSER */
    00186
    00190 static int rdToken(ParseState state)
    00191         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00192         /*@modifies state->nextToken, state->p, state->tokenValue,
    00193                 rpmGlobalMacroContext, internalState @*/
    00194 {
    00195     int token;
    00196     Value v = NULL;
    00197     char *p = state->p;
    00198
    00199     /* Skip whitespace before the next token. */
    00200     while (*p && xisspace(*p)) p++;
    00201
    00202     switch (*p) {
    00203     case '\0':
    00204         token = TOK_EOF;
    00205         p--;
    00206         break;
    00207     case '+':
    00208         token = TOK_ADD;
    00209         break;
    00210     case '-':
    00211         token = TOK_MINUS;
    00212         break;
    00213     case '*':
    00214         token = TOK_MULTIPLY;
    00215         break;
    00216     case '/':
    00217         token = TOK_DIVIDE;
    00218         break;
    00219     case '(':
    00220         token = TOK_OPEN_P;
    00221         break;
    00222     case ')':
    00223         token = TOK_CLOSE_P;
    00224         break;
    00225     case '=':
    00226         if (p[1] == '=') {
    00227             token = TOK_EQ;
    00228             p++;
    00229         } else {
    00230             rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n"));
    00231             return -1;
    00232         }
    00233         break;
    00234     case '!':
    00235         if (p[1] == '=') {
    00236             token = TOK_NEQ;
    00237             p++;
    00238         } else
    00239             token = TOK_NOT;
    00240         break;
    00241     case '<':
    00242         if (p[1] == '=') {
    00243             token = TOK_LE;
    00244             p++;
    00245         } else
    00246             token = TOK_LT;
    00247         break;
    00248     case '>':
    00249         if (p[1] == '=') {
    00250             token = TOK_GE;
    00251             p++;
    00252         } else
    00253             token = TOK_GT;
    00254         break;
    00255     case '&':
    00256         if (p[1] == '&') {
    00257             token = TOK_LOGICAL_AND;
    00258             p++;
    00259         } else {
    00260             rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n"));
    00261             return -1;
    00262         }
    00263         break;
    00264     case '|':
    00265         if (p[1] == '|') {
    00266             token = TOK_LOGICAL_OR;
    00267             p++;
    00268         } else {
    00269             rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n"));
    00270             return -1;
    00271         }
    00272         break;
    00273
    00274     default:
    00275         if (xisdigit(*p)) {
    00276             char temp[EXPRBUFSIZ], *t = temp;
    00277
    00278             temp[0] = '\0';
    00279             while (*p && xisdigit(*p))
    00280                 *t++ = *p++;
    00281             *t++ = '\0';
    00282             p--;
    00283
    00284             token = TOK_INTEGER;
    00285             v = valueMakeInteger(atoi(temp));
    00286
    00287         } else if (xisalpha(*p)) {
    00288             char temp[EXPRBUFSIZ], *t = temp;
    00289
    00290             temp[0] = '\0';
    00291             while (*p && (xisalnum(*p) || *p == '_'))
    00292                 *t++ = *p++;
    00293             *t++ = '\0';
    00294             p--;
    00295
    00296             token = TOK_IDENTIFIER;
    00297             v = valueMakeString( xstrdup(temp) );
    00298
    00299         } else if (*p == '\"') {
    00300             char temp[EXPRBUFSIZ], *t = temp;
    00301
    00302             temp[0] = '\0';
    00303             p++;
    00304             while (*p && *p != '\"')
    00305                 *t++ = *p++;
    00306             *t++ = '\0';
    00307
    00308             token = TOK_STRING;
    00309             v = valueMakeString( rpmExpand(temp, NULL) );
    00310
    00311         } else {
    00312             rpmlog(RPMLOG_ERR, _("parse error in expression\n"));
    00313             return -1;
    00314         }
    00315     }
    00316
    00317     state->p = p + 1;
    00318     state->nextToken = token;
    00319     state->tokenValue = v;
    00320
    00321     DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
    00322     DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
    00323
    00324     return 0;
    00325 }
    00326
    00327 /*@null@*/
    00328 static Value doLogical(ParseState state)
    00329         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00330         /*@modifies state->nextToken, state->p, state->tokenValue,
    00331                 rpmGlobalMacroContext, internalState @*/;
    00332
    00336 /*@null@*/
    00337 static Value doPrimary(ParseState state)
    00338         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00339         /*@modifies state->nextToken, state->p, state->tokenValue,
    00340                 rpmGlobalMacroContext, internalState @*/
    00341 {
    00342     Value v;
    00343
    00344     DEBUG(printf("doPrimary()\n"));
    00345
    00346     switch (state->nextToken) {
    00347     case TOK_OPEN_P:
    00348         if (rdToken(state))
    00349             return NULL;
    00350         v = doLogical(state);
    00351         if (state->nextToken != TOK_CLOSE_P) {
    00352             rpmlog(RPMLOG_ERR, _("unmatched (\n"));
    00353             return NULL;
    00354         }
    00355         if (rdToken(state))
    00356             return NULL;
    00357         break;
    00358
    00359     case TOK_INTEGER:
    00360     case TOK_STRING:
    00361         v = state->tokenValue;
    00362         if (rdToken(state))
    00363             return NULL;
    00364         break;
    00365
    00366     case TOK_IDENTIFIER: {
    00367         const char *name = state->tokenValue->data.s;
    00368
    00369         v = valueMakeString( rpmExpand(name, NULL) );
    00370         if (rdToken(state))
    00371             return NULL;
    00372         break;
    00373     }
    00374
    00375     case TOK_MINUS:
    00376         if (rdToken(state))
    00377             return NULL;
    00378
    00379         v = doPrimary(state);
    00380         if (v == NULL)
    00381             return NULL;
    00382
    00383         if (! valueIsInteger(v)) {
    00384             rpmlog(RPMLOG_ERR, _("- only on numbers\n"));
    00385             return NULL;
    00386         }
    00387
    00388         v = valueMakeInteger(- v->data.i);
    00389         break;
    00390
    00391     case TOK_NOT:
    00392         if (rdToken(state))
    00393             return NULL;
    00394
    00395         v = doPrimary(state);
    00396         if (v == NULL)
    00397             return NULL;
    00398
    00399         if (! valueIsInteger(v)) {
    00400             rpmlog(RPMLOG_ERR, _("! only on numbers\n"));
    00401             return NULL;
    00402         }
    00403
    00404         v = valueMakeInteger(! v->data.i);
    00405         break;
    00406     default:
    00407         return NULL;
    00408         /*@notreached@*/ break;
    00409     }
    00410
    00411     DEBUG(valueDump("doPrimary:", v, stdout));
    00412     return v;
    00413 }
    00414
    00418 /*@null@*/
    00419 static Value doMultiplyDivide(ParseState state)
    00420         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00421         /*@modifies state->nextToken, state->p, state->tokenValue,
    00422                 rpmGlobalMacroContext, internalState @*/
    00423 {
    00424     Value v1, v2 = NULL;
    00425
    00426     DEBUG(printf("doMultiplyDivide()\n"));
    00427
    00428     v1 = doPrimary(state);
    00429     if (v1 == NULL)
    00430         return NULL;
    00431
    00432     while (state->nextToken == TOK_MULTIPLY
    00433          || state->nextToken == TOK_DIVIDE)
    00434     {
    00435         int op = state->nextToken;
    00436
    00437         if (rdToken(state))
    00438             return NULL;
    00439
    00440         if (v2) valueFree(v2);
    00441
    00442         v2 = doPrimary(state);
    00443         if (v2 == NULL)
    00444             return NULL;
    00445
    00446         if (! valueSameType(v1, v2)) {
    00447             rpmlog(RPMLOG_ERR, _("types must match\n"));
    00448             return NULL;
    00449         }
    00450
    00451         if (valueIsInteger(v1)) {
    00452             int i1 = v1->data.i, i2 = v2->data.i;
    00453
    00454             valueFree(v1);
    00455             if (op == TOK_MULTIPLY)
    00456                 v1 = valueMakeInteger(i1 * i2);
    00457             else
    00458                 v1 = valueMakeInteger(i1 / i2);
    00459         } else {
    00460             rpmlog(RPMLOG_ERR, _("* / not suported for strings\n"));
    00461             return NULL;
    00462         }
    00463     }
    00464
    00465     if (v2) valueFree(v2);
    00466     return v1;
    00467 }
    00468
    00472 /*@null@*/
    00473 static Value doAddSubtract(ParseState state)
    00474         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00475         /*@modifies state->nextToken, state->p, state->tokenValue,
    00476                 rpmGlobalMacroContext, internalState @*/
    00477 {
    00478     Value v1, v2 = NULL;
    00479
    00480     DEBUG(printf("doAddSubtract()\n"));
    00481
    00482     v1 = doMultiplyDivide(state);
    00483     if (v1 == NULL)
    00484         return NULL;
    00485
    00486     while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
    00487         int op = state->nextToken;
    00488
    00489         if (rdToken(state))
    00490             return NULL;
    00491
    00492         if (v2) valueFree(v2);
    00493
    00494         v2 = doMultiplyDivide(state);
    00495         if (v2 == NULL)
    00496             return NULL;
    00497
    00498         if (! valueSameType(v1, v2)) {
    00499             rpmlog(RPMLOG_ERR, _("types must match\n"));
    00500             return NULL;
    00501         }
    00502
    00503         if (valueIsInteger(v1)) {
    00504             int i1 = v1->data.i, i2 = v2->data.i;
    00505
    00506             valueFree(v1);
    00507             if (op == TOK_ADD)
    00508                 v1 = valueMakeInteger(i1 + i2);
    00509             else
    00510                 v1 = valueMakeInteger(i1 - i2);
    00511         } else {
    00512             char *copy;
    00513
    00514             if (op == TOK_MINUS) {
    00515                 rpmlog(RPMLOG_ERR, _("- not suported for strings\n"));
    00516                 return NULL;
    00517             }
    00518
    00519             copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
    00520             (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
    00521
    00522             valueFree(v1);
    00523             v1 = valueMakeString(copy);
    00524         }
    00525     }
    00526
    00527     if (v2) valueFree(v2);
    00528     return v1;
    00529 }
    00530
    00534 /*@null@*/
    00535 static Value doRelational(ParseState state)
    00536         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00537         /*@modifies state->nextToken, state->p, state->tokenValue,
    00538                 rpmGlobalMacroContext, internalState @*/
    00539 {
    00540     Value v1, v2 = NULL;
    00541
    00542     DEBUG(printf("doRelational()\n"));
    00543
    00544     v1 = doAddSubtract(state);
    00545     if (v1 == NULL)
    00546         return NULL;
    00547
    00548     while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
    00549         int op = state->nextToken;
    00550
    00551         if (rdToken(state))
    00552             return NULL;
    00553
    00554         if (v2) valueFree(v2);
    00555
    00556         v2 = doAddSubtract(state);
    00557         if (v2 == NULL)
    00558             return NULL;
    00559
    00560         if (! valueSameType(v1, v2)) {
    00561             rpmlog(RPMLOG_ERR, _("types must match\n"));
    00562             return NULL;
    00563         }
    00564
    00565         if (valueIsInteger(v1)) {
    00566             int i1 = v1->data.i, i2 = v2->data.i, r = 0;
    00567             switch (op) {
    00568             case TOK_EQ:
    00569                 r = (i1 == i2);
    00570                 /*@switchbreak@*/ break;
    00571             case TOK_NEQ:
    00572                 r = (i1 != i2);
    00573                 /*@switchbreak@*/ break;
    00574             case TOK_LT:
    00575                 r = (i1 < i2);
    00576                 /*@switchbreak@*/ break;
    00577             case TOK_LE:
    00578                 r = (i1 <= i2);
    00579                 /*@switchbreak@*/ break;
    00580             case TOK_GT:
    00581                 r = (i1 > i2);
    00582                 /*@switchbreak@*/ break;
    00583             case TOK_GE:
    00584                 r = (i1 >= i2);
    00585                 /*@switchbreak@*/ break;
    00586             default:
    00587                 /*@switchbreak@*/ break;
    00588             }
    00589             valueFree(v1);
    00590             v1 = valueMakeInteger(r);
    00591         } else {
    00592             const char * s1 = v1->data.s;
    00593             const char * s2 = v2->data.s;
    00594             int r = 0;
    00595             switch (op) {
    00596             case TOK_EQ:
    00597                 r = (strcmp(s1,s2) == 0);
    00598                 /*@switchbreak@*/ break;
    00599             case TOK_NEQ:
    00600                 r = (strcmp(s1,s2) != 0);
    00601                 /*@switchbreak@*/ break;
    00602             case TOK_LT:
    00603                 r = (strcmp(s1,s2) < 0);
    00604                 /*@switchbreak@*/ break;
    00605             case TOK_LE:
    00606                 r = (strcmp(s1,s2) <= 0);
    00607                 /*@switchbreak@*/ break;
    00608             case TOK_GT:
    00609                 r = (strcmp(s1,s2) > 0);
    00610                 /*@switchbreak@*/ break;
    00611             case TOK_GE:
    00612                 r = (strcmp(s1,s2) >= 0);
    00613                 /*@switchbreak@*/ break;
    00614             default:
    00615                 /*@switchbreak@*/ break;
    00616             }
    00617             valueFree(v1);
    00618             v1 = valueMakeInteger(r);
    00619         }
    00620     }
    00621
    00622     if (v2) valueFree(v2);
    00623     return v1;
    00624 }
    00625
    00629 static Value doLogical(ParseState state)
    00630         /*@globals rpmGlobalMacroContext, h_errno @*/
    00631         /*@modifies state->nextToken, state->p, state->tokenValue,
    00632                 rpmGlobalMacroContext @*/
    00633 {
    00634     Value v1, v2 = NULL;
    00635
    00636     DEBUG(printf("doLogical()\n"));
    00637
    00638     v1 = doRelational(state);
    00639     if (v1 == NULL)
    00640         return NULL;
    00641
    00642     while (state->nextToken == TOK_LOGICAL_AND
    00643         || state->nextToken == TOK_LOGICAL_OR)
    00644     {
    00645         int op = state->nextToken;
    00646
    00647         if (rdToken(state))
    00648             return NULL;
    00649
    00650         if (v2) valueFree(v2);
    00651
    00652         v2 = doRelational(state);
    00653         if (v2 == NULL)
    00654             return NULL;
    00655
    00656         if (! valueSameType(v1, v2)) {
    00657             rpmlog(RPMLOG_ERR, _("types must match\n"));
    00658             return NULL;
    00659         }
    00660
    00661         if (valueIsInteger(v1)) {
    00662             int i1 = v1->data.i, i2 = v2->data.i;
    00663
    00664             valueFree(v1);
    00665             if (op == TOK_LOGICAL_AND)
    00666                 v1 = valueMakeInteger(i1 && i2);
    00667             else
    00668                 v1 = valueMakeInteger(i1 || i2);
    00669         } else {
    00670             rpmlog(RPMLOG_ERR, _("&& and || not suported for strings\n"));
    00671             return NULL;
    00672         }
    00673     }
    00674
    00675     if (v2) valueFree(v2);
    00676     return v1;
    00677 }
    00678
    00679 int parseExpressionBoolean(Spec spec, const char *expr)
    00680 {
    00681     struct _parseState state;
    00682     int result = -1;
    00683     Value v;
    00684
    00685     DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
    00686
    00687     /* Initialize the expression parser state. */
    00688     state.p = state.str = xstrdup(expr);
    00689     state.spec = spec;
    00690     state.nextToken = 0;
    00691     state.tokenValue = NULL;
    00692     (void) rdToken(&state);
    00693
    00694     /* Parse the expression. */
    00695     v = doLogical(&state);
    00696     if (!v) {
    00697         state.str = _free(state.str);
    00698         return -1;
    00699     }
    00700
    00701     /* If the next token is not TOK_EOF, we have a syntax error. */
    00702     if (state.nextToken != TOK_EOF) {
    00703         rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
    00704         state.str = _free(state.str);
    00705         return -1;
    00706     }
    00707
    00708     DEBUG(valueDump("parseExprBoolean:", v, stdout));
    00709
    00710     switch (v->type) {
    00711     case VALUE_TYPE_INTEGER:
    00712         result = v->data.i != 0;
    00713         break;
    00714     case VALUE_TYPE_STRING:
    00715         result = v->data.s[0] != '\0';
    00716         break;
    00717     default:
    00718         break;
    00719     }
    00720
    00721     state.str = _free(state.str);
    00722     valueFree(v);
    00723     return result;
    00724 }
    00725
    00726 char * parseExpressionString(Spec spec, const char *expr)
    00727 {
    00728     struct _parseState state;
    00729     char *result = NULL;
    00730     Value v;
    00731
    00732     DEBUG(printf("parseExprString(?, '%s')\n", expr));
    00733
    00734     /* Initialize the expression parser state. */
    00735     state.p = state.str = xstrdup(expr);
    00736     state.spec = spec;
    00737     state.nextToken = 0;
    00738     state.tokenValue = NULL;
    00739     (void) rdToken(&state);
    00740
    00741     /* Parse the expression. */
    00742     v = doLogical(&state);
    00743     if (!v) {
    00744         state.str = _free(state.str);
    00745         return NULL;
    00746     }
    00747
    00748     /* If the next token is not TOK_EOF, we have a syntax error. */
    00749     if (state.nextToken != TOK_EOF) {
    00750         rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
    00751         state.str = _free(state.str);
    00752         return NULL;
    00753     }
    00754
    00755     DEBUG(valueDump("parseExprString:", v, stdout));
    00756
    00757     switch (v->type) {
    00758     case VALUE_TYPE_INTEGER: {
    00759         char buf[128];
    00760         sprintf(buf, "%d", v->data.i);
    00761         result = xstrdup(buf);
    00762     } break;
    00763     case VALUE_TYPE_STRING:
    00764         result = xstrdup(v->data.s);
    00765         break;
    00766     default:
    00767         break;
    00768     }
    00769
    00770     state.str = _free(state.str);
    00771     valueFree(v);
    00772     return result;
    00773 }