* basic_filters.c (lookup_class, qualify_syscall_class, qualify_syscall,
qualify_syscall_tokens, parse_syscall_filter, qualify_tokens,
parse_fd_filter, parse_path_filter): Add qualify_mode argument.
(qualify_tokens, qualify_syscall_tokens): Use set inversion only in
qualify mode.
(lookup_class): Use deprecated class names only in qualify mode.
* defs.h (qualify): Remove declaration.
(filtering_parse): Add new declaration.
* filter.c (struct filter_type, parse_filter):
Add bool argument to parse_*_filter declarations.
* filter.h (parse_filter_action, parse_qualify_action,
parse_filter_expression): Add new declarations.
(parse_filter, qualify_tokens, qualify_syscall_tokens):
Add qualify_mode argument.
(DECL_FILTER): Add bool argument to parse_*_filter declarations.
* filter_action.c (parse_filter_action): Add new parsing function.
(inject_path_tracing): Use filtering_parse instead of qualify.
* filter_expression.c (parse_filter_expression): Implement parsing of filter
expression.
(parse_operator, push_operator, is_higher_priority): Add helper functions.
(is_space_ascii, is_allowed_in_name): Add new declarations.
* filter_parse.c: New file.
* filter_qualify.c (qualify_read, qualify_write, qualify_signals,
qualify_trace, qualify_abbrev, qualify_verbose, qualify_raw,
qualify_inject_common, qualify_fault, qualify_inject): Use main_part and args
arguments.
* strace.c (init): Use filtering_parse instead of qualify.
* Makefile.am (strace_SOURCES): Add filter_parse.c.
---
 Makefile.am         |   1 +
 basic_filters.c     |  67 ++++++++------
 defs.h              |   2 +-
 filter.c            |   6 +-
 filter.h            |  14 ++-
 filter_action.c     |  15 +++-
 filter_expression.c | 188 +++++++++++++++++++++++++++++++++++++++
 filter_parse.c      | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 filter_qualify.c    |  94 ++++++++++++--------
 strace.c            |   6 +-
 10 files changed, 567 insertions(+), 78 deletions(-)
 create mode 100644 filter_parse.c

diff --git a/Makefile.am b/Makefile.am
index b928eb28..81a46616 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -136,6 +136,7 @@ strace_SOURCES =    \
        file_ioctl.c    \
        filter_action.c \
        filter_expression.c \
+       filter_parse.c  \
        filter_qualify.c \
        filter.c        \
        filter.h        \
diff --git a/basic_filters.c b/basic_filters.c
index 48851076..217cac74 100644
--- a/basic_filters.c
+++ b/basic_filters.c
@@ -96,7 +96,7 @@ qualify_syscall_regex(const char *s, struct number_set *set)
 }
 
 static unsigned int
