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     break;
00356 
00357   case TOK_INTEGER:
00358   case TOK_STRING:
00359     v = state->tokenValue;
00360     if (rdToken(state))
00361       return NULL;
00362     break;
00363 
00364   case TOK_IDENTIFIER: {
00365     const char *name = state->tokenValue->data.s;
00366 
00367     v = valueMakeString( rpmExpand(name, NULL) );
00368     if (rdToken(state))
00369       return NULL;
00370     break;
00371   }
00372 
00373   case TOK_MINUS:
00374     if (rdToken(state))
00375       return NULL;
00376 
00377     v = doPrimary(state);
00378     if (v == NULL)
00379       return NULL;
00380 
00381     if (! valueIsInteger(v)) {
00382       rpmError(RPMERR_BADSPEC, _("- only on numbers\n"));
00383       return NULL;
00384     }
00385 
00386     v = valueMakeInteger(- v->data.i);
00387     break;
00388 
00389   case TOK_NOT:
00390     if (rdToken(state))
00391       return NULL;
00392 
00393     v = doPrimary(state);
00394     if (v == NULL)
00395       return NULL;
00396 
00397     if (! valueIsInteger(v)) {
00398       rpmError(RPMERR_BADSPEC, _("! only on numbers\n"));
00399       return NULL;
00400     }
00401 
00402     v = valueMakeInteger(! v->data.i);
00403     break;
00404   default:
00405     return NULL;
00406     /*@notreached@*/ break;
00407   }
00408   /*@=branchstate@*/
00409 
00410   DEBUG(valueDump("doPrimary:", v, stdout));
00411   return v;
00412 }
00413 
00417 /*@null@*/
00418 static Value doMultiplyDivide(ParseState state)
00419         /*@globals rpmGlobalMacroContext, h_errno @*/
00420         /*@modifies state->nextToken, state->p, state->tokenValue,
00421                 rpmGlobalMacroContext @*/
00422 {
00423   Value v1, v2 = NULL;
00424 
00425   DEBUG(printf("doMultiplyDivide()\n"));
00426 
00427   v1 = doPrimary(state);
00428   if (v1 == NULL)
00429     return NULL;
00430 
00431   /*@-branchstate@*/
00432   while (state->nextToken == TOK_MULTIPLY
00433          || state->nextToken == TOK_DIVIDE) {
00434     int op = state->nextToken;
00435 
00436     if (rdToken(state))
00437       return NULL;
00438 
00439     if (v2) valueFree(v2);
00440 
00441     v2 = doPrimary(state);
00442     if (v2 == NULL)
00443       return NULL;
00444 
00445     if (! valueSameType(v1, v2)) {
00446       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00447       return NULL;
00448     }
00449 
00450     if (valueIsInteger(v1)) {
00451       int i1 = v1->data.i, i2 = v2->data.i;
00452 
00453       valueFree(v1);
00454       if (op == TOK_MULTIPLY)
00455         v1 = valueMakeInteger(i1 * i2);
00456       else
00457         v1 = valueMakeInteger(i1 / i2);
00458     } else {
00459       rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
00460       return NULL;
00461     }
00462   }
00463   /*@=branchstate@*/
00464 
00465   if (v2) valueFree(v2);
00466   return v1;
00467 }
00468 
00472 /*@-boundswrite@*/
00473 /*@null@*/
00474 static Value doAddSubtract(ParseState state)
00475         /*@globals rpmGlobalMacroContext, h_errno @*/
00476         /*@modifies state->nextToken, state->p, state->tokenValue,
00477                 rpmGlobalMacroContext @*/
00478 {
00479   Value v1, v2 = NULL;
00480 
00481   DEBUG(printf("doAddSubtract()\n"));
00482 
00483   v1 = doMultiplyDivide(state);
00484   if (v1 == NULL)
00485     return NULL;
00486 
00487   /*@-branchstate@*/
00488   while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
00489     int op = state->nextToken;
00490 
00491     if (rdToken(state))
00492       return NULL;
00493 
00494     if (v2) valueFree(v2);
00495 
00496     v2 = doMultiplyDivide(state);
00497     if (v2 == NULL)
00498       return NULL;
00499 
00500     if (! valueSameType(v1, v2)) {
00501       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00502       return NULL;
00503     }
00504 
00505     if (valueIsInteger(v1)) {
00506       int i1 = v1->data.i, i2 = v2->data.i;
00507 
00508       valueFree(v1);
00509       if (op == TOK_ADD)
00510         v1 = valueMakeInteger(i1 + i2);
00511       else
00512         v1 = valueMakeInteger(i1 - i2);
00513     } else {
00514       char *copy;
00515 
00516       if (op == TOK_MINUS) {
00517         rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
00518         return NULL;
00519       }
00520 
00521       copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
00522       (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
00523 
00524       valueFree(v1);
00525       v1 = valueMakeString(copy);
00526     }
00527   }
00528   /*@=branchstate@*/
00529 
00530   if (v2) valueFree(v2);
00531   return v1;
00532 }
00533 /*@=boundswrite@*/
00534 
00538 /*@null@*/
00539 static Value doRelational(ParseState state)
00540         /*@globals rpmGlobalMacroContext, h_errno @*/
00541         /*@modifies state->nextToken, state->p, state->tokenValue,
00542                 rpmGlobalMacroContext @*/
00543 {
00544   Value v1, v2 = NULL;
00545 
00546   DEBUG(printf("doRelational()\n"));
00547 
00548   v1 = doAddSubtract(state);
00549   if (v1 == NULL)
00550     return NULL;
00551 
00552   /*@-branchstate@*/
00553   while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
00554     int op = state->nextToken;
00555 
00556     if (rdToken(state))
00557       return NULL;
00558 
00559     if (v2) valueFree(v2);
00560 
00561     v2 = doAddSubtract(state);
00562     if (v2 == NULL)
00563       return NULL;
00564 
00565     if (! valueSameType(v1, v2)) {
00566       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00567       return NULL;
00568     }
00569 
00570     if (valueIsInteger(v1)) {
00571       int i1 = v1->data.i, i2 = v2->data.i, r = 0;
00572       switch (op) {
00573       case TOK_EQ:
00574         r = (i1 == i2);
00575         /*@switchbreak@*/ break;
00576       case TOK_NEQ:
00577         r = (i1 != i2);
00578         /*@switchbreak@*/ break;
00579       case TOK_LT:
00580         r = (i1 < i2);
00581         /*@switchbreak@*/ break;
00582       case TOK_LE:
00583         r = (i1 <= i2);
00584         /*@switchbreak@*/ break;
00585       case TOK_GT:
00586         r = (i1 > i2);
00587         /*@switchbreak@*/ break;
00588       case TOK_GE:
00589         r = (i1 >= i2);
00590         /*@switchbreak@*/ break;
00591       default:
00592         /*@switchbreak@*/ break;
00593       }
00594       valueFree(v1);
00595       v1 = valueMakeInteger(r);
00596     } else {
00597       const char * s1 = v1->data.s;
00598       const char * s2 = v2->data.s;
00599       int r = 0;
00600       switch (op) {
00601       case TOK_EQ:
00602         r = (strcmp(s1,s2) == 0);
00603         /*@switchbreak@*/ break;
00604       case TOK_NEQ:
00605         r = (strcmp(s1,s2) != 0);
00606         /*@switchbreak@*/ break;
00607       case TOK_LT:
00608         r = (strcmp(s1,s2) < 0);
00609         /*@switchbreak@*/ break;
00610       case TOK_LE:
00611         r = (strcmp(s1,s2) <= 0);
00612         /*@switchbreak@*/ break;
00613       case TOK_GT:
00614         r = (strcmp(s1,s2) > 0);
00615         /*@switchbreak@*/ break;
00616       case TOK_GE:
00617         r = (strcmp(s1,s2) >= 0);
00618         /*@switchbreak@*/ break;
00619       default:
00620         /*@switchbreak@*/ break;
00621       }
00622       valueFree(v1);
00623       v1 = valueMakeInteger(r);
00624     }
00625   }
00626   /*@=branchstate@*/
00627 
00628   if (v2) valueFree(v2);
00629   return v1;
00630 }
00631 
00635 static Value doLogical(ParseState state)
00636         /*@globals rpmGlobalMacroContext, h_errno @*/
00637         /*@modifies state->nextToken, state->p, state->tokenValue,
00638                 rpmGlobalMacroContext @*/
00639 {
00640   Value v1, v2 = NULL;
00641 
00642   DEBUG(printf("doLogical()\n"));
00643 
00644   v1 = doRelational(state);
00645   if (v1 == NULL)
00646     return NULL;
00647 
00648   /*@-branchstate@*/
00649   while (state->nextToken == TOK_LOGICAL_AND
00650          || state->nextToken == TOK_LOGICAL_OR) {
00651     int op = state->nextToken;
00652 
00653     if (rdToken(state))
00654       return NULL;
00655 
00656     if (v2) valueFree(v2);
00657 
00658     v2 = doRelational(state);
00659     if (v2 == NULL)
00660       return NULL;
00661 
00662     if (! valueSameType(v1, v2)) {
00663       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00664       return NULL;
00665     }
00666 
00667     if (valueIsInteger(v1)) {
00668       int i1 = v1->data.i, i2 = v2->data.i;
00669 
00670       valueFree(v1);
00671       if (op == TOK_LOGICAL_AND)
00672         v1 = valueMakeInteger(i1 && i2);
00673       else
00674         v1 = valueMakeInteger(i1 || i2);
00675     } else {
00676       rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
00677       return NULL;
00678     }
00679   }
00680   /*@=branchstate@*/
00681 
00682   if (v2) valueFree(v2);
00683   return v1;
00684 }
00685 
00686 int parseExpressionBoolean(Spec spec, const char *expr)
00687 {
00688   struct _parseState state;
00689   int result = -1;
00690   Value v;
00691 
00692   DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
00693 
00694   /* Initialize the expression parser state. */
00695   state.p = state.str = xstrdup(expr);
00696   state.spec = spec;
00697   state.nextToken = 0;
00698   state.tokenValue = NULL;
00699   (void) rdToken(&state);
00700 
00701   /* Parse the expression. */
00702   v = doLogical(&state);
00703   if (!v) {
00704     state.str = _free(state.str);
00705     return -1;
00706   }
00707 
00708   /* If the next token is not TOK_EOF, we have a syntax error. */
00709   if (state.nextToken != TOK_EOF) {
00710     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00711     state.str = _free(state.str);
00712     return -1;
00713   }
00714 
00715   DEBUG(valueDump("parseExprBoolean:", v, stdout));
00716 
00717   switch (v->type) {
00718   case VALUE_TYPE_INTEGER:
00719     result = v->data.i != 0;
00720     break;
00721   case VALUE_TYPE_STRING:
00722 /*@-boundsread@*/
00723     result = v->data.s[0] != '\0';
00724 /*@=boundsread@*/
00725     break;
00726   default:
00727     break;
00728   }
00729 
00730   state.str = _free(state.str);
00731   valueFree(v);
00732   return result;
00733 }
00734 
00735 char * parseExpressionString(Spec spec, const char *expr)
00736 {
00737   struct _parseState state;
00738   char *result = NULL;
00739   Value v;
00740 
00741   DEBUG(printf("parseExprString(?, '%s')\n", expr));
00742 
00743   /* Initialize the expression parser state. */
00744   state.p = state.str = xstrdup(expr);
00745   state.spec = spec;
00746   state.nextToken = 0;
00747   state.tokenValue = NULL;
00748   (void) rdToken(&state);
00749 
00750   /* Parse the expression. */
00751   v = doLogical(&state);
00752   if (!v) {
00753     state.str = _free(state.str);
00754     return NULL;
00755   }
00756 
00757   /* If the next token is not TOK_EOF, we have a syntax error. */
00758   if (state.nextToken != TOK_EOF) {
00759     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00760     state.str = _free(state.str);
00761     return NULL;
00762   }
00763 
00764   DEBUG(valueDump("parseExprString:", v, stdout));
00765 
00766   /*@-branchstate@*/
00767   switch (v->type) {
00768   case VALUE_TYPE_INTEGER: {
00769     char buf[128];
00770     sprintf(buf, "%d", v->data.i);
00771     result = xstrdup(buf);
00772   } break;
00773   case VALUE_TYPE_STRING:
00774     result = xstrdup(v->data.s);
00775     break;
00776   default:
00777     break;
00778   }
00779   /*@=branchstate@*/
00780 
00781   state.str = _free(state.str);
00782   valueFree(v);
00783   return result;
00784 }

Generated on Wed Jan 28 12:45:23 2009 for rpm by  doxygen 1.4.7