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

# build/expression.c

Go to the documentation of this file.

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