]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-var-expand: Add var_expand_program_to_string()
authorAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 20 Mar 2026 09:03:31 +0000 (11:03 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 20 Mar 2026 16:11:43 +0000 (16:11 +0000)
src/lib-var-expand/expansion-program.c
src/lib-var-expand/test-var-expand.c
src/lib-var-expand/var-expand.h

index 5cc50ac8089acb57bccc70ec256f095dc4181afe..8f3668d4a83eb27f0f31d327f16f52530e061ecf 100644 (file)
@@ -376,6 +376,120 @@ const char *var_expand_program_export(const struct var_expand_program *program)
        return str_c(dest);
 }
 
+static void
+var_expand_program_to_string_calculate(const struct var_expand_statement *stmt,
+                                      string_t *dest)
+{
+       if (str_len(dest) > 0)
+               str_append_c(dest, ' ');
+       switch (stmt->params->value.num) {
+       case VAR_EXPAND_STATEMENT_OPER_PLUS:
+               str_append_c(dest, '+');
+               break;
+       case VAR_EXPAND_STATEMENT_OPER_MINUS:
+               str_append_c(dest, '-');
+               break;
+       case VAR_EXPAND_STATEMENT_OPER_STAR:
+               str_append_c(dest, '*');
+               break;
+       case VAR_EXPAND_STATEMENT_OPER_SLASH:
+               str_append_c(dest, '/');
+               break;
+       case VAR_EXPAND_STATEMENT_OPER_MODULO:
+               str_append_c(dest, '%');
+               break;
+       case VAR_EXPAND_STATEMENT_OPER_COUNT:
+       default:
+               i_unreached();
+       }
+       str_append_c(dest, ' ');
+       str_printfa(dest, "%jd", stmt->params->next->value.num);
+}
+
+void
+var_expand_program_to_string_append_one(string_t *dest,
+                                       const struct var_expand_program *program)
+{
+       if (program->only_literal) {
+               i_assert(program->first->params->value_type ==
+                        VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING);
+               str_append(dest, program->first->params->value.str);
+               return;
+       }
+       str_append(dest, "%{");
+       const struct var_expand_statement *stmt = program->first;
+       while (stmt != NULL) {
+               bool braces = FALSE;
+               if (strcmp(stmt->function, "calculate") == 0) {
+                       var_expand_program_to_string_calculate(stmt, dest);
+                       goto next;
+               }
+               str_append(dest, stmt->function);
+               const struct var_expand_parameter *param = stmt->params;
+               if (param != NULL) {
+                       str_append_c(dest, '(');
+                       braces = TRUE;
+               }
+               while (param != NULL) {
+                       if (param->key != NULL) {
+                               str_append(dest, param->key);
+                               str_append_c(dest, '=');
+                       }
+                       switch (param->value_type) {
+                       case VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING:
+                               str_append_c(dest, '\'');
+                               str_append_escaped(dest, param->value.str,
+                                                  strlen(param->value.str));
+                               str_append_c(dest, '\'');
+                               break;
+                       case VAR_EXPAND_PARAMETER_VALUE_TYPE_INT:
+                               str_printfa(dest, "%jd", param->value.num);
+                               break;
+                       case VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE:
+                               str_append(dest, param->value.str);
+                               break;
+                       default:
+                               i_unreached();
+                       }
+                       param = param->next;
+                       if (param != NULL)
+                               str_append(dest, ", ");
+               }
+               if (braces)
+                       str_append_c(dest, ')');
+next:          stmt = stmt->next;
+               if (stmt != NULL && strcmp(stmt->function, "calculate") != 0)
+                       str_append(dest, " | ");
+       }
+       str_append_c(dest, '}');
+}
+
+void var_expand_program_to_string_append(string_t *dest,
+                                        const struct var_expand_program *program)
+{
+       i_assert(program != NULL);
+       i_assert(dest != NULL);
+
+       while (program != NULL) {
+               var_expand_program_to_string_append_one(dest, program);
+               program = program->next;
+       }
+}
+
+const char *var_expand_program_to_string_one(const struct var_expand_program *program)
+{
+       string_t *dest = t_str_new(64);
+       var_expand_program_to_string_append_one(dest, program);
+       return str_c(dest);
+}
+
+const char *var_expand_program_to_string(const struct var_expand_program *program)
+{
+       string_t *dest = t_str_new(64);
+       var_expand_program_to_string_append(dest, program);
+       return str_c(dest);
+}
+
 /* Import code */
 
 static int extract_name(char *data, size_t size,
index e10e39f903574c66560fb93f3665461e1a85a9ee..d3ce05cfdaf8e4f6f2050368df8655314663c8b2 100644 (file)
@@ -28,6 +28,22 @@ struct var_expand_test {
 /* Run with -b to set TRUE */
 static bool do_bench = FALSE;
 
+static void test_assert_program_equal(const char *prog, int idx, const char *file, long long line)
+{
+       struct var_expand_program *program;
+       const char *error;
+       if (var_expand_program_create(prog, &program, &error) < 0) {
+               test_assert_failed_idx(error, file, line, idx);
+               return;
+       }
+       const char *str = var_expand_program_to_string(program);
+       var_expand_program_free(&program);
+       test_assert_strcmp_idx(prog, str, idx);
+}
+
+#define test_assert_program_equal_idx(prog, idx) \
+       test_assert_program_equal((prog), (idx), __FILE__, __LINE__);
+
 static void run_var_expand_tests(const struct var_expand_params *params,
                                 const struct var_expand_test tests[],
                                 size_t test_count)
@@ -1386,6 +1402,38 @@ static void test_var_expand_split(void)
        test_end();
 }
 
+static void test_var_expand_to_string(void)
+{
+       const char *const test_cases[] = {
+               "%{hello}",
+               "%{literal('hello')}'",
+               "%{literal('\\'hello\\'')}",
+               "simple",
+               "simple %{test} case",
+               "%{complex | upper | lower | truncate(5)}",
+               "%{test | func(a=1, b='2', c=t)}",
+               "%{hello | sha256 % 4}",
+               "%{hello % 4}",
+       };
+       for (size_t i = 0; i < N_ELEMENTS(test_cases); i++)
+               test_assert_program_equal_idx(test_cases[i], i);
+
+       const char *template_2 = "%{only} %{first} %{program}";
+       struct var_expand_program *program;
+       const char *error;
+       test_assert_program_equal_idx(template_2, 0);
+
+       if (var_expand_program_create(template_2, &program, &error) < 0) {
+               test_assert_failed(error, __FILE__, __LINE__);
+               return;
+       }
+
+       const char *first = var_expand_program_to_string_one(program);
+       test_assert_strcmp(first, "%{only}");
+
+       var_expand_program_free(&program);
+}
+
 int main(int argc, char *const argv[])
 {
        void (*const tests[])(void) = {
@@ -1407,6 +1455,7 @@ int main(int argc, char *const argv[])
                test_var_expand_generate,
                test_var_expand_export_import,
                test_var_expand_split,
+               test_var_expand_to_string,
                test_var_expand_bench,
                NULL
        };
index f1fc2c20ffc4356a5006c1e3c507fbb9ba9415f0..d46963ae6859101be10009696ba1233451a89321 100644 (file)
@@ -238,6 +238,14 @@ const char *var_expand_program_export(const struct var_expand_program *program);
 void var_expand_program_export_append(string_t *dest,
                                      const struct var_expand_program *program);
 
+/* Reconstruct var_expand program */
+const char *var_expand_program_to_string_one(const struct var_expand_program *program);
+const char *var_expand_program_to_string(const struct var_expand_program *program);
+void var_expand_program_to_string_append_one(string_t *dest,
+                                        const struct var_expand_program *program);
+void var_expand_program_to_string_append(string_t *dest,
+                                        const struct var_expand_program *program);
+
 /* Imports a variable expansion program exported by var_expand_program_export(). */
 
 int var_expand_program_import(const char *data,