From 53ea26031474c64386ef6db43cc802edd684591a Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sat, 5 Jan 2008 23:11:59 +0100
Subject: [PATCH] First successful variable substitutions.

---
 src/lib-sieve/plugins/variables/cmd-set.c     |  19 +-
 .../plugins/variables/ext-variables-common.c  | 332 +++++++++++++++++-
 .../plugins/variables/ext-variables-common.h  |  17 +-
 .../plugins/variables/ext-variables.c         | 108 +-----
 .../plugins/variables/variables.sieve         |   3 +
 src/lib-sieve/sieve-ast.c                     |  15 +-
 src/lib-sieve/sieve-ast.h                     |  97 +++--
 src/lib-sieve/sieve-validator.c               |  52 ++-
 8 files changed, 467 insertions(+), 176 deletions(-)

diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c
index d5e0a4719..2d911f85a 100644
--- a/src/lib-sieve/plugins/variables/cmd-set.c
+++ b/src/lib-sieve/plugins/variables/cmd-set.c
@@ -75,8 +75,6 @@ static bool tag_modifier_validate
 (struct sieve_validator *validator, struct sieve_ast_argument **arg, 
 	struct sieve_command_context *cmd)
 {
-	struct sieve_ast_argument *tag = *arg;
-	
 	/* Skip parameter */
 	*arg = sieve_ast_argument_next(*arg);
 	
@@ -165,7 +163,7 @@ static bool cmd_set_validate(struct sieve_validator *validator,
 		(validator, cmd, arg, "value", 2, SAAT_STRING) ) {
 		return FALSE;
 	}
-	sieve_validator_argument_activate(validator, cmd, arg, TRUE);	
+	sieve_validator_argument_activate(validator, cmd, arg, FALSE);	
 	
 	return TRUE;
 }
@@ -211,11 +209,20 @@ static bool cmd_set_operation_execute
 (const struct sieve_operation *op ATTR_UNUSED,
 	const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
-	struct sieve_variable_storage *storage = 
-		ext_variables_interpreter_get_storage(renv->interp);
-
+	struct sieve_variable_storage *storage;
+	unsigned int var_index;
+	string_t *value;
 	
+	printf(">> SET\n");
 	
+	if ( !ext_variables_opr_variable_read(renv, address, &storage, &var_index) )
+		return FALSE;
+		
+	if ( !sieve_opr_string_read(renv, address, &value) )
+		return FALSE;
+		
+	sieve_variable_assign(storage, var_index, value);
+		
 	return TRUE;
 }
 
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c
index 59826b6b9..9fc481226 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-common.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -14,6 +14,8 @@
 
 #include "ext-variables-common.h"
 
+#include <ctype.h>
+
 /* Forward declarations */
 
 extern const struct ext_variables_set_modifier lower_modifier;
@@ -223,7 +225,11 @@ struct sieve_variable_storage *ext_variables_interpreter_get_storage
 	return ctx->local_storage;
 }
 
