[PATCH xserver] Multiple matching modes (incl. regex) selection support in Match* statements

Peter Hutterer peter.hutterer at who-t.net
Thu Jun 2 23:30:26 PDT 2011


On Thu, Jun 02, 2011 at 05:53:14PM +0300, Oleh Nykyforchyn wrote:
> Please consider a final version of the patch.
> 
> -------------------------------
> 
> Multiple matching modes selection (incl. regex) support in Match* statements
> 
> Any number of patterns can be written in one line either as
>       MatchProduct "foo|!bar" "something|re:^else$" "you" "!regex:n..d"
> or as
>       MatchProduct "foo" "!bar|something" "regex:^else$" "you|!re:n..d"
> etc. A regex ends at the end of the respective "piece", irrespective of '|'s.
> 
> Regexes are prefixed by "regex:" of "re:", prefixes are also added for all
> other matching modes in two versions each: short and real name of the used
> function. Negated conditions are prefixed by "!", which can be combined with
> each other prefix. Different modes, as well as usual and negated patterns,
> can be mixed in one line.

whoa, this was a bit scary to look at at first :)
a few high-level comments, the patch itself looks sane on a first glance.
- let's just use "regex:", not either of "regex:" or "re:". one is enough.
- I'm not a big fan of allowing a mix of multiple arguments and |. We could
  just require escaping | in a regex so that the result is a
      MatchProduct "foo|!bar|regex:^blah$|regex:a\|b"
  imo this is much easier to read than a multi-argument mix like your two
  examples above.
- I don't think we should expose the multi-match modes. As I said in a
  previous email - if we have true regex support I think we can emulate all
  of them with a regex. fnmatch etc. will still need to be used internally
  for the native matching mode.
- man page additions please

Also, it'd be awesome if you could split the patch up into patches that
change the architecture and patches that then introduce the new
functionality. Makes it much easier to review and to find bugs through
bisection. I can see several separate patches in here - one to switch
over to use lists instead of char**, one to add the negation operator, one
to add regex support.

Cheers,
  Peter

