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 static int rdToken(ParseState state, int warn)
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       if (warn)
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 
00322 static Value doLogical(ParseState state)
00323         /*@globals rpmGlobalMacroContext @*/
00324         /*@modifies state->nextToken, state->p, state->tokenValue,
00325                 rpmGlobalMacroContext @*/;
00326 
00330 static Value doPrimary(ParseState state)
00331         /*@globals rpmGlobalMacroContext @*/
00332         /*@modifies state->nextToken, state->p, state->tokenValue,
00333                 rpmGlobalMacroContext @*/
00334 {
00335   Value v;
00336 
00337   DEBUG(printf("doPrimary()\n"));
00338 
00339   /*@-branchstate@*/
00340   switch (state->nextToken) {
00341   case TOK_OPEN_P:
00342     if (rdToken(state, 1))
00343       return NULL;
00344     v = doLogical(state);
00345     if (state->nextToken != TOK_CLOSE_P) {
00346       rpmError(RPMERR_BADSPEC, _("unmatched (\n"));
00347       return NULL;
00348     }
00349     break;
00350 
00351   case TOK_INTEGER:
00352   case TOK_STRING:
00353     v = state->tokenValue;
00354     if (rdToken(state, 1))
00355       return NULL;
00356     break;
00357 
00358   case TOK_IDENTIFIER: {
00359     const char *name = state->tokenValue->data.s;
00360 
00361     v = valueMakeString( rpmExpand(name, NULL) );
00362     if (rdToken(state, 1))
00363       return NULL;
00364     break;
00365   }
00366 
00367   case TOK_MINUS:
00368     if (rdToken(state, 1))
00369       return NULL;
00370 
00371     v = doPrimary(state);
00372     if (v == NULL)
00373       return NULL;
00374 
00375     if (! valueIsInteger(v)) {
00376       rpmError(RPMERR_BADSPEC, _("- only on numbers\n"));
00377       return NULL;
00378     }
00379 
00380     v = valueMakeInteger(- v->data.i);
00381     break;
00382 
00383   case TOK_NOT:
00384     if (rdToken(state, 1))
00385       return NULL;
00386 
00387     v = doPrimary(state);
00388     if (v == NULL)
00389       return NULL;
00390 
00391     if (! valueIsInteger(v)) {
00392       rpmError(RPMERR_BADSPEC, _("! only on numbers\n"));
00393       return NULL;
00394     }
00395 
00396     v = valueMakeInteger(! v->data.i);
00397     break;
00398   default:
00399     return NULL;
00400     /*@notreached@*/ break;
00401   }
00402   /*@=branchstate@*/
00403 
00404   DEBUG(valueDump("doPrimary:", v, stdout));
00405   return v;
00406 }
00407 
00411 static Value doMultiplyDivide(ParseState state)
00412         /*@globals rpmGlobalMacroContext @*/
00413         /*@modifies state->nextToken, state->p, state->tokenValue,
00414                 rpmGlobalMacroContext @*/
00415 {
00416   Value v1, v2 = NULL;
00417 
00418   DEBUG(printf("doMultiplyDivide()\n"));
00419 
00420   v1 = doPrimary(state);
00421   if (v1 == NULL)
00422     return NULL;
00423 
00424   /*@-branchstate@*/
00425   while (state->nextToken == TOK_MULTIPLY
00426          || state->nextToken == TOK_DIVIDE) {
00427     int op = state->nextToken;
00428 
00429     if (rdToken(state, 1))
00430       return NULL;
00431 
00432     if (v2) valueFree(v2);
00433 
00434     v2 = doPrimary(state);
00435     if (v2 == NULL)
00436       return NULL;
00437 
00438     if (! valueSameType(v1, v2)) {
00439       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00440       return NULL;
00441     }
00442 
00443     if (valueIsInteger(v1)) {
00444       int i1 = v1->data.i, i2 = v2->data.i;
00445 
00446       valueFree(v1);
00447       if (op == TOK_MULTIPLY)
00448         v1 = valueMakeInteger(i1 * i2);
00449       else
00450         v1 = valueMakeInteger(i1 / i2);
00451     } else {
00452       rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
00453       return NULL;
00454     }
00455   }
00456   /*@=branchstate@*/
00457 
00458   if (v2) valueFree(v2);
00459   return v1;
00460 }
00461 
00465 static Value doAddSubtract(ParseState state)
00466         /*@globals rpmGlobalMacroContext @*/
00467         /*@modifies state->nextToken, state->p, state->tokenValue,
00468                 rpmGlobalMacroContext @*/
00469 {
00470   Value v1, v2 = NULL;
00471 
00472   DEBUG(printf("doAddSubtract()\n"));
00473 
00474   v1 = doMultiplyDivide(state);
00475   if (v1 == NULL)
00476     return NULL;
00477 
00478   /*@-branchstate@*/
00479   while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
00480     int op = state->nextToken;
00481 
00482     if (rdToken(state, 1))
00483       return NULL;
00484 
00485     if (v2) valueFree(v2);
00486 
00487     v2 = doMultiplyDivide(state);
00488     if (v2 == NULL)
00489       return NULL;
00490 
00491     if (! valueSameType(v1, v2)) {
00492       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00493       return NULL;
00494     }
00495 
00496     if (valueIsInteger(v1)) {
00497       int i1 = v1->data.i, i2 = v2->data.i;
00498 
00499       valueFree(v1);
00500       if (op == TOK_ADD)
00501         v1 = valueMakeInteger(i1 + i2);
00502       else
00503         v1 = valueMakeInteger(i1 - i2);
00504     } else {
00505       char *copy;
00506 
00507       if (op == TOK_MINUS) {
00508         rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
00509         return NULL;
00510       }
00511 
00512       copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
00513       (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
00514 
00515       valueFree(v1);
00516       v1 = valueMakeString(copy);
00517     }
00518   }
00519   /*@=branchstate@*/
00520 
00521   if (v2) valueFree(v2);
00522   return v1;
00523 }
00524 
00528 static Value doRelational(ParseState state)
00529         /*@globals rpmGlobalMacroContext @*/
00530         /*@modifies state->nextToken, state->p, state->tokenValue,
00531                 rpmGlobalMacroContext @*/
00532 {
00533   Value v1, v2 = NULL;
00534 
00535   DEBUG(printf("doRelational()\n"));
00536 
00537   v1 = doAddSubtract(state);
00538   if (v1 == NULL)
00539     return NULL;
00540 
00541   /*@-branchstate@*/
00542   while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
00543     int op = state->nextToken;
00544 
00545     if (rdToken(state, 1))
00546       return NULL;
00547 
00548     if (v2) valueFree(v2);
00549 
00550     v2 = doAddSubtract(state);
00551     if (v2 == NULL)
00552       return NULL;
00553 
00554     if (! valueSameType(v1, v2)) {
00555       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00556       return NULL;
00557     }
00558 
00559     if (valueIsInteger(v1)) {
00560       int i1 = v1->data.i, i2 = v2->data.i, r = 0;
00561       switch (op) {
00562       case TOK_EQ:
00563         r = (i1 == i2);
00564         /*@switchbreak@*/ break;
00565       case TOK_NEQ:
00566         r = (i1 != i2);
00567         /*@switchbreak@*/ break;
00568       case TOK_LT:
00569         r = (i1 < i2);
00570         /*@switchbreak@*/ break;
00571       case TOK_LE:
00572         r = (i1 <= i2);
00573         /*@switchbreak@*/ break;
00574       case TOK_GT:
00575         r = (i1 > i2);
00576         /*@switchbreak@*/ break;
00577       case TOK_GE:
00578         r = (i1 >= i2);
00579         /*@switchbreak@*/ break;
00580       default:
00581         /*@switchbreak@*/ break;
00582       }
00583       valueFree(v1);
00584       v1 = valueMakeInteger(r);
00585     } else {
00586       const char * s1 = v1->data.s;
00587       const char * s2 = v2->data.s;
00588       int r = 0;
00589       switch (op) {
00590       case TOK_EQ:
00591         r = (strcmp(s1,s2) == 0);
00592         /*@switchbreak@*/ break;
00593       case TOK_NEQ:
00594         r = (strcmp(s1,s2) != 0);
00595         /*@switchbreak@*/ break;
00596       case TOK_LT:
00597         r = (strcmp(s1,s2) < 0);
00598         /*@switchbreak@*/ break;
00599       case TOK_LE:
00600         r = (strcmp(s1,s2) <= 0);
00601         /*@switchbreak@*/ break;
00602       case TOK_GT:
00603         r = (strcmp(s1,s2) > 0);
00604         /*@switchbreak@*/ break;
00605       case TOK_GE:
00606         r = (strcmp(s1,s2) >= 0);
00607         /*@switchbreak@*/ break;
00608       default:
00609         /*@switchbreak@*/ break;
00610       }
00611       valueFree(v1);
00612       v1 = valueMakeInteger(r);
00613     }
00614   }
00615   /*@=branchstate@*/
00616 
00617   if (v2) valueFree(v2);
00618   return v1;
00619 }
00620 
00624 static Value doLogical(ParseState state)
00625         /*@globals rpmGlobalMacroContext @*/
00626         /*@modifies state->nextToken, state->p, state->tokenValue,
00627                 rpmGlobalMacroContext @*/
00628 {
00629   Value v1, v2 = NULL;
00630 
00631   DEBUG(printf("doLogical()\n"));
00632 
00633   v1 = doRelational(state);
00634   if (v1 == NULL)
00635     return NULL;
00636 
00637   /*@-branchstate@*/
00638   while (state->nextToken == TOK_LOGICAL_AND
00639          || state->nextToken == TOK_LOGICAL_OR) {
00640     int op = state->nextToken;
00641 
00642     if (rdToken(state, 1))
00643       return NULL;
00644 
00645     if (v2) valueFree(v2);
00646 
00647     v2 = doRelational(state);
00648     if (v2 == NULL)
00649       return NULL;
00650 
00651     if (! valueSameType(v1, v2)) {
00652       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00653       return NULL;
00654     }
00655 
00656     if (valueIsInteger(v1)) {
00657       int i1 = v1->data.i, i2 = v2->data.i;
00658 
00659       valueFree(v1);
00660       if (op == TOK_LOGICAL_AND)
00661         v1 = valueMakeInteger(i1 && i2);
00662       else
00663         v1 = valueMakeInteger(i1 || i2);
00664     } else {
00665       rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
00666       return NULL;
00667     }
00668   }
00669   /*@=branchstate@*/
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, spec->readStack->reading);
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     rpmError(RPMERR_BADSPEC, _("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, 1);
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     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00747     state.str = _free(state.str);
00748     return NULL;
00749   }
00750 
00751   DEBUG(valueDump("parseExprString:", v, stdout));
00752 
00753   /*@-branchstate@*/
00754   switch (v->type) {
00755   case VALUE_TYPE_INTEGER: {
00756     char buf[128];
00757     sprintf(buf, "%d", v->data.i);
00758     result = xstrdup(buf);
00759   } break;
00760   case VALUE_TYPE_STRING:
00761     result = xstrdup(v->data.s);
00762     break;
00763   default:
00764     break;
00765   }
00766   /*@=branchstate@*/
00767 
00768   state.str = _free(state.str);
00769   valueFree(v);
00770   return result;
00771 }

Generated on Sun Feb 2 23:31:58 2003 for rpm by doxygen1.2.18