-/* Variable arguments */
+/* 
+ * Arguments 
+ */
+
+/* Variable argument */
 
 static bool arg_variable_generate
 	(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
@@ -232,6 +238,28 @@ static bool arg_variable_generate
 const struct sieve_argument variable_argument = 
 	{ "@variable", NULL, NULL, NULL, arg_variable_generate };
 
+static struct sieve_ast_argument *ext_variables_variable_argument_create
+(struct sieve_validator *validator, struct sieve_ast *ast, 
+	unsigned int source_line,	const char *variable)
+{
+	struct ext_variables_validator_context *ctx;
+	struct sieve_variable *var;
+	struct sieve_ast_argument *arg;
+	
+	ctx = ext_variables_validator_context_get(validator);
+	var = sieve_variable_scope_get_variable(ctx->main_scope, variable);
+
+	if ( var == NULL ) 
+		return NULL;
+	
+	arg = sieve_ast_argument_create(ast, source_line);
+	arg->type = SAAT_STRING;
+	arg->argument = &variable_argument;
+	arg->context = (void *) var;
+	
+	return arg;
+}
+
 void ext_variables_variable_argument_activate
 (struct sieve_validator *validator, struct sieve_ast_argument *arg)
 {
@@ -257,21 +285,219 @@ static bool arg_variable_generate
 	return TRUE;
 }
 
-/* Variable operands */
+/* Variable string argument */
+
+static bool arg_variable_string_validate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+		struct sieve_command_context *context);
+static bool arg_variable_string_generate
+(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+	struct sieve_command_context *context);
+
+const struct sieve_argument variable_string_argument = { 
+	"@variable-string", 
+	NULL, 
+	arg_variable_string_validate, 
+	NULL, 
+	arg_variable_string_generate,
+};
+
+struct _variable_string_data {
+	struct sieve_ast_arg_list *str_parts;
+};
+
+inline static struct sieve_ast_argument *_add_string_element
+(struct sieve_ast_arg_list *list, struct sieve_ast_argument *arg)
+{
+	struct sieve_ast_argument *strarg = 
+		sieve_ast_argument_create(arg->ast, arg->source_line);
+	sieve_ast_arg_list_add(list, strarg);
+	strarg->type = SAAT_STRING;
+
+	return strarg;
+}
+
+static bool arg_variable_string_validate
+(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+		struct sieve_command_context *cmd)
+{
+	enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE;
+	pool_t pool = sieve_ast_pool((*arg)->ast);
+	struct sieve_ast_arg_list *arglist = NULL;
+	string_t *str = sieve_ast_argument_str(*arg);
+	string_t *ident;
+	const char *p, *mark, *strstart, *substart = NULL;
+	const char *strval = (const char *) str_data(str);
+	const char *strend = strval + str_len(str);
+	struct _variable_string_data *strdata;
+	bool result = TRUE;
+	
+	//t_push();
+			
+	ident = t_str_new(32);	
+		
+	p = strval;
+	strstart = p;
+	while ( result && p < strend ) {
+		switch ( state ) {
+		case ST_NONE:
+			if ( *p == '$' ) {
+				substart = p;
+				state = ST_OPEN;
+			}
+			p++;
+			break;
+		case ST_OPEN:
+			if ( *p == '{' ) {
+				state = ST_VARIABLE;
+				p++;
+			} else 
+				state = ST_NONE;
+			break;
+		case ST_VARIABLE:
+			mark = p;
+			
+			if ( p < strend ) {
+				if (*p == '_' || isalpha(*p) ) {
+					str_append_c(ident, *p);
+					p++;
+				
+					while ( p < strend && (*p == '_' || isalnum(*p)) ) {
+						str_append_c(ident, *p);
+						p++;
+					}
+					state = ST_CLOSE;
+				} else if ( isdigit(*p) ) {
+					unsigned int num_variable = *p - '0';
+					p++;
+					
+					while ( p < strend && isdigit(*p) ) {
+						num_variable = num_variable*10 + (*p - '0');
+						p++;
+					} 
+					state = ST_CLOSE;
+				} else 
+					state = ST_NONE;
+			}  			
+			
+			break;
+		case ST_CLOSE:
+			if ( *p == '}' ) {				
+				struct sieve_ast_argument *strarg;
+				
+				/* We now know that the substitution is valid */	
+				
+				if ( arglist == NULL ) {
+					arglist = sieve_ast_arg_list_create(pool);
+				}
+				
+				if ( substart > strstart ) {
+					strarg = _add_string_element(arglist, *arg);
+					strarg->_value.str = str_new(pool, substart - strstart);
+					str_append_n(strarg->_value.str, strstart, substart - strstart); 
+					
+					if ( !sieve_validator_argument_activate_super
+						(validator, cmd, strarg, FALSE) )
+						return FALSE;
+						
+					printf("STR: %s\n", t_strdup_until(strstart, substart));
+				}
+				
+				/* Find the variable */
+				strarg = ext_variables_variable_argument_create
+					(validator, (*arg)->ast, (*arg)->source_line, str_c(ident));
+				if ( strarg != NULL )
+					sieve_ast_arg_list_add(arglist, strarg);
+
+				printf("VAR: %s\n", str_c(ident));
+				str_truncate(ident, 0);
+				
+				strstart = p + 1;
+				substart = strstart;
+			}
+			state = ST_NONE;
+			p++;	
+		}
+	}
+
+	//t_pop();
+	
+	if ( arglist == NULL ) {
+		printf("STR: %s\n", strval);
+		return sieve_validator_argument_activate_super
+			(validator, cmd, *arg, TRUE);
+	}
+	
+	if ( substart > strstart ) {
+		struct sieve_ast_argument *strarg = _add_string_element(arglist, *arg);
+		strarg->_value.str = str_new(pool, substart - strstart);
+		str_append_n(strarg->_value.str, strstart, substart - strstart); 
+		
+		if ( !sieve_validator_argument_activate_super
+			(validator, cmd, strarg, FALSE) )
+			return FALSE;
+
+		printf("STR: %s\n", t_strdup_until(strstart, substart));
+	}	
+	
+	strdata = p_new(pool, struct _variable_string_data, 1);
+	strdata->str_parts = arglist;
+	
+	(*arg)->context = (void *) strdata;
+
+	return TRUE;
+}
+
+#define _string_data_first(data) __AST_LIST_FIRST((data)->str_parts)
+#define _string_data_count(data) __AST_LIST_COUNT((data)->str_parts)
+#define _string_data_next(item) __AST_LIST_NEXT(item)
+
+static bool arg_variable_string_generate
+(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+	struct sieve_command_context *cmd) 
+{
+	struct sieve_binary *sbin = sieve_generator_get_binary(generator);
+	struct _variable_string_data *strdata = 
+		(struct _variable_string_data *) arg->context;
+	struct sieve_ast_argument *strpart;
+	
+	if ( _string_data_count(strdata) == 1 )
+		sieve_generate_argument(generator, _string_data_first(strdata), cmd);
+	else {
+		ext_variables_opr_variable_string_emit(sbin, _string_data_count(strdata));
+
+		strpart = _string_data_first(strdata);
+		while ( strpart != NULL ) {
+			if ( !sieve_generate_argument(generator, strpart, cmd) )
+				return FALSE;
+			
+			strpart = _string_data_next(strpart);
+		}
+	}
+	
+	return TRUE;
+}
+
+/* 
+ * Operands 
+ */
+
+/* Variable operand */
 
