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@*/
00105     char *str;          
00106 /*@dependent@*/
00107     char *p;            
00108     int nextToken;      
00109 /*@relnull@*/
00110     Value tokenValue;   
00111     Spec spec;          
00112 } *ParseState;
00113 
00114 
00119 #define TOK_EOF          1
00120 #define TOK_INTEGER      2
00121 #define TOK_STRING       3
00122 #define TOK_IDENTIFIER   4
00123 #define TOK_ADD          5
00124 #define TOK_MINUS        6
00125 #define TOK_MULTIPLY     7
00126 #define TOK_DIVIDE       8
00127 #define TOK_OPEN_P       9
00128 #define TOK_CLOSE_P     10
00129 #define TOK_EQ          11
00130 #define TOK_NEQ         12
00131 #define TOK_LT          13
00132 #define TOK_LE          14
00133 #define TOK_GT          15
00134 #define TOK_GE          16
00135 #define TOK_NOT         17
00136 #define TOK_LOGICAL_AND 18
00137 #define TOK_LOGICAL_OR  19
00138 
00140 #define EXPRBUFSIZ      BUFSIZ
00141 
00142 #if defined(DEBUG_PARSER)
00143 typedef struct exprTokTableEntry {
00144     const char *name;
00145     int val;
00146 } ETTE_t;
00147 
00148 ETTE_t exprTokTable[] = {
00149     { "EOF",    TOK_EOF },
00150     { "I",      TOK_INTEGER },
00151     { "S",      TOK_STRING },
00152     { "ID",     TOK_IDENTIFIER },
00153     { "+",      TOK_ADD },
00154     { "-",      TOK_MINUS },
00155     { "*",      TOK_MULTIPLY },
00156     { "/",      TOK_DIVIDE },
00157     { "( ",     TOK_OPEN_P },
00158     { " )",     TOK_CLOSE_P },
00159     { "==",     TOK_EQ },
00160     { "!=",     TOK_NEQ },
00161     { "<",      TOK_LT },
00162     { "<=",     TOK_LE },
00163     { ">",      TOK_GT },
00164     { ">=",     TOK_GE },
00165     { "!",      TOK_NOT },
00166     { "&&",     TOK_LOGICAL_AND },
00167     { "||",     TOK_LOGICAL_OR },
00168     { NULL, 0 }
00169 };
00170 
00171 static const char *prToken(int val)
00172         /*@*/
00173 {
00174     ETTE_t *et;
00175     
00176     for (et = exprTokTable; et->name != NULL; et++) {
00177         if (val == et->val)
00178             return et->name;
00179     }
00180     return "???";
00181 }
00182 #endif  /* DEBUG_PARSER */
00183 
00187 /*@-boundswrite@*/
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       rpmError(RPMERR_BADSPEC, _("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       rpmError(RPMERR_BADSPEC, _("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       rpmError(RPMERR_BADSPEC, _("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       rpmError(RPMERR_BADSPEC, _("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 /*@=boundswrite@*/
00325 
00326 /*@null@*/
00327 static Value doLogical(ParseState state)
00328         /*@globals rpmGlobalMacroContext, h_errno @*/
00329         /*@modifies state->nextToken, state->p, state->tokenValue,
00330                 rpmGlobalMacroContext @*/;
00331 
00335 /*@null@*/
00336 static Value doPrimary(ParseState state)
00337         /*@globals rpmGlobalMacroContext, h_errno @*/
00338         /*@modifies state->nextToken, state->p, state->tokenValue,
00339                 rpmGlobalMacroContext @*/
00340 {
00341   Value v;
00342 
00343   DEBUG(printf("doPrimary()\n"));
00344 
00345   /*@-branchstate@*/
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       rpmError(RPMERR_BADSPEC, _("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       rpmError(RPMERR_BADSPEC, _("- 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       rpmError(RPMERR_BADSPEC, _("! 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   /*@=branchstate@*/
00411 
00412   DEBUG(valueDump("doPrimary:", v, stdout));
00413   return v;
00414 }
00415 
00419 /*@null@*/
00420 static Value doMultiplyDivide(ParseState state)
00421         /*@globals rpmGlobalMacroContext, h_errno @*/
00422         /*@modifies state->nextToken, state->p, state->tokenValue,
00423                 rpmGlobalMacroContext @*/
00424 {
00425   Value v1, v2 = NULL;
00426 
00427   DEBUG(printf("doMultiplyDivide()\n"));
00428 
00429   v1 = doPrimary(state);
00430   if (v1 == NULL)
00431     return NULL;
00432 
00433   /*@-branchstate@*/
00434   while (state->nextToken == TOK_MULTIPLY
00435          || state->nextToken == TOK_DIVIDE) {
00436     int op = state->nextToken;
00437 
00438     if (rdToken(state))
00439       return NULL;
00440 
00441     if (v2) valueFree(v2);
00442 
00443     v2 = doPrimary(state);
00444     if (v2 == NULL)
00445       return NULL;
00446 
00447     if (! valueSameType(v1, v2)) {
00448       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00449       return NULL;
00450     }
00451 
00452     if (valueIsInteger(v1)) {
00453       int i1 = v1->data.i, i2 = v2->data.i;
00454 
00455       valueFree(v1);
00456       if (op == TOK_MULTIPLY)
00457         v1 = valueMakeInteger(i1 * i2);
00458       else
00459         v1 = valueMakeInteger(i1 / i2);
00460     } else {
00461       rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
00462       return NULL;
00463     }
00464   }
00465   /*@=branchstate@*/
00466 
00467   if (v2) valueFree(v2);
00468   return v1;
00469 }
00470 
00474 /*@-boundswrite@*/
00475 /*@null@*/
00476 static Value doAddSubtract(ParseState state)
00477         /*@globals rpmGlobalMacroContext, h_errno @*/
00478         /*@modifies state->nextToken, state->p, state->tokenValue,
00479                 rpmGlobalMacroContext @*/
00480 {
00481   Value v1, v2 = NULL;
00482 
00483   DEBUG(printf("doAddSubtract()\n"));
00484 
00485   v1 = doMultiplyDivide(state);
00486   if (v1 == NULL)
00487     return NULL;
00488 
00489   /*@-branchstate@*/
00490   while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
00491     int op = state->nextToken;
00492 
00493     if (rdToken(state))
00494       return NULL;
00495 
00496     if (v2) valueFree(v2);
00497 
00498     v2 = doMultiplyDivide(state);
00499     if (v2 == NULL)
00500       return NULL;
00501 
00502     if (! valueSameType(v1, v2)) {
00503       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00504       return NULL;
00505     }
00506 
00507     if (valueIsInteger(v1)) {
00508       int i1 = v1->data.i, i2 = v2->data.i;
00509 
00510       valueFree(v1);
00511       if (op == TOK_ADD)
00512         v1 = valueMakeInteger(i1 + i2);
00513       else
00514         v1 = valueMakeInteger(i1 - i2);
00515     } else {
00516       char *copy;
00517 
00518       if (op == TOK_MINUS) {
00519         rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
00520         return NULL;
00521       }
00522 
00523       copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
00524       (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
00525 
00526       valueFree(v1);
00527       v1 = valueMakeString(copy);
00528     }
00529   }
00530   /*@=branchstate@*/
00531 
00532   if (v2) valueFree(v2);
00533   return v1;
00534 }
00535 /*@=boundswrite@*/
00536 
00540 /*@null@*/
00541 static Value doRelational(ParseState state)
00542         /*@globals rpmGlobalMacroContext, h_errno @*/
00543         /*@modifies state->nextToken, state->p, state->tokenValue,
00544                 rpmGlobalMacroContext @*/
00545 {
00546   Value v1, v2 = NULL;
00547 
00548   DEBUG(printf("doRelational()\n"));
00549 
00550   v1 = doAddSubtract(state);
00551   if (v1 == NULL)
00552     return NULL;
00553 
00554   /*@-branchstate@*/
00555   while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
00556     int op = state->nextToken;
00557 
00558     if (rdToken(state))
00559       return NULL;
00560 
00561     if (v2) valueFree(v2);
00562 
00563     v2 = doAddSubtract(state);
00564     if (v2 == NULL)
00565       return NULL;
00566 
00567     if (! valueSameType(v1, v2)) {
00568       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00569       return NULL;
00570     }
00571 
00572     if (valueIsInteger(v1)) {
00573       int i1 = v1->data.i, i2 = v2->data.i, r = 0;
00574       switch (op) {
00575       case TOK_EQ:
00576         r = (i1 == i2);
00577         /*@switchbreak@*/ break;
00578       case TOK_NEQ:
00579         r = (i1 != i2);
00580         /*@switchbreak@*/ break;
00581       case TOK_LT:
00582         r = (i1 < i2);
00583         /*@switchbreak@*/ break;
00584       case TOK_LE:
00585         r = (i1 <= i2);
00586         /*@switchbreak@*/ break;
00587       case TOK_GT:
00588         r = (i1 > i2);
00589         /*@switchbreak@*/ break;
00590       case TOK_GE:
00591         r = (i1 >= i2);
00592         /*@switchbreak@*/ break;
00593       default:
00594         /*@switchbreak@*/ break;
00595       }
00596       valueFree(v1);
00597       v1 = valueMakeInteger(r);
00598     } else {
00599       const char * s1 = v1->data.s;
00600       const char * s2 = v2->data.s;
00601       int r = 0;
00602       switch (op) {
00603       case TOK_EQ:
00604         r = (strcmp(s1,s2) == 0);
00605         /*@switchbreak@*/ break;
00606       case TOK_NEQ:
00607         r = (strcmp(s1,s2) != 0);
00608         /*@switchbreak@*/ break;
00609       case TOK_LT:
00610         r = (strcmp(s1,s2) < 0);
00611         /*@switchbreak@*/ break;
00612       case TOK_LE:
00613         r = (strcmp(s1,s2) <= 0);
00614         /*@switchbreak@*/ break;
00615       case TOK_GT:
00616         r = (strcmp(s1,s2) > 0);
00617         /*@switchbreak@*/ break;
00618       case TOK_GE:
00619         r = (strcmp(s1,s2) >= 0);
00620         /*@switchbreak@*/ break;
00621       default:
00622         /*@switchbreak@*/ break;
00623       }
00624       valueFree(v1);
00625       v1 = valueMakeInteger(r);
00626     }
00627   }
00628   /*@=branchstate@*/
00629 
00630   if (v2) valueFree(v2);
00631   return v1;
00632 }
00633 
00637 static Value doLogical(ParseState state)
00638         /*@globals rpmGlobalMacroContext, h_errno @*/
00639         /*@modifies state->nextToken, state->p, state->tokenValue,
00640                 rpmGlobalMacroContext @*/
00641 {
00642   Value v1, v2 = NULL;
00643 
00644   DEBUG(printf("doLogical()\n"));
00645 
00646   v1 = doRelational(state);
00647   if (v1 == NULL)
00648     return NULL;
00649 
00650   /*@-branchstate@*/
00651   while (state->nextToken == TOK_LOGICAL_AND
00652          || state->nextToken == TOK_LOGICAL_OR) {
00653     int op = state->nextToken;
00654 
00655     if (rdToken(state))
00656       return NULL;
00657 
00658     if (v2) valueFree(v2);
00659 
00660     v2 = doRelational(state);
00661     if (v2 == NULL)
00662       return NULL;
00663 
00664     if (! valueSameType(v1, v2)) {
00665       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00666       return NULL;
00667     }
00668 
00669     if (valueIsInteger(v1)) {
00670       int i1 = v1->data.i, i2 = v2->data.i;
00671 
00672       valueFree(v1);
00673       if (op == TOK_LOGICAL_AND)
00674         v1 = valueMakeInteger(i1 && i2);
00675       else
00676         v1 = valueMakeInteger(i1 || i2);
00677     } else {
00678       rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
00679       return NULL;
00680     }
00681   }
00682   /*@=branchstate@*/
00683 
00684   if (v2) valueFree(v2);
00685   return v1;
00686 }
00687 
00688 int parseExpressionBoolean(Spec spec, const char *expr)
00689 {
00690   struct _parseState state;
00691   int result = -1;
00692   Value v;
00693 
00694   DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
00695 
00696   /* Initialize the expression parser state. */
00697   state.p = state.str = xstrdup(expr);
00698   state.spec = spec;
00699   state.nextToken = 0;
00700   state.tokenValue = NULL;
00701   (void) rdToken(&state);
00702 
00703   /* Parse the expression. */
00704   v = doLogical(&state);
00705   if (!v) {
00706     state.str = _free(state.str);
00707     return -1;
00708   }
00709 
00710   /* If the next token is not TOK_EOF, we have a syntax error. */
00711   if (state.nextToken != TOK_EOF) {
00712     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00713     state.str = _free(state.str);
00714     return -1;
00715   }
00716 
00717   DEBUG(valueDump("parseExprBoolean:", v, stdout));
00718 
00719   switch (v->type) {
00720   case VALUE_TYPE_INTEGER:
00721     result = v->data.i != 0;
00722     break;
00723   case VALUE_TYPE_STRING:
00724 /*@-boundsread@*/
00725     result = v->data.s[0] != '\0';
00726 /*@=boundsread@*/
00727     break;
00728   default:
00729     break;
00730   }
00731 
00732   state.str = _free(state.str);
00733   valueFree(v);
00734   return result;
00735 }
00736 
00737 char * parseExpressionString(Spec spec, const char *expr)
00738 {
00739   struct _parseState state;
00740   char *result = NULL;
00741   Value v;
00742 
00743   DEBUG(printf("parseExprString(?, '%s')\n", expr));
00744 
00745   /* Initialize the expression parser state. */
00746   state.p = state.str = xstrdup(expr);
00747   state.spec = spec;
00748   state.nextToken = 0;
00749   state.tokenValue = NULL;
00750   (void) rdToken(&state);
00751 
00752   /* Parse the expression. */
00753   v = doLogical(&state);
00754   if (!v) {
00755     state.str = _free(state.str);
00756     return NULL;
00757   }
00758 
00759   /* If the next token is not TOK_EOF, we have a syntax error. */
00760   if (state.nextToken != TOK_EOF) {
00761     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00762     state.str = _free(state.str);
00763     return NULL;
00764   }
00765 
00766   DEBUG(valueDump("parseExprString:", v, stdout));
00767 
00768   /*@-branchstate@*/
00769   switch (v->type) {
00770   case VALUE_TYPE_INTEGER: {
00771     char buf[128];
00772     sprintf(buf, "%d", v->data.i);
00773     result = xstrdup(buf);
00774   } break;
00775   case VALUE_TYPE_STRING:
00776     result = xstrdup(v->data.s);
00777     break;
00778   default:
00779     break;
00780   }
00781   /*@=branchstate@*/
00782 
00783   state.str = _free(state.str);
00784   valueFree(v);
00785   return result;
00786 }

Generated on Fri Oct 12 08:44:53 2007 for rpm by  doxygen 1.5.2