From b0868f2efa43beebbef32ea0ae9417f11ae68c2d Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 26 Oct 2007 22:13:41 +0200
Subject: [PATCH] First steps towards implementing code generation and
 interpretation for proper comperators.

---
 sieve/tests/comparator.sieve                  |   6 +
 src/lib-sieve/Makefile.am                     |   2 +
 src/lib-sieve/plugins/vacation/ext-vacation.c |  37 ++++-
 src/lib-sieve/sieve-ast.c                     |  81 ++++++++--
 src/lib-sieve/sieve-ast.h                     |  21 ++-
 src/lib-sieve/sieve-code.c                    |  82 ++++++-----
 src/lib-sieve/sieve-code.h                    |  16 +-
 src/lib-sieve/sieve-commands.h                |   4 +-
 src/lib-sieve/sieve-common.h                  |   2 +-
 src/lib-sieve/sieve-comparators.c             | 139 ++++++++++++++++++
 src/lib-sieve/sieve-comparators.h             |  19 +++
 src/lib-sieve/sieve-generator.c               |  17 +++
 src/lib-sieve/sieve-generator.h               |   3 +
 src/lib-sieve/sieve-validator.c               |  95 +++++++-----
 src/lib-sieve/sieve-validator.h               |  15 +-
 src/lib-sieve/tst-size.c                      |  25 ++--
 16 files changed, 447 insertions(+), 117 deletions(-)
 create mode 100644 sieve/tests/comparator.sieve
 create mode 100644 src/lib-sieve/sieve-comparators.c
 create mode 100644 src/lib-sieve/sieve-comparators.h

diff --git a/sieve/tests/comparator.sieve b/sieve/tests/comparator.sieve
new file mode 100644
index 000000000..12de98223
--- /dev/null
+++ b/sieve/tests/comparator.sieve
@@ -0,0 +1,6 @@
+if header :is :comparator "i;ascii-casemap" "from" "STEPHAN@drunksnipers.com" {
+	discard;
+	stop;
+}
+
+keep;
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 2af9df205..9c08e2289 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -37,6 +37,7 @@ libsieve_a_SOURCES = \
 	sieve-interpreter.c \
 	sieve-result.c \
 	sieve-error.c \
+	sieve-comparators.c \
 	sieve-commands.c \
 	sieve-code.c \
 	$(tsts) \
@@ -55,6 +56,7 @@ noinst_HEADERS = \
 	sieve-interpreter.h \
 	sieve-result.h
 	sieve-error.h \
+	sieve-comparators.h \
 	sieve-commands.h \
 	sieve-code.h \
 	sieve-extensions.h \
diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c
index 64f2da6cf..a24ffc338 100644
--- a/src/lib-sieve/plugins/vacation/ext-vacation.c
+++ b/src/lib-sieve/plugins/vacation/ext-vacation.c
@@ -40,7 +40,10 @@ static bool cmd_vacation_validate_days_tag
 			"the :days tag for the vacation command requires one number argument, but %s was found", sieve_ast_argument_name(*arg) );
 		return FALSE;
 	}
-	
+
+	/* Skip argument */
+	*arg = sieve_ast_argument_next(*arg);
+
 	/* FIXME: assign somewhere */
 	
 	return TRUE;
@@ -64,6 +67,9 @@ static bool cmd_vacation_validate_subject_tag
 		return FALSE;
 	}
 	
+	/* Skip tag */
+	*arg = sieve_ast_argument_next(*arg);
+
 	/* FIXME: assign somewhere */
 	
 	return TRUE;
@@ -86,7 +92,10 @@ static bool cmd_vacation_validate_from_tag
 				sieve_ast_argument_name(*arg) );
 		return FALSE;
 	}
-	
+
+	/* Skip argument */
+	*arg = sieve_ast_argument_next(*arg);
+
 	/* FIXME: assign somewhere */
 	
 	return TRUE;
@@ -112,6 +121,9 @@ static bool cmd_vacation_validate_addresses_tag
 	
 	/* FIXME: assign somewhere */
 	
+	/* Skip tag */
+	*arg = sieve_ast_argument_next(*arg);
+
 	return TRUE;
 }
 
