#include "sieve-common.h"
#include "sieve-commands.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-code.h"
#include "sieve-binary.h"
static bool cmd_if_validate
(struct sieve_validator *valdtr, struct sieve_command *cmd);
static bool cmd_if_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
const struct sieve_command_def cmd_if = {
"if",
SCT_COMMAND,
0, 1, TRUE, TRUE,
NULL, NULL,
cmd_if_validate,
cmd_if_generate,
NULL
};
static bool cmd_elsif_validate
(struct sieve_validator *valdtr, struct sieve_command *cmd);
const struct sieve_command_def cmd_elsif = {
"elsif",
SCT_COMMAND,
0, 1, TRUE, TRUE,
NULL, NULL,
cmd_elsif_validate,
cmd_if_generate,
NULL
};
static bool cmd_else_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
const struct sieve_command_def cmd_else = {
"else",
SCT_COMMAND,
0, 0, TRUE, TRUE,
NULL, NULL,
cmd_elsif_validate,
cmd_else_generate,
NULL
};
struct cmd_if_context_data {
struct cmd_if_context_data *previous;
struct cmd_if_context_data *next;
bool jump_generated;
sieve_size_t exit_jump;
};
static void cmd_if_initialize_context_data
(struct sieve_command *cmd, struct cmd_if_context_data *previous)
{
struct cmd_if_context_data *cmd_data;
cmd_data = p_new(sieve_command_pool(cmd), struct cmd_if_context_data, 1);
cmd_data->exit_jump = 0;
cmd_data->jump_generated = FALSE;
cmd_data->previous = previous;
cmd_data->next = NULL;
if ( previous != NULL )
previous->next = cmd_data;
cmd->data = cmd_data;
}
static bool cmd_if_validate
(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd)
{
cmd_if_initialize_context_data(cmd, NULL);
return TRUE;
}
static bool cmd_elsif_validate
(struct sieve_validator *valdtr, struct sieve_command *cmd)
{
struct sieve_command *prev = sieve_command_prev(cmd);
if ( prev == NULL ||
( !sieve_command_is(prev, cmd_if) && !sieve_command_is(prev, cmd_elsif) ) )
{
sieve_command_validate_error(valdtr, cmd,
"the %s command must follow an if or elseif command",
sieve_command_identifier(cmd));
return FALSE;
}
cmd_if_initialize_context_data(cmd, prev->data);
return TRUE;
}
static void cmd_if_resolve_exit_jumps
(struct sieve_binary_block *sblock, struct cmd_if_context_data *cmd_data)
{
struct cmd_if_context_data *if_ctx = cmd_data->previous;
while ( if_ctx != NULL ) {
if ( if_ctx->jump_generated )
sieve_binary_resolve_offset(sblock, if_ctx->exit_jump);
if_ctx = if_ctx->previous;
}
}
static bool cmd_if_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
{
struct sieve_binary_block *sblock = cgenv->sblock;
struct cmd_if_context_data *cmd_data =
(struct cmd_if_context_data *) cmd->data;
struct sieve_ast_node *test;
struct sieve_jumplist jmplist;
sieve_jumplist_init_temp(&jmplist, sblock);
test = sieve_ast_test_first(cmd->ast_node);
if ( !sieve_generate_test(cgenv, test, &jmplist, FALSE) )
return FALSE;
if ( !sieve_generate_block(cgenv, cmd->ast_node) )
return FALSE;
if ( cmd_data->next != NULL ) {
if ( !sieve_command_block_exits_unconditionally(cmd) ) {
sieve_operation_emit(sblock, NULL, &sieve_jmp_operation);
cmd_data->exit_jump = sieve_binary_emit_offset(sblock, 0);
cmd_data->jump_generated = TRUE;
}
} else {
cmd_if_resolve_exit_jumps(sblock, cmd_data);
}
sieve_jumplist_resolve(&jmplist);
return TRUE;
}
static bool cmd_else_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
{
struct cmd_if_context_data *cmd_data =
(struct cmd_if_context_data *) cmd->data;
if ( !sieve_generate_block(cgenv, cmd->ast_node) )
return FALSE;
cmd_if_resolve_exit_jumps(cgenv->sblock, cmd_data);
return TRUE;
}