-static bool opr_variable_read
+static bool opr_variable_read_value
 	(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str);
 static bool opr_variable_dump
 	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
 
 const struct sieve_opr_string_interface variable_interface = { 
 	opr_variable_dump,
-	opr_variable_read
+	opr_variable_read_value
 };
 		
 const struct sieve_operand variable_operand = { 
 	"variable", 
-	&variables_extension, 0,
+	&variables_extension, 
+	EXT_VARIABLES_OPERAND_VARIABLE,
 	&string_class,
 	&variable_interface
 };	
@@ -297,14 +523,17 @@ static bool opr_variable_dump
 	return FALSE;
 }
 
-static bool opr_variable_read
+static bool opr_variable_read_value
   (const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str)
 { 
+	struct sieve_variable_storage *storage;
 	sieve_size_t index = 0;
 	
+	storage = ext_variables_interpreter_get_storage(renv->interp);
+	if ( storage == NULL ) return FALSE;
+		
 	if (sieve_binary_read_integer(renv->sbin, address, &index) ) {
-		*str = t_str_new(10);
-		str_append(*str, "VARIABLE");
+		sieve_variable_get(storage, index, str);
 
 		return TRUE;
 	}
@@ -312,20 +541,97 @@ static bool opr_variable_read
 	return FALSE;
 }
 
-bool ext_variables_opr_variable_assign
-	(struct sieve_binary *sbin, sieve_size_t *address, string_t *str)
+bool ext_variables_opr_variable_read
+	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+		struct sieve_variable_storage **storage, unsigned int *var_index)
 {
-	sieve_size_t index = 0;
+	const struct sieve_operand *operand = sieve_operand_read(renv->sbin, address);
+	sieve_size_t idx = 0;
 	
-	if (sieve_binary_read_integer(sbin, address, &index) ) {
-		/* FIXME: Assign */
+	if ( operand != &variable_operand ) 
+		return FALSE;
+		
+	*storage = ext_variables_interpreter_get_storage(renv->interp);
+	if ( *storage == NULL ) return FALSE;
 	
+	if (sieve_binary_read_integer(renv->sbin, address, &idx) ) {
+		*var_index = idx;
 		return TRUE;
 	}
 	
 	return FALSE;
 }
 
+/* Variable string operand */
+
+static bool opr_variable_string_read
+	(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str);
+static bool opr_variable_string_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
+
+const struct sieve_opr_string_interface variable_string_interface = { 
+	opr_variable_string_dump,
+	opr_variable_string_read
+};
+		
+const struct sieve_operand variable_string_operand = { 
+	"variable-string", 
+	&variables_extension, 
+	EXT_VARIABLES_OPERAND_VARIABLE_STRING,
+	&string_class,
+	&variable_string_interface
+};	
+
+void ext_variables_opr_variable_string_emit
+	(struct sieve_binary *sbin, unsigned int elements) 
+{
+	(void) sieve_operand_emit_code
+		(sbin, &variable_string_operand, ext_variables_my_id);
+	(void) sieve_binary_emit_integer(sbin, elements);
+}
+
+static bool opr_variable_string_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address) 
+{
+	sieve_size_t elements = 0;
+	unsigned int i;
+	
+	if (!sieve_binary_read_integer(denv->sbin, address, &elements) )
+		return FALSE;
+	
+	sieve_code_dumpf(denv, "VARSTR [%ld]:", (long) elements);
+
+	sieve_code_descend(denv);
+	for ( i = 0; i < (unsigned int) elements; i++ ) {
+		sieve_opr_string_dump(denv, address);
+	}
+	sieve_code_ascend(denv);
+	
+	return TRUE;
+}
+
+static bool opr_variable_string_read
+  (const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str)
+{ 
+	sieve_size_t elements = 0;
+	unsigned int i;
+		
+	if ( !sieve_binary_read_integer(renv->sbin, address, &elements) )
+		return FALSE;
+
+	*str = t_str_new(128);
+	for ( i = 0; i < (unsigned int) elements; i++ ) {
+		string_t *strelm;
+		
+		if ( !sieve_opr_string_read(renv, address, &strelm) ) 
+			return FALSE;
+		
+		str_append_str(*str, strelm);
+	}
+
+	return TRUE;
+}
+
 /* Set modifier registration */
 
 const struct ext_variables_set_modifier *ext_variables_set_modifier_find
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.h b/src/lib-sieve/plugins/variables/ext-variables-common.h
index 5dcadf54b..7e9db1f19 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-common.h
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.h
@@ -14,6 +14,11 @@ enum ext_variables_opcode {
 	EXT_VARIABLES_OPERATION_STRING
 };
 
