conf: support ! to negate the match

So that you can do "application.name" = "!pw-cat" to match anything
that is not pw-cat and "application.name" = "!~pw-cat$".

Fixes #3460
This commit is contained in:
Wim Taymans 2023-08-25 11:39:28 +02:00
parent 0bfc02581f
commit 8dd294bf44
5 changed files with 21 additions and 14 deletions

View file

@ -95,7 +95,7 @@ jack.properties = {
jack.rules = [ jack.rules = [
{ matches = [ { matches = [
{ {
# all keys must match the value. ~ starts regex. # all keys must match the value. ! negates. ~ starts regex.
#client.name = "Carla" #client.name = "Carla"
#application.process.binary = "jack_simple_client" #application.process.binary = "jack_simple_client"
#application.name = "~jack_simple_client.*" #application.name = "~jack_simple_client.*"

View file

@ -116,7 +116,7 @@ pulse.rules = [
{ {
matches = [ matches = [
{ {
# all keys must match the value. ~ starts regex. # all keys must match the value. ! negates. ~ starts regex.
#client.name = "Firefox" #client.name = "Firefox"
#application.process.binary = "teams" #application.process.binary = "teams"
#application.name = "~speech-dispatcher.*" #application.name = "~speech-dispatcher.*"

View file

@ -93,7 +93,7 @@
* # any of the items in matches needs to match, if one does, * # any of the items in matches needs to match, if one does,
* # actions are emited. * # actions are emited.
* { * {
* # all keys must match the value. ~ in value starts regex. * # all keys must match the value. ! negates. ~ starts regex.
* #node.name = "~alsa_input.*" * #node.name = "~alsa_input.*"
* media.class = "Audio/Sink" * media.class = "Audio/Sink"
* } * }

View file

@ -75,7 +75,7 @@
* { matches = [ * { matches = [
* # any of the items in matches needs to match, if one does, * # any of the items in matches needs to match, if one does,
* # actions are emited. * # actions are emited.
* { # all keys must match the value. ~ in value starts regex. * { # all keys must match the value. ! negates. ~ starts regex.
* #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0" * #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0"
* #rtp.payload = "127" * #rtp.payload = "127"
* #rtp.fmt = "L16/48000/2" * #rtp.fmt = "L16/48000/2"
@ -91,7 +91,7 @@
* } * }
* } * }
* { matches = [ * { matches = [
* { # all keys must match the value. ~ in value starts regex. * { # all keys must match the value. ! negates. ~ starts regex.
* #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0" * #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0"
* #rtp.payload = "127" * #rtp.payload = "127"
* #rtp.fmt = "L16/48000/2" * #rtp.fmt = "L16/48000/2"

View file

@ -586,6 +586,7 @@ static int load_module(struct pw_context *context, const char *key, const char *
/* /*
* { * {
* # all keys must match the value. ~ in value starts regex. * # all keys must match the value. ~ in value starts regex.
* # ! as the first char of the value negates the match
* <key> = <value> * <key> = <value>
* ... * ...
* } * }
@ -598,7 +599,7 @@ static bool find_match(struct spa_json *arr, const struct spa_dict *props)
char key[256], val[1024]; char key[256], val[1024];
const char *str, *value; const char *str, *value;
int match = 0, fail = 0; int match = 0, fail = 0;
int len; int len, skip = 0;
while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) { while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) {
bool success = false; bool success = false;
@ -615,23 +616,28 @@ static bool find_match(struct spa_json *arr, const struct spa_dict *props)
continue; continue;
value = val; value = val;
len = strlen(val); len = strlen(val);
if (len > 0 && value[0] == '!') {
success = !success;
skip++;
}
} }
if (str != NULL) { if (str != NULL) {
if (value[0] == '~') { if (value[skip] == '~') {
regex_t preg; regex_t preg;
int res; int res;
if ((res = regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB)) != 0) { skip++;
if ((res = regcomp(&preg, value+skip, REG_EXTENDED | REG_NOSUB)) != 0) {
char errbuf[1024]; char errbuf[1024];
regerror(res, &preg, errbuf, sizeof(errbuf)); regerror(res, &preg, errbuf, sizeof(errbuf));
pw_log_warn("invalid regex %s: %s", value+1, errbuf); pw_log_warn("invalid regex %s: %s", value+skip, errbuf);
} else { } else {
if (regexec(&preg, str, 0, NULL, 0) == 0) if (regexec(&preg, str, 0, NULL, 0) == 0)
success = true; success = !success;
regfree(&preg); regfree(&preg);
} }
} else if (strncmp(str, value, len) == 0 && } else if (strncmp(str, value+skip, len-skip) == 0 &&
strlen(str) == (size_t)len) { strlen(str) == (size_t)(len-skip)) {
success = true; success = !success;
} }
} }
if (success) { if (success) {
@ -639,6 +645,7 @@ static bool find_match(struct spa_json *arr, const struct spa_dict *props)
pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value);
} }
else { else {
pw_log_debug("'%s' fail '%s' < > '%.*s'", key, str, len, value);
fail++; fail++;
break; break;
} }
@ -1080,7 +1087,7 @@ int pw_conf_load_conf_for_context(struct pw_properties *props, struct pw_propert
* # any of the items in matches needs to match, if one does, * # any of the items in matches needs to match, if one does,
* # actions are emited. * # actions are emited.
* { * {
* # all keys must match the value. ~ in value starts regex. * # all keys must match the value. ! negates. ~ starts regex.
* <key> = <value> * <key> = <value>
* ... * ...
* } * }