mirror of
https://github.com/X11Libre/xserver.git
synced 2026-03-24 08:04:30 +00:00
The header is always present (we're inside xfree86), so no need for that #ifdef. Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
211 lines
6.3 KiB
C
211 lines
6.3 KiB
C
/* SPDX-License-Identifier: MIT or X11
|
|
*
|
|
* Copyright (c) 2025 Oleh Nykyforchyn <oleh.nyk@gmail.com>
|
|
*
|
|
*/
|
|
#include <xorg-config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "misc.h"
|
|
#include "xf86Parser_priv.h"
|
|
#include "configProcs.h"
|
|
#include "os.h"
|
|
|
|
/*
|
|
* Utilities used by InputClass.c and OutputClass.c
|
|
*/
|
|
|
|
/* A group (which is a struct xf86MatchGroup) represents a complex condition
|
|
* that should be satisfied if is_negated_is true or should not be satisfied
|
|
* otherwise, for an input or output device to be accepted. A group contains
|
|
* an xorg_list patterns.
|
|
*
|
|
* Each pattern (a struct xf86MatchPattern) is a subcondition. The logical value
|
|
* of a group is true if and only if at least one subcondition is true, i.e.,
|
|
* the patterns are combined by logical 'OR', which is represented by '|' in
|
|
* the string that defines a group.
|
|
*
|
|
* If a string that defines a pattern is preceded by '!', then the logical
|
|
* value of the pattern is negated. Note that a second '!' does not cancel
|
|
* the first one, so '!!' does not make sense.
|
|
*
|
|
* A string correponding to a pattern (after an eventual '!') can be treated
|
|
* either as a regular expression, if it is prepended with '~', or otherwise
|
|
* as a string with which an attribute of a device is compared.
|
|
* The mode of comparison is of the type enum xf86MatchMode and depends on
|
|
* the type of the attribute.
|
|
*
|
|
* If a string is not a regular expression but contains one or more '&'s, then
|
|
* it is treated as a sequence of &'-separated substrings that should ALL be
|
|
* present in an attribute (in arbitrary places and order) for the logical value
|
|
* to be positive (so empty substrings are inessential and dropped).
|
|
* They are kept in pattern.str '\0'-separated, with a final second '\0'.
|
|
*/
|
|
|
|
#define LOG_OR '|'
|
|
#define LOG_AND '&'
|
|
|
|
#define NEG_FLAG '!'
|
|
#define REGEX_FLAG '~'
|
|
|
|
|
|
xf86MatchGroup*
|
|
xf86createMatchGroup(const char *arg, xf86MatchMode pref_mode,
|
|
Bool negated)
|
|
{
|
|
xf86MatchPattern *pattern;
|
|
xf86MatchGroup *group;
|
|
const char *str = arg;
|
|
unsigned n;
|
|
static const char sep_or[2] = { LOG_OR, '\0' };
|
|
static const char sep_and[2] = { LOG_AND, '\0' };
|
|
|
|
if (!str)
|
|
return NULL;
|
|
|
|
group = malloc(sizeof(*group));
|
|
if (!group) return NULL;
|
|
xorg_list_init(&group->patterns);
|
|
xorg_list_init(&group->entry);
|
|
group->is_negated = negated;
|
|
|
|
again:
|
|
/* start new pattern */
|
|
if ((pattern = malloc(sizeof(*pattern))) == NULL)
|
|
goto fail;
|
|
|
|
xorg_list_add(&pattern->entry, &group->patterns);
|
|
|
|
/* Pattern starting with '!' should NOT be matched */
|
|
if (*str == NEG_FLAG) {
|
|
pattern->is_negated = TRUE;
|
|
str++;
|
|
}
|
|
else
|
|
pattern->is_negated = FALSE;
|
|
|
|
pattern->str = NULL;
|
|
pattern->regex = NULL;
|
|
|
|
/* Check if there is a regex prefix */
|
|
if (*str == REGEX_FLAG) {
|
|
pattern->mode = MATCH_REGEX;
|
|
str ++;
|
|
if (*str) {
|
|
char *last;
|
|
last = strchr(str+1, *str);
|
|
if (last)
|
|
n = last-str-1;
|
|
else
|
|
n = strlen(str+1);
|
|
pattern->str = strndup(str+1, n);
|
|
if (pattern->str == NULL)
|
|
goto fail;
|
|
*(pattern->str+n) = '\0';
|
|
str += n+1;
|
|
if (*str) str++;
|
|
}
|
|
else {
|
|
/* no regex, notning to match against */
|
|
pattern->mode = MATCH_IS_INVALID;
|
|
LogMessageVerb(X_ERROR, 1,
|
|
"No regular expression supplied after \'%c\' in \"%s\", ignoring\n",
|
|
REGEX_FLAG, arg);
|
|
free(pattern->str);
|
|
pattern->str = NULL;
|
|
}
|
|
}
|
|
else {
|
|
n = strcspn(str, sep_or);
|
|
if (n > strcspn(str, sep_and)) {
|
|
pattern->mode = MATCH_SUBSTRINGS_SEQUENCE;
|
|
pattern->str = malloc(n+2);
|
|
if (pattern->str) {
|
|
char *s, *d;
|
|
strncpy(pattern->str, str, n);
|
|
str += n;
|
|
*(pattern->str+n) = '\0';
|
|
s = d = pattern->str;
|
|
n = 0;
|
|
next_chunk:
|
|
while ((*s) && (*s != LOG_AND)) {
|
|
if (n == -1) {
|
|
*(d++) = '\0';
|
|
n = 0;
|
|
}
|
|
*(d++) = *(s++);
|
|
n++;
|
|
}
|
|
while ((*s) == LOG_AND) s++;
|
|
if (*s) {
|
|
n = -1;
|
|
goto next_chunk;
|
|
}
|
|
if (d == pattern->str) {
|
|
/* All chunks are empty */
|
|
pattern->mode = MATCH_IS_INVALID;
|
|
LogMessageVerb(X_ERROR, 1,
|
|
"No non-empty substrings supplied in the alternative \"%s\" of \"%s\", ignoring\n",
|
|
pattern->str, arg);
|
|
}
|
|
*(++d) = '\0';
|
|
}
|
|
else
|
|
goto fail;
|
|
}
|
|
else {
|
|
pattern->mode = pref_mode;
|
|
pattern->str = strndup(str, n);
|
|
if (pattern->str == NULL)
|
|
goto fail;
|
|
*(pattern->str+n) = '\0'; /* should already be, but to be sure */
|
|
str += n;
|
|
}
|
|
}
|
|
|
|
while (*str == LOG_OR)
|
|
str++;
|
|
|
|
if (*str)
|
|
goto again;
|
|
|
|
return group;
|
|
|
|
fail:
|
|
xf86freeMatchGroup(group);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
xf86printMatchPattern(FILE * cf, const xf86MatchPattern *pattern, Bool not_first)
|
|
{
|
|
if (!pattern) return;
|
|
if (not_first)
|
|
fprintf(cf, "%c", LOG_OR);
|
|
if (pattern->is_negated)
|
|
fprintf(cf, "%c", NEG_FLAG);
|
|
if (pattern->mode == MATCH_IS_INVALID)
|
|
fprintf(cf, "invalid:%s",
|
|
pattern->str ? pattern->str : "(none)");
|
|
else if (pattern->mode == MATCH_REGEX)
|
|
/* FIXME: Hope there is no '~' in the pattern */
|
|
fprintf(cf, "%c%s%c", REGEX_FLAG,
|
|
pattern->str ? pattern->str : "(none)", REGEX_FLAG);
|
|
else if (pattern->mode == MATCH_SUBSTRINGS_SEQUENCE) {
|
|
Bool after = FALSE;
|
|
char *str = pattern->str;
|
|
while (*str) {
|
|
if (after)
|
|
fprintf(cf, "%c", LOG_AND);
|
|
fprintf(cf, "%s", str);
|
|
str += strlen(str);
|
|
str++;
|
|
after = TRUE;
|
|
}
|
|
}
|
|
else
|
|
fprintf(cf, "%s",
|
|
pattern->str ? pattern->str : "(none)");
|
|
}
|