diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-01-21 11:44:32 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-01-21 11:44:32 -0500 |
commit | d41d342e3bc4f3c15dd56a483053471378222de8 (patch) | |
tree | 6ac4720cd32ec80bf549fe67282e48ed65451c11 | |
parent | b8b14ccd92219d98b6336069de79d5605b4e3f45 (diff) |
parse-events: Rewrite filter algorithm to simplify it
New rewrite of the parse event filter to greatly simplify
the logic. Should be much easier to modify it now.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | parse-events.h | 3 | ||||
-rw-r--r-- | parse-filter.c | 1196 |
2 files changed, 652 insertions, 547 deletions
diff --git a/parse-events.h b/parse-events.h index 3ffe56f..43213d4 100644 --- a/parse-events.h +++ b/parse-events.h | |||
@@ -620,7 +620,8 @@ enum filter_arg_type { | |||
620 | 620 | ||
621 | enum filter_value_type { | 621 | enum filter_value_type { |
622 | FILTER_NUMBER, | 622 | FILTER_NUMBER, |
623 | FILTER_STRING | 623 | FILTER_STRING, |
624 | FILTER_CHAR | ||
624 | }; | 625 | }; |
625 | 626 | ||
626 | struct fliter_arg; | 627 | struct fliter_arg; |
diff --git a/parse-filter.c b/parse-filter.c index e5ff909..bd52033 100644 --- a/parse-filter.c +++ b/parse-filter.c | |||
@@ -324,226 +324,13 @@ static void free_events(struct event_list *events) | |||
324 | } | 324 | } |
325 | } | 325 | } |
326 | 326 | ||
327 | static enum event_type | 327 | static struct filter_arg * |
328 | process_paren(struct event_format *event, struct filter_arg **parg, | 328 | create_arg_item(struct event_format *event, |
329 | char **tok, char **error_str); | 329 | const char *token, enum filter_arg_type type, |
330 | 330 | char **error_str) | |
331 | static enum event_type | ||
332 | process_not(struct event_format *event, struct filter_arg **parg, | ||
333 | char **tok, char **error_str); | ||
334 | |||
335 | static enum event_type | ||
336 | process_value_token(struct event_format *event, struct filter_arg **parg, | ||
337 | enum event_type type, char **tok, char **error_str); | ||
338 | |||
339 | static enum event_type | ||
340 | process_op_token(struct event_format *event, struct filter_arg *larg, | ||
341 | struct filter_arg **parg, enum event_type type, char **tok, | ||
342 | char **error_str); | ||
343 | |||
344 | /* | ||
345 | * process_token | ||
346 | * Called when a new expression is found. Processes an op, or | ||
347 | * ends early if a ')' is found. | ||
348 | * | ||
349 | * Output: tok, parg | ||
350 | */ | ||
351 | static enum event_type | ||
352 | process_token(struct event_format *event, struct filter_arg **parg, | ||
353 | char **tok, char **error_str) | ||
354 | { | ||
355 | struct filter_arg *arg = NULL; | ||
356 | enum event_type type; | ||
357 | char *token; | ||
358 | |||
359 | *tok = NULL; | ||
360 | *parg = NULL; | ||
361 | |||
362 | type = read_token(&token); | ||
363 | |||
364 | /* | ||
365 | * This is a start of a new expresion. We expect to find | ||
366 | * a item or a parenthesis. | ||
367 | */ | ||
368 | switch (type) { | ||
369 | case EVENT_SQUOTE: | ||
370 | case EVENT_DQUOTE: | ||
371 | case EVENT_ITEM: | ||
372 | type = process_value_token(event, &arg, type, &token, error_str); | ||
373 | if (type == EVENT_ERROR) { | ||
374 | free_token(token); | ||
375 | return type; | ||
376 | } | ||
377 | type = read_token(&token); | ||
378 | break; | ||
379 | case EVENT_DELIM: | ||
380 | if (strcmp(token, "(") != 0) | ||
381 | break; | ||
382 | |||
383 | free_token(token); | ||
384 | type = process_paren(event, &arg, &token, error_str); | ||
385 | if (type == EVENT_NONE) { | ||
386 | *tok = token; | ||
387 | *parg = arg; | ||
388 | return type; | ||
389 | } | ||
390 | if (arg) { | ||
391 | /* | ||
392 | * If the parenthesis was a full expression, | ||
393 | * then just return it. Otherwise, we may still | ||
394 | * need to find an op. | ||
395 | */ | ||
396 | switch (arg->type) { | ||
397 | case FILTER_ARG_OP: | ||
398 | case FILTER_ARG_NUM: | ||
399 | case FILTER_ARG_STR: | ||
400 | *tok = token; | ||
401 | *parg = arg; | ||
402 | return type; | ||
403 | default: | ||
404 | break; | ||
405 | } | ||
406 | } | ||
407 | break; | ||
408 | |||
409 | case EVENT_OP: | ||
410 | if (strcmp(token, "!") != 0) | ||
411 | break; | ||
412 | |||
413 | /* | ||
414 | * A not is its own filter, it just negates, | ||
415 | * process it by itself. | ||
416 | */ | ||
417 | *tok = token; | ||
418 | type = process_not(event, parg, tok, error_str); | ||
419 | return type; | ||
420 | |||
421 | default: | ||
422 | break; | ||
423 | } | ||
424 | |||
425 | for (;;) { | ||
426 | if (type == EVENT_NONE) { | ||
427 | show_error(error_str, "unexpected end of filter"); | ||
428 | type = EVENT_ERROR; | ||
429 | |||
430 | } else if (type == EVENT_DELIM && strcmp(token, ")") == 0) { | ||
431 | /* Parenthesis call this and may return at anytime. */ | ||
432 | *tok = token; | ||
433 | *parg = arg; | ||
434 | return type; | ||
435 | |||
436 | } else if (type != EVENT_OP) { | ||
437 | show_error(error_str, "Expected an OP but found %s", token); | ||
438 | type = EVENT_ERROR; | ||
439 | } | ||
440 | |||
441 | if (type == EVENT_ERROR) { | ||
442 | free_token(token); | ||
443 | return type; | ||
444 | } | ||
445 | |||
446 | *tok = token; | ||
447 | *parg = NULL; | ||
448 | type = process_op_token(event, arg, parg, type, tok, error_str); | ||
449 | |||
450 | if (type == EVENT_ERROR) { | ||
451 | free_arg(*parg); | ||
452 | *parg = NULL; | ||
453 | return EVENT_ERROR; | ||
454 | } | ||
455 | |||
456 | if (!(*parg) || (*parg)->type != FILTER_ARG_EXP) | ||
457 | break; | ||
458 | |||
459 | /* | ||
460 | * This op was an expression (value return) | ||
461 | * It's not fine by itself, there had better be an OP | ||
462 | * after it. | ||
463 | */ | ||
464 | token = *tok; | ||
465 | *tok = NULL; | ||
466 | arg = *parg; | ||
467 | } | ||
468 | |||
469 | return type; | ||
470 | } | ||
471 | |||
472 | /* | ||
473 | * Input: tok | ||
474 | * Output: parg, tok | ||
475 | */ | ||
476 | static enum event_type | ||
477 | process_bool(struct event_format *event, struct filter_arg *larg, | ||
478 | struct filter_arg **parg, char **tok, char **error_str) | ||
479 | { | ||
480 | struct filter_arg *rarg; | ||
481 | struct filter_arg *arg; | ||
482 | enum event_type type; | ||
483 | enum filter_op_type btype; | ||
484 | |||
485 | /* Can only be called with '&&' or '||' */ | ||
486 | btype = strcmp(*tok, "&&") == 0 ? | ||
487 | FILTER_OP_AND : FILTER_OP_OR; | ||
488 | |||
489 | type = process_token(event, &rarg, tok, error_str); | ||
490 | if (type == EVENT_ERROR) { | ||
491 | free_arg(larg); | ||
492 | *parg = NULL; | ||
493 | return type; | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * If larg or rarg is null then if this is AND, the whole expression | ||
498 | * becomes NULL, else if this is an OR, then we use the non NULL | ||
499 | * condition. | ||
500 | */ | ||
501 | if (!larg || !rarg) { | ||
502 | if (btype == FILTER_OP_AND || | ||
503 | (!larg && !rarg)) { | ||
504 | free_arg(larg); | ||
505 | free_arg(rarg); | ||
506 | *parg = NULL; | ||
507 | return type; | ||
508 | } | ||
509 | *parg = larg ? larg : rarg; | ||
510 | return type; | ||
511 | } | ||
512 | |||
513 | arg = allocate_arg(); | ||
514 | arg->type = FILTER_ARG_OP; | ||
515 | arg->op.type = btype; | ||
516 | arg->op.left = larg; | ||
517 | arg->op.right = rarg; | ||
518 | |||
519 | |||
520 | /* | ||
521 | * If the next token is also a boolean expression, then | ||
522 | * make the next boolean the parent.. | ||
523 | */ | ||
524 | if (type != EVENT_OP || | ||
525 | (strcmp(*tok, "&&") != 0 && strcmp(*tok, "||") != 0)) { | ||
526 | *parg = arg; | ||
527 | return type; | ||
528 | } | ||
529 | |||
530 | return process_bool(event, arg, parg, tok, error_str); | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Input: tok | ||
535 | * Output: parg | ||
536 | */ | ||
537 | static enum event_type | ||
538 | process_value_token(struct event_format *event, struct filter_arg **parg, | ||
539 | enum event_type type, char **tok, char **error_str) | ||
540 | { | 331 | { |
541 | struct format_field *field; | 332 | struct format_field *field; |
542 | struct filter_arg *arg; | 333 | struct filter_arg *arg; |
543 | char *token; | ||
544 | |||
545 | token = *tok; | ||
546 | *tok = NULL; | ||
547 | 334 | ||
548 | arg = allocate_arg(); | 335 | arg = allocate_arg(); |
549 | 336 | ||
@@ -552,8 +339,11 @@ process_value_token(struct event_format *event, struct filter_arg **parg, | |||
552 | case EVENT_SQUOTE: | 339 | case EVENT_SQUOTE: |
553 | case EVENT_DQUOTE: | 340 | case EVENT_DQUOTE: |
554 | arg->type = FILTER_ARG_VALUE; | 341 | arg->type = FILTER_ARG_VALUE; |
555 | arg->value.type = FILTER_STRING; | 342 | arg->value.type = |
556 | arg->value.str = token; | 343 | type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; |
344 | arg->value.str = strdup(token); | ||
345 | if (!arg->value.str) | ||
346 | die("malloc string"); | ||
557 | break; | 347 | break; |
558 | case EVENT_ITEM: | 348 | case EVENT_ITEM: |
559 | /* if it is a number, then convert it */ | 349 | /* if it is a number, then convert it */ |
@@ -561,24 +351,20 @@ process_value_token(struct event_format *event, struct filter_arg **parg, | |||
561 | arg->type = FILTER_ARG_VALUE; | 351 | arg->type = FILTER_ARG_VALUE; |
562 | arg->value.type = FILTER_NUMBER; | 352 | arg->value.type = FILTER_NUMBER; |
563 | arg->value.val = strtoll(token, NULL, 0); | 353 | arg->value.val = strtoll(token, NULL, 0); |
564 | free_token(token); | ||
565 | break; | 354 | break; |
566 | } | 355 | } |
567 | /* Consider this a field */ | 356 | /* Consider this a field */ |
568 | field = pevent_find_any_field(event, token); | 357 | field = pevent_find_any_field(event, token); |
569 | if (!field) { | 358 | if (!field) { |
570 | if (strcmp(token, COMM) != 0) { | 359 | if (strcmp(token, COMM) != 0) { |
571 | /* not a field, so NULL it up */ | 360 | /* not a field, Make it false */ |
572 | free_token(token); | 361 | arg->type = FILTER_ARG_BOOLEAN; |
573 | free_arg(arg); | 362 | arg->bool.value = FILTER_FALSE; |
574 | arg = NULL; | ||
575 | break; | 363 | break; |
576 | } | 364 | } |
577 | /* If token is 'COMM' then it is special */ | 365 | /* If token is 'COMM' then it is special */ |
578 | field = &comm; | 366 | field = &comm; |
579 | } | 367 | } |
580 | free_token(token); | ||
581 | |||
582 | arg->type = FILTER_ARG_FIELD; | 368 | arg->type = FILTER_ARG_FIELD; |
583 | arg->field.field = field; | 369 | arg->field.field = field; |
584 | break; | 370 | break; |
@@ -586,428 +372,745 @@ process_value_token(struct event_format *event, struct filter_arg **parg, | |||
586 | free_arg(arg); | 372 | free_arg(arg); |
587 | show_error(error_str, "expected a value but found %s", | 373 | show_error(error_str, "expected a value but found %s", |
588 | token); | 374 | token); |
589 | free_token(token); | 375 | return NULL; |
590 | return EVENT_ERROR; | ||
591 | } | 376 | } |
377 | return arg; | ||
378 | } | ||
592 | 379 | ||
593 | *parg = arg; | 380 | static struct filter_arg * |
594 | return type; | 381 | create_arg_op(enum filter_op_type btype) |
382 | { | ||
383 | struct filter_arg *arg; | ||
384 | |||
385 | arg = allocate_arg(); | ||
386 | arg->type = FILTER_ARG_OP; | ||
387 | arg->op.type = btype; | ||
388 | |||
389 | return arg; | ||
595 | } | 390 | } |
596 | 391 | ||
597 | /* | 392 | static struct filter_arg * |
598 | * Output: parg, tok | 393 | create_arg_exp(enum filter_exp_type etype) |
599 | */ | ||
600 | static enum event_type | ||
601 | process_value(struct event_format *event, struct filter_arg **parg, | ||
602 | enum event_type *orig_type, char **tok, char **error_str) | ||
603 | { | 394 | { |
604 | enum event_type type; | 395 | struct filter_arg *arg; |
605 | char *token; | 396 | |
606 | 397 | arg = allocate_arg(); | |
607 | *tok = NULL; | 398 | arg->type = FILTER_ARG_EXP; |
608 | type = read_token(&token); | 399 | arg->op.type = etype; |
609 | *orig_type = type; | 400 | |
610 | if (type == EVENT_DELIM && strcmp(token, "(") == 0) { | 401 | return arg; |
611 | type = process_paren(event, parg, &token, error_str); | 402 | } |
612 | /* Must be a expression or value */ | 403 | |
613 | if (type == EVENT_ERROR || !(*parg)) { | 404 | static struct filter_arg * |
614 | free_token(token); | 405 | create_arg_cmp(enum filter_exp_type etype) |
615 | return type; | 406 | { |
616 | } | 407 | struct filter_arg *arg; |
617 | switch ((*parg)->type) { | 408 | |
618 | case FILTER_ARG_BOOLEAN: | 409 | arg = allocate_arg(); |
410 | /* Use NUM and change if necessary */ | ||
411 | arg->type = FILTER_ARG_NUM; | ||
412 | arg->op.type = etype; | ||
413 | |||
414 | return arg; | ||
415 | } | ||
416 | |||
417 | static int add_right(struct filter_arg *op, struct filter_arg *arg, | ||
418 | char **error_str) | ||
419 | { | ||
420 | struct filter_arg *left; | ||
421 | char *str; | ||
422 | int op_type; | ||
423 | int ret; | ||
424 | |||
425 | switch (op->type) { | ||
426 | case FILTER_ARG_EXP: | ||
427 | if (op->exp.right) | ||
428 | goto out_fail; | ||
429 | op->exp.right = arg; | ||
430 | break; | ||
431 | |||
432 | case FILTER_ARG_OP: | ||
433 | if (op->op.right) | ||
434 | goto out_fail; | ||
435 | op->op.right = arg; | ||
436 | break; | ||
437 | |||
438 | case FILTER_ARG_NUM: | ||
439 | if (op->op.right) | ||
440 | goto out_fail; | ||
441 | /* | ||
442 | * The arg must be num, str, or field | ||
443 | */ | ||
444 | switch (arg->type) { | ||
619 | case FILTER_ARG_VALUE: | 445 | case FILTER_ARG_VALUE: |
620 | case FILTER_ARG_FIELD: | 446 | case FILTER_ARG_FIELD: |
621 | case FILTER_ARG_EXP: | ||
622 | break; | 447 | break; |
623 | default: | 448 | default: |
624 | show_error(error_str, "expected a value"); | 449 | show_error(error_str, |
625 | free_token(token); | 450 | "Illegal rvalue"); |
626 | return EVENT_ERROR; | 451 | return -1; |
627 | } | 452 | } |
628 | } else { | ||
629 | type = process_value_token(event, parg, type, &token, error_str); | ||
630 | free_token(token); | ||
631 | if (type == EVENT_ERROR) | ||
632 | return type; | ||
633 | type = read_token(&token); | ||
634 | } | ||
635 | 453 | ||
636 | *tok = token; | 454 | /* |
637 | return type; | 455 | * Depending on the type, we may need to |
638 | } | 456 | * convert this to a string or regex. |
457 | */ | ||
458 | switch (arg->value.type) { | ||
459 | case FILTER_CHAR: | ||
460 | /* | ||
461 | * A char should be converted to number if | ||
462 | * the string is 1 byte, and the compare | ||
463 | * is not a REGEX. | ||
464 | */ | ||
465 | if (strlen(arg->value.str) == 1 && | ||
466 | op->num.type != FILTER_CMP_REGEX && | ||
467 | op->num.type != FILTER_CMP_NOT_REGEX) { | ||
468 | arg->value.type = FILTER_NUMBER; | ||
469 | goto do_int; | ||
470 | } | ||
471 | /* fall through */ | ||
472 | case FILTER_STRING: | ||
639 | 473 | ||
640 | /* | 474 | /* convert op to a string arg */ |
641 | * Input: larg | 475 | op_type = op->num.type; |
642 | * Output: parg, tok | 476 | left = op->num.left; |
643 | */ | 477 | str = arg->value.str; |
644 | static enum event_type | ||
645 | process_cmp(struct event_format *event, enum filter_cmp_type op_type, | ||
646 | struct filter_arg *larg, struct filter_arg **parg, | ||
647 | char **tok, char **error_str) | ||
648 | { | ||
649 | struct filter_arg *arg; | ||
650 | struct filter_arg *rarg = NULL; | ||
651 | enum event_type orig_type; | ||
652 | enum event_type type; | ||
653 | int ret; | ||
654 | 478 | ||
655 | *parg = NULL; | 479 | /* reset the op for the new field */ |
480 | memset(op, 0, sizeof(*op)); | ||
656 | 481 | ||
657 | type = process_value(event, &rarg, &orig_type, tok, error_str); | 482 | /* |
658 | if (type == EVENT_ERROR) { | 483 | * If left arg was a field not found then |
659 | free_arg(rarg); | 484 | * NULL the entire op. |
660 | return type; | 485 | */ |
661 | } | 486 | if (left->type == FILTER_ARG_BOOLEAN) { |
487 | free_arg(left); | ||
488 | free_arg(arg); | ||
489 | op->type = FILTER_ARG_BOOLEAN; | ||
490 | op->bool.value = FILTER_FALSE; | ||
491 | break; | ||
492 | } | ||
662 | 493 | ||
663 | arg = allocate_arg(); | 494 | /* Left arg must be a field */ |
664 | /* | 495 | if (left->type != FILTER_ARG_FIELD) { |
665 | * If either arg is NULL or right was field not found. | 496 | show_error(error_str, |
666 | * Then make the entire expression NULL. (will turn to FALSE) | 497 | "Illegal lvalue for string comparison"); |
667 | */ | 498 | return -1; |
668 | if (!larg || !rarg) { | 499 | } |
669 | free_arg(larg); | ||
670 | free_arg(rarg); | ||
671 | free_arg(arg); | ||
672 | arg = NULL; | ||
673 | goto cont; | ||
674 | } | ||
675 | 500 | ||
676 | switch (orig_type) { | 501 | /* Make sure this is a valid string compare */ |
677 | case EVENT_SQUOTE: | ||
678 | /* treat this as a character if string is of length 1? */ | ||
679 | if (strlen(rarg->str.val) == 1) { | ||
680 | switch (op_type) { | 502 | switch (op_type) { |
503 | case FILTER_CMP_EQ: | ||
504 | op_type = FILTER_CMP_MATCH; | ||
505 | break; | ||
506 | case FILTER_CMP_NE: | ||
507 | op_type = FILTER_CMP_NOT_MATCH; | ||
508 | break; | ||
509 | |||
681 | case FILTER_CMP_REGEX: | 510 | case FILTER_CMP_REGEX: |
682 | case FILTER_CMP_NOT_REGEX: | 511 | case FILTER_CMP_NOT_REGEX: |
683 | /* regex can't be used with ints */ | 512 | ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB); |
513 | if (ret) { | ||
514 | show_error(error_str, | ||
515 | "RegEx '%s' did not compute", | ||
516 | str); | ||
517 | return -1; | ||
518 | } | ||
684 | break; | 519 | break; |
685 | default: | 520 | default: |
686 | goto as_int; | 521 | show_error(error_str, |
522 | "Illegal comparison for string"); | ||
523 | return -1; | ||
687 | } | 524 | } |
688 | } | ||
689 | /* fall through */ | ||
690 | case EVENT_DQUOTE: | ||
691 | arg->type = FILTER_ARG_STR; | ||
692 | |||
693 | if (larg->type != FILTER_ARG_FIELD) { | ||
694 | free(larg); | ||
695 | free(rarg); | ||
696 | show_error(error_str, | ||
697 | "Illegal lval for string comparison"); | ||
698 | free_arg(arg); | ||
699 | return EVENT_ERROR; | ||
700 | } | ||
701 | 525 | ||
702 | arg->str.field = larg->field.field; | 526 | op->type = FILTER_ARG_STR; |
703 | free_arg(larg); | 527 | op->str.type = op_type; |
528 | op->str.field = left->field.field; | ||
529 | op->str.val = strdup(str); | ||
530 | if (!op->str.val) | ||
531 | die("malloc string"); | ||
532 | /* | ||
533 | * Need a buffer to copy data for tests | ||
534 | */ | ||
535 | op->str.buffer = malloc_or_die(op->str.field->size + 1); | ||
536 | /* Null terminate this buffer */ | ||
537 | op->str.buffer[op->str.field->size] = 0; | ||
704 | 538 | ||
705 | /* free the rarg, and use its token */ | 539 | /* We no longer have left or right args */ |
706 | arg->str.val = rarg->value.str; | 540 | free_arg(arg); |
707 | rarg->value.str = NULL; | 541 | free_arg(left); |
708 | free_arg(rarg); | ||
709 | 542 | ||
710 | /* Make sure this is a valid string compare */ | ||
711 | switch (op_type) { | ||
712 | case FILTER_CMP_EQ: | ||
713 | op_type = FILTER_CMP_MATCH; | ||
714 | break; | ||
715 | case FILTER_CMP_NE: | ||
716 | op_type = FILTER_CMP_NOT_MATCH; | ||
717 | break; | 543 | break; |
718 | 544 | ||
719 | case FILTER_CMP_REGEX: | 545 | case FILTER_NUMBER: |
720 | case FILTER_CMP_NOT_REGEX: | 546 | |
721 | ret = regcomp(&arg->str.reg, arg->str.val, REG_ICASE|REG_NOSUB); | 547 | do_int: |
722 | if (ret) { | 548 | switch (op->num.type) { |
549 | case FILTER_CMP_REGEX: | ||
550 | case FILTER_CMP_NOT_REGEX: | ||
723 | show_error(error_str, | 551 | show_error(error_str, |
724 | "RegEx '%s' did not compute", | 552 | "Op not allowed with integers"); |
725 | arg->str.val); | 553 | return -1; |
726 | free_arg(arg); | 554 | |
727 | return EVENT_ERROR; | 555 | default: |
556 | break; | ||
728 | } | 557 | } |
558 | |||
559 | /* numeric compare */ | ||
560 | op->num.right = arg; | ||
729 | break; | 561 | break; |
730 | default: | 562 | default: |
731 | show_error(error_str, | 563 | goto out_fail; |
732 | "Illegal comparison for string"); | ||
733 | free_arg(arg); | ||
734 | return EVENT_ERROR; | ||
735 | } | 564 | } |
736 | |||
737 | arg->str.type = op_type; | ||
738 | |||
739 | /* | ||
740 | * Need a buffer to copy data int for tests */ | ||
741 | arg->str.buffer = malloc_or_die(arg->str.field->size + 1); | ||
742 | /* Null terminate this buffer */ | ||
743 | arg->str.buffer[arg->str.field->size] = 0; | ||
744 | |||
745 | break; | 565 | break; |
746 | default: | 566 | default: |
747 | as_int: | 567 | goto out_fail; |
748 | switch (op_type) { | ||
749 | case FILTER_CMP_REGEX: | ||
750 | case FILTER_CMP_NOT_REGEX: | ||
751 | show_error(error_str, | ||
752 | "Op not allowed with integers"); | ||
753 | free_arg(arg); | ||
754 | return EVENT_ERROR; | ||
755 | default: | ||
756 | break; | ||
757 | } | ||
758 | /* numeric compare */ | ||
759 | arg->type = FILTER_ARG_NUM; | ||
760 | arg->num.type = op_type; | ||
761 | arg->num.left = larg; | ||
762 | arg->num.right = rarg; | ||
763 | break; | ||
764 | } | 568 | } |
765 | cont: | 569 | |
766 | *parg = arg; | 570 | return 0; |
767 | return type; | 571 | |
572 | out_fail: | ||
573 | show_error(error_str, | ||
574 | "Syntax error"); | ||
575 | return -1; | ||
768 | } | 576 | } |
769 | 577 | ||
770 | /* | 578 | static struct filter_arg * |
771 | * Input: larg | 579 | rotate_op_right(struct filter_arg *a, struct filter_arg *b) |
772 | * Output: parg, tok | ||
773 | */ | ||
774 | static enum event_type | ||
775 | process_exp(struct event_format *event, enum filter_exp_type etype, | ||
776 | struct filter_arg *larg, struct filter_arg **parg, | ||
777 | char **tok, char **error_str) | ||
778 | { | 580 | { |
779 | struct filter_arg *rarg = NULL; | ||
780 | struct filter_arg *arg; | 581 | struct filter_arg *arg; |
781 | enum event_type orig_type; | ||
782 | enum event_type type; | ||
783 | 582 | ||
784 | type = process_value(event, &rarg, &orig_type, tok, error_str); | 583 | arg = a->op.right; |
785 | if (type == EVENT_ERROR) { | 584 | a->op.right = b; |
786 | free_arg(rarg); | 585 | return arg; |
787 | return type; | 586 | } |
788 | } | ||
789 | 587 | ||
790 | /* larg can be NULL if a field did not match */ | 588 | static int add_left(struct filter_arg *op, struct filter_arg *arg) |
791 | if (!larg) { | 589 | { |
792 | /* syntax is correct, just return NULL */ | 590 | switch (op->type) { |
793 | arg = NULL; | 591 | case FILTER_ARG_EXP: |
794 | free_arg(rarg); | 592 | if (arg->type == FILTER_ARG_OP) |
795 | goto cont; | 593 | arg = rotate_op_right(arg, op); |
796 | } | 594 | op->exp.left = arg; |
595 | break; | ||
797 | 596 | ||
798 | arg = allocate_arg(); | 597 | case FILTER_ARG_OP: |
799 | arg->type = FILTER_ARG_EXP; | 598 | op->op.left = arg; |
800 | arg->exp.type = etype; | 599 | break; |
801 | arg->exp.left = larg; | 600 | case FILTER_ARG_NUM: |
802 | arg->exp.right = rarg; | 601 | if (arg->type == FILTER_ARG_OP) |
602 | arg = rotate_op_right(arg, op); | ||
803 | 603 | ||
804 | cont: | 604 | /* left arg of compares must be a field */ |
805 | /* still need a cmp */ | 605 | if (arg->type != FILTER_ARG_FIELD && |
806 | type = process_op_token(event, arg, parg, type, tok, error_str); | 606 | arg->type != FILTER_ARG_BOOLEAN) |
807 | return type; | 607 | return -1; |
608 | op->num.left = arg; | ||
609 | break; | ||
610 | default: | ||
611 | return -1; | ||
612 | } | ||
613 | return 0; | ||
808 | } | 614 | } |
809 | 615 | ||
810 | /* | 616 | enum op_type { |
811 | * Input: tok | 617 | OP_NONE, |
812 | * Output: parg, tok | 618 | OP_BOOL, |
813 | */ | 619 | OP_NOT, |
814 | static enum event_type | 620 | OP_EXP, |
815 | process_op_token(struct event_format *event, struct filter_arg *larg, | 621 | OP_CMP, |
816 | struct filter_arg **parg, enum event_type type, char **tok, | 622 | }; |
817 | char **error_str) | 623 | |
624 | static enum op_type process_op(const char *token, | ||
625 | enum filter_op_type *btype, | ||
626 | enum filter_cmp_type *ctype, | ||
627 | enum filter_exp_type *etype) | ||
818 | { | 628 | { |
819 | enum filter_cmp_type ctype; | 629 | *btype = FILTER_OP_NOT; |
820 | enum filter_exp_type etype = FILTER_EXP_NONE; | 630 | *etype = FILTER_EXP_NONE; |
821 | char *token; | 631 | *ctype = FILTER_CMP_NONE; |
822 | 632 | ||
823 | token = *tok; | 633 | if (strcmp(token, "&&") == 0) |
824 | *parg = NULL; | 634 | *btype = FILTER_OP_AND; |
635 | else if (strcmp(token, "||") == 0) | ||
636 | *btype = FILTER_OP_OR; | ||
637 | else if (strcmp(token, "!") == 0) | ||
638 | return OP_NOT; | ||
825 | 639 | ||
826 | if (type != EVENT_OP) { | 640 | if (*btype != FILTER_OP_NOT) |
827 | *parg = larg; | 641 | return OP_BOOL; |
828 | return type; | ||
829 | } | ||
830 | |||
831 | if (strcmp(token, "&&") == 0 || strcmp(token, "||") == 0) { | ||
832 | /* handle boolean cases */ | ||
833 | return process_bool(event, larg, parg, tok, error_str); | ||
834 | } | ||
835 | 642 | ||
836 | /* Check for value expressions */ | 643 | /* Check for value expressions */ |
837 | if (strcmp(token, "+") == 0) { | 644 | if (strcmp(token, "+") == 0) { |
838 | etype = FILTER_EXP_ADD; | 645 | *etype = FILTER_EXP_ADD; |
839 | } else if (strcmp(token, "-") == 0) { | 646 | } else if (strcmp(token, "-") == 0) { |
840 | etype = FILTER_EXP_SUB; | 647 | *etype = FILTER_EXP_SUB; |
841 | } else if (strcmp(token, "*") == 0) { | 648 | } else if (strcmp(token, "*") == 0) { |
842 | etype = FILTER_EXP_MUL; | 649 | *etype = FILTER_EXP_MUL; |
843 | } else if (strcmp(token, "/") == 0) { | 650 | } else if (strcmp(token, "/") == 0) { |
844 | etype = FILTER_EXP_DIV; | 651 | *etype = FILTER_EXP_DIV; |
845 | } else if (strcmp(token, "%") == 0) { | 652 | } else if (strcmp(token, "%") == 0) { |
846 | etype = FILTER_EXP_MOD; | 653 | *etype = FILTER_EXP_MOD; |
847 | } else if (strcmp(token, ">>") == 0) { | 654 | } else if (strcmp(token, ">>") == 0) { |
848 | etype = FILTER_EXP_RSHIFT; | 655 | *etype = FILTER_EXP_RSHIFT; |
849 | } else if (strcmp(token, "<<") == 0) { | 656 | } else if (strcmp(token, "<<") == 0) { |
850 | etype = FILTER_EXP_LSHIFT; | 657 | *etype = FILTER_EXP_LSHIFT; |
851 | } else if (strcmp(token, "&") == 0) { | 658 | } else if (strcmp(token, "&") == 0) { |
852 | etype = FILTER_EXP_AND; | 659 | *etype = FILTER_EXP_AND; |
853 | } else if (strcmp(token, "|") == 0) { | 660 | } else if (strcmp(token, "|") == 0) { |
854 | etype = FILTER_EXP_OR; | 661 | *etype = FILTER_EXP_OR; |
855 | } else if (strcmp(token, "^") == 0) { | 662 | } else if (strcmp(token, "^") == 0) { |
856 | etype = FILTER_EXP_XOR; | 663 | *etype = FILTER_EXP_XOR; |
857 | } else if (strcmp(token, "~") == 0) | 664 | } else if (strcmp(token, "~") == 0) |
858 | etype = FILTER_EXP_NOT; | 665 | *etype = FILTER_EXP_NOT; |
666 | |||
667 | if (*etype != FILTER_EXP_NONE) | ||
668 | return OP_EXP; | ||
669 | |||
670 | /* Check for compares */ | ||
671 | if (strcmp(token, "==") == 0) | ||
672 | *ctype = FILTER_CMP_EQ; | ||
673 | else if (strcmp(token, "!=") == 0) | ||
674 | *ctype = FILTER_CMP_NE; | ||
675 | else if (strcmp(token, "<") == 0) | ||
676 | *ctype = FILTER_CMP_LT; | ||
677 | else if (strcmp(token, ">") == 0) | ||
678 | *ctype = FILTER_CMP_GT; | ||
679 | else if (strcmp(token, "<=") == 0) | ||
680 | *ctype = FILTER_CMP_LE; | ||
681 | else if (strcmp(token, ">=") == 0) | ||
682 | *ctype = FILTER_CMP_GE; | ||
683 | else if (strcmp(token, "=~") == 0) | ||
684 | *ctype = FILTER_CMP_REGEX; | ||
685 | else if (strcmp(token, "!~") == 0) | ||
686 | *ctype = FILTER_CMP_NOT_REGEX; | ||
687 | else | ||
688 | return OP_NONE; | ||
859 | 689 | ||
860 | if (etype != FILTER_EXP_NONE) { | 690 | return OP_CMP; |
861 | free_token(token); | 691 | } |
862 | return process_exp(event, etype, larg, parg, tok, error_str); | ||
863 | } | ||
864 | 692 | ||
865 | if (strcmp(token, "==") == 0) { | 693 | static int check_op_done(struct filter_arg *arg) |
866 | ctype = FILTER_CMP_EQ; | 694 | { |
867 | } else if (strcmp(token, "!=") == 0) { | 695 | switch (arg->type) { |
868 | ctype = FILTER_CMP_NE; | 696 | case FILTER_ARG_EXP: |
869 | } else if (strcmp(token, "<") == 0) { | 697 | return arg->exp.right != NULL; |
870 | ctype = FILTER_CMP_LT; | ||
871 | } else if (strcmp(token, ">") == 0) { | ||
872 | ctype = FILTER_CMP_GT; | ||
873 | } else if (strcmp(token, "<=") == 0) { | ||
874 | ctype = FILTER_CMP_LE; | ||
875 | } else if (strcmp(token, ">=") == 0) { | ||
876 | ctype = FILTER_CMP_GE; | ||
877 | } else if (strcmp(token, "=~") == 0) { | ||
878 | ctype = FILTER_CMP_REGEX; | ||
879 | } else if (strcmp(token, "!~") == 0) { | ||
880 | ctype = FILTER_CMP_NOT_REGEX; | ||
881 | } else { | ||
882 | show_error(error_str, | ||
883 | "Unknown op '%s'", token); | ||
884 | free_token(token); | ||
885 | return EVENT_ERROR; | ||
886 | } | ||
887 | 698 | ||
888 | free_token(token); | 699 | case FILTER_ARG_OP: |
889 | *tok = NULL; | 700 | return arg->op.right != NULL; |
890 | return process_cmp(event, ctype, larg, parg, tok, error_str); | 701 | |
702 | case FILTER_ARG_NUM: | ||
703 | return arg->num.right != NULL; | ||
704 | |||
705 | case FILTER_ARG_STR: | ||
706 | /* A string conversion is always done */ | ||
707 | return 1; | ||
708 | |||
709 | case FILTER_ARG_BOOLEAN: | ||
710 | /* field not found, is ok */ | ||
711 | return 1; | ||
712 | |||
713 | default: | ||
714 | return 0; | ||
715 | } | ||
891 | } | 716 | } |
892 | 717 | ||
893 | static enum event_type | 718 | enum filter_vals { |
894 | process_filter(struct event_format *event, struct filter_arg **parg, | 719 | FILTER_VAL_NORM, |
895 | char **tok, char **error_str) | 720 | FILTER_VAL_FALSE, |
896 | { | 721 | FILTER_VAL_TRUE, |
897 | struct filter_arg *larg = NULL; | 722 | }; |
898 | enum event_type type; | ||
899 | 723 | ||
900 | *parg = NULL; | 724 | void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, |
901 | *tok = NULL; | 725 | struct filter_arg *arg) |
726 | { | ||
727 | struct filter_arg *other_child; | ||
728 | struct filter_arg **ptr; | ||
729 | |||
730 | if (parent->type != FILTER_ARG_OP && | ||
731 | arg->type != FILTER_ARG_OP) | ||
732 | die("can not reparent other than OP"); | ||
733 | |||
734 | /* Get the sibling */ | ||
735 | if (old_child->op.right == arg) { | ||
736 | ptr = &old_child->op.right; | ||
737 | other_child = old_child->op.left; | ||
738 | } else if (old_child->op.left == arg) { | ||
739 | ptr = &old_child->op.left; | ||
740 | other_child = old_child->op.right; | ||
741 | } else | ||
742 | die("Error in reparent op, find other child"); | ||
902 | 743 | ||
903 | type = process_token(event, parg, tok, error_str); | 744 | /* Detach arg from old_child */ |
745 | *ptr = NULL; | ||
904 | 746 | ||
905 | if (type == EVENT_OP && | 747 | /* Check for root */ |
906 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | 748 | if (parent == old_child) { |
907 | larg = *parg; | 749 | free_arg(other_child); |
908 | *parg = NULL; | 750 | *parent = *arg; |
909 | type = process_bool(event, larg, parg, tok, error_str); | 751 | /* Free arg without recussion */ |
752 | free(arg); | ||
753 | return; | ||
910 | } | 754 | } |
911 | 755 | ||
912 | return type; | 756 | if (parent->op.right == old_child) |
757 | ptr = &parent->op.right; | ||
758 | else if (parent->op.left == old_child) | ||
759 | ptr = &parent->op.left; | ||
760 | else | ||
761 | die("Error in reparent op"); | ||
762 | *ptr = arg; | ||
763 | |||
764 | free_arg(old_child); | ||
913 | } | 765 | } |
914 | 766 | ||
915 | static enum event_type | 767 | enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) |
916 | process_paren(struct event_format *event, struct filter_arg **parg, | ||
917 | char **tok, char **error_str) | ||
918 | { | 768 | { |
919 | struct filter_arg *arg; | 769 | enum filter_vals lval, rval; |
920 | enum event_type type; | ||
921 | 770 | ||
922 | *parg = NULL; | 771 | switch (arg->type) { |
923 | 772 | ||
924 | type = process_token(event, &arg, tok, error_str); | 773 | /* bad case */ |
925 | if (type == EVENT_ERROR) { | 774 | case FILTER_ARG_BOOLEAN: |
926 | free_arg(arg); | 775 | return FILTER_VAL_FALSE + arg->bool.value; |
927 | return type; | ||
928 | } | ||
929 | 776 | ||
930 | if (type == EVENT_OP && | 777 | /* good cases: */ |
931 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | 778 | case FILTER_ARG_STR: |
932 | type = process_bool(event, arg, parg, tok, error_str); | 779 | case FILTER_ARG_VALUE: |
933 | } | 780 | case FILTER_ARG_FIELD: |
781 | return FILTER_VAL_NORM; | ||
934 | 782 | ||
935 | if (type != EVENT_DELIM || strcmp(*tok, ")") != 0) { | 783 | case FILTER_ARG_EXP: |
936 | if (*tok) | 784 | lval = test_arg(arg, arg->exp.left); |
937 | show_error(error_str, | 785 | if (lval != FILTER_VAL_NORM) |
938 | "Expected ')' but found %s", *tok); | 786 | return lval; |
939 | else | 787 | rval = test_arg(arg, arg->exp.right); |
940 | show_error(error_str, | 788 | if (rval != FILTER_VAL_NORM) |
941 | "Unexpected end of filter; Expected ')'"); | 789 | return rval; |
942 | free_token(*tok); | 790 | return FILTER_VAL_NORM; |
943 | *tok = NULL; | ||
944 | free_arg(arg); | ||
945 | return EVENT_ERROR; | ||
946 | } | ||
947 | free_token(*tok); | ||
948 | *tok = NULL; | ||
949 | 791 | ||
950 | *parg = arg; | 792 | case FILTER_ARG_NUM: |
793 | lval = test_arg(arg, arg->num.left); | ||
794 | if (lval != FILTER_VAL_NORM) | ||
795 | return lval; | ||
796 | rval = test_arg(arg, arg->num.right); | ||
797 | if (rval != FILTER_VAL_NORM) | ||
798 | return rval; | ||
799 | return FILTER_VAL_NORM; | ||
800 | |||
801 | case FILTER_ARG_OP: | ||
802 | if (arg->op.type != FILTER_OP_NOT) { | ||
803 | lval = test_arg(arg, arg->op.left); | ||
804 | switch (lval) { | ||
805 | case FILTER_VAL_NORM: | ||
806 | break; | ||
807 | case FILTER_VAL_TRUE: | ||
808 | if (arg->op.type == FILTER_OP_OR) | ||
809 | return FILTER_VAL_TRUE; | ||
810 | rval = test_arg(arg, arg->op.right); | ||
811 | if (rval != FILTER_VAL_NORM) | ||
812 | return rval; | ||
813 | |||
814 | reparent_op_arg(parent, arg, arg->op.right); | ||
815 | return FILTER_VAL_NORM; | ||
816 | |||
817 | case FILTER_VAL_FALSE: | ||
818 | if (arg->op.type == FILTER_OP_AND) | ||
819 | return FILTER_VAL_FALSE; | ||
820 | rval = test_arg(arg, arg->op.right); | ||
821 | if (rval != FILTER_VAL_NORM) | ||
822 | return rval; | ||
823 | |||
824 | reparent_op_arg(parent, arg, arg->op.right); | ||
825 | return FILTER_VAL_NORM; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | rval = test_arg(arg, arg->op.right); | ||
830 | switch (rval) { | ||
831 | case FILTER_VAL_NORM: | ||
832 | break; | ||
833 | case FILTER_VAL_TRUE: | ||
834 | if (arg->op.type == FILTER_OP_OR) | ||
835 | return FILTER_VAL_TRUE; | ||
836 | if (arg->op.type == FILTER_OP_NOT) | ||
837 | return FILTER_VAL_FALSE; | ||
838 | |||
839 | reparent_op_arg(parent, arg, arg->op.left); | ||
840 | return FILTER_VAL_NORM; | ||
841 | |||
842 | case FILTER_VAL_FALSE: | ||
843 | if (arg->op.type == FILTER_OP_AND) | ||
844 | return FILTER_VAL_FALSE; | ||
845 | if (arg->op.type == FILTER_OP_NOT) | ||
846 | return FILTER_VAL_TRUE; | ||
847 | |||
848 | reparent_op_arg(parent, arg, arg->op.left); | ||
849 | return FILTER_VAL_NORM; | ||
850 | } | ||
951 | 851 | ||
952 | return read_token(tok); | 852 | return FILTER_VAL_NORM; |
853 | default: | ||
854 | die("bad arg in filter tree"); | ||
855 | } | ||
856 | return FILTER_VAL_NORM; | ||
953 | } | 857 | } |
954 | 858 | ||
955 | static enum event_type | 859 | /* Remove any unknown event fields */ |
956 | process_not(struct event_format *event, struct filter_arg **parg, | 860 | static struct filter_arg *collapse_tree(struct filter_arg *arg) |
957 | char **tok, char **error_str) | ||
958 | { | 861 | { |
959 | struct filter_arg *arg; | 862 | enum filter_vals ret; |
960 | enum event_type type; | ||
961 | 863 | ||
962 | arg = allocate_arg(); | 864 | ret = test_arg(arg, arg); |
963 | arg->type = FILTER_ARG_OP; | 865 | switch (ret) { |
964 | arg->op.type = FILTER_OP_NOT; | 866 | case FILTER_VAL_NORM: |
867 | return arg; | ||
965 | 868 | ||
966 | arg->op.left = NULL; | 869 | case FILTER_VAL_TRUE: |
967 | type = process_token(event, &arg->op.right, tok, error_str); | 870 | case FILTER_VAL_FALSE: |
968 | if (type == EVENT_ERROR) { | ||
969 | free_arg(arg); | 871 | free_arg(arg); |
970 | *parg = NULL; | 872 | arg = allocate_arg(); |
971 | free_token(*tok); | ||
972 | *tok = NULL; | ||
973 | return EVENT_ERROR; | ||
974 | } | ||
975 | /* If the bool value is NULL, then make this into TRUE */ | ||
976 | if (!arg->op.right) { | ||
977 | arg->type = FILTER_ARG_BOOLEAN; | 873 | arg->type = FILTER_ARG_BOOLEAN; |
978 | arg->bool.value = FILTER_TRUE; | 874 | arg->bool.value = ret == FILTER_VAL_TRUE; |
979 | } | 875 | } |
980 | 876 | ||
981 | *parg = arg; | 877 | return arg; |
982 | free_token(*tok); | 878 | } |
983 | *tok = NULL; | ||
984 | 879 | ||
985 | return type; | 880 | static int |
881 | process_filter(struct event_format *event, struct filter_arg **parg, | ||
882 | char **error_str, int not) | ||
883 | { | ||
884 | enum event_type type; | ||
885 | char *token = NULL; | ||
886 | struct filter_arg *current_op = NULL; | ||
887 | struct filter_arg *current_exp = NULL; | ||
888 | struct filter_arg *left_item = NULL; | ||
889 | struct filter_arg *arg = NULL; | ||
890 | enum op_type op_type; | ||
891 | enum filter_op_type btype; | ||
892 | enum filter_exp_type etype; | ||
893 | enum filter_cmp_type ctype; | ||
894 | int ret; | ||
895 | |||
896 | *parg = NULL; | ||
897 | |||
898 | do { | ||
899 | free(token); | ||
900 | type = read_token(&token); | ||
901 | switch (type) { | ||
902 | case EVENT_SQUOTE: | ||
903 | case EVENT_DQUOTE: | ||
904 | case EVENT_ITEM: | ||
905 | arg = create_arg_item(event, token, type, error_str); | ||
906 | if (!arg) | ||
907 | goto fail; | ||
908 | if (!left_item) | ||
909 | left_item = arg; | ||
910 | else if (current_exp) { | ||
911 | ret = add_right(current_exp, arg, error_str); | ||
912 | if (ret < 0) | ||
913 | goto fail; | ||
914 | left_item = NULL; | ||
915 | /* Not's only one one expression */ | ||
916 | if (not) { | ||
917 | arg = NULL; | ||
918 | if (current_op) | ||
919 | goto fail_print; | ||
920 | free(token); | ||
921 | *parg = current_exp; | ||
922 | return 0; | ||
923 | } | ||
924 | } else | ||
925 | goto fail_print; | ||
926 | arg = NULL; | ||
927 | break; | ||
928 | |||
929 | case EVENT_DELIM: | ||
930 | if (*token == ',') { | ||
931 | show_error(error_str, | ||
932 | "Illegal token ','"); | ||
933 | goto fail; | ||
934 | } | ||
935 | |||
936 | if (*token == '(') { | ||
937 | if (left_item) { | ||
938 | show_error(error_str, | ||
939 | "Open paren can not come after item"); | ||
940 | goto fail; | ||
941 | } | ||
942 | if (current_exp) { | ||
943 | show_error(error_str, | ||
944 | "Open paren can not come after expression"); | ||
945 | goto fail; | ||
946 | } | ||
947 | |||
948 | ret = process_filter(event, &arg, error_str, 0); | ||
949 | if (ret != 1) { | ||
950 | if (ret == 0) | ||
951 | show_error(error_str, | ||
952 | "Unbalanced number of '('"); | ||
953 | goto fail; | ||
954 | } | ||
955 | ret = 0; | ||
956 | |||
957 | /* A not wants just one expression */ | ||
958 | if (not) { | ||
959 | if (current_op) | ||
960 | goto fail_print; | ||
961 | *parg = arg; | ||
962 | return 0; | ||
963 | } | ||
964 | |||
965 | if (current_op) | ||
966 | ret = add_right(current_op, arg, error_str); | ||
967 | else | ||
968 | current_exp = arg; | ||
969 | |||
970 | if (ret < 0) | ||
971 | goto fail; | ||
972 | |||
973 | } else { /* ')' */ | ||
974 | if (!current_op && !current_exp) | ||
975 | goto fail_print; | ||
976 | |||
977 | /* Make sure everything is finished at this level */ | ||
978 | if (current_exp && !check_op_done(current_exp)) | ||
979 | goto fail_print; | ||
980 | if (current_op && !check_op_done(current_op)) | ||
981 | goto fail_print; | ||
982 | |||
983 | if (current_op) | ||
984 | *parg = current_op; | ||
985 | else | ||
986 | *parg = current_exp; | ||
987 | return 1; | ||
988 | } | ||
989 | break; | ||
990 | |||
991 | case EVENT_OP: | ||
992 | op_type = process_op(token, &btype, &ctype, &etype); | ||
993 | |||
994 | /* All expect a left arg except for NOT */ | ||
995 | switch (op_type) { | ||
996 | case OP_BOOL: | ||
997 | /* Logic ops need a left expression */ | ||
998 | if (!current_exp && !current_op) | ||
999 | goto fail_print; | ||
1000 | /* fall through */ | ||
1001 | case OP_NOT: | ||
1002 | /* logic only processes ops and exp */ | ||
1003 | if (left_item) | ||
1004 | goto fail_print; | ||
1005 | break; | ||
1006 | case OP_EXP: | ||
1007 | case OP_CMP: | ||
1008 | if (!left_item) | ||
1009 | goto fail_print; | ||
1010 | break; | ||
1011 | case OP_NONE: | ||
1012 | show_error(error_str, | ||
1013 | "Unknown op token %s", token); | ||
1014 | goto fail; | ||
1015 | } | ||
1016 | |||
1017 | ret = 0; | ||
1018 | switch (op_type) { | ||
1019 | case OP_BOOL: | ||
1020 | arg = create_arg_op(btype); | ||
1021 | if (current_op) | ||
1022 | ret = add_left(arg, current_op); | ||
1023 | else | ||
1024 | ret = add_left(arg, current_exp); | ||
1025 | current_op = arg; | ||
1026 | current_exp = NULL; | ||
1027 | break; | ||
1028 | |||
1029 | case OP_NOT: | ||
1030 | arg = create_arg_op(btype); | ||
1031 | if (current_op) | ||
1032 | ret = add_right(current_op, arg, error_str); | ||
1033 | if (ret < 0) | ||
1034 | goto fail; | ||
1035 | current_exp = arg; | ||
1036 | ret = process_filter(event, &arg, error_str, 1); | ||
1037 | if (ret < 0) | ||
1038 | goto fail; | ||
1039 | ret = add_right(current_exp, arg, error_str); | ||
1040 | if (ret < 0) | ||
1041 | goto fail; | ||
1042 | break; | ||
1043 | |||
1044 | case OP_EXP: | ||
1045 | case OP_CMP: | ||
1046 | if (op_type == OP_EXP) | ||
1047 | arg = create_arg_exp(etype); | ||
1048 | else | ||
1049 | arg = create_arg_cmp(ctype); | ||
1050 | |||
1051 | if (current_op) | ||
1052 | ret = add_right(current_op, arg, error_str); | ||
1053 | if (ret < 0) | ||
1054 | goto fail; | ||
1055 | ret = add_left(arg, left_item); | ||
1056 | if (ret < 0) { | ||
1057 | arg = NULL; | ||
1058 | goto fail_print; | ||
1059 | } | ||
1060 | current_exp = arg; | ||
1061 | break; | ||
1062 | default: | ||
1063 | break; | ||
1064 | } | ||
1065 | arg = NULL; | ||
1066 | if (ret < 0) | ||
1067 | goto fail_print; | ||
1068 | break; | ||
1069 | case EVENT_NONE: | ||
1070 | break; | ||
1071 | default: | ||
1072 | goto fail_print; | ||
1073 | } | ||
1074 | } while (type != EVENT_NONE); | ||
1075 | |||
1076 | if (!current_op && !current_exp) | ||
1077 | goto fail_print; | ||
1078 | |||
1079 | if (!current_op) | ||
1080 | current_op = current_exp; | ||
1081 | |||
1082 | current_op = collapse_tree(current_op); | ||
1083 | |||
1084 | *parg = current_op; | ||
1085 | |||
1086 | return 0; | ||
1087 | |||
1088 | fail_print: | ||
1089 | show_error(error_str, "Syntax error"); | ||
1090 | fail: | ||
1091 | free_arg(current_op); | ||
1092 | free_arg(current_exp); | ||
1093 | free_arg(arg); | ||
1094 | free(token); | ||
1095 | return -1; | ||
986 | } | 1096 | } |
987 | 1097 | ||
988 | static int | 1098 | static int |
989 | process_event(struct event_format *event, const char *filter_str, | 1099 | process_event(struct event_format *event, const char *filter_str, |
990 | struct filter_arg **parg, char **error_str) | 1100 | struct filter_arg **parg, char **error_str) |
991 | { | 1101 | { |
992 | enum event_type type; | 1102 | int ret; |
993 | char *token; | ||
994 | 1103 | ||
995 | pevent_buffer_init(filter_str, strlen(filter_str)); | 1104 | pevent_buffer_init(filter_str, strlen(filter_str)); |
996 | 1105 | ||
997 | type = process_filter(event, parg, &token, error_str); | 1106 | ret = process_filter(event, parg, error_str, 0); |
998 | 1107 | if (ret == 1) { | |
999 | if (type == EVENT_ERROR) | ||
1000 | return -1; | ||
1001 | |||
1002 | if (type != EVENT_NONE) { | ||
1003 | show_error(error_str, | 1108 | show_error(error_str, |
1004 | "Expected end where %s was found", | 1109 | "Unbalanced number of ')'"); |
1005 | token); | ||
1006 | free_token(token); | ||
1007 | free_arg(*parg); | ||
1008 | *parg = NULL; | ||
1009 | return -1; | 1110 | return -1; |
1010 | } | 1111 | } |
1112 | if (ret < 0) | ||
1113 | return ret; | ||
1011 | 1114 | ||
1012 | /* If parg is NULL, then make it into FALSE */ | 1115 | /* If parg is NULL, then make it into FALSE */ |
1013 | if (!*parg) { | 1116 | if (!*parg) { |
@@ -1031,6 +1134,7 @@ static int filter_event(struct event_filter *filter, | |||
1031 | ret = process_event(event, filter_str, &arg, error_str); | 1134 | ret = process_event(event, filter_str, &arg, error_str); |
1032 | if (ret < 0) | 1135 | if (ret < 0) |
1033 | return ret; | 1136 | return ret; |
1137 | |||
1034 | } else { | 1138 | } else { |
1035 | /* just add a TRUE arg */ | 1139 | /* just add a TRUE arg */ |
1036 | arg = allocate_arg(); | 1140 | arg = allocate_arg(); |