+enum ext_variables_operand {
+	EXT_VARIABLES_OPERAND_VARIABLE,
+	EXT_VARIABLES_OPERAND_VARIABLE_STRING
+};
+
 /* Extension */
 
 void ext_variables_validator_initialize(struct sieve_validator *validator);
@@ -40,12 +45,20 @@ struct ext_variables_set_modifier {
 const struct ext_variables_set_modifier *ext_variables_set_modifier_find
 	(struct sieve_validator *validator, const char *identifier);
 	
+/* Arguments */
+
+extern const struct sieve_argument variable_string_argument;
+	
 /* Variables */
 
 void ext_variables_opr_variable_emit
 	(struct sieve_binary *sbin, struct sieve_variable *var);
-bool ext_variables_opr_variable_assign
-	(struct sieve_binary *sbin, sieve_size_t *address, string_t *str);
+bool ext_variables_opr_variable_read
+	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+		struct sieve_variable_storage **storage, unsigned int *var_index);
+
+void ext_variables_opr_variable_string_emit
+	(struct sieve_binary *sbin, unsigned int elements);
 
 void ext_variables_variable_argument_activate
 	(struct sieve_validator *validator, struct sieve_ast_argument *arg);
diff --git a/src/lib-sieve/plugins/variables/ext-variables.c b/src/lib-sieve/plugins/variables/ext-variables.c
index 4f2f41f1a..0b3828892 100644
--- a/src/lib-sieve/plugins/variables/ext-variables.c
+++ b/src/lib-sieve/plugins/variables/ext-variables.c
@@ -43,6 +43,12 @@ const struct sieve_operation *ext_variables_operations[] = {
 /* Operands */
 
 extern const struct sieve_operand variable_operand;
+extern const struct sieve_operand variable_string_operand;
+
+const struct sieve_operation *ext_variables_operands[] = {
+	&variable_operand, &variable_string_operand
+};
+
 
 /* Extension definitions */
 
@@ -55,7 +61,7 @@ struct sieve_extension variables_extension = {
 	NULL, NULL,
 	ext_variables_interpreter_load, 
 	SIEVE_EXT_DEFINE_OPERATIONS(ext_variables_operations), 
-	SIEVE_EXT_DEFINE_OPERAND(variable_operand)
+	SIEVE_EXT_DEFINE_OPERANDS(ext_variables_operands)
 };
 
 static bool ext_variables_load(int ext_id) 
@@ -64,109 +70,13 @@ static bool ext_variables_load(int ext_id)
 	return TRUE;
 }
 
