From 63c4b49cff24edb52fad235b04c818392eaf574f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 19 May 2020 16:48:43 +0200 Subject: [PATCH] ucm: configuration - add DefineRegex Syntax: DefineRegex.VariableName { String "something to match" Regex "(me)thi(ng)" } Result: - configuration variable "VariableName" is equal to "mething" - configuration variable "VariableName1" is equal to "me" - configuration variable "VariableName2" is equal to "ng" Notes: The "Flags" string in the DefineRegex compound may specify the compilation flags (e = extended, i = ignore case, s = no substitutes, n = newline). See 'man 3 regex' for more details. Signed-off-by: Jaroslav Kysela --- src/ucm/Makefile.am | 3 +- src/ucm/parser.c | 41 ++++++++++- src/ucm/ucm_local.h | 4 + src/ucm/ucm_regex.c | 174 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 src/ucm/ucm_regex.c diff --git a/src/ucm/Makefile.am b/src/ucm/Makefile.am index 416db229..feb3c0c1 100644 --- a/src/ucm/Makefile.am +++ b/src/ucm/Makefile.am @@ -1,6 +1,7 @@ EXTRA_LTLIBRARIES = libucm.la -libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c main.c +libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \ + ucm_regex.c main.c noinst_HEADERS = ucm_local.h diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 7cc09379..ef96a965 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -193,6 +193,42 @@ int parse_get_safe_id(snd_config_t *n, const char **id) return 0; } +/* + * Evaluate variable regex definitions (in-place delete) + */ +static int evaluate_regex(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *d, *n; + const char *id; + int err; + + err = snd_config_search(cfg, "DefineRegex", &d); + if (err == -ENOENT) + return 1; + if (err < 0) + return err; + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineRegex"); + return -EINVAL; + } + + snd_config_for_each(i, next, d) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + err = uc_mgr_define_regex(uc_mgr, id, n); + if (err < 0) + return err; + } + + snd_config_delete(d); + return 0; +} + /* * Evaluate variable definitions (in-place delete) */ @@ -207,7 +243,7 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr, err = snd_config_search(cfg, "Define", &d); if (err == -ENOENT) - return 1; + return evaluate_regex(uc_mgr, cfg); if (err < 0) return err; @@ -233,7 +269,8 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr, } snd_config_delete(d); - return 0; + + return evaluate_regex(uc_mgr, cfg); } /* diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 2aea43e4..25a50b4d 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -317,6 +317,10 @@ int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *parent, snd_config_t *cond); +int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, + const char *name, + snd_config_t *eval); + /** The name of the environment variable containing the UCM directory */ #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM" diff --git a/src/ucm/ucm_regex.c b/src/ucm/ucm_regex.c new file mode 100644 index 00000000..be8f277a --- /dev/null +++ b/src/ucm/ucm_regex.c @@ -0,0 +1,174 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2019 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include "ucm_local.h" +#include +#include + +static int get_string(snd_config_t *compound, const char *key, const char **str) +{ + snd_config_t *node; + int err; + + err = snd_config_search(compound, key, &node); + if (err < 0) + return err; + return snd_config_get_string(node, str); +} + +static char *extract_substring(const char *data, regmatch_t *match) +{ + char *s; + size_t len; + + len = match->rm_eo - match->rm_so; + s = malloc(len + 1); + if (s == NULL) + return NULL; + memcpy(s, data + match->rm_so, len); + s[len] = '\0'; + return s; +} + +static int set_variables(snd_use_case_mgr_t *uc_mgr, const char *data, + unsigned int match_size, regmatch_t *match, + const char *name) +{ + size_t name2_len = strlen(name) + 16; + char *name2 = alloca(name2_len); + char *s; + unsigned int i; + int err; + + if (match[0].rm_so < 0 || match[0].rm_eo < 0) + return 0; + s = extract_substring(data, &match[0]); + if (s == NULL) + return -ENOMEM; + err = uc_mgr_set_variable(uc_mgr, name, s); + free(s); + if (err < 0) + return err; + for (i = 1; i < match_size; i++) { + if (match[0].rm_so < 0 || match[0].rm_eo < 0) + return 0; + s = extract_substring(data, &match[i]); + if (s == NULL) + return -ENOMEM; + snprintf(name2, name2_len, "%s%u", name, i); + err = uc_mgr_set_variable(uc_mgr, name2, s); + free(s); + if (err < 0) + return err; + } + return 0; +} + +int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name, + snd_config_t *eval) +{ + const char *string, *regex_string, *flags_string; + char *s; + regex_t re; + int options = 0; + regmatch_t match[20]; + int err; + + if (uc_mgr->conf_format < 3) { + uc_error("define regex is supported in v3+ syntax"); + return -EINVAL; + } + + if (snd_config_get_type(eval) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineRegex"); + return -EINVAL; + } + + err = get_string(eval, "String", &string); + if (err < 0) { + uc_error("DefineRegex error (String)"); + return -EINVAL; + } + + err = get_string(eval, "Regex", ®ex_string); + if (err < 0) { + uc_error("DefineRegex error (Regex string)"); + return -EINVAL; + } + + err = get_string(eval, "Flags", &flags_string); + if (err == -ENOENT) { + options = REG_EXTENDED; + } else if (err < 0) { + uc_error("DefineRegex error (Flags string)"); + return -EINVAL; + } else { + while (*flags_string) { + switch (tolower(*flags_string)) { + case 'e': + options |= REG_EXTENDED; + break; + case 'i': + options |= REG_ICASE; + break; + case 's': + options |= REG_NOSUB; + break; + case 'n': + options |= REG_NEWLINE; + break; + default: + uc_error("DefineRegex error (unknown flag '%c')", *flags_string); + return -EINVAL; + } + flags_string++; + } + } + + err = uc_mgr_get_substituted_value(uc_mgr, &s, regex_string); + if (err < 0) + return err; + err = regcomp(&re, s, options); + free(s); + if (err) { + uc_error("Regex '%s' compilation failed (code %d)", err); + return -EINVAL; + } + + err = uc_mgr_get_substituted_value(uc_mgr, &s, string); + if (err < 0) { + regfree(&re); + return err; + } + err = regexec(&re, s, ARRAY_SIZE(match), match, 0); + if (err < 0) + err = -errno; + else + err = set_variables(uc_mgr, s, ARRAY_SIZE(match), match, name); + free(s); + regfree(&re); + return err; +} -- 2.47.1