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 /*@-boundswrite@*/
00185 static int rdToken(ParseState state)
00186         /*@globals rpmGlobalMacroContext @*/
00187         /*@modifies state->nextToken, state->p, state->tokenValue,
00188                 rpmGlobalMacroContext @*/
00189 {
00190   int token;
00191   Value v = NULL;
00192   char *p = state->p;
00193 
00194   /* Skip whitespace before the next token. */
00195   while (*p && xisspace(*p)) p++;
00196 
00197   switch (*p) {
00198   case '\0':
00199     token = TOK_EOF;
00200     p--;
00201     break;
00202   case '+':
00203     token = TOK_ADD;
00204     break;
00205   case '-':
00206     token = TOK_MINUS;
00207     break;
00208   case '*':
00209     token = TOK_MULTIPLY;
00210     break;
00211   case '/':
00212     token = TOK_DIVIDE;
00213     break;
00214   case '(':
00215     token = TOK_OPEN_P;
00216     break;
00217   case ')':
00218     token = TOK_CLOSE_P;
00219     break;
00220   case '=':
00221     if (p[1] == '=') {
00222       token = TOK_EQ;
00223       p++;
00224     } else {
00225       rpmError(RPMERR_BADSPEC, _("syntax error while parsing ==\n"));
00226       return -1;
00227     }
00228     break;
00229   case '!':
00230     if (p[1] == '=') {
00231       token = TOK_NEQ;
00232       p++;
00233     } else
00234       token = TOK_NOT;
00235     break;
00236   case '<':
00237     if (p[1] == '=') {
00238       token = TOK_LE;
00239       p++;
00240     } else
00241       token = TOK_LT;
00242     break;
00243   case '>':
00244     if (p[1] == '=') {
00245       token = TOK_GE;
00246       p++;
00247     } else
00248       token = TOK_GT;
00249     break;
00250   case '&':
00251     if (p[1] == '&') {
00252       token = TOK_LOGICAL_AND;
00253       p++;
00254     } else {
00255       rpmError(RPMERR_BADSPEC, _("syntax error while parsing &&\n"));
00256       return -1;
00257     }
00258     break;
00259   case '|':
00260     if (p[1] == '|') {
00261       token = TOK_LOGICAL_OR;
00262       p++;
00263     } else {
00264       rpmError(RPMERR_BADSPEC, _("syntax error while parsing ||\n"));
00265       return -1;
00266     }
00267     break;
00268 
00269   default:
00270     if (xisdigit(*p)) {
00271       char temp[EXPRBUFSIZ], *t = temp;
00272 
00273       temp[0] = '\0';
00274       while (*p && xisdigit(*p))
00275         *t++ = *p++;
00276       *t++ = '\0';
00277       p--;
00278 
00279       token = TOK_INTEGER;
00280       v = valueMakeInteger(atoi(temp));
00281 
00282     } else if (xisalpha(*p)) {
00283       char temp[EXPRBUFSIZ], *t = temp;
00284 
00285       temp[0] = '\0';
00286       while (*p && (xisalnum(*p) || *p == '_'))
00287         *t++ = *p++;
00288       *t++ = '\0';
00289       p--;
00290 
00291       token = TOK_IDENTIFIER;
00292       v = valueMakeString( xstrdup(temp) );
00293 
00294     } else if (*p == '\"') {
00295       char temp[EXPRBUFSIZ], *t = temp;
00296 
00297       temp[0] = '\0';
00298       p++;
00299       while (*p && *p != '\"')
00300         *t++ = *p++;
00301       *t++ = '\0';
00302 
00303       token = TOK_STRING;
00304       v = valueMakeString( rpmExpand(temp, NULL) );
00305 
00306     } else {
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 /*@=boundswrite@*/
00322 
00323 static Value doLogical(ParseState state)
00324         /*@globals rpmGlobalMacroContext @*/
00325         /*@modifies state->nextToken, state->p, state->tokenValue,
00326                 rpmGlobalMacroContext @*/;
00327 
00331 static Value doPrimary(ParseState state)
00332         /*@globals rpmGlobalMacroContext @*/
00333         /*@modifies state->nextToken, state->p, state->tokenValue,
00334                 rpmGlobalMacroContext @*/
00335 {
00336   Value v;
00337 
00338   DEBUG(printf("doPrimary()\n"));
00339 
00340   /*@-branchstate@*/
00341   switch (state->nextToken) {
00342   case TOK_OPEN_P:
00343     if (rdToken(state))
00344       return NULL;
00345     v = doLogical(state);
00346     if (state->nextToken != TOK_CLOSE_P) {
00347       rpmError(RPMERR_BADSPEC, _("unmatched (\n"));
00348       return NULL;
00349     }
00350     break;
00351 
00352   case TOK_INTEGER:
00353   case TOK_STRING:
00354     v = state->tokenValue;
00355     if (rdToken(state))
00356       return NULL;
00357     break;
00358 
00359   case TOK_IDENTIFIER: {
00360     const char *name = state->tokenValue->data.s;
00361 
00362     v = valueMakeString( rpmExpand(name, NULL) );
00363     if (rdToken(state))
00364       return NULL;
00365     break;
00366   }
00367 
00368   case TOK_MINUS:
00369     if (rdToken(state))
00370       return NULL;
00371 
00372     v = doPrimary(state);
00373     if (v == NULL)
00374       return NULL;
00375 
00376     if (! valueIsInteger(v)) {
00377       rpmError(RPMERR_BADSPEC, _("- only on numbers\n"));
00378       return NULL;
00379     }
00380 
00381     v = valueMakeInteger(- v->data.i);
00382     break;
00383 
00384   case TOK_NOT:
00385     if (rdToken(state))
00386       return NULL;
00387 
00388     v = doPrimary(state);
00389     if (v == NULL)
00390       return NULL;
00391 
00392     if (! valueIsInteger(v)) {
00393       rpmError(RPMERR_BADSPEC, _("! only on numbers\n"));
00394       return NULL;
00395     }
00396 
00397     v = valueMakeInteger(! v->data.i);
00398     break;
00399   default:
00400     return NULL;
00401     /*@notreached@*/ break;
00402   }
00403   /*@=branchstate@*/
00404 
00405   DEBUG(valueDump("doPrimary:", v, stdout));
00406   return v;
00407 }
00408 
00412 static Value doMultiplyDivide(ParseState state)
00413         /*@globals rpmGlobalMacroContext @*/
00414         /*@modifies state->nextToken, state->p, state->tokenValue,
00415                 rpmGlobalMacroContext @*/
00416 {
00417   Value v1, v2 = NULL;
00418 
00419   DEBUG(printf("doMultiplyDivide()\n"));
00420 
00421   v1 = doPrimary(state);
00422   if (v1 == NULL)
00423     return NULL;
00424 
00425   /*@-branchstate@*/
00426   while (state->nextToken == TOK_MULTIPLY
00427          || state->nextToken == TOK_DIVIDE) {
00428     int op = state->nextToken;
00429 
00430     if (rdToken(state))
00431       return NULL;
00432 
00433     if (v2) valueFree(v2);
00434 
00435     v2 = doPrimary(state);
00436     if (v2 == NULL)
00437       return NULL;
00438 
00439     if (! valueSameType(v1, v2)) {
00440       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00441       return NULL;
00442     }
00443 
00444     if (valueIsInteger(v1)) {
00445       int i1 = v1->data.i, i2 = v2->data.i;
00446 
00447       valueFree(v1);
00448       if (op == TOK_MULTIPLY)
00449         v1 = valueMakeInteger(i1 * i2);
00450       else
00451         v1 = valueMakeInteger(i1 / i2);
00452     } else {
00453       rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
00454       return NULL;
00455     }
00456   }
00457   /*@=branchstate@*/
00458 
00459   if (v2) valueFree(v2);
00460   return v1;
00461 }
00462 
00466 /*@-boundswrite@*/
00467 static Value doAddSubtract(ParseState state)
00468         /*@globals rpmGlobalMacroContext @*/
00469         /*@modifies state->nextToken, state->p, state->tokenValue,
00470                 rpmGlobalMacroContext @*/
00471 {
00472   Value v1, v2 = NULL;
00473 
00474   DEBUG(printf("doAddSubtract()\n"));
00475 
00476   v1 = doMultiplyDivide(state);
00477   if (v1 == NULL)
00478     return NULL;
00479 
00480   /*@-branchstate@*/
00481   while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
00482     int op = state->nextToken;
00483 
00484     if (rdToken(state))
00485       return NULL;
00486 
00487     if (v2) valueFree(v2);
00488 
00489     v2 = doMultiplyDivide(state);
00490     if (v2 == NULL)
00491       return NULL;
00492 
00493     if (! valueSameType(v1, v2)) {
00494       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00495       return NULL;
00496     }
00497 
00498     if (valueIsInteger(v1)) {
00499       int i1 = v1->data.i, i2 = v2->data.i;
00500 
00501       valueFree(v1);
00502       if (op == TOK_ADD)
00503         v1 = valueMakeInteger(i1 + i2);
00504       else
00505         v1 = valueMakeInteger(i1 - i2);
00506     } else {
00507       char *copy;
00508 
00509       if (op == TOK_MINUS) {
00510         rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
00511         return NULL;
00512       }
00513 
00514       copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
00515       (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
00516 
00517       valueFree(v1);
00518       v1 = valueMakeString(copy);
00519     }
00520   }
00521   /*@=branchstate@*/
00522 
00523   if (v2) valueFree(v2);
00524   return v1;
00525 }
00526 /*@=boundswrite@*/
00527 
00531 static Value doRelational(ParseState state)
00532         /*@globals rpmGlobalMacroContext @*/
00533         /*@modifies state->nextToken, state->p, state->tokenValue,
00534                 rpmGlobalMacroContext @*/
00535 {
00536   Value v1, v2 = NULL;
00537 
00538   DEBUG(printf("doRelational()\n"));
00539 
00540   v1 = doAddSubtract(state);
00541   if (v1 == NULL)
00542     return NULL;
00543 
00544   /*@-branchstate@*/
00545   while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
00546     int op = state->nextToken;
00547 
00548     if (rdToken(state))
00549       return NULL;
00550 
00551     if (v2) valueFree(v2);
00552 
00553     v2 = doAddSubtract(state);
00554     if (v2 == NULL)
00555       return NULL;
00556 
00557     if (! valueSameType(v1, v2)) {
00558       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00559       return NULL;
00560     }
00561 
00562     if (valueIsInteger(v1)) {
00563       int i1 = v1->data.i, i2 = v2->data.i, r = 0;
00564       switch (op) {
00565       case TOK_EQ:
00566         r = (i1 == i2);
00567         /*@switchbreak@*/ break;
00568       case TOK_NEQ:
00569         r = (i1 != i2);
00570         /*@switchbreak@*/ break;
00571       case TOK_LT:
00572         r = (i1 < i2);
00573         /*@switchbreak@*/ break;
00574       case TOK_LE:
00575         r = (i1 <= i2);
00576         /*@switchbreak@*/ break;
00577       case TOK_GT:
00578         r = (i1 > i2);
00579         /*@switchbreak@*/ break;
00580       case TOK_GE:
00581         r = (i1 >= i2);
00582         /*@switchbreak@*/ break;
00583       default:
00584         /*@switchbreak@*/ break;
00585       }
00586       valueFree(v1);
00587       v1 = valueMakeInteger(r);
00588     } else {
00589       const char * s1 = v1->data.s;
00590       const char * s2 = v2->data.s;
00591       int r = 0;
00592       switch (op) {
00593       case TOK_EQ:
00594         r = (strcmp(s1,s2) == 0);
00595         /*@switchbreak@*/ break;
00596       case TOK_NEQ:
00597         r = (strcmp(s1,s2) != 0);
00598         /*@switchbreak@*/ break;
00599       case TOK_LT:
00600         r = (strcmp(s1,s2) < 0);
00601         /*@switchbreak@*/ break;
00602       case TOK_LE:
00603         r = (strcmp(s1,s2) <= 0);
00604         /*@switchbreak@*/ break;
00605       case TOK_GT:
00606         r = (strcmp(s1,s2) > 0);
00607         /*@switchbreak@*/ break;
00608       case TOK_GE:
00609         r = (strcmp(s1,s2) >= 0);
00610         /*@switchbreak@*/ break;
00611       default:
00612         /*@switchbreak@*/ break;
00613       }
00614       valueFree(v1);
00615       v1 = valueMakeInteger(r);
00616     }
00617   }
00618   /*@=branchstate@*/
00619 
00620   if (v2) valueFree(v2);
00621   return v1;
00622 }
00623 
00627 static Value doLogical(ParseState state)
00628         /*@globals rpmGlobalMacroContext @*/
00629         /*@modifies state->nextToken, state->p, state->tokenValue,
00630                 rpmGlobalMacroContext @*/
00631 {
00632   Value v1, v2 = NULL;
00633 
00634   DEBUG(printf("doLogical()\n"));
00635 
00636   v1 = doRelational(state);
00637   if (v1 == NULL)
00638     return NULL;
00639 
00640   /*@-branchstate@*/
00641   while (state->nextToken == TOK_LOGICAL_AND
00642          || state->nextToken == TOK_LOGICAL_OR) {
00643     int op = state->nextToken;
00644 
00645     if (rdToken(state))
00646       return NULL;
00647 
00648     if (v2) valueFree(v2);
00649 
00650     v2 = doRelational(state);
00651     if (v2 == NULL)
00652       return NULL;
00653 
00654     if (! valueSameType(v1, v2)) {
00655       rpmError(RPMERR_BADSPEC, _("types must match\n"));
00656       return NULL;
00657     }
00658 
00659     if (valueIsInteger(v1)) {
00660       int i1 = v1->data.i, i2 = v2->data.i;
00661 
00662       valueFree(v1);
00663       if (op == TOK_LOGICAL_AND)
00664         v1 = valueMakeInteger(i1 && i2);
00665       else
00666         v1 = valueMakeInteger(i1 || i2);
00667     } else {
00668       rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
00669       return NULL;
00670     }
00671   }
00672   /*@=branchstate@*/
00673 
00674   if (v2) valueFree(v2);
00675   return v1;
00676 }
00677 
00678 int parseExpressionBoolean(Spec spec, const char *expr)
00679 {
00680   struct _parseState state;
00681   int result = -1;
00682   Value v;
00683 
00684   DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
00685 
00686   /* Initialize the expression parser state. */
00687   state.p = state.str = xstrdup(expr);
00688   state.spec = spec;
00689   state.nextToken = 0;
00690   state.tokenValue = NULL;
00691   (void) rdToken(&state);
00692 
00693   /* Parse the expression. */
00694   v = doLogical(&state);
00695   if (!v) {
00696     state.str = _free(state.str);
00697     return -1;
00698   }
00699 
00700   /* If the next token is not TOK_EOF, we have a syntax error. */
00701   if (state.nextToken != TOK_EOF) {
00702     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00703     state.str = _free(state.str);
00704     return -1;
00705   }
00706 
00707   DEBUG(valueDump("parseExprBoolean:", v, stdout));
00708 
00709   switch (v->type) {
00710   case VALUE_TYPE_INTEGER:
00711     result = v->data.i != 0;
00712     break;
00713   case VALUE_TYPE_STRING:
00714 /*@-boundsread@*/
00715     result = v->data.s[0] != '\0';
00716 /*@=boundsread@*/
00717     break;
00718   default:
00719     break;
00720   }
00721 
00722   state.str = _free(state.str);
00723   valueFree(v);
00724   return result;
00725 }
00726 
00727 char * parseExpressionString(Spec spec, const char *expr)
00728 {
00729   struct _parseState state;
00730   char *result = NULL;
00731   Value v;
00732 
00733   DEBUG(printf("parseExprString(?, '%s')\n", expr));
00734 
00735   /* Initialize the expression parser state. */
00736   state.p = state.str = xstrdup(expr);
00737   state.spec = spec;
00738   state.nextToken = 0;
00739   state.tokenValue = NULL;
00740   (void) rdToken(&state);
00741 
00742   /* Parse the expression. */
00743   v = doLogical(&state);
00744   if (!v) {
00745     state.str = _free(state.str);
00746     return NULL;
00747   }
00748 
00749   /* If the next token is not TOK_EOF, we have a syntax error. */
00750   if (state.nextToken != TOK_EOF) {
00751     rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
00752     state.str = _free(state.str);
00753     return NULL;
00754   }
00755 
00756   DEBUG(valueDump("parseExprString:", v, stdout));
00757 
00758   /*@-branchstate@*/
00759   switch (v->type) {
00760   case VALUE_TYPE_INTEGER: {
00761     char buf[128];
00762     sprintf(buf, "%d", v->data.i);
00763     result = xstrdup(buf);
00764   } break;
00765   case VALUE_TYPE_STRING:
00766     result = xstrdup(v->data.s);
00767     break;
00768   default:
00769     break;
00770   }
00771   /*@=branchstate@*/
00772 
00773   state.str = _free(state.str);
00774   valueFree(v);
00775   return result;
00776 }

Generated on Sun Oct 26 13:01:56 2003 for rpm by doxygen1.2.18