@@ -120,6 +132,9 @@ static bool cmd_vacation_validate_mime_tag
 	struct sieve_ast_argument **arg ATTR_UNUSED, 
 	struct sieve_command_context *cmd ATTR_UNUSED)
 {
+	/* Skip tag */
+	*arg = sieve_ast_argument_next(*arg);
+
 	/* FIXME: assign somewhere */
 		
 	return TRUE;
@@ -150,12 +165,18 @@ static bool cmd_vacation_validate_handle_tag
 
 /* Command registration */
 
-static const struct sieve_tag vacation_days_tag = { "days", cmd_vacation_validate_days_tag };
-static const struct sieve_tag vacation_subject_tag = { "subject", cmd_vacation_validate_subject_tag };
-static const struct sieve_tag vacation_from_tag = { "from", cmd_vacation_validate_from_tag };
-static const struct sieve_tag vacation_addresses_tag = { "addresses", cmd_vacation_validate_addresses_tag };
-static const struct sieve_tag vacation_mime_tag = { "mime", cmd_vacation_validate_mime_tag };
-static const struct sieve_tag vacation_handle_tag = { "handle", cmd_vacation_validate_handle_tag };
+static const struct sieve_argument vacation_days_tag = 
+	{ "days", cmd_vacation_validate_days_tag, NULL };
+static const struct sieve_argument vacation_subject_tag =
+	{ "subject", cmd_vacation_validate_subject_tag, NULL };
+static const struct sieve_argument vacation_from_tag = 
+	{ "from", cmd_vacation_validate_from_tag, NULL };
+static const struct sieve_argument vacation_addresses_tag = 
+	{ "addresses", cmd_vacation_validate_addresses_tag, NULL };
+static const struct sieve_argument vacation_mime_tag = 
+	{ "mime", cmd_vacation_validate_mime_tag, NULL };
+static const struct sieve_argument vacation_handle_tag = 
+	{ "handle", cmd_vacation_validate_handle_tag, NULL };
 
 static bool cmd_vacation_registered(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
 {
diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c
index ab1e45e2e..3b45748df 100644
--- a/src/lib-sieve/sieve-ast.c
+++ b/src/lib-sieve/sieve-ast.c
@@ -28,6 +28,7 @@
 			list->tail = node; \
 		} \
 		list->len++; \
+		node->list = list; \
 	}	 
 	
 /* List of AST nodes */
@@ -44,12 +45,49 @@ static struct sieve_ast_arg_list *sieve_ast_arg_list_create( pool_t pool )
 static void sieve_ast_arg_list_add( struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument )
 	__LIST_ADD(list, argument)
 
+static struct sieve_ast_argument *sieve_ast_arg_list_delete
+	(const struct sieve_ast_argument *first, const unsigned int count)
+{
+	const struct sieve_ast_argument *last;
+	unsigned int left;
+	
+	i_assert(first->list != NULL);
+	
+	/* Find the last of the deleted arguments */
+	left = count - 1;
+	last = first;
+	while ( left > 0 && last->next != NULL ) {
+		left--;
+		last = last->next;
+	}
+
+	/* Perform substitution */
+		
+	if ( first->list->head == first ) 
+		first->list->head = last->next;
+
+	if ( first->list->tail == last )
+		first->list->tail = first->prev;
+		
+	if ( first->prev != NULL )
+		first->prev->next = last->next;
+		
+	if ( last->next != NULL )
+		last->next->prev = first->prev;
+		
+	first->list->len -= count - left;
+	
+	/* Deleted objects are freed along with AST pool */
+	
+	return last->next;
+}
+
 /* AST Node */
 
 static struct sieve_ast_node *sieve_ast_node_create
 	(struct sieve_ast *ast, struct sieve_ast_node *parent, enum sieve_ast_type type, 
-	unsigned int source_line) {
-
+	unsigned int source_line) 
+{
 	struct sieve_ast_node *node = p_new(ast->pool, struct sieve_ast_node, 1);
 	
 	node->ast = ast;
@@ -71,7 +109,8 @@ static struct sieve_ast_node *sieve_ast_node_create
 	return node;
 }
 
-static void sieve_ast_node_add_command(struct sieve_ast_node *node, struct sieve_ast_node *command) {
+static void sieve_ast_node_add_command(struct sieve_ast_node *node, struct sieve_ast_node *command) 
+{
 	i_assert( command->type == SAT_COMMAND && (node->type == SAT_ROOT || node->type == SAT_COMMAND) );
 	
 	if (node->commands == NULL) node->commands = sieve_ast_list_create(node->ast->pool);
@@ -79,7 +118,8 @@ static void sieve_ast_node_add_command(struct sieve_ast_node *node, struct sieve
 	sieve_ast_list_add(node->commands, command);
 }
 
-static void sieve_ast_node_add_test(struct sieve_ast_node *node, struct sieve_ast_node *test) {
+static void sieve_ast_node_add_test(struct sieve_ast_node *node, struct sieve_ast_node *test) 
+{
 	i_assert( test->type == SAT_TEST && (node->type == SAT_TEST || node->type == SAT_COMMAND) );
 	
 	if (node->tests == NULL) node->tests = sieve_ast_list_create(node->ast->pool);
@@ -87,7 +127,8 @@ static void sieve_ast_node_add_test(struct sieve_ast_node *node, struct sieve_as
 	sieve_ast_list_add(node->tests, test);
 }
 
-static void sieve_ast_node_add_argument(struct sieve_ast_node *node, struct sieve_ast_argument *argument) {
+static void sieve_ast_node_add_argument(struct sieve_ast_node *node, struct sieve_ast_argument *argument) 
+{
 	i_assert( node->type == SAT_TEST || node->type == SAT_COMMAND );
 	
 	if (node->arguments == NULL) node->arguments = sieve_ast_arg_list_create(node->ast->pool);
@@ -97,8 +138,8 @@ static void sieve_ast_node_add_argument(struct sieve_ast_node *node, struct siev
 
 /* Argument AST node */
 static struct sieve_ast_argument *sieve_ast_argument_create
-	(struct sieve_ast *ast, unsigned int source_line) {
-	
+	(struct sieve_ast *ast, unsigned int source_line) 
+{	
 	struct sieve_ast_argument *arg = p_new(ast->pool, struct sieve_ast_argument, 1);
 	
 	arg->ast = ast;
@@ -107,13 +148,15 @@ static struct sieve_ast_argument *sieve_ast_argument_create
 	arg->next = NULL;
 	
 	arg->source_line = source_line;
+	arg->context = NULL;
+	arg->argument = NULL;
 			
 	return arg;
 }
 
 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_node *node, const string_t *str, unsigned int source_line) 
+{	
 	struct sieve_ast_argument *argument = sieve_ast_argument_create
 		(node->ast, source_line);
 		
@@ -129,7 +172,8 @@ struct sieve_ast_argument *sieve_ast_argument_string_create
 }
 
 struct sieve_ast_argument *sieve_ast_argument_stringlist_create
-	(struct sieve_ast_node *node, unsigned int source_line) {
+	(struct sieve_ast_node *node, unsigned int source_line) 
+{
 
 	struct sieve_ast_argument *argument = sieve_ast_argument_create(node->ast, source_line);
 	
@@ -142,7 +186,8 @@ struct sieve_ast_argument *sieve_ast_argument_stringlist_create
 }
 
 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) 
+{
 	struct sieve_ast_argument *stritem;
 	
 	i_assert( list->type == SAAT_STRING_LIST );
@@ -162,8 +207,8 @@ void sieve_ast_stringlist_add
 }
 
 struct sieve_ast_argument *sieve_ast_argument_tag_create
-	(struct sieve_ast_node *node, const char *tag, unsigned int source_line) {
-	
+	(struct sieve_ast_node *node, const char *tag, unsigned int source_line) 
+{	
 	struct sieve_ast_argument *argument = 
 		sieve_ast_argument_create(node->ast, source_line);
 	
@@ -176,7 +221,8 @@ struct sieve_ast_argument *sieve_ast_argument_tag_create
 }
 
 struct sieve_ast_argument *sieve_ast_argument_number_create
-	(struct sieve_ast_node *node, int number, unsigned int source_line) {
+	(struct sieve_ast_node *node, int number, unsigned int source_line) 
+{
 	
 	struct sieve_ast_argument *argument = 
 		sieve_ast_argument_create(node->ast, source_line);
@@ -189,6 +235,12 @@ struct sieve_ast_argument *sieve_ast_argument_number_create
 	return argument;
 }
 
+struct sieve_ast_argument *sieve_ast_arguments_delete
+	(struct sieve_ast_argument *first, unsigned int count) 
+{	
+	return sieve_ast_arg_list_delete(first, count);
+}
+
 const char *sieve_ast_argument_name(struct sieve_ast_argument *argument) {
 	switch ( argument->type ) {
 	
@@ -197,6 +249,7 @@ const char *sieve_ast_argument_name(struct sieve_ast_argument *argument) {
 	case SAAT_STRING: return "a string";
 	case SAAT_NUMBER: return "a number";
 	case SAAT_TAG: return "a tag";
+	
 	default: return "??ARGUMENT??";
 	}
 }
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index bcbeae6a6..1288e8d44 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -49,10 +49,12 @@ struct sieve_ast_value;
 
 enum sieve_ast_argument_type {
 	SAAT_NONE,
+	
+	/* Arguments generated by parser */
 	SAAT_STRING,
 	SAAT_STRING_LIST,
 	SAAT_TAG,
-	SAAT_NUMBER
+	SAAT_NUMBER,
 };
 
 struct sieve_ast_argument {
@@ -61,9 +63,13 @@ struct sieve_ast_argument {
 	/* Back reference to the AST object */
 	struct sieve_ast *ast;
 
+	/* List related */
+	struct sieve_ast_arg_list *list;
 	struct sieve_ast_argument *next;
 	struct sieve_ast_argument *prev;
   
+  /* Parser-assigned data */
+  
 	union {	
 		string_t *str;
 		struct sieve_ast_arg_list *strlist;
@@ -72,8 +78,13 @@ struct sieve_ast_argument {
   } _value;
   
   unsigned int source_line;
+  
+  /* Assigned during validation */
 
-	/* Arbitrary data associated with this ast element */
+	/* Argument associated with this ast element  */
+	const struct sieve_argument *argument;
+
+	/* Context data associated with this ast element */
 	void *context;
 };
 
@@ -105,7 +116,8 @@ struct sieve_ast_node {
 	/* Back reference to this node's parent */
 	struct sieve_ast_node *parent;
 	
-	/* Linked list references to tree siblings */
+	/* Linked list references */
+	struct sieve_ast_list *list;
 	struct sieve_ast_node *next;
 	struct sieve_ast_node *prev;
 	
@@ -144,6 +156,9 @@ struct sieve_ast_argument *sieve_ast_argument_tag_create
 struct sieve_ast_argument *sieve_ast_argument_number_create
 	(struct sieve_ast_node *node, int number, unsigned int source_line);
 
+struct sieve_ast_argument *sieve_ast_arguments_delete
+	(struct sieve_ast_argument *first, unsigned int count);
+	
 const char *sieve_ast_argument_name(struct sieve_ast_argument *argument);
 
 void sieve_ast_stringlist_add
diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c
index fafb6c1be..b3c6ecde1 100644
--- a/src/lib-sieve/sieve-code.c
+++ b/src/lib-sieve/sieve-code.c
@@ -3,26 +3,32 @@
 #include "sieve-code.h"
 #include "sieve-interpreter.h"
 
-static bool sieve_code_dump_jmp(struct sieve_interpreter *interpreter);
-static bool sieve_code_dump_jmptrue(struct sieve_interpreter *interpreter);
-static bool sieve_code_dump_jmpfalse(struct sieve_interpreter *interpreter);
-static bool sieve_code_dump_stop(struct sieve_interpreter *interpreter);
-static bool sieve_code_dump_keep(struct sieve_interpreter *interpreter);
-static bool sieve_code_dump_discard(struct sieve_interpreter *interpreter);
-
-static bool sieve_code_execute_jmp(struct sieve_interpreter *interpreter);
-static bool sieve_code_execute_jmptrue(struct sieve_interpreter *interpreter);
-static bool sieve_code_execute_jmpfalse(struct sieve_interpreter *interpreter);
-static bool sieve_code_execute_stop(struct sieve_interpreter *interpreter);
-static bool sieve_code_execute_keep(struct sieve_interpreter *interpreter);
-static bool sieve_code_execute_discard(struct sieve_interpreter *interpreter);
-
-const struct sieve_opcode sieve_opcode_jmp = { sieve_code_dump_jmp, sieve_code_execute_jmp };
-const struct sieve_opcode sieve_opcode_jmptrue = { sieve_code_dump_jmptrue, sieve_code_execute_jmptrue };
-const struct sieve_opcode sieve_opcode_jmpfalse = { sieve_code_dump_jmpfalse, sieve_code_execute_jmpfalse };
-const struct sieve_opcode sieve_opcode_stop = { sieve_code_dump_stop, sieve_code_execute_stop };
-const struct sieve_opcode sieve_opcode_keep = { sieve_code_dump_keep, sieve_code_execute_keep };
-const struct sieve_opcode sieve_opcode_discard = { sieve_code_dump_discard, sieve_code_execute_discard };
+/* Operands */
+
+
+
+/* Opcodes */
+
+static bool opc_jmp_dump(struct sieve_interpreter *interpreter);
+static bool opc_jmptrue_dump(struct sieve_interpreter *interpreter);
+static bool opc_jmpfalse_dump(struct sieve_interpreter *interpreter);
+static bool opc_stop_dump(struct sieve_interpreter *interpreter);
+static bool	opc_keep_dump(struct sieve_interpreter *interpreter);
+static bool opc_discard_dump(struct sieve_interpreter *interpreter);
+
+static bool opc_jmp_execute(struct sieve_interpreter *interpreter);
+static bool opc_jmptrue_execute(struct sieve_interpreter *interpreter);
+static bool opc_jmpfalse_execute(struct sieve_interpreter *interpreter);
+static bool opc_stop_execute(struct sieve_interpreter *interpreter);
+static bool opc_keep_execute(struct sieve_interpreter *interpreter);
+static bool opc_discard_execute(struct sieve_interpreter *interpreter);
+
+const struct sieve_opcode jmp_opcode = { opc_jmp_dump, opc_jmp_execute };
+const struct sieve_opcode jmptrue_opcode = { opc_jmptrue_dump, opc_jmptrue_execute };
+const struct sieve_opcode jmpfalse_opcode = { opc_jmpfalse_dump, opc_jmpfalse_execute };
+const struct sieve_opcode stop_opcode = { opc_stop_dump, opc_stop_execute };
+const struct sieve_opcode keep_opcode = { opc_keep_dump, opc_keep_execute };
+const struct sieve_opcode discard_opcode = { opc_discard_dump, opc_discard_execute };
 
 extern const struct sieve_opcode tst_address_opcode;
 extern const struct sieve_opcode tst_header_opcode;
@@ -31,12 +37,12 @@ extern const struct sieve_opcode tst_size_over_opcode;
 extern const struct sieve_opcode tst_size_under_opcode;
 
 const struct sieve_opcode *sieve_opcodes[] = {
-  &sieve_opcode_jmp,
-  &sieve_opcode_jmptrue, 
-  &sieve_opcode_jmpfalse,
-  &sieve_opcode_stop,
-  &sieve_opcode_keep,
-  &sieve_opcode_discard,
+  &jmp_opcode,
+  &jmptrue_opcode, 
+  &jmpfalse_opcode,
+  &stop_opcode,
+  &keep_opcode,
+  &discard_opcode,
 
   &tst_address_opcode,
   &tst_header_opcode,
@@ -50,7 +56,7 @@ const unsigned int sieve_opcode_count =
 
 /* Code dump for core commands */
 
-static bool sieve_code_dump_jmp(struct sieve_interpreter *interpreter)
+static bool opc_jmp_dump(struct sieve_interpreter *interpreter)
 {
 	unsigned int pc = sieve_interpreter_program_counter(interpreter);
 	int offset;
@@ -63,7 +69,7 @@ static bool sieve_code_dump_jmp(struct sieve_interpreter *interpreter)
 	return TRUE;
 }	
 		
-static bool sieve_code_dump_jmptrue(struct sieve_interpreter *interpreter)
+static bool opc_jmptrue_dump(struct sieve_interpreter *interpreter)
 {	
 	unsigned int pc = sieve_interpreter_program_counter(interpreter);
 	int offset;
@@ -76,7 +82,7 @@ static bool sieve_code_dump_jmptrue(struct sieve_interpreter *interpreter)
 	return TRUE;
 }
 
-static bool sieve_code_dump_jmpfalse(struct sieve_interpreter *interpreter)
+static bool opc_jmpfalse_dump(struct sieve_interpreter *interpreter)
 {	
 	unsigned int pc = sieve_interpreter_program_counter(interpreter);
 	int offset;
@@ -89,21 +95,21 @@ static bool sieve_code_dump_jmpfalse(struct sieve_interpreter *interpreter)
 	return TRUE;
 }	
 	
-static bool sieve_code_dump_stop(struct sieve_interpreter *interpreter ATTR_UNUSED)
+static bool opc_stop_dump(struct sieve_interpreter *interpreter ATTR_UNUSED)
 {	
 	printf("STOP\n");
 	
 	return TRUE;
 }
 
-static bool sieve_code_dump_keep(struct sieve_interpreter *interpreter ATTR_UNUSED)
+static bool opc_keep_dump(struct sieve_interpreter *interpreter ATTR_UNUSED)
 {	
 	printf("KEEP\n");
 	
 	return TRUE;
 }
 
-static bool sieve_code_dump_discard(struct sieve_interpreter *interpreter ATTR_UNUSED)
+static bool opc_discard_dump(struct sieve_interpreter *interpreter ATTR_UNUSED)
 {	
 	printf("DISCARD\n");
 	
@@ -112,7 +118,7 @@ static bool sieve_code_dump_discard(struct sieve_interpreter *interpreter ATTR_U
 
 /* Code execution for core commands */
 
-static bool sieve_code_execute_jmp(struct sieve_interpreter *interpreter)
+static bool opc_jmp_execute(struct sieve_interpreter *interpreter)
 {
 	printf("JMP\n");
 	if ( !sieve_interpreter_program_jump(interpreter, TRUE) )
@@ -121,7 +127,7 @@ static bool sieve_code_execute_jmp(struct sieve_interpreter *interpreter)
 	return TRUE;
 }	
 		
-static bool sieve_code_execute_jmptrue(struct sieve_interpreter *interpreter)
+static bool opc_jmptrue_execute(struct sieve_interpreter *interpreter)
 {	
 	if ( !sieve_interpreter_program_jump(interpreter,
 		sieve_interpreter_get_test_result(interpreter)) )
@@ -132,7 +138,7 @@ static bool sieve_code_execute_jmptrue(struct sieve_interpreter *interpreter)
 	return TRUE;
 }
 
-static bool sieve_code_execute_jmpfalse(struct sieve_interpreter *interpreter)
+static bool opc_jmpfalse_execute(struct sieve_interpreter *interpreter)
 {	
 	if ( !sieve_interpreter_program_jump(interpreter,
 		!sieve_interpreter_get_test_result(interpreter)) )
@@ -143,21 +149,21 @@ static bool sieve_code_execute_jmpfalse(struct sieve_interpreter *interpreter)
 	return TRUE;
 }	
 	
-static bool sieve_code_execute_stop(struct sieve_interpreter *interpreter ATTR_UNUSED)
+static bool opc_stop_execute(struct sieve_interpreter *interpreter ATTR_UNUSED)
 {	
 	printf(">> STOP\n");
 	
 	return FALSE;
 }
 
-static bool sieve_code_execute_keep(struct sieve_interpreter *interpreter ATTR_UNUSED)
+static bool opc_keep_execute(struct sieve_interpreter *interpreter ATTR_UNUSED)
 {	
 	printf(">> KEEP\n");
 	
 	return TRUE;
 }
 
-static bool sieve_code_execute_discard(struct sieve_interpreter *interpreter ATTR_UNUSED)
+static bool opc_discard_execute(struct sieve_interpreter *interpreter ATTR_UNUSED)
 {	
 	printf(">> DISCARD\n");
 	
diff --git a/src/lib-sieve/sieve-code.h b/src/lib-sieve/sieve-code.h
index 3e34eb5fc..c3a2041c1 100644
--- a/src/lib-sieve/sieve-code.h
+++ b/src/lib-sieve/sieve-code.h
@@ -9,6 +9,7 @@
 
 typedef size_t sieve_size_t;
 
+/* Opcode: identifies what's to be done */
 struct sieve_opcode {
 	bool (*dump)(struct sieve_interpreter *interpreter);
 	bool (*execute)(struct sieve_interpreter *interpreter);
@@ -31,10 +32,19 @@ enum sieve_core_operation {
 extern const struct sieve_opcode *sieve_opcodes[];
 extern const unsigned int sieve_opcode_count;
 
+/* Operand: argument to and opcode */
+struct sieve_operand {
+	bool (*dump)(struct sieve_interpreter *interpreter);
+	bool (*execute)(struct sieve_interpreter *interpreter);
+};
+
 enum sieve_core_operand {
-  SIEVE_OPERAND_NUMBER       = 0x01,
-  SIEVE_OPERAND_STRING       = 0x02,
-  SIEVE_OPERAND_STRING_LIST  = 0x03
+  SIEVE_OPERAND_NUMBER,
+  SIEVE_OPERAND_STRING,
+  SIEVE_OPERAND_STRING_LIST,
+  SIEVE_OPERAND_COMPARATOR,
+  SIEVE_OPERAND_MATCH_TYPE,
+  SIEVE_OPERAND_ADDR_PART  
 };
 
 #define SIEVE_OPCODE_CORE_MASK  0x1F
diff --git a/src/lib-sieve/sieve-commands.h b/src/lib-sieve/sieve-commands.h
index 98148da09..42a9efb55 100644
--- a/src/lib-sieve/sieve-commands.h
+++ b/src/lib-sieve/sieve-commands.h
@@ -13,11 +13,13 @@ struct sieve_command_context;
 
 /* Command */
 
-struct sieve_tag {
+struct sieve_argument {
 	const char *identifier;
 	
 	bool (*validate)(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
 		struct sieve_command_context *context);
+	bool (*generate)(struct sieve_generator *generator, struct sieve_ast_argument **arg, 
+		struct sieve_command_context *context);
 };
 
 enum sieve_command_type {
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index 4bdce1511..60d83b922 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -11,7 +11,7 @@ struct sieve_ast_node;
 struct sieve_ast_argument;
 
 /* sieve-commands.h */
-struct sieve_tag;
+struct sieve_argument;
 struct sieve_command;
 struct sieve_command_context;
 struct sieve_command_registration;
diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c
new file mode 100644
index 000000000..1a451bed4
--- /dev/null
+++ b/src/lib-sieve/sieve-comparators.c
@@ -0,0 +1,139 @@
+#include "lib.h"
+#include "compat.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-comparators.h"
+
+#include <string.h>
+
+/* 
+ * Predeclarations 
+ */
+
+static int cmp_i_octet_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size);
+static int cmp_i_ascii_casemap_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size);
+
+/* 
+ * Comparator tag 
+ */
+ 
+static bool tag_comparator_validate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd);
+
+const struct sieve_argument comparator_tag = 
+	{ "comparator", tag_comparator_validate, NULL };
+
+static bool tag_comparator_validate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+	const struct sieve_comparator *cmp;
+	
+	/* Skip tag */
+	*arg = sieve_ast_argument_next(*arg);
+	
+	/* Check syntax:
+	 *   ":comparator" <comparator-name: string>
+	 */
+	if ( (*arg)->type != SAAT_STRING ) {
+		sieve_command_validate_error(validator, cmd, 
+			":comparator tag requires one string argument, but %s was found", sieve_ast_argument_name(*arg) );
+		return FALSE;
+	}
+	
+	/* Get comparator from registry */
+	cmp = sieve_validator_find_comparator(validator, sieve_ast_argument_strc(*arg));
+	
+	if ( cmp == NULL ) {
+		sieve_command_validate_error(validator, cmd, 
+			"unknown comparator '%s'", sieve_ast_argument_strc(*arg));
+
+		return FALSE;
+	}
+	
+	/* String argument not needed during code generation, so delete it */
+	*arg = sieve_ast_arguments_delete(*arg, 1);
+	
+	/* Store comparator in context */
+	tag->context = (void *) cmp;
+	
+	return TRUE;
+}
+
+static bool tag_comparator_generate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	
+}
+
+/* 
+ * Core comparators  
+ */
+
+const struct sieve_comparator i_octet_comparator = {
+	"i;octet",
+	cmp_i_octet_compare
+};
+
+const struct sieve_comparator i_ascii_casemap_comparator = {
+	"i;ascii-casemap",
+	cmp_i_ascii_casemap_compare
+};
+
+const struct sieve_comparator *sieve_core_comparators[] = {
+	&i_octet_comparator, &i_ascii_casemap_comparator
+};
+
+const unsigned int sieve_core_comparators_count =
+	(sizeof(sieve_core_comparators) / sizeof(sieve_core_comparators[0]));
+
+
+static int cmp_i_octet_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size)
+{
+	int result;
+
+	if ( val1_size == val2_size ) {
+		return memcmp(val1, val2, val1_size);
+	} 
+	
+	if ( val1_size > val2_size ) {
+		result = memcmp(val1, val2, val2_size);
+		
+		if ( result == 0 ) return 1;
+		
+		return result;
+	} 
+
+	result = memcmp(val1, val2, val1_size);
+		
+	if ( result == 0 ) return -1;
+		
+	return result;
+}
+
+static int cmp_i_ascii_casemap_compare(const void *val1, size_t val1_size, const void *val2, size_t val2_size)
+{
+	int result;
+
+	if ( val1_size == val2_size ) {
+		return strncasecmp((const char *) val1, (const char *) val2, val1_size);
+	} 
+	
+	if ( val1_size > val2_size ) {
+		result = strncasecmp((const char *) val1, (const char *) val2, val2_size);
+		
+		if ( result == 0 ) return 1;
+		
+		return result;
+	} 
+
+	result = strncasecmp((const char *) val1, (const char *) val2, val1_size);
+		
+	if ( result == 0 ) return -1;
+		
+	return result;
+}
diff --git a/src/lib-sieve/sieve-comparators.h b/src/lib-sieve/sieve-comparators.h
new file mode 100644
index 000000000..5583a02d2
--- /dev/null
+++ b/src/lib-sieve/sieve-comparators.h
@@ -0,0 +1,19 @@
+#ifndef __SIEVE_COMPARATORS_H
+#define __SIEVE_COMPARATORS_H
+
+struct sieve_comparator {
+	const char *identifier;
+	
+	/* Equality, ordering, prefix and substring match */
+	
+	/* ( output similar to strncmp ) */
+	int (*compare)(const void *val1, size_t val1_size, const void *val2, size_t val2_size);
+};
+
+extern const struct sieve_argument comparator_tag;
+
+extern const struct sieve_comparator *sieve_core_comparators[];
+extern const unsigned int sieve_core_comparators_count;
+
+
+#endif /* __SIEVE_COMPARATORS_H */
diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c
index e5a715817..7dfbd47aa 100644
--- a/src/lib-sieve/sieve-generator.c
+++ b/src/lib-sieve/sieve-generator.c
@@ -266,6 +266,23 @@ sieve_size_t sieve_generator_emit_ext_opcode
 
 /* Generator functions */
 
+bool sieve_generate_arguments(struct sieve_generator *generator, 
+	struct sieve_command_context *cmd, struct sieve_ast_argument **arg)
+{
+	/* Parse all arguments with assigned generator function */
+	while ( *arg != NULL && (*arg)->argument != NULL) {
+		const struct sieve_argument *argument = (*arg)->argument;
+		
+		/* Call the generation function for the argument */ 
+		if ( argument->generate != NULL ) { 
+			if ( !argument->generate(generator, arg, cmd) ) 
+				return FALSE;
+		} else break;
+	}
+	
+	return TRUE;
+}
+
 bool sieve_generate_test
 	(struct sieve_generator *generator, struct sieve_ast_node *tst_node,
 		struct sieve_jumplist *jlist, bool jump_true) 
diff --git a/src/lib-sieve/sieve-generator.h b/src/lib-sieve/sieve-generator.h
index cffaf0580..73a7f0a9e 100644
--- a/src/lib-sieve/sieve-generator.h
+++ b/src/lib-sieve/sieve-generator.h
@@ -54,6 +54,9 @@ bool sieve_generator_emit_stringlist_argument
 	(struct sieve_generator *generator, struct sieve_ast_argument *arg);
 
 /* AST generation API */
+bool sieve_generate_arguments(struct sieve_generator *generator, 
+	struct sieve_command_context *cmd, struct sieve_ast_argument **arg);
+
 bool sieve_generate_block(struct sieve_generator *generator, struct sieve_ast_node *block);
 bool sieve_generate_test(struct sieve_generator *generator, struct sieve_ast_node *tst_node, 
 	struct sieve_jumplist *jlist, bool jump_true);
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index 8da75647c..22b501d8c 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -7,6 +7,7 @@
 #include "sieve-commands-private.h"
 #include "sieve-validator.h"
 #include "sieve-extensions.h"
+#include "sieve-comparators.h"
 
 /* Context/Semantics checker implementation */
 
@@ -17,13 +18,15 @@ struct sieve_validator {
 	struct sieve_error_handler *ehandler;
 	
 	/* Registries */
-	struct hash_table *commands; 
+	struct hash_table *commands;
+	struct hash_table *comparators; 
 };
 
 /* Predeclared statics */
 
 static void sieve_validator_register_core_commands(struct sieve_validator *validator);
 static void sieve_validator_register_core_tests(struct sieve_validator *validator);
+static void sieve_validator_register_core_comparators(struct sieve_validator *validator);
 
 /* Error management */
 
@@ -66,12 +69,18 @@ struct sieve_validator *sieve_validator_create(struct sieve_ast *ast, struct sie
 	sieve_validator_register_core_commands(validator);
 	sieve_validator_register_core_tests(validator);
 	
+	/* Setup comparator registry */
+	validator->comparators = hash_create
+		(pool, pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
+	sieve_validator_register_core_comparators(validator);
+	
 	return validator;
 }
 
 void sieve_validator_free(struct sieve_validator *validator) 
 {
 	hash_destroy(&validator->commands);
+	hash_destroy(&validator->comparators);
 	
 	sieve_ast_unref(&validator->ast);
 	pool_unref(&(validator->pool));
@@ -165,11 +174,11 @@ static bool _unknown_tag_validate
 	return FALSE;
 }
 
-static const struct sieve_tag _unknown_tag = { "", _unknown_tag_validate };
+static const struct sieve_argument _unknown_tag = { "", _unknown_tag_validate, NULL };
 
 void sieve_validator_register_tag
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
-	const struct sieve_tag *tag) 
+	const struct sieve_argument *tag) 
 {
 	if ( cmd_reg->tags == NULL ) {
 		cmd_reg->tags = hash_create
@@ -191,12 +200,12 @@ static void sieve_validator_register_unknown_tag
 	hash_insert(cmd_reg->tags, (void *) tag, (void *) &_unknown_tag);
 }
 
-static const struct sieve_tag *sieve_validator_find_tag
+static const struct sieve_argument *sieve_validator_find_tag
 	(struct sieve_command_registration *cmd_reg, const char *tag) 
 {
 	if ( cmd_reg->tags == NULL ) return NULL;
 	
-  return (struct sieve_tag *) hash_lookup(cmd_reg->tags, tag);
+  return (struct sieve_argument *) hash_lookup(cmd_reg->tags, tag);
 }
 
 /* Extension support */
@@ -222,30 +231,30 @@ const struct sieve_extension *sieve_validator_load_extension
 	return ext;
 }
 
-/* Comparator validation */
+/* Comparator registry */
 
-static bool sieve_validate_comparator_tag
-	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
-	struct sieve_command_context *cmd)
+void sieve_validator_register_comparator
+	(struct sieve_validator *validator, const struct sieve_comparator *cmp) 
 {
-	/* Only one possible tag, so we don't bother checking the identifier */
-	*arg = sieve_ast_argument_next(*arg);
+	hash_insert(validator->comparators, (void *) cmp->identifier, (void *) cmp);
+}
+
+const struct sieve_comparator *sieve_validator_find_comparator
+		(struct sieve_validator *validator, const char *comparator) 
+{
+  return 	(const struct sieve_comparator *) hash_lookup(validator->comparators, comparator);
+}
+
+static void sieve_validator_register_core_comparators(struct sieve_validator *validator) 
+{
+	unsigned int i;
 	
-	/* Check syntax:
-	 *   ":comparator" <comparator-name: string>
-	 */
-	if ( (*arg)->type != SAAT_STRING ) {
-		sieve_command_validate_error(validator, cmd, 
-			":comparator tag requires one string argument, but %s was found", sieve_ast_argument_name(*arg) );
-		return FALSE;
+	for ( i = 0; i < sieve_core_comparators_count; i++ ) {
+		sieve_validator_register_comparator(validator, sieve_core_comparators[i]); 
 	}
-	
-	/* FIXME: assign comparator somewhere */
-	
-	return TRUE;
 }
 
-static const struct sieve_tag comparator_tag = { "comparator", sieve_validate_comparator_tag };
+/* Comparator validation */
 
 void sieve_validator_link_comparator_tag
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
@@ -257,9 +266,13 @@ void sieve_validator_link_comparator_tag
 
 static bool sieve_validate_match_type_tag
 	(struct sieve_validator *validator ATTR_UNUSED, 
-	struct sieve_ast_argument **arg ATTR_UNUSED, 
+	struct sieve_ast_argument **arg, 
 	struct sieve_command_context *cmd ATTR_UNUSED)
 {
+	/* Skip tag */
+	*arg = sieve_ast_argument_next(*arg);
+
+
 	/* Syntax:   
 	 *   ":is" / ":contains" / ":matches"
    */
@@ -269,9 +282,12 @@ static bool sieve_validate_match_type_tag
 	return TRUE;
 }
 
-static const struct sieve_tag match_is_tag = { "is", sieve_validate_match_type_tag };
-static const struct sieve_tag match_contains_tag = { "contains", sieve_validate_match_type_tag };
-static const struct sieve_tag match_matches_tag = { "matches", sieve_validate_match_type_tag };
+static const struct sieve_argument match_is_tag = 
+	{ "is", sieve_validate_match_type_tag, NULL };
+static const struct sieve_argument match_contains_tag = 
+	{ "contains", sieve_validate_match_type_tag, NULL };
+static const struct sieve_argument match_matches_tag = 
+	{ "matches", sieve_validate_match_type_tag, NULL };
 
 void sieve_validator_link_match_type_tags
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
@@ -285,9 +301,12 @@ void sieve_validator_link_match_type_tags
 
 static bool sieve_validate_address_part_tag
 	(struct sieve_validator *validator ATTR_UNUSED, 
-	struct sieve_ast_argument **arg ATTR_UNUSED, 
+	struct sieve_ast_argument **arg, 
 	struct sieve_command_context *cmd ATTR_UNUSED)
 {
+	/* Skip argument */
+	*arg = sieve_ast_argument_next(*arg);
+
 	/* Syntax:   
 	 *   ":localpart" / ":domain" / ":all"
    */
@@ -297,9 +316,12 @@ static bool sieve_validate_address_part_tag
 	return TRUE;
 }
 
-static const struct sieve_tag address_localpart_tag = { "localpart", sieve_validate_address_part_tag };
-static const struct sieve_tag address_domain_tag = { "domain", sieve_validate_address_part_tag };
-static const struct sieve_tag address_all_tag = { "all", sieve_validate_address_part_tag };
+static const struct sieve_argument address_localpart_tag = 
+	{ "localpart", sieve_validate_address_part_tag, NULL };
+static const struct sieve_argument address_domain_tag = 
+	{ "domain", sieve_validate_address_part_tag, NULL };
+static const struct sieve_argument address_all_tag = 
+	{ "all", sieve_validate_address_part_tag, NULL };
 
 void sieve_validator_link_address_part_tags
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
@@ -344,7 +366,7 @@ bool sieve_validate_command_arguments
 	
 	/* Parse tagged and optional arguments */
 	while ( sieve_ast_argument_type(arg) == SAAT_TAG ) {
-		const struct sieve_tag *tag = sieve_validator_find_tag(cmd_reg, sieve_ast_argument_tag(arg));
+		const struct sieve_argument *tag = sieve_validator_find_tag(cmd_reg, sieve_ast_argument_tag(arg));
 		
 		if ( tag == NULL ) {
 			sieve_command_validate_error(validator, cmd, 
@@ -358,16 +380,17 @@ bool sieve_validate_command_arguments
 		if ( *(tag->identifier) == '\0' ) 
 			return FALSE;
 		
+		/* Assign the tagged argument type to the ast for later reference (in generator) */
+		arg->argument = tag;
+		
 		/* Call the validation function for the tag (if present)
 		 *   Fail if the validation fails.
 		 */ 
 		if ( tag->validate != NULL ) { 
 			if ( !tag->validate(validator, &arg, cmd) ) 
 				return FALSE;
-		}  
-			
-		/* Skip to next argument */ 
-		arg = sieve_ast_argument_next(arg);
+		} else
+			arg = sieve_ast_argument_next(arg);  
 	} 
 	
 	/* Remaining arguments should be positional (tags are not allowed here) */
diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h
index a1bd69f18..9ee059aee 100644
--- a/src/lib-sieve/sieve-validator.h
+++ b/src/lib-sieve/sieve-validator.h
@@ -3,6 +3,7 @@
 
 #include "lib.h"
 
+#include "sieve-comparators.h"
 #include "sieve-common.h"
 #include "sieve-error.h"
 
@@ -25,12 +26,18 @@ void sieve_validator_error
 void sieve_validator_register_command
 	(struct sieve_validator *validator, const struct sieve_command *command);
 
-/* Tag registration */
+/* Argument registration */
 void sieve_validator_register_tag
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
-	const struct sieve_tag *tag);
-
-/* Special test tags */
+	const struct sieve_argument *argument);
+	
+/* Comparator registration */
+void sieve_validator_register_comparator
+	(struct sieve_validator *validator, const struct sieve_comparator *cmp);
+const struct sieve_comparator *sieve_validator_find_comparator
+		(struct sieve_validator *validator, const char *comparator); 
+
+/* Special test arguments */
 void sieve_validator_link_comparator_tag
 	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg); 
 void sieve_validator_link_match_type_tags
diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c
index 20f89cd76..4a1bb8288 100644
--- a/src/lib-sieve/tst-size.c
+++ b/src/lib-sieve/tst-size.c
@@ -23,7 +23,6 @@ const struct sieve_opcode tst_size_under_opcode =
 
 struct tst_size_context_data {
 	enum { SIZE_UNASSIGNED, SIZE_UNDER, SIZE_OVER } type;
-	unsigned int limit;
 };
 
 #define TST_SIZE_ERROR_DUP_TAG \
@@ -33,7 +32,7 @@ struct tst_size_context_data {
 
 static bool tst_size_validate_over_tag
 	(struct sieve_validator *validator, 
-	struct sieve_ast_argument **arg ATTR_UNUSED, 
+	struct sieve_ast_argument **arg, 
 	struct sieve_command_context *tst)
 {
 	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data;	
@@ -44,6 +43,10 @@ static bool tst_size_validate_over_tag
 	}
 	
 	ctx_data->type = SIZE_OVER;
+	
+	/* Delete this tag */
+	*arg = sieve_ast_arguments_delete(*arg, 1);
+	
 	return TRUE;
 }
 
@@ -59,14 +62,20 @@ static bool tst_size_validate_under_tag
 		return FALSE;		
 	}
 	
-	ctx_data->type = SIZE_UNDER;	
+	ctx_data->type = SIZE_UNDER;
+	
+	/* Delete this tag */
+	*arg = sieve_ast_arguments_delete(*arg, 1);
+		
 	return TRUE;
 }
 
 /* Test registration */
 
-static const struct sieve_tag size_over_tag = { "over", tst_size_validate_over_tag };
-static const struct sieve_tag size_under_tag = { "under", tst_size_validate_under_tag };
+static const struct sieve_argument size_over_tag = 
+	{ "over", tst_size_validate_over_tag, NULL };
+static const struct sieve_argument size_under_tag = 
+	{ "under", tst_size_validate_under_tag, NULL };
 
 bool tst_size_registered(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
 {
@@ -86,7 +95,6 @@ bool tst_size_validate(struct sieve_validator *validator, struct sieve_command_c
 	/* Assign context */
 	ctx_data = p_new(sieve_command_pool(tst), struct tst_size_context_data, 1);
 	ctx_data->type = SIZE_UNASSIGNED;
-	ctx_data->limit = 0;
 	tst->data = ctx_data;
 	
 	/* Check envelope test syntax:
@@ -109,8 +117,6 @@ bool tst_size_validate(struct sieve_validator *validator, struct sieve_command_c
 		return FALSE; 
 	}
 	
-	ctx_data->limit = sieve_ast_argument_number(arg);
-	
 	return TRUE;
 }
 
@@ -120,6 +126,7 @@ bool tst_size_generate
 	(struct sieve_generator *generator, 
 		struct sieve_command_context *ctx) 
 {
+	struct sieve_ast_argument *arg = sieve_ast_argument_first(ctx->ast_node);
 	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) ctx->data;
 
 	if ( ctx_data->type == SIZE_OVER ) 
@@ -127,7 +134,7 @@ bool tst_size_generate
 	else
 		sieve_generator_emit_opcode(generator, SIEVE_OPCODE_SIZEUNDER);
 
-	sieve_generator_emit_number(generator, ctx_data->limit);
+	sieve_generator_emit_number(generator, sieve_ast_argument_number(arg));
 	  
 	return TRUE;
 }
-- 
GitLab