diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 9ad44e736e12fd48ceee8aa01fb4a5198551957f..6aca9080c7732b68392c9f1606c344db8a0f3b96 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -84,6 +84,7 @@ libdovecot_sieve_la_SOURCES = \
 	sieve-validator.c \
 	sieve-generator.c \
 	sieve-interpreter.c \
+	sieve-runtime-trace.c \
 	sieve-code-dumper.c \
 	sieve-binary-dumper.c \
 	sieve-result.c \
@@ -125,6 +126,8 @@ headers = \
 	sieve-validator.h \
 	sieve-generator.h \
 	sieve-interpreter.h \
+	sieve-runtime-trace.h \
+	sieve-runtime.h \
 	sieve-code-dumper.h \
 	sieve-binary-dumper.h \
 	sieve-dump.h \
diff --git a/src/lib-sieve/cmd-discard.c b/src/lib-sieve/cmd-discard.c
index c85b86dda4126210a700467acb4fa5b459600b37..1d43cb26c7924966930885e3bd764f62e9b0021e 100644
--- a/src/lib-sieve/cmd-discard.c
+++ b/src/lib-sieve/cmd-discard.c
@@ -94,7 +94,7 @@ static bool cmd_discard_operation_dump
 	sieve_code_dumpf(denv, "DISCARD");
 	sieve_code_descend(denv);
 
-	return sieve_code_dumper_print_optional_operands(denv, address);
+	return ( sieve_action_opr_optional_dump(denv, address, NULL) == 0 );
 }
 
 /*
@@ -108,9 +108,9 @@ static int cmd_discard_operation_execute
 	unsigned int source_line;
 	
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
+	source_line = sieve_runtime_get_command_location(renv);
 
-	sieve_runtime_trace(renv, "DISCARD action");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "discard action");
 
 	return ( sieve_result_add_action
 		(renv, NULL, &act_discard, NULL, source_line, NULL, 0) >= 0 );
diff --git a/src/lib-sieve/cmd-keep.c b/src/lib-sieve/cmd-keep.c
index eff2f09039b6882e3b078558ebdc1486d7387237..2838fd892a969240b1f6a0ba416dbc65423723d7 100644
--- a/src/lib-sieve/cmd-keep.c
+++ b/src/lib-sieve/cmd-keep.c
@@ -73,7 +73,7 @@ static bool cmd_keep_operation_dump
 	sieve_code_dumpf(denv, "KEEP");
 	sieve_code_descend(denv);
 
-	return sieve_code_dumper_print_optional_operands(denv, address);
+	return ( sieve_action_opr_optional_dump(denv, address, NULL) == 0 );
 }
 
 /*
@@ -85,17 +85,24 @@ static int cmd_keep_operation_execute
 {	
 	struct sieve_side_effects_list *slist = NULL;
 	unsigned int source_line;
-	int ret = 0;	
+	int ret = 0;
+
+	/*
+	 * Read data
+	 */
 
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
+	source_line = sieve_runtime_get_command_location(renv);
 	
 	/* Optional operands (side effects only) */
-	if ( (ret=sieve_interpreter_handle_optional_operands
-		(renv, address, &slist)) <= 0 ) 
-		return ret;
+	if ( (ret=sieve_action_opr_optional_read(renv, address, NULL, &slist)) != 0 ) 
+		return SIEVE_EXEC_BIN_CORRUPT;
+
+	/*
+	 * Perform operation
+	 */
 
-	sieve_runtime_trace(renv, "KEEP action");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "keep action");
 	
 	/* Add keep action to result. 
 	 */
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index 13d0c8d85cc4dd8c8401e8c742c113267bff2759..98f26ae9e4a9f85d751480a151ab005ed0794d76 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -176,14 +176,14 @@ static bool cmd_redirect_operation_dump
 	sieve_code_dumpf(denv, "REDIRECT");
 	sieve_code_descend(denv);
 
-	if ( !sieve_code_dumper_print_optional_operands(denv, address) )
+	if ( sieve_action_opr_optional_dump(denv, address, NULL) != 0 )
 		return FALSE;
 
 	return sieve_opr_string_dump(denv, address, "reason");
 }
 
 /*
- * Intepretation
+ * Code execution
  */
 
 static int cmd_redirect_operation_execute
@@ -197,24 +197,29 @@ static int cmd_redirect_operation_execute
 	pool_t pool;
 	int ret = 0;
 
+	/*
+	 * Read data
+	 */
+
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
+	source_line = sieve_runtime_get_command_location(renv);
 
-	/* Optional operands (side effects) */
-	if ( (ret=sieve_interpreter_handle_optional_operands
-		(renv, address, &slist)) <= 0 )
-		return ret;
+	/* Optional operands (side effects only) */
+	if ( (ret=sieve_action_opr_optional_read(renv, address, NULL, &slist)) < 0 ) 
+		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Read the address */
-	if ( !sieve_opr_string_read(renv, address, &redirect) ) {
-		sieve_runtime_trace_error(renv, "invalid address string");
+	if ( !sieve_opr_string_read(renv, address, "address", &redirect) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
+
+	/*
+	 * Perform operation
+	 */
 
 	/* FIXME: perform address normalization if the string is not a string literal
 	 */
 
-	sieve_runtime_trace(renv, "REDIRECT action (\"%s\")",
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "redirect action (\"%s\")",
 		str_sanitize(str_c(redirect), 64));
 	
 	/* Add redirect action to the result */
diff --git a/src/lib-sieve/cmd-stop.c b/src/lib-sieve/cmd-stop.c
index eaae8affea1c44b2ada92af3704e7accd3639932..f908285e78df2c87a829fa47eb22cc64b7eca294 100644
--- a/src/lib-sieve/cmd-stop.c
+++ b/src/lib-sieve/cmd-stop.c
@@ -77,7 +77,7 @@ static bool cmd_stop_generate
 static int opc_stop_execute
 (const struct sieve_runtime_env *renv,  sieve_size_t *address ATTR_UNUSED)
 {	
-	sieve_runtime_trace(renv, "STOP");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "STOP");
 	
 	sieve_interpreter_interrupt(renv->interp);
 
diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c
index 2c294226b58286eb44f60e9143548952d2bb8661..1dbe5e80e648969b21782673351f175fc9b5df0a 100644
--- a/src/lib-sieve/ext-envelope.c
+++ b/src/lib-sieve/ext-envelope.c
@@ -308,7 +308,7 @@ static bool ext_envelope_operation_dump
 	sieve_code_descend(denv);
 
 	/* Handle any optional arguments */
-	if ( !sieve_addrmatch_default_dump_optionals(denv, address) )
+	if ( sieve_addrmatch_opr_optional_dump(denv, address, NULL) != 0 )
 		return FALSE;
 
 	return
@@ -426,24 +426,27 @@ static int ext_envelope_operation_execute
 	/*
 	 * Read operands
 	 */
-	
-	sieve_runtime_trace(renv, "ENVELOPE test");
 
-	if ( (ret=sieve_addrmatch_default_get_optionals
-		(renv, address, &addrp, &mcht, &cmp)) <= 0 )
-		return ret; 
+	/* Read optional operands */
+	if ( (ret=sieve_addrmatch_opr_optional_read
+		(renv, address, NULL, &addrp, &mcht, &cmp)) < 0 )
+		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Read envelope-part */
-	if ( (envp_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid envelope-part operand");
+	if ( (envp_list=sieve_opr_stringlist_read(renv, address, "envelope-part"))
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
+
+	/* 
+	 * Perform test
+	 */
+
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "envelope test");
 	
 	/* Initialize match */
 	mctx = sieve_match_begin(renv->interp, &mcht, &cmp, NULL, key_list);
diff --git a/src/lib-sieve/ext-fileinto.c b/src/lib-sieve/ext-fileinto.c
index 994cf513701a8b68d8f2049819e74da1161ad813..0197c55033a65c6c5703ac75df7040dd6a3576b7 100644
--- a/src/lib-sieve/ext-fileinto.c
+++ b/src/lib-sieve/ext-fileinto.c
@@ -139,9 +139,8 @@ static bool ext_fileinto_operation_dump
 	sieve_code_dumpf(denv, "FILEINTO");
 	sieve_code_descend(denv);
 
-	if ( !sieve_code_dumper_print_optional_operands(denv, address) ) {
+	if ( sieve_action_opr_optional_dump(denv, address, NULL) != 0 )
 		return FALSE;
-	}
 
 	return sieve_opr_string_dump(denv, address, "folder");
 }
@@ -164,25 +163,23 @@ static int ext_fileinto_operation_execute
 	 */
 
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
+	source_line = sieve_runtime_get_command_location(renv);
 	
-	/* Optional operands */
-	if ( (ret=sieve_interpreter_handle_optional_operands(renv, address, &slist)) 
-		<= 0 )
-		return ret;
+	/* Optional operands (side effects only) */
+	if ( (ret=sieve_action_opr_optional_read(renv, address, NULL, &slist)) < 0 ) 
+		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Folder operand */
-	if ( !sieve_opr_string_read(renv, address, &folder) ) {
-		sieve_runtime_trace_error(renv, "invalid folder operand");
+	if ( !sieve_opr_string_read(renv, address, "folder", &folder) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/*
 	 * Perform operation
 	 */
 
 	mailbox = str_sanitize(str_c(folder), 64);
-	sieve_runtime_trace(renv, "FILEINTO action (\"%s\")", mailbox);
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, 
+		"fileinto action (\"%s\")", mailbox);
 		
 	/* Add action to result */	
 	ret = sieve_act_store_add_to_result
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index 7907303db521634155ecfe0a90c615d4395dddb0..8736b65375b7afc8b9fda49ab5cb8dfd7b291f7c 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -246,12 +246,10 @@ static bool cmd_reject_generate
 static bool ext_reject_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &denv->oprtn;
-
-	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
+	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn));
 	sieve_code_descend(denv);
 
-	if ( !sieve_code_dumper_print_optional_operands(denv, address) )
+	if ( sieve_action_opr_optional_dump(denv, address, NULL) != 0 )
 		return FALSE;
 	
 	return sieve_opr_string_dump(denv, address, "reason");
@@ -264,8 +262,8 @@ static bool ext_reject_operation_dump
 static int ext_reject_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &renv->oprtn;
-	const struct sieve_extension *this_ext = op->ext;
+	const struct sieve_operation *oprtn = renv->oprtn;
+	const struct sieve_extension *this_ext = oprtn->ext;
 	struct sieve_side_effects_list *slist = NULL;
 	struct act_reject_context *act;
 	string_t *reason;
@@ -273,28 +271,37 @@ static int ext_reject_operation_execute
 	pool_t pool;
 	int ret;
 
+	/*
+	 * Read data
+	 */
+
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, op->address);
-	
-	/* Optional operands (side effects) */
-	if ( (ret=sieve_interpreter_handle_optional_operands
-		(renv, address, &slist)) <= 0 )
-		return ret;
+	source_line = sieve_runtime_get_command_location(renv);
+
+	/* Optional operands (side effects only) */
+	if ( (ret=sieve_action_opr_optional_read(renv, address, NULL, &slist)) < 0 ) 
+		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Read rejection reason */
-	if ( !sieve_opr_string_read(renv, address, &reason) ) {
-		sieve_runtime_trace_error(renv, "invalid reason operand");
+	if ( !sieve_opr_string_read(renv, address, "reason", &reason) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
-	sieve_runtime_trace(renv, "%s action (\"%s\")", sieve_operation_mnemonic(op), 
-		str_sanitize(str_c(reason), 64));
+	/*
+	 * Perform operation
+	 */
+
+	if ( sieve_operation_is(oprtn, ereject_operation) )
+		sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
+			"ereject action (\"%s\")", str_sanitize(str_c(reason), 64));
+	else
+		sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS,
+			"reject action (\"%s\")", str_sanitize(str_c(reason), 64));
 
 	/* Add reject action to the result */
 	pool = sieve_result_pool(renv->result);
 	act = p_new(pool, struct act_reject_context, 1);
 	act->reason = p_strdup(pool, str_c(reason));
-	act->ereject = ( sieve_operation_is(op, ereject_operation) );
+	act->ereject = ( sieve_operation_is(oprtn, ereject_operation) );
 	
 	ret = sieve_result_add_action
 		(renv, this_ext, &act_reject, slist, source_line, (void *) act, 0);
diff --git a/src/lib-sieve/plugins/body/ext-body-common.c b/src/lib-sieve/plugins/body/ext-body-common.c
index b2710aa05d60653580bf0def8db2ee2a2961b012..55958868817a985ef07f2238d049b011d95a856c 100644
--- a/src/lib-sieve/plugins/body/ext-body-common.c
+++ b/src/lib-sieve/plugins/body/ext-body-common.c
@@ -13,6 +13,7 @@
 #include "message-decoder.h"
 
 #include "sieve-common.h"
+#include "sieve-code.h"
 #include "sieve-message.h"
 #include "sieve-interpreter.h"
 
@@ -328,7 +329,7 @@ bool ext_body_get_content
 (const struct sieve_runtime_env *renv, const char * const *content_types,
 	int decode_to_plain, struct ext_body_part **parts_r)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_body_message_context *ctx = 
 		ext_body_get_context(this_ext, renv->msgctx);
 	bool result = TRUE;
@@ -353,7 +354,7 @@ bool ext_body_get_content
 bool ext_body_get_raw
 (const struct sieve_runtime_env *renv, struct ext_body_part **parts_r)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_body_message_context *ctx = 
 		ext_body_get_context(this_ext, renv->msgctx);
 	struct ext_body_part *return_part;
diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c
index 91f4ba386cc3c6db50db92960aa1cc078ff74ee9..f282529528b71284e38f51f50ed467807515155f 100644
--- a/src/lib-sieve/plugins/body/tst-body.c
+++ b/src/lib-sieve/plugins/body/tst-body.c
@@ -263,14 +263,16 @@ static bool ext_body_operation_dump
 	sieve_code_descend(denv);
 
 	/* Handle any optional arguments */
-	do {
-		
-		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_dump(denv, address, &opt_code)) 
+			< 0 )
 			return FALSE;
 
+		if ( ret == 0 ) break;
+
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_BODY_TRANSFORM:
 			if ( !sieve_binary_read_byte(denv->sblock, address, &transform) )
 				return FALSE;
@@ -297,7 +299,7 @@ static bool ext_body_operation_dump
 		default: 
 			return FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+	};
 
 	return sieve_opr_stringlist_dump(denv, address, "key list");
 }
@@ -329,43 +331,45 @@ static int ext_body_operation_execute
 	 * Read operands
 	 */
 	
-	/* Handle any optional operands */
-	do {
-		if ( (ret=sieve_match_read_optional_operands
-			(renv, address, &opt_code, &cmp, &mtch)) <= 0 )
-			return ret;
+	/* Optional operands */
+
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_read
+			(renv, address, &opt_code, &cmp, &mtch)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
 			
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END: 
-			break;
 		case OPT_BODY_TRANSFORM:
 			if ( !sieve_binary_read_byte(renv->sblock, address, &transform) ||
 				transform > TST_BODY_TRANSFORM_TEXT ) {
 				sieve_runtime_trace_error(renv, "invalid body transform type");
-				return SIEVE_EXEC_BIN_CORRUPT;
+				opok = FALSE;
 			}
 			
-			if ( transform == TST_BODY_TRANSFORM_CONTENT ) {				
-				if ( (ctype_list=sieve_opr_stringlist_read(renv, address)) 
-					== NULL ) {
-					sieve_runtime_trace_error(renv, 
-						"invalid :content body transform operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
+			if ( opok && transform == TST_BODY_TRANSFORM_CONTENT ) {				
+				ctype_list = sieve_opr_stringlist_read
+					(renv, address, "content-type-list"); 
+				opok = ( ctype_list != NULL );
 			}
 			break;
 
 		default:
 			sieve_runtime_trace_error(renv, "unknown optional operand");
-			return SIEVE_EXEC_BIN_CORRUPT;
+			opok = FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+
+		if ( !opok) return SIEVE_EXEC_BIN_CORRUPT;
+	} 
 		
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) == NULL ) 
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	if ( ctype_list != NULL && !sieve_coded_stringlist_read_all
 		(ctype_list, pool_datastack_create(), &content_types) ) {
@@ -377,7 +381,7 @@ static int ext_body_operation_execute
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "BODY action");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "body test");
 	
 	/* Extract requested parts */
 	
diff --git a/src/lib-sieve/plugins/date/ext-date-common.c b/src/lib-sieve/plugins/date/ext-date-common.c
index 9fdc56febcfed26358846437d3ca964285f097f8..28ce60634f3db3ccf53f19f9a9d80758e176ee50 100644
--- a/src/lib-sieve/plugins/date/ext-date-common.c
+++ b/src/lib-sieve/plugins/date/ext-date-common.c
@@ -5,6 +5,7 @@
 #include "utc-offset.h"
 
 #include "sieve-common.h"
+#include "sieve-code.h"
 #include "sieve-interpreter.h"
 #include "sieve-message.h"
 
