aboutsummaryrefslogtreecommitdiff
path: root/src/prog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/prog.c')
-rw-r--r--src/prog.c738
1 files changed, 557 insertions, 181 deletions
diff --git a/src/prog.c b/src/prog.c
index 75b52284..2be8cbb7 100644
--- a/src/prog.c
+++ b/src/prog.c
@@ -1,5 +1,5 @@
/* This file is part of Mailfromd.
- Copyright (C) 2006-2019 Sergey Poznyakoff
+ Copyright (C) 2006-2024 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,7 +60,6 @@ size_t dataseg_reloc_count;
struct rt_regex *regtab;
size_t regcount;
mu_opool_t regpool;
-
void
code_init()
@@ -149,7 +148,34 @@ code_peek(prog_counter_t pos)
assert(pos < pmax);
return prog[pos];
}
+
+static size_t transform_count;
+static size_t transform_max;
+static transform_t *transform_tab;
+size_t
+next_transform_index(void)
+{
+ if (transform_count == transform_max) {
+ transform_tab = mu_2nrealloc(transform_tab,
+ &transform_max,
+ sizeof(transform_tab[0]));
+ }
+ transform_tab[transform_count] = NULL;
+ return transform_count++;
+}
+
+void
+install_transform(size_t index, transform_t tp)
+{
+ transform_tab[index] = tp;
+}
+
+transform_t
+get_transform(size_t index)
+{
+ return transform_tab[index];
+}
/* Regexps*/
@@ -213,7 +239,7 @@ disable_prog_trace(const char *modlist)
}
/* ========================================================================
- Drzewa w górê, rzeki w dó³.
+ Drzewa w górę, rzeki w dół.
Jacek Kaczmarski.
"Upadek Ikara"
@@ -225,8 +251,6 @@ disable_prog_trace(const char *modlist)
See comment to env_fixup_autos, below. */
#define MAX_AUTO_PTR 128
-#define ENVF_MSGMOD 0x01 /* message modofication instruction has been used */
-
struct exception_context {
prog_counter_t pc;
prog_counter_t tos;
@@ -237,10 +261,11 @@ struct exception_context {
struct environ_locus {
unsigned long file; /* File name */
- unsigned int line; /* Line number */
+ unsigned int line; /* Line number */
};
struct environ_cleanup_closure {
+ int when;
void (*cleanup)(void *);
void *data;
};
@@ -262,6 +287,7 @@ struct eval_environ {
struct environ_locus locus;
/* Temporary heap space */
+ int temp_active;
size_t temp_start;
size_t temp_size;
@@ -271,14 +297,13 @@ struct eval_environ {
/* Sendmail interaction data: */
SMFICTX *ctx; /* Milter Context */
+ int conn_family; /* SMTP connection family. */
void *data; /* MTA symbol table */
/* methods to access the latter */
const char *(*getsym)(void *data, const char *str);
int (*setreply)(void *data, char *code, char *xcode, char *message);
void (*msgmod)(void *data, struct msgmod_closure *c);
- int flags;
-
/* Regular expression matching */
regmatch_t *matches; /* Match map */
size_t matchcount; /* Number of used entries in matches */
@@ -292,7 +317,10 @@ struct eval_environ {
its end before writing. */
mu_header_t header; /* Headers from stream, converted to a MU
object. */
-
+
+ /* Message modification queue */
+ mu_list_t mmq;
+
/* Non-local exits */
struct exception_context *defcatch_ctx;
struct exception_context *catch_ctx;
@@ -301,8 +329,8 @@ struct eval_environ {
void **bi_priv_array;
/* Function clean-up sequence */
- mu_list_t cleanup_list;
-
+ mu_list_t function_cleanup_list;
+
/* Exit information */
sfsistat status; /* Program exit status */
jmp_buf x_jmp; /* Return point for runtime errors */
@@ -421,63 +449,71 @@ _cleanup_compare(const void *a, const void *b)
{
struct environ_cleanup_closure const *closa = a;
struct environ_cleanup_closure const *closb = b;
- return closa->data != closb->data;
+ return !(closa->when == closb->when && closa->data == closb->data);
}
-
+
static void
-_cleanup_destroy(void *item)
+env_cleanup_list_create(mu_list_t *plist)
{
- struct environ_cleanup_closure *clos = item;
- if (clos->cleanup)
- clos->cleanup(clos->data);
+ mu_list_t list;
+ mu_list_create(&list);//FIXME: error checking
+ mu_list_set_comparator(list, _cleanup_compare);
+ *plist = list;
}
static void
-env_create_cleanup_list(eval_environ_t env)
+env_cleanup_list_append(mu_list_t list, int when, void *data, void (*func)(void *))
{
- mu_list_create(&env->cleanup_list);
- mu_list_set_destroy_item(env->cleanup_list, _cleanup_destroy);
- mu_list_set_comparator(env->cleanup_list, _cleanup_compare);
+ struct environ_cleanup_closure *clos = mu_zalloc(sizeof(*clos));
+
+ clos->when = when;
+ clos->cleanup = func ? func : free;
+ clos->data = data;
+ mu_list_append(list, clos);
+}
+
+static int
+_cleanup_flush (void *item, void *data)
+{
+ struct environ_cleanup_closure *clos = item;
+ int when = *(int*)data;
+
+ if (when == CLEANUP_ALWAYS ||
+ clos->when == CLEANUP_ALWAYS ||
+ clos->when == when) {
+ clos->cleanup(clos->data);
+ }
+ return 0;
}
void
-env_function_cleanup_flush(eval_environ_t env, void *ptr)
+env_function_cleanup_flush(eval_environ_t env, int when)
{
- if (ptr) {
- struct environ_cleanup_closure clos;
- clos.data = ptr;
- mu_list_remove(env->cleanup_list, &clos);
- } else {
- mu_list_clear(env->cleanup_list);
- }
+ mu_list_foreach(env->function_cleanup_list, _cleanup_flush, &when);
+ mu_list_clear(env->function_cleanup_list);
}
void
-env_function_cleanup_del(eval_environ_t env, void *ptr)
+env_function_cleanup_del(eval_environ_t env, int when, void *ptr)
{
int rc;
struct environ_cleanup_closure clos, *cptr;
+ clos.when = when;
clos.data = ptr;
- rc = mu_list_locate(env->cleanup_list, &clos, (void**)&cptr);
+ rc = mu_list_locate(env->function_cleanup_list, &clos, (void**)&cptr);
if (rc == 0) {
cptr->cleanup = NULL;
- mu_list_remove(env->cleanup_list, &clos);
+ mu_list_remove(env->function_cleanup_list, &clos);
}
}
void
-env_function_cleanup_add(eval_environ_t env, void *data,
- void (*func)(void *))
+env_function_cleanup_add(eval_environ_t env, int when, void *data, void (*func)(void *))
{
- struct environ_cleanup_closure *clos = mu_zalloc(sizeof(*clos));
-
- clos->cleanup = func ? func : free;
- clos->data = data;
- mu_list_append(env->cleanup_list, clos);
+ env_cleanup_list_append(env->function_cleanup_list, when, data, func);
}
-
/* A call to expand_dataseg (see below) invalidates any C variables that
pointed to the dataseg before the call. To avoid dereferencing invalid
@@ -494,7 +530,7 @@ static void
env_register_auto(eval_environ_t env, void *ptr)
{
char *addr = *(char**)ptr;
-
+
if (env->numautos == MAX_AUTO_PTR)
runtime_error(env, "INTERNAL ERROR at %s:%d, please report",
__FILE__, __LINE__);
@@ -531,46 +567,39 @@ env_fixup_autos(eval_environ_t env, ptrdiff_t offset)
int
-expand_dataseg(eval_environ_t env, size_t count, const char *errtext)
+expand_dataseg(eval_environ_t env, size_t count)
{
STKVAL *newds;
ptrdiff_t offset;
size_t new_stack_size;
size_t diff;
+ enum { DATASEG_MAX = ((size_t)-1) / sizeof(STKVAL) };
switch (stack_expand_policy) {
case stack_expand_add:
diff = ((count + stack_expand_incr - 1) / stack_expand_incr)
* stack_expand_incr;
+ if (DATASEG_MAX - diff < env->stack_size)
+ return -1;
new_stack_size = env->stack_size + diff;
break;
case stack_expand_twice:
- new_stack_size = 2 * env->stack_size;
- if (new_stack_size < env->stack_size) {
- if (errtext)
- runtime_error(env, "%s", errtext);
- else
- return 1;
- }
- diff = new_stack_size - env->stack_size;
- if (diff < count) {
- if (errtext)
- runtime_error(env, "%s", errtext);
- else
- return 1;
- }
+ new_stack_size = env->stack_size;
+ do {
+ if (DATASEG_MAX / 2 < new_stack_size)
+ return -1;
+ new_stack_size *= 2;
+ diff = new_stack_size - env->stack_size;
+ } while (diff < count);
}
if ((stack_max_size && new_stack_size > stack_max_size)
+ || DATASEG_MAX - datasize < new_stack_size
|| (newds = realloc(env->dataseg,
(new_stack_size + datasize)
- * sizeof env->dataseg[0])) == NULL) {
- if (errtext)
- runtime_error(env, "%s", errtext);
- else
- return 1;
- }
+ * sizeof env->dataseg[0])) == NULL)
+ return 1;
offset = (char*)newds - (char*)env->dataseg;
env->dataseg = newds;
@@ -603,50 +632,72 @@ prog_trace(eval_environ_t env, const char *fmt, ...)
buf);
}
+void instr_builtin(eval_environ_t env);
void instr_funcall(eval_environ_t env);
void instr_locus(eval_environ_t env);
+void instr_catch(eval_environ_t env);
+
+static struct mu_locus_point *
+stack_find_locus(eval_environ_t env, prog_counter_t pc, prog_counter_t off, struct mu_locus_point *pt)
+{
+ int i;
+
+ for (i = 0; i < 32; i++, off++) {
+ if (pc > off && prog[pc - off].c_instr == instr_locus) {
+ pt->mu_file = (char*)(env->dataseg
+ + mf_cell_c_value(prog[pc - off + 1], ulong));
+ pt->mu_line = mf_cell_c_value(prog[pc - off + 2], uint);
+ return pt;
+ }
+ }
+ return NULL;
+}
+
+static void
+stack_trace_entry(eval_environ_t env, prog_counter_t pc)
+{
+ char *name;
+ struct mu_locus_point *pt, ptbuf;
+
+ if (mf_cell_instr(prog[pc-2]) == instr_funcall ||
+ mf_cell_instr(prog[pc-2]) == instr_builtin) {
+ name = (char*)(env->dataseg + mf_cell_c_value(prog[pc-1], size));
+ pc -= 2;
+ pt = stack_find_locus(env, pc, 3, &ptbuf);
+ } else if (mf_cell_instr(prog[pc-2]) == instr_catch) {
+ name = "(in catch)";
+ pc -= 2;
+ pt = stack_find_locus(env, pc, 3, &ptbuf);
+ } else {
+ name = "(unknown)";
+ pt = stack_find_locus(env, pc, 0, &ptbuf);
+ }
+
+ if (pt)
+ mu_error("%04lu: %s:%u: %s",
+ (unsigned long) pc,
+ pt->mu_file, pt->mu_line, name);
+ else
+ mu_error("%04lu: %s",
+ (unsigned long) pc, name);
+}
void
runtime_stack_trace(eval_environ_t env)
{
size_t base;
-
+
mu_error(_("stack trace:"));
+ stack_trace_entry(env, env->pc);
for (base = env->base; base < datasize + env->stack_size - 4;
base = mf_c_val(env->dataseg[base + 1], size) + base + 1) {
- int i;
- prog_counter_t pc = mf_c_val(env->dataseg[base + 2], ulong) - 1;
- char *name;
- struct mu_locus_point *ploc = NULL, loc;
-
+ prog_counter_t pc = mf_c_val(env->dataseg[base + 2], ulong);
if (pc < 2)
break; /*FIXME*/
- if (mf_cell_instr(prog[pc-2]) == instr_funcall) {
- name = (char*)(env->dataseg + mf_cell_c_value(prog[pc-1], size));
- pc -= 2;
- } else {
- name = "(in catch)";
- pc -= 3;
- }
-
- for (i = 0; i < 10; i++) {
- if (pc > i + 3
- && prog[pc - i - 3].c_instr == instr_locus) {
- loc.mu_file = (char*)(env->dataseg
- + mf_cell_c_value(prog[pc - i - 2], size));
- loc.mu_line = mf_cell_c_value(prog[pc - i - 1], size);
- ploc = &loc;
- break;
- }
+ stack_trace_entry(env, pc);
+ if (mf_cell_instr(prog[pc-2]) == instr_catch) {
+ stack_trace_entry(env, mf_c_val(env->dataseg[base + 5], size));
}
-
- if (ploc)
- mu_error("%04lu: %s:%u: %s",
- (unsigned long) pc,
- ploc->mu_file, ploc->mu_line, name);
- else
- mu_error("%04lu: %s",
- (unsigned long) pc, name);
}
mu_error(_("stack trace finishes"));
}
@@ -660,10 +711,11 @@ runtime_warning(eval_environ_t env, const char *fmt, ...)
_("RUNTIME WARNING near %s:%u: "),
ENV_LOC_FILE(env), ENV_LOC_LINE(env));
va_start(ap, fmt);
- mu_diag_output(MU_DIAG_WARNING, fmt, ap);
+ mu_diag_cont_vprintf(fmt, ap);
va_end(ap);
+ mu_diag_cont_printf("\n");
}
-
+
void
runtime_error(eval_environ_t env, const char *fmt, ...)
{
@@ -732,8 +784,9 @@ push(eval_environ_t env, STKVAL val)
("tos=%lu, toh=%lu",
(unsigned long) env->tos,
(unsigned long) env->toh));
- expand_dataseg(env, 1,
- _("out of stack space; increase #pragma stacksize"));
+ if (expand_dataseg(env, 1))
+ runtime_error(env, "%s",
+ _("out of stack space; increase #pragma stacksize"));
}
env->dataseg[env->tos--] = val;
}
@@ -750,9 +803,11 @@ size_t
heap_reserve_words(eval_environ_t env, size_t words)
{
size_t off = env->toh;
- if (env->toh + words > env->tos)
- expand_dataseg(env, words,
- _("heap overrun; increase #pragma stacksize"));
+ if (env->toh + words > env->tos) {
+ if (expand_dataseg(env, words))
+ runtime_error(env, "%s",
+ _("heap overrun; increase #pragma stacksize"));
+ }
env->toh += words;
return off;
}
@@ -768,9 +823,11 @@ heap_tempspace(eval_environ_t env, size_t size)
{
size_t words = B2STACK(size);
STKVAL ret;
- if (env->toh + words > env->tos)
- expand_dataseg(env, words,
- _("heap overrun; increase #pragma stacksize"));
+ if (env->toh + words > env->tos) {
+ if (expand_dataseg(env, words))
+ runtime_error(env, "%s",
+ _("heap overrun; increase #pragma stacksize"));
+ }
mf_c_val(ret, ptr) = env->dataseg + env->toh;
return ret;
}
@@ -780,14 +837,18 @@ heap_obstack_begin(eval_environ_t env)
{
env->temp_start = env->toh;
env->temp_size = 0;
+ env->temp_active = 1;
}
void
heap_obstack_cancel(eval_environ_t env)
{
- env->toh = env->temp_start;
- env->temp_start = 0;
- env->temp_size = 0;
+ if (env->temp_active) {
+ env->toh = env->temp_start;
+ env->temp_start = 0;
+ env->temp_size = 0;
+ env->temp_active = 0;
+ }
}
STKVAL
@@ -797,9 +858,58 @@ heap_obstack_finish(eval_environ_t env)
mf_c_val(ret, size) = env->temp_start;
env->temp_start = 0;
env->temp_size = 0;
+ env->temp_active = 0;
return ret;
}
+static inline size_t
+heap_obstack_size(eval_environ_t env)
+{
+ return (env->tos - env->toh - B2STACK(env->temp_size))
+ * sizeof env->dataseg[0];
+}
+
+void
+heap_obstack_vsprintf(eval_environ_t env, const char *fmt, va_list ap)
+{
+ size_t n;
+ while (1) {
+ size_t size;
+ va_list apc;
+
+ if (env->tos == env->toh)
+ if (expand_dataseg(env, B2STACK(strlen(fmt)))) {
+ runtime_error(env, "%s",
+ _("memory chunk too big to fit into heap"));
+ }
+
+ size = heap_obstack_size(env);
+ va_copy(apc, ap);
+ n = vsnprintf((char*) env_data_ref(env, env->temp_start) + env->temp_size,
+ size, fmt, apc);
+ va_end(apc);
+ if (n >= size) {
+ if (expand_dataseg(env, B2STACK(n))) {
+ runtime_error(env, "%s",
+ _("memory chunk too big to fit into heap"));
+ }
+ continue;
+ }
+ break;
+ }
+ env->temp_size += n;
+ env->toh += B2STACK(n);
+}
+
+void
+heap_obstack_sprintf(eval_environ_t env, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ heap_obstack_vsprintf(env, fmt, ap);
+ va_end(ap);
+}
+
void *
heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size)
{
@@ -807,9 +917,11 @@ heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size)
char *ret;
env_register_auto(env, (void*) &ptr);
- if (env->tos - env->toh < words + B2STACK(env->temp_size))
- expand_dataseg(env, words,
- _("memory chunk too big to fit into heap"));
+ if (env->tos - env->toh < words + B2STACK(env->temp_size)) {
+ if (expand_dataseg(env, words))
+ runtime_error(env, "%s",
+ _("memory chunk too big to fit into heap"));
+ }
ret = (char*) env_data_ref(env, env->temp_start) + env->temp_size;
if (ptr)
memmove(ret, ptr, size);
@@ -819,6 +931,26 @@ heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size)
return ret;
}
+void
+heap_obstack_reclaim(eval_environ_t env, size_t size)
+{
+ if (size > env->temp_size)
+ abort();
+ env->temp_size -= size;
+ env->toh -= B2STACK(size);
+}
+
+void
+heap_obstack_truncate(eval_environ_t env, size_t size)
+{
+ if (size == env->temp_size)
+ return;
+ else if (size > env->temp_size)
+ heap_obstack_grow(env, NULL, size - env->temp_size);
+ else
+ heap_obstack_reclaim(env, env->temp_size - size);
+}
+
void *
heap_obstack_base(eval_environ_t env)
{
@@ -854,7 +986,7 @@ pushn(eval_environ_t env, long n)
void
instr_locus(eval_environ_t env)
{
- env->locus.file = mf_c_val(get_immediate(env, 0), uint);
+ env->locus.file = mf_c_val(get_immediate(env, 0), ulong);
env->locus.line = mf_c_val(get_immediate(env, 1), uint);
if (PROG_TRACE_ENGINE)
prog_trace(env, "LOCUS");
@@ -865,8 +997,8 @@ void
dump_locus(prog_counter_t i)
{
printf("\"%s\" %lu",
- (char*) (dataseg + mf_cell_c_value(prog[i], size)),
- (unsigned long) mf_cell_c_value(prog[i+1], size));
+ (char*) (dataseg + mf_cell_c_value(prog[i], ulong)),
+ (unsigned long) mf_cell_c_value(prog[i+1], uint));
}
/* Stack manipulation instructions */
@@ -884,9 +1016,42 @@ instr_xchg(eval_environ_t env)
void
instr_dup(eval_environ_t env)
{
- if (PROG_TRACE_ENGINE)
+ if (PROG_TRACE_ENGINE)
prog_trace(env, "DUP");
- push(env, get_arg(env, 0));
+ push(env, (STKVAL)0L);
+ env->dataseg[env->tos+1] = env->dataseg[env->tos+2];
+}
+
+/*
+ * push COUNT
+ * push OFFSET
+ * dupn FRAME
+ *
+ * Pushes on stack COUNT stack words starting from OFFSET in the given
+ * FRAME, in the same order they appear on stack.
+ */
+void
+instr_dupn(eval_environ_t env)
+{
+ size_t frame = mf_c_val(get_immediate(env, 0), size);
+ long count = mf_c_val(get_arg(env, 1), long);
+ long off = mf_c_val(get_arg(env, 0), long);
+ size_t i;
+
+ if (count < 0)
+ runtime_error(env, _("dupn: negative count"));
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "DUPN frame=%zu off=%ld count=%zu",
+ frame, off, count);
+
+ adjust_stack(env, 2);
+
+ for (i = count; i > 0; i--) {
+ push(env, (STKVAL)0L);
+ env->dataseg[env->tos+1] = env->dataseg[env_base(env, frame) + off + i - 1];
+ }
+
+ advance_pc(env, 1);
}
void
@@ -925,7 +1090,7 @@ instr_memstk(eval_environ_t env)
push(env, env_base(env, frame) + off)
However, push() itself might cause dataseg expansion,
- which would change tos/toh pointers and render the previously
+ which would change tos/base pointers and render the previously
computed argument value invalid. The correct way is to push
a placeholder value first, thereby occupying stack slot and
eventually causing dataseg expansion, and then compute val
@@ -997,10 +1162,12 @@ instr_stkalloc(eval_environ_t env)
(unsigned long) env->tos,
(unsigned long) env->toh,
n));
- expand_dataseg(env, env->toh - (env->tos - n),
- _("Out of stack space; increase #pragma stacksize"));
+ if (expand_dataseg(env, env->toh - (env->tos - n)))
+ runtime_error(env, "%s",
+ _("Out of stack space; increase #pragma stacksize"));
}
env->tos -= n;
+ memset(env->dataseg + env->tos, 0, n * sizeof(env->dataseg[0]));
advance_pc(env, 1);
}
@@ -1135,7 +1302,7 @@ dump_symbol(prog_counter_t i)
(char *) (dataseg + mf_cell_c_value(prog[i], size)));
}
-/* Comparation instructions */
+/* Comparison instructions */
void
instr_eqn(eval_environ_t env)
{
@@ -1325,7 +1492,7 @@ instr_jmp(eval_environ_t env)
advance_pc(env, off+1);
}
-/* Longean instructions */
+/* Boolean instructions */
void
instr_not(eval_environ_t env)
{
@@ -1336,7 +1503,7 @@ instr_not(eval_environ_t env)
pushn(env, !v);
}
-/* Bitwise arithmetics */
+/* Bitwise arithmetic */
void
instr_logand(eval_environ_t env)
{
@@ -1588,7 +1755,61 @@ dump_regcomp(prog_counter_t i)
printf("%s", regex_flags_to_string(rtx->regflags,
buffer, sizeof buffer));
}
+
+void
+instr_sedcomp(eval_environ_t env)
+{
+ char * MFL_DATASEG str;
+ size_t index = mf_c_val(get_immediate(env, 0), size);
+ transform_t *tp = &transform_tab[index];
+ int flags = mf_c_val(get_immediate(env, 1), int);
+
+ get_string_arg(env, 0, &str);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "SUBCOMP %s %zu %d", str, index, flags);
+
+ advance_pc(env, 2);
+ adjust_stack(env, 1);
+
+ transform_free(*tp);
+
+ *tp = transform_compile(str, flags);
+ if (*tp == NULL)
+ runtime_error(env,
+ _("invalid transform string \"%s\": %s"),
+ str,
+ transform_error_string());
+
+ push(env, (STKVAL) index);
+}
+
+void
+dump_sedcomp(prog_counter_t i)
+{
+ size_t index = mf_cell_c_value(prog[i], size);
+ int flags = mf_cell_c_value(prog[i+1], int);
+ printf("%zu %d", index, flags);
+}
+
+void
+instr_sed(eval_environ_t env)
+{
+ char * MFL_DATASEG arg;
+ long i = mf_c_val(get_arg(env, 0), long);
+ char *res;
+
+ get_string_arg(env, 1, &arg);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "SED %s %ld", arg, i);
+
+ adjust_stack(env, 2);
+ res = transform_string(transform_tab[i], arg);
+ pushs(env, res);
+ free(res);
+}
+
void
instr_fnmatch(eval_environ_t env)
{
@@ -1711,7 +1932,7 @@ instr_result(eval_environ_t env)
SP(code),
SP(xcode),
SP(message));
-
+
trace("%s%s:%u: %s %s %s %s",
mailfromd_msgid(env->ctx),
ENV_LOC_FILE(env), ENV_LOC_LINE(env),
@@ -1725,11 +1946,12 @@ instr_result(eval_environ_t env)
if (xcode[0] == 0)
xcode = NULL;
- if (status == SMFIS_ACCEPT && (env->flags & ENVF_MSGMOD))
+ if (status == SMFIS_ACCEPT && env_msgmod_count(env))
runtime_warning(env, _("`accept' causes previous message "
"modification commands to be ignored; "
"call mmq_purge() prior to `accept', "
"to suppress this warning"));
+
env->status = status;
env->setreply(env->data, code, xcode, message);
advance_pc(env, 1);
@@ -1763,7 +1985,7 @@ instr_header(eval_environ_t env)
msgmod_opcode_str(opcode),
name, SP(value));
- env_msgmod(env, opcode, name, value, 1);
+ env_msgmod_append(env, opcode, name, value, 1);
}
void
@@ -1813,21 +2035,15 @@ instr_concat(eval_environ_t env)
}
void
-dump_adjust(prog_counter_t i)
-{
- printf("%lu ", mf_cell_c_value(prog[i], ulong));
-}
-
-void
instr_asgn(eval_environ_t env)
{
STKVAL val = get_arg(env, 1);
size_t dest = mf_c_val(get_arg(env, 0), size);
adjust_stack(env, 2);
if (PROG_TRACE_ENGINE)
- prog_trace(env, "ASGN %lu=%u",
+ prog_trace(env, "ASGN %lu=%lu",
(unsigned long) dest,
- mf_c_val(val, uint));
+ mf_c_val(val, ulong));
env->dataseg[dest] = val;
}
@@ -1835,7 +2051,7 @@ void
instr_catch(eval_environ_t env)
{
long off = mf_c_val(get_immediate(env, 0), long);
- unsigned toff = mf_c_val(get_immediate(env, 1), uint);
+ size_t toff = mf_c_val(get_immediate(env, 1), size);
size_t count = mf_c_val(env->dataseg[toff], size);
STKVAL *tab = (STKVAL *) (env->dataseg + toff + 1);
size_t i;
@@ -1880,7 +2096,6 @@ instr_throw(eval_environ_t env)
{
unsigned long n = mf_c_val(get_immediate(env, 0), ulong);
size_t off = mf_c_val(get_arg(env, 0), size);
- advance_pc(env, 1);
adjust_stack(env, 1);
if (n > exception_count)
runtime_error(env, _("invalid exception number: %lu"), n);
@@ -1892,8 +2107,8 @@ instr_throw(eval_environ_t env)
void
dump_throw(prog_counter_t i)
{
- printf("%s (%u)", mf_exception_str(mf_cell_c_value(prog[i], uint)),
- mf_cell_c_value(prog[i], uint));
+ printf("%s (%lu)", mf_exception_str(mf_cell_c_value(prog[i], ulong)),
+ mf_cell_c_value(prog[i], ulong));
}
@@ -1915,7 +2130,6 @@ instr_return(eval_environ_t env)
if (PROG_TRACE_ENGINE)
prog_trace(env, "RETURN");
env_leave_frame(env, 0);
- env->pc--;
}
void
@@ -1924,25 +2138,40 @@ instr_retcatch(eval_environ_t env)
prog_counter_t pc = env->pc;
if (PROG_TRACE_ENGINE)
prog_trace(env, "RETCATCH");
- env_leave_frame(env, 2);
+ env_leave_frame(env, 3);
env->pc = pc;
}
+/*
+ * Return true if the given bit is set in the exception mask starting at
+ * offset off.
+ */
+static inline int
+env_exmask_isset(eval_environ_t env, size_t off, int bit)
+{
+ return ((bitmask_bits_t)mf_c_val(env->dataseg[off + 1 + BIT_WORD(bit)], uint)) & BIT_MASK(bit);
+}
+
void
instr_saveex(eval_environ_t env)
{
- unsigned off = mf_c_val(get_immediate(env, 0), uint);
+ size_t off = mf_c_val(get_immediate(env, 0), size);
size_t count = mf_c_val(env->dataseg[off], size);
- STKVAL *tab = (STKVAL *) (env->dataseg + off + 1);
size_t i;
if (PROG_TRACE_ENGINE)
- prog_trace(env, "SAVEEX %x (%lu ex.)", off,
+ prog_trace(env, "SAVEEX %lx (%lu ex.)",
+ (unsigned long) off,
(unsigned long) count);
advance_pc(env, 1);
+ /*
+ * Note: in the loop that follows each push can lead to data segment
+ * expansion, therefore the actual start address of the exception table
+ * needs to be computed in each iteration using env_exmask_isset.
+ */
for (i = 0; i < count * NBMBITS; i++)
- if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i)) {
+ if (env_exmask_isset(env, off, i)) {
mu_debug(MF_SOURCE_PROG, MU_DEBUG_TRACE9,
("Push Exception: %lu %lu <- pc=%lu, tos=%lu, base=%lu",
(unsigned long) i,
@@ -1974,14 +2203,14 @@ dump_saveex(prog_counter_t ctr)
void
instr_restex(eval_environ_t env)
{
- unsigned off = mf_c_val(pop(env), uint);
+ size_t off = mf_c_val(pop(env), size);
size_t count = mf_c_val(env->dataseg[off], size);
STKVAL *tab = (STKVAL *) (env->dataseg + off + 1);
size_t i;
if (PROG_TRACE_ENGINE)
- prog_trace(env, "RESTEX %x (%lu ex.)",
- off, (unsigned long) count);
+ prog_trace(env, "RESTEX %lx (%lu ex.)",
+ (unsigned long) off, (unsigned long) count);
if (!count)
return;
i = count * NBMBITS - 1;
@@ -2003,6 +2232,12 @@ instr_restex(eval_environ_t env)
}
void
+dump_adjust(prog_counter_t i)
+{
+ printf("%lu ", mf_cell_c_value(prog[i], ulong));
+}
+
+void
instr_adjust(eval_environ_t env)
{
long nargs = mf_c_val(get_immediate(env, 0), long);
@@ -2012,6 +2247,30 @@ instr_adjust(eval_environ_t env)
advance_pc(env, 1);
}
+/*
+ * Adjust stack after return from a variadic function.
+ *
+ * When such function is called, it is passed the actual number
+ * of arguments in its first (hidden) argument.
+ *
+ * The inline parameter to adjustx gives the number of arguments the
+ * function is declared to take (both mandatory and optional). These
+ * are always on stack, so this value is used instead of the actual
+ * number if the latter is less than the former. This can happen,
+ * e.g. if a function takes both optional and variable arguments and
+ * is given only mandatory ones.
+ */
+void
+instr_adjustx(eval_environ_t env)
+{
+ long nargs = mf_c_val(get_immediate(env, 0), long);
+ size_t n = mf_c_val(pop(env), size);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "ADJUSTX %zu %ld", n, nargs);
+ adjust_stack(env, n < nargs ? nargs : n);
+ advance_pc(env, 1);
+}
+
void
instr_popreg(eval_environ_t env)
{
@@ -2082,14 +2341,15 @@ instr_xlat(eval_environ_t env)
void
instr_xlats(eval_environ_t env)
{
- unsigned long i;
- unsigned long count = mf_c_val(get_immediate(env, 0), uint);
+ size_t i;
+ size_t count = mf_c_val(get_immediate(env, 0), size);
size_t off = mf_c_val(get_immediate(env, 1), size);
STKVAL *tab = (STKVAL *) (env->dataseg + off);
char *str = (char*) env_data_ref(env, mf_c_val(env->reg, size));
if (PROG_TRACE_ENGINE)
- prog_trace(env, "XLATS %lu %lu", count, (unsigned long) off);
+ prog_trace(env, "XLATS %lu %lu",
+ (unsigned long) count, (unsigned long) off);
advance_pc(env, 2);
for (i = 0; i < count; i += 2) {
if (strcmp((char*)(env->dataseg + mf_c_val(tab[i], size)), str) == 0) {
@@ -2104,12 +2364,12 @@ instr_xlats(eval_environ_t env)
void
dump_xlat(prog_counter_t i)
{
- unsigned long j;
- unsigned long count = mf_cell_c_value(prog[i], ulong);
+ size_t j;
+ size_t count = mf_cell_c_value(prog[i], size);
size_t off = mf_cell_c_value(prog[i+1], size);
STKVAL *tab = (STKVAL *) (dataseg + off);
- printf("%lu %lu ", count, (unsigned long) off);
+ printf("%lu %lu ", (unsigned long) count, (unsigned long) off);
for (j = 0; j < count; j += 2)
printf("(%ld %ld) ", mf_c_val(tab[j], long), mf_c_val(tab[j+1], long));
}
@@ -2211,6 +2471,7 @@ env_init(eval_environ_t env)
env->status = SMFIS_CONTINUE;
env->tos = datasize + env->stack_size - 1;
env->base = 0;
+ env->pc = 0;
mf_c_val(env->reg, long) = 0; //FIXME
env->matches = NULL;
@@ -2242,17 +2503,9 @@ env_init_dataseg(eval_environ_t env)
}
void
-env_make_frame0(eval_environ_t env)
-{
- push(env, (STKVAL) 0);
- push(env, (STKVAL) (env->base - env->tos));
- env->base = env->tos;
-}
-
-void
env_make_frame(eval_environ_t env)
{
- push(env, (STKVAL) (env->pc + 1));
+ push(env, (STKVAL) env->pc);
push(env, (STKVAL) (env->base - env->tos));
env->base = env->tos;
}
@@ -2320,9 +2573,10 @@ env_vsprintf(eval_environ_t env, const char *biname,
while (1) {
size_t k;
size_t size;
+ va_list apc;
if (env->tos == env->toh)
- if (expand_dataseg(env, B2STACK(strlen(fmt)), NULL)) {
+ if (expand_dataseg(env, B2STACK(strlen(fmt)))) {
env_vsprintf_error(fmt, ap);
break;
}
@@ -2334,17 +2588,19 @@ env_vsprintf(eval_environ_t env, const char *biname,
if (biname) {
n = snprintf(s, size, "%s: ", biname);
if (n >= size) {
- n += strlen(fmt); /* rough estimation */
- if (expand_dataseg(env, B2STACK(n), NULL)) {
+ n += strlen(fmt); /* rough estimate */
+ if (expand_dataseg(env, B2STACK(n))) {
env_vsprintf_error(fmt, ap);
break;
}
continue;
}
}
- k = vsnprintf(s + n, size - n, fmt, ap);
+ va_copy(apc, ap);
+ k = vsnprintf(s + n, size - n, fmt, apc);
+ va_end(apc);
if (k >= size) {
- if (expand_dataseg(env, B2STACK(k), NULL)) {
+ if (expand_dataseg(env, B2STACK(k))) {
env_vsprintf_error(fmt, ap);
break;
}
@@ -2364,13 +2620,15 @@ env_throw_0(eval_environ_t env, mf_exception status, size_t off)
{
prog_counter_t pc;
- env_function_cleanup_flush(env, NULL);
+ env_function_cleanup_flush(env, CLEANUP_THROW);
if (status > exception_count)
runtime_error(env, _("unknown exception: %d: %s"),
status, (char*) env_data_ref(env, off));
pc = env->catch_ctx[status].pc;
if (pc) {
+ prog_counter_t throw_pc = env->pc;
+
/* Restore tos */
env->tos = TOS_INVARIANT(env, env->catch_ctx[status].tos);
env->base = TOS_INVARIANT(env, env->catch_ctx[status].base);
@@ -2379,6 +2637,7 @@ env_throw_0(eval_environ_t env, mf_exception status, size_t off)
/* Fixup the program counter */
env->pc = pc - 1;
/* Generate normal entry frame */
+ push(env, (STKVAL) throw_pc);
push(env, (STKVAL) off);
env_push_number(env, status);
env_make_frame(env);
@@ -2406,6 +2665,9 @@ env_throw_bi(eval_environ_t env, mf_exception status, const char *biname,
{
va_list ap;
size_t off;
+
+ heap_obstack_cancel(env);
+
va_start(ap, fmt);
off = env_vsprintf(env, biname, fmt, ap);
va_end(ap);
@@ -2453,7 +2715,7 @@ env_capture_start(eval_environ_t env)
/* Drop any previously captured message data */
env_free_captured(env);
mu_header_destroy(&env->header);
- /* Truncate existing stream and reposition to its
+ /* Truncate existing stream and re-position to its
beginning */
rc = mu_stream_truncate(env->stream, 0);
if (rc == 0 &&
@@ -2595,7 +2857,7 @@ env_get_header(eval_environ_t env, mu_header_t *hdr)
}
text[total] = 0;
- /* FIXME: Reposition the stream at its end.
+ /* FIXME: Re-position the stream at its end.
This call may happen in the middle of capturing
so I have to make sure to not disturb the capturing
process.
@@ -2622,19 +2884,30 @@ env_get_header(eval_environ_t env, mu_header_t *hdr)
*hdr = env->header;
return 0;
}
+
+/* MMQ */
void
-env_clear_msgmod(eval_environ_t env)
+env_msgmod_clear(eval_environ_t env)
{
if (PROG_TRACE_ENGINE)
prog_trace(env, "Clearing message modification queue");
- env->msgmod(env->data, NULL);
- env->flags &= ~ENVF_MSGMOD;
+ if (env->msgmod)
+ env->msgmod(env->data, NULL);
+ mu_list_clear(env->mmq);
}
void
-env_msgmod(eval_environ_t env, enum msgmod_opcode opcode,
- const char *name, const char *value, unsigned idx)
+destroy_msgmod_closure(void *item)
+{
+ struct msgmod_closure *cmd = item;
+ free(cmd->name);
+ free(cmd->value);
+}
+
+void
+env_msgmod_append(eval_environ_t env, enum msgmod_opcode opcode,
+ const char *name, const char *value, unsigned idx)
{
struct msgmod_closure *cp = mu_alloc(sizeof *cp);
@@ -2647,8 +2920,35 @@ env_msgmod(eval_environ_t env, enum msgmod_opcode opcode,
cp->name = name ? mu_strdup(name) : NULL;
cp->value = value ? mu_strdup(value) : NULL;
cp->idx = idx;
- env->msgmod(env->data, cp);
- env->flags |= ENVF_MSGMOD;
+
+ if (!env->mmq) {
+ mu_list_create(&env->mmq);
+ mu_list_set_destroy_item(env->mmq, destroy_msgmod_closure);
+ }
+ mu_debug(MF_SOURCE_ENGINE, MU_DEBUG_TRACE5,
+ ("adding msgmod_closure: %s \"%s\" %s %u",
+ msgmod_opcode_str(cp->opcode),
+ SP(cp->name), SP(cp->value), cp->idx));
+ if (env->msgmod)
+ env->msgmod(env->data, cp);
+ mu_list_append(env->mmq, cp);