# build/expression.c

Go to the documentation of this file.

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