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,
/* 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)
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) = {
test_var_expand_generate,
test_var_expand_export_import,
test_var_expand_split,
+ test_var_expand_to_string,
test_var_expand_bench,
NULL
};