-/* New argument */
-
-static bool arg_variable_string_validate
-	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
-		struct sieve_command_context *context);
-
-const struct sieve_argument variable_string_argument =
-	{ "@variable-string", NULL, arg_variable_string_validate, NULL, NULL };
-
-static bool arg_variable_string_validate
-(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
-		struct sieve_command_context *cmd)
-{
-	bool result = TRUE;
-	enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } 
-		state = ST_NONE;
-	string_t *str = sieve_ast_argument_str(*arg);
-	string_t *tmpstr, *newstr = NULL;
-	const char *p, *mark, *strstart, *substart = NULL;
-	const char *strval = (const char *) str_data(str);
-	const char *strend = strval + str_len(str);
-
-	T_FRAME(		
-		tmpstr = t_str_new(32);	
-			
-		p = strval;
-		strstart = p;
-		while ( result && p < strend ) {
-			switch ( state ) {
-			case ST_NONE:
-				if ( *p == '$' ) {
-					substart = p;
-					state = ST_OPEN;
-				}
-				p++;
-				break;
-			case ST_OPEN:
-				if ( *p == '{' ) {
-					state = ST_VARIABLE;
-					p++;
-				} else 
-					state = ST_NONE;
-				break;
-			case ST_VARIABLE:
-				mark = p;
-				
-				if ( p < strend ) {
-					if (*p == '_' || isalpha(*p) ) {
-						p++;
-					
-						while ( p < strend && (*p == '_' || isalnum(*p)) ) {
-							p++;
-						}
-					} else if ( isdigit(*p) ) {
-						unsigned int num_variable = *p - '0';
-						p++;
-						
-						while ( p < strend && isdigit(*p) ) {
-							num_variable = num_variable*10 + (*p - '0');
-							p++;
-						} 
-					}
-				} 			
-				
-				break;
-			case ST_CLOSE:
-				if ( *p == '}' ) {				
-					/* We now know that the substitution is valid */	
-					
-					if ( newstr == NULL ) {
-						newstr = str_new(sieve_ast_pool((*arg)->ast), str_len(str)*2);
-					}
-					
-					str_append_n(newstr, strstart, substart-strstart);
-					str_append_str(newstr, tmpstr);
-					
-					strstart = p + 1;
-					substart = strstart;
-				}
-				state = ST_NONE;
-				p++;	
-			}
-		}
-	);
-	
-	if ( newstr != NULL ) {
-		if ( strstart != strend )
-			str_append_n(newstr, strstart, strend-strstart);	
-	
-		sieve_ast_argument_str_set(*arg, newstr);
-	}
-	
-	return sieve_validator_argument_activate_super
-		(validator, cmd, *arg, TRUE);
-}
-
 /* Load extension into validator */
 
 static bool ext_variables_validator_load
 	(struct sieve_validator *validator)
 {
-	/*sieve_validator_argument_override(validator, SAT_VAR_STRING, 
-		&variable_string_argument);*/ 
+	sieve_validator_argument_override(validator, SAT_VAR_STRING, 
+		&variable_string_argument); 
 		
 	sieve_validator_register_command(validator, &cmd_set);
 	sieve_validator_register_command(validator, &tst_string);
diff --git a/src/lib-sieve/plugins/variables/variables.sieve b/src/lib-sieve/plugins/variables/variables.sieve
index 498c48ae7..207dbc0ec 100644
--- a/src/lib-sieve/plugins/variables/variables.sieve
+++ b/src/lib-sieve/plugins/variables/variables.sieve
@@ -1,4 +1,7 @@
 require "variables";
+require "fileinto";
 
 set :upper "foo" "foosome";
 set :lower "bar" "bareable";
+
+fileinto "${foo}.${bar}";
diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c
index 500a0717f..c3bef9fb7 100644
--- a/src/lib-sieve/sieve-ast.c
+++ b/src/lib-sieve/sieve-ast.c
@@ -135,16 +135,16 @@ static void sieve_ast_list_add( struct sieve_ast_list *list, struct sieve_ast_no
 	__LIST_ADD(list, node)
 
 /* List of argument AST nodes */
-static struct sieve_ast_arg_list *sieve_ast_arg_list_create( pool_t pool ) 
+struct sieve_ast_arg_list *sieve_ast_arg_list_create( pool_t pool ) 
 	__LIST_CREATE(pool, struct sieve_ast_arg_list)
 	
-static void sieve_ast_arg_list_add
+void sieve_ast_arg_list_add
 	( struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument )
 	__LIST_ADD(list, argument)
 
-inline static void sieve_ast_arg_list_substitute
-(struct sieve_ast_arg_list *list, 
-	struct sieve_ast_argument *argument, struct sieve_ast_argument *replacement) 
+void sieve_ast_arg_list_substitute
+(struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument, 
+	struct sieve_ast_argument *replacement)
 {
 	if ( list->head == argument )
 		list->head = replacement;
@@ -269,10 +269,11 @@ static void sieve_ast_argument_substitute
 }
 
 /* Argument AST node */
-static struct sieve_ast_argument *sieve_ast_argument_create
+struct sieve_ast_argument *sieve_ast_argument_create
 	(struct sieve_ast *ast, unsigned int source_line) 
 {	
-	struct sieve_ast_argument *arg = p_new(ast->pool, struct sieve_ast_argument, 1);
+	struct sieve_ast_argument *arg = 
+		p_new(ast->pool, struct sieve_ast_argument, 1);
 	
 	arg->ast = ast;
 	
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index 1cbfcbefd..76262be54 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -34,13 +34,13 @@
 	                 |   [number | tag | *string]
 	                 .
 	                 
-	 Tests and commands are defined using the same structure: sieve_ast_node. However, arguments and 
-	 string-lists are described using sieve_ast_argument.  
+	 Tests and commands are defined using the same structure: sieve_ast_node. 
+	 However, arguments and string-lists are described using sieve_ast_argument.  
 */
 
-/* IMPORTANT NOTICE: Do not decorate the AST with objects other than those allocated on 
- * the ast's pool or static const objects. Otherwise it is possible that pointers in the tree 
- * become dangling which is highly undesirable.
+/* IMPORTANT NOTICE: Do not decorate the AST with objects other than those 
+ * allocated on the ast's pool or static const objects. Otherwise it is possible 
+ * that pointers in the tree become dangling which is highly undesirable.
  */
 
 struct sieve_ast_list;
@@ -166,6 +166,17 @@ void sieve_ast_error
 	struct sieve_ast_node *node, const char *fmt, va_list args);
 	
 /* sieve_ast_argument */
+
+struct sieve_ast_argument *sieve_ast_argument_create
+	(struct sieve_ast *ast, unsigned int source_line);
+
+struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool);	
+void sieve_ast_arg_list_add
+	(struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument);
+void sieve_ast_arg_list_substitute
+(struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument, 
+	struct sieve_ast_argument *replacement);
+
 struct sieve_ast_argument *sieve_ast_argument_string_create
 	(struct sieve_ast_node *node, const string_t *str, unsigned int source_line);
 struct sieve_ast_argument *sieve_ast_argument_tag_create
@@ -186,17 +197,21 @@ const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type);
 	sieve_ast_argument_type_name((argument)->type)
 
 void sieve_ast_stringlist_add
