rpm  4.5
expression.c
Go to the documentation of this file.
1 
14 #include "system.h"
15 
16 #include <rpmbuild.h>
17 #include <rpmlib.h>
18 
19 #include "debug.h"
20 
21 /* #define DEBUG_PARSER 1 */
22 
23 #ifdef DEBUG_PARSER
24 #include <stdio.h>
25 #define DEBUG(x) do { x ; } while (0)
26 #else
27 #define DEBUG(x)
28 #endif
29 
33 typedef struct _value {
35  union {
36  const char *s;
37  int i;
38  } data;
39 } *Value;
40 
43 static Value valueMakeInteger(int i)
44  /*@*/
45 {
46  Value v;
47 
48  v = (Value) xmalloc(sizeof(*v));
49  v->type = VALUE_TYPE_INTEGER;
50  v->data.i = i;
51  return v;
52 }
53 
56 static Value valueMakeString(/*@only@*/ const char *s)
57  /*@*/
58 {
59  Value v;
60 
61  v = (Value) xmalloc(sizeof(*v));
62  v->type = VALUE_TYPE_STRING;
63  v->data.s = s;
64  return v;
65 }
66 
69 static void valueFree( /*@only@*/ Value v)
70  /*@modifies v @*/
71 {
72  if (v) {
73  if (v->type == VALUE_TYPE_STRING)
74  v->data.s = _free(v->data.s);
75  v = _free(v);
76  }
77 }
78 
79 #ifdef DEBUG_PARSER
80 static void valueDump(const char *msg, Value v, FILE *fp)
81  /*@*/
82 {
83  if (msg)
84  fprintf(fp, "%s ", msg);
85  if (v) {
86  if (v->type == VALUE_TYPE_INTEGER)
87  fprintf(fp, "INTEGER %d\n", v->data.i);
88  else
89  fprintf(fp, "STRING '%s'\n", v->data.s);
90  } else
91  fprintf(fp, "NULL\n");
92 }
93 #endif
94 
95 #define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
96 #define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
97 #define valueSameType(v1,v2) ((v1)->type == (v2)->type)
98 
99 
103 typedef struct _parseState {
104 /*@owned@*/
105  char *str;
106 /*@dependent@*/
107  char *p;
108  int nextToken;
109 /*@relnull@*/
112 } *ParseState;
113 
114 
119 #define TOK_EOF 1
120 #define TOK_INTEGER 2
121 #define TOK_STRING 3
122 #define TOK_IDENTIFIER 4
123 #define TOK_ADD 5
124 #define TOK_MINUS 6
125 #define TOK_MULTIPLY 7
126 #define TOK_DIVIDE 8
127 #define TOK_OPEN_P 9
128 #define TOK_CLOSE_P 10
129 #define TOK_EQ 11
130 #define TOK_NEQ 12
131 #define TOK_LT 13
132 #define TOK_LE 14
133 #define TOK_GT 15
134 #define TOK_GE 16
135 #define TOK_NOT 17
136 #define TOK_LOGICAL_AND 18
137 #define TOK_LOGICAL_OR 19
138 
140 #define EXPRBUFSIZ BUFSIZ
141 
142 #if defined(DEBUG_PARSER)
143 typedef struct exprTokTableEntry {
144  const char *name;
145  int val;
146 } ETTE_t;
147 
148 ETTE_t exprTokTable[] = {
149  { "EOF", TOK_EOF },
150  { "I", TOK_INTEGER },
151  { "S", TOK_STRING },
152  { "ID", TOK_IDENTIFIER },
153  { "+", TOK_ADD },
154  { "-", TOK_MINUS },
155  { "*", TOK_MULTIPLY },
156  { "/", TOK_DIVIDE },
157  { "( ", TOK_OPEN_P },
158  { " )", TOK_CLOSE_P },
159  { "==", TOK_EQ },
160  { "!=", TOK_NEQ },
161  { "<", TOK_LT },
162  { "<=", TOK_LE },
163  { ">", TOK_GT },
164  { ">=", TOK_GE },
165  { "!", TOK_NOT },
166  { "&&", TOK_LOGICAL_AND },
167  { "||", TOK_LOGICAL_OR },
168  { NULL, 0 }
169 };
170 
171 static const char *prToken(int val)
172  /*@*/
173 {
174  ETTE_t *et;
175 
176  for (et = exprTokTable; et->name != NULL; et++) {
177  if (val == et->val)
178  return et->name;
179  }
180  return "???";
181 }
182 #endif /* DEBUG_PARSER */
183 
187 /*@-boundswrite@*/
188 static int rdToken(ParseState state)
189  /*@globals rpmGlobalMacroContext, h_errno @*/
190  /*@modifies state->nextToken, state->p, state->tokenValue,
191  rpmGlobalMacroContext @*/
192 {
193  int token;
194  Value v = NULL;
195  char *p = state->p;
196 
197  /* Skip whitespace before the next token. */
198  while (*p && xisspace(*p)) p++;
199 
200  switch (*p) {
201  case '\0':
202  token = TOK_EOF;
203  p--;
204  break;
205  case '+':
206  token = TOK_ADD;
207  break;
208  case '-':
209  token = TOK_MINUS;
210  break;
211  case '*':
212  token = TOK_MULTIPLY;
213  break;
214  case '/':
215  token = TOK_DIVIDE;
216  break;
217  case '(':
218  token = TOK_OPEN_P;
219  break;
220  case ')':
221  token = TOK_CLOSE_P;
222  break;
223  case '=':
224  if (p[1] == '=') {
225  token = TOK_EQ;
226  p++;
227  } else {
228  rpmError(RPMERR_BADSPEC, _("syntax error while parsing ==\n"));
229  return -1;
230  }
231  break;
232  case '!':
233  if (p[1] == '=') {
234  token = TOK_NEQ;
235  p++;
236  } else
237  token = TOK_NOT;
238  break;
239  case '<':
240  if (p[1] == '=') {
241  token = TOK_LE;
242  p++;
243  } else
244  token = TOK_LT;
245  break;
246  case '>':
247  if (p[1] == '=') {
248  token = TOK_GE;
249  p++;
250  } else
251  token = TOK_GT;
252  break;
253  case '&':
254  if (p[1] == '&') {
255  token = TOK_LOGICAL_AND;
256  p++;
257  } else {
258  rpmError(RPMERR_BADSPEC, _("syntax error while parsing &&\n"));
259  return -1;
260  }
261  break;
262  case '|':
263  if (p[1] == '|') {
264  token = TOK_LOGICAL_OR;
265  p++;
266  } else {
267  rpmError(RPMERR_BADSPEC, _("syntax error while parsing ||\n"));
268  return -1;
269  }
270  break;
271 
272  default:
273  if (xisdigit(*p)) {
274  char temp[EXPRBUFSIZ], *t = temp;
275 
276  temp[0] = '\0';
277  while (*p && xisdigit(*p))
278  *t++ = *p++;
279  *t++ = '\0';
280  p--;
281 
282  token = TOK_INTEGER;
283  v = valueMakeInteger(atoi(temp));
284 
285  } else if (xisalpha(*p)) {
286  char temp[EXPRBUFSIZ], *t = temp;
287 
288  temp[0] = '\0';
289  while (*p && (xisalnum(*p) || *p == '_'))
290  *t++ = *p++;
291  *t++ = '\0';
292  p--;
293 
294  token = TOK_IDENTIFIER;
295  v = valueMakeString( xstrdup(temp) );
296 
297  } else if (*p == '\"') {
298  char temp[EXPRBUFSIZ], *t = temp;
299 
300  temp[0] = '\0';
301  p++;
302  while (*p && *p != '\"')
303  *t++ = *p++;
304  *t++ = '\0';
305 
306  token = TOK_STRING;
307  v = valueMakeString( rpmExpand(temp, NULL) );
308 
309  } else {
310  rpmError(RPMERR_BADSPEC, _("parse error in expression\n"));
311  return -1;
312  }
313  }
314 
315  state->p = p + 1;
316  state->nextToken = token;
317  state->tokenValue = v;
318 
319  DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
320  DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
321 
322  return 0;
323 }
324 /*@=boundswrite@*/
325 
326 /*@null@*/
327 static Value doLogical(ParseState state)
328  /*@globals rpmGlobalMacroContext, h_errno @*/
329  /*@modifies state->nextToken, state->p, state->tokenValue,
330  rpmGlobalMacroContext @*/;
331 
335 /*@null@*/
337  /*@globals rpmGlobalMacroContext, h_errno @*/
338  /*@modifies state->nextToken, state->p, state->tokenValue,
339  rpmGlobalMacroContext @*/
340 {
341  Value v;
342 
343  DEBUG(printf("doPrimary()\n"));
344 
345  /*@-branchstate@*/
346  switch (state->nextToken) {
347  case TOK_OPEN_P:
348  if (rdToken(state))
349  return NULL;
350  v = doLogical(state);
351  if (state->nextToken != TOK_CLOSE_P) {
352  rpmError(RPMERR_BADSPEC, _("unmatched (\n"));
353  return NULL;
354  }
355  if (rdToken(state))
356  return NULL;
357  break;
358 
359  case TOK_INTEGER:
360  case TOK_STRING:
361  v = state->tokenValue;
362  if (rdToken(state))
363  return NULL;
364  break;
365 
366  case TOK_IDENTIFIER: {
367  const char *name = state->tokenValue->data.s;
368 
369  v = valueMakeString( rpmExpand(name, NULL) );
370  if (rdToken(state))
371  return NULL;
372  break;
373  }
374 
375  case TOK_MINUS:
376  if (rdToken(state))
377  return NULL;
378 
379  v = doPrimary(state);
380  if (v == NULL)
381  return NULL;
382 
383  if (! valueIsInteger(v)) {
384  rpmError(RPMERR_BADSPEC, _("- only on numbers\n"));
385  return NULL;
386  }
387 
388  v = valueMakeInteger(- v->data.i);
389  break;
390 
391  case TOK_NOT:
392  if (rdToken(state))
393  return NULL;
394 
395  v = doPrimary(state);
396  if (v == NULL)
397  return NULL;
398 
399  if (! valueIsInteger(v)) {
400  rpmError(RPMERR_BADSPEC, _("! only on numbers\n"));
401  return NULL;
402  }
403 
404  v = valueMakeInteger(! v->data.i);
405  break;
406  default:
407  return NULL;
408  /*@notreached@*/ break;
409  }
410  /*@=branchstate@*/
411 
412  DEBUG(valueDump("doPrimary:", v, stdout));
413  return v;
414 }
415 
419 /*@null@*/
421  /*@globals rpmGlobalMacroContext, h_errno @*/
422  /*@modifies state->nextToken, state->p, state->tokenValue,
423  rpmGlobalMacroContext @*/
424 {
425  Value v1, v2 = NULL;
426 
427  DEBUG(printf("doMultiplyDivide()\n"));
428 
429  v1 = doPrimary(state);
430  if (v1 == NULL)
431  return NULL;
432 
433  /*@-branchstate@*/
434  while (state->nextToken == TOK_MULTIPLY
435  || state->nextToken == TOK_DIVIDE) {
436  int op = state->nextToken;
437 
438  if (rdToken(state))
439  return NULL;
440 
441  if (v2) valueFree(v2);
442 
443  v2 = doPrimary(state);
444  if (v2 == NULL)
445  return NULL;
446 
447  if (! valueSameType(v1, v2)) {
448  rpmError(RPMERR_BADSPEC, _("types must match\n"));
449  return NULL;
450  }
451 
452  if (valueIsInteger(v1)) {
453  int i1 = v1->data.i, i2 = v2->data.i;
454 
455  valueFree(v1);
456  if (op == TOK_MULTIPLY)
457  v1 = valueMakeInteger(i1 * i2);
458  else
459  v1 = valueMakeInteger(i1 / i2);
460  } else {
461  rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
462  return NULL;
463  }
464  }
465  /*@=branchstate@*/
466 
467  if (v2) valueFree(v2);
468  return v1;
469 }
470 
474 /*@-boundswrite@*/
475 /*@null@*/
477  /*@globals rpmGlobalMacroContext, h_errno @*/
478  /*@modifies state->nextToken, state->p, state->tokenValue,
479  rpmGlobalMacroContext @*/
480 {
481  Value v1, v2 = NULL;
482 
483  DEBUG(printf("doAddSubtract()\n"));
484 
485  v1 = doMultiplyDivide(state);
486  if (v1 == NULL)
487  return NULL;
488 
489  /*@-branchstate@*/
490  while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
491  int op = state->nextToken;
492 
493  if (rdToken(state))
494  return NULL;
495 
496  if (v2) valueFree(v2);
497 
498  v2 = doMultiplyDivide(state);
499  if (v2 == NULL)
500  return NULL;
501 
502  if (! valueSameType(v1, v2)) {
503  rpmError(RPMERR_BADSPEC, _("types must match\n"));
504  return NULL;
505  }
506 
507  if (valueIsInteger(v1)) {
508  int i1 = v1->data.i, i2 = v2->data.i;
509 
510  valueFree(v1);
511  if (op == TOK_ADD)
512  v1 = valueMakeInteger(i1 + i2);
513  else
514  v1 = valueMakeInteger(i1 - i2);
515  } else {
516  char *copy;
517 
518  if (op == TOK_MINUS) {
519  rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
520  return NULL;
521  }
522 
523  copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
524  (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
525 
526  valueFree(v1);
527  v1 = valueMakeString(copy);
528  }
529  }
530  /*@=branchstate@*/
531 
532  if (v2) valueFree(v2);
533  return v1;
534 }
535 /*@=boundswrite@*/
536 
540 /*@null@*/
542  /*@globals rpmGlobalMacroContext, h_errno @*/
543  /*@modifies state->nextToken, state->p, state->tokenValue,
544  rpmGlobalMacroContext @*/
545 {
546  Value v1, v2 = NULL;
547 
548  DEBUG(printf("doRelational()\n"));
549 
550  v1 = doAddSubtract(state);
551  if (v1 == NULL)
552  return NULL;
553 
554  /*@-branchstate@*/
555  while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
556  int op = state->nextToken;
557 
558  if (rdToken(state))
559  return NULL;
560 
561  if (v2) valueFree(v2);
562 
563  v2 = doAddSubtract(state);
564  if (v2 == NULL)
565  return NULL;
566 
567  if (! valueSameType(v1, v2)) {
568  rpmError(RPMERR_BADSPEC, _("types must match\n"));
569  return NULL;
570  }
571 
572  if (valueIsInteger(v1)) {
573  int i1 = v1->data.i, i2 = v2->data.i, r = 0;
574  switch (op) {
575  case TOK_EQ:
576  r = (i1 == i2);
577  /*@switchbreak@*/ break;
578  case TOK_NEQ:
579  r = (i1 != i2);
580  /*@switchbreak@*/ break;
581  case TOK_LT:
582  r = (i1 < i2);
583  /*@switchbreak@*/ break;
584  case TOK_LE:
585  r = (i1 <= i2);
586  /*@switchbreak@*/ break;
587  case TOK_GT:
588  r = (i1 > i2);
589  /*@switchbreak@*/ break;
590  case TOK_GE:
591  r = (i1 >= i2);
592  /*@switchbreak@*/ break;
593  default:
594  /*@switchbreak@*/ break;
595  }
596  valueFree(v1);
597  v1 = valueMakeInteger(r);
598  } else {
599  const char * s1 = v1->data.s;
600  const char * s2 = v2->data.s;
601  int r = 0;
602  switch (op) {
603  case TOK_EQ:
604  r = (strcmp(s1,s2) == 0);
605  /*@switchbreak@*/ break;
606  case TOK_NEQ:
607  r = (strcmp(s1,s2) != 0);
608  /*@switchbreak@*/ break;
609  case TOK_LT:
610  r = (strcmp(s1,s2) < 0);
611  /*@switchbreak@*/ break;
612  case TOK_LE:
613  r = (strcmp(s1,s2) <= 0);
614  /*@switchbreak@*/ break;
615  case TOK_GT:
616  r = (strcmp(s1,s2) > 0);
617  /*@switchbreak@*/ break;
618  case TOK_GE:
619  r = (strcmp(s1,s2) >= 0);
620  /*@switchbreak@*/ break;
621  default:
622  /*@switchbreak@*/ break;
623  }
624  valueFree(v1);
625  v1 = valueMakeInteger(r);
626  }
627  }
628  /*@=branchstate@*/
629 
630  if (v2) valueFree(v2);
631  return v1;
632 }
633 
638  /*@globals rpmGlobalMacroContext, h_errno @*/
639  /*@modifies state->nextToken, state->p, state->tokenValue,
640  rpmGlobalMacroContext @*/
641 {
642  Value v1, v2 = NULL;
643 
644  DEBUG(printf("doLogical()\n"));
645 
646  v1 = doRelational(state);
647  if (v1 == NULL)
648  return NULL;
649 
650  /*@-branchstate@*/
651  while (state->nextToken == TOK_LOGICAL_AND
652  || state->nextToken == TOK_LOGICAL_OR) {
653  int op = state->nextToken;
654 
655  if (rdToken(state))
656  return NULL;
657 
658  if (v2) valueFree(v2);
659 
660  v2 = doRelational(state);
661  if (v2 == NULL)
662  return NULL;
663 
664  if (! valueSameType(v1, v2)) {
665  rpmError(RPMERR_BADSPEC, _("types must match\n"));
666  return NULL;
667  }
668 
669  if (valueIsInteger(v1)) {
670  int i1 = v1->data.i, i2 = v2->data.i;
671 
672  valueFree(v1);
673  if (op == TOK_LOGICAL_AND)
674  v1 = valueMakeInteger(i1 && i2);
675  else
676  v1 = valueMakeInteger(i1 || i2);
677  } else {
678  rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
679  return NULL;
680  }
681  }
682  /*@=branchstate@*/
683 
684  if (v2) valueFree(v2);
685  return v1;
686 }
687 
688 int parseExpressionBoolean(Spec spec, const char *expr)
689 {
690  struct _parseState state;
691  int result = -1;
692  Value v;
693 
694  DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
695 
696  /* Initialize the expression parser state. */
697  state.p = state.str = xstrdup(expr);
698  state.spec = spec;
699  state.nextToken = 0;
700  state.tokenValue = NULL;
701  (void) rdToken(&state);
702 
703  /* Parse the expression. */
704  v = doLogical(&state);
705  if (!v) {
706  state.str = _free(state.str);
707  return -1;
708  }
709 
710  /* If the next token is not TOK_EOF, we have a syntax error. */
711  if (state.nextToken != TOK_EOF) {
712  rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
713  state.str = _free(state.str);
714  return -1;
715  }
716 
717  DEBUG(valueDump("parseExprBoolean:", v, stdout));
718 
719  switch (v->type) {
720  case VALUE_TYPE_INTEGER:
721  result = v->data.i != 0;
722  break;
723  case VALUE_TYPE_STRING:
724 /*@-boundsread@*/
725  result = v->data.s[0] != '\0';
726 /*@=boundsread@*/
727  break;
728  default:
729  break;
730  }
731 
732  state.str = _free(state.str);
733  valueFree(v);
734  return result;
735 }
736 
737 char * parseExpressionString(Spec spec, const char *expr)
738 {
739  struct _parseState state;
740  char *result = NULL;
741  Value v;
742 
743  DEBUG(printf("parseExprString(?, '%s')\n", expr));
744 
745  /* Initialize the expression parser state. */
746  state.p = state.str = xstrdup(expr);
747  state.spec = spec;
748  state.nextToken = 0;
749  state.tokenValue = NULL;
750  (void) rdToken(&state);
751 
752  /* Parse the expression. */
753  v = doLogical(&state);
754  if (!v) {
755  state.str = _free(state.str);
756  return NULL;
757  }
758 
759  /* If the next token is not TOK_EOF, we have a syntax error. */
760  if (state.nextToken != TOK_EOF) {
761  rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
762  state.str = _free(state.str);
763  return NULL;
764  }
765 
766  DEBUG(valueDump("parseExprString:", v, stdout));
767 
768  /*@-branchstate@*/
769  switch (v->type) {
770  case VALUE_TYPE_INTEGER: {
771  char buf[128];
772  sprintf(buf, "%d", v->data.i);
773  result = xstrdup(buf);
774  } break;
775  case VALUE_TYPE_STRING:
776  result = xstrdup(v->data.s);
777  break;
778  default:
779  break;
780  }
781  /*@=branchstate@*/
782 
783  state.str = _free(state.str);
784  valueFree(v);
785  return result;
786 }