rpm 5.2.1

build/expression.c

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