-	(struct sieve_ast_argument *list, const string_t *str, unsigned int source_line);
+	(struct sieve_ast_argument *list, const string_t *str, 
+		unsigned int source_line);
 void sieve_ast_stringlist_add_strc
-	(struct sieve_ast_argument *list, const char *str, unsigned int source_line);
+	(struct sieve_ast_argument *list, const char *str, 
+		unsigned int source_line);
 
 /* sieve_ast_test */
 struct sieve_ast_node *sieve_ast_test_create
-	(struct sieve_ast_node *parent, const char *identifier, unsigned int source_line);
+	(struct sieve_ast_node *parent, const char *identifier, 
+		unsigned int source_line);
 	
 /* sieve_ast_command */
 struct sieve_ast_node *sieve_ast_command_create
-	(struct sieve_ast_node *parent, const char *identifier, unsigned int source_line);
+	(struct sieve_ast_node *parent, const char *identifier, 
+		unsigned int source_line);
 	
 /* Debug */
 void sieve_ast_unparse(struct sieve_ast *ast);
@@ -204,45 +219,54 @@ void sieve_ast_unparse(struct sieve_ast *ast);
 /* AST access macros */
 
 /* Generic list access macros */
-#define __LIST_FIRST(node, list) ((node)->list == NULL ? NULL : (node)->list->head)
-#define __LIST_LAST(node, list) ((node)->list == NULL ? NULL : (node)->list->tail)
-#define __LIST_NEXT(item) ((item)->next)
-#define __LIST_PREV(item) ((item)->prev)
-#define __LIST_COUNT(node, list) ((node)->list == NULL || (node)->list->head == NULL ? 0 : (node)->list->len)
+#define __AST_LIST_FIRST(list) \
+	((list) == NULL ? NULL : (list)->head)
+#define __AST_LIST_LAST(list) \
+	((list) == NULL ? NULL : (list)->tail)
+#define __AST_LIST_COUNT(list) \
+	((list) == NULL || (list)->head == NULL ? 0 : (list)->len)
+#define __AST_LIST_NEXT(item) ((item)->next)
+#define __AST_LIST_PREV(item) ((item)->prev)
+
+#define __AST_NODE_LIST_FIRST(node, list) __AST_LIST_FIRST((node)->list)
+#define __AST_NODE_LIST_LAST(node, list) __AST_LIST_LAST((node)->list)
+#define __AST_NODE_LIST_COUNT(node, list) __AST_LIST_COUNT((node)->list)
 
 /* AST macros */
 
 /* AST node macros */
 #define sieve_ast_node_pool(node) (sieve_ast_pool((node)->ast))
 #define sieve_ast_node_parent(node) ((node)->parent)