-lookup_class(const char *s)
+lookup_class(const char *s, bool qualify_mode)
 {
        static const struct {
                const char *name;
@@ -127,6 +127,8 @@ lookup_class(const char *s)
 
        unsigned int i;
        for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
+               if (!qualify_mode && *s != '%')
+                       continue;
                if (strcmp(s, syscall_class[i].name) == 0) {
                        return syscall_class[i].value;
                }
@@ -136,9 +138,9 @@ lookup_class(const char *s)
 }
 
 static bool
-qualify_syscall_class(const char *s, struct number_set *set)
+qualify_syscall_class(const char *s, struct number_set *set, bool qualify_mode)
 {
-       const unsigned int n = lookup_class(s);
+       const unsigned int n = lookup_class(s, qualify_mode);
        if (!n)
                return false;
 
@@ -181,7 +183,7 @@ qualify_syscall_name(const char *s, struct number_set *set)
 }
 
 static bool
-qualify_syscall(const char *token, struct number_set *set)
+qualify_syscall(const char *token, struct number_set *set, bool qualify_mode)
 {
        bool ignore_fail = false;
 
@@ -193,7 +195,7 @@ qualify_syscall(const char *token, struct number_set *set)
                return qualify_syscall_number(token, set) || ignore_fail;
        if (*token == '/')
                return qualify_syscall_regex(token + 1, set) || ignore_fail;
-       return qualify_syscall_class(token, set)
+       return qualify_syscall_class(token, set, qualify_mode)
               || qualify_syscall_name(token, set)
               || ignore_fail;
 }
@@ -204,7 +206,7 @@ qualify_syscall(const char *token, struct number_set *set)
  */
 void
 qualify_syscall_tokens(const char *const str, struct number_set *const set,
-                      const char *const name)
+                      bool qualify_mode)
 {
        /* Clear all sets. */
        clear_number_set_array(set, SUPPORTED_PERSONALITIES);
@@ -214,10 +216,15 @@ qualify_syscall_tokens(const char *const str, struct 
number_set *const set,
         * of the remaining specification.
         */
        const char *s = str;
-handle_inversion:
-       while (*s == '!') {
-               invert_number_set_array(set, SUPPORTED_PERSONALITIES);
-               ++s;
+       if (qualify_mode) {
+               /*
+                * Each leading ! character means inversion
+                * of the remaining specification.
+                */
+               while (*s == '!') {
+                       invert_number_set_array(set, SUPPORTED_PERSONALITIES);
+                       ++s;
+               }
        }
 
        if (strcmp(s, "none") == 0) {
@@ -228,8 +235,8 @@ handle_inversion:
                 */
                return;
        } else if (strcmp(s, "all") == 0) {
-               s = "!none";
-               goto handle_inversion;
+               invert_number_set_array(set, SUPPORTED_PERSONALITIES);
+               return;
        }
 
        /*
@@ -246,26 +253,26 @@ handle_inversion:
 
        for (token = strtok_r(copy, ",", &saveptr); token;
             token = strtok_r(NULL, ",", &saveptr)) {
-               done = qualify_syscall(token, set);
+               done = qualify_syscall(token, set, qualify_mode);
                if (!done) {
-                       error_msg_and_die("invalid %s '%s'", name, token);
+                       error_msg_and_die("invalid system call '%s'", token);
                }
        }
 
        free(copy);
 
        if (!done) {
-               error_msg_and_die("invalid %s '%s'", name, str);
+               error_msg_and_die("invalid system call '%s'", str);
        }
 }
 
 void *
-parse_syscall_filter(const char *str)
+parse_syscall_filter(const char *str, bool qualify_mode)
 {
        struct number_set *set;
 
        set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
-       qualify_syscall_tokens(str, set, "system call");
+       qualify_syscall_tokens(str, set, qualify_mode);
        return set;
 }
 
@@ -290,7 +297,8 @@ free_syscall_filter(void *priv_data)
  */
 void
 qualify_tokens(const char *const str, struct number_set *const set,
-              string_to_uint_func func, const char *const name)
+              string_to_uint_func func, const char *const name,
+              bool qualify_mode)
 {
        /* Clear the set. */
        clear_number_set_array(set, 1);
@@ -300,10 +308,15 @@ qualify_tokens(const char *const str, struct number_set 
*const set,
         * of the remaining specification.
         */
        const char *s = str;
-handle_inversion:
-       while (*s == '!') {
-               invert_number_set_array(set, 1);
-               ++s;
+       if (qualify_mode) {
+               /*
+                * Each leading ! character means inversion
+                * of the remaining specification.
+                */
+               while (*s == '!') {
+                       invert_number_set_array(set, 1);
+                       ++s;
+               }
        }
 
        if (strcmp(s, "none") == 0) {
@@ -314,8 +327,8 @@ handle_inversion:
                 */
                return;
        } else if (strcmp(s, "all") == 0) {
-               s = "!none";
-               goto handle_inversion;
+               invert_number_set_array(set, 1);
+               return;
        }
 
        /*
@@ -348,12 +361,12 @@ handle_inversion:
 }
 
 void *
-parse_fd_filter(const char *str)
+parse_fd_filter(const char *str, bool qualify_mode)
 {
        struct number_set *set;
 
        set = alloc_number_set_array(1);
-       qualify_tokens(str, set, string_to_uint, "descriptor");
+       qualify_tokens(str, set, string_to_uint, "descriptor", qualify_mode);
        return set;
 }
 
@@ -394,7 +407,7 @@ free_fd_filter(void *priv_data)
 }
 
 void *
-parse_path_filter(const char *path)
+parse_path_filter(const char *path, bool qualify_mode)
 {
        struct path_set *set = xcalloc(1, sizeof(struct path_set));
 
diff --git a/defs.h b/defs.h
index c03010b5..7789e293 100644
--- a/defs.h
+++ b/defs.h
@@ -651,7 +651,7 @@ print_struct_statfs64(struct tcb *, kernel_ulong_t addr, 
kernel_ulong_t size);
 
 extern void print_ifindex(unsigned int);
 
-extern void qualify(const char *);
+extern void filtering_parse(const char *);
 extern void filtering_parsing_finish(void);
 extern void filter_syscall(struct tcb *);
 
diff --git a/filter.c b/filter.c
index 9b996cf5..bd100355 100644
--- a/filter.c
+++ b/filter.c
@@ -35,7 +35,7 @@
 
 static const struct filter_type {
        const char *name;
-       void *(*parse_filter)(const char *);
+       void *(*parse_filter)(const char *, bool);
        bool (*run_filter)(struct tcb *, void *);
        void (*free_priv_data)(void *);
 } filter_types[] = {
@@ -79,9 +79,9 @@ add_filter_to_array(struct filter **filters, unsigned int 
*nfilters,
 }
 
 void
-parse_filter(struct filter *filter, const char *str)
+parse_filter(struct filter *filter, const char *str, bool qualify_mode)
 {
-       filter->priv_data = filter->type->parse_filter(str);
+       filter->priv_data = filter->type->parse_filter(str, qualify_mode);
 }
 
 static bool
diff --git a/filter.h b/filter.h
index 1d8c5f7a..249a2a93 100644
--- a/filter.h
+++ b/filter.h
@@ -40,9 +40,10 @@ struct bool_expression;
 typedef int (*string_to_uint_func)(const char *);
 
 void qualify_tokens(const char *str, struct number_set *set,
-                   string_to_uint_func func, const char *name);
+                   string_to_uint_func func, const char *name,
+                   bool qualify_mode);
 void qualify_syscall_tokens(const char *str, struct number_set *set,
-                           const char *name);
+                           bool qualify_mode);
 void parse_inject_common_args(char *, struct inject_opts *,
                              const bool fault_tokens_only, bool qualify_mode);
 bool is_traced(struct tcb *);
@@ -54,7 +55,7 @@ int match_fd_common(struct tcb *, match_fd_func, void *);
 /* filter api */
 struct filter* add_filter_to_array(struct filter **, unsigned int *nfilters,
                                   const char *name);
-void parse_filter(struct filter *, const char *str);
+void parse_filter(struct filter *, const char *str, bool qualify_mode);
 void run_filters(struct tcb *, struct filter *, unsigned int, bool *);
 void free_filter(struct filter *);
 void set_filters_qualify_mode(struct filter **, unsigned int *nfilters,
@@ -64,6 +65,7 @@ void set_filter_priv_data(struct filter *, void *);
 /* filter action api */
 struct filter *create_filter(struct filter_action *, const char *name);
 struct filter_action *find_or_add_action(const char *);
+void parse_filter_action(const char *, const char *, const char *);
 void set_qualify_mode(struct filter_action *, unsigned int);
 void set_filter_action_priv_data(struct filter_action *, void *);
 
@@ -72,10 +74,14 @@ struct bool_expression *create_expression();
 bool run_expression(struct bool_expression *, bool *, unsigned int);
 void set_expression_qualify_mode(struct bool_expression *, unsigned int);
 void expression_add_filter_and(struct bool_expression *, unsigned int);
+void parse_filter_expression(struct bool_expression *, const char *,
+                             struct filter_action *, unsigned int);
+
+void parse_qualify_action(const char *, const char *, const char *);
 
 #define DECL_FILTER(name)                                              \
 extern void *                                                          \
-parse_ ## name ## _filter(const char *);                               \
+parse_ ## name ## _filter(const char *, bool);                         \
 extern bool                                                            \
 run_ ## name ## _filter(struct tcb *, void *);                         \
 extern void                                                            \
diff --git a/filter_action.c b/filter_action.c
index 0a0b72f2..fea3e877 100644
--- a/filter_action.c
+++ b/filter_action.c
@@ -94,7 +94,7 @@ inject_path_tracing(void)
        struct filter *path_filter;
 
        if (!action->nfilters)
-               qualify("trace=all");
+               filtering_parse("trace=all");
        path_filter = add_filter_to_array(&action->filters, &action->nfilters,
                                          "path");
        set_filter_priv_data(path_filter, &global_path_set);
@@ -175,6 +175,19 @@ find_or_add_action(const char *name)
        return add_action(type);
 }
 
+void
+parse_filter_action(const char *action_name, const char *expr, const char 
*args)
+{
+       struct filter_action *action = find_or_add_action(action_name);
+
+       parse_filter_expression(action->expr, expr, action, action->nfilters);
+       if (args && action->type->parse_args == &parse_null)
+               error_msg("%s action takes no arguments, ignored arguments "
+                         "'%s'", action->type->name, args);
+       action->priv_data = action->type->parse_args(args);
+}
+
+
 static void
 run_filter_action(struct tcb *tcp, struct filter_action *action)
 {
diff --git a/filter_expression.c b/filter_expression.c
index 73aaae3a..98dbbcc5 100644
--- a/filter_expression.c
+++ b/filter_expression.c
@@ -29,6 +29,9 @@
 #include <stdarg.h>
 #include "filter.h"
 
+extern bool is_space_ascii(char);
+extern bool is_allowed_in_name(char);
+
 struct expression_token {
        enum token_type {
                TOK_VARIABLE,
@@ -44,6 +47,9 @@ struct expression_token {
        } data;
 };
 
+/* Pseudo-operator used for parsing */
+#define OP_PARENTHESIS 3
+
 struct bool_expression {
        unsigned int ntokens;
        struct expression_token *tokens;
@@ -279,3 +285,185 @@ run_expression(struct bool_expression *expr, bool 
*variables,
                                            variables, variables_num);
        return stack[0];
 }
+
+/*
+ * Parse operator and add operator length to str and pos.
+ * Return -1 if no operator found.
+ */
+static int
+parse_operator(char **str, unsigned int *pos)
+{
+#define _OP(s, op) { s, sizeof(s) - 1, op }
+       struct {
+               const char *str;
+               int len;
+               enum operator_type op;
+       } ops[] = {
+               _OP("!",        OP_NOT),
+               _OP("not",      OP_NOT),
+               _OP("&&",       OP_AND),
+               _OP("and",      OP_AND),
+               _OP("||",       OP_OR),
+               _OP("or",       OP_OR),
+       };
+#undef _OP
+       char *p = *str;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(ops); i++) {
+               if (!strncmp(p, ops[i].str, ops[i].len) &&
+                   (!is_allowed_in_name(ops[i].str[0]) ||
+                   !is_allowed_in_name(p[ops[i].len]))) {
+                       *str += ops[i].len - 1;
+                       *pos += ops[i].len - 1;
+                       return ops[i].op;
+               }
+       }
+       return -1;
+}
+
+static char *
+unescape_argument(char **str)
+{
+       char *p;
+       char *p_new;
+       bool escaped = false;
+       unsigned int size = 1;
+       char *new_str = xcalloc(strlen(*str) + 1, 1);
+
+       for (p = *str, p_new = new_str; *p; ++p) {
+               if (!escaped) {
+                       if (*p == '\\') {
+                               escaped = true;
+                               continue;
+                       } else if (is_space_ascii(*p) || *p == ')' || *p == '|'
+                                  || *p == '&') {
+                               break;
+                       }
+               }
+               escaped = false;
+               *(p_new++) = *p;
+               size++;
+       }
+       *str = p - 1;
+       return xreallocarray(new_str, size, 1);
+}
+
+static void
+push_operator(int *stack, unsigned int *stack_size, int op)
+{
+       if (*stack_size == MAX_STACK_SIZE)
+               error_msg_and_die("stack overflow (expression is too complex)");
+       stack[*stack_size] = op;
+       (*stack_size)++;
+}
+
+static bool
+is_higher_priority(int op_a, int op_b)
+{
+       bool op_priority[] = {
+               [OP_NOT] = 2,
+               [OP_AND] = 1,
+               [OP_OR]  = 0,
+       };
+       return op_priority[op_a] > op_priority[op_b];
+}
+
+void
+parse_filter_expression(struct bool_expression *expr, const char *str,
+                       struct filter_action *action, unsigned int start_id)
+{
+       enum {
+               WAIT_FILTER,
+               FILTER_NAME,
+               FILTER_ARG,
+               WAIT_OPERATOR,
+       } state = WAIT_FILTER;
+       unsigned int variable_id = start_id;
+       /* Current stack stack_size */
+       unsigned int st_size = 0;
+       int stack[MAX_STACK_SIZE];
+       char *buf = xstrdup(str);
+       struct filter *cur_filter = NULL;
+       char *filter_name = NULL;
+       char *filter_arg = NULL;
+       int op;
+       char *p;
+       unsigned int pos = 0;
+
+       for (p = buf; *p; ++p, ++pos) {
+               switch (state) {
+               case WAIT_FILTER:
+                       if (*p == '(') {
+                               push_operator(stack, &st_size, OP_PARENTHESIS);
+                       } else if ((op = parse_operator(&p, &pos)) >= 0) {
+                               if (op == OP_NOT) {
+                                       push_operator(stack, &st_size, op);
+                               } else {
+                                       error_msg_and_die("invalid operator "
+                                                         "at '%s':%u",
+                                                         str, pos);
+                               }
+                       } else if (!is_space_ascii(*p)) {
+                               filter_name = p;
+                               state = FILTER_NAME;
+                       }
+                       break;
+
+               case FILTER_NAME:
+                       if (is_space_ascii(*p)) {
+                               *p = '\0';
+                               cur_filter = create_filter(action, filter_name);
+                               filter_arg = NULL;
+                               state = FILTER_ARG;
+                       }
+                       break;
+
+               case FILTER_ARG:
+                       if (!filter_arg && is_space_ascii(*p))
+                               break;
+                       filter_arg = unescape_argument(&p);
+                       parse_filter(cur_filter, filter_arg, false);
+                       free(filter_arg);
+                       add_variable_token(expr, variable_id++);
+                       state = WAIT_OPERATOR;
+                       break;
+
+               case WAIT_OPERATOR:
+                       if (is_space_ascii(*p))
+                               break;
+                       if (*p == ')') {
+                               while ((st_size > 0) &&
+                                      (stack[st_size - 1] != OP_PARENTHESIS)) {
+                                               op = stack[--st_size];
+                                               add_operator_token(expr, op);
+                                       }
+                               --st_size;
+                               break;
+                       }
+                       op = parse_operator(&p, &pos);
+                       if (op < 0 || op == OP_NOT)
+                               error_msg_and_die("invalid operator at '%s':%u",
+                                                 str, pos);
+
+                       /* Pop operators with higher priority. */
+                       while ((st_size > 0) &&
+                              (stack[st_size - 1] != OP_PARENTHESIS) &&
+                              is_higher_priority(stack[st_size - 1], op))
+                               add_operator_token(expr, stack[--st_size]);
+
+                       push_operator(stack, &st_size, op);
+                       state = WAIT_FILTER;
+                       break;
+               }
+       }
+
+       free(buf);
+       if (state != WAIT_OPERATOR)
+               error_msg_and_die("unfinished filter expression '%s'", str);
+
+       while (st_size > 0)
+               add_operator_token(expr, stack[--st_size]);
+       if (start_id > 0)
+               add_operator_token(expr, OP_OR);
+}
diff --git a/filter_parse.c b/filter_parse.c
new file mode 100644
index 00000000..e0fd8e58
--- /dev/null
+++ b/filter_parse.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2017 Nikolay Marchuk <marchuk.nikola...@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+#include "filter.h"
+
+bool
+is_space_ascii(char c)
+{
+       return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') ||
+              (c == '\v') || (c == '\f');
+}
+
+bool
+is_allowed_in_name(char c)
+{
+       return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+              || (c >= '0' && c <= '9') || (c == '_');
+}
+
+/*
+ * Split expression into action name, filter expression or qualify set
+ * and action arguments.
+ */
+void
+filtering_parse(const char *str)
+{
+       enum parsing_states {
+               F_EMPTY,
+               F_BEGIN,
+               F_QUAL_SET,
+               F_FILT_EXPR,
+               F_QUAL_ARGS,
+               F_FILT_ARGS,
+               F_END,
+       } state = F_EMPTY;
+       const char *begin = NULL;
+       const char *action_name = "trace";
+       const char *main_part = NULL;
+       const char *args = NULL;
+       bool action_specified = false;
+       bool escaped = false;
+       int parentheses_count = 0;
+       /* Used to store position of last terminating parenthesis. */
+       char *expression_end = NULL;
+       /* Used to provide diagnostics. */
+       unsigned int pos = 0;
+       char *buf = xstrdup(str);
+       char *p;
+
+       for (p = buf; *p; ++p, ++pos) {
+               switch (state) {
+               case F_EMPTY:
+                       switch (*p) {
+                       /* trace(), action name omitted */
+                       case '(':
+                               parentheses_count++;
+                               main_part = p;
+                               state = F_FILT_EXPR;
+                               break;
+                       /* missing action name */
+                       case '=':
+                               *p = '\0';
+                               error_msg_and_die("invalid filter action '%s'",
+                                                 buf);
+                       default:
+                               if (is_space_ascii(*p)) {
+                                       break;
+                               } else if (!strncmp(p, "not", 3) && *(p + 3) &&
+                                          (is_space_ascii(*(p + 3)) ||
+                                          *(p + 3) == '(')) {
+                                       main_part = p;
+                                       state = F_FILT_EXPR;
+                                       break;
+                               } else {
+                                       begin = p;
+                                       state = F_BEGIN;
+                               }
+                       }
+                       if (state != F_BEGIN)
+                               break;
+                       /* else fall through to check for qualify set */
+
+               case F_BEGIN:
+                       switch (*p) {
+                       /* action(...) */
+                       case '(':
+                               if (*begin == '!') {
+                                       main_part = begin;
+                               } else {
+                                       action_name = begin;
+                                       action_specified = true;
+                                       *p = '\0';
+                                       main_part = p + 1;
+                               }
+                               state = F_FILT_EXPR;
+                               parentheses_count++;
+                               break;
+                       /* action=... */
+                       case '=':
+                               action_name = begin;
+                               action_specified = true;
+                               *p = '\0';
+                               main_part = p + 1;
+                               state = F_QUAL_SET;
+                               break;
+                       case ':':
+                               main_part = begin;
+                               *p = '\0';
+                               args = p + 1;
+                               state = F_QUAL_ARGS;
+                               break;
+                       case ';':
+                               error_msg_and_die("invalid arguments position "
+                                                 "'%s':%u",
+                                                 str, pos);
+                       /* qualify set without action. */
+                       case ',':
+                       case '?':
+                       case '/':
+                       case '%':
+                       case '-':
+                               main_part = begin;
+                               state = F_QUAL_SET;
+                               break;
+                       default:
+                               /* new expression without action. */
+                               if (is_space_ascii(*p)) {
+                                       main_part = begin;
+                                       state = F_FILT_EXPR;
+                               }
+                       }
+                       break;
+
+               case F_QUAL_SET:
+                       if (*p == ':') {
+                               *p = '\0';
+                               args = p + 1;
+                               state = F_QUAL_ARGS;
+                       }
+                       break;
+
+               case F_FILT_EXPR:
+                       if (!escaped) {
+                               switch (*p) {
+                               case ';':
+                                       if (parentheses_count != 1 ||
+                                           !action_specified)
+                                               error_msg_and_die("invalid "
+                                                                 "arguments "
+                                                                 "position "
+                                                                 "'%s':%u",
+                                                                 str, pos);
+                                       *p = '\0';
+                                       args = p + 1;
+                                       state = F_FILT_ARGS;
+                                       break;
+                               case '(':
+                                       parentheses_count++;
+                                       break;
+                               case ')':
+                                       if (parentheses_count <= 0)
+                                               error_msg_and_die("unexpected "
+                                                                 "')' at "
+                                                                 "'%s':%u",
+                                                                 str, pos);
+                                       parentheses_count--;
+                                       expression_end = p;
+                                       if (action_specified &&
+                                           parentheses_count == 0)
+                                               state = F_END;
+                                       break;
+                               case '\\':
+                                       escaped = true;
+                                       break;
+                               }
+                       } else
+                               escaped = false;
+                       break;
+
+               case F_QUAL_ARGS:
+                       break;
+               case F_FILT_ARGS:
+                       if (!escaped) {
+                               switch (*p) {
+                               case ')':
+                                       parentheses_count--;
+                                       expression_end = p;
+                                       state = F_END;
+                                       break;
+                               case '\\':
+                                       escaped = true;
+                                       break;
+                               }
+                       } else
+                               escaped = false;
+                       break;
+               case F_END:
+                       if (!is_space_ascii(*p))
+                               error_msg_and_die("unexpected '%c' at "
+                                                 "'%s':%u", *p, str, pos);
+               }
+       }
+
+       switch (state) {
+       case F_EMPTY:
+               main_part = buf;
+               parse_qualify_action(action_name, main_part, args);
+               break;
+       case F_BEGIN:
+               main_part = begin;
+               /* fall through */
+       case F_QUAL_SET:
+       case F_QUAL_ARGS:
+               parse_qualify_action(action_name, main_part, args);
+               break;
+       case F_FILT_EXPR:
+       case F_FILT_ARGS:
+       case F_END:
+               if (parentheses_count != 0)
+                       error_msg_and_die("missing ')' in '%s'", str);
+               if (action_specified && expression_end)
+                       *expression_end = '\0';
+               parse_filter_action(action_name, main_part, args);
+               break;
+       }
+       free(buf);
+}
diff --git a/filter_qualify.c b/filter_qualify.c
index 18efdd9d..5e06de1f 100644
--- a/filter_qualify.c
+++ b/filter_qualify.c
@@ -175,92 +175,110 @@ parse_inject_common_args(char *str, struct inject_opts 
*const opts,
 }
 
 static void
-qualify_read(const char *const str)
+qualify_read(const char *const main_part, const char *const args)
 {
        struct filter_action *action = find_or_add_action("read");
        struct filter *filter = create_filter(action, "fd");
 
-       parse_filter(filter, str);
+       parse_filter(filter, main_part, true);
+       if (args)
+               error_msg("read action takes no arguments, ignored arguments "
+                         "'%s'", args);
        set_qualify_mode(action, 1);
 }
 
 static void
-qualify_write(const char *const str)
+qualify_write(const char *const main_part, const char *const args)
 {
        struct filter_action *action = find_or_add_action("write");
        struct filter *filter = create_filter(action, "fd");
 
-       parse_filter(filter, str);
+       parse_filter(filter, main_part, true);
+       if (args)
+               error_msg("write action takes no arguments, ignored arguments "
+                         "'%s'", args);
        set_qualify_mode(action, 1);
 }
 
 static void
-qualify_signals(const char *const str)
+qualify_signals(const char *const main_part, const char *const args)
 {
        if (!signal_set)
                signal_set = alloc_number_set_array(1);
-       qualify_tokens(str, signal_set, sigstr_to_uint, "signal");
+
+       qualify_tokens(main_part, signal_set, sigstr_to_uint, "signal", true);
+       if (args)
+               error_msg("signal action takes no arguments, ignored arguments "
+                         "'%s'", args);
 }
 
 static void
-qualify_trace(const char *const str)
+qualify_trace(const char *const main_part, const char *const args)
 {
        struct filter_action *action = find_or_add_action("trace");
        struct filter *filter = create_filter(action, "syscall");
 
-       parse_filter(filter, str);
+       parse_filter(filter, main_part, true);
+       if (args)
+               error_msg("trace action takes no arguments, ignored arguments "
+                         "'%s'", args);
        set_qualify_mode(action, 1);
 }
 
 static void
-qualify_abbrev(const char *const str)
+qualify_abbrev(const char *const main_part, const char *const args)
 {
        struct filter_action *action = find_or_add_action("abbrev");
        struct filter *filter = create_filter(action, "syscall");
 
-       parse_filter(filter, str);
+       parse_filter(filter, main_part, true);
+       if (args)
+               error_msg("abbrev action takes no arguments, ignored arguments "
+                         "'%s'", args);
        set_qualify_mode(action, 1);
 }
 
 static void
-qualify_verbose(const char *const str)
+qualify_verbose(const char *const main_part, const char *const args)
 {
        struct filter_action *action = find_or_add_action("verbose");
        struct filter *filter = create_filter(action, "syscall");
 
-       parse_filter(filter, str);
+       parse_filter(filter, main_part, true);
+       if (args)
+               error_msg("verbose action takes no arguments, ignored arguments"
+                         " '%s'", args);
        set_qualify_mode(action, 1);
 }
 
 static void
-qualify_raw(const char *const str)
+qualify_raw(const char *const main_part, const char *const args)
 {
        struct filter_action *action = find_or_add_action("raw");
        struct filter *filter = create_filter(action, "syscall");
 
-       parse_filter(filter, str);
+       parse_filter(filter, main_part, true);
+       if (args)
+               error_msg("raw action takes no arguments, ignored arguments "
+                         "'%s'", args);
        set_qualify_mode(action, 1);
 }
 
 static void
-qualify_inject_common(const char *const str,
+qualify_inject_common(const char *const main_part, const char *const args,
                      const bool fault_tokens_only,
                      const char *const description)
 {
        struct inject_opts *opts = xmalloc(sizeof(struct inject_opts));
-       char *buf = xstrdup(str);
-       char *args = strchr(buf, ':');
+       char *buf = xstrdup(args);
        struct filter_action *action;
        struct filter *filter;
 
-       if (args)
-               *(args++) = '\0';
-
        action = find_or_add_action(fault_tokens_only ? "fault" : "inject");
        filter = create_filter(action, "syscall");
-       parse_filter(filter, buf);
+       parse_filter(filter, main_part, true);
        set_qualify_mode(action, 1);
-       parse_inject_common_args(args, opts, fault_tokens_only, true);
+       parse_inject_common_args(buf, opts, fault_tokens_only, true);
 
        if (!opts->init)
                error_msg_and_die("invalid %s argument '%s'", description,
@@ -271,20 +289,20 @@ qualify_inject_common(const char *const str,
 }
 
 static void
-qualify_fault(const char *const str)
+qualify_fault(const char *const main_part, const char *const args)
 {
-       qualify_inject_common(str, true, "fault");
+       qualify_inject_common(main_part, args, true, "fault");
 }
 
 static void
-qualify_inject(const char *const str)
+qualify_inject(const char *const main_part, const char *const args)
 {
-       qualify_inject_common(str, false, "inject");
+       qualify_inject_common(main_part, args, false, "inject");
 }
 
 static const struct qual_options {
        const char *name;
-       void (*qualify)(const char *);
+       void (*qualify)(const char *, const char *);
 } qual_options[] = {
        { "trace",      qualify_trace   },
        { "t",          qualify_trace   },
@@ -308,22 +326,20 @@ static const struct qual_options {
 };
 
 void
-qualify(const char *str)
+parse_qualify_action(const char *action_name, const char *main_part,
+                    const char *args)
 {
-       const struct qual_options *opt = qual_options;
+       const struct qual_options *opt = NULL;
        unsigned int i;
 
        for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
-               const char *name = qual_options[i].name;
-               const size_t len = strlen(name);
-               const char *val = str_strip_prefix_len(str, name, len);
-
-               if (val == str || *val != '=')
-                       continue;
-               str = val + 1;
-               opt = &qual_options[i];
-               break;
+               if (!strcmp(action_name, qual_options[i].name)) {
+                       opt = &qual_options[i];
+                       break;
+               }
        }
 
-       opt->qualify(str);
+       if (!opt)
+               error_msg_and_die("invalid filter action '%s'", action_name);
+       opt->qualify(main_part ? main_part : "", args);
 }
diff --git a/strace.c b/strace.c
index d2fececa..08cdb2f6 100644
--- a/strace.c
+++ b/strace.c
@@ -1580,7 +1580,7 @@ init(int argc, char *argv[])
 #if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
 # error Bug in DEFAULT_QUAL_FLAGS
 #endif
-       qualify("signal=all");
+       filtering_parse("signal=all");
        while ((c = getopt(argc, argv,
                "+b:cCdfFhiqrtTvVwxyz"
 #ifdef USE_LIBUNWIND
@@ -1647,7 +1647,7 @@ init(int argc, char *argv[])
                        show_fd_path++;
                        break;
                case 'v':
-                       qualify("abbrev=none");
+                       filtering_parse("abbrev=none");
                        break;
                case 'V':
                        print_version();
@@ -1662,7 +1662,7 @@ init(int argc, char *argv[])
                                error_opt_arg(c, optarg);
                        break;
                case 'e':
-                       qualify(optarg);
+                       filtering_parse(optarg);
                        break;
                case 'o':
                        outfname = optarg;
-- 
2.11.0


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Strace-devel mailing list
Strace-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/strace-devel

Reply via email to