mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2026-05-06 06:46:43 -04:00
ucm: add DefineRegex 'all' scheme for multiple pattern matches (Syntax 9)
Extend DefineRegex to support two matching schemes: - "first" (default): matches pattern once, extracts capture groups - "all": matches pattern multiple times, extracts all matches The "all" scheme creates variables with naming pattern nameN for Nth match and nameN_M for Nth match's Mth capture group, enabling extraction of repeating patterns from strings. Updated documentation with detailed examples and comprehensive description of regex flags, schemes, and variable naming conventions. Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
96f23dda14
commit
3592e5c781
2 changed files with 198 additions and 35 deletions
|
|
@ -738,19 +738,135 @@ Define {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The *DefineRegex* allows substring extraction like:
|
The *DefineRegex* allows substring extraction using regular expressions (POSIX extended regex).
|
||||||
|
It can match patterns in strings and extract matched substrings into UCM variables.
|
||||||
|
|
||||||
|
#### DefineRegex Structure
|
||||||
|
|
||||||
~~~{.html}
|
~~~{.html}
|
||||||
DefineRegex.rval {
|
DefineRegex.name {
|
||||||
Regex "(hello)|(regex)"
|
String "text to match against"
|
||||||
String "hello, it's my regex"
|
Regex "regex_pattern"
|
||||||
|
Flags "e"
|
||||||
|
Scheme "first"
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The result will be stored to variables *rval1* as *hello* and *rval2* as *regex* (every matched
|
Field | Description
|
||||||
substrings are stored to a separate variable with the sequence number postfix.
|
---------------------|---------------------
|
||||||
|
String | The input string to match the regex pattern against
|
||||||
|
Regex | POSIX extended regular expression pattern
|
||||||
|
Flags | Optional regex flags (see below)
|
||||||
|
Scheme | Matching scheme: "first" (default) or "all" [**Syntax 9**]
|
||||||
|
|
||||||
Variables can be substituted using the `${var:rval1}` reference for example.
|
#### Regex Flags
|
||||||
|
|
||||||
|
The Flags field is optional and accepts the following characters:
|
||||||
|
|
||||||
|
Flag | Description
|
||||||
|
-------|---------------------
|
||||||
|
e | Extended POSIX regex (REG_EXTENDED) - default recommended
|
||||||
|
i | Case-insensitive matching (REG_ICASE)
|
||||||
|
s | Report only success/fail (REG_NOSUB)
|
||||||
|
n | Newline-sensitive matching (REG_NEWLINE)
|
||||||
|
|
||||||
|
Multiple flags can be combined, e.g., "ei" for extended and case-insensitive.
|
||||||
|
|
||||||
|
#### Matching Schemes
|
||||||
|
|
||||||
|
**Scheme "first"** (default): Matches the pattern once and extracts capture groups
|
||||||
|
|
||||||
|
The variables created are:
|
||||||
|
- `name` - the full matched string
|
||||||
|
- `name1` - first capture group (parentheses in regex)
|
||||||
|
- `name2` - second capture group
|
||||||
|
- `nameN` - Nth capture group
|
||||||
|
|
||||||
|
Example with "first" scheme:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
DefineRegex.hwdev {
|
||||||
|
String "hw:2,0"
|
||||||
|
Regex "hw:([0-9]+),([0-9]+)"
|
||||||
|
Flags "e"
|
||||||
|
Scheme "first"
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
This creates variables:
|
||||||
|
- `hwdev` = "hw:2,0" (full match)
|
||||||
|
- `hwdev1` = "2" (first capture group - card number)
|
||||||
|
- `hwdev2` = "0" (second capture group - device number)
|
||||||
|
|
||||||
|
**Scheme "all"** [**Syntax 9**]: Matches the pattern multiple times and extracts all matches
|
||||||
|
|
||||||
|
The variables created are:
|
||||||
|
- `nameN` - Nth full match (N starts at 1)
|
||||||
|
- `nameN_1` - Nth match, first capture group
|
||||||
|
- `nameN_2` - Nth match, second capture group
|
||||||
|
- `nameN_M` - Nth match, Mth capture group
|
||||||
|
|
||||||
|
Example with "all" scheme:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
DefineRegex.devices {
|
||||||
|
String "device1 device2 device3"
|
||||||
|
Regex "device([0-9]+)"
|
||||||
|
Flags "e"
|
||||||
|
Scheme "all"
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
This creates variables:
|
||||||
|
- `devices1` = "device1" (first full match)
|
||||||
|
- `devices1_1` = "1" (first match, capture group 1)
|
||||||
|
- `devices2` = "device2" (second full match)
|
||||||
|
- `devices2_1` = "2" (second match, capture group 1)
|
||||||
|
- `devices3` = "device3" (third full match)
|
||||||
|
- `devices3_1` = "3" (third match, capture group 1)
|
||||||
|
|
||||||
|
#### Practical Examples
|
||||||
|
|
||||||
|
Extract USB device vendor and product IDs:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
DefineRegex.usbids {
|
||||||
|
String "${sys:bus/usb/devices/1-1/uevent}"
|
||||||
|
Regex "PRODUCT=([0-9a-f]+)/([0-9a-f]+)"
|
||||||
|
Flags "e"
|
||||||
|
Scheme "first"
|
||||||
|
}
|
||||||
|
# Creates: usbids (full match), usbids1 (vendor), usbids2 (product)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Parse multiple key=value pairs:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
DefineRegex.params {
|
||||||
|
String "rate=48000,channels=2,format=S16_LE"
|
||||||
|
Regex "([a-z]+)=([^,]+)"
|
||||||
|
Flags "e"
|
||||||
|
Scheme "all"
|
||||||
|
}
|
||||||
|
# Creates: params1="rate=48000", params1_1="rate", params1_2="48000"
|
||||||
|
# params2="channels=2", params2_1="channels", params2_2="2"
|
||||||
|
# params3="format=S16_LE", params3_1="format", params3_2="S16_LE"
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Extract text components:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
DefineRegex.model {
|
||||||
|
String "USB Audio Device Model XYZ123"
|
||||||
|
Regex "([A-Z]+).*Model ([A-Z0-9]+)"
|
||||||
|
Flags "e"
|
||||||
|
Scheme "first"
|
||||||
|
}
|
||||||
|
# Creates: model (full match), model1="USB", model2="XYZ123"
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Variables can be substituted using `${var:name}` reference. For example, to use the extracted
|
||||||
|
card number: `PlaybackPCM "hw:${var:hwdev1},0"`
|
||||||
|
|
||||||
### Macros
|
### Macros
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,47 +54,78 @@ static char *extract_substring(const char *data, regmatch_t *match)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_variables(snd_use_case_mgr_t *uc_mgr, const char *data,
|
static int set_variables(snd_use_case_mgr_t *uc_mgr, const char *data,
|
||||||
unsigned int match_size, regmatch_t *match,
|
regex_t *re, const char *name, int scheme_all)
|
||||||
const char *name)
|
|
||||||
{
|
{
|
||||||
size_t name2_len = strlen(name) + 16;
|
size_t name_len = strlen(name) + 32;
|
||||||
char *name2 = alloca(name2_len);
|
char *var_name = alloca(name_len);
|
||||||
|
regmatch_t match[20];
|
||||||
char *s;
|
char *s;
|
||||||
|
unsigned int match_idx = 1;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int err;
|
int err;
|
||||||
|
const char *pos;
|
||||||
|
|
||||||
if (match[0].rm_so < 0 || match[0].rm_eo < 0)
|
pos = data;
|
||||||
return 0;
|
while (1) {
|
||||||
s = extract_substring(data, &match[0]);
|
err = regexec(re, pos, ARRAY_SIZE(match), match, 0);
|
||||||
if (s == NULL)
|
if (err == REG_NOMATCH)
|
||||||
return -ENOMEM;
|
break;
|
||||||
err = uc_mgr_set_variable(uc_mgr, name, s);
|
if (err != 0)
|
||||||
free(s);
|
return -EINVAL;
|
||||||
if (err < 0)
|
|
||||||
return err;
|
if (match[0].rm_so < 0 || match[0].rm_eo < 0)
|
||||||
for (i = 1; i < match_size; i++) {
|
break;
|
||||||
if (match[i].rm_so < 0 || match[i].rm_eo < 0)
|
|
||||||
return 0;
|
s = extract_substring(pos, &match[0]);
|
||||||
s = extract_substring(data, &match[i]);
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
snprintf(name2, name2_len, "%s%u", name, i);
|
if (scheme_all) {
|
||||||
err = uc_mgr_set_variable(uc_mgr, name2, s);
|
snprintf(var_name, name_len, "%s%u", name, match_idx);
|
||||||
|
err = uc_mgr_set_variable(uc_mgr, var_name, s);
|
||||||
|
} else {
|
||||||
|
err = uc_mgr_set_variable(uc_mgr, name, s);
|
||||||
|
}
|
||||||
free(s);
|
free(s);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i < ARRAY_SIZE(match); i++) {
|
||||||
|
if (match[i].rm_so < 0 || match[i].rm_eo < 0)
|
||||||
|
break;
|
||||||
|
s = extract_substring(pos, &match[i]);
|
||||||
|
if (s == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
if (scheme_all)
|
||||||
|
snprintf(var_name, name_len, "%s%u_%u", name, match_idx, i);
|
||||||
|
else
|
||||||
|
snprintf(var_name, name_len, "%s%u", name, i);
|
||||||
|
err = uc_mgr_set_variable(uc_mgr, var_name, s);
|
||||||
|
free(s);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scheme_all)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pos += match[0].rm_eo;
|
||||||
|
match_idx++;
|
||||||
|
|
||||||
|
if (*pos == '\0')
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
||||||
snd_config_t *eval)
|
snd_config_t *eval)
|
||||||
{
|
{
|
||||||
const char *string, *regex_string, *flags_string;
|
const char *string, *regex_string, *flags_string, *scheme_string;
|
||||||
char *s;
|
char *s;
|
||||||
regex_t re;
|
regex_t re;
|
||||||
int options = 0;
|
int options = 0;
|
||||||
regmatch_t match[20];
|
int use_scheme_all = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (uc_mgr->conf_format < 3) {
|
if (uc_mgr->conf_format < 3) {
|
||||||
|
|
@ -119,6 +150,27 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = get_string(eval, "Scheme", &scheme_string);
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
use_scheme_all = 0;
|
||||||
|
} else if (err < 0) {
|
||||||
|
snd_error(UCM, "DefineRegex error (Scheme string)");
|
||||||
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
|
if (strcmp(scheme_string, "first") == 0) {
|
||||||
|
use_scheme_all = 0;
|
||||||
|
} else if (strcmp(scheme_string, "all") == 0) {
|
||||||
|
if (uc_mgr->conf_format < 9) {
|
||||||
|
snd_error(UCM, "DefineRegex 'all' scheme is supported in v9+ syntax");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
use_scheme_all = 1;
|
||||||
|
} else {
|
||||||
|
snd_error(UCM, "DefineRegex error (unknown scheme '%s')", scheme_string);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = get_string(eval, "Flags", &flags_string);
|
err = get_string(eval, "Flags", &flags_string);
|
||||||
if (err == -ENOENT) {
|
if (err == -ENOENT) {
|
||||||
options = REG_EXTENDED;
|
options = REG_EXTENDED;
|
||||||
|
|
@ -164,13 +216,8 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name,
|
||||||
regfree(&re);
|
regfree(&re);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = regexec(&re, s, ARRAY_SIZE(match), match, 0);
|
|
||||||
if (err < 0)
|
err = set_variables(uc_mgr, s, &re, name, use_scheme_all);
|
||||||
err = -errno;
|
|
||||||
else if (err == REG_NOMATCH)
|
|
||||||
err = 0;
|
|
||||||
else
|
|
||||||
err = set_variables(uc_mgr, s, ARRAY_SIZE(match), match, name);
|
|
||||||
free(s);
|
free(s);
|
||||||
regfree(&re);
|
regfree(&re);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue