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

Generated at Fri Feb 15 10:30:34 2002 for rpm by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001