expansion-filter-crypt.c \
expansion-program.c \
var-expand.c \
+ var-expand-split.c \
var-expand-parser.y \
var-expand-lexer.l
headers = \
var-expand.h \
+ var-expand-split.h \
var-expand-private.h
pkginc_libdir=$(pkgincludedir)
/* Copyright (c) 2024 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "array.h"
#include "test-common.h"
#include "cpu-count.h"
#include "str.h"
#include "hostpid.h"
#include "var-expand-private.h"
+#include "var-expand-split.h"
#include "expansion.h"
#include "dovecot-version.h"
#include "time-util.h"
test_end();
}
+static void test_var_expand_split(void)
+{
+ test_begin("var_expand_split");
+ pool_t pool = pool_datastack_create();
+ const struct var_expand_params params = {
+ .table = (const struct var_expand_table[]) {
+ { .key = "login", .value = "user" },
+ { .key = "host", .value = "localhost" },
+ { .key = "user", .value = "remote user" },
+ VAR_EXPAND_TABLE_END
+ },
+ };
+
+ const char *prog =
+ "ssh -l%{login} -- %{host} doveadm dsync-server "
+ "-u%{user | upper | lower}";
+ ARRAY_TYPE(const_expansion_program) parts = ARRAY_INIT;
+ const char *const *template;
+ const char *const *ptr;
+ const char *template2;
+ struct var_expand_program *program;
+ const char *error;
+
+ var_expand_program_create(prog, &program, &error);
+
+ const char *placeholder = ";";
+ t_array_init(&parts, 8);
+ var_expand_program_split(pool, program, placeholder, " ", &template, &parts);
+
+ /* expand the split program */
+ unsigned int i = 0;
+ string_t *result = t_str_new(32);
+
+ for (ptr = template; *ptr != NULL; ptr++) {
+ if (*ptr == placeholder) {
+ const struct var_expand_program *p =
+ array_idx_elem(&parts, i++);
+ var_expand_program_execute_one(result, p, ¶ms, &error);
+ } else {
+ str_append(result, *ptr);
+ }
+ if (ptr[1] != NULL)
+ str_append_c(result, ':');
+ }
+
+ test_assert_strcmp("ssh:-l:user:--:localhost:doveadm:dsync-server:"
+ "-u:remote user", str_c(result));
+
+ array_free(&parts);
+ t_array_init(&parts, 8);
+ var_expand_program_template(pool, program, placeholder, &template2, &parts);
+
+ test_assert_strcmp("ssh -l; -- ; doveadm dsync-server -u;", template2);
+
+ var_expand_program_free(&program);
+
+ test_end();
+}
+
int main(int argc, char *const argv[])
{
void (*const tests[])(void) = {
test_var_expand_set_copy,
test_var_expand_generate,
test_var_expand_export_import,
+ test_var_expand_split,
test_var_expand_bench,
NULL
};
--- /dev/null
+/* Copyright (c) 2026 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "var-expand-private.h"
+#include "var-expand-split.h"
+#include "expansion.h"
+
+void var_expand_program_template(pool_t pool, const struct var_expand_program *program,
+ const char *placeholder,
+ const char **template_r,
+ ARRAY_TYPE(const_expansion_program) *parts_r)
+{
+ string_t *dest = str_new(pool, 32);
+ while (program != NULL) {
+ if (program->only_literal) {
+ const char *literal = program->first->params->value.str;
+ str_append(dest, literal);
+ } else {
+ str_append(dest, placeholder);
+ array_push_back(parts_r, &program);
+ }
+ program = program->next;
+ }
+ *template_r = str_c(dest);
+}
+
+void var_expand_program_split(pool_t pool, const struct var_expand_program *program,
+ const char *placeholder, const char *sep,
+ const char *const **template_r,
+ ARRAY_TYPE(const_expansion_program) *parts_r)
+{
+ ARRAY_TYPE(const_string) literals;
+ p_array_init(&literals, pool, 1);
+ while (program != NULL) {
+ if (program->only_literal) {
+ const char *literal = program->first->params->value.str;
+ const char *const *split = (const char *const *)
+ p_strsplit_spaces(pool, literal, sep);
+ array_append(&literals, split, str_array_length(split));
+ } else {
+ array_push_back(&literals, &placeholder);
+ array_push_back(parts_r, &program);
+ }
+ program = program->next;
+ }
+ array_append_zero(&literals);
+ *template_r = array_idx(&literals, 0);
+}
--- /dev/null
+#ifndef VAR_EXPAND_SPLIT_H
+#define VAR_EXPAND_SPLIT_H 1
+
+ARRAY_DEFINE_TYPE(const_expansion_program, const struct var_expand_program*);
+
+void var_expand_program_split(pool_t pool, const struct var_expand_program *program,
+ const char *placeholder, const char *sep,
+ const char *const **template_r,
+ ARRAY_TYPE(const_expansion_program) *parts_r);
+
+void var_expand_program_template(pool_t pool, const struct var_expand_program *program,
+ const char *placeholder,
+ const char **template_r,
+ ARRAY_TYPE(const_expansion_program) *parts_r);
+
+#endif
The function var_expand_program_create() parses a string that can contain
one or more programs, and chains them together. This is usually what is wanted.
+
+ There are some cases though when you need to deal with programs one by one,
+ and this is handled by 'var-expand-split.h', and there are a few functions here
+ that do not seem to make much sense alone:
+
+ The var_expand_program_execute_one() and var_expand_program_has_variable() are
+ such ones. The execute_one() only makes sense to use with individual programs
+ that can be extracted with var_expand_program_template() or
+ var_expand_program_split(), which are in their own header as they have
+ array.h dependency.
+
+ The var_expand_program_has_variable() can be used for generic purposes too
+ by leaving the first_program_only as FALSE.
*/
/* Used for getting either prefix:key values, or dynamic values for keys