]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-var-expand: Add var_expand_program_template|split()
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 29 Jan 2026 09:19:25 +0000 (11:19 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 20 Mar 2026 16:11:43 +0000 (16:11 +0000)
These functions can be used to separate literals and actual programs
from a expansion program, this can be useful when template needs
to be processed for SQL queries or similar purposes.

src/lib-var-expand/Makefile.am
src/lib-var-expand/test-var-expand.c
src/lib-var-expand/var-expand-split.c [new file with mode: 0644]
src/lib-var-expand/var-expand-split.h [new file with mode: 0644]
src/lib-var-expand/var-expand.h

index 10c05c06ea0617c98b3ef6144551484480d5f99c..6a1b4cd2ff27443d9bcedd4c60baad7393c70cc2 100644 (file)
@@ -35,6 +35,7 @@ libvar_expand_la_SOURCES = \
        expansion-filter-crypt.c \
        expansion-program.c \
        var-expand.c \
+       var-expand-split.c \
        var-expand-parser.y \
        var-expand-lexer.l
 
@@ -50,6 +51,7 @@ noinst_HEADERS = \
 
 headers = \
        var-expand.h \
+       var-expand-split.h \
        var-expand-private.h
 
 pkginc_libdir=$(pkgincludedir)
index a3ddf82bb773db08bd1414d9595d36b51117a10a..e10e39f903574c66560fb93f3665461e1a85a9ee 100644 (file)
@@ -1,11 +1,13 @@
 /* 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"
@@ -1325,6 +1327,65 @@ static void test_var_expand_bench(void)
        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, &params, &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) = {
@@ -1345,6 +1406,7 @@ int main(int argc, char *const argv[])
                test_var_expand_set_copy,
                test_var_expand_generate,
                test_var_expand_export_import,
+               test_var_expand_split,
                test_var_expand_bench,
                NULL
        };
diff --git a/src/lib-var-expand/var-expand-split.c b/src/lib-var-expand/var-expand-split.c
new file mode 100644 (file)
index 0000000..e9badd0
--- /dev/null
@@ -0,0 +1,50 @@
+/* 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);
+}
diff --git a/src/lib-var-expand/var-expand-split.h b/src/lib-var-expand/var-expand-split.h
new file mode 100644 (file)
index 0000000..be75409
--- /dev/null
@@ -0,0 +1,16 @@
+#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
index c71fba21b284ad87d4764f2be995cf8aa4c2e62d..e0393054208ac270d46f3ff12ea08cbb5bda7ffe 100644 (file)
 
   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