@@ -104,7 +105,7 @@ bool ext_date_parse_timezone
 time_t ext_date_get_current_date
 (const struct sieve_runtime_env *renv, int *zone_offset_r)
 {	
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_date_context *dctx = (struct ext_date_context *) 
 		sieve_message_context_extension_get(renv->msgctx, this_ext);
 
diff --git a/src/lib-sieve/plugins/date/tst-date.c b/src/lib-sieve/plugins/date/tst-date.c
index 53e6d67e7a7ede01d4980966da85ccf530aaf9e3..aa99017f4cf884ccb8234f4e592fe47af69328c8 100644
--- a/src/lib-sieve/plugins/date/tst-date.c
+++ b/src/lib-sieve/plugins/date/tst-date.c
@@ -328,20 +328,22 @@ static bool tst_date_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
 	int opt_code = 0;
-	const struct sieve_operation *op = &denv->oprtn;
+	const struct sieve_operation *op = denv->oprtn;
 	struct sieve_operand operand;
 
 	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
 	sieve_code_descend(denv);
 	
 	/* Handle any optional arguments */
-  do {
-		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 )
 			return FALSE;
 
+		if ( ret == 0 ) break;
+
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_DATE_ZONE:
 			if ( !sieve_operand_read(denv->sblock, address, &operand) ) {
 				sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
@@ -359,7 +361,7 @@ static bool tst_date_operation_dump
     default:
 			return FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+	} 
 
 	if ( sieve_operation_is(op, date_operation) &&
 		!sieve_opr_string_dump(denv, address, "header name") )
@@ -377,7 +379,7 @@ static bool tst_date_operation_dump
 static int tst_date_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
-	const struct sieve_operation *op = &renv->oprtn;
+	const struct sieve_operation *op = renv->oprtn;
 	bool result = TRUE, zone_specified = FALSE, got_date = FALSE, matched = FALSE;
 	int opt_code = 0;
 	const struct sieve_message_data *msgdata = renv->msgdata;
@@ -396,26 +398,24 @@ static int tst_date_operation_execute
 	int ret;
 	
 	/* Read optional operands */
-	do {
-		if ( (ret=sieve_match_read_optional_operands
-			(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
-			return ret;
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_read
+			(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
 
+		if ( ret == 0 ) break;
+	
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_DATE_ZONE:
-			if ( !sieve_operand_read(renv->sblock, address, &operand) ) {
-				sieve_runtime_trace_error(renv, "invalid operand");
+			if ( !sieve_operand_runtime_read(renv, address, "zone", &operand) )
 				return SIEVE_EXEC_BIN_CORRUPT;
-			}
 
 			if ( !sieve_operand_is_omitted(&operand) ) {
 				if ( !sieve_opr_string_read_data
-					(renv, &operand, address, &zone) ) {
-					sieve_runtime_trace_error(renv, "invalid zone operand");
+					(renv, &operand, address, "zone", &zone) )
 					return SIEVE_EXEC_BIN_CORRUPT;
-				}
 			}
 
 			zone_specified = TRUE;
@@ -424,32 +424,26 @@ static int tst_date_operation_execute
 			sieve_runtime_trace_error(renv, "unknown optional operand");
 			return SIEVE_EXEC_BIN_CORRUPT;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
-
+	} 
 
 	if ( sieve_operation_is(op, date_operation) ) {
 		/* Read header name */
-		if ( !sieve_opr_string_read(renv, address, &header_name) ) {
-			sieve_runtime_trace_error(renv, "invalid header-name operand");
+		if ( !sieve_opr_string_read(renv, address, "header-name", &header_name) )
 			return SIEVE_EXEC_BIN_CORRUPT;
-		}
 	}
 
 	/* Read date part */
-	if ( !sieve_opr_string_read(renv, address, &date_part) ) {
-		sieve_runtime_trace_error(renv, "invalid date-part operand");
+	if ( !sieve_opr_string_read(renv, address, "date-part", &date_part) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 		
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
-
-	/* Perform test */
-
-	sieve_runtime_trace(renv, "%s test", sieve_operation_mnemonic(op));
+	
+	/* 
+	 * Perform test 
+	 */
 
 	/* Get the date value */
 
@@ -459,6 +453,8 @@ static int tst_date_operation_execute
 		const char *header_value;
 		const char *date_string;
 
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "date test");
+
 		/* Get date from the message */
 
 		/* Read first header
@@ -486,6 +482,8 @@ static int tst_date_operation_execute
 	} else if ( sieve_operation_is(op, currentdate_operation) ) {
 		/* Use time stamp recorded at the time the script first started */
 
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "currentdatedate test");
+
 		date_value = local_time;
 		original_zone = local_zone;
 		got_date = TRUE;
diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c
index 8dc834d5bca3feb5a0fef9dc699ca7cfb44ea63a..2db21f783b78384c5b3895e923c300d03b4335d5 100644
--- a/src/lib-sieve/plugins/enotify/cmd-notify.c
+++ b/src/lib-sieve/plugins/enotify/cmd-notify.c
@@ -347,47 +347,44 @@ static bool cmd_notify_generate
 static bool cmd_notify_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {	
-	int opt_code = 1;
+	int opt_code = 0;
 	
 	sieve_code_dumpf(denv, "NOTIFY");
 	sieve_code_descend(denv);	
 
 	/* Dump optional operands */
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			sieve_code_mark(denv);
-			
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) 
-				return FALSE;
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case CMD_NOTIFY_OPT_IMPORTANCE:
-				if ( !sieve_opr_number_dump(denv, address, "importance") )
-					return FALSE;
-				break;
-			case CMD_NOTIFY_OPT_FROM:
-				if ( !sieve_opr_string_dump(denv, address, "from") )
-					return FALSE;
-				break;
-			case CMD_NOTIFY_OPT_OPTIONS:
-				if ( !sieve_opr_stringlist_dump(denv, address, "options") )
-					return FALSE;
-				break;
-			case CMD_NOTIFY_OPT_MESSAGE:
-				if ( !sieve_opr_string_dump(denv, address, "message") )
-					return FALSE;
-				break;
-			default:
-				return FALSE;
-			}
+
+	for (;;) {
+		int ret;
+		bool opok = TRUE;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+			return FALSE;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case CMD_NOTIFY_OPT_IMPORTANCE:
+			opok = sieve_opr_number_dump(denv, address, "importance");
+			break;
+		case CMD_NOTIFY_OPT_FROM:
+			opok = sieve_opr_string_dump(denv, address, "from");
+			break;
+		case CMD_NOTIFY_OPT_OPTIONS:
+			opok = sieve_opr_stringlist_dump(denv, address, "options");
+			break;
+		case CMD_NOTIFY_OPT_MESSAGE:
+			opok = sieve_opr_string_dump(denv, address, "message");
+			break;
+		default:
+			return FALSE;
 		}
+
+		if ( !opok ) return FALSE;
 	}
 	
-	/* Dump reason and handle operands */
-	return 
-		sieve_opr_string_dump(denv, address, "method");
+	/* Dump method operand */
+	return sieve_opr_string_dump(denv, address, "method");
 }
 
 /* 
@@ -397,12 +394,12 @@ static bool cmd_notify_operation_dump
 static int cmd_notify_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct sieve_side_effects_list *slist = NULL;
 	struct sieve_enotify_action *act;
 	void *method_context;
 	pool_t pool;
-	int opt_code = 1, result = SIEVE_EXEC_OK;
+	int opt_code = 0, result = SIEVE_EXEC_OK;
 	sieve_number_t importance = 2;
 	struct sieve_coded_stringlist *options = NULL;
 	const struct sieve_enotify_method *method;
@@ -414,68 +411,61 @@ static int cmd_notify_operation_execute
 	 */
 		
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
-	
-	/* Optional operands */	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case CMD_NOTIFY_OPT_IMPORTANCE:
-				if ( !sieve_opr_number_read(renv, address, &importance) ) {
-					sieve_runtime_trace_error(renv, "invalid importance operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
+
+	source_line = sieve_runtime_get_command_location(renv);
 	
-				/* Enforce 0 < importance < 4 (just to be sure) */
-				if ( importance < 1 ) 
-					importance = 1;
-				else if ( importance > 3 )
-					importance = 3;
-				break;
-			case CMD_NOTIFY_OPT_FROM:
-				if ( !sieve_opr_string_read(renv, address, &from) ) {
-					sieve_runtime_trace_error(renv, "invalid from operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case CMD_NOTIFY_OPT_MESSAGE:
-				if ( !sieve_opr_string_read(renv, address, &message) ) {
-					sieve_runtime_trace_error(renv, "invalid from operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case CMD_NOTIFY_OPT_OPTIONS:
-				if ( (options=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-					sieve_runtime_trace_error(renv, "invalid options operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			default:
-				sieve_runtime_trace_error(renv, "unknown optional operand: %d", 
-					opt_code);
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
+	/* Optional operands */
+
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case CMD_NOTIFY_OPT_IMPORTANCE:
+			opok = sieve_opr_number_read(renv, address, "importance", &importance);	
+			break;
+		case CMD_NOTIFY_OPT_FROM:
+			opok = sieve_opr_string_read(renv, address, "from", &from);
+			break;
+		case CMD_NOTIFY_OPT_MESSAGE:
+			opok = sieve_opr_string_read(renv, address, "message", &message);
+			break;
+		case CMD_NOTIFY_OPT_OPTIONS:
+			options = sieve_opr_stringlist_read(renv, address, "options");
+			opok = ( options != NULL );
+			break;
+		default:
+			sieve_runtime_trace_error(renv, "unknown optional operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
 		}
+
+		if ( !opok ) return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
-	/* Reason operand */
-	if ( !sieve_opr_string_read(renv, address, &method_uri) ) {
-		sieve_runtime_trace_error(renv, "invalid method operand");
+	/* Method operand */
+
+	if ( !sieve_opr_string_read(renv, address, "method", &method_uri) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 		
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "NOTIFY action");	
+	/* Enforce 0 < importance < 4 (just to be sure) */
+
+	if ( importance < 1 ) 
+		importance = 1;
+	else if ( importance > 3 )
+		importance = 3;
+
+	/* Trace */
+
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "notify action");	
 
 	/* Check operands */
 
diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
index 4a9a66c87c4bd7f8da107c4e9b5083cbf6f0d0c9..ac77d628aa773a56358881ade74b9c4f6d0e4671 100644
--- a/src/lib-sieve/plugins/enotify/ext-enotify-common.c
+++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.c
@@ -474,7 +474,7 @@ bool ext_enotify_runtime_method_validate
 (const struct sieve_runtime_env *renv, unsigned int source_line,
 	string_t *method_uri)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	const struct sieve_enotify_method *method;
 	const char *uri = str_c(method_uri);
 	const char *scheme;
@@ -513,7 +513,7 @@ static const struct sieve_enotify_method *ext_enotify_get_method
 (const struct sieve_runtime_env *renv, unsigned int source_line,
 	string_t *method_uri, const char **uri_body_r)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	const struct sieve_enotify_method *method;
 	const char *uri = str_c(method_uri);
 	const char *scheme;
diff --git a/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c b/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c
index 7d87b017b1d4792269f583632b83c6be0cec2975..9037bff7a8081314d3a008cd6f676b42db260637 100644
--- a/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c
+++ b/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c
@@ -160,10 +160,7 @@ static bool tst_notifymc_operation_dump
 	sieve_code_descend(denv);
 
 	/* Handle any optional arguments */
-	if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
-		return FALSE;
-
-	if ( opt_code != SIEVE_MATCH_OPT_END )
+	if ( sieve_match_opr_optional_dump(denv, address, &opt_code) != 0 )
 		return FALSE;
 		
 	return
@@ -197,39 +194,35 @@ static int tst_notifymc_operation_execute
 	 */
 	
 	/* Handle match-type and comparator operands */
-	if ( (ret=sieve_match_read_optional_operands
-		(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
-		return ret;
-	
-	/* Check whether we neatly finished the list of optional operands */
-	if ( opt_code != SIEVE_MATCH_OPT_END) {
+	if ( (ret=sieve_match_opr_optional_read
+		(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+		return SIEVE_EXEC_BIN_CORRUPT;
+
+	/* Check whether we neatly finished the list of optional operands*/
+	if ( ret > 0 ) {
 		sieve_runtime_trace_error(renv, "invalid optional operand");
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 
 	/* Read notify uri */
-	if ( !sieve_opr_string_read(renv, address, &notify_uri) ) {
-		sieve_runtime_trace_error(renv, "invalid notify-uri operand");
+	if ( !sieve_opr_string_read(renv, address, "notify-uri", &notify_uri) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/* Read notify capability */
-	if ( !sieve_opr_string_read(renv, address, &notify_capability) ) {
-		sieve_runtime_trace_error(renv, "invalid notify-uri operand");
+	if ( !sieve_opr_string_read
+		(renv, address, "notify-capability", &notify_capability) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "NOTIFY_METHOD_CAPABILITY test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "notify_method_capability test");
 
 	cap_value = ext_enotify_runtime_get_method_capability
 		(renv, 0 /* FIXME */, notify_uri, str_c(notify_capability));
diff --git a/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c b/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c
index db45c68f32d013fe03b94a4b16e845fcf5caf74d..20fc00da6ca6964fff4b2ad90df3dd176e5fc6d3 100644
--- a/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c
+++ b/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c
@@ -113,16 +113,15 @@ static int tst_vnotifym_operation_execute
 	 */
 	
 	/* Read notify uris */
-	if ( (notify_uris=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid notify-uris operand");
+	if ( (notify_uris=sieve_opr_stringlist_read(renv, address, "notify-uris")) 
+		== NULL ) 
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "VALID_NOTIFY_METHOD test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "valid_notify_method test");
 
 	uri_item = NULL;
 	while ( (result=sieve_coded_stringlist_next_item(notify_uris, &uri_item)) 
diff --git a/src/lib-sieve/plugins/environment/tst-environment.c b/src/lib-sieve/plugins/environment/tst-environment.c
index 110f053e1db955ebe975a681188f944896b45383..28bd55e53b756c2206b38b91bd8dd52106afd7d9 100644
--- a/src/lib-sieve/plugins/environment/tst-environment.c
+++ b/src/lib-sieve/plugins/environment/tst-environment.c
@@ -137,11 +137,8 @@ static bool tst_environment_operation_dump
 	sieve_code_dumpf(denv, "ENVIRONMENT");
 	sieve_code_descend(denv);
 
-	/* Handle any optional arguments */
-	if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
-		return FALSE;
-
-	if ( opt_code != SIEVE_MATCH_OPT_END )
+	/* Optional operands */
+	if ( sieve_match_opr_optional_dump(denv, address, &opt_code) != 0 )
 		return FALSE;
 		
 	return
@@ -156,7 +153,7 @@ static bool tst_environment_operation_dump
 static int tst_environment_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	int ret, mret;
 	bool result = TRUE;
 	int opt_code = 0;
@@ -175,33 +172,30 @@ static int tst_environment_operation_execute
 	 */
 	
 	/* Handle match-type and comparator operands */
-	if ( (ret=sieve_match_read_optional_operands
-		(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
-		return ret;
-	
+	if ( (ret=sieve_match_opr_optional_read
+		(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+		return SIEVE_EXEC_BIN_CORRUPT;
+
 	/* Check whether we neatly finished the list of optional operands*/
-	if ( opt_code != SIEVE_MATCH_OPT_END) {
+	if ( ret > 0 ) {
 		sieve_runtime_trace_error(renv, "invalid optional operand");
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 
 	/* Read source */
-	if ( !sieve_opr_string_read(renv, address, &name) ) {
-		sieve_runtime_trace_error(renv, "invalid name operand");
+	if ( !sieve_opr_string_read(renv, address, "name", &name) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "ENVIRONMENT test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "environment test");
 
 	env_item = ext_environment_item_get_value
 		(this_ext, str_c(name), renv->scriptenv);
diff --git a/src/lib-sieve/plugins/imap4flags/cmd-flag.c b/src/lib-sieve/plugins/imap4flags/cmd-flag.c
index 8cc194a0dd13c4f8cab08d4a7ee8d4fd2c3214ee..805e79acb784ff3f87b79a4f99d703e293fd472e 100644
--- a/src/lib-sieve/plugins/imap4flags/cmd-flag.c
+++ b/src/lib-sieve/plugins/imap4flags/cmd-flag.c
@@ -145,7 +145,7 @@ bool cmd_flag_operation_dump
 {
 	struct sieve_operand operand;
 
-	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(&denv->oprtn));
+	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn));
 	sieve_code_descend(denv);
 	
 	sieve_code_mark(denv);
@@ -174,7 +174,7 @@ bool cmd_flag_operation_dump
 static int cmd_flag_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &renv->oprtn;
+	const struct sieve_operation *op = renv->oprtn;
 	struct sieve_operand operand;
 	bool result = TRUE;
 	string_t *flag_item;
@@ -188,40 +188,37 @@ static int cmd_flag_operation_execute
 	 * Read operands 
 	 */
 
-	if ( !sieve_operand_read(renv->sblock, address, &operand) ) {
-		sieve_runtime_trace_error(renv, "invalid operand");
+	/* Read bare operand (two types possible) */
+	if ( !sieve_operand_runtime_read(renv, address, NULL, &operand) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 		
+	/* Variable operand (optional) */
 	if ( sieve_operand_is_variable(&operand) ) {		
-
 		/* Read the variable operand */
 		if ( !sieve_variable_operand_read_data
-			(renv, &operand, address, &storage, &var_index) ) {
-			sieve_runtime_trace_error(renv, "invalid variable operand");
+				(renv, &operand, address, "variable", &storage, &var_index) )
 			return SIEVE_EXEC_BIN_CORRUPT;
-		}
-		
+
 		/* Read flag list */
-		if ( (flag_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-			sieve_runtime_trace_error(renv, "invalid flag-list operand");
+		if ( (flag_list=sieve_opr_stringlist_read(renv, address, "flag-list")) 
+			== NULL )
 			return SIEVE_EXEC_BIN_CORRUPT;
-		}
-
+		
+	/* Flag-list operand */
 	} else if ( sieve_operand_is_stringlist(&operand) ) {	
 		storage = NULL;
 		var_index = 0;
 		
 		/* Read flag list */
 		if ( (flag_list=sieve_opr_stringlist_read_data
-			(renv, &operand, address)) == NULL ) {
-			sieve_runtime_trace_error(renv, "invalid flag-list operand");
+			(renv, &operand, address, "flag-list")) == NULL )
 			return SIEVE_EXEC_BIN_CORRUPT;
-		}
 
+	/* Invalid */
 	} else {
-		sieve_runtime_trace_error(renv, "unexpected operand '%s'", 
-			sieve_operand_name(&operand));
+		sieve_runtime_trace_error
+			(renv, "expected variable or string-list (flag-list) operand "
+				"but found %s", sieve_operand_name(&operand));
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
@@ -229,18 +226,20 @@ static int cmd_flag_operation_execute
 	 * Perform operation
 	 */	
 	
-	sieve_runtime_trace(renv, "%s command", sieve_operation_mnemonic(op));
-
 	/* Determine what to do */
 
-	if ( sieve_operation_is(op, setflag_operation) )
+	if ( sieve_operation_is(op, setflag_operation) ) {
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "setflag command");
 		flag_op = ext_imap4flags_set_flags;
-	else if ( sieve_operation_is(op, addflag_operation) )
+	} else if ( sieve_operation_is(op, addflag_operation) ) {
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "addflag command");
 		flag_op = ext_imap4flags_add_flags;
-	else if ( sieve_operation_is(op, removeflag_operation) )
+	} else if ( sieve_operation_is(op, removeflag_operation) ) {
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "removeflag command");
 		flag_op = ext_imap4flags_remove_flags;
-	else
+	} else {
 		i_unreached();
+	}
 
 	/* Iterate through all flags and perform requested operation */
 	
diff --git a/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c b/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
index 3f04a81be84c5da10e2ec34f8b5b080665f41f79..44dae44b3581de5d26ae821367bd9588b4f50b3f 100644
--- a/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
+++ b/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
@@ -445,7 +445,7 @@ int ext_imap4flags_set_flags
 		if ( !sieve_variable_get_modifiable(storage, var_index, &cur_flags) )
 			return SIEVE_EXEC_BIN_CORRUPT;
 	} else
-		cur_flags = _get_flags_string(renv->oprtn.ext, renv->result);
+		cur_flags = _get_flags_string(renv->oprtn->ext, renv->result);
 
 	if ( cur_flags != NULL )
 		flags_list_set_flags(cur_flags, flags);		
@@ -463,7 +463,7 @@ int ext_imap4flags_add_flags
 		if ( !sieve_variable_get_modifiable(storage, var_index, &cur_flags) )
 			return SIEVE_EXEC_BIN_CORRUPT;
 	} else
-		cur_flags = _get_flags_string(renv->oprtn.ext, renv->result);
+		cur_flags = _get_flags_string(renv->oprtn->ext, renv->result);
 	
 	if ( cur_flags != NULL )
 		flags_list_add_flags(cur_flags, flags);
@@ -481,7 +481,7 @@ int ext_imap4flags_remove_flags
 		if ( !sieve_variable_get_modifiable(storage, var_index, &cur_flags) )
 			return SIEVE_EXEC_BIN_CORRUPT;
 	} else
-		cur_flags = _get_flags_string(renv->oprtn.ext, renv->result);
+		cur_flags = _get_flags_string(renv->oprtn->ext, renv->result);
 	
 	if ( cur_flags != NULL )
 		flags_list_remove_flags(cur_flags, flags);		
@@ -499,7 +499,7 @@ int ext_imap4flags_get_flags_string
 		if ( !sieve_variable_get_modifiable(storage, var_index, &cur_flags) )
 			return SIEVE_EXEC_BIN_CORRUPT;
 	} else
-		cur_flags = _get_flags_string(renv->oprtn.ext, renv->result);
+		cur_flags = _get_flags_string(renv->oprtn->ext, renv->result);
 	
 	if ( cur_flags == NULL )
 		*flags = "";
@@ -521,7 +521,7 @@ void ext_imap4flags_get_flags_init
 		flags_list_set_flags(cur_flags, flags_list);
 	}
 	else
-		cur_flags = _get_flags_string(renv->oprtn.ext, renv->result);
+		cur_flags = _get_flags_string(renv->oprtn->ext, renv->result);
 	
 	ext_imap4flags_iter_init(iter, cur_flags);		
 }
diff --git a/src/lib-sieve/plugins/imap4flags/tag-flags.c b/src/lib-sieve/plugins/imap4flags/tag-flags.c
index 1645a0871458b86fa4bc996db3410a82e00f7f9f..b7115d4dbe24893e56e234d814f860ba7a86d4e1 100644
--- a/src/lib-sieve/plugins/imap4flags/tag-flags.c
+++ b/src/lib-sieve/plugins/imap4flags/tag-flags.c
@@ -269,13 +269,12 @@ static bool seff_flags_read_context
 	t_push();
 
 	/* Check whether explicit flag list operand is present */
-	if ( !sieve_operand_read(renv->sblock, address, &operand) ) {
-        sieve_runtime_trace_error(renv, "invalid operand");
-		t_pop();
-        return FALSE;
-    }
-
-    if ( sieve_operand_is_omitted(&operand) ) {
+	if ( !sieve_operand_runtime_read(renv, address, NULL, &operand) ) {
+ 		t_pop();
+		return FALSE;
+	}
+	
+	if ( sieve_operand_is_omitted(&operand) ) {
 		/* Flag list is omitted, use current value of internal 
 		 * variable to construct side effect context.
 		 */
@@ -287,7 +286,7 @@ static bool seff_flags_read_context
 	
 	/* Read flag-list */
 	if ( (flag_list=sieve_opr_stringlist_read_data
-		(renv, &operand, address)) == NULL ) {
+		(renv, &operand, address, NULL)) == NULL ) {
 		t_pop();
 		return FALSE;
 	}
diff --git a/src/lib-sieve/plugins/imap4flags/tst-hasflag.c b/src/lib-sieve/plugins/imap4flags/tst-hasflag.c
index cbcec03def96d859ed3c81191a207a8880511173..aa18d90c069d510dc1e7759747ade0a6920867fb 100644
--- a/src/lib-sieve/plugins/imap4flags/tst-hasflag.c
+++ b/src/lib-sieve/plugins/imap4flags/tst-hasflag.c
@@ -140,21 +140,28 @@ static bool tst_hasflag_operation_dump
 	sieve_code_dumpf(denv, "HASFLAG");
 	sieve_code_descend(denv);
 
-	/* Handle any optional arguments */
-	do {
-		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	/* Optional operands */
+
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_dump(denv, address, &opt_code)) 
+			< 0 )
 			return FALSE;
 
+		if ( ret == 0 ) break;
+
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_VARIABLES:
-			sieve_opr_stringlist_dump(denv, address, "variables");
+			opok = sieve_opr_stringlist_dump(denv, address, "variables");
 			break;
 		default:
 			return FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+
+		if ( !opok ) return FALSE;
+	}
 			
 	return 
 		sieve_opr_stringlist_dump(denv, address, "list of flags");
@@ -197,7 +204,7 @@ static const struct sieve_match_key_extractor _flag_extractor = {
 static int tst_hasflag_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	int ret, mret;
+	int mret;
 	bool result = TRUE;
 	int opt_code = 0;
 	struct sieve_comparator cmp = 
@@ -214,39 +221,43 @@ static int tst_hasflag_operation_execute
 	 * Read operands
 	 */
 
-	/* Handle match-type and comparator operands */
-	do {
-		if ( (ret=sieve_match_read_optional_operands
-			(renv, address, &opt_code, &cmp, &mtch)) <= 0 )
-			return ret;
+	/* Optional operands */
+
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_read
+			(renv, address, &opt_code, &cmp, &mtch)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
 	
-		/* Check whether we neatly finished the list of optional operands*/
 		switch ( opt_code ) { 
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_VARIABLES:
-			if ( (variables_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-					sieve_runtime_trace_error(renv, "invalid variables-list operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
+			variables_list = sieve_opr_stringlist_read
+				(renv, address, "variables-list");
+			opok = ( variables_list != NULL );
 			break;
 		default:
 			sieve_runtime_trace_error(renv, "invalid optional operand");
 			return SIEVE_EXEC_BIN_CORRUPT;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
-		
-	/* Read flag list */
-	if ( (flag_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid flag-list operand");
+
+		if ( !opok ) return SIEVE_EXEC_BIN_CORRUPT;
+	} 
+
+	/* Fixed operands */
+
+	if ( (flag_list=sieve_opr_stringlist_read(renv, address, "flag-list")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "HASFLAG test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "hasflag test");
 
 	matched = FALSE;
 	mctx = sieve_match_begin
diff --git a/src/lib-sieve/plugins/include/cmd-global.c b/src/lib-sieve/plugins/include/cmd-global.c
index 1c50483bc76d93fc60031c6dd29b69d09c2e259a..834f9d519f4b5d1657dc0068a99c7b881ca86117 100644
--- a/src/lib-sieve/plugins/include/cmd-global.c
+++ b/src/lib-sieve/plugins/include/cmd-global.c
@@ -239,7 +239,7 @@ static bool cmd_global_generate
 static bool opc_global_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	const struct sieve_extension *this_ext = denv->oprtn.ext;
+	const struct sieve_extension *this_ext = denv->oprtn->ext;
 	unsigned int count, i, var_count;
 	struct sieve_variable_scope *scope;
 	struct sieve_variable * const *vars;
@@ -275,7 +275,7 @@ static bool opc_global_dump
 static int opc_global_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct sieve_variable_scope *scope;	
 	struct sieve_variable_storage *storage;
 	struct sieve_variable * const *vars;
@@ -289,7 +289,7 @@ static int opc_global_execute
 	scope = ext_include_binary_get_global_scope(this_ext, renv->sbin);
 	vars = sieve_variable_scope_get_variables(scope, &var_count);
 	storage = ext_include_interpreter_get_global_variables
-		(renv->oprtn.ext, renv->interp);
+		(this_ext, renv->interp);
 
 	for ( i = 0; i < count; i++ ) {
 		unsigned int index;
@@ -304,6 +304,10 @@ static int opc_global_execute
 				index, var_count);
 			return SIEVE_EXEC_BIN_CORRUPT;
 		}
+
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+			"exporting global variable '%s' (index: %u)", vars[index]->identifier, 
+			index);
 		
 		/* Make sure variable is initialized (export) */
 		(void)sieve_variable_get_modifiable(storage, index, NULL); 
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index 2be54eee282aa1c3e46f6a6e85221f8a4474d45e..c36f31e5bb4235e74e34e9f30f0938c51542227e 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -314,7 +314,7 @@ static bool opc_include_dump
 	if ( !sieve_binary_read_byte(denv->sblock, address, &flags) )
 		return FALSE;
 
-	binctx = ext_include_binary_get_context(denv->oprtn.ext, denv->sbin);
+	binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin);
 	included = ext_include_binary_script_get_included(binctx, include_id);
 	if ( included == NULL )
 		return FALSE;
diff --git a/src/lib-sieve/plugins/include/cmd-return.c b/src/lib-sieve/plugins/include/cmd-return.c
index 37625400bddbce5f0f9677024408c8da8f6296b2..3b5a71546e4aff49501276f96f8a34e464df59b2 100644
--- a/src/lib-sieve/plugins/include/cmd-return.c
+++ b/src/lib-sieve/plugins/include/cmd-return.c
@@ -64,8 +64,6 @@ static bool cmd_return_generate
 static int opc_return_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED)
 {	
-	sieve_runtime_trace(renv, "RETURN command");
-
 	ext_include_execute_return(renv);
 	return SIEVE_EXEC_OK;
 }
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index 6a3289030723717e24072cc043c1ba7eac4a5a01..1be4152ca4700411687fab9c2ec362b5cdaaf23e 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -540,7 +540,7 @@ static bool ext_include_runtime_include_mark
 int ext_include_execute_include
 (const struct sieve_runtime_env *renv, unsigned int include_id, bool once)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	int result = SIEVE_EXEC_OK;
 	struct ext_include_interpreter_context *ctx;
 	const struct ext_include_script_info *included;
@@ -559,16 +559,17 @@ int ext_include_execute_include
 	
 	block_id = sieve_binary_block_get_id(included->block);
 
-	sieve_runtime_trace(renv, 
-		"INCLUDE command (script: %s, id: %d block: %d) START::", 
-		sieve_script_name(included->script), include_id, block_id);
-
 	/* If :once modifier is specified, check for duplicate include */
-	if ( !ext_include_runtime_include_mark(ctx, included, once) ) {
+	if ( ext_include_runtime_include_mark(ctx, included, once) ) {
+		sieve_runtime_trace(renv, SIEVE_TRLVL_MINIMUM,
+			"include script '%s' (id: %d, block: %d) STARTED ::", 
+			sieve_script_name(included->script), include_id, block_id);
+	} else {
 		/* skip */
-
-		sieve_runtime_trace(renv, 
-			"INCLUDE command (block: %d) SKIPPED ::", block_id);
+		sieve_runtime_trace(renv, SIEVE_TRLVL_MINIMUM, 
+			"include script '%s' (id: %d, block: %d); "
+			"SKIPPED, because already run once", 
+			sieve_script_name(included->script), include_id, block_id);
 		return result;
 	}
 
@@ -598,7 +599,8 @@ int ext_include_execute_include
 			 * (first sub-interpreter) 
 			 */
 			subinterp = sieve_interpreter_create_for_block
-				(included->block, ehandler);
+				(included->block, included->script, renv->msgdata, renv->scriptenv, 
+					ehandler);
 
 			if ( subinterp != NULL ) {			
 				curctx = ext_include_interpreter_context_init_child
@@ -606,8 +608,7 @@ int ext_include_execute_include
 
 				/* Activate and start the top-level included script */
 				result = ( sieve_interpreter_start
-					(subinterp, renv->msgdata, renv->scriptenv, renv->result, 
-						&interrupted) == 1 );
+					(subinterp, renv->result, &interrupted) == 1 );
 			} else
 				result = SIEVE_EXEC_BIN_CORRUPT;
 		}
@@ -624,7 +625,10 @@ int ext_include_execute_include
 					
 					/* Sub-interpreter ended or executed return */
 					
-					sieve_runtime_trace(renv, "INCLUDE command (block: %d) END ::", 
+					sieve_runtime_trace(renv, SIEVE_TRLVL_MINIMUM,
+						"included script '%s' (id: %d, block: %d) ENDED ::",  
+						sieve_script_name(curctx->script_info->script),
+						curctx->script_info->id,
 						sieve_binary_block_get_id(curctx->script_info->block));
 
 					/* Ascend interpreter stack */
@@ -649,7 +653,8 @@ int ext_include_execute_include
 						if ( result == SIEVE_EXEC_OK ) {
 							/* Create sub-interpreter */
 							subinterp = sieve_interpreter_create_for_block
-								(curctx->include->block, ehandler);			
+								(curctx->include->block, curctx->include->script, renv->msgdata,
+									renv->scriptenv, ehandler);			
 
 							if ( subinterp != NULL ) {
 								curctx = ext_include_interpreter_context_init_child
@@ -659,9 +664,8 @@ int ext_include_execute_include
 								/* Start the sub-include's interpreter */
 								curctx->include = NULL;
 								curctx->returned = FALSE;
-								result = ( sieve_interpreter_start
-									(subinterp, renv->msgdata, renv->scriptenv, renv->result, 
-										&interrupted) == 1 );		 	
+								result = ( sieve_interpreter_start(subinterp, renv->result, 
+									&interrupted) == 1 );		 	
 							} else
 								result = SIEVE_EXEC_BIN_CORRUPT;
 						}
@@ -675,9 +679,13 @@ int ext_include_execute_include
 					}
 				}
 			}
-		} else 
-			sieve_runtime_trace(renv, "INCLUDE command (block: %d) END ::", 
+		} else {
+			sieve_runtime_trace(renv, SIEVE_TRLVL_MINIMUM,
+				"included script '%s' (id: %d, block: %d) ENDED ::",  
+				sieve_script_name(curctx->script_info->script),
+				curctx->script_info->id,
 				sieve_binary_block_get_id(curctx->script_info->block));
+		}
 
 		/* Free any sub-interpreters that might still be active */
 		while ( curctx != NULL && curctx->parent != NULL ) {
@@ -704,10 +712,12 @@ int ext_include_execute_include
 void ext_include_execute_return
 (const struct sieve_runtime_env *renv)
 {
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_include_interpreter_context *ctx =
 		ext_include_get_interpreter_context(this_ext, renv->interp);
 	
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "return");
+
 	ctx->returned = TRUE;
 	sieve_interpreter_interrupt(renv->interp);	
 }
diff --git a/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c b/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
index ac9044e6409491c066814387b2eefd1d0d8041a1..208f806ee5db3d61f971c31a09c21309a8ae7a24 100644
--- a/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
+++ b/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
@@ -114,16 +114,15 @@ static int tst_mailboxexists_operation_execute
 	 */
 	
 	/* Read notify uris */
-	if ( (mailbox_names=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid mailbox-names operand");
+	if ( (mailbox_names=sieve_opr_stringlist_read(renv, address, "mailbox-names"))
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "MAILBOXEXISTS command");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "mailboxexists test");
 
 	if ( renv->scriptenv->namespaces != NULL ) {
 		mailbox_item = NULL;
diff --git a/src/lib-sieve/plugins/notify/cmd-denotify.c b/src/lib-sieve/plugins/notify/cmd-denotify.c
index 870f646af565fe8e3b226c0e58cff3d7bd8b66fb..36b2dcc33246ae24cffca1426e667a14520f8f29 100644
--- a/src/lib-sieve/plugins/notify/cmd-denotify.c
+++ b/src/lib-sieve/plugins/notify/cmd-denotify.c
@@ -231,39 +231,36 @@ static bool cmd_denotify_generate
 static bool cmd_denotify_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {	
-	const struct sieve_operation *op = &denv->oprtn;
-	int opt_code = 1;
+	const struct sieve_operation *op = denv->oprtn;
+	int opt_code = 0;
 	
 	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
 	sieve_code_descend(denv);	
 
-	/* Dump optional operands */
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			sieve_code_mark(denv);
-			
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) 
-				return FALSE;
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_MATCH_KEY:
-				if ( !sieve_opr_string_dump(denv, address, "key-string") )
-					return FALSE;
-				break;
-			case OPT_MATCH_TYPE:
-				if ( !sieve_opr_match_type_dump(denv, address) )
-					return FALSE;
-				break;
-			case OPT_IMPORTANCE:
-				if ( !sieve_opr_number_dump(denv, address, "importance") )
-					return FALSE;
-				break;
-			default:
-				return FALSE;
-			}
+	for (;;) {
+		int ret;
+		bool opok = TRUE;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+			return FALSE;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_MATCH_KEY:
+			opok = sieve_opr_string_dump(denv, address, "key-string");
+			break;
+		case OPT_MATCH_TYPE:
+			opok = sieve_opr_match_type_dump(denv, address);
+			break;
+		case OPT_IMPORTANCE:
+			opok = sieve_opr_number_dump(denv, address, "importance");
+			break;
+		default:
+			return FALSE;
 		}
+
+		if ( !opok ) return FALSE;
 	}
 	
 	return TRUE;
@@ -277,7 +274,7 @@ static int cmd_denotify_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
 	bool result = TRUE;
-	int opt_code = 1;
+	int opt_code = 0;
 	struct sieve_match_type mcht = 
 		SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
 	const struct sieve_comparator cmp = 
@@ -294,53 +291,49 @@ static int cmd_denotify_operation_execute
 	 */
 	
 	/* Optional operands */	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
 
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_MATCH_TYPE:
-				if ( !sieve_opr_match_type_read(renv, address, &mcht) ) {
-					sieve_runtime_trace_error(renv, "invalid match type operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_MATCH_KEY:
-				if ( (match_key=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-					sieve_runtime_trace_error(renv, "invalid match key operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_IMPORTANCE:
-				if ( !sieve_opr_number_read(renv, address, &importance) ) {
-					sieve_runtime_trace_error(renv, "invalid importance operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-	
-				/* Enforce 0 < importance < 4 (just to be sure) */
-				if ( importance < 1 ) 
-					importance = 1;
-				else if ( importance > 3 )
-					importance = 3;
-				break;
-			default:
-				sieve_runtime_trace_error(renv, "unknown optional operand: %d", 
-					opt_code);
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_MATCH_TYPE:
+			opok = sieve_opr_match_type_read(renv, address, &mcht);
+			break;
+		case OPT_MATCH_KEY:
+			match_key = sieve_opr_stringlist_read(renv, address, "match key");
+			opok = ( match_key != NULL );
+			break;
+		case OPT_IMPORTANCE:
+			opok = sieve_opr_number_read(renv, address, "importance", &importance);
+			break;
+		default:
+			sieve_runtime_trace_error(renv, "unknown optional operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
 		}
+
+		if ( !opok ) return SIEVE_EXEC_BIN_CORRUPT;
 	}
 		
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "DENOTIFY action");
+	/* Enforce 0 < importance < 4 (just to be sure) */
+
+	if ( importance < 1 ) 
+		importance = 1;
+	else if ( importance > 3 )
+		importance = 3;
+
+	/* Trace */
+
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "denotify action");
 
 	/* Either do string matching or just kill all notify actions */
 	if ( match_key != NULL ) { 	
diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
index aa38e5e4ee78ecb35bfe56570cc2260887b310d6..813d07d23d17e40da3ebc7375de4aa35f6f3fc6e 100644
--- a/src/lib-sieve/plugins/notify/cmd-notify.c
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -366,42 +366,39 @@ static bool cmd_notify_generate
 static bool cmd_notify_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {	
-	int opt_code = 1;
+	int opt_code = 0;
 	
 	sieve_code_dumpf(denv, "NOTIFY");
 	sieve_code_descend(denv);	
 
 	/* Dump optional operands */
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			sieve_code_mark(denv);
-			
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) 
-				return FALSE;
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_IMPORTANCE:
-				if ( !sieve_opr_number_dump(denv, address, "importance") )
-					return FALSE;
-				break;
-			case OPT_ID:
-				if ( !sieve_opr_string_dump(denv, address, "id") )
-					return FALSE;
-				break;
-			case OPT_OPTIONS:
-				if ( !sieve_opr_stringlist_dump(denv, address, "options") )
-					return FALSE;
-				break;
-			case OPT_MESSAGE:
-				if ( !sieve_opr_string_dump(denv, address, "message") )
-					return FALSE;
-				break;
-			default:
-				return FALSE;
-			}
+	for (;;) {
+		int ret;
+		bool opok = TRUE;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+			return FALSE;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_IMPORTANCE:
+			opok = sieve_opr_number_dump(denv, address, "importance");
+			break;
+		case OPT_ID:
+			opok = sieve_opr_string_dump(denv, address, "id");
+			break;
+		case OPT_OPTIONS:
+			opok = sieve_opr_stringlist_dump(denv, address, "options");
+			break;
+		case OPT_MESSAGE:
+			opok = sieve_opr_string_dump(denv, address, "message");
+			break;
+		default:
+			return FALSE;
 		}
+
+		if ( !opok ) return FALSE;
 	}
 	
 	return TRUE;
@@ -415,10 +412,10 @@ static bool cmd_notify_operation_dump
 static int cmd_notify_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_notify_action *act;
 	pool_t pool;
-	int opt_code = 1;
+	int opt_code = 0;
 	sieve_number_t importance = 1;
 	struct sieve_coded_stringlist *options = NULL;
 	string_t *message = NULL, *id = NULL; 
@@ -429,62 +426,56 @@ static int cmd_notify_operation_execute
 	 */
 		
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
+
+	source_line = sieve_runtime_get_command_location(renv);
 	
 	/* Optional operands */	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
 
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_IMPORTANCE:
-				if ( !sieve_opr_number_read(renv, address, &importance) ) {
-					sieve_runtime_trace_error(renv, "invalid importance operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-	
-				/* Enforce 0 < importance < 4 (just to be sure) */
-				if ( importance < 1 ) 
-					importance = 1;
-				else if ( importance > 3 )
-					importance = 3;
-				break;
-			case OPT_ID:
-				if ( !sieve_opr_string_read(renv, address, &id) ) {
-					sieve_runtime_trace_error(renv, "invalid id operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_MESSAGE:
-				if ( !sieve_opr_string_read(renv, address, &message) ) {
-					sieve_runtime_trace_error(renv, "invalid from operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_OPTIONS:
-				if ( (options=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-					sieve_runtime_trace_error(renv, "invalid options operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			default:
-				sieve_runtime_trace_error(renv, "unknown optional operand: %d", 
-					opt_code);
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_IMPORTANCE:
+			opok = sieve_opr_number_read(renv, address, "importance", &importance);
+			break;
+		case OPT_ID:
+			opok = sieve_opr_string_read(renv, address, "id", &id);
+			break;
+		case OPT_MESSAGE:
+			opok = sieve_opr_string_read(renv, address, "from", &message);
+			break;
+		case OPT_OPTIONS:
+			options = sieve_opr_stringlist_read(renv, address, "options");
+			opok = ( options != NULL );
+			break;
+		default:
+			sieve_runtime_trace_error(renv, "unknown optional operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
 		}
+
+		if ( !opok ) return SIEVE_EXEC_BIN_CORRUPT;
 	}
 		
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "NOTIFY action");	
+	/* Enforce 0 < importance < 4 (just to be sure) */
+
+	if ( importance < 1 ) 
+		importance = 1;
+	else if ( importance > 3 )
+		importance = 3;
+
+	/* Trace */
+
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "notify action");	
 
 	/* Compose action */
 	if ( options != NULL ) {
diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c
index 717ea5c1c4d3e75d7fdf90288ce2b9868d952a5a..f5039824f46bf204c099a7abbece2a20c33d69ef 100644
--- a/src/lib-sieve/plugins/notify/ext-notify-common.c
+++ b/src/lib-sieve/plugins/notify/ext-notify-common.c
@@ -150,7 +150,7 @@ static bool _is_text_content(const struct message_header_line *hdr)
 static buffer_t *cmd_notify_extract_body_text
 (const struct sieve_runtime_env *renv)
 { 
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct ext_notify_message_context *mctx;
 	struct message_parser_ctx *parser;
 	struct message_decoder_context *decoder;
diff --git a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
index 05a531a86b088f7d75a7372215d615871235f61f..06f95f77ac9b71c1e2f9ae3eced3f8b2b0ddc159 100644
--- a/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
+++ b/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
@@ -10,6 +10,7 @@
 #include "sieve-extensions.h"
 #include "sieve-message.h"
 #include "sieve-interpreter.h"
+#include "sieve-runtime-trace.h"
 
 #include "ext-spamvirustest-common.h"
 
@@ -459,7 +460,8 @@ const char *ext_spamvirustest_get_value
 	 * Check whether extension is properly configured 
 	 */
 	if ( ext_data == NULL ) {
-		sieve_runtime_trace(renv, "%s: extension not configured", ext_name);
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, 
+			"%s: extension not configured", ext_name);
 		return "0";
 	}
 
@@ -492,7 +494,8 @@ const char *ext_spamvirustest_get_value
 			if ( mail_get_first_header_utf8
 				(msgdata->mail, max_header->header_name, &header_value) < 0 ||
 				header_value == NULL ) {
-				sieve_runtime_trace(renv, "%s: header '%s' not found in message", 
+				sieve_runtime_trace(renv,  SIEVE_TRLVL_TESTS, 
+					"%s: header '%s' not found in message", 
 					ext_name, max_header->header_name);
 				goto failed;
 			} 
@@ -501,14 +504,16 @@ const char *ext_spamvirustest_get_value
 				/* Execute regex */
 				if ( regexec(&max_header->regexp, header_value, 2, match_values, 0) 
 					!= 0 ) {
-					sieve_runtime_trace(renv, "%s: regexp for header '%s' did not match "
+					sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+						"%s: regexp for header '%s' did not match "
 						"on value '%s'", ext_name, max_header->header_name, header_value);
 					goto failed;
 				}
 
 				max = _regexp_match_get_value(header_value, 1, match_values, 2);
 				if ( max == NULL ) {
-					sieve_runtime_trace(renv, "%s: regexp did not return match value "
+					sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+						"%s: regexp did not return match value "
 						"for string '%s'", ext_name, header_value);
 					goto failed;
 				}
@@ -517,8 +522,8 @@ const char *ext_spamvirustest_get_value
 			}
 
 			if ( !ext_spamvirustest_parse_decimal_value(max, &max_value, &error) ) {
-				sieve_runtime_trace(renv, "%s: failed to parse maximum value: %s", 
-					ext_name, error);
+				sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+					"%s: failed to parse maximum value: %s", ext_name, error);
 				goto failed;
 			}
 		} else {
@@ -526,7 +531,8 @@ const char *ext_spamvirustest_get_value
 		}
 
 		if ( max_value == 0 ) {
-			sieve_runtime_trace(renv, "%s: max value is 0", ext_name);
+			sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, 
+				"%s: max value is 0", ext_name);
 			goto failed;
 		}
 	} else {
@@ -541,7 +547,8 @@ const char *ext_spamvirustest_get_value
 	if ( mail_get_first_header_utf8
 		(msgdata->mail, status_header->header_name, &header_value) < 0 ||
 		header_value == NULL ) {
-		sieve_runtime_trace(renv, "%s: header '%s' not found in message", 
+		sieve_runtime_trace(renv,  SIEVE_TRLVL_TESTS,
+			"%s: header '%s' not found in message", 
 			ext_name, status_header->header_name);
 		goto failed;
 	}
@@ -550,15 +557,17 @@ const char *ext_spamvirustest_get_value
 	if ( status_header->regexp_match ) {
 		if ( regexec(&status_header->regexp, header_value, 2, match_values, 0) 
 			!= 0 ) {
-			sieve_runtime_trace(renv, "%s: regexp for header '%s' did not match "
-				"on value '%s'", ext_name, status_header->header_name, header_value);
+			sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+				"%s: regexp for header '%s' did not match on value '%s'",
+			ext_name, status_header->header_name, header_value);
 			goto failed;
 		} 
 		
 		status = _regexp_match_get_value(header_value, 1, match_values, 2);
 		if ( status == NULL ) {
-			sieve_runtime_trace(renv, "%s: regexp did not return match value "
-				"for string '%s'", ext_name, header_value);
+			sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, 
+				"%s: regexp did not return match value for string '%s'", 
+				ext_name, header_value);
 			goto failed;
 		}
 	} else {
@@ -569,7 +578,8 @@ const char *ext_spamvirustest_get_value
 	case EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE:
 		if ( !ext_spamvirustest_parse_decimal_value
 			(status, &status_value, &error) ) {
-			sieve_runtime_trace(renv, "%s: failed to parse status value '%s': %s", 
+			sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+				"%s: failed to parse status value '%s': %s", 
 				ext_name, status, error);
 			goto failed;
 		}
@@ -577,7 +587,8 @@ const char *ext_spamvirustest_get_value
 	case EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN:
 		if ( !ext_spamvirustest_parse_strlen_value
 			(status, &status_value, &error) ) {
-			sieve_runtime_trace(renv, "%s: failed to parse status value '%s': %s", 
+			sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, 
+				"%s: failed to parse status value '%s': %s", 
 				ext_name, status, error);
 			goto failed;
 		}
@@ -597,7 +608,8 @@ const char *ext_spamvirustest_get_value
 		}
 
 		if ( i > max_text ) {
-			sieve_runtime_trace(renv, "%s: failed to match textstatus value '%s'", 
+			sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, 
+				"%s: failed to match textstatus value '%s'", 
 				ext_name, status);
 			goto failed;	
 		}		
diff --git a/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c b/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c
index 3f1103d8a4eefc16205e71d0c069add31acbda28..f13fe943ee176ebd67c3493141b8b17f6b6beafa 100644
--- a/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c
+++ b/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c
@@ -205,26 +205,28 @@ static bool tst_spamvirustest_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
 	int opt_code = 0;
-	const struct sieve_operation *op = &denv->oprtn;
+	const struct sieve_operation *op = denv->oprtn;
 
 	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
 	sieve_code_descend(denv);
 	
-	/* Handle any optional arguments */
-  do {
-		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	/* Optional operands */
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 )
 			return FALSE;
 
+		if ( ret == 0 ) break;
+
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_SPAMTEST_PERCENT:
 			sieve_code_dumpf(denv, "percent");
 			break;
     default:
 			return FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+	}
 
 	return
 		sieve_opr_string_dump(denv, address, "value");
@@ -237,7 +239,7 @@ static bool tst_spamvirustest_operation_dump
 static int tst_spamvirustest_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
-	const struct sieve_operation *op = &renv->oprtn;
+	const struct sieve_operation *op = renv->oprtn;
 	const struct sieve_extension *this_ext = op->ext;
 	bool result = TRUE, matched = FALSE;
 	int opt_code = 0;
@@ -252,14 +254,16 @@ static int tst_spamvirustest_operation_execute
 	int ret;
 	
 	/* Read optional operands */
-	do {
-		if ( (ret=sieve_match_read_optional_operands
-			(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
-			return ret;
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_read
+			(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
 
+		if ( ret == 0 ) break;
+	
 		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
 		case OPT_SPAMTEST_PERCENT:
 			percent = TRUE;
 			break;
@@ -267,17 +271,16 @@ static int tst_spamvirustest_operation_execute
 			sieve_runtime_trace_error(renv, "unknown optional operand");
 			return SIEVE_EXEC_BIN_CORRUPT;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+	}
 
 	/* Read value part */
-	if ( (key_value=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid value operand");
+	if ( (key_value=sieve_opr_stringlist_read(renv, address, "value")) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 			
 	/* Perform test */
 
-	sieve_runtime_trace(renv, "%s test", sieve_operation_mnemonic(op));
+	sieve_runtime_trace
+		(renv, SIEVE_TRLVL_TESTS, "%s test", sieve_operation_mnemonic(op));
 
 	/* Initialize match */
 	mctx = sieve_match_begin(renv->interp, &mcht, &cmp, NULL, key_value); 	
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index 48d8e5a30cd0caaaf433ede35614be70bcea73e6..6152eaf0cf0be3c04a60f65f9a85f14146eb57db 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -482,46 +482,43 @@ static bool cmd_vacation_generate
 static bool ext_vacation_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {	
-	int opt_code = 1;
+	int opt_code = 0;
 	
 	sieve_code_dumpf(denv, "VACATION");
 	sieve_code_descend(denv);	
 
 	/* Dump optional operands */
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			sieve_code_mark(denv);
-			
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) 
-				return FALSE;
 
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_DAYS:
-				if ( !sieve_opr_number_dump(denv, address, "days") )
-					return FALSE;
-				break;
-			case OPT_SUBJECT:
-				if ( !sieve_opr_string_dump(denv, address, "subject") )
-					return FALSE;
-				break;
-			case OPT_FROM:
-				if ( !sieve_opr_string_dump(denv, address, "from") )
-					return FALSE;
-				break;
-			case OPT_ADDRESSES:
-				if ( !sieve_opr_stringlist_dump(denv, address, "addresses") )
-					return FALSE;
-				break;
-			case OPT_MIME:
-				sieve_code_dumpf(denv, "mime");	
-				break;
-			
-			default:
-				return FALSE;
-			}
+	for (;;) {
+		int ret;
+		bool opok = TRUE;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+			return FALSE;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_DAYS:
+			opok = sieve_opr_number_dump(denv, address, "days");
+			break;
+		case OPT_SUBJECT:
+			opok = sieve_opr_string_dump(denv, address, "subject");
+			break;
+		case OPT_FROM:
+			opok = sieve_opr_string_dump(denv, address, "from");
+			break;
+		case OPT_ADDRESSES:
+			opok = sieve_opr_stringlist_dump(denv, address, "addresses");
+			break;
+		case OPT_MIME:
+			sieve_code_dumpf(denv, "mime");	
+			break;		
+		default:
+			return FALSE;
 		}
+
+		if ( !opok ) return FALSE;
 	}
 	
 	/* Dump reason and handle operands */
@@ -537,11 +534,11 @@ static bool ext_vacation_operation_dump
 static int ext_vacation_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
-	const struct sieve_extension *this_ext = renv->oprtn.ext;
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	struct sieve_side_effects_list *slist = NULL;
 	struct act_vacation_context *act;
 	pool_t pool;
-	int opt_code = 1;
+	int opt_code = 0;
 	sieve_number_t days = 7;
 	bool mime = FALSE;
 	struct sieve_coded_stringlist *addresses = NULL;
@@ -550,76 +547,53 @@ static int ext_vacation_operation_execute
 	const char *from_normalized = NULL;
 
 	/*
-	 * Read operands
+	 * Read code
 	 */
 		
 	/* Source line */
-	source_line = sieve_runtime_get_source_location(renv, renv->oprtn.address);
-	
-	/* Optional operands */	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
 
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_DAYS:
-				if ( !sieve_opr_number_read(renv, address, &days) ) {
-					sieve_runtime_trace_error(renv, 
-						"invalid days operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
+	source_line = sieve_runtime_get_command_location(renv);
 	
-				/* Enforce days > 0 (just to be sure) */
-				if ( days == 0 )
-					days = 1;
-				break;
-			case OPT_SUBJECT:
-				if ( !sieve_opr_string_read(renv, address, &subject) ) {
-					sieve_runtime_trace_error(renv, 
-						"invalid subject operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_FROM:
-				if ( !sieve_opr_string_read(renv, address, &from) ) {
-					sieve_runtime_trace_error(renv, 
-						"invalid from address operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_ADDRESSES:
-				if ( (addresses=sieve_opr_stringlist_read(renv, address))
-					== NULL ) {
-					sieve_runtime_trace_error(renv, 
-						"invalid addresses operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case OPT_MIME:
-				mime = TRUE;
-				break;
-			default:
-				sieve_runtime_trace_error(renv, 
-					"unknown optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
+	/* Optional operands */
+
+	for (;;) {
+		bool opok = TRUE;
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_DAYS:
+			opok = sieve_opr_number_read(renv, address, "days", &days);
+			break;
+		case OPT_SUBJECT:
+			opok = sieve_opr_string_read(renv, address, "subject", &subject);
+			break;
+		case OPT_FROM:
+			opok = sieve_opr_string_read(renv, address, "from", &from);
+			break;
+		case OPT_ADDRESSES:
+			addresses = sieve_opr_stringlist_read(renv, address, "addresses");
+			opok = ( addresses != NULL );
+			break;
+		case OPT_MIME:
+			mime = TRUE;
+			break;
+		default:
+			sieve_runtime_trace_error(renv, "unknown optional operand");
+			opok = FALSE;
 		}
+
+		if ( !opok ) return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
-	/* Reason operand */
-	if ( !sieve_opr_string_read(renv, address, &reason) ) {
-		sieve_runtime_trace_error(renv, "invalid reason operand");
-		return SIEVE_EXEC_BIN_CORRUPT;
-	}
-	
-	/* Handle operand */
-	if ( !sieve_opr_string_read(renv, address, &handle) ) {
-		sieve_runtime_trace_error(renv, "invalid handle operand");
+	/* Fixed operands */
+
+	if ( !sieve_opr_string_read(renv, address, "reason", &reason) ||
+		!sieve_opr_string_read(renv, address, "handle", &handle) ) {
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
@@ -627,7 +601,14 @@ static int ext_vacation_operation_execute
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "VACATION action");	
+	/* Enforce days > 0 (just to be sure) */
+
+	if ( days == 0 )
+		days = 1;
+
+	/* Trace */
+	
+	sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "vacation action");	
 
 	/* Check and normalize :from address */
 	if ( from != NULL ) {
diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c
index 1870a07aaf46e539e6bae941675415b797473111..35bd7dfa2e6ede9cf254823cb7534631b040a8c5 100644
--- a/src/lib-sieve/plugins/variables/cmd-set.c
+++ b/src/lib-sieve/plugins/variables/cmd-set.c
@@ -291,20 +291,13 @@ static int cmd_set_operation_execute
 	 * Read the normal operands
 	 */
 		
-	/* Read the variable */
 	if ( !sieve_variable_operand_read
-		(renv, address, &storage, &var_index) ) {
-		sieve_runtime_trace_error(renv, "invalid variable operand");
+		(renv, address, "variable", &storage, &var_index) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 		
-	/* Read the raw string value */
-	if ( !sieve_opr_string_read(renv, address, &value) ) {
-		sieve_runtime_trace_error(renv, "invalid string operand");
+	if ( !sieve_opr_string_read(renv, address, "string", &value) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
-		
-	/* Read the number of modifiers used */
+
 	if ( !sieve_binary_read_byte(renv->sblock, address, &mdfs) ) {
 		sieve_runtime_trace_error(renv, "invalid modifier count");
 		return SIEVE_EXEC_BIN_CORRUPT;
@@ -314,7 +307,7 @@ static int cmd_set_operation_execute
 	 * Determine and assign the value 
 	 */
 
-	sieve_runtime_trace(renv, "SET action");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "set command");
 
 	/* Hold value within limits */
 	if ( str_len(value) > EXT_VARIABLES_MAX_VARIABLE_SIZE )
@@ -329,8 +322,6 @@ static int cmd_set_operation_execute
 					
 				if ( !ext_variables_opr_modifier_read(renv, address, &modf) ) {
 					value = NULL;
-
-					sieve_runtime_trace_error(renv, "invalid modifier operand");
 					ret = SIEVE_EXEC_BIN_CORRUPT;
 					break;
 				}
diff --git a/src/lib-sieve/plugins/variables/ext-variables-modifiers.h b/src/lib-sieve/plugins/variables/ext-variables-modifiers.h
index 92ee6ccebd06efe3d87e9b6ce6e23a3c8cecf605..64dc163fac42e93c6303d3fc3c0d7704d04fc9f3 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-modifiers.h
+++ b/src/lib-sieve/plugins/variables/ext-variables-modifiers.h
@@ -4,8 +4,10 @@
 #ifndef __EXT_VARIABLES_MODIFIERS_H
 #define __EXT_VARIABLES_MODIFIERS_H
 
+#include "sieve-common.h"
+#include "sieve-runtime-trace.h"
+
 #include "ext-variables-common.h"
-#include "sieve-ext-variables.h"
 
 #define ext_variables_namespace_name(nspc) \
 	(nspc)->object->def->name
@@ -45,8 +47,10 @@ static inline bool ext_variables_opr_modifier_read
 	struct sieve_variables_modifier *modf)
 {
 	if ( !sieve_opr_object_read
-		(renv, &sieve_variables_modifier_operand_class, address, &modf->object) )
+		(renv, &sieve_variables_modifier_operand_class, address, &modf->object) ) {
+		sieve_runtime_trace_error(renv, "invalid modifier operand");
 		return FALSE;
+	}
 
 	modf->def = (const struct sieve_variables_modifier_def *) modf->object.def;
 	return TRUE;
diff --git a/src/lib-sieve/plugins/variables/ext-variables-operands.c b/src/lib-sieve/plugins/variables/ext-variables-operands.c
index fccfd673f4c7b6cb1ec577a1c5ff55345ae46641..bb2b76d229dee280fbf6987242feac852080cad2 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-operands.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-operands.c
@@ -137,42 +137,55 @@ static bool opr_variable_read_value
 		
 bool sieve_variable_operand_read_data
 (const struct sieve_runtime_env *renv, const struct sieve_operand *operand, 
-	sieve_size_t *address, struct sieve_variable_storage **storage, 
-	unsigned int *var_index)
+	sieve_size_t *address, const char *field_name, 
+	struct sieve_variable_storage **storage, unsigned int *var_index)
 {
 	const struct sieve_extension *ext;
 	unsigned int code = 1; /* Initially set to offset value */
 	unsigned int idx = 0;
 
 	if ( !sieve_operand_is_variable(operand) ) {
+		sieve_runtime_trace_operand_error
+			(renv, operand, field_name, "expected variable operand but found %s",
+				sieve_operand_name(operand));
 		return FALSE;
 	}
 
-	if ( !sieve_binary_read_extension(renv->sblock, address, &code, &ext) )
+	if ( !sieve_binary_read_extension(renv->sblock, address, &code, &ext) ) {
+		sieve_runtime_trace_operand_error
+			(renv, operand, field_name, "variable operand: failed to read extension");
 		return FALSE;
+	}
 		
 	*storage = sieve_ext_variables_get_storage(operand->ext, renv->interp, ext);
-	if ( *storage == NULL )	
+	if ( *storage == NULL )	{
+		sieve_runtime_trace_operand_error(renv, operand, field_name, 
+			"variable operand: failed to get variable storage");
 		return FALSE;
+	}
 	
-	if ( !sieve_binary_read_unsigned(renv->sblock, address, &idx) )
-		return FALSE;		
+	if ( !sieve_binary_read_unsigned(renv->sblock, address, &idx) ) {
+		sieve_runtime_trace_operand_error
+			(renv, operand, field_name, "variable operand: failed to read index");
+		return FALSE;
+	}
 
 	*var_index = idx;
 	return TRUE;
 }
 
 bool sieve_variable_operand_read
-(const struct sieve_runtime_env *renv, sieve_size_t *address, 
-	struct sieve_variable_storage **storage, unsigned int *var_index)
+(const struct sieve_runtime_env *renv, sieve_size_t *address,
+	const char *field_name, struct sieve_variable_storage **storage, 
+	unsigned int *var_index)
 {
 	struct sieve_operand operand;
 
-	if ( !sieve_operand_read(renv->sblock, address, &operand) )
+	if ( !sieve_operand_runtime_read(renv, address, field_name, &operand) )
 		return FALSE;
 
 	return sieve_variable_operand_read_data
-		(renv, &operand, address, storage, var_index);
+		(renv, &operand, address, field_name, storage, var_index);
 }
 	
 /* 
diff --git a/src/lib-sieve/plugins/variables/sieve-ext-variables.h b/src/lib-sieve/plugins/variables/sieve-ext-variables.h
index 3a6aa7208b2aa84dd5340b006529f7f7762892c3..2779e44c595f39d8b65cc0949bda76bfafa4549e 100644
--- a/src/lib-sieve/plugins/variables/sieve-ext-variables.h
+++ b/src/lib-sieve/plugins/variables/sieve-ext-variables.h
@@ -212,11 +212,12 @@ void sieve_variables_opr_match_value_emit
 
 bool sieve_variable_operand_read_data
 	(const struct sieve_runtime_env *renv, const struct sieve_operand *operand, 
-		sieve_size_t *address, struct sieve_variable_storage **storage, 
-		unsigned int *var_index);
-bool sieve_variable_operand_read
-	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+		sieve_size_t *address, const char *field_name,
 		struct sieve_variable_storage **storage, unsigned int *var_index);
+bool sieve_variable_operand_read
+	(const struct sieve_runtime_env *renv, sieve_size_t *address,
+		const char *field_name, struct sieve_variable_storage **storage,
+		unsigned int *var_index);
 		
 static inline bool sieve_operand_is_variable
 (const struct sieve_operand *operand)
diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c
index 7ca25ab3655346ad0aff4bbcc3f94537cda9c2b5..fc05636ac60719a709e59ca82e8837a9b71abba5 100644
--- a/src/lib-sieve/plugins/variables/tst-string.c
+++ b/src/lib-sieve/plugins/variables/tst-string.c
@@ -147,13 +147,10 @@ static bool tst_string_operation_dump
 	sieve_code_dumpf(denv, "STRING-TEST");
 	sieve_code_descend(denv);
 
-	/* Handle any optional arguments */
-	if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	/* Optional operands */
+	if ( sieve_match_opr_optional_dump(denv, address, &opt_code) != 0 )
 		return FALSE;
 
-	if ( opt_code != SIEVE_MATCH_OPT_END )
-		return FALSE;
-		
 	return
 		sieve_opr_stringlist_dump(denv, address, "source") &&
 		sieve_opr_stringlist_dump(denv, address, "key list");
@@ -184,33 +181,29 @@ static int tst_string_operation_execute
 	 */
 	
 	/* Handle match-type and comparator operands */
-	if ( (ret=sieve_match_read_optional_operands
-		(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
-		return ret;
-	
+	if ( (ret=sieve_match_opr_optional_read
+		(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+		return SIEVE_EXEC_BIN_CORRUPT;
+
 	/* Check whether we neatly finished the list of optional operands*/
-	if ( opt_code != SIEVE_MATCH_OPT_END) {
+	if ( ret > 0 ) {
 		sieve_runtime_trace_error(renv, "invalid optional operand");
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
-
+	
 	/* Read source */
-	if ( (source=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid source operand");
+	if ( (source=sieve_opr_stringlist_read(renv, address, "source")) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "STRING test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "string test");
 
 	mctx = sieve_match_begin(renv->interp, &mcht, &cmp, NULL, key_list); 	
 
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index d1d0e30ee234f7cb889fda72404c943a553adc58..1d61d3a8532c0200fede42a42e4e12628ddd0fca 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -73,6 +73,81 @@ bool sieve_opr_side_effect_dump
 	return TRUE;
 }
 
+/*
+ * Optional operands
+ */
+
+int sieve_action_opr_optional_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address,
+	signed int *opt_code)
+{	
+	signed int _opt_code = 0;
+	bool final = FALSE, opok = TRUE;
+
+	if ( opt_code == NULL ) {
+		opt_code = &_opt_code;
+		final = TRUE;
+	}
+	
+	while ( opok ) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 )
+			return ret;
+
+		if ( *opt_code == SIEVE_OPT_SIDE_EFFECT ) {
+			opok = sieve_opr_side_effect_dump(denv, address);
+		} else {
+			return ( final ? -1 : 1 );
+		}
+	}
+
+	return -1;
+}
+
+int sieve_action_opr_optional_read
+(const struct sieve_runtime_env *renv, sieve_size_t *address,
+	signed int *opt_code, struct sieve_side_effects_list **list)
+{
+	signed int _opt_code = 0;
+	bool final = FALSE;
+
+	if ( opt_code == NULL ) {
+		opt_code = &_opt_code;
+		final = TRUE;
+	}
+
+	for ( ;; ) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, opt_code)) <= 0 )
+			return ret;
+
+		if ( *opt_code == SIEVE_OPT_SIDE_EFFECT ) {
+			struct sieve_side_effect seffect;
+		
+			if ( list == NULL )
+				return -1;
+				
+			if ( !sieve_opr_side_effect_read(renv, address, &seffect) )
+				return -1;
+		
+			if ( *list == NULL ) 
+				*list = sieve_side_effects_list_create(renv->result);
+
+			sieve_side_effects_list_add(*list, &seffect);
+		} else {
+			if ( final ) {
+				sieve_runtime_trace_error(renv, "invalid optional operand");
+				return -1;
+			}
+			return 1;
+		}
+	}
+
+	return -1;
+}
+
 /*
  * Store action
  */
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index 211f5606e8e4b2a914b9345ebc4b1b51b81dd3db..f84a7aaaed1c45aa44de9a039b9c579fe0a72bea 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -183,6 +183,18 @@ bool sieve_opr_side_effect_read
 bool sieve_opr_side_effect_dump
 	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
 
+/*
+ * Optional operands
+ */
+
+int sieve_action_opr_optional_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address,
+		signed int *opt_code);
+
+int sieve_action_opr_optional_read
+	(const struct sieve_runtime_env *renv, sieve_size_t *address,
+		signed int *opt_code, struct sieve_side_effects_list **list);
+
 /* 
  * Core actions 
  */
diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c
index 247848aa54ccdc12852ce65e573e24c4c8e77930..d163e8ce79b5f0d574a32723ed5075842b869d2b 100644
--- a/src/lib-sieve/sieve-address-parts.c
+++ b/src/lib-sieve/sieve-address-parts.c
@@ -290,74 +290,81 @@ int sieve_address_match
  * Default ADDRESS-PART, MATCH-TYPE, COMPARATOR access
  */
  
-bool sieve_addrmatch_default_dump_optionals
-(const struct sieve_dumptime_env *denv, sieve_size_t *address) 
+int sieve_addrmatch_opr_optional_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address, 
+	signed int *opt_code) 
 {
-	int opt_code = 1;
-	
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) 
-				return FALSE;
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case SIEVE_AM_OPT_COMPARATOR:
-				if ( !sieve_opr_comparator_dump(denv, address) )
-					return FALSE;
-				break;
-			case SIEVE_AM_OPT_MATCH_TYPE:
-				if ( !sieve_opr_match_type_dump(denv, address) )
-					return FALSE;
-				break;
-			case SIEVE_AM_OPT_ADDRESS_PART:
-				if ( !sieve_opr_address_part_dump(denv, address) )
-					return FALSE;
-				break;
-			default:
-				return FALSE;
-			}
+	signed int _opt_code = 0;
+	bool final = FALSE, opok = TRUE;
+
+	if ( opt_code == NULL ) {
+		opt_code = &_opt_code;
+		final = TRUE;
+	}
+
+	while ( opok ) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 )
+			return ret;
+
+		switch ( *opt_code ) {
+		case SIEVE_AM_OPT_COMPARATOR:
+			opok = sieve_opr_comparator_dump(denv, address);
+			break;
+		case SIEVE_AM_OPT_MATCH_TYPE:
+			opok = sieve_opr_match_type_dump(denv, address);
+			break;
+		case SIEVE_AM_OPT_ADDRESS_PART:
+			opok = sieve_opr_address_part_dump(denv, address);
+			break;
+		default:
+			return ( final ? -1 : 1 );
 		}
 	}
-	
-	return TRUE;
+
+	return -1;
 }
 
-bool sieve_addrmatch_default_get_optionals
-(const struct sieve_runtime_env *renv, sieve_size_t *address, 
-	struct sieve_address_part *addrp, struct sieve_match_type *mtch, 
-	struct sieve_comparator *cmp) 
+int sieve_addrmatch_opr_optional_read
+(const struct sieve_runtime_env *renv, sieve_size_t *address,
+	signed int *opt_code, struct sieve_address_part *addrp,
+	struct sieve_match_type *mtch, struct sieve_comparator *cmp) 
 {
-	int opt_code = 1;
-	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) )
-				return FALSE;
-				  
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case SIEVE_AM_OPT_COMPARATOR:
-				if ( !sieve_opr_comparator_read(renv, address, cmp) )
-					return FALSE;
-				break;
-			case SIEVE_AM_OPT_MATCH_TYPE:
-				if ( !sieve_opr_match_type_read(renv, address, mtch) )
-					return FALSE;
-				break;
-			case SIEVE_AM_OPT_ADDRESS_PART:
-				if ( !sieve_opr_address_part_read(renv, address, addrp) )
-					return FALSE;
-				break;
-			default:
-				return FALSE;
+	signed int _opt_code = 0;
+	bool final = FALSE, opok = TRUE;
+
+	if ( opt_code == NULL ) {
+		opt_code = &_opt_code;
+		final = TRUE;
+	}
+
+	while ( opok ) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, opt_code)) <= 0 )
+			return ret;
+
+		switch ( *opt_code ) {
+		case SIEVE_AM_OPT_COMPARATOR:
+			opok = sieve_opr_comparator_read(renv, address, cmp);
+			break;
+		case SIEVE_AM_OPT_MATCH_TYPE:
+			opok = sieve_opr_match_type_read(renv, address, mtch);
+			break;
+		case SIEVE_AM_OPT_ADDRESS_PART:
+			opok = sieve_opr_address_part_read(renv, address, addrp);
+			break;
+		default:
+			if ( final ) {
+				sieve_runtime_trace_error(renv, "invalid optional operand");
+				return -1;
 			}
+			return 1;
 		}
 	}
-	
-	return TRUE;
+
+	return -1;
 }
 
 /* 
diff --git a/src/lib-sieve/sieve-address-parts.h b/src/lib-sieve/sieve-address-parts.h
index 699bf23983c23034333ec5d7d243c7c18e533696..3df79c380f65a34eb6aa0c2d9757eccffa2c40d3 100644
--- a/src/lib-sieve/sieve-address-parts.h
+++ b/src/lib-sieve/sieve-address-parts.h
@@ -121,12 +121,13 @@ enum sieve_addrmatch_opt_operand {
 	SIEVE_AM_OPT_MATCH_TYPE
 };
 
-bool sieve_addrmatch_default_dump_optionals
-	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
+int sieve_addrmatch_opr_optional_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address,
+		signed int *opt_code);
 
-bool sieve_addrmatch_default_get_optionals
+int sieve_addrmatch_opr_optional_read
 	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
-		struct sieve_address_part *addrp, 
+		signed int *opt_code, struct sieve_address_part *addrp,
 		struct sieve_match_type *mtch, struct sieve_comparator *cmp);
 
 #endif /* __SIEVE_ADDRESS_PARTS_H */
diff --git a/src/lib-sieve/sieve-code-dumper.c b/src/lib-sieve/sieve-code-dumper.c
index 1c224c13c317371588dc9dca0a928354ff31be86..cd12e700f2576b425798fd8dd7cfa7745e53f5f7 100644
--- a/src/lib-sieve/sieve-code-dumper.c
+++ b/src/lib-sieve/sieve-code-dumper.c
@@ -34,10 +34,8 @@ struct sieve_code_dumper_extension_reg {
 struct sieve_code_dumper {
 	pool_t pool;
 					
-	/* Dump status */
-	sieve_size_t pc;          /* Program counter */
-	
-	const struct sieve_operation *operation;
+	/* Dump status */	
+	struct sieve_operation oprtn;
 	sieve_size_t mark_address;
 	unsigned int mark_line;
 	unsigned int mark_last_line;
@@ -55,71 +53,70 @@ struct sieve_code_dumper *sieve_code_dumper_create
 (struct sieve_dumptime_env *denv) 
 {
 	pool_t pool;
-	struct sieve_code_dumper *dumper;
+	struct sieve_code_dumper *cdumper;
 	
 	pool = pool_alloconly_create("sieve_code_dumper", 4096);	
-	dumper = p_new(pool, struct sieve_code_dumper, 1);
-	dumper->pool = pool;
-	dumper->dumpenv = denv;
-	dumper->pc = 0;
+	cdumper = p_new(pool, struct sieve_code_dumper, 1);
+	cdumper->pool = pool;
+	cdumper->dumpenv = denv;
 	
 	/* Setup storage for extension contexts */		
-	p_array_init(&dumper->extensions, pool, 
+	p_array_init(&cdumper->extensions, pool, 
 		sieve_extensions_get_count(denv->svinst));
 
-	return dumper;
+	return cdumper;
 }
 
-void sieve_code_dumper_free(struct sieve_code_dumper **dumper) 
+void sieve_code_dumper_free(struct sieve_code_dumper **cdumper) 
 {	
-	sieve_binary_debug_reader_deinit(&(*dumper)->dreader);
+	sieve_binary_debug_reader_deinit(&(*cdumper)->dreader);
 
-	pool_unref(&((*dumper)->pool));
-	*dumper = NULL;
+	pool_unref(&((*cdumper)->pool));
+	*cdumper = NULL;
 }
 
-pool_t sieve_code_dumper_pool(struct sieve_code_dumper *dumper)
+pool_t sieve_code_dumper_pool(struct sieve_code_dumper *cdumper)
 {
-	return dumper->pool;
+	return cdumper->pool;
 }
 
 /* EXtension support */
 
 void sieve_dump_extension_register
-(struct sieve_code_dumper *dumper, const struct sieve_extension *ext,
+(struct sieve_code_dumper *cdumper, const struct sieve_extension *ext,
 	const struct sieve_code_dumper_extension *cdmpext, void *context)
 {
 	struct sieve_code_dumper_extension_reg *reg;
 
 	if ( ext->id < 0 ) return;
 
-	reg = array_idx_modifiable(&dumper->extensions, (unsigned int) ext->id);
+	reg = array_idx_modifiable(&cdumper->extensions, (unsigned int) ext->id);
 	reg->cdmpext = cdmpext;
 	reg->ext = ext;
 	reg->context = context;
 }
 
 void sieve_dump_extension_set_context
-(struct sieve_code_dumper *dumper, const struct sieve_extension *ext, 
+(struct sieve_code_dumper *cdumper, const struct sieve_extension *ext, 
 	void *context)
 {
 	struct sieve_code_dumper_extension_reg *reg;
 
 	if ( ext->id < 0 ) return;
 
-	reg = array_idx_modifiable(&dumper->extensions, (unsigned int) ext->id);
+	reg = array_idx_modifiable(&cdumper->extensions, (unsigned int) ext->id);
 	reg->context = context;
 }
 
 void *sieve_dump_extension_get_context
-(struct sieve_code_dumper *dumper, const struct sieve_extension *ext) 
+(struct sieve_code_dumper *cdumper, const struct sieve_extension *ext) 
 {
 	const struct sieve_code_dumper_extension_reg *reg;
 
-	if  ( ext->id < 0 || ext->id >= (int) array_count(&dumper->extensions) )
+	if  ( ext->id < 0 || ext->id >= (int) array_count(&cdumper->extensions) )
 		return NULL;
 	
-	reg = array_idx(&dumper->extensions, (unsigned int) ext->id);		
+	reg = array_idx(&cdumper->extensions, (unsigned int) ext->id);		
 
 	return reg->context;
 }
@@ -169,8 +166,8 @@ static inline void sieve_code_line_mark
 
 void sieve_code_mark(const struct sieve_dumptime_env *denv)
 {
-	denv->cdumper->mark_address = denv->cdumper->pc;
-	sieve_code_line_mark(denv, denv->cdumper->pc);
+	denv->cdumper->mark_address = denv->offset;
+	sieve_code_line_mark(denv, denv->offset);
 }
 
 void sieve_code_mark_specific
@@ -190,53 +187,30 @@ void sieve_code_ascend(const struct sieve_dumptime_env *denv)
 	if ( denv->cdumper->indent > 0 )
 		denv->cdumper->indent--;
 }
-
-/* Operations and operands */
-
-bool sieve_code_dumper_print_optional_operands
-	(const struct sieve_dumptime_env *denv, sieve_size_t *address)
-{
-	int opt_code = -1;
-	
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		
-		while ( opt_code != 0 ) {			
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) {
-				return FALSE;
-			}
-
-			if ( opt_code == SIEVE_OPT_SIDE_EFFECT ) {
-				if ( !sieve_opr_side_effect_dump(denv, address) )
-					return FALSE;
-			}
-		}
-	} 
-	return TRUE;
-}
  
 /* Code Dump */
 
 static bool sieve_code_dumper_print_operation
-	(struct sieve_code_dumper *dumper) 
+(struct sieve_code_dumper *cdumper) 
 {	
-	struct sieve_dumptime_env *denv = dumper->dumpenv;
-	struct sieve_operation *oprtn = &denv->oprtn;
-	sieve_size_t address;
-	
+	struct sieve_dumptime_env *denv = cdumper->dumpenv;
+	struct sieve_operation *oprtn = &(cdumper->oprtn);
+	sieve_size_t *address	= &(denv->offset);
+
 	/* Mark start address of operation */
-	dumper->indent = 0;
-	address = dumper->mark_address = dumper->pc;
+	cdumper->indent = 0;
+	cdumper->mark_address = *address;
 
-	sieve_code_line_mark(denv, address);
+	sieve_code_line_mark(denv, *address);
 
 	/* Read operation */
-	if ( sieve_operation_read(denv->sblock, &(dumper->pc), oprtn) ) {
-		const struct sieve_operation_def *op = oprtn->def;
+	if ( sieve_operation_read(denv->sblock, address, oprtn) ) {
+		const struct sieve_operation_def *opdef = oprtn->def;
 
-		if ( op->dump != NULL )
-			return op->dump(denv, &(dumper->pc));
-		else if ( op->mnemonic != NULL )
-			sieve_code_dumpf(denv, "%s", op->mnemonic);
+		if ( opdef->dump != NULL )
+			return opdef->dump(denv, address);
+		else if ( opdef->mnemonic != NULL )
+			sieve_code_dumpf(denv, "%s", opdef->mnemonic);
 		else
 			return FALSE;
 			
@@ -247,15 +221,18 @@ static bool sieve_code_dumper_print_operation
 	return FALSE;
 }
 
-void sieve_code_dumper_run(struct sieve_code_dumper *dumper) 
+void sieve_code_dumper_run(struct sieve_code_dumper *cdumper) 
 {
-	const struct sieve_dumptime_env *denv = dumper->dumpenv;
+	struct sieve_dumptime_env *denv = cdumper->dumpenv;
 	struct sieve_binary *sbin = denv->sbin;
 	struct sieve_binary_block *sblock = denv->sblock;
 	unsigned int debug_block_id, ext_count;
 	bool success = TRUE;
+	sieve_size_t *address;
 
-	dumper->pc = 0;
+	denv->offset = 0;
+	denv->oprtn = &(cdumper->oprtn);
+	address = &(denv->offset);
 
 	/* Heading */
 	o_stream_send_str(denv->stream, "Address   Line  Code\n");
@@ -263,7 +240,7 @@ void sieve_code_dumper_run(struct sieve_code_dumper *dumper)
 	/* Load debug block */
 	sieve_code_mark(denv);
 	
-	if ( sieve_binary_read_unsigned(sblock, &dumper->pc, &debug_block_id) ) {
+	if ( sieve_binary_read_unsigned(sblock, address, &debug_block_id) ) {
 		struct sieve_binary_block *debug_block =
 			sieve_binary_block_get(sbin, debug_block_id);
 
@@ -272,7 +249,7 @@ void sieve_code_dumper_run(struct sieve_code_dumper *dumper)
 			return;
 		} else {
 			/* Initialize debug reader */
-			dumper->dreader = sieve_binary_debug_reader_init(debug_block);
+			cdumper->dreader = sieve_binary_debug_reader_init(debug_block);
 
 			/* Dump block id */
 			sieve_code_dumpf(denv, "DEBUG BLOCK: %d", debug_block_id);
@@ -285,7 +262,7 @@ void sieve_code_dumper_run(struct sieve_code_dumper *dumper)
 	/* Load and dump extensions listed in code */
 	sieve_code_mark(denv);
 	
-	if ( sieve_binary_read_unsigned(sblock, &dumper->pc, &ext_count) ) {
+	if ( sieve_binary_read_unsigned(sblock, address, &ext_count) ) {
 		unsigned int i;
 		
 		sieve_code_dumpf(denv, "EXTENSIONS [%d]:", ext_count);
@@ -298,7 +275,7 @@ void sieve_code_dumper_run(struct sieve_code_dumper *dumper)
 			T_BEGIN {
 				sieve_code_mark(denv);
 			
-				if ( !sieve_binary_read_extension(sblock, &dumper->pc, &code, &ext) ) {
+				if ( !sieve_binary_read_extension(sblock, address, &code, &ext) ) {
 					success = FALSE;
 					break;
 				}
@@ -307,7 +284,7 @@ void sieve_code_dumper_run(struct sieve_code_dumper *dumper)
       
 				if ( ext->def != NULL && ext->def->code_dump != NULL ) {
 					sieve_code_descend(denv);
-					if ( !ext->def->code_dump(ext, denv, &dumper->pc) ) {
+					if ( !ext->def->code_dump(ext, denv, address) ) {
 						success = FALSE;
 						break;
 					}
@@ -325,21 +302,20 @@ void sieve_code_dumper_run(struct sieve_code_dumper *dumper)
 		return;
 	}
 	
-	while ( dumper->pc < 
-		sieve_binary_block_get_size(sblock) ) {
+	while ( *address < sieve_binary_block_get_size(sblock) ) {
 
 		T_BEGIN {
-			success = sieve_code_dumper_print_operation(dumper);
+			success = sieve_code_dumper_print_operation(cdumper);
 		} T_END;
 
 		if ( !success ) {
-			sieve_code_dumpf(dumper->dumpenv, "Binary is corrupt.");
+			sieve_code_dumpf(denv, "Binary is corrupt.");
 			return;
 		}
 	}
 	
 	/* Mark end of the binary */
-	dumper->indent = 0;
-	dumper->mark_address = sieve_binary_block_get_size(sblock);
-	sieve_code_dumpf(dumper->dumpenv, "[End of code]");	
+	cdumper->indent = 0;
+	cdumper->mark_address = sieve_binary_block_get_size(sblock);
+	sieve_code_dumpf(denv, "[End of code]");	
 }
diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c
index c63a10482bab63efd0b942721836ddb1eeb71ffa..6f15607ac30625aa39a79fb23acb1b4e953b5afd 100644
--- a/src/lib-sieve/sieve-code.c
+++ b/src/lib-sieve/sieve-code.c
@@ -62,7 +62,7 @@ bool sieve_coded_stringlist_next_item
 	else {
 		address = strlist->current_offset;
   	
-		if ( sieve_opr_string_read(strlist->runenv, &address, str_r) ) {
+		if ( sieve_opr_string_read(strlist->runenv, &address, NULL, str_r) ) {
 			strlist->index++;
 			strlist->current_offset = address;
 			return TRUE;
@@ -233,30 +233,31 @@ bool sieve_operand_read
 	return ( operand->def != NULL );
 }
 
-bool sieve_operand_optional_present
-(struct sieve_binary_block *sblock, sieve_size_t *address)
+/*
+ * Optional operand
+ */
+
+int sieve_opr_optional_next
+(struct sieve_binary_block *sblock, sieve_size_t *address, signed int *opt_code)
 {	
-	sieve_size_t tmp_addr = *address;
-	unsigned int op = -1;
+	/* Start of optional operand block */
+	if ( *opt_code == 0 ) {
+		sieve_size_t tmp_addr = *address;
+		unsigned int op;
+	
+		if ( !sieve_binary_read_byte(sblock, &tmp_addr, &op) ||
+			op != SIEVE_OPERAND_OPTIONAL )
+			return 0;
 	
-	if ( sieve_binary_read_byte(sblock, &tmp_addr, &op) && 
-		(op == SIEVE_OPERAND_OPTIONAL) ) {
 		*address = tmp_addr;
-		return TRUE;
 	}
-	
-	return FALSE;
-}
 
-bool sieve_operand_optional_read
-(struct sieve_binary_block *sblock, sieve_size_t *address, signed int *id_code)
-{
-	if ( sieve_binary_read_code(sblock, address, id_code) ) 
-		return TRUE;
+	/* Read optional operand code */
+	if ( !sieve_binary_read_code(sblock, address, opt_code) ) 
+		return -1;
 	
-	*id_code = 0;
-
-	return FALSE;
+	/* Return 0 at end of list */
+	return ( *opt_code != 0 ? 1 : 0 );
 }
 
 /* 
@@ -418,32 +419,42 @@ bool sieve_opr_number_dump
 }
 
 bool sieve_opr_number_read_data
-(const struct sieve_runtime_env *renv, const struct sieve_operand *opr,
-	sieve_size_t *address, sieve_number_t *number_r)
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+	sieve_size_t *address, const char *field_name, sieve_number_t *number_r)
 {
 	const struct sieve_opr_number_interface *intf;
 		
-	if ( !sieve_operand_is_number(opr) ) 
-		return FALSE;	
+	if ( !sieve_operand_is_number(oprnd) ) {
+		sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+			"expected number operand but found %s", sieve_operand_name(oprnd));
+		return FALSE;
+	}
 		
-	intf = (const struct sieve_opr_number_interface *) opr->def->interface; 
+	intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; 
 	
 	if ( intf->read == NULL )
 		return FALSE;
 
-	return intf->read(renv, address, number_r);  
+	if ( !intf->read(renv, address, number_r) ) {
+		sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+			"invalid number operand");
+		return FALSE;	
+	}
+
+	return TRUE;
 }
 
 bool sieve_opr_number_read
 (const struct sieve_runtime_env *renv, sieve_size_t *address, 
-	sieve_number_t *number_r)
+	const char *field_name, sieve_number_t *number_r)
 {
 	struct sieve_operand operand;
 
-	if ( !sieve_operand_read(renv->sblock, address, &operand) )
+	if ( !sieve_operand_runtime_read(renv, address, field_name, &operand) )
 		return FALSE;
 		
-	return sieve_opr_number_read_data(renv, &operand, address, number_r);
+	return sieve_opr_number_read_data
+		(renv, &operand, address, field_name, number_r);
 }
 
 static bool opr_number_dump
@@ -536,47 +547,52 @@ bool sieve_opr_string_dump_ex
 } 
 
 bool sieve_opr_string_read_data
-(const struct sieve_runtime_env *renv, const struct sieve_operand *opr,
-	sieve_size_t *address, string_t **str_r)
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+	sieve_size_t *address, const char *field_name, string_t **str_r)
 {
 	const struct sieve_opr_string_interface *intf;
 	
-	if ( opr == NULL || opr->def == NULL || opr->def->class != &string_class ) 
+	if ( !sieve_operand_is_string(oprnd) ) {
+		sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+			"expected string operand but found %s", sieve_operand_name(oprnd));
 		return FALSE;
+	}
 		
-	intf = (const struct sieve_opr_string_interface *) opr->def->interface; 
+	intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; 
 	
-	if ( intf->read == NULL )
+	if ( intf->read == NULL ) {
+		sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+			"invalid string operand");
 		return FALSE;
+	}
 
-	return intf->read(renv, opr, address, str_r);  
+	return intf->read(renv, oprnd, address, str_r);  
 }
 
 bool sieve_opr_string_read
-(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str_r)
+(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+	const char *field_name, string_t **str_r)
 {
 	struct sieve_operand operand;
 
-	if ( !sieve_operand_read(renv->sblock, address, &operand) ) {
+	if ( !sieve_operand_runtime_read(renv, address, field_name, &operand) )
 		return FALSE;
-	}
 
-	return sieve_opr_string_read_data(renv, &operand, address, str_r);
+	return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r);
 }
 
 bool sieve_opr_string_read_ex
-(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str_r,
-	bool *literal_r)
+(const struct sieve_runtime_env *renv, sieve_size_t *address,
+	const char *field_name, string_t **str_r, bool *literal_r)
 {
 	struct sieve_operand operand;
 
-	if ( !sieve_operand_read(renv->sblock, address, &operand) ) {
+	if ( !sieve_operand_runtime_read(renv, address, field_name, &operand) )
 		return FALSE;
-	}
 
 	*literal_r = sieve_operand_is(&operand, string_operand);
 
-	return sieve_opr_string_read_data(renv, &operand, address, str_r);
+	return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r);
 }
 
 static void _dump_string
@@ -700,45 +716,57 @@ bool sieve_opr_stringlist_dump
 }
 
 struct sieve_coded_stringlist *sieve_opr_stringlist_read_data
-(const struct sieve_runtime_env *renv, const struct sieve_operand *opr,
-	sieve_size_t *address)
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+	sieve_size_t *address, const char *field_name)
 {
-	if ( opr == NULL || opr->def == NULL )
+	if ( oprnd == NULL || oprnd->def == NULL )
 		return NULL;
 		
-	if ( opr->def->class == &stringlist_class ) {
+	if ( oprnd->def->class == &stringlist_class ) {
 		const struct sieve_opr_stringlist_interface *intf = 
-			(const struct sieve_opr_stringlist_interface *) opr->def->interface;
+			(const struct sieve_opr_stringlist_interface *) oprnd->def->interface;
+		struct sieve_coded_stringlist *strlist;
 			
 		if ( intf->read == NULL ) 
 			return NULL;
 
-		return intf->read(renv, address);  
-	} else if ( opr->def->class == &string_class ) {
+		if ( (strlist=intf->read(renv, address)) == NULL ) {
+			sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+				"invalid stringlist operand");
+			return NULL;
+		}
+
+		return strlist;
+	} else if ( oprnd->def->class == &string_class ) {
 		/* Special case, accept single string as string list as well. */
 		const struct sieve_opr_string_interface *intf = 
-			(const struct sieve_opr_string_interface *) opr->def->interface;
+			(const struct sieve_opr_string_interface *) oprnd->def->interface;
 				
-		if ( intf->read == NULL || !intf->read(renv, opr, address, NULL) ) {
+		if ( intf->read == NULL || !intf->read(renv, oprnd, address, NULL) ) {
+			sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+				"invalid stringlist string operand");
 			return NULL;
 		}
 		
-		return sieve_coded_stringlist_create(renv, opr->address, 1, *address); 
+		return sieve_coded_stringlist_create(renv, oprnd->address, 1, *address); 
 	}	
-	
+
+	sieve_runtime_trace_operand_error(renv, oprnd, field_name,
+		"expected stringlist or string operand but found %s", 
+		sieve_operand_name(oprnd));
 	return NULL;
 }
 
 struct sieve_coded_stringlist *sieve_opr_stringlist_read
-(const struct sieve_runtime_env *renv, sieve_size_t *address)
+(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+	const char *field_name)
 {
 	struct sieve_operand operand;
 
-	if ( !sieve_operand_read(renv->sblock, address, &operand) ) {
+	if ( !sieve_operand_runtime_read(renv, address, field_name, &operand) )
 		return NULL;
-	}
 	
-	return sieve_opr_stringlist_read_data(renv, &operand, address);
+	return sieve_opr_stringlist_read_data(renv, &operand, address, field_name);
 }
 
 static bool opr_stringlist_dump
@@ -838,7 +866,7 @@ static bool opr_catenated_string_read
 	 */
 	if ( str == NULL ) {
 		for ( i = 0; i < (unsigned int) elements; i++ ) {		
-			if ( !sieve_opr_string_read(renv, address, NULL) ) 
+			if ( !sieve_opr_string_read(renv, address, NULL, NULL) ) 
 				return FALSE;
 		}
 	} else {
@@ -848,7 +876,7 @@ static bool opr_catenated_string_read
 		*str = t_str_new(128);
 		for ( i = 0; i < (unsigned int) elements; i++ ) {
 		
-			if ( !sieve_opr_string_read(renv, address, elm) ) 
+			if ( !sieve_opr_string_read(renv, address, NULL, elm) ) 
 				return FALSE;
 		
 			if ( elm != NULL ) {
@@ -1002,13 +1030,13 @@ bool sieve_operation_read
 static bool opc_jmp_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &denv->oprtn;
+	const struct sieve_operation *oprtn = denv->oprtn;
 	unsigned int pc = *address;
 	int offset;
 	
 	if ( sieve_binary_read_offset(denv->sblock, address, &offset) ) 
 		sieve_code_dumpf(denv, "%s %d [%08x]", 
-			sieve_operation_mnemonic(op), offset, pc + offset);
+			sieve_operation_mnemonic(oprtn), offset, pc + offset);
 	else
 		return FALSE;
 	
@@ -1020,7 +1048,7 @@ static bool opc_jmp_dump
 static int opc_jmp_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) 
 {
-	sieve_runtime_trace(renv, "JMP");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump");
 	
 	return sieve_interpreter_program_jump(renv->interp, TRUE);
 }	
@@ -1030,7 +1058,8 @@ static int opc_jmptrue_execute
 {	
 	bool result = sieve_interpreter_get_test_result(renv->interp);
 	
-	sieve_runtime_trace(renv, "JMPTRUE (%s)", result ? "true" : "false");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if true (%s)", 
+		result ? "true" : "false");
 	
 	return sieve_interpreter_program_jump(renv->interp, result);
 }
@@ -1040,7 +1069,8 @@ static int opc_jmpfalse_execute
 {	
 	bool result = sieve_interpreter_get_test_result(renv->interp);
 	
-	sieve_runtime_trace(renv, "JMPFALSE (%s)", result ? "true" : "false" );
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if false (%s)",
+		result ? "true" : "false" );
 	
 	return sieve_interpreter_program_jump(renv->interp, !result);
 }	
diff --git a/src/lib-sieve/sieve-code.h b/src/lib-sieve/sieve-code.h
index 47583d1415ed801357a31d6d505e3cf4582e6357..a350ecc5eb7118ec4e6bb699789f499aed20539b 100644
--- a/src/lib-sieve/sieve-code.h
+++ b/src/lib-sieve/sieve-code.h
@@ -10,6 +10,9 @@
 #include "array.h"
 
 #include "sieve-common.h"
+#include "sieve-runtime.h"
+#include "sieve-runtime-trace.h"
+#include "sieve-dump.h"
 
 /* 
  * Coded string list 
@@ -68,11 +71,52 @@ bool sieve_operand_read
 	(struct sieve_binary_block *sblock, sieve_size_t *address, 
 		struct sieve_operand *oprnd);
 
-bool sieve_operand_optional_present
-	(struct sieve_binary_block *sblock, sieve_size_t *address);
-bool sieve_operand_optional_read	
-	(struct sieve_binary_block *sblock, sieve_size_t *address, 
-		signed int *id_code);
+static inline bool sieve_operand_runtime_read
+(const struct sieve_runtime_env *renv, sieve_size_t *address,
+	const char *field_name, struct sieve_operand *operand)
+{
+	if ( !sieve_operand_read(renv->sblock, address, operand) ) {
+		sieve_runtime_trace_operand_error
+			(renv, operand, field_name, "invalid operand");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*
+ * Optional operands
+ */
+
+int sieve_opr_optional_next
+(struct sieve_binary_block *sblock, sieve_size_t *address, 
+	signed int *opt_code);
+
+static inline int sieve_opr_optional_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address, 
+	signed int *opt_code)
+{
+	sieve_size_t pc = *address;
+	int ret;
+
+	if ( (ret=sieve_opr_optional_next(denv->sblock, address, opt_code)) <= 0 )
+		return ret;
+
+	sieve_code_mark_specific(denv, pc);
+	return ret; 
+}
+
+static inline int sieve_opr_optional_read
+(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+	signed int *opt_code)
+{
+	int ret;
+
+	if ( (ret=sieve_opr_optional_next(renv->sblock, address, opt_code)) < 0 )
+		sieve_runtime_trace_error(renv, "invalid optional operand code");
+
+	return ret;
+}
 
 /*
  * Core operands
@@ -165,10 +209,10 @@ bool sieve_opr_number_dump
 		const char *field_name); 
 bool sieve_opr_number_read_data
 	(const struct sieve_runtime_env *renv, const struct sieve_operand *operand,
-		sieve_size_t *address, sieve_number_t *number_r);
+		sieve_size_t *address, const char *field_name, sieve_number_t *number_r);
 bool sieve_opr_number_read
 	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
-		sieve_number_t *number_r);
+		const char *field_name, sieve_number_t *number_r);
 
 static inline bool sieve_operand_is_number
 (const struct sieve_operand *operand)
@@ -192,12 +236,13 @@ bool sieve_opr_string_dump_ex
 		const char *field_name, bool *literal_r); 
 bool sieve_opr_string_read_data
 	(const struct sieve_runtime_env *renv, const struct sieve_operand *operand,
-		sieve_size_t *address, string_t **str_r);
+		sieve_size_t *address, const char *field_name, string_t **str_r);
 bool sieve_opr_string_read
-	(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str_r);
+	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+		const char *field_name, string_t **str_r);
 bool sieve_opr_string_read_ex
-	(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str_r,
-		bool *literal_r);
+	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
+		const char *field_name, string_t **str_r, bool *literal_r);
 
 static inline bool sieve_operand_is_string
 (const struct sieve_operand *operand)
@@ -223,9 +268,10 @@ bool sieve_opr_stringlist_dump
 		const char *field_name);
 struct sieve_coded_stringlist *sieve_opr_stringlist_read_data
 	(const struct sieve_runtime_env *renv, const struct sieve_operand *operand, 
-		sieve_size_t *address);
+		sieve_size_t *address, const char *field_name);
 struct sieve_coded_stringlist *sieve_opr_stringlist_read
-	(const struct sieve_runtime_env *renv, sieve_size_t *address);
+	(const struct sieve_runtime_env *renv, sieve_size_t *address,
+		const char *field_name);
 
 static inline bool sieve_operand_is_stringlist
 (const struct sieve_operand *operand)
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index a6f9963229acb804321127c8121c89ebf9b90e7f..a2edd81ab64ca7b8e292aa957f4c5252c18ddaa4 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -65,8 +65,10 @@ struct sieve_codegen_env;
 struct sieve_runtime_env;
 struct sieve_interpreter;
 
-/* sieve-binary-dumper.h */
+/* sieve-dump.h */
 struct sieve_dumptime_env;
+
+/* sieve-binary-dumper.h */
 struct sieve_binary_dumper;
 
 /* sieve-code-dumper.h */
diff --git a/src/lib-sieve/sieve-dump.h b/src/lib-sieve/sieve-dump.h
index 5e0a96f3cd41bd818bf079f36d707c2079d05d14..a5c20ca77c683577b659e3f0a5b3b5450d7ad033 100644
--- a/src/lib-sieve/sieve-dump.h
+++ b/src/lib-sieve/sieve-dump.h
@@ -5,26 +5,28 @@
 #define __SIEVE_DUMP_H
 
 #include "sieve-common.h"
-
-#include "sieve-code.h"
-#include "sieve-binary-dumper.h"
 #include "sieve-code-dumper.h"
+#include "sieve-binary-dumper.h"
 
 /*
  * Dumptime environment
  */
 
 struct sieve_dumptime_env {
+	/* Dumpers */
+	struct sieve_instance *svinst;
 	struct sieve_binary_dumper *dumper;
 	struct sieve_code_dumper *cdumper;
 
-	struct sieve_instance *svinst;
-
+	/* Binary */
 	struct sieve_binary *sbin;
 	struct sieve_binary_block *sblock;
 
-	struct sieve_operation oprtn;
+	/* Code position */
+	const struct sieve_operation *oprtn;
+	sieve_size_t offset;
 	
+	/* Output stream */
 	struct ostream *stream;
 };
 
diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index c0da74b3696501343aa402cbe23ec62af87d76ad..d301c266f8f3bf9f616bf9146127fb3ca31d83b2 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -57,14 +57,20 @@ struct sieve_interpreter {
 	bool test_result;         /* Result of previous test command */
 	
 	/* Runtime environment */
-	struct sieve_runtime_env runenv; 
+	struct sieve_runtime_env runenv;
 
+	/* Current operation */
+	struct sieve_operation oprtn; 
+
+	/* Location information */
 	struct sieve_binary_debug_reader *dreader;
+	unsigned int command_line;
 };
 
 static struct sieve_interpreter *_sieve_interpreter_create
 (struct sieve_binary *sbin, struct sieve_binary_block *sblock, 
-	struct sieve_error_handler *ehandler) 
+	struct sieve_script *script, const struct sieve_message_data *msgdata,
+	const struct sieve_script_env *senv, struct sieve_error_handler *ehandler) 
 {
 	unsigned int i, ext_count;
 	struct sieve_interpreter *interp;
@@ -72,6 +78,7 @@ static struct sieve_interpreter *_sieve_interpreter_create
 	struct sieve_instance *svinst;
 	const struct sieve_extension *const *ext_preloaded;
 	unsigned int debug_block_id;
+	sieve_size_t *address;
 	bool success = TRUE;
 	
 	pool = pool_alloconly_create("sieve_interpreter", 4096);	
@@ -81,17 +88,34 @@ static struct sieve_interpreter *_sieve_interpreter_create
 	interp->ehandler = ehandler;
 	sieve_error_handler_ref(ehandler);
 
-	interp->runenv.interp = interp;	
+	interp->runenv.interp = interp;
+	interp->runenv.oprtn = &interp->oprtn;
 	interp->runenv.sbin = sbin;
 	interp->runenv.sblock = sblock;
 	sieve_binary_ref(sbin);
 
 	svinst = sieve_binary_svinst(sbin);
 
-	interp->runenv.svinst = svinst;	
-	interp->runenv.script = sieve_binary_script(sbin); 
+	interp->runenv.svinst = svinst;
+	interp->runenv.msgdata = msgdata;
+	interp->runenv.scriptenv = senv;
+	interp->runenv.trace_stream = senv->trace_stream;
+	interp->runenv.trace_level = senv->trace_level;
+
+	if ( senv->exec_status == NULL ) 
+		interp->runenv.exec_status = p_new(interp->pool, struct sieve_exec_status, 1);
+	else
+		interp->runenv.exec_status = senv->exec_status;
+
+	if ( script == NULL )	
+		interp->runenv.script = sieve_binary_script(sbin);
+	else
+		interp->runenv.script = script;
 	
-	interp->pc = 0;
+	interp->runenv.pc = 0;
+	address = &(interp->runenv.pc);
+
+	sieve_runtime_trace_begin(&(interp->runenv));
 
 	p_array_init(&interp->extensions, pool, sieve_extensions_get_count(svinst));
 
@@ -102,11 +126,11 @@ static struct sieve_interpreter *_sieve_interpreter_create
 
 		if ( ext_def != NULL && ext_def->interpreter_load != NULL )
 			(void)ext_def->interpreter_load
-				(ext_preloaded[i], &interp->runenv, &interp->pc);		
+				(ext_preloaded[i], &interp->runenv, address);		
 	}
 
 	/* Load debug block */
-	if ( sieve_binary_read_unsigned(sblock, &interp->pc, &debug_block_id) ) {
+	if ( sieve_binary_read_unsigned(sblock, address, &debug_block_id) ) {
 		struct sieve_binary_block *debug_block =
 			sieve_binary_block_get(sbin, debug_block_id);
 
@@ -121,19 +145,19 @@ static struct sieve_interpreter *_sieve_interpreter_create
 
 	/* Load other extensions listed in code */
 	if ( success && 
-		sieve_binary_read_unsigned(sblock, &interp->pc, &ext_count) ) {
+		sieve_binary_read_unsigned(sblock, address, &ext_count) ) {
 
 		for ( i = 0; i < ext_count; i++ ) {
 			unsigned int code = 0;
 			const struct sieve_extension *ext;
 			
-			if ( !sieve_binary_read_extension(sblock, &interp->pc, &code, &ext) ) {
+			if ( !sieve_binary_read_extension(sblock, address, &code, &ext) ) {
 				success = FALSE;
 				break;
 			}
  
 			if ( ext->def != NULL && ext->def->interpreter_load != NULL && 
-				!ext->def->interpreter_load(ext, &interp->runenv, &interp->pc) ) {
+				!ext->def->interpreter_load(ext, &interp->runenv, address) ) {
 				success = FALSE;
 				break;
 			}
@@ -144,27 +168,31 @@ static struct sieve_interpreter *_sieve_interpreter_create
 	if ( !success ) {
 		sieve_interpreter_free(&interp);
 	} else {
-		interp->reset_vector = interp->pc;
+		interp->reset_vector = *address;
 	}
 	
 	return interp;
 }
 
 struct sieve_interpreter *sieve_interpreter_create
-(struct sieve_binary *sbin, struct sieve_error_handler *ehandler) 
+(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
+	const struct sieve_script_env *senv, struct sieve_error_handler *ehandler) 
 {
 	struct sieve_binary_block *sblock =
 		sieve_binary_block_get(sbin, SBIN_SYSBLOCK_MAIN_PROGRAM);
 
- 	return _sieve_interpreter_create(sbin, sblock, ehandler);
+ 	return _sieve_interpreter_create(sbin, sblock, NULL, msgdata, senv, ehandler);
 }
 
 struct sieve_interpreter *sieve_interpreter_create_for_block
-(struct sieve_binary_block *sblock, struct sieve_error_handler *ehandler) 
+(struct sieve_binary_block *sblock, struct sieve_script *script,
+	const struct sieve_message_data *msgdata, const struct sieve_script_env *senv, 
+	struct sieve_error_handler *ehandler) 
 {
 	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
 
- 	return _sieve_interpreter_create(sbin, sblock, ehandler);
+ 	return _sieve_interpreter_create
+		(sbin, sblock, script, msgdata, senv, ehandler);
 }
 
 void sieve_interpreter_free(struct sieve_interpreter **interp) 
@@ -172,6 +200,8 @@ void sieve_interpreter_free(struct sieve_interpreter **interp)
 	const struct sieve_interpreter_extension_reg *eregs;
 	unsigned int ext_count, i;
 
+	sieve_runtime_trace_end(&((*interp)->runenv));
+
 	/* Signal registered extensions that the interpreter is being destroyed */
 	eregs = array_get(&(*interp)->extensions, &ext_count);
 	for ( i = 0; i < ext_count; i++ ) {
@@ -231,12 +261,11 @@ void sieve_interpreter_set_result
 
 /* This is not particularly user friendly, so avoid using this
  */
-const char *sieve_runtime_location(const struct sieve_runtime_env *runenv)
+const char *sieve_runtime_location(const struct sieve_runtime_env *renv)
 {
-	const char *op = runenv->oprtn.def == NULL ?
-		"<<NOOP>>" : runenv->oprtn.def->mnemonic;
-	return t_strdup_printf("%s: #%08llx: %s", sieve_script_name(runenv->script),
-		(unsigned long long) runenv->oprtn.address, op);
+	const char *op = sieve_operation_mnemonic(renv->oprtn);
+	return t_strdup_printf("%s: #%08llx: %s", sieve_script_name(renv->script),
+		(unsigned long long) renv->oprtn->address, op);
 }
 
 void sieve_runtime_error
@@ -279,50 +308,39 @@ void sieve_runtime_log
 }
 
 /*
- * Runtime trace
+ * Source location
  */
 
 unsigned int sieve_runtime_get_source_location
 (const struct sieve_runtime_env *renv, sieve_size_t code_address)
 {
-	if ( renv->interp->dreader != NULL ) 
-		return sieve_binary_debug_read_line(renv->interp->dreader, code_address);
+	struct sieve_interpreter *interp = renv->interp;
 
-	return 0;
-}
+	if ( interp->dreader == NULL )
+		return 0;
 
-#ifdef SIEVE_RUNTIME_TRACE
-void _sieve_runtime_trace
-(const struct sieve_runtime_env *runenv, const char *fmt, ...)
-{	
-	string_t *outbuf = t_str_new(128);
-	va_list args;
-	
-	va_start(args, fmt);	
-	str_printfa(outbuf, "%08llx: ", (unsigned long long) runenv->oprtn.address); 
-	str_vprintfa(outbuf, fmt, args); 
-	str_append_c(outbuf, '\n');
-	va_end(args);
-	
-	o_stream_send(runenv->trace_stream, str_data(outbuf), str_len(outbuf));	
+	if ( interp->command_line == 0 ) {
+		interp->command_line = sieve_binary_debug_read_line
+			(interp->dreader, renv->oprtn->address);
+	}
+
+	return sieve_binary_debug_read_line(interp->dreader, code_address);
 }
 
-void _sieve_runtime_trace_error
-(const struct sieve_runtime_env *runenv, const char *fmt, ...)
+unsigned int sieve_runtime_get_command_location
+(const struct sieve_runtime_env *renv)
 {
-	string_t *outbuf = t_str_new(128);
-	va_list args;
+	struct sieve_interpreter *interp = renv->interp;
 
-	va_start(args, fmt);
-	str_printfa(outbuf, "%08llx: [[ERROR: %s: ", 
-		(unsigned long long) runenv->interp->pc, runenv->oprtn.def->mnemonic);
-	str_vprintfa(outbuf, fmt, args);
-    str_append(outbuf, "]]\n");
-	va_end(args);
+	if ( interp->dreader == NULL )
+		return 0;
+
+	if ( interp->command_line == 0 )
+		interp->command_line = sieve_binary_debug_read_line
+			(interp->dreader, renv->oprtn->address);
 
-	o_stream_send(runenv->trace_stream, str_data(outbuf), str_len(outbuf));
+	return interp->command_line;
 }
-#endif
 
 /* 
  * Extension support 
@@ -373,10 +391,9 @@ void *sieve_interpreter_extension_get_context
 
 void sieve_interpreter_reset(struct sieve_interpreter *interp) 
 {
-	interp->pc = interp->reset_vector;
+	interp->runenv.pc = interp->reset_vector;
 	interp->interrupted = FALSE;
 	interp->test_result = FALSE;
-	interp->runenv.msgdata = NULL;
 	interp->runenv.result = NULL;
 }
 
@@ -387,27 +404,32 @@ void sieve_interpreter_interrupt(struct sieve_interpreter *interp)
 
 sieve_size_t sieve_interpreter_program_counter(struct sieve_interpreter *interp)
 {
-	return interp->pc;
+	return interp->runenv.pc;
 }
 
 int sieve_interpreter_program_jump
 (struct sieve_interpreter *interp, bool jump)
 {
 	const struct sieve_runtime_env *renv = &interp->runenv;
-	sieve_size_t pc = interp->pc;
-	int offset;
+	sieve_size_t *address = &(interp->runenv.pc);
+	sieve_size_t jmp_start = *address;
+	int jmp_offset;
 	
-	if ( !sieve_binary_read_offset(renv->sblock, &(interp->pc), &offset) )
+	if ( !sieve_binary_read_offset(renv->sblock, address, &jmp_offset) )
 	{
 		sieve_runtime_trace_error(renv, "invalid jump offset"); 
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 
-	if ( pc + offset <= sieve_binary_block_get_size(renv->sblock) && 
-		pc + offset > 0 ) 
+	if ( jmp_start + jmp_offset <= sieve_binary_block_get_size(renv->sblock) && 
+		jmp_start + jmp_offset > 0 ) 
 	{	
-		if ( jump )
-			interp->pc = pc + offset;
+		if ( jump ) {
+			sieve_runtime_trace_here(renv, SIEVE_TRLVL_COMMANDS, "jump to #%08llx", 
+				(long long unsigned int) jmp_start + jmp_offset);
+
+			*address = jmp_start + jmp_offset;
+		}
 		
 		return SIEVE_EXEC_OK;
 	}
@@ -432,44 +454,6 @@ bool sieve_interpreter_get_test_result
 	return interp->test_result;
 }
 
-/* 
- * Operations and operands 
- */
-
-int sieve_interpreter_handle_optional_operands
-(const struct sieve_runtime_env *renv, sieve_size_t *address,
-	struct sieve_side_effects_list **list)
-{
-	signed int opt_code = -1;
-	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
-
-			if ( opt_code == SIEVE_OPT_SIDE_EFFECT ) {
-				struct sieve_side_effect seffect;
-			
-				if ( list == NULL )
-					return SIEVE_EXEC_BIN_CORRUPT;
-					
-				if ( !sieve_opr_side_effect_read(renv, address, &seffect) ) {
-					sieve_runtime_trace_error(renv, "invalid side effect operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-			
-				if ( *list == NULL ) 
-					*list = sieve_side_effects_list_create(renv->result);
-
-				sieve_side_effects_list_add(*list, &seffect);
-			}
-		}
-	}
-	return TRUE;
-}
- 
 /* 
  * Code execute 
  */
@@ -477,31 +461,40 @@ int sieve_interpreter_handle_optional_operands
 static int sieve_interpreter_operation_execute
 (struct sieve_interpreter *interp) 
 {
-	struct sieve_operation *oprtn = &(interp->runenv.oprtn);
+	struct sieve_operation *oprtn = &(interp->oprtn);
+	sieve_size_t *address = &(interp->runenv.pc);
 
-	if ( sieve_operation_read(interp->runenv.sblock, &(interp->pc), oprtn) ) {
+	/* Read the operation */
+	if ( sieve_operation_read(interp->runenv.sblock, address, oprtn) ) {
 		const struct sieve_operation_def *op = oprtn->def;
-
 		int result = SIEVE_EXEC_OK;
 
+		/* Reset cached command location */
+		interp->command_line = 0;
+
+		/* Execute the operation */
 		if ( op->execute != NULL ) { /* Noop ? */
 			T_BEGIN {
-				result = op->execute(&(interp->runenv), &(interp->pc));
+				result = op->execute(&(interp->runenv), address);
 			} T_END;
 		} else {
-			sieve_runtime_trace(&interp->runenv, "OP: %s (NOOP)", op->mnemonic);
+			sieve_runtime_trace
+				(&interp->runenv, SIEVE_TRLVL_COMMANDS, "OP: %s (NOOP)", 
+					sieve_operation_mnemonic(oprtn));
 		}
 
 		return result;
 	}
 	
-	sieve_runtime_trace(&interp->runenv, "Encountered invalid operation");	
+	/* Binary corrupt */
+	sieve_runtime_trace_error(&interp->runenv, "Encountered invalid operation");	
 	return SIEVE_EXEC_BIN_CORRUPT;
 }		
 
 int sieve_interpreter_continue
 (struct sieve_interpreter *interp, bool *interrupted) 
 {
+	sieve_size_t *address = &(interp->runenv.pc);
 	int ret = SIEVE_EXEC_OK;
 	
 	sieve_result_ref(interp->runenv.result);
@@ -511,12 +504,13 @@ int sieve_interpreter_continue
 		*interrupted = FALSE;
 	
 	while ( ret == SIEVE_EXEC_OK && !interp->interrupted && 
-		interp->pc < sieve_binary_block_get_size(interp->runenv.sblock) ) {
+		*address < sieve_binary_block_get_size(interp->runenv.sblock) ) {
 		
 		ret = sieve_interpreter_operation_execute(interp);
 
 		if ( ret != SIEVE_EXEC_OK ) {
-			sieve_runtime_trace(&interp->runenv, "[[EXECUTION ABORTED]]");
+			sieve_runtime_trace(&interp->runenv, SIEVE_TRLVL_MINIMUM, 
+				"[[EXECUTION ABORTED]]");
 		}
 	}
 	
@@ -528,22 +522,13 @@ int sieve_interpreter_continue
 }
 
 int sieve_interpreter_start
-(struct sieve_interpreter *interp, const struct sieve_message_data *msgdata,
-	const struct sieve_script_env *senv, struct sieve_result *result, bool *interrupted) 
+(struct sieve_interpreter *interp, struct sieve_result *result, bool *interrupted) 
 {
 	const struct sieve_interpreter_extension_reg *eregs;
 	unsigned int ext_count, i;
 	
-	interp->runenv.msgdata = msgdata;
 	interp->runenv.result = result;
 	interp->runenv.msgctx = sieve_result_get_message_context(result);		
-	interp->runenv.scriptenv = senv;
-	interp->runenv.trace_stream = senv->trace_stream;
-
-	if ( senv->exec_status == NULL ) 
-		interp->runenv.exec_status = p_new(interp->pool, struct sieve_exec_status, 1);
-	else
-		interp->runenv.exec_status = senv->exec_status;
 	
 	/* Signal registered extensions that the interpreter is being run */
 	eregs = array_get(&interp->extensions, &ext_count);
@@ -556,15 +541,14 @@ int sieve_interpreter_start
 }
 
 int sieve_interpreter_run
-(struct sieve_interpreter *interp, const struct sieve_message_data *msgdata,
-	const struct sieve_script_env *senv, struct sieve_result *result)
+(struct sieve_interpreter *interp, struct sieve_result *result)
 {
 	int ret = 0;
 	
 	sieve_interpreter_reset(interp);
 	sieve_result_ref(result);
 	
-	ret = sieve_interpreter_start(interp, msgdata, senv, result, NULL);
+	ret = sieve_interpreter_start(interp, result, NULL);
 	
 	sieve_result_unref(&result);
 	
diff --git a/src/lib-sieve/sieve-interpreter.h b/src/lib-sieve/sieve-interpreter.h
index ba08f8f63aca096a5f59ff078097f7df7b17f6fc..15d3f9ad4ca6d381c20c42030e3a425b5be5d6b3 100644
--- a/src/lib-sieve/sieve-interpreter.h
+++ b/src/lib-sieve/sieve-interpreter.h
@@ -10,46 +10,19 @@
 #include "mail-storage.h"
 
 #include "sieve-common.h"
-#include "sieve-code.h"
-
-/*
- * Forward declarations
- */
- 
-struct sieve_interpreter;
-
-/*
- * Runtime environment
- */
-
-struct sieve_runtime_env {
-	struct sieve_interpreter *interp;
-	struct sieve_instance *svinst;
-
-	struct sieve_binary *sbin;
-	struct sieve_binary_block *sblock;
-	struct sieve_operation oprtn; 
-
-	struct sieve_script *script;
-	const struct sieve_script_env *scriptenv;
-	
-	const struct sieve_message_data *msgdata;
-	struct sieve_message_context *msgctx;
-
-	struct sieve_result *result;
-	
-	struct sieve_exec_status *exec_status;
-	struct ostream *trace_stream;
-};
+#include "sieve-runtime.h"
 
 /* 
  * Interpreter 
  */
 
 struct sieve_interpreter *sieve_interpreter_create
-	(struct sieve_binary *sbin, struct sieve_error_handler *ehandler);
+	(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
+		const struct sieve_script_env *senv, struct sieve_error_handler *ehandler);
 struct sieve_interpreter *sieve_interpreter_create_for_block
-	(struct sieve_binary_block *sblock, struct sieve_error_handler *ehandler);
+	(struct sieve_binary_block *sblock, struct sieve_script *script,
+		const struct sieve_message_data *msgdata, 
+		const struct sieve_script_env *senv, struct sieve_error_handler *ehandler);
 void sieve_interpreter_free(struct sieve_interpreter **interp);
 
 /*
@@ -94,12 +67,18 @@ void sieve_interpreter_set_test_result
 bool sieve_interpreter_get_test_result
 	(struct sieve_interpreter *interp);
 	
-/* 
- * Error handling 
+/*
+ * Source location
  */
 
 unsigned int sieve_runtime_get_source_location
 	(const struct sieve_runtime_env *renv, sieve_size_t code_address);
+unsigned int sieve_runtime_get_command_location
+	(const struct sieve_runtime_env *renv);
+
+/* 
+ * Error handling 
+ */
 
 /* This is not particularly user-friendly, so avoid using this.. */
 const char *sieve_runtime_location(const struct sieve_runtime_env *runenv);
@@ -114,33 +93,6 @@ void sieve_runtime_log
 	(const struct sieve_runtime_env *runenv, const char *location, 
 		const char *fmt, ...) ATTR_FORMAT(3, 4);
 
-/* 
- * Runtime Trace 
- */
-
-#ifdef SIEVE_RUNTIME_TRACE
-		
-void _sieve_runtime_trace
-	(const struct sieve_runtime_env *runenv, const char *fmt, ...)
-		ATTR_FORMAT(2, 3);
-void _sieve_runtime_trace_error
-	(const struct sieve_runtime_env *runenv, const char *fmt, ...)
-		ATTR_FORMAT(2, 3);
-		
-# define sieve_runtime_trace(runenv, ...) STMT_START { \
-		if ( (runenv)->trace_stream != NULL ) \
-			_sieve_runtime_trace((runenv), __VA_ARGS__); \
-	} STMT_END
-# define sieve_runtime_trace_error(runenv, ...) STMT_START { \
-		if ( (runenv)->trace_stream != NULL ) \
-			_sieve_runtime_trace_error((runenv), __VA_ARGS__); \
-		} STMT_END	
-
-#else
-# define sieve_runtime_trace(runenv, ...)
-# define sieve_runtime_trace_error(runenv, ...)
-#endif
-
 /* 
  * Extension support 
  */
@@ -180,11 +132,9 @@ int sieve_interpreter_handle_optional_operands
 int sieve_interpreter_continue
 	(struct sieve_interpreter *interp, bool *interrupted);
 int sieve_interpreter_start
-	(struct sieve_interpreter *interp, const struct sieve_message_data *msgdata,
-		const struct sieve_script_env *senv, struct sieve_result *result, 
+	(struct sieve_interpreter *interp, struct sieve_result *result, 
 		bool *interrupted);
 int sieve_interpreter_run
-	(struct sieve_interpreter *interp, const struct sieve_message_data *msgdata,
-		const struct sieve_script_env *senv, struct sieve_result *result);
+	(struct sieve_interpreter *interp, struct sieve_result *result);
 
 #endif /* __SIEVE_INTERPRETER_H */
diff --git a/src/lib-sieve/sieve-match.c b/src/lib-sieve/sieve-match.c
index 88aa4e5175e26bfea98bb7d4b6fe3460bbb0fd97..7797fb192931d01c8ccf9d4385017059d8429ec8 100644
--- a/src/lib-sieve/sieve-match.c
+++ b/src/lib-sieve/sieve-match.c
@@ -136,69 +136,56 @@ int sieve_match_end(struct sieve_match_context **mctx)
  * Reading match operands
  */
  
-bool sieve_match_dump_optional_operands
+int sieve_match_opr_optional_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address, int *opt_code)
 {
-	if ( *opt_code != SIEVE_MATCH_OPT_END || 
-		sieve_operand_optional_present(denv->sblock, address) ) {
-		do {
-			if ( !sieve_operand_optional_read(denv->sblock, address, opt_code) ) 
-				return FALSE;
-
-			switch ( *opt_code ) {
-			case SIEVE_MATCH_OPT_END:
-				break;
-			case SIEVE_MATCH_OPT_COMPARATOR:
-				if ( !sieve_opr_comparator_dump(denv, address) )
-					return FALSE;
-				break;
-			case SIEVE_MATCH_OPT_MATCH_TYPE:
-				if ( !sieve_opr_match_type_dump(denv, address) )
-					return FALSE;
-				break;
-			default: 
-				return TRUE;
-			}
- 		} while ( *opt_code != SIEVE_MATCH_OPT_END );
+	bool opok = TRUE;
+
+	while ( opok ) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 )
+			return ret;
+
+		switch ( *opt_code ) {
+		case SIEVE_MATCH_OPT_COMPARATOR:
+			opok = sieve_opr_comparator_dump(denv, address);
+			break;
+		case SIEVE_MATCH_OPT_MATCH_TYPE:
+			opok = sieve_opr_match_type_dump(denv, address);
+			break;
+		default:
+			return 1;
+		}
 	}
-	
-	return TRUE;
+
+	return -1;
 }
 
-int sieve_match_read_optional_operands
+int sieve_match_opr_optional_read
 (const struct sieve_runtime_env *renv, sieve_size_t *address, int *opt_code,
 	struct sieve_comparator *cmp, struct sieve_match_type *mcht)
-{	 
-	/* Handle any optional arguments */
-	if ( *opt_code != SIEVE_MATCH_OPT_END || 
-		sieve_operand_optional_present(renv->sblock, address) ) {
-		do {
-			if ( !sieve_operand_optional_read(renv->sblock, address, opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
-
-			switch ( *opt_code ) {
-			case SIEVE_MATCH_OPT_END: 
-				break;
-			case SIEVE_MATCH_OPT_COMPARATOR:
-				if ( !sieve_opr_comparator_read(renv, address, cmp) ) {
-					sieve_runtime_trace_error(renv, "invalid comparator operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			case SIEVE_MATCH_OPT_MATCH_TYPE:
-				if ( !sieve_opr_match_type_read(renv, address, mcht) ) {
-					sieve_runtime_trace_error(renv, "invalid match type operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
-			default:
-				return SIEVE_EXEC_OK;
-			}
-		} while ( *opt_code != SIEVE_MATCH_OPT_END );
+{
+	bool opok = TRUE;
+
+	while ( opok ) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, opt_code)) <= 0 )
+			return ret;
+
+		switch ( *opt_code ) {
+		case SIEVE_MATCH_OPT_COMPARATOR:
+			opok = sieve_opr_comparator_read(renv, address, cmp);
+			break;
+		case SIEVE_MATCH_OPT_MATCH_TYPE:
+			opok = sieve_opr_match_type_read(renv, address, mcht);
+			break;
+		default:
+			return 1;
+		}
 	}
-	
-	return SIEVE_EXEC_OK;
+
+	return -1;
 }
 
diff --git a/src/lib-sieve/sieve-match.h b/src/lib-sieve/sieve-match.h
index e76931804c09b432995e7f5caa27d19baf3dbd8e..89d13c6aa1cef1d65900ac3cdf8cf7803a0c675b 100644
--- a/src/lib-sieve/sieve-match.h
+++ b/src/lib-sieve/sieve-match.h
@@ -53,10 +53,10 @@ enum sieve_match_opt_operand {
 	SIEVE_MATCH_OPT_LAST
 };
 
-bool sieve_match_dump_optional_operands
+int sieve_match_opr_optional_dump
 	(const struct sieve_dumptime_env *denv, sieve_size_t *addres, int *opt_code);
 
-int sieve_match_read_optional_operands
+int sieve_match_opr_optional_read
 	(const struct sieve_runtime_env *renv, sieve_size_t *address, int *opt_code,
 		struct sieve_comparator *cmp, struct sieve_match_type *mcht);
 
diff --git a/src/lib-sieve/sieve-runtime-trace.c b/src/lib-sieve/sieve-runtime-trace.c
new file mode 100644
index 0000000000000000000000000000000000000000..b570fa01782390efb4e0f6200bc11054d14c0470
--- /dev/null
+++ b/src/lib-sieve/sieve-runtime-trace.c
@@ -0,0 +1,139 @@
+#include "lib.h"
+#include "str.h"
+#include "ostream.h"
+
+#include "sieve-common.h"
+#include "sieve-script.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-interpreter.h"
+#include "sieve-runtime.h"
+#include "sieve-runtime-trace.h"
+
+static inline string_t *_trace_line_new
+(sieve_size_t address, unsigned int cmd_line)
+{
+	string_t *trline;
+	
+	trline = t_str_new(128);
+	str_printfa(trline, "%08llx: ", (unsigned long long) address);
+	if ( cmd_line > 0 )	
+		str_printfa(trline, "%4d: ", cmd_line); 
+	else
+		str_append(trline, "      "); 
+
+	return trline;
+}
+
+static inline void _trace_line_print
+(string_t *trline, const struct sieve_runtime_env *renv)
+{
+	str_append_c(trline, '\n');
+
+	o_stream_send(renv->trace_stream, str_data(trline), str_len(trline));
+}
+
+static inline void _trace_line_print_empty
+(const struct sieve_runtime_env *renv)
+{
+	o_stream_send_str(renv->trace_stream, "\n");
+}
+
+/*
+ * Trace errors
+ */
+
+void _sieve_runtime_trace_error
+(const struct sieve_runtime_env *renv, const char *fmt, va_list args)
+{
+	string_t *trline = _trace_line_new(renv->pc, 0);
+
+	str_printfa(trline, "%s: #ERROR#: ", sieve_operation_mnemonic(renv->oprtn));
+	str_vprintfa(trline, fmt, args);
+
+	_trace_line_print(trline, renv);
+}
+
+void _sieve_runtime_trace_operand_error
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+	const char *field_name, const char *fmt, va_list args)
+{
+	string_t *trline = _trace_line_new
+		(oprnd->address, sieve_runtime_get_source_location(renv, oprnd->address));
+
+	str_printfa(trline, "%s: #ERROR#: ", sieve_operation_mnemonic(renv->oprtn));
+
+	if ( field_name != NULL )
+		str_printfa(trline, "%s: ", field_name);
+
+	str_vprintfa(trline, fmt, args);
+
+	_trace_line_print(trline, renv);
+}
+
+/*
+ * Trace info
+ */
+
+static inline void _sieve_runtime_trace_vprintf
+(const struct sieve_runtime_env *renv, sieve_size_t address,
+	unsigned int cmd_line, const char *fmt, va_list args)
+{	
+	string_t *trline = _trace_line_new(address, cmd_line);
+		
+	str_vprintfa(trline, fmt, args); 
+	
+	_trace_line_print(trline, renv);
+}
+
+static inline void _sieve_runtime_trace_printf
+(const struct sieve_runtime_env *renv, sieve_size_t address,
+	unsigned int cmd_line, const char *fmt, ...)
+{
+	va_list args;
+	
+	va_start(args, fmt);
+	_sieve_runtime_trace_vprintf(renv, address, cmd_line, fmt, args); 
+	va_end(args);
+}
+
+void _sieve_runtime_trace
+(const struct sieve_runtime_env *renv, const char *fmt, va_list args)
+{	
+	_sieve_runtime_trace_vprintf
+		(renv, renv->oprtn->address, sieve_runtime_get_command_location(renv),
+			fmt, args); 
+}
+
+void _sieve_runtime_trace_address
+(const struct sieve_runtime_env *renv, sieve_size_t address, 
+	const char *fmt, va_list args)
+{	
+	_sieve_runtime_trace_vprintf
+		(renv, address, sieve_runtime_get_source_location(renv, address), fmt,
+			args); 
+}
+
+/* 
+ * Trace boundaries
+ */
+
+void _sieve_runtime_trace_begin(const struct sieve_runtime_env *renv)
+{
+	const char *script_name = ( renv->script != NULL ? 
+		sieve_script_name(renv->script) : sieve_binary_path(renv->sbin) );
+
+	_trace_line_print_empty(renv);
+	_sieve_runtime_trace_printf(renv, renv->pc, 0, 
+		"## Started executing script '%s'", script_name);
+}
+
+void _sieve_runtime_trace_end(const struct sieve_runtime_env *renv)
+{
+	const char *script_name = ( renv->script != NULL ? 
+		sieve_script_name(renv->script) : sieve_binary_path(renv->sbin) );
+
+	_sieve_runtime_trace_printf(renv, renv->pc, 0, 
+		"## Finished executing script '%s'", script_name);
+	_trace_line_print_empty(renv);
+}
diff --git a/src/lib-sieve/sieve-runtime-trace.h b/src/lib-sieve/sieve-runtime-trace.h
new file mode 100644
index 0000000000000000000000000000000000000000..6210b4fb209ec19e91b22e519c3ed72e64dc8dca
--- /dev/null
+++ b/src/lib-sieve/sieve-runtime-trace.h
@@ -0,0 +1,132 @@
+#ifndef __SIEVE_TRACE_H
+#define __SIEVE_TRACE_H
+
+#include "sieve-common.h"
+#include "sieve-runtime.h"
+
+/*
+ * Runtime trace
+ */
+
+/* Trace errors */
+
+void _sieve_runtime_trace_error
+	(const struct sieve_runtime_env *renv, const char *fmt, va_list args);	
+
+void _sieve_runtime_trace_operand_error
+	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+		const char *field_name, const char *fmt, va_list args);
+
+static inline void sieve_runtime_trace_error
+	(const struct sieve_runtime_env *renv, const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
+
+static inline void sieve_runtime_trace_operand_error
+	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, 
+		const char *field_name, const char *fmt, ...) ATTR_FORMAT(4, 5);
+
+static inline void sieve_runtime_trace_error
+	(const struct sieve_runtime_env *renv, const char *fmt, ...)
+{
+	va_list args;
+	
+	va_start(args, fmt);
+	if ( renv->trace_stream != NULL )
+		_sieve_runtime_trace_error(renv, fmt, args);	
+	va_end(args);
+}
+
+static inline void sieve_runtime_trace_operand_error
+	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, 
+		const char *field_name, const char *fmt, ...)
+{
+	va_list args;
+	
+	va_start(args, fmt);
+	if ( renv->trace_stream != NULL )
+		_sieve_runtime_trace_operand_error(renv, oprnd, field_name, fmt, args);
+	va_end(args);
+}
+
+/* Trace info */
+
+void _sieve_runtime_trace
+	(const struct sieve_runtime_env *renv, const char *fmt, va_list args);
+
+static inline void sieve_runtime_trace
+	(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
+		const char *fmt, ...) ATTR_FORMAT(3, 4);
+
+static inline void sieve_runtime_trace
+(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
+	const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if ( renv->trace_stream != NULL && trace_level <= renv->trace_level ) {
+		_sieve_runtime_trace(renv, fmt, args);
+	}
+
+	va_end(args);
+}
+
+void _sieve_runtime_trace_address
+	(const struct sieve_runtime_env *renv, sieve_size_t address, 
+		const char *fmt, va_list args);
+
+static inline void sieve_runtime_trace_address
+	(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
+		sieve_size_t address, const char *fmt, ...) ATTR_FORMAT(4, 5);
+
+static inline void sieve_runtime_trace_address
+(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
+	sieve_size_t address, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if ( renv->trace_stream != NULL && trace_level <= renv->trace_level ) {
+		_sieve_runtime_trace_address(renv, address, fmt, args);
+	}
+
+	va_end(args);
+}
+
+static inline void sieve_runtime_trace_here
+(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
+	const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if ( renv->trace_stream != NULL && trace_level <= renv->trace_level ) {
+		_sieve_runtime_trace_address(renv, renv->pc, fmt, args);
+	}
+
+	va_end(args);
+}
+
+/* Trace boundaries */
+
+void _sieve_runtime_trace_begin(const struct sieve_runtime_env *renv);
+void _sieve_runtime_trace_end(const struct sieve_runtime_env *renv);
+
+static inline void sieve_runtime_trace_begin
+(const struct sieve_runtime_env *renv)
+{
+	if ( renv->trace_stream != NULL )
+		_sieve_runtime_trace_begin(renv);
+}
+
+static inline void sieve_runtime_trace_end
+(const struct sieve_runtime_env *renv)
+{
+	if ( renv->trace_stream != NULL )
+		_sieve_runtime_trace_end(renv);
+}
+
+#endif /* __SIEVE_TRACE_H */
diff --git a/src/lib-sieve/sieve-runtime.h b/src/lib-sieve/sieve-runtime.h
new file mode 100644
index 0000000000000000000000000000000000000000..ab304dd7b1fabea4fa780f83e0fe59b28b246e6f
--- /dev/null
+++ b/src/lib-sieve/sieve-runtime.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#ifndef __SIEVE_RUNTIME_H
+#define __SIEVE_RUNTIME_H
+
+#include "sieve-common.h"
+
+/*
+ * Runtime environment
+ */
+
+struct sieve_runtime_env {
+	/* Interpreter */
+	struct sieve_instance *svinst;
+	struct sieve_interpreter *interp;
+
+	/* Executing script */
+	struct sieve_script *script;
+	const struct sieve_script_env *scriptenv;
+	struct sieve_exec_status *exec_status;
+
+	/* Executing binary */
+	struct sieve_binary *sbin;
+	struct sieve_binary_block *sblock;
+	
+	/* Current code */
+	sieve_size_t pc;
+	const struct sieve_operation *oprtn; 	
+	
+	/* Tested message */
+	const struct sieve_message_data *msgdata;
+	struct sieve_message_context *msgctx;
+
+	/* Filter result */
+	struct sieve_result *result;
+
+	/* Runtime tracing */
+	struct ostream *trace_stream;
+	sieve_trace_level_t trace_level;
+};
+
+#endif /* __SIEVE_RUNTIME_H */
diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h
index 6eb44d905ba8bbb8d7fb031cdb5bec8260de0e2c..5dc3de9c5486c363d56c3991c8dc3d79d1263d70 100644
--- a/src/lib-sieve/sieve-types.h
+++ b/src/lib-sieve/sieve-types.h
@@ -48,6 +48,19 @@ struct sieve_message_data {
 	const char *id;
 };
 
+/*
+ * Runtime trace level
+ */
+
+typedef enum {
+	SIEVE_TRLVL_MINIMUM,
+	SIEVE_TRLVL_ACTIONS,
+	SIEVE_TRLVL_COMMANDS,
+	SIEVE_TRLVL_TESTS,
+	SIEVE_TRLVL_MATCH,
+	SIEVE_TRLVL_DEBUG
+} sieve_trace_level_t;
+
 /* 
  * Script environment
  *
@@ -91,8 +104,9 @@ struct sieve_script_env {
 	/* Execution status record */	
 	struct sieve_exec_status *exec_status;
 		
-	/* Trace stream */
+	/* Runtime trace*/
 	struct ostream *trace_stream;
+	sieve_trace_level_t trace_level;
 };
 
 #define SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) \
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 2dc7198afd0b37ced616d4c910048654a9aacddb..9328ab15ae0973c79304e0d0cf829cca6b611ed0 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -237,7 +237,8 @@ static int sieve_run
 	int ret = 0;
 
 	/* Create the interpreter */
-	if ( (interp=sieve_interpreter_create(sbin, ehandler)) == NULL )
+	if ( (interp=sieve_interpreter_create(sbin, msgdata, senv, ehandler)) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Reset execution status */
@@ -253,7 +254,7 @@ static int sieve_run
 	}
 							
 	/* Run the interpreter */
-	ret = sieve_interpreter_run(interp, msgdata, senv, *result);
+	ret = sieve_interpreter_run(interp, *result);
 	
 	/* Free the interpreter */
 	sieve_interpreter_free(&interp);
diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c
index 44e52cb493f7aaa9f48fab063d9eb623af5d7d07..daede9ae0d1b10ada6f4b03106c10ce23d349d74 100644
--- a/src/lib-sieve/tst-address.c
+++ b/src/lib-sieve/tst-address.c
@@ -208,7 +208,7 @@ static bool tst_address_operation_dump
 	sieve_code_descend(denv);
 	
 	/* Handle any optional arguments */
-	if ( !sieve_addrmatch_default_dump_optionals(denv, address) )
+	if ( sieve_addrmatch_opr_optional_dump(denv, address, NULL) != 0 )
 		return FALSE;
 
 	return
@@ -238,23 +238,21 @@ static int tst_address_operation_execute
 	int ret;
 	
 	/* Read optional operands */
-	if ( (ret=sieve_addrmatch_default_get_optionals
-		(renv, address, &addrp, &mcht, &cmp)) <= 0 ) 
-		return ret;
+	if ( (ret=sieve_addrmatch_opr_optional_read
+		(renv, address, NULL, &addrp, &mcht, &cmp)) < 0 ) 
+		return SIEVE_EXEC_BIN_CORRUPT;
 		
 	/* Read header-list */
-	if ( (hdr_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid header-list operand");
+	if ( (hdr_list=sieve_opr_stringlist_read(renv, address, "header-list"))
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list"))
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
-	sieve_runtime_trace(renv, "ADDRESS test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "address test");
 
 	/* Initialize match context */
 	mctx = sieve_match_begin(renv->interp, &mcht, &cmp, NULL, key_list);
diff --git a/src/lib-sieve/tst-exists.c b/src/lib-sieve/tst-exists.c
index e70b064a1b8d97c70705fea37036971c978c3cc0..ee3851ac685f57bc2754499944373f273df96fee 100644
--- a/src/lib-sieve/tst-exists.c
+++ b/src/lib-sieve/tst-exists.c
@@ -89,7 +89,7 @@ static bool tst_exists_generate
 static bool tst_exists_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-    sieve_code_dumpf(denv, "EXISTS");
+	sieve_code_dumpf(denv, "EXISTS");
 	sieve_code_descend(denv);
 
 	return sieve_opr_stringlist_dump(denv, address, "header names");
@@ -107,13 +107,20 @@ static int tst_exists_operation_execute
 	string_t *hdr_item;
 	bool matched;
 	
+	/*
+	 * Read operands
+	 */
+
 	/* Read header-list */
-	if ( (hdr_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid header-list operand");
+	if ( (hdr_list=sieve_opr_stringlist_read(renv, address, "header-list"))
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
-	sieve_runtime_trace(renv, "EXISTS test");
+	/*
+	 * Perfrom test
+	 */
+
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "exists test");
 		
 	/* Iterate through all requested headers to match (must find all specified) */
 	hdr_item = NULL;
diff --git a/src/lib-sieve/tst-header.c b/src/lib-sieve/tst-header.c
index 872b3e5daae5d5cacb06ff239ce31d64305a6872..4e25b3d8bfab9aa42ac0ffc367529cbefe734a5c 100644
--- a/src/lib-sieve/tst-header.c
+++ b/src/lib-sieve/tst-header.c
@@ -135,11 +135,8 @@ static bool tst_header_operation_dump
 	sieve_code_dumpf(denv, "HEADER");
 	sieve_code_descend(denv);
 
-	/* Handle any optional arguments */
-	if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
-		return FALSE;
-
-	if ( opt_code != SIEVE_MATCH_OPT_END )
+	/* Optional operands */
+	if ( sieve_match_opr_optional_dump(denv, address, &opt_code) != 0 )
 		return FALSE;
 	
 	return
@@ -181,30 +178,36 @@ static int tst_header_operation_execute
 	bool matched;
 	int ret;
 	
+	/* 
+	 * Read operands
+	 */
+
 	/* Handle match-type and comparator operands */
-	if ( (ret=sieve_match_read_optional_operands
-		(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
-		return ret;
-	
+	if ( (ret=sieve_match_opr_optional_read
+		(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+		return SIEVE_EXEC_BIN_CORRUPT;
+
 	/* Check whether we neatly finished the list of optional operands*/
-	if ( opt_code != SIEVE_MATCH_OPT_END) {
+	if ( ret > 0 ) {
 		sieve_runtime_trace_error(renv, "invalid optional operand");
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 		
 	/* Read header-list */
-	if ( (hdr_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid header-list operand");
+	if ( (hdr_list=sieve_opr_stringlist_read(renv, address, "header-list")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list"))
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
-	sieve_runtime_trace(renv, "HEADER test");
+	/*
+	 * Perform test
+	 */
+
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "header test");
 
 	/* Initialize match */
 	mctx = sieve_match_begin(renv->interp, &mcht, &cmp, NULL, key_list); 	
diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c
index 437a224f1608651df34536e101cd3756a187d32b..d5fd31a563b61cffd0b93ab6d82e7c10cff25756 100644
--- a/src/lib-sieve/tst-size.c
+++ b/src/lib-sieve/tst-size.c
@@ -218,8 +218,7 @@ bool tst_size_generate
 static bool tst_size_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &denv->oprtn;
-	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
+	sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn));
 	sieve_code_descend(denv);
 	
 	return 
@@ -246,16 +245,19 @@ static inline bool tst_size_get
 static int tst_size_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &renv->oprtn;
 	sieve_number_t mail_size, limit;
 		
+	/*
+	 * Read operands
+	 */
+
 	/* Read size limit */
-	if ( !sieve_opr_number_read(renv, address, &limit) ) {
-		sieve_runtime_trace_error(renv, "invalid limit operand");
+	if ( !sieve_opr_number_read(renv, address, "limit", &limit) )
 		return SIEVE_EXEC_BIN_CORRUPT;	
-	}
 
-	sieve_runtime_trace(renv, "%s test", sieve_operation_mnemonic(op));
+	/*
+	 * Perform test
+	 */
 	
 	/* Get the size of the message */
 	if ( !tst_size_get(renv, &mail_size) ) {
@@ -265,10 +267,15 @@ static int tst_size_operation_execute
 	}
 	
 	/* Perform the test */
-	if ( sieve_operation_is(op, tst_size_over_operation) )
+	if ( sieve_operation_is(renv->oprtn, tst_size_over_operation) ) {
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "size :over test");
+
 		sieve_interpreter_set_test_result(renv->interp, (mail_size > limit));
-	else
+	} else {
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "size :under test");
+
 		sieve_interpreter_set_test_result(renv->interp, (mail_size < limit));
+	}
 
 	return SIEVE_EXEC_OK;
 }
diff --git a/src/sieve-tools/debug/cmd-debug-print.c b/src/sieve-tools/debug/cmd-debug-print.c
index c6e3cfda5695ce02329de2dbe920105fc4328d21..2fb27867ef5a18e7803fcfa24a72695764f9b13c 100644
--- a/src/sieve-tools/debug/cmd-debug-print.c
+++ b/src/sieve-tools/debug/cmd-debug-print.c
@@ -111,16 +111,14 @@ static int cmd_debug_print_operation_execute
 	
 	/* Read message */
 
-	if ( sieve_opr_string_read(renv, address, &message) < 0 ) {
-		sieve_runtime_trace_error(renv, "invalid message operand");
+	if ( sieve_opr_string_read(renv, address, "message", &message) < 0 )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "DEBUG_PRINT");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "DEBUG_PRINT");
 	
 	/* FIXME: give this proper source location */
 	sieve_runtime_log(renv, "DEBUG", "%s", str_c(message));
diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c
index d8feb1877f5c56cfaebb548bded6a46154ea60f2..c459b69ace1bed131b2f757054f2280f570b5066 100644
--- a/src/sieve-tools/sieve-test.c
+++ b/src/sieve-tools/sieve-test.c
@@ -337,6 +337,7 @@ int main(int argc, char **argv)
 		scriptenv.duplicate_mark = duplicate_mark;
 		scriptenv.duplicate_check = duplicate_check;
 		scriptenv.trace_stream = ( trace ? teststream : NULL );
+		scriptenv.trace_level = SIEVE_TRLVL_TESTS;
 		scriptenv.exec_status = &estatus;
 	
 		/* Run the test */
diff --git a/src/testsuite/cmd-test-binary.c b/src/testsuite/cmd-test-binary.c
index c7361a5050593df73b33e5ec259cc9b211711fb6..6f9d48608ae7affd1a5e46317188d9eb756fa84e 100644
--- a/src/testsuite/cmd-test-binary.c
+++ b/src/testsuite/cmd-test-binary.c
@@ -205,9 +205,7 @@ static bool cmd_test_binary_generate
 static bool cmd_test_binary_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &denv->oprtn;
-
-	sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(op));
+	sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(denv->oprtn));
 	
 	sieve_code_descend(denv);
 	
@@ -222,7 +220,7 @@ static bool cmd_test_binary_operation_dump
 static int cmd_test_binary_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &renv->oprtn;
+	const struct sieve_operation *oprtn = renv->oprtn;
 	string_t *binary_name = NULL;
 
 	/* 
@@ -231,21 +229,19 @@ static int cmd_test_binary_operation_execute
 
 	/* Binary Name */
 
-	if ( !sieve_opr_string_read(renv, address, &binary_name) ) {
-		sieve_runtime_trace_error(renv, "invalid mailbox operand");
+	if ( !sieve_opr_string_read(renv, address, "binary-name", &binary_name) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
-		
-	sieve_runtime_trace
-		(renv, "%s %s", sieve_operation_mnemonic(op), str_c(binary_name));
 
-	if ( sieve_operation_is(op, test_binary_load_operation) ) {
+	if ( sieve_operation_is(oprtn, test_binary_load_operation) ) {
 		struct sieve_binary *sbin = testsuite_binary_load(str_c(binary_name));
 
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+			"binary :load %s", str_c(binary_name));
+
 		if ( sbin != NULL ) {
 			testsuite_script_set_binary(sbin);
 
@@ -255,9 +251,12 @@ static int cmd_test_binary_operation_execute
 			return SIEVE_EXEC_FAILURE;
 		}
 
-	} else if ( sieve_operation_is(op, test_binary_save_operation) ) {
+	} else if ( sieve_operation_is(oprtn, test_binary_save_operation) ) {
 		struct sieve_binary *sbin = testsuite_script_get_binary();
 
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+			"binary :save %s", str_c(binary_name));
+
 		if ( sbin != NULL ) 
 			testsuite_binary_save(sbin, str_c(binary_name));
 		else {
diff --git a/src/testsuite/cmd-test-config.c b/src/testsuite/cmd-test-config.c
index 1949469360b9e047d60455932d558eb9113a3a0a..23afc7c9d03a30e3312743753f7280af8f0899c8 100644
--- a/src/testsuite/cmd-test-config.c
+++ b/src/testsuite/cmd-test-config.c
@@ -324,25 +324,19 @@ static int cmd_test_config_set_operation_execute
 	 */
 
 	/* Setting */
-
-	if ( !sieve_opr_string_read(renv, address, &setting) ) {
-		sieve_runtime_trace_error(renv, "invalid setting operand");
+	if ( !sieve_opr_string_read(renv, address, "setting", &setting) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/* Value */
-
-	if ( !sieve_opr_string_read(renv, address, &value) ) {
-		sieve_runtime_trace_error(renv, "invalid value operand");
+	if ( !sieve_opr_string_read(renv, address, "value", &value) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 		
-	sieve_runtime_trace(renv, "TEST_CONFIG_SET %s = '%s'", 
-		str_c(setting), str_c(value));
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+		"TEST_CONFIG_SET %s = '%s'", str_c(setting), str_c(value));
 
 	testsuite_setting_set(str_c(setting), str_c(value));
 
@@ -358,16 +352,16 @@ static int cmd_test_config_unset_operation_execute
 	 * Read operands 
 	 */
 
-	if ( !sieve_opr_string_read(renv, address, &setting) ) {
-		sieve_runtime_trace_error(renv, "invalid setting operand");
+	/* Setting */
+	if ( !sieve_opr_string_read(renv, address, "setting", &setting) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 		
-	sieve_runtime_trace(renv, "TEST_CONFIG_UNSET %s", str_c(setting));
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+		"TEST_CONFIG_UNSET %s", str_c(setting));
 
 	testsuite_setting_unset(str_c(setting));
 
@@ -385,17 +379,15 @@ static int cmd_test_config_reload_operation_execute
 	 */
 
 	/* Extension */
-
-	if ( !sieve_opr_string_read(renv, address, &extension) ) {
-		sieve_runtime_trace_error(renv, "invalid extension operand");
+	if ( !sieve_opr_string_read(renv, address, "extension", &extension) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 		
-	sieve_runtime_trace(renv, "TEST_CONFIG_RELOAD [%s]", str_c(extension));
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+		"TEST_CONFIG_RELOAD [%s]", str_c(extension));
 
 	ext = sieve_extension_get_by_name(renv->svinst, str_c(extension));
 	if ( ext == NULL ) {
diff --git a/src/testsuite/cmd-test-fail.c b/src/testsuite/cmd-test-fail.c
index ac43cdda34d335c2b7a75e359d262767d1f873e6..e5921ed311661e60d706d7446c21ce0611aebec0 100644
--- a/src/testsuite/cmd-test-fail.c
+++ b/src/testsuite/cmd-test-fail.c
@@ -132,12 +132,11 @@ static int cmd_test_fail_operation_execute
 {
 	string_t *reason;
 
-	if ( !sieve_opr_string_read(renv, address, &reason) ) {
-		sieve_runtime_trace_error(renv, "invalid reason operand");
+	if ( !sieve_opr_string_read(renv, address, "reason", &reason) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
-	sieve_runtime_trace(renv, "TEST FAIL");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "TEST FAIL");
+
 	testsuite_test_fail(reason);
 	
 	return sieve_interpreter_program_jump(renv->interp, TRUE);
diff --git a/src/testsuite/cmd-test-mailbox.c b/src/testsuite/cmd-test-mailbox.c
index e61cc44d6c855b5fd826f5dfd92e5ce58542d303..53e97d4bf0e98cdf5f4a1a7f2d6e0835ef1c3893 100644
--- a/src/testsuite/cmd-test-mailbox.c
+++ b/src/testsuite/cmd-test-mailbox.c
@@ -206,9 +206,7 @@ static bool cmd_test_mailbox_generate
 static bool cmd_test_mailbox_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &denv->oprtn;
-	
-	sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(op));
+	sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(denv->oprtn));
 	
 	sieve_code_descend(denv);
 	
@@ -223,7 +221,7 @@ static bool cmd_test_mailbox_operation_dump
 static int cmd_test_mailbox_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
-	const struct sieve_operation *op = &renv->oprtn;
+	const struct sieve_operation *oprtn = renv->oprtn;
 	string_t *mailbox = NULL;
 
 	/* 
@@ -232,19 +230,17 @@ static int cmd_test_mailbox_operation_execute
 
 	/* Index */
 
-	if ( !sieve_opr_string_read(renv, address, &mailbox) ) {
-		sieve_runtime_trace_error(renv, "invalid mailbox operand");
+	if ( !sieve_opr_string_read(renv, address, "mailbox", &mailbox) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 		
-	sieve_runtime_trace
-		(renv, "%s %s:", sieve_operation_mnemonic(op), str_c(mailbox));
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+		"%s %s:", sieve_operation_mnemonic(oprtn), str_c(mailbox));
 
-	if ( sieve_operation_is(op, test_mailbox_create_operation) )
+	if ( sieve_operation_is(oprtn, test_mailbox_create_operation) )
 		testsuite_mailstore_mailbox_create(renv, str_c(mailbox));
 
 	return SIEVE_EXEC_OK;
diff --git a/src/testsuite/cmd-test-message.c b/src/testsuite/cmd-test-message.c
index 8df89a0babc4d215d2baea73ff21d31e6f3a9818..afb99848de414f123e0231d2afd3347b6e4e9f44 100644
--- a/src/testsuite/cmd-test-message.c
+++ b/src/testsuite/cmd-test-message.c
@@ -315,17 +315,19 @@ static int cmd_test_message_smtp_operation_execute
 
 	/* Index */
 
-	if ( !sieve_opr_number_read(renv, address, &msg_index) ) {
-		sieve_runtime_trace_error(renv, "invalid index operand");
+	if ( !sieve_opr_number_read(renv, address, "index", &msg_index) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 		
-	sieve_runtime_trace(renv, "TEST_MESSAGE_SMTP (%s) [%d]", 
-		( is_test ? "TEST" : "COMMAND" ), msg_index);
+	if ( is_test )
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+			"TEST_MESSAGE_SMTP test [%d]", msg_index);
+	else
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+			"TEST_MESSAGE_SMTP command [%d]", msg_index);
 
 	result = testsuite_smtp_get(renv, msg_index);
 
@@ -359,23 +361,23 @@ static int cmd_test_message_mailbox_operation_execute
 	}
 
 	/* Folder */
-	if ( !sieve_opr_string_read(renv, address, &folder) ) {
-		sieve_runtime_trace_error(renv, "invalid folder operand");
+	if ( !sieve_opr_string_read(renv, address, "folder", &folder) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
 	/* Index */
-	if ( !sieve_opr_number_read(renv, address, &msg_index) ) {
-		sieve_runtime_trace_error(renv, "invalid index operand");
+	if ( !sieve_opr_number_read(renv, address, "index", &msg_index) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
-		
-	sieve_runtime_trace(renv, "TEST_MESSAGE_MAILBOX (%s) \"%s\" [%d]", 
-		( is_test ? "TEST" : "COMMAND" ), str_c(folder), msg_index);
+
+	if ( is_test ) 		
+		sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, 
+			"TEST_MESSAGE_MAILBOX test \"%s\" [%d]", str_c(folder), msg_index);
+	else
+		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, 
+			"TEST_MESSAGE_MAILBOX command \"%s\" [%d]", str_c(folder), msg_index);
 
 	result = testsuite_mailstore_mail_index(renv, str_c(folder), msg_index);
 
diff --git a/src/testsuite/cmd-test-set.c b/src/testsuite/cmd-test-set.c
index 590934c3315ee317d88ad6e99443d5b1e899de4f..459e4e86fa7b257991121f1c933ec9583733a216 100644
--- a/src/testsuite/cmd-test-set.c
+++ b/src/testsuite/cmd-test-set.c
@@ -135,12 +135,11 @@ static int cmd_test_set_operation_execute
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 
-	if ( !sieve_opr_string_read(renv, address, &value) ) {
-		sieve_runtime_trace_error(renv, "invalid string operand");
+	if ( !sieve_opr_string_read(renv, address, "string", &value) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
-	sieve_runtime_trace(renv, "TEST SET command (%s = \"%s\")", 
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, 
+		"test_set '%s' = \"%s\"", 
 		testsuite_object_member_name(&tobj, member_id), str_c(value));
 	
 	if ( tobj.def == NULL || tobj.def->set_member == NULL ) {
diff --git a/src/testsuite/cmd-test.c b/src/testsuite/cmd-test.c
index b81382571b6708b13abb6f3398367265ef2b15e5..b60758979ca4a470e4a60bb64bdedb36cc7548f6 100644
--- a/src/testsuite/cmd-test.c
+++ b/src/testsuite/cmd-test.c
@@ -152,12 +152,11 @@ static int cmd_test_operation_execute
 {
 	string_t *test_name;
 
-	if ( !sieve_opr_string_read(renv, address, &test_name) ) {
-		sieve_runtime_trace_error(renv, "invalid test name operand");
+	if ( !sieve_opr_string_read(renv, address, "test name", &test_name) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 	
-	sieve_runtime_trace(renv, "TEST \"%s\"", str_c(test_name));
+	sieve_runtime_trace(renv, SIEVE_TRLVL_MINIMUM, 
+		"** Test start: \"%s\"", str_c(test_name));
 
 	testsuite_test_start(test_name);
 	return SIEVE_EXEC_OK;
@@ -167,7 +166,7 @@ static int cmd_test_finish_operation_execute
 (const struct sieve_runtime_env *renv ATTR_UNUSED, 
 	sieve_size_t *address ATTR_UNUSED)
 {
-	sieve_runtime_trace(renv, "TEST FINISHED");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_MINIMUM, "** Test end");
 	
 	testsuite_test_succeed(NULL);
 	return SIEVE_EXEC_OK;
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index 91bc7e9dd59138fa3c6c64abdc44e70954491ba1..f939ce4be9e65400f364a53c3f19589706d07f97 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -105,18 +105,18 @@ bool testsuite_script_run(const struct sieve_runtime_env *renv)
 	scriptenv.duplicate_check = NULL;
 	scriptenv.namespaces = renv->scriptenv->namespaces;
 	scriptenv.trace_stream = renv->scriptenv->trace_stream;
+	scriptenv.trace_level = renv->scriptenv->trace_level;
 	
 	result = testsuite_result_get();
 
 	/* Execute the script */
-	interp=sieve_interpreter_create
-		(_testsuite_compiled_script, testsuite_log_ehandler);
+	interp=sieve_interpreter_create(_testsuite_compiled_script, renv->msgdata, 
+		&scriptenv, testsuite_log_ehandler);
 	
 	if ( interp == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
 		
-	ret = sieve_interpreter_run
-		(interp, renv->msgdata, &scriptenv, result);
+	ret = sieve_interpreter_run(interp, result);
 
 	sieve_interpreter_free(&interp);
 
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 4739cc07fb117358cf652b69194d74ae00342186..4964965a9a41233c6cc49bc8ded2c23fbeb8467e 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -77,17 +77,13 @@ static int testsuite_run
 	int ret = 0;
 
 	/* Create the interpreter */
-	if ( (interp=sieve_interpreter_create(sbin, ehandler)) == NULL )
+	if ( (interp=sieve_interpreter_create(sbin, msgdata, senv, ehandler)) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
 
-	/* Reset execution status */
-	if ( senv->exec_status != NULL )
-		memset(senv->exec_status, 0, sizeof(*senv->exec_status));
-
 	/* Run the interpreter */
 	result = testsuite_result_get();
 	sieve_result_ref(result);
-	ret = sieve_interpreter_run(interp, msgdata, senv, result);
+	ret = sieve_interpreter_run(interp, result);
 	sieve_result_unref(&result);
 
 	/* Free the interpreter */
@@ -253,6 +249,7 @@ int main(int argc, char **argv)
 		scriptenv.smtp_open = testsuite_smtp_open;
 		scriptenv.smtp_close = testsuite_smtp_close;
 		scriptenv.trace_stream = ( trace ? o_stream_create_fd(1, 0, FALSE) : NULL );
+		scriptenv.trace_level = SIEVE_TRLVL_TESTS;
 
 		testsuite_scriptenv = &scriptenv;
 
diff --git a/src/testsuite/tst-test-error.c b/src/testsuite/tst-test-error.c
index cb3b9ae24beb122257b35c65bdd56727d9b1315b..9dcbac7fc42efdf19c4f8e7d774ee28457ba5f02 100644
--- a/src/testsuite/tst-test-error.c
+++ b/src/testsuite/tst-test-error.c
@@ -182,21 +182,22 @@ static bool tst_test_error_operation_dump
 	sieve_code_descend(denv);
 
 	/* Handle any optional arguments */
-	do {
-		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_dump(denv, address, &opt_code)) 
+			< 0 )
 			return FALSE;
 
-		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
-		case OPT_INDEX:
+		if ( ret == 0 ) break;
+
+		if ( opt_code == OPT_INDEX ) {
 			if ( !sieve_opr_number_dump(denv, address, "index") )
 				return FALSE;
-			break;
-		default:
+		} else {
 			return FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+	} 
 
 	return sieve_opr_stringlist_dump(denv, address, "key list");
 }
@@ -223,41 +224,37 @@ static int tst_test_error_operation_execute
 	 * Read operands
 	 */
 
-	/* Handle optional operands */
-	do {
+	/* Read optional operands */
+	for (;;) {
 		sieve_number_t number; 
+		int ret;
 
-		if ( (ret=sieve_match_read_optional_operands
-			(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
- 			return ret;
+		if ( (ret=sieve_match_opr_optional_read
+			(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
 
-		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
-		case OPT_INDEX:
-			if ( !sieve_opr_number_read(renv, address, &number) ) {
-				sieve_runtime_trace_error(renv, "invalid index operand");
+		if ( ret == 0 ) break;
+	
+		if ( opt_code == OPT_INDEX ) {
+			if ( !sieve_opr_number_read(renv, address, "index", &number) )
 				return SIEVE_EXEC_BIN_CORRUPT;
-			}
 			index = (int) number;
-			break;
-		default:
+		} else {
 			sieve_runtime_trace_error(renv, "invalid optional operand");
 			return SIEVE_EXEC_BIN_CORRUPT;
 		}	
-	} while ( opt_code != SIEVE_MATCH_OPT_END);
+	}
 
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key_list")) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 	
-	sieve_runtime_trace(renv, "TEST_ERROR test (index: %d)", index);
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+		"TEST_ERROR test (index: %d)", index);
 
 	testsuite_log_get_error_init();
 
diff --git a/src/testsuite/tst-test-multiscript.c b/src/testsuite/tst-test-multiscript.c
index 7dba89b6845e9e0159c9e5a3009c2d1a4ca8f13f..5ab3a6fac097b7d0a264160ca32118829dfc598c 100644
--- a/src/testsuite/tst-test-multiscript.c
+++ b/src/testsuite/tst-test-multiscript.c
@@ -117,16 +117,15 @@ static int tst_test_multiscript_operation_execute
 	 * Read operands
 	 */
 
-  if ( (scripts_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid scripts operand");
+  if ( (scripts_list=sieve_opr_stringlist_read(renv, address, "scripts")) 
+		== NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "TEST MULTISCRIPT");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "TEST MULTISCRIPT");
 
 	t_array_init(&scriptfiles, 16);
 
diff --git a/src/testsuite/tst-test-result-execute.c b/src/testsuite/tst-test-result-execute.c
index b7bfe28fedc8c7410924871403ae841d1f5d3dc4..285e789c7dcbafb17fcd6cf17db1edcb8ed0ae25 100644
--- a/src/testsuite/tst-test-result-execute.c
+++ b/src/testsuite/tst-test-result-execute.c
@@ -74,7 +74,7 @@ static int tst_test_result_execute_operation_execute
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "TEST_RESULT_EXECUTE test");
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "TEST_RESULT_EXECUTE test");
 
 	result = testsuite_result_execute(renv);
 
diff --git a/src/testsuite/tst-test-result.c b/src/testsuite/tst-test-result.c
index 1c0e5a21a11928681e025c792914ba06c5615284..d6cccc071ea03a5a8adbf2de7844c677afdd7531 100644
--- a/src/testsuite/tst-test-result.c
+++ b/src/testsuite/tst-test-result.c
@@ -187,21 +187,22 @@ static bool tst_test_result_operation_dump
 	sieve_code_descend(denv);
 
 	/* Handle any optional arguments */
-	do {
-		if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_match_opr_optional_dump(denv, address, &opt_code)) 
+			< 0 )
 			return FALSE;
 
-		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
-		case OPT_INDEX:
+		if ( ret == 0 ) break;
+
+		if ( opt_code == OPT_INDEX ) {
 			if ( !sieve_opr_number_dump(denv, address, "index") )
 				return FALSE;
-			break;
-		default:
+		} else {
 			return FALSE;
 		}
-	} while ( opt_code != SIEVE_MATCH_OPT_END );
+	} 
 
 	return sieve_opr_stringlist_dump(denv, address, "key list");
 }
@@ -230,41 +231,37 @@ static int tst_test_result_operation_execute
 	 * Read operands
 	 */
 
-	/* Handle optional operands */
-	do {
+	/* Read optional operands */
+	for (;;) {
 		sieve_number_t number; 
+		int ret;
 
-		if ( (ret=sieve_match_read_optional_operands
-			(renv, address, &opt_code, &cmp, &mcht)) <= 0 )
- 			return ret;
+		if ( (ret=sieve_match_opr_optional_read
+			(renv, address, &opt_code, &cmp, &mcht)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
 
-		switch ( opt_code ) {
-		case SIEVE_MATCH_OPT_END:
-			break;
-		case OPT_INDEX:
-			if ( !sieve_opr_number_read(renv, address, &number) ) {
-				sieve_runtime_trace_error(renv, "invalid index operand");
+		if ( ret == 0 ) break;
+	
+		if ( opt_code == OPT_INDEX ) {
+			if ( !sieve_opr_number_read(renv, address, "index", &number) )
 				return SIEVE_EXEC_BIN_CORRUPT;
-			}
 			index = (int) number;
-			break;
-		default:
+		} else {
 			sieve_runtime_trace_error(renv, "invalid optional operand");
 			return SIEVE_EXEC_BIN_CORRUPT;
 		}	
-	} while ( opt_code != SIEVE_MATCH_OPT_END);
+	}
 
 	/* Read key-list */
-	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
-		sieve_runtime_trace_error(renv, "invalid key-list operand");
+	if ( (key_list=sieve_opr_stringlist_read(renv, address, "key-list")) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 	
-	sieve_runtime_trace(renv, "TEST_RESULT test (index: %d)", index);
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+		"TEST_RESULT test (index: %d)", index);
 
 	rictx = testsuite_result_iterate_init();
 
diff --git a/src/testsuite/tst-test-script-compile.c b/src/testsuite/tst-test-script-compile.c
index ad1705289d42aa19df6ba4082752be7bca60506f..c76699e9db95266537862eba026e708db2596038 100644
--- a/src/testsuite/tst-test-script-compile.c
+++ b/src/testsuite/tst-test-script-compile.c
@@ -94,7 +94,7 @@ static bool tst_test_script_compile_operation_dump
 	sieve_code_dumpf(denv, "TEST_SCRIPT_COMPILE:");
 	sieve_code_descend(denv);
 
-	if ( !sieve_opr_string_dump(denv, address, "script") ) 
+	if ( !sieve_opr_string_dump(denv, address, "script-name") ) 
 		return FALSE;
 
 	return TRUE;
@@ -115,16 +115,15 @@ static int tst_test_script_compile_operation_execute
 	 * Read operands
 	 */
 
-	if ( !sieve_opr_string_read(renv, address, &script_name) ) {
-		sieve_runtime_trace_error(renv, "invalid script name operand");
+	if ( !sieve_opr_string_read(renv, address, "script-name", &script_name) )
 		return SIEVE_EXEC_BIN_CORRUPT;
-	}
 
 	/*
 	 * Perform operation
 	 */
 
-	sieve_runtime_trace(renv, "TEST COMPILE: %s", str_c(script_name));
+	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+		"testsuite: compile script '%s'", str_c(script_name));
 
 	script_path = sieve_script_dirpath(renv->script);
 	if ( script_path == NULL ) 
diff --git a/src/testsuite/tst-test-script-run.c b/src/testsuite/tst-test-script-run.c
index 8c2bf0e2c66a24490e0d7e43099a2150e995ff49..262da7ed934fa05dc2862e1a73501d1c2ef70c12 100644
--- a/src/testsuite/tst-test-script-run.c
+++ b/src/testsuite/tst-test-script-run.c
@@ -104,29 +104,26 @@ static bool tst_test_script_run_generate
 static bool tst_test_script_run_operation_dump
 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {	
-	int opt_code = 1;
+	int opt_code = 0;
 	
 	sieve_code_dumpf(denv, "TEST_SCRIPT_RUN");
 	sieve_code_descend(denv);	
 
 	/* Dump optional operands */
-	if ( sieve_operand_optional_present(denv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			sieve_code_mark(denv);
-			
-			if ( !sieve_operand_optional_read(denv->sblock, address, &opt_code) ) 
-				return FALSE;
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_APPEND_RESULT:
-				sieve_code_dumpf(denv, "append_result");	
-				break;
-			
-			default:
-				return FALSE;
-			}
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+			return FALSE;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_APPEND_RESULT:
+			sieve_code_dumpf(denv, "append_result");	
+			break;
+		default:
+			return FALSE;
 		}
 	}
 	
@@ -142,7 +139,7 @@ static int tst_test_script_run_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
 	bool append_result = FALSE;
-	int opt_code = 1;
+	int opt_code = 0;
 	bool result = TRUE;
 
 	/*
@@ -150,24 +147,22 @@ static int tst_test_script_run_operation_execute
 	 */
 
 	/* Optional operands */	
-	if ( sieve_operand_optional_present(renv->sblock, address) ) {
-		while ( opt_code != 0 ) {
-			if ( !sieve_operand_optional_read(renv->sblock, address, &opt_code) ) {
-				sieve_runtime_trace_error(renv, "invalid optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
-
-			switch ( opt_code ) {
-			case 0:
-				break;
-			case OPT_APPEND_RESULT:
-				append_result = TRUE;
-				break;
-			default:
-				sieve_runtime_trace_error(renv, 
-					"unknown optional operand");
-				return SIEVE_EXEC_BIN_CORRUPT;
-			}
+	for (;;) {
+		int ret;
+
+		if ( (ret=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+			return SIEVE_EXEC_BIN_CORRUPT;
+
+		if ( ret == 0 ) break;
+
+		switch ( opt_code ) {
+		case OPT_APPEND_RESULT:
+			append_result = TRUE;
+			break;
+		default:
+			sieve_runtime_trace_error(renv, 
+				"unknown optional operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
 		}
 	}
 
@@ -175,6 +170,9 @@ static int tst_test_script_run_operation_execute
 	 * Perform operation
 	 */
 
+	sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, 
+		"testsuite: run compiled script");
+
 	/* Reset result object */
 	if ( !append_result ) 
 		testsuite_result_reset(renv);