From 31a0147a720f2f0ef30df41512f41f43734662dd Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Tue, 9 May 2023 11:43:22 -0700 Subject: til_settings: introduce til_setting_spec_t concept vs. desc For recursive settings the individual setting being described needs to get added to a potentially different settings instance than the one being operated on at the top of the current setup_func phase. The settings instance being passed around for a setup_func to operate on is constified, mainly to try ensure modules don't start directly mucking with the settings. They're supposed to just describe what they want next and iterate back and forth, with the front-end creating the settings from the returned descs however is appropriate, eventually building up the settings to completion. But since it's the setup_func that decides which settings instance is appropriate for containing the setting.. at some point it must associate a settings instance with the desc it's producing, one that is going to be necessarily written to. So here I'm just turning the existing til_setting_desc_t to a "spec", unchanged. And introducing a new til_setting_desc_t embedding the spec, accompanied by a non-const til_settings_t* "container". Now what setup_funcs use to express settings are a spec, otherwise identically to before. Instead of cloning a desc to allocate it for returning to the front-end, the desc is created from a spec with the target settings instance passed in. This turns the desc step where we take a constified settings instance and cast it into a non-const a more formal act of going from spec->desc, binding the spec to a specific settings instance. It will also serve to isolate that hacky cast to a til_settings function, and all the accessors of til_setting_desc_t needing to operate on the containing settings instance can just do so. As of this commit, the container pointer is just sitting in the desc_t but isn't being made use of or even assigned yet. This is just to minimize the amount of churn happening in this otherwise mostly mechanical and sprawling commit. There's also been some small changes surrounding the desc generators and plumbing of the settings instance where there previously wasn't any. It's unclear to me if desc generators will stay desc generators or turn into spec generators. For now those are mostly just used by the drm_fb stuff anyways, modules haven't made use of them, so they can stay a little crufty harmlessly for now. --- src/modules/blinds/blinds.c | 4 ++-- src/modules/checkers/checkers.c | 14 +++++++------- src/modules/compose/compose.c | 4 ++-- src/modules/drizzle/drizzle.c | 4 ++-- src/modules/flui2d/flui2d.c | 10 +++++----- src/modules/moire/moire.c | 2 +- src/modules/pixbounce/pixbounce.c | 4 ++-- src/modules/plato/plato.c | 4 ++-- src/modules/rkt/rkt.c | 14 +++++++------- src/modules/rtv/rtv.c | 12 ++++++------ src/modules/shapes/shapes.c | 14 +++++++------- src/modules/sparkler/sparkler.c | 8 ++++---- src/modules/stars/stars.c | 2 +- src/modules/strobe/strobe.c | 2 +- src/modules/submit/submit.c | 2 +- src/modules/swarm/swarm.c | 2 +- src/modules/voronoi/voronoi.c | 6 +++--- 17 files changed, 54 insertions(+), 54 deletions(-) (limited to 'src/modules') diff --git a/src/modules/blinds/blinds.c b/src/modules/blinds/blinds.c index f8c2597..6a0639b 100644 --- a/src/modules/blinds/blinds.c +++ b/src/modules/blinds/blinds.c @@ -152,7 +152,7 @@ static int blinds_setup(const til_settings_t *settings, til_setting_t **res_sett int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Blinds orientation", .key = "orientation", .regex = "^(horizontal|vertical)", @@ -167,7 +167,7 @@ static int blinds_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Blinds count", .key = "count", .regex = "\\.[0-9]+", diff --git a/src/modules/checkers/checkers.c b/src/modules/checkers/checkers.c index bd651d5..a2a767d 100644 --- a/src/modules/checkers/checkers.c +++ b/src/modules/checkers/checkers.c @@ -462,7 +462,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Checker size", .key = "size", .regex = "\\.[0-9]+", @@ -477,7 +477,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Checkers pattern", .key = "pattern", .preferred = pattern_values[0], @@ -491,7 +491,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Filled cell module (\"none\" for plain checkers)", .key = "fill_module", .preferred = fill_module_values[0], @@ -505,7 +505,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Checkers dynamics", .key = "dynamics", .preferred = dynamics_values[0], @@ -520,7 +520,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se if (strcasecmp(dynamics, "odd") && strcasecmp(dynamics, "even")) { r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Checkers dynamics rate", .key = "dynamics_rate", .preferred = dynamics_rate_values[0], @@ -535,7 +535,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se } r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fill mode", .key = "fill", .preferred = fill_values[CHECKERS_DEFAULT_FILL], @@ -552,7 +552,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se * if there's no texture or no underlay to sample, we should have a color to fallback on. */ r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fill color", .key = "color", .preferred = TIL_SETTINGS_STR(CHECKERS_DEFAULT_COLOR), diff --git a/src/modules/compose/compose.c b/src/modules/compose/compose.c index 14a23f4..73c0181 100644 --- a/src/modules/compose/compose.c +++ b/src/modules/compose/compose.c @@ -260,7 +260,7 @@ static int compose_setup(const til_settings_t *settings, til_setting_t **res_set int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Colon-separated list of module layers, in draw-order", .key = "layers", .preferred = "drizzle:stars:spiro:plato", @@ -274,7 +274,7 @@ static int compose_setup(const til_settings_t *settings, til_setting_t **res_set return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Module to use for source texture, \"none\" to disable", .key = "texture", .preferred = texture_values[0], diff --git a/src/modules/drizzle/drizzle.c b/src/modules/drizzle/drizzle.c index 9445da6..8a5607f 100644 --- a/src/modules/drizzle/drizzle.c +++ b/src/modules/drizzle/drizzle.c @@ -377,7 +377,7 @@ static int drizzle_setup(const til_settings_t *settings, til_setting_t **res_set int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Puddle viscosity", .key = "viscosity", .regex = "\\.[0-9]+", @@ -392,7 +392,7 @@ static int drizzle_setup(const til_settings_t *settings, til_setting_t **res_set return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Overlay style", .key = "style", .regex = "[a-z]+", diff --git a/src/modules/flui2d/flui2d.c b/src/modules/flui2d/flui2d.c index 84306f5..f8e25d3 100644 --- a/src/modules/flui2d/flui2d.c +++ b/src/modules/flui2d/flui2d.c @@ -455,7 +455,7 @@ static int flui2d_setup(const til_settings_t *settings, til_setting_t **res_sett int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fluid viscosity", .key = "viscosity", .regex = "\\.[0-9]+", @@ -470,7 +470,7 @@ static int flui2d_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fluid diffusion", .key = "diffusion", .regex = "\\.[0-9]+", @@ -485,7 +485,7 @@ static int flui2d_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fluid decay", .key = "decay", .regex = "\\.[0-9]+", @@ -500,7 +500,7 @@ static int flui2d_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fluid emitters style", .key = "emitters", .regex = "^(figure8|clockgrid)", @@ -516,7 +516,7 @@ static int flui2d_setup(const til_settings_t *settings, til_setting_t **res_sett if (!strcasecmp(emitters, "clockgrid")) { r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Fluid clockgrid emitters clock step", .key = "clockstep", .regex = "\\.[0-9]+", diff --git a/src/modules/moire/moire.c b/src/modules/moire/moire.c index 736a3bf..f7d0f3b 100644 --- a/src/modules/moire/moire.c +++ b/src/modules/moire/moire.c @@ -133,7 +133,7 @@ static int moire_setup(const til_settings_t *settings, til_setting_t **res_setti int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Number of radial centers", .key = "centers", .regex = "\\.[0-9]+", diff --git a/src/modules/pixbounce/pixbounce.c b/src/modules/pixbounce/pixbounce.c index 7fa7f34..6f7502c 100644 --- a/src/modules/pixbounce/pixbounce.c +++ b/src/modules/pixbounce/pixbounce.c @@ -347,7 +347,7 @@ int pixbounce_setup(const til_settings_t *settings, til_setting_t **res_setting, int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Pixmap size", .key = "pixmap_size", .regex = "(0|1|0\\.[0-9]{1,2})", @@ -362,7 +362,7 @@ int pixbounce_setup(const til_settings_t *settings, til_setting_t **res_setting, return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Pixmap", .key = "pixmap", .regex = ":[alnum]:+", diff --git a/src/modules/plato/plato.c b/src/modules/plato/plato.c index f952674..0b584c5 100644 --- a/src/modules/plato/plato.c +++ b/src/modules/plato/plato.c @@ -711,7 +711,7 @@ static int plato_setup(const til_settings_t *settings, til_setting_t **res_setti int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Orbit rate and direction", .key = "orbit_rate", .regex = "\\.[0-9]+", /* FIXME */ @@ -726,7 +726,7 @@ static int plato_setup(const til_settings_t *settings, til_setting_t **res_setti return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Spin rate and direction", .key = "spin_rate", .regex = "\\.[0-9]+", /* FIXME */ diff --git a/src/modules/rkt/rkt.c b/src/modules/rkt/rkt.c index ac361c0..6947fbb 100644 --- a/src/modules/rkt/rkt.c +++ b/src/modules/rkt/rkt.c @@ -287,7 +287,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting * might work for getting full-blown demos sequenced via rocket. */ r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Module to sequence", .key = "seq_module", .preferred = "compose", @@ -300,7 +300,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Rocket \"base\" label", .key = "base", .preferred = "tiller", @@ -313,7 +313,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Beats per minute", .key = "bpm", .preferred = "125", @@ -326,7 +326,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Rows per beat", .key = "rpb", .preferred = "8", @@ -339,7 +339,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Editor connection toggle", .key = "connect", /* TODO: regex */ @@ -355,7 +355,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting if (!strcasecmp(connect, "on")) { r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Editor host", .key = "host", .preferred = "localhost", @@ -369,7 +369,7 @@ static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Editor port", .key = "port", .preferred = TIL_SETTINGS_STR(SYNC_DEFAULT_PORT), diff --git a/src/modules/rtv/rtv.c b/src/modules/rtv/rtv.c index 661afc9..1984f30 100644 --- a/src/modules/rtv/rtv.c +++ b/src/modules/rtv/rtv.c @@ -322,7 +322,7 @@ static int rtv_setup(const til_settings_t *settings, til_setting_t **res_setting int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Colon-separated list of channel modules, \"all\" for all", .key = "channels", .preferred = "compose", @@ -335,7 +335,7 @@ static int rtv_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Channel duration, in seconds", .key = "duration", .regex = "\\.[0-9]+", @@ -349,7 +349,7 @@ static int rtv_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Context duration, in seconds", .key = "context_duration", .regex = "\\.[0-9]+", @@ -363,7 +363,7 @@ static int rtv_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Caption duration, in seconds", .key = "caption_duration", .regex = "\\.[0-9]+", @@ -377,7 +377,7 @@ static int rtv_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Snow on channel-switch duration, in seconds", .key = "snow_duration", .regex = "\\.[0-9]+", @@ -391,7 +391,7 @@ static int rtv_setup(const til_settings_t *settings, til_setting_t **res_setting return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Module for snow (\"blank\" for blanking, \"none\" to disable)", .key = "snow_module", .preferred = RTV_DEFAULT_SNOW_MODULE, diff --git a/src/modules/shapes/shapes.c b/src/modules/shapes/shapes.c index 5073002..e0f020a 100644 --- a/src/modules/shapes/shapes.c +++ b/src/modules/shapes/shapes.c @@ -350,7 +350,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Shape type", .key = "type", .regex = "[a-zA-Z]+", @@ -365,7 +365,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Scaling factor", .key = "scale", .regex = "(1|0?\\.[0-9]{1,2})", @@ -380,7 +380,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Pinch factor", .key = "pinch", .regex = "(1|0?\\.[0-9]{1,2})", @@ -396,7 +396,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett if (strcasecmp(pinch, "0")) { r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Pinch spin factor", .key = "pinch_spin", .regex = "-?(0|1|0?\\.[0-9]{1,2})", @@ -411,7 +411,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Number of pinches", .key = "pinches", .regex = "[0-9]+", @@ -428,7 +428,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett if (!strcasecmp(type, "star") || !strcasecmp(type, "pinwheel")) { r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Number of points", .key = "points", .regex = "[0-9]+", @@ -443,7 +443,7 @@ static int shapes_setup(const til_settings_t *settings, til_setting_t **res_sett return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Spin factor", .key = "spin", .regex = "-?(0|1|0?\\.[0-9]{1,2})", /* Derived from pixbounce, I'm sure when regexes start getting actually applied we're going to have to revisit all of these and fix them with plenty of lols. */ diff --git a/src/modules/sparkler/sparkler.c b/src/modules/sparkler/sparkler.c index 6b27813..50815e9 100644 --- a/src/modules/sparkler/sparkler.c +++ b/src/modules/sparkler/sparkler.c @@ -121,7 +121,7 @@ static int sparkler_setup(const til_settings_t *settings, til_setting_t **res_se /* TODO: return -EINVAL on parse errors? */ r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Show BSP-tree leaf-node bounding boxes", .key = "show_bsp_leafs", .preferred = "off", @@ -144,7 +144,7 @@ static int sparkler_setup(const til_settings_t *settings, til_setting_t **res_se }; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Minimum BSP-tree depth for shown leaf-nodes", .key = "show_bsp_leafs_min_depth", .preferred = "8", @@ -158,7 +158,7 @@ static int sparkler_setup(const til_settings_t *settings, til_setting_t **res_se } r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Show BSP-tree search broad-phase match candidates", .key = "show_bsp_matches", .preferred = "off", @@ -172,7 +172,7 @@ static int sparkler_setup(const til_settings_t *settings, til_setting_t **res_se if (!strcasecmp(show_bsp_matches, "on")) { r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Show only narrow-phase affected match results", .key = "show_bsp_matches_affected_only", .preferred = "off", diff --git a/src/modules/stars/stars.c b/src/modules/stars/stars.c index d57de53..8dc18b8 100644 --- a/src/modules/stars/stars.c +++ b/src/modules/stars/stars.c @@ -266,7 +266,7 @@ int stars_setup(const til_settings_t *settings, til_setting_t **res_setting, con int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Rotation rate", .key = "rot_adj", .regex = "\\.[0-9]+", diff --git a/src/modules/strobe/strobe.c b/src/modules/strobe/strobe.c index 7d0acbf..fa245f7 100644 --- a/src/modules/strobe/strobe.c +++ b/src/modules/strobe/strobe.c @@ -114,7 +114,7 @@ static int strobe_setup(const til_settings_t *settings, til_setting_t **res_sett int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Strobe period", .key = "period", .regex = "\\.[0-9]+", diff --git a/src/modules/submit/submit.c b/src/modules/submit/submit.c index 69e67f0..285ef92 100644 --- a/src/modules/submit/submit.c +++ b/src/modules/submit/submit.c @@ -340,7 +340,7 @@ static int submit_setup(const til_settings_t *settings, til_setting_t **res_sett int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Bilinearly interpolate cell colors", .key = "bilerp", .regex = NULL, diff --git a/src/modules/swarm/swarm.c b/src/modules/swarm/swarm.c index e1b9640..adb8df8 100644 --- a/src/modules/swarm/swarm.c +++ b/src/modules/swarm/swarm.c @@ -430,7 +430,7 @@ static int swarm_setup(const til_settings_t *settings, til_setting_t **res_setti int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Particle drawing style", .key = "style", .values = styles, diff --git a/src/modules/voronoi/voronoi.c b/src/modules/voronoi/voronoi.c index c258165..209310d 100644 --- a/src/modules/voronoi/voronoi.c +++ b/src/modules/voronoi/voronoi.c @@ -351,7 +351,7 @@ static int voronoi_setup(const til_settings_t *settings, til_setting_t **res_set int r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Voronoi cells quantity", .key = "cells", .regex = "^[0-9]+", @@ -366,7 +366,7 @@ static int voronoi_setup(const til_settings_t *settings, til_setting_t **res_set return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Constantly randomize cell placement", .key = "randomize", .regex = "^(on|off)", @@ -381,7 +381,7 @@ static int voronoi_setup(const til_settings_t *settings, til_setting_t **res_set return r; r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ + &(til_setting_spec_t){ .name = "Use faster, imperfect method", .key = "dirty", .regex = "^(on|off)", -- cgit v1.2.1