> Signed-off-by: Oleh Nykyforchyn <oleh.nyk at gmail.com>
> ---
>  hw/xfree86/common/xf86Xinput.c |  147 +++++++++------
>  hw/xfree86/parser/InputClass.c |  407 +++++++++++++++++++++++++++-------------
>  hw/xfree86/parser/xf86Parser.h |   30 +++-
>  3 files changed, 396 insertions(+), 188 deletions(-)
> 
> diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
> index 26051ad..3152064 100644
> --- a/hw/xfree86/common/xf86Xinput.c
> +++ b/hw/xfree86/common/xf86Xinput.c
> @@ -443,79 +443,108 @@ HostOS(void)
>  #endif
>  }
>  
> +/*
> + * Match an attribute against a pattern. Matching mode is
> + * determined by pattern->mode member. If the mode is REGEX,
> + * then regex_t is allocated and compiled only during
> + * the first call, to save time and memory.
> + */
>  static int
> -match_substring(const char *attr, const char *pattern)
> +multi_match(const char *attr, xf86MatchPattern *pattern)
>  {
> -    return (strstr(attr, pattern)) ? 0 : -1;
> -}
> -
> -#ifdef HAVE_FNMATCH_H
> -static int
> -match_pattern(const char *attr, const char *pattern)
> -{
> -    return fnmatch(pattern, attr, 0);
> -}
> -#else
> -#define match_pattern match_substring
> -#endif
> +    if (!pattern) return 0;
>  
> +    switch (pattern->mode)
> +    {
> +        case MATCH_IS_INVALID:
> +            return 0;
> +        case MATCH_IS_STRCMP:
> +            return (strcmp(attr, pattern->str)) ? 0 : -1;
> +        case MATCH_IS_STRCASECMP:
> +            return (strcasecmp(attr, pattern->str)) ? 0 : -1;
> +        /*
> +         * If no Layout section is found, xf86ServerLayout.id becomes "(implicit)"
> +         * It is convenient that "" in patterns means "no explicit layout"
> +         */
> +        case MATCH_IS_STRIMPLICIT:
> +            if (*(pattern->str))
> +                return (strcmp(attr, pattern->str)) ? 0 : -1;
> +            else
> +                return (strcmp(attr, "(implicit)")) ? 0 : -1;
> +        case MATCH_IS_STRSTR:
> +            return (strstr(attr, pattern->str)) ? -1 : 0;
> +        case MATCH_IS_STRCASESTR:
> +            return (strcasestr(attr, pattern->str)) ? -1 : 0;
>  #ifdef HAVE_FNMATCH_H
> -static int
> -match_path_pattern(const char *attr, const char *pattern)
> -{
> -    return fnmatch(pattern, attr, FNM_PATHNAME);
> -}
> +        case MATCH_IS_FILENAME:
> +            return (fnmatch(pattern->str, attr, 0)) ? 0 : -1;
> +        case MATCH_IS_PATHNAME:
> +            return (fnmatch(pattern->str, attr, FNM_PATHNAME)) ? 0 : -1;
>  #else
> -#define match_path_pattern match_substring
> +        case MATCH_IS_FILENAME:
> +            return (strstr(attr, pattern->str)) ? -1 : 0;
> +        case MATCH_IS_PATHNAME:
> +            return (strstr(attr, pattern->str)) ? -1 : 0;
>  #endif
> -
> -/*
> - * If no Layout section is found, xf86ServerLayout.id becomes "(implicit)"
> - * It is convenient that "" in patterns means "no explicit layout"
> - */
> -static int
> -match_string_implicit(const char *attr, const char *pattern)
> -{
> -    if (strlen(pattern)) {
> -        return strcmp(attr, pattern);
> -    } else {
> -        return strcmp(attr,"(implicit)");
> +        case MATCH_IS_REGEX:
> +        default:
> +            if (pattern->regex == NULL) {
> +                int r;
> +                if ((pattern->regex = malloc(sizeof(regex_t))) == NULL) {
> +                    pattern->mode = MATCH_IS_INVALID;
> +                    return 0;
> +                }
> +                r = regcomp(pattern->regex, pattern->str, REG_EXTENDED | REG_NOSUB);
> +                if (r) { /* Wrong regex */
> +                    regfree(pattern->regex);
> +                    free(pattern->regex);
> +                    xf86Msg(X_ERROR, "Wrong regex: \"%s\"\n", pattern->str);
> +                    pattern->mode = MATCH_IS_INVALID;
> +                    return 0;
> +                }
> +            }
> +            return (regexec(pattern->regex, attr,0, NULL, 0)) ? 0 : -1;
>      }
>  }
>  
>  /*
> - * Match an attribute against a list of NULL terminated arrays of patterns.
> - * If a pattern in each list entry is matched, return TRUE.
> + * Match an attribute against a list of xf86MatchGroup's.
> + * Return TRUE only if each list entry is successful.
>   */
>  static Bool
> -MatchAttrToken(const char *attr, struct list *patterns,
> -               int (*compare)(const char *attr, const char *pattern))
> +MatchAttrToken(const char *attr, struct list *groups)
>  {
> -    const xf86MatchGroup *group;
> +    xf86MatchGroup *group;
> +    xf86MatchPattern *pattern;
>  
> -    /* If there are no patterns, accept the match */
> -    if (list_is_empty(patterns))
> +    /* If there are no groups, accept the match */
> +    if (list_is_empty(groups))
>          return TRUE;
>  
> -    /* If there are patterns but no attribute, reject the match */
> +    /* If there are groups but no attribute, reject the match */
>      if (!attr)
>          return FALSE;
>  
>      /*
> -     * Otherwise, iterate the list of patterns ensuring each entry has a
> -     * match. Each list entry is a separate Match line of the same type.
> +     * Otherwise, iterate the list of groups ensuring each entry has a
> +     * match. Each list entry is a list of patterns obtained from
> +     * a separate Match line.
>       */
> -    list_for_each_entry(group, patterns, entry) {
> -        char * const *cur;
> +    list_for_each_entry(group, groups, entry) {
>          Bool match = FALSE;
>  
> -        for (cur = group->values; *cur; cur++)
> -            if ((*compare)(attr, *cur) == 0) {
> -                match = TRUE;
> -                break;
> +        list_for_each_entry(pattern, &group->patterns, entry) {
> +            if (multi_match(attr, pattern)) {
> +                if (pattern->is_negated)  /* negated pattern matched */
> +                    return FALSE;         /* failure */
> +                else                      /* appropriate pattern reached */
> +                    return TRUE;          /* success */
>              }
> -        if (!match)
> -            return FALSE;
> +            else if (pattern->is_negated) /* at least negated pattern not */
> +                match = TRUE;             /* matched - partial success */
> +        }
> +        if (!match)          /* nothing successful is this Match line */
> +            return FALSE;    /* failure */
>      }
>  
>      /* All the entries in the list matched the attribute */
> @@ -531,31 +560,31 @@ InputClassMatches(const XF86ConfInputClassPtr iclass, const InputInfoPtr idev,
>                    const InputAttributes *attrs)
>  {
>      /* MatchProduct substring */
> -    if (!MatchAttrToken(attrs->product, &iclass->match_product, match_substring))
> +    if (!MatchAttrToken(attrs->product, &iclass->match_product))
>          return FALSE;
>  
>      /* MatchVendor substring */
> -    if (!MatchAttrToken(attrs->vendor, &iclass->match_vendor, match_substring))
> +    if (!MatchAttrToken(attrs->vendor, &iclass->match_vendor))
>          return FALSE;
>  
>      /* MatchDevicePath pattern */
> -    if (!MatchAttrToken(attrs->device, &iclass->match_device, match_path_pattern))
> +    if (!MatchAttrToken(attrs->device, &iclass->match_device))
>          return FALSE;
>  
>      /* MatchOS case-insensitive string */
> -    if (!MatchAttrToken(HostOS(), &iclass->match_os, strcasecmp))
> +    if (!MatchAttrToken(HostOS(), &iclass->match_os))
>          return FALSE;
>  
>      /* MatchPnPID pattern */
> -    if (!MatchAttrToken(attrs->pnp_id, &iclass->match_pnpid, match_pattern))
> +    if (!MatchAttrToken(attrs->pnp_id, &iclass->match_pnpid))
>          return FALSE;
>  
>      /* MatchUSBID pattern */
> -    if (!MatchAttrToken(attrs->usb_id, &iclass->match_usbid, match_pattern))
> +    if (!MatchAttrToken(attrs->usb_id, &iclass->match_usbid))
>          return FALSE;
>  
>      /* MatchDriver string */
> -    if (!MatchAttrToken(idev->driver, &iclass->match_driver, strcmp))
> +    if (!MatchAttrToken(idev->driver, &iclass->match_driver))
>          return FALSE;
>  
>      /*
> @@ -569,7 +598,7 @@ InputClassMatches(const XF86ConfInputClassPtr iclass, const InputInfoPtr idev,
>          if (!attrs->tags)
>              return FALSE;
>          for (tag = attrs->tags, match = FALSE; *tag; tag++) {
> -            if (MatchAttrToken(*tag, &iclass->match_tag, strcmp)) {
> +            if (MatchAttrToken(*tag, &iclass->match_tag)) {
>                  match = TRUE;
>                  break;
>              }
> @@ -579,9 +608,7 @@ InputClassMatches(const XF86ConfInputClassPtr iclass, const InputInfoPtr idev,
>      }
>  
>      /* MatchLayout string */
> -    if (!list_is_empty(&iclass->match_layout)) {
> -        if (!MatchAttrToken(xf86ConfigLayout.id,
> -                            &iclass->match_layout, match_string_implicit))
> +    if (!MatchAttrToken(xf86ConfigLayout.id, &iclass->match_layout))
>              return FALSE;
>      }
>  
> diff --git a/hw/xfree86/parser/InputClass.c b/hw/xfree86/parser/InputClass.c
> index 3f80170..b8500a8 100644
> --- a/hw/xfree86/parser/InputClass.c
> +++ b/hw/xfree86/parser/InputClass.c
> @@ -64,25 +64,113 @@ xf86ConfigSymTabRec InputClassTab[] =
>  
>  #define CLEANUP xf86freeInputClassList
>  
> -#define TOKEN_SEP "|"
> +#define TOKEN_SEP '|'
> +
> +#define NEG_FLAG '!'
> +
> +static struct
> +{
> +    const char *prefix;
> +    xf86MatchMode mode;
> +}
> +mode_prefixes[] = {
> +    { "regex:",       MATCH_IS_REGEX },       /* regular expression */
> +    { "re:",          MATCH_IS_REGEX },
> +    { "str:",         MATCH_IS_STRCMP },      /* strings coincide */
> +    { "strcmp:",      MATCH_IS_STRCMP },
> +    { "cistr:",       MATCH_IS_STRCASECMP },  /* the same, but case insensitive */
> +    { "strcasecmp:",  MATCH_IS_STRCASECMP },
> +    { "stri:",        MATCH_IS_STRIMPLICIT }, /* "" matches "(implicit)" layout */
> +    { "strcmpimpl:",  MATCH_IS_STRIMPLICIT },
> +    { "sub:",         MATCH_IS_STRSTR },      /* contains a substring */
> +    { "strstr:",      MATCH_IS_STRSTR },
> +    { "cisub:",       MATCH_IS_STRCASESTR },  /* the same, but case insensitive */
> +    { "strcasestr:",  MATCH_IS_STRCASESTR },
> +    { "file:",        MATCH_IS_FILENAME },    /* match a shell wildcard pattern */
> +    { "fnmatch:",     MATCH_IS_FILENAME },
> +    { "path:",        MATCH_IS_PATHNAME },    /* the same, but '/' treated separately */
> +    { "pnmatch:",     MATCH_IS_PATHNAME },
> +    {  NULL,          MATCH_IS_INVALID }      /* sentinel */
> +};
>  
>  static void
> -add_group_entry(struct list *head, char **values)
> +add_to_group_entry(xf86MatchGroup **group, const char *str,
> +                   xf86MatchMode pref_mode)
>  {
> -    xf86MatchGroup *group;
> +    xf86MatchPattern *pattern;
> +    unsigned n;
> +    char *next;
> +
> +    if (!str) return;
> +
> +    if (!*group) {
> +        *group = malloc(sizeof(**group));
> +        if (!*group) return;
> +        list_init(&(*group)->patterns);
> +    }
> +
> +  again:
> +    /* start new pattern: not negated, MATCH_IS_AUTO */
> +    if ((pattern = calloc(sizeof(*pattern),1)) == NULL)
> +        return;
> +    list_add(&pattern->entry, &(*group)->patterns);
> +
> +    /* Pattern starting with '!' should NOT be matched */
> +    if (*str == NEG_FLAG) {
> +        pattern->is_negated = TRUE;
> +        str++;
> +    }
> +
> +    /* Check if there is a mode prefix */
> +    for (n=0; mode_prefixes[n].prefix; n++) {
> +        if (!strncmp(str, mode_prefixes[n].prefix,
> +                     strlen(mode_prefixes[n].prefix))) {
> +            pattern->mode = mode_prefixes[n].mode;
> +            str += strlen(mode_prefixes[n].prefix);
> +            break;
> +        }
> +    }
> +
> +    if (pattern->mode == MATCH_IS_AUTO)
> +        pattern->mode = pref_mode;
> +
> +    if ((next = index(str, TOKEN_SEP)))
> +        n = next-str;
> +    else
> +        n = strlen(str);
> +
> +    if ((pattern->str = strndup(str, n)) == NULL) {
> +        pattern->mode = MATCH_IS_INVALID;
> +    }
>  
> -    group = malloc(sizeof(*group));
> -    if (group) {
> -        group->values = values;
> -        list_add(&group->entry, head);
> +    if (next) {
> +        str = next+1;
> +        goto again;
>      }
>  }
>  
> +static void
> +free_group(xf86MatchGroup *group)
> +{
> +    xf86MatchPattern *pattern, *next_pattern;
> +    list_for_each_entry_safe(pattern, next_pattern, &group->patterns, entry) {
> +        list_del(&pattern->entry);
> +        if (pattern->str) free(pattern->str);
> +        if ((pattern->mode == MATCH_IS_REGEX) && pattern->regex) {
> +            regfree(pattern->regex);
> +            free(pattern->regex);
> +        }
> +        free(pattern);
> +    }
> +    free(group);
> +}
> +
>  XF86ConfInputClassPtr
>  xf86parseInputClassSection(void)
>  {
>      int has_ident = FALSE;
>      int token;
> +    xf86MatchGroup *group;
>  
>      parsePrologue(XF86ConfInputClassPtr, XF86ConfInputClassRec)
>  
> @@ -98,6 +186,7 @@ xf86parseInputClassSection(void)
>      list_init(&ptr->match_layout);
>  
>      while ((token = xf86getToken(InputClassTab)) != ENDSECTION) {
> +        group = NULL;
>          switch (token) {
>          case COMMENT:
>              ptr->comment = xf86addComment(ptr->comment, val.str);
> @@ -123,60 +212,110 @@ xf86parseInputClassSection(void)
>          case OPTION:
>              ptr->option_lst = xf86parseOption(ptr->option_lst);
>              break;
> +        /*
> +         * Argument of each Match{Product,..,Layout} statement
> +         * can be a sequence of >=1 \"-quoted and space separated
> +         * strings, which in turn are divided further by '|' chars
> +         * to make a final list of patterns, unless a regular
> +         * expression lasts until the closing \" of the current
> +         * string, irrespective of '|'s. Hence
> +         *     MatchProduct "foo" "bar" "regex:^Yes|No$" "$100"
> +         *     MatchProduct "foo" "bar|regex:^Yes|No$" "$100"
> +         * and
> +         *     MatchProduct "foo|bar" "regex:^Yes|No$" "$100"
> +         * are equivalent, but not
> +         *     MatchProduct "foo" "bar" "regex:^Yes|No$|$100"
> +         */
>          case MATCH_PRODUCT:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_STRSTR);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_product);
> +            else
>                  Error(QUOTE_MSG, "MatchProduct");
> -            add_group_entry(&ptr->match_product,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_VENDOR:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_STRSTR);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_vendor);
> +            else
>                  Error(QUOTE_MSG, "MatchVendor");
> -            add_group_entry(&ptr->match_vendor,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_DEVICE_PATH:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_PATHNAME);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_device);
> +            else
>                  Error(QUOTE_MSG, "MatchDevicePath");
> -            add_group_entry(&ptr->match_device,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_OS:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_STRCASECMP);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_os);
> +            else
>                  Error(QUOTE_MSG, "MatchOS");
> -            add_group_entry(&ptr->match_os,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_PNPID:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_FILENAME);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_pnpid);
> +            else
>                  Error(QUOTE_MSG, "MatchPnPID");
> -            add_group_entry(&ptr->match_pnpid,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_USBID:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_FILENAME);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_usbid);
> +            else
>                  Error(QUOTE_MSG, "MatchUSBID");
> -            add_group_entry(&ptr->match_usbid,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_DRIVER:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_STRCMP);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_driver);
> +            else
>                  Error(QUOTE_MSG, "MatchDriver");
> -            add_group_entry(&ptr->match_driver,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_TAG:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_STRCMP);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_tag);
> +            else
>                  Error(QUOTE_MSG, "MatchTag");
> -            add_group_entry(&ptr->match_tag,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_LAYOUT:
> -            if (xf86getSubToken(&(ptr->comment)) != STRING)
> +            while ((token=xf86getSubTokenWithTab(&(ptr->comment),InputClassTab)) == STRING) {
> +                add_to_group_entry(&group, val.str, MATCH_IS_STRIMPLICIT);
> +            }
> +            xf86unGetToken(token);
> +            if (group)
> +                list_add(&group->entry, &ptr->match_layout);
> +            else
>                  Error(QUOTE_MSG, "MatchLayout");
> -            add_group_entry(&ptr->match_layout,
> -                            xstrtokenize(val.str, TOKEN_SEP));
> -            break;
> +            continue;
>          case MATCH_IS_KEYBOARD:
>              if (xf86getSubToken(&(ptr->comment)) != STRING)
>                  Error(QUOTE_MSG, "MatchIsKeyboard");
> @@ -244,11 +383,35 @@ xf86parseInputClassSection(void)
>      return ptr;
>  }
>  
> +static void
> +print_pattern(FILE * cf, const xf86MatchPattern *pattern)
> +{
> +    if (!pattern) return;
> +
> +    fprintf(cf, "\"");
> +    if (pattern->is_negated) fprintf(cf, "%c", NEG_FLAG);
> +    if (pattern->mode == MATCH_IS_INVALID)
> +        fprintf(cf, "invalid:");
> +    else {
> +        unsigned n;
> +        for (n=0; mode_prefixes[n].prefix; n++) {
> +            if (mode_prefixes[n].mode == pattern->mode) {
> +                fprintf(cf, "%s", mode_prefixes[n].prefix);
> +                break;
> +            }
> +        }
> +    }
> +    if (pattern->str)
> +        fprintf(cf, "%s\"", pattern->str);
> +    else
> +        fprintf(cf, "(none)\"");
> +}
> +
>  void
>  xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr)
>  {
>      const xf86MatchGroup *group;
> -    char * const *cur;
> +    const xf86MatchPattern *pattern;
>  
>      while (ptr) {
>          fprintf(cf, "Section \"InputClass\"\n");
> @@ -260,67 +423,76 @@ xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr)
>              fprintf(cf, "\tDriver          \"%s\"\n", ptr->driver);
>  
>          list_for_each_entry(group, &ptr->match_product, entry) {
> -            fprintf(cf, "\tMatchProduct    \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchProduct    ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_vendor, entry) {
> -            fprintf(cf, "\tMatchVendor     \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchVendor     ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_device, entry) {
> -            fprintf(cf, "\tMatchDevicePath \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchDevicePath ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_os, entry) {
> -            fprintf(cf, "\tMatchOS         \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchOS         ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_pnpid, entry) {
> -            fprintf(cf, "\tMatchPnPID      \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchPnPID      ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_usbid, entry) {
> -            fprintf(cf, "\tMatchUSBID      \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchUSBID      ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_driver, entry) {
> -            fprintf(cf, "\tMatchDriver     \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchDriver     ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_tag, entry) {
> -            fprintf(cf, "\tMatchTag        \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchTag        ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>          list_for_each_entry(group, &ptr->match_layout, entry) {
> -            fprintf(cf, "\tMatchLayout     \"");
> -            for (cur = group->values; *cur; cur++)
> -                fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP,
> -                        *cur);
> -            fprintf(cf, "\"\n");
> +            fprintf(cf, "\tMatchLayout     ");
> +            list_for_each_entry(pattern, &group->patterns, entry) {
> +                fprintf(cf, " ");
> +                print_pattern(cf, pattern);
> +            }
> +            fprintf(cf, "\n");
>          }
>  
>          if (ptr->is_keyboard.set)
> @@ -353,65 +525,46 @@ xf86freeInputClassList (XF86ConfInputClassPtr ptr)
>      XF86ConfInputClassPtr prev;
>  
>      while (ptr) {
> -        xf86MatchGroup *group, *next;
> -        char **list;
> +        xf86MatchGroup *group, *next_group;
>  
>          TestFree(ptr->identifier);
>          TestFree(ptr->driver);
>  
> -        list_for_each_entry_safe(group, next, &ptr->match_product, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_product, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_vendor, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_vendor, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_device, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_device, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_os, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_os, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_pnpid, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_pnpid, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_usbid, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_usbid, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_driver, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_driver, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_tag, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_tag, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
> -        list_for_each_entry_safe(group, next, &ptr->match_layout, entry) {
> +        list_for_each_entry_safe(group, next_group, &ptr->match_layout, entry) {
>              list_del(&group->entry);
> -            for (list = group->values; *list; list++)
> -                free(*list);
> -            free(group);
> +            free_group(group);
>          }
>  
>          TestFree(ptr->comment);
> diff --git a/hw/xfree86/parser/xf86Parser.h b/hw/xfree86/parser/xf86Parser.h
> index a8785c5..0fea7c4 100644
> --- a/hw/xfree86/parser/xf86Parser.h
> +++ b/hw/xfree86/parser/xf86Parser.h
> @@ -68,6 +68,9 @@
>  #include "xf86Optrec.h"
>  #include "list.h"
>  
> +#include <sys/types.h>
> +#include <regex.h>
> +
>  #define HAVE_PARSER_DECLS
>  
>  typedef struct
> @@ -341,10 +344,35 @@ xf86TriState;
>  typedef struct
>  {
>  	struct list entry;
> -	char **values;
> +	struct list patterns;
>  }
>  xf86MatchGroup;
>  
> +typedef enum
> +{
> +	MATCH_IS_INVALID = -1,
> +	MATCH_IS_AUTO = 0,
> +	MATCH_IS_STRCMP,
> +	MATCH_IS_STRCASECMP,
> +	MATCH_IS_STRIMPLICIT,
> +	MATCH_IS_STRSTR,
> +	MATCH_IS_STRCASESTR,
> +	MATCH_IS_FILENAME,
> +	MATCH_IS_PATHNAME,
> +	MATCH_IS_REGEX
> +}
> +xf86MatchMode;
> +
> +typedef struct
> +{
> +	struct list entry;
> +	Bool is_negated;
> +	xf86MatchMode mode;
> +	char *str;
> +	regex_t *regex;
> +}
> +xf86MatchPattern;
> +
>  typedef struct
>  {
>  	GenericListRec list;
> -- 
> 1.7.0.1
> 
> 
> Best regards,
> Oleh


More information about the xorg-devel mailing list