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