Files
xserver/hw/xfree86/parser/patterns.c
Enrico Weigelt, metux IT consult 7dad061eab xfree86: parser: dont ifdef on HAVE_XORG_CONFIG_H
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>
2026-02-13 12:33:40 +01:00

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)");
}