-#define sieve_ast_node_prev(node) __LIST_PREV(node)
-#define sieve_ast_node_next(node) __LIST_NEXT(node)
+#define sieve_ast_node_prev(node) __AST_LIST_PREV(node)
+#define sieve_ast_node_next(node) __AST_LIST_NEXT(node)
 #define sieve_ast_node_type(node) ((node) == NULL ? SAT_NONE : (node)->type)
 #define sieve_ast_node_line(node) ((node) == NULL ? 0 : (node)->source_line)
 
 /* AST command node macros */
-#define sieve_ast_command_first(node) __LIST_FIRST(node, commands)
-#define sieve_ast_command_prev(command) __LIST_PREV(command)
-#define sieve_ast_command_next(command) __LIST_NEXT(command)
-#define sieve_ast_command_count(node) __LIST_COUNT(node, commands)
+#define sieve_ast_command_first(node) __AST_NODE_LIST_FIRST(node, commands)
+#define sieve_ast_command_count(node) __AST_NODE_LIST_COUNT(node, commands)
+#define sieve_ast_command_prev(command) __AST_LIST_PREV(command)
+#define sieve_ast_command_next(command) __AST_LIST_NEXT(command)
 
 /* Compare the identifier of the previous command */
 #define sieve_ast_prev_cmd_is(cmd, id) \
 	( (cmd)->prev == NULL ? FALSE : strncasecmp((cmd)->prev->identifier, id, sizeof(id)-1) == 0 )
 	
 /* AST test macros */
-#define sieve_ast_test_first(node) __LIST_FIRST(node, tests)
-#define sieve_ast_test_next(test) __LIST_NEXT(test)
-#define sieve_ast_test_count(node) __LIST_COUNT(node, tests)
+#define sieve_ast_test_count(node) __AST_NODE_LIST_COUNT(node, tests)
+#define sieve_ast_test_first(node) __AST_NODE_LIST_FIRST(node, tests)
+#define sieve_ast_test_next(test) __AST_LIST_NEXT(test)
 
 /* AST argument macros */
-#define sieve_ast_argument_first(node) __LIST_FIRST(node, arguments)
-#define sieve_ast_argument_last(node) __LIST_LAST(node, arguments)
-#define sieve_ast_argument_prev(argument) __LIST_PREV(argument)
-#define sieve_ast_argument_next(argument) __LIST_NEXT(argument)
-#define sieve_ast_argument_count(node) __LIST_COUNT(node, arguments)
-#define sieve_ast_argument_type(argument) ((argument) == NULL ? SAAT_NONE : (argument)->type)
-#define sieve_ast_argument_line(argument) ((argument) == NULL ? 0 : (argument)->source_line)
+#define sieve_ast_argument_first(node) __AST_NODE_LIST_FIRST(node, arguments)
+#define sieve_ast_argument_last(node) __AST_NODE_LIST_LAST(node, arguments)
+#define sieve_ast_argument_count(node) __AST_NODE_LIST_COUNT(node, arguments)
+#define sieve_ast_argument_prev(argument) __AST_LIST_PREV(argument)
+#define sieve_ast_argument_next(argument) __AST_LIST_NEXT(argument)
+#define sieve_ast_argument_type(argument) \
+	((argument) == NULL ? SAAT_NONE : (argument)->type)
+#define sieve_ast_argument_line(argument) \
+	((argument) == NULL ? 0 : (argument)->source_line)
 
 #define sieve_ast_argument_str(argument) ((argument)->_value.str)
 #define sieve_ast_argument_strc(argument) (str_c((argument)->_value.str))
@@ -254,12 +278,15 @@ void sieve_ast_unparse(struct sieve_ast *ast);
 
 /* AST string list macros */
 // @UNSAFE: should check whether we are actually accessing a string list
-#define sieve_ast_strlist_first(list) __LIST_FIRST(list, _value.strlist)
-#define sieve_ast_strlist_last(list) __LIST_LAST(list, _value.strlist)
-#define sieve_ast_strlist_next(str) __LIST_NEXT(str)
-#define sieve_ast_strlist_prev(str) __LIST_PREV(str)
+#define sieve_ast_strlist_first(list) \
+	__AST_NODE_LIST_FIRST(list, _value.strlist)
+#define sieve_ast_strlist_last(list) \
+	__AST_NODE_LIST_LAST(list, _value.strlist)
+#define sieve_ast_strlist_count(list) \
+	__AST_NODE_LIST_COUNT(list, _value.strlist)
+#define sieve_ast_strlist_next(str) __AST_LIST_NEXT(str)
+#define sieve_ast_strlist_prev(str) __AST_LIST_PREV(str)
 #define sieve_ast_strlist_str(str) sieve_ast_argument_str(str)
 #define sieve_ast_strlist_strc(str) sieve_ast_argument_strc(str)
-#define sieve_ast_strlist_count(list) __LIST_COUNT(list, _value.strlist)
 
 #endif /* __SIEVE_AST_H */
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index c6bbff2ee..16f0aa79d 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -37,8 +37,10 @@ struct sieve_validator {
 	
 	ARRAY_DEFINE(ext_contexts, void *);
 	
+	/* This is currently a wee bit ugly and needs more thought */
 	struct sieve_default_argument default_arguments[SAT_COUNT];
 	struct sieve_default_argument *current_defarg;
+	enum sieve_argument_type current_defarg_type;
 };
 
 /* Predeclared statics */
@@ -427,12 +429,15 @@ void sieve_validator_argument_override
 (struct sieve_validator *validator, enum sieve_argument_type type, 
 	const struct sieve_argument *argument)
 {
-	struct sieve_default_argument *arg = 
-		p_new(validator->pool, struct sieve_default_argument, 1);
+	struct sieve_default_argument *arg;
 	
-	*arg = validator->default_arguments[type];
+	if ( validator->default_arguments[type].argument != NULL ) {
+		arg = p_new(validator->pool, struct sieve_default_argument, 1);
+		*arg = validator->default_arguments[type];	
+		
+		validator->default_arguments[type].overrides = arg;
+	}
 	
-	validator->default_arguments[type].overrides = arg;
 	validator->default_arguments[type].argument = argument;
 }
 
@@ -459,12 +464,29 @@ bool sieve_validator_argument_activate_super
 (struct sieve_validator *validator, struct sieve_command_context *cmd, 
 	struct sieve_ast_argument *arg, bool constant ATTR_UNUSED)
 {
-	if ( validator->current_defarg == NULL && 
-		validator->current_defarg->overrides == NULL )
+	struct sieve_default_argument *defarg;
+	
+	if ( validator->current_defarg == NULL )
 		return FALSE;
 	
+	if ( validator->current_defarg->overrides == NULL ) {
+		switch ( validator->current_defarg_type ) {
+		case SAT_NUMBER:
+		case SAT_CONST_STRING:
+		case SAT_STRING_LIST:
+			return FALSE;
+		case SAT_VAR_STRING:
+			validator->current_defarg_type = SAT_CONST_STRING;
+			defarg = &validator->default_arguments[validator->current_defarg_type];
+			break;
+		default: 
+			return FALSE;
+		}
+	} else
+		defarg = validator->current_defarg->overrides;
+	
 	return sieve_validator_argument_default_activate
-		(validator, cmd, validator->current_defarg->overrides, arg);
+		(validator, cmd, defarg, arg);
 }
 
 bool sieve_validator_argument_activate
@@ -474,23 +496,25 @@ bool sieve_validator_argument_activate
 	struct sieve_default_argument *defarg;
 	
 	switch ( sieve_ast_argument_type(arg) ) {
-	case SAAT_NUMBER:
-		defarg = &validator->default_arguments[SAT_NUMBER];
+	case SAAT_NUMBER:	
+		validator->current_defarg_type = SAT_NUMBER;
 		break;
 	case SAAT_STRING:
-		if ( validator->default_arguments[SAT_VAR_STRING].argument == NULL ||
-			constant )
-			defarg = &validator->default_arguments[SAT_CONST_STRING];
+		if ( constant || 
+			validator->default_arguments[SAT_VAR_STRING].argument == NULL )
+			validator->current_defarg_type = SAT_CONST_STRING;
 		else
-			defarg = &validator->default_arguments[SAT_VAR_STRING];
+			validator->current_defarg_type = SAT_VAR_STRING;
 		break;
 	case SAAT_STRING_LIST:
-		defarg = &validator->default_arguments[SAT_STRING_LIST];
+		validator->current_defarg_type = SAT_STRING_LIST;
 		break;
 	default:
 		return FALSE;
 	}
 	
+	defarg = &validator->default_arguments[validator->current_defarg_type];
+	
 	return sieve_validator_argument_default_activate(validator, cmd, defarg, arg);
 }
 
-- 
GitLab