NUL = no error, followed by settings>
Repeat until "filter settings size" is reached:
<32bit big-endian: key index number>
- [<strlist/boollist key>]
+ [+|$ <strlist/boollist key>]
<NUL-terminated string: value>
Repeat for "filter count":
<32bit big-endian: event filter string index number>
config_dump_full_stdout_callback(const struct config_export_setting *set,
struct dump_context *ctx)
{
- if (set->type == CONFIG_KEY_LIST) {
+ if (set->type != CONFIG_KEY_LIST)
+ ;
+ else if (set->list_count == 0 && set->value_stop_list &&
+ set->def_type == SET_BOOLLIST) {
+ /* filter empties a boollist setting */
+ } else {
/* these aren't needed */
return;
}
&ctx->info->defines[set->key_define_idx];
if (def->type == SET_STRLIST || def->type == SET_BOOLLIST) {
const char *suffix;
- if (!str_begins(set->key, def->key, &suffix) ||
- suffix[0] != '/')
+ if (!str_begins(set->key, def->key, &suffix))
i_unreached();
-
- suffix++;
- o_stream_nsend_str(ctx->output,
- t_strdup_until(set->key, suffix));
- o_stream_nsend_str(ctx->output,
- settings_section_escape(suffix));
+ else if (suffix[0] == '/') {
+ suffix++;
+ o_stream_nsend_str(ctx->output,
+ t_strdup_until(set->key, suffix));
+ o_stream_nsend_str(ctx->output,
+ settings_section_escape(suffix));
+ } else {
+ /* emptying boollist */
+ i_assert(suffix[0] == '\0');
+ i_assert(set->type == CONFIG_KEY_LIST);
+ o_stream_nsend_str(ctx->output, set->key);
+ }
} else {
o_stream_nsend_str(ctx->output, set->key);
}
o_stream_nsend_str(ctx->output, "=");
o_stream_nsend_str(ctx->output, str_tabescape(set->value));
+ if (set->value_stop_list)
+ o_stream_nsend_str(ctx->output, " # stop boollist");
o_stream_nsend_str(ctx->output, "\n");
} T_END;
}
{
const char *suffix;
- if (set->type == CONFIG_KEY_LIST) {
+ if (set->type != CONFIG_KEY_LIST)
+ ;
+ else if (set->list_count == 0 && set->value_stop_list &&
+ set->def_type == SET_BOOLLIST) {
+ /* filter empties a boollist setting */
+ } else {
/* these aren't needed */
return;
}
&ctx->info->defines[set->key_define_idx];
if (def->type == SET_STRLIST || def->type == SET_BOOLLIST) {
const char *suffix;
- if (!str_begins(set->key, def->key, &suffix) ||
- suffix[0] != '/')
+ if (!str_begins(set->key, def->key, &suffix))
i_unreached();
- else {
+ else if (suffix[0] == '/') {
suffix = settings_section_escape(suffix + 1);
+ o_stream_nsend(ctx->output,
+ set->value_stop_list ?
+ SET_BOOLLIST_REPLACE :
+ SET_BOOLLIST_APPEND, 1);
o_stream_nsend(ctx->output, suffix,
strlen(suffix) + 1);
+ } else {
+ /* emptying boollist */
+ i_assert(suffix[0] == '\0');
+ i_assert(set->type == CONFIG_KEY_LIST);
+ o_stream_nsend(ctx->output,
+ SET_BOOLLIST_CLEAR, 1 + 1);
}
}
o_stream_nsend(ctx->output, set->value, strlen(set->value)+1);
static int config_apply_boollist(struct config_parser_context *ctx,
const char *key, const char *value,
- ARRAY_TYPE(const_string) **strlistp)
+ ARRAY_TYPE(const_string) **strlistp,
+ bool *stop_list)
{
ARRAY_TYPE(const_string) boollist;
const char *error;
ctx->error = p_strdup(ctx->pool, error);
return -1;
}
+ /* Preserve stop_list's original value. We may be updating a
+ list within the same filter, and the previous setting might
+ have wanted to stop the list already. */
return config_apply_strlist(ctx, key, value, strlistp);
}
array_push_back(*strlistp, &key);
array_push_back(*strlistp, &yes);
}
+ *stop_list = TRUE;
return 0;
}
switch (l->info->defines[config_key->define_idx].type) {
case SET_STRLIST:
if (config_apply_strlist(ctx, key, value,
- &l->settings[config_key->define_idx].array) < 0)
+ &l->settings[config_key->define_idx].array.values) < 0)
return -1;
break;
case SET_BOOLLIST:
if (config_apply_boollist(ctx, key, value,
- &l->settings[config_key->define_idx].array) < 0)
+ &l->settings[config_key->define_idx].array.values,
+ &l->settings[config_key->define_idx].array.stop_list) < 0)
return -1;
break;
case SET_FILTER_ARRAY:
return -1;
}
if (config_apply_filter_array(ctx, line, value,
- &l->settings[config_key->define_idx].array) < 0)
+ &l->settings[config_key->define_idx].array.values) < 0)
return -1;
break;
case SET_FILE: {
case SET_BOOLLIST: {
unsigned int j, count;
const char *const *strings =
- array_get(p->settings[i].array, &count);
+ array_get(p->settings[i].array.values, &count);
for (j = 0; j < count; j += 2) T_BEGIN {
const char *key = t_strdup_printf("%s/%s",
p->info->defines[i].key,
}
case SET_FILTER_ARRAY: {
const char *name;
- array_foreach_elem(p->settings[i].array, name) T_BEGIN {
+ array_foreach_elem(p->settings[i].array.values, name) T_BEGIN {
(void)settings_parse_keyidx_value(parser, i,
p->info->defines[i].key,
settings_section_escape(name));
master_service_get_import_environment_keyvals() for the original
implementation. */
if (strcmp(key, "import_environment") == 0) {
- unsigned int len = array_count(l->settings[key_idx].array);
+ unsigned int len = array_count(l->settings[key_idx].array.values);
string_t *keyvals = t_str_new(64);
for (unsigned int i = 0; i < len; i += 2) {
- const char *const *key = array_idx(l->settings[key_idx].array, i);
- const char *const *val = array_idx(l->settings[key_idx].array, i + 1);
+ const char *const *key = array_idx(l->settings[key_idx].array.values, i);
+ const char *const *val = array_idx(l->settings[key_idx].array.values, i + 1);
str_append(keyvals, t_strdup_printf("%s=%s", *key, *val));
if (i + 2 < len)
unsigned int set_count;
union config_module_parser_setting {
const char *str;
- ARRAY_TYPE(const_string) *array;
+ struct {
+ ARRAY_TYPE(const_string) *values;
+ bool stop_list;
+ } array;
} *settings; /* [set_count] */
uint8_t *change_counters; /* [set_count] */
/* Set if CONFIG_PARSE_FLAG_DELAY_ERRORS is enabled. The error won't
break;
}
+ bool value_stop_list = FALSE;
dump = FALSE;
str_truncate(ctx->value, 0);
switch (def->type) {
case SET_STRLIST:
case SET_BOOLLIST: {
const ARRAY_TYPE(const_string) *val =
- module_parser->settings[define_idx].array;
+ module_parser->settings[define_idx].array.values;
const char *const *strings;
+ value_stop_list = module_parser->settings[define_idx].array.stop_list;
if (hash_table_is_created(ctx->keys) &&
hash_table_lookup(ctx->keys, def->key) != NULL) {
/* already added all of these */
.key_define_idx = define_idx,
.value = "",
.list_count = count / 2,
+ .value_stop_list = value_stop_list,
};
ctx->callback(&export_set, ctx->context);
strings[i]);
export_set.list_idx = i / 2;
export_set.value = strings[i+1];
- ctx->callback(&export_set, ctx->context);
+ /* only the last element stops the list */
+ export_set.value_stop_list = value_stop_list &&
+ i + 2 == count;
+ if (def->type == SET_BOOLLIST &&
+ strcmp(export_set.value, "no") == 0 &&
+ value_stop_list)
+ ; /* ignore */
+ else
+ ctx->callback(&export_set, ctx->context);
} T_END;
break;
}
case SET_FILTER_ARRAY: {
const ARRAY_TYPE(const_string) *val =
- module_parser->settings[define_idx].array;
+ module_parser->settings[define_idx].array.values;
const char *name;
if (val == NULL)
unsigned int list_idx;
unsigned int list_count;
const char *value;
+ bool value_stop_list;
};
typedef void config_request_callback_t(const struct config_export_setting *set,
value = p_strdup_printf(ctx->pool, "%s=", set->key);
break;
}
+ if (set->value_stop_list && set->def_type == SET_BOOLLIST) {
+ value = p_strdup_printf(ctx->pool, "%s=", set->key);
+ array_push_back(&ctx->strings, &value);
+ }
value = p_strdup_printf(ctx->pool, LIST_KEY_PREFIX"%s=%s",
set->key, set->value);
break;
#define SET_TIME_INFINITE UINT_MAX
#define SET_TIME_MSECS_INFINITE UINT_MAX
+#define SET_BOOLLIST_APPEND "+"
+#define SET_BOOLLIST_REPLACE "$"
+#define SET_BOOLLIST_CLEAR "."
+
#define SET_FILE_INLINE_PREFIX "inline:"
enum setting_type {
whole list is replaced. */
set_apply = FALSE;
}
+ bool list_stop = FALSE, list_clear = FALSE;
+ if (ctx->info->defines[key_idx].type == SET_BOOLLIST ||
+ ctx->info->defines[key_idx].type == SET_STRLIST) {
+ const char *type =
+ (const char *)mmap->mmap_base + offset;
+ if (type[0] == SET_BOOLLIST_REPLACE[0])
+ list_stop = TRUE;
+ else if (type[0] == SET_BOOLLIST_CLEAR[0]) {
+ list_stop = TRUE;
+ list_clear = TRUE;
+ } else if (type[0] != SET_BOOLLIST_APPEND[0]) {
+ *error_r = t_strdup_printf(
+ "List type is invalid (offset=%zu)",
+ offset);
+ return -1;
+ }
+ offset++;
+ }
+
if (ctx->info->defines[key_idx].type == SET_STRLIST ||
ctx->info->defines[key_idx].type == SET_BOOLLIST) {
list_key = (const char *)mmap->mmap_base + offset;
if (!set_apply)
ret = 0;
else T_BEGIN {
- ret = settings_mmap_apply_key(ctx, key_idx, list_key,
- value, error_r);
+ if (list_clear)
+ ret = 0;
+ else {
+ ret = settings_mmap_apply_key(ctx, key_idx,
+ list_key, value, error_r);
+ }
+ if (list_stop) {
+ /* The lists are filled in reverse order.
+ Mark it now as "stopped", so the following
+ list settings won't modify it. */
+ settings_parse_array_stop(ctx->parser, key_idx);
+ }
} T_END_PASS_STR_IF(ret < 0, error_r);
if (ret < 0)
return -1;