rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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 {
37 enum { VALUE_TYPE_INTEGER, VALUE_TYPE_STRING } type;
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@*/
113 Value tokenValue;
114 Spec spec;
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@*/
337 static Value doPrimary(ParseState state)
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@*/
419 static Value doMultiplyDivide(ParseState state)
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@*/
473 static Value doAddSubtract(ParseState state)
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@*/
535 static Value doRelational(ParseState state)
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
629 static Value doLogical(ParseState state)
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 }