Main Page Modules Data Structures File List Data Fields Globals Related Pages

# 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 /*@-boundswrite@*/
    00185 static int rdToken(ParseState state)
    00186         /*@globals rpmGlobalMacroContext @*/
    00187         /*@modifies state->nextToken, state->p, state->tokenValue,
    00188                 rpmGlobalMacroContext @*/
    00189 {
    00190   int token;
    00191   Value v = NULL;
    00192   char *p = state->p;
    00193
    00194   /* Skip whitespace before the next token. */
    00195   while (*p && xisspace(*p)) p++;
    00196
    00197   switch (*p) {
    00198   case '\0':
    00199     token = TOK_EOF;
    00200     p--;
    00201     break;
    00202   case '+':
    00203     token = TOK_ADD;
    00204     break;
    00205   case '-':
    00206     token = TOK_MINUS;
    00207     break;
    00208   case '*':
    00209     token = TOK_MULTIPLY;
    00210     break;
    00211   case '/':
    00212     token = TOK_DIVIDE;
    00213     break;
    00214   case '(':
    00215     token = TOK_OPEN_P;
    00216     break;
    00217   case ')':
    00218     token = TOK_CLOSE_P;
    00219     break;
    00220   case '=':
    00221     if (p[1] == '=') {
    00222       token = TOK_EQ;
    00223       p++;
    00224     } else {
    00225       rpmError(RPMERR_BADSPEC, _("syntax error while parsing ==\n"));
    00226       return -1;
    00227     }
    00228     break;
    00229   case '!':
    00230     if (p[1] == '=') {
    00231       token = TOK_NEQ;
    00232       p++;
    00233     } else
    00234       token = TOK_NOT;
    00235     break;
    00236   case '<':
    00237     if (p[1] == '=') {
    00238       token = TOK_LE;
    00239       p++;
    00240     } else
    00241       token = TOK_LT;
    00242     break;
    00243   case '>':
    00244     if (p[1] == '=') {
    00245       token = TOK_GE;
    00246       p++;
    00247     } else
    00248       token = TOK_GT;
    00249     break;
    00250   case '&':
    00251     if (p[1] == '&') {
    00252       token = TOK_LOGICAL_AND;
    00253       p++;
    00254     } else {
    00255       rpmError(RPMERR_BADSPEC, _("syntax error while parsing &&\n"));
    00256       return -1;
    00257     }
    00258     break;
    00259   case '|':
    00260     if (p[1] == '|') {
    00261       token = TOK_LOGICAL_OR;
    00262       p++;
    00263     } else {
    00264       rpmError(RPMERR_BADSPEC, _("syntax error while parsing ||\n"));
    00265       return -1;
    00266     }
    00267     break;
    00268
    00269   default:
    00270     if (xisdigit(*p)) {
    00271       char temp[EXPRBUFSIZ], *t = temp;
    00272
    00273       temp[0] = '\0';
    00274       while (*p && xisdigit(*p))
    00275         *t++ = *p++;
    00276       *t++ = '\0';
    00277       p--;
    00278
    00279       token = TOK_INTEGER;
    00280       v = valueMakeInteger(atoi(temp));
    00281
    00282     } else if (xisalpha(*p)) {
    00283       char temp[EXPRBUFSIZ], *t = temp;
    00284
    00285       temp[0] = '\0';
    00286       while (*p && (xisalnum(*p) || *p == '_'))
    00287         *t++ = *p++;
    00288       *t++ = '\0';
    00289       p--;
    00290
    00291       token = TOK_IDENTIFIER;
    00292       v = valueMakeString( xstrdup(temp) );
    00293
    00294     } else if (*p == '\"') {
    00295       char temp[EXPRBUFSIZ], *t = temp;
    00296
    00297       temp[0] = '\0';
    00298       p++;
    00299       while (*p && *p != '\"')
    00300         *t++ = *p++;
    00301       *t++ = '\0';
    00302
    00303       token = TOK_STRING;
    00304       v = valueMakeString( rpmExpand(temp, NULL) );
    00305
    00306     } else {
    00307       rpmError(RPMERR_BADSPEC, _("parse error in expression\n"));
    00308       return -1;
    00309     }
    00310   }
    00311
    00312   state->p = p + 1;
    00313   state->nextToken = token;
    00314   state->tokenValue = v;
    00315
    00316   DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
    00317   DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
    00318
    00319   return 0;
    00320 }
    00321 /*@=boundswrite@*/
    00322
    00323 static Value doLogical(ParseState state)
    00324         /*@globals rpmGlobalMacroContext @*/
    00325         /*@modifies state->nextToken, state->p, state->tokenValue,
    00326                 rpmGlobalMacroContext @*/;
    00327
    00331 static Value doPrimary(ParseState state)
    00332         /*@globals rpmGlobalMacroContext @*/
    00333         /*@modifies state->nextToken, state->p, state->tokenValue,
    00334                 rpmGlobalMacroContext @*/
    00335 {
    00336   Value v;
    00337
    00338   DEBUG(printf("doPrimary()\n"));
    00339
    00340   /*@-branchstate@*/
    00341   switch (state->nextToken) {
    00342   case TOK_OPEN_P:
    00343     if (rdToken(state))
    00344       return NULL;
    00345     v = doLogical(state);
    00346     if (state->nextToken != TOK_CLOSE_P) {
    00347       rpmError(RPMERR_BADSPEC, _("unmatched (\n"));
    00348       return NULL;
    00349     }
    00350     break;
    00351
    00352   case TOK_INTEGER:
    00353   case TOK_STRING:
    00354     v = state->tokenValue;
    00355     if (rdToken(state))
    00356       return NULL;
    00357     break;
    00358
    00359   case TOK_IDENTIFIER: {
    00360     const char *name = state->tokenValue->data.s;
    00361
    00362     v = valueMakeString( rpmExpand(name, NULL) );
    00363     if (rdToken(state))
    00364       return NULL;
    00365     break;
    00366   }
    00367
    00368   case TOK_MINUS:
    00369     if (rdToken(state))
    00370       return NULL;
    00371
    00372     v = doPrimary(state);
    00373     if (v == NULL)
    00374       return NULL;
    00375
    00376     if (! valueIsInteger(v)) {
    00377       rpmError(RPMERR_BADSPEC, _("- only on numbers\n"));
    00378       return NULL;
    00379     }
    00380
    00381     v = valueMakeInteger(- v->data.i);
    00382     break;
    00383
    00384   case TOK_NOT:
    00385     if (rdToken(state))
    00386       return NULL;
    00387
    00388     v = doPrimary(state);
    00389     if (v == NULL)
    00390       return NULL;
    00391
    00392     if (! valueIsInteger(v)) {
    00393       rpmError(RPMERR_BADSPEC, _("! only on numbers\n"));
    00394       return NULL;
    00395     }
    00396
    00397     v = valueMakeInteger(! v->data.i);
    00398     break;
    00399   default:
    00400     return NULL;
    00401     /*@notreached@*/ break;
    00402   }
    00403   /*@=branchstate@*/
    00404
    00405   DEBUG(valueDump("doPrimary:", v, stdout));
    00406   return v;
    00407 }
    00408
    00412 static Value doMultiplyDivide(ParseState state)
    00413         /*@globals rpmGlobalMacroContext @*/
    00414         /*@modifies state->nextToken, state->p, state->tokenValue,
    00415                 rpmGlobalMacroContext @*/
    00416 {
    00417   Value v1, v2 = NULL;
    00418
    00419   DEBUG(printf("doMultiplyDivide()\n"));
    00420
    00421   v1 = doPrimary(state);
    00422   if (v1 == NULL)
    00423     return NULL;
    00424
    00425   /*@-branchstate@*/
    00426   while (state->nextToken == TOK_MULTIPLY
    00427          || state->nextToken == TOK_DIVIDE) {
    00428     int op = state->nextToken;
    00429
    00430     if (rdToken(state))
    00431       return NULL;
    00432
    00433     if (v2) valueFree(v2);
    00434
    00435     v2 = doPrimary(state);
    00436     if (v2 == NULL)
    00437       return NULL;
    00438
    00439     if (! valueSameType(v1, v2)) {
    00440       rpmError(RPMERR_BADSPEC, _("types must match\n"));
    00441       return NULL;
    00442     }
    00443
    00444     if (valueIsInteger(v1)) {
    00445       int i1 = v1->data.i, i2 = v2->data.i;
    00446
    00447       valueFree(v1);
    00448       if (op == TOK_MULTIPLY)
    00449         v1 = valueMakeInteger(i1 * i2);
    00450       else
    00451         v1 = valueMakeInteger(i1 / i2);
    00452     } else {
    00453       rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
    00454       return NULL;
    00455     }
    00456   }
    00457   /*@=branchstate@*/
    00458
    00459   if (v2) valueFree(v2);
    00460   return v1;
    00461 }
    00462
    00466 /*@-boundswrite@*/
    00467 static Value doAddSubtract(ParseState state)
    00468         /*@globals rpmGlobalMacroContext @*/
    00469         /*@modifies state->nextToken, state->p, state->tokenValue,
    00470                 rpmGlobalMacroContext @*/
    00471 {
    00472   Value v1, v2 = NULL;
    00473
    00474   DEBUG(printf("doAddSubtract()\n"));
    00475
    00476   v1 = doMultiplyDivide(state);
    00477   if (v1 == NULL)
    00478     return NULL;
    00479
    00480   /*@-branchstate@*/
    00481   while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
    00482     int op = state->nextToken;
    00483
    00484     if (rdToken(state))
    00485       return NULL;
    00486
    00487     if (v2) valueFree(v2);
    00488
    00489     v2 = doMultiplyDivide(state);
    00490     if (v2 == NULL)
    00491       return NULL;
    00492
    00493     if (! valueSameType(v1, v2)) {
    00494       rpmError(RPMERR_BADSPEC, _("types must match\n"));
    00495       return NULL;
    00496     }
    00497
    00498     if (valueIsInteger(v1)) {
    00499       int i1 = v1->data.i, i2 = v2->data.i;
    00500
    00501       valueFree(v1);
    00502       if (op == TOK_ADD)
    00503         v1 = valueMakeInteger(i1 + i2);
    00504       else
    00505         v1 = valueMakeInteger(i1 - i2);
    00506     } else {
    00507       char *copy;
    00508
    00509       if (op == TOK_MINUS) {
    00510         rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
    00511         return NULL;
    00512       }
    00513
    00514       copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
    00515       (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
    00516
    00517       valueFree(v1);
    00518       v1 = valueMakeString(copy);
    00519     }
    00520   }
    00521   /*@=branchstate@*/
    00522
    00523   if (v2) valueFree(v2);
    00524   return v1;
    00525 }
    00526 /*@=boundswrite@*/
    00527
    00531 static Value doRelational(ParseState state)
    00532         /*@globals rpmGlobalMacroContext @*/
    00533         /*@modifies state->nextToken, state->p, state->tokenValue,
    00534                 rpmGlobalMacroContext @*/
    00535 {
    00536   Value v1, v2 = NULL;
    00537
    00538   DEBUG(printf("doRelational()\n"));
    00539
    00540   v1 = doAddSubtract(state);
    00541   if (v1 == NULL)
    00542     return NULL;
    00543
    00544   /*@-branchstate@*/
    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       rpmError(RPMERR_BADSPEC, _("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   /*@=branchstate@*/
    00619
    00620   if (v2) valueFree(v2);
    00621   return v1;
    00622 }
    00623
    00627 static Value doLogical(ParseState state)
    00628         /*@globals rpmGlobalMacroContext @*/
    00629         /*@modifies state->nextToken, state->p, state->tokenValue,
    00630                 rpmGlobalMacroContext @*/
    00631 {
    00632   Value v1, v2 = NULL;
    00633
    00634   DEBUG(printf("doLogical()\n"));
    00635
    00636   v1 = doRelational(state);
    00637   if (v1 == NULL)
    00638     return NULL;
    00639
    00640   /*@-branchstate@*/
    00641   while (state->nextToken == TOK_LOGICAL_AND
    00642          || state->nextToken == TOK_LOGICAL_OR) {
    00643     int op = state->nextToken;
    00644
    00645     if (rdToken(state))
    00646       return NULL;
    00647
    00648     if (v2) valueFree(v2);
    00649
    00650     v2 = doRelational(state);
    00651     if (v2 == NULL)
    00652       return NULL;
    00653
    00654     if (! valueSameType(v1, v2)) {
    00655       rpmError(RPMERR_BADSPEC, _("types must match\n"));
    00656       return NULL;
    00657     }
    00658
    00659     if (valueIsInteger(v1)) {
    00660       int i1 = v1->data.i, i2 = v2->data.i;
    00661
    00662       valueFree(v1);
    00663       if (op == TOK_LOGICAL_AND)
    00664         v1 = valueMakeInteger(i1 && i2);
    00665       else
    00666         v1 = valueMakeInteger(i1 || i2);
    00667     } else {
    00668       rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
    00669       return NULL;
    00670     }
    00671   }
    00672   /*@=branchstate@*/
    00673
    00674   if (v2) valueFree(v2);
    00675   return v1;
    00676 }
    00677
    00678 int parseExpressionBoolean(Spec spec, const char *expr)
    00679 {
    00680   struct _parseState state;
    00681   int result = -1;
    00682   Value v;
    00683
    00684   DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
    00685
    00686   /* Initialize the expression parser state. */
    00687   state.p = state.str = xstrdup(expr);
    00688   state.spec = spec;
    00689   state.nextToken = 0;
    00690   state.tokenValue = NULL;
    00691   (void) rdToken(&state);
    00692
    00693   /* Parse the expression. */
    00694   v = doLogical(&state);
    00695   if (!v) {
    00696     state.str = _free(state.str);
    00697     return -1;
    00698   }
    00699
    00700   /* If the next token is not TOK_EOF, we have a syntax error. */
    00701   if (state.nextToken != TOK_EOF) {
    00702     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
    00703     state.str = _free(state.str);
    00704     return -1;
    00705   }
    00706
    00707   DEBUG(valueDump("parseExprBoolean:", v, stdout));
    00708
    00709   switch (v->type) {
    00710   case VALUE_TYPE_INTEGER:
    00711     result = v->data.i != 0;
    00712     break;
    00713   case VALUE_TYPE_STRING:
    00714 /*@-boundsread@*/
    00715     result = v->data.s[0] != '\0';
    00716 /*@=boundsread@*/
    00717     break;
    00718   default:
    00719     break;
    00720   }
    00721
    00722   state.str = _free(state.str);
    00723   valueFree(v);
    00724   return result;
    00725 }
    00726
    00727 char * parseExpressionString(Spec spec, const char *expr)
    00728 {
    00729   struct _parseState state;
    00730   char *result = NULL;
    00731   Value v;
    00732
    00733   DEBUG(printf("parseExprString(?, '%s')\n", expr));
    00734
    00735   /* Initialize the expression parser state. */
    00736   state.p = state.str = xstrdup(expr);
    00737   state.spec = spec;
    00738   state.nextToken = 0;
    00739   state.tokenValue = NULL;
    00740   (void) rdToken(&state);
    00741
    00742   /* Parse the expression. */
    00743   v = doLogical(&state);
    00744   if (!v) {
    00745     state.str = _free(state.str);
    00746     return NULL;
    00747   }
    00748
    00749   /* If the next token is not TOK_EOF, we have a syntax error. */
    00750   if (state.nextToken != TOK_EOF) {
    00751     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
    00752     state.str = _free(state.str);
    00753     return NULL;
    00754   }
    00755
    00756   DEBUG(valueDump("parseExprString:", v, stdout));
    00757
    00758   /*@-branchstate@*/
    00759   switch (v->type) {
    00760   case VALUE_TYPE_INTEGER: {
    00761     char buf[128];
    00762     sprintf(buf, "%d", v->data.i);
    00763     result = xstrdup(buf);
    00764   } break;
    00765   case VALUE_TYPE_STRING:
    00766     result = xstrdup(v->data.s);
    00767     break;
    00768   default:
    00769     break;
    00770   }
    00771   /*@=branchstate@*/
    00772
    00773   state.str = _free(state.str);
    00774   valueFree(v);
    00775   return result;
    00776 }