aboutsummaryrefslogtreecommitdiff
path: root/mfd/prog.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2008-02-10 14:08:36 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2008-02-10 14:08:36 +0000
commita1871918823166cac9f0c12164ba27cb3b1f0c2c (patch)
tree7260d118aa5af6a8f006f9b2dac6cabde782db8c /mfd/prog.c
parent455b645247bdc0f01239b2352a9d8f07f446024f (diff)
downloadmailfromd-a1871918823166cac9f0c12164ba27cb3b1f0c2c.tar.gz
mailfromd-a1871918823166cac9f0c12164ba27cb3b1f0c2c.tar.bz2
Merged HEAD from branches/gmach
git-svn-id: file:///svnroot/mailfromd/trunk@1612 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'mfd/prog.c')
-rw-r--r--mfd/prog.c2316
1 files changed, 2316 insertions, 0 deletions
diff --git a/mfd/prog.c b/mfd/prog.c
new file mode 100644
index 00000000..2a33ba22
--- /dev/null
+++ b/mfd/prog.c
@@ -0,0 +1,2316 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2006, 2007, 2008 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
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define MF_SOURCE_NAME MF_SOURCE_PROG
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <setjmp.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <fnmatch.h>
+#include <syslog.h>
+#include "mailfromd.h"
+#include "prog.h"
+#include "optab.h"
+#define DEFINE_BUILTIN_MODULE
+#include "builtin.h"
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+#include <obstack.h>
+
+
+/* Code generation support */
+#define CODE_INITIAL 128
+#define CODE_INCREMENT 128
+
+static prog_counter_t pc, pmax;
+instr_t *prog;
+size_t stack_size = 4096;
+
+/* Data segment */
+STKVAL *dataseg;
+size_t datasize;
+
+/* Table of relocatable entries in the data segment */
+size_t *dataseg_reloc;
+size_t dataseg_reloc_count;
+
+/* Regular expressions */
+struct rt_regex *regtab;
+size_t regcount;
+struct obstack regstk;
+
+
+void
+code_init()
+{
+ pmax = CODE_INITIAL;
+ prog = calloc(pmax, sizeof prog[0]);
+ if (!prog) {
+ mu_error(_("Not enough memory to compile the program"));
+ exit(1);
+ }
+}
+
+prog_counter_t
+code_get_counter()
+{
+ return pc;
+}
+
+prog_counter_t
+code_reserve(size_t count)
+{
+ prog_counter_t ret = pc;
+ if (pc + count > pmax) {
+ size_t incr = (pc + count - pmax + CODE_INCREMENT - 1)
+ / CODE_INCREMENT;
+ pmax += incr * CODE_INCREMENT;
+ prog = realloc(prog, pmax*sizeof prog[0]);
+ if (!prog) {
+ mu_error(_("Not enough memory to compile the program"));
+ exit(1);
+ }
+ }
+ pc += count;
+ return ret;
+}
+
+prog_counter_t
+code_instr(const instr_t ptr)
+{
+ if (pc >= pmax) {
+ pmax += CODE_INCREMENT;
+ prog = realloc(prog, pmax*sizeof prog[0]);
+ if (!prog) {
+ mu_error(_("Not enough memory to compile the program"));
+ exit(1);
+ }
+ }
+ prog[pc] = ptr;
+ return pc++;
+}
+
+prog_counter_t
+code_op(unsigned code)
+{
+ return code_instr((instr_t)code);
+}
+
+prog_counter_t
+code_immediate(const void *ptr)
+{
+ return code_instr(ptr);
+}
+
+void
+code_put(prog_counter_t pos, void *ptr)
+{
+ assert(pos < pmax);
+ prog[pos] = (instr_t) ptr;
+}
+
+instr_t
+code_peek(prog_counter_t pos)
+{
+ assert(pos < pmax);
+ return prog[pos];
+}
+
+
+/* Regexps*/
+
+void
+register_regex(struct sym_regex *rp)
+{
+ struct rt_regex r;
+ if (regcount == 0)
+ obstack_init(&regstk);
+ r.compiled = 0; /* Will be compiled later */
+ r.expr = rp->lit ? rp->lit->off : 0;
+ r.regflags = rp->regflags;
+ rp->index = regcount++;
+ obstack_grow(&regstk, &r, sizeof r);
+}
+
+void
+finalize_regex()
+{
+ if (regcount)
+ regtab = obstack_finish(&regstk);
+}
+
+
+unsigned long prog_trace_option = 0;
+#define PROG_TRACE_ENGINE (prog_trace_option & BUILTIN_MASK_prog)
+
+void
+enable_module_trace(char *name)
+{
+ struct builtin_module *mod;
+
+ for (mod = builtin_module; mod->name; mod++)
+ if (strcmp(mod->name, name) == 0) {
+ prog_trace_option |= mod->mask;
+ return;
+ }
+
+ mu_error(_("No such module: %s"), name);
+}
+
+void
+disable_module_trace(char *name)
+{
+ struct builtin_module *mod;
+
+ for (mod = builtin_module; mod->name; mod++)
+ if (strcmp(mod->name, name) == 0) {
+ prog_trace_option &= ~mod->mask;
+ return;
+ }
+
+ mu_error(_("No such module: %s"), name);
+}
+
+void
+enable_prog_trace(const char *modlist)
+{
+ if (strcmp(modlist, "all") == 0) {
+ prog_trace_option = ULONG_MAX;
+ return;
+ } else {
+ char *modcopy, *s;
+ modcopy = xstrdup(modlist);
+
+ for (s = strtok(modcopy,","); s; s = strtok(NULL, ","))
+ enable_module_trace(s);
+ free(modcopy);
+ }
+}
+
+void
+disable_prog_trace(const char *modlist)
+{
+ if (strcmp(modlist, "all") == 0) {
+ prog_trace_option = 0;
+ return;
+ } else {
+ char *modcopy, *s;
+ modcopy = xstrdup(modlist);
+
+ for (s = strtok(modcopy,","); s; s = strtok(NULL, ","))
+ disable_module_trace(s);
+ free(modcopy);
+ }
+}
+
+/* ========================================================================
+ Drzewa w górę, rzeki w dół.
+
+ Jacek Kaczmarski.
+ "Upadek Ikara"
+ ======================================================================== */
+
+/* Run-time evaluation */
+
+struct eval_environ {
+ prog_counter_t pc; /* Program counter */
+
+ prog_counter_t tos; /* Top of stack:
+ toh <= tos <= datasize + stack_size */
+ prog_counter_t toh; /* Top of heap:
+ datasize <= toh <= tos */
+
+ prog_counter_t base; /* Base pointer */
+ STKVAL reg; /* General purpose register */
+ STKVAL *dataseg; /* Data space */
+ size_t stack_size; /* Size of allocated stack + heap */
+
+ struct locus locus; /* Program locus corresponding to PC */
+
+ /* Temporary heap space */
+ size_t temp_start;
+ size_t temp_size;
+
+ /* Sendmail interaction data: */
+ SMFICTX *ctx; /* Milter Context */
+ void *data; /* MTA symbol table */
+ /* methods to access the latter */
+ char *(*getsym)(void *data, char *str);
+ int (*setreply)(void *data, char *code, char *xcode, char *message);
+ void (*msgmod)(void *data, struct msgmod_closure *c);
+
+ /* Regular expression matching */
+ regmatch_t *matches; /* Match map */
+ size_t matchcount; /* Number of used entries in matches */
+ size_t matchsize; /* Total number of entries in matches */
+ char *string; /* Last matched string */
+
+ mu_stream_t stream; /* Capture stream */
+
+ /* Non-local exits */
+ prog_counter_t defcatch[mf_status_count];
+ prog_counter_t catch[mf_status_count];
+
+ /* Built-in private data */
+ void **bi_priv_array;
+
+ /* Exit information */
+ sfsistat status; /* Program exit status */
+ jmp_buf x_jmp; /* Return point for runtime errors */
+ jmp_buf catch_jmp; /* Return point for throws */
+};
+
+void
+advance_pc(eval_environ_t env, unsigned cnt)
+{
+ env->pc += cnt;
+}
+
+void
+adjust_stack(eval_environ_t env, unsigned cnt)
+{
+ env->tos += cnt;
+}
+
+void
+unroll_stack(eval_environ_t env, unsigned cnt)
+{
+ env->tos -= cnt;
+}
+
+size_t
+env_base(eval_environ_t env, size_t frame)
+{
+ size_t base = env->base;
+ for (; frame; frame--)
+ base = (size_t) env->dataseg[base + 1] + base + 1;
+ return base;
+}
+
+void
+env_var_inc(eval_environ_t env, size_t off)
+{
+ ++*env_data_ref(env, off);
+}
+
+const struct locus *
+env_get_locus(eval_environ_t env)
+{
+ return &env->locus;
+}
+
+mu_stream_t
+env_get_stream(eval_environ_t env)
+{
+ return env->stream;
+}
+
+#define STACK_EXPAND_BLOCK 64
+
+static int
+expand_dataseg(eval_environ_t env, size_t count, const char *errtext)
+{
+ STKVAL *newds;
+ count = ((count + STACK_EXPAND_BLOCK - 1) / STACK_EXPAND_BLOCK)
+ * STACK_EXPAND_BLOCK;
+ newds = realloc(env->dataseg,
+ (env->stack_size + datasize + count)
+ * sizeof env->dataseg[0]);
+ if (!newds) {
+ if (errtext)
+ runtime_error(env, errtext);
+ else
+ return 1;
+ }
+
+ env->dataseg = newds;
+ env->stack_size += count;
+ env->tos += count;
+ env->base += count;
+ memmove(newds + env->tos, newds + env->tos - count,
+ (datasize + env->stack_size - env->tos)
+ * sizeof newds[0]);
+ mu_error(_("Warning: stack segment expanded, new size=%lu"),
+ (unsigned long) env->stack_size);
+ return 0;
+}
+
+
+void
+prog_trace(eval_environ_t env, const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ va_end(ap);
+ logmsg(LOG_DEBUG, "%4lu: %s:%lu: %s",
+ (unsigned long) env->pc,
+ env->locus.file, (unsigned long) env->locus.line,
+ buf);
+}
+
+void instr_funcall(eval_environ_t env);
+void instr_locus(eval_environ_t env);
+
+void
+runtime_stack_trace(eval_environ_t env)
+{
+ size_t base;
+
+ mu_error(_("Stack trace:"));
+ for (base = env->base; base < datasize + env->stack_size - 4;
+ base = (size_t) env->dataseg[base + 1] + base + 1) {
+ int i;
+ prog_counter_t pc = (prog_counter_t)env->dataseg[base + 2] - 1;
+ char *name;
+ struct locus *ploc = NULL, loc;
+
+ if (prog[pc-2] == instr_funcall) {
+ name = (char*)(env->dataseg + (size_t) prog[pc-1]);
+ pc -= 2;
+ } else {
+ name = "(in catch)";
+ pc -= 3;
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (pc > i + 3
+ && prog[pc - i - 3] == instr_locus) {
+ loc.file = (char*)(env->dataseg
+ + (size_t) prog[pc - i - 2]);
+ loc.line = (size_t) prog[pc - i - 1];
+ ploc = &loc;
+ break;
+ }
+ }
+
+ if (ploc)
+ mu_error("%04lu: %s:%lu: %s",
+ (unsigned long) pc,
+ ploc->file, (unsigned long) ploc->line,
+ name);
+ else
+ mu_error("%04lu: %s",
+ (unsigned long) pc,
+ name);
+ }
+ mu_error(_("Stack trace finishes"));
+}
+
+void
+runtime_warning(eval_environ_t env, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+ char buf[512];
+
+ n = snprintf(buf, sizeof buf, _("RUNTIME WARNING near %s:%lu: "),
+ env->locus.file, (unsigned long) env->locus.line);
+ va_start(ap, fmt);
+ vsnprintf(buf + n, sizeof buf - n, fmt, ap);
+ va_end(ap);
+ mu_error(buf);
+}
+
+void
+runtime_error(eval_environ_t env, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+ char buf[512];
+
+ n = snprintf(buf, sizeof buf, _("RUNTIME ERROR near %s:%lu: "),
+ env->locus.file, (unsigned long) env->locus.line);
+ va_start(ap, fmt);
+ vsnprintf(buf + n, sizeof buf - n, fmt, ap);
+ va_end(ap);
+ mu_error(buf);
+ if (stack_trace_option)
+ runtime_stack_trace(env);
+ env->status = SMFIS_TEMPFAIL; /* FIXME */
+ longjmp(env->x_jmp, 1);
+}
+
+void *
+get_immediate(eval_environ_t env, unsigned n)
+{
+ return prog[env->pc + n + 1];
+}
+
+const char *
+get_literal(eval_environ_t env, unsigned n)
+{
+ return (char*)(env->dataseg + (size_t) get_immediate(env, n));
+}
+
+STKVAL
+get_arg(eval_environ_t env, unsigned n)
+{
+ return env->dataseg[env->tos + n + 1];
+}
+
+char *
+get_string_arg(eval_environ_t env, unsigned n)
+{
+ return (char*) (env->dataseg + (size_t) get_arg(env, n));
+}
+
+size_t
+get_numeric_arg(eval_environ_t env, unsigned n)
+{
+ return (size_t) get_arg(env, n);
+}
+
+void
+push(eval_environ_t env, STKVAL val)
+{
+ if (env->tos < env->toh)
+ runtime_error(env, "INTERNAL ERROR at %s:%d, please report",
+ __FILE__, __LINE__);
+
+ if (env->tos == env->toh) {
+ debug2(100, "tos=%lu, toh=%lu",
+ (unsigned long) env->tos,
+ (unsigned long) env->toh);
+ expand_dataseg(env, 1,
+ _("Out of stack space; increase #pragma stacksize"));
+ }
+ env->dataseg[env->tos--] = val;
+}
+
+STKVAL
+pop(eval_environ_t env)
+{
+ if (env->tos == datasize + env->stack_size - 1)
+ runtime_error(env, _("Stack underflow"));
+ return env->dataseg[++env->tos];
+}
+
+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"));
+ env->toh += words;
+ return off;
+}
+
+size_t
+heap_reserve(eval_environ_t env, size_t size)
+{
+ return heap_reserve_words(env, B2STACK(size));
+}
+
+STKVAL
+heap_tempspace(eval_environ_t env, size_t size)
+{
+ size_t words = B2STACK(size);
+
+ if (env->toh + words > env->tos)
+ expand_dataseg(env, words,
+ _("Heap overrun; increase #pragma stacksize"));
+ return env->dataseg + env->toh;
+}
+
+void
+heap_obstack_begin(eval_environ_t env)
+{
+ env->temp_start = env->toh;
+ env->temp_size = 0;
+}
+
+STKVAL
+heap_obstack_finish(eval_environ_t env)
+{
+ size_t ret = env->temp_start;
+ env->temp_start = 0;
+ env->temp_size = 0;
+ return (STKVAL) ret;
+}
+
+void *
+heap_obstack_grow(eval_environ_t env, void *ptr, size_t size)
+{
+ size_t words = B2STACK(size);
+ char *ret;
+
+ if (env->tos - env->toh < words + B2STACK(env->temp_size))
+ expand_dataseg(env, words,
+ _("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);
+ env->temp_size += size;
+ env->toh += words;
+ return ret;
+}
+
+STKVAL *
+env_data_ref(eval_environ_t env, size_t off)
+{
+ return env->dataseg + off;
+}
+
+void
+pushs(eval_environ_t env, const char *s)
+{
+ size_t off = heap_reserve(env, strlen(s) + 1);
+ strcpy((char*) env_data_ref(env, off), s);
+ push(env, (STKVAL) off);
+}
+
+/* Auxiliary instructions */
+
+void
+instr_locus(eval_environ_t env)
+{
+ env->locus.file = get_literal(env, 0);
+ env->locus.line = (size_t) get_immediate(env, 1);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LOCUS");
+ advance_pc(env, 2);
+}
+
+void
+dump_locus(prog_counter_t i)
+{
+ printf("\"%s\" %lu", (char*) (dataseg + (size_t)prog[i]),
+ (unsigned long) (size_t) prog[i+1]);
+}
+
+/* Stack manipulation instructions */
+void
+instr_xchg(eval_environ_t env)
+{
+ prog_counter_t p = env->tos + 1;
+ STKVAL tmp = env->dataseg[p];
+ env->dataseg[p] = env->dataseg[p + 1];
+ env->dataseg[p + 1] = tmp;
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "XCHG");
+}
+
+void
+instr_dup(eval_environ_t env)
+{
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "DUP");
+ push(env, get_arg(env, 0));
+}
+
+void
+instr_pop(eval_environ_t env)
+{
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "POP");
+ pop(env);
+}
+
+void
+instr_push(eval_environ_t env)
+{
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "PUSH %x", get_immediate(env, 0));
+ push(env, get_immediate(env, 0));
+ advance_pc(env, 1);
+}
+
+void
+dump_push(prog_counter_t i)
+{
+ printf("%lx", (unsigned long) prog[i]);
+}
+
+void
+instr_memstk(eval_environ_t env)
+{
+ size_t frame = (size_t) get_immediate(env, 0);
+ long off = (long) get_immediate(env, 1);
+ size_t val = env_base(env, frame) + off;
+ advance_pc(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "MEMSTK %lu(%ld)=%lu",
+ (unsigned long) frame, off,
+ (unsigned long) val);
+ push(env, (STKVAL) val);
+}
+
+void
+dump_memstk(prog_counter_t i)
+{
+ printf("%lu(%ld)", (unsigned long) prog[i], (long) prog[i+1]);
+}
+
+void
+instr_deref(eval_environ_t env)
+{
+ size_t off = (size_t) get_arg(env, 0);
+ STKVAL val = env->dataseg[off];
+ adjust_stack(env, 1);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "DEREF %lu=%lu (%p)",
+ (unsigned long) off,
+ (unsigned long) val, val);
+ push(env, val);
+}
+
+void
+instr_stkalloc(eval_environ_t env)
+{
+ unsigned n = (unsigned) get_immediate(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "STKALLOC %p", n);
+ env->tos -= n;
+ advance_pc(env, 1);
+}
+
+void
+dump_stkalloc(prog_counter_t i)
+{
+ printf("%u", (unsigned)prog[i]);
+}
+
+void
+instr_backref(eval_environ_t env)
+{
+ unsigned n = (unsigned) get_immediate(env, 0);
+ size_t matchlen;
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "BACKREF %u", (unsigned)get_immediate(env, 0));
+ advance_pc(env, 1);
+
+ if (!env->matches || !env->string) {
+ /* FIXME: Try to throw exception first:
+ env_throw(env, mf_no_regex);
+ */
+ runtime_error(env, _("No previous regular expression"));
+ }
+ if (n > env->matchcount) {
+ /* FIXME: See above */
+ runtime_error(env, _("Invalid back-reference number"));
+ }
+
+ if (env->matches[n].rm_so == -1) {
+ push(env, 0);
+ } else {
+ char *s;
+ size_t off;
+
+ matchlen = env->matches[n].rm_eo - env->matches[n].rm_so;
+ off = heap_reserve(env, matchlen + 1);
+ s = (char*) env_data_ref(env, off);
+ memcpy(s, env->string + env->matches[n].rm_so, matchlen);
+ s[matchlen] = 0;
+ push(env, (STKVAL) off);
+ }
+}
+
+void
+dump_backref(prog_counter_t i)
+{
+ printf("%u", (unsigned) prog[i]);
+}
+
+/* Type conversion instructions */
+void
+instr_ston(eval_environ_t env)
+{
+ char *s = get_string_arg(env, 0);
+ char *p;
+ long v = strtol(s, &p, 0);
+
+ adjust_stack(env, 1);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "STON %s", s);
+ if (*p)
+ env_throw(env, mf_ston_conv,
+ "Cannot convert stack value to number (stopped at %8.8s)",
+ p);
+
+ push(env, (STKVAL) v);
+}
+
+void
+instr_ntos(eval_environ_t env)
+{
+ long v = (long) get_numeric_arg(env, 0);
+ char buf[NUMERIC_BUFSIZE_BOUND];
+
+ adjust_stack(env, 1);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "NTOS %ld", v);
+
+ snprintf(buf, sizeof buf, "%ld", v);
+ pushs(env, buf);
+}
+
+/* Evaluation instructions */
+void
+instr_cmp(eval_environ_t env)
+{
+ long l = (long) get_numeric_arg(env, 1);
+ long r = (long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "CMP %ld %ld", l, r);
+ push(env, (STKVAL) (l == r));
+}
+
+void
+instr_symbol(eval_environ_t env)
+{
+ char *symbol = (char *) get_literal(env, 0);
+ char *s = env->getsym(env->data, symbol);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "SYMBOL %s", symbol);
+ if (!s)
+ env_throw(env, mf_macroundef, _("Macro not defined: %s"),
+ symbol);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "%s dereferenced to %s", symbol, s);
+
+ advance_pc(env, 1);
+
+ pushs(env, s);
+}
+
+void
+dump_symbol(prog_counter_t i)
+{
+ printf("%08lx %s", (unsigned long) prog[i],
+ (char *) (dataseg + (size_t) prog[i]));
+}
+
+/* Comparation instructions */
+void
+instr_eqn(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "EQN %ld %ld", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (a == b));
+}
+
+void
+instr_eqs(eval_environ_t env)
+{
+ char *a = get_string_arg(env, 1);
+ char *b = get_string_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "EQS %s %s", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (strcmp(a, b) == 0));
+}
+
+void
+instr_nen(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "NEN %ld %ld", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (a != b));
+}
+
+void
+instr_nes(eval_environ_t env)
+{
+ char *a = get_string_arg(env, 1);
+ char *b = get_string_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "NES %s %s", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) strcmp(a, b));
+}
+
+void
+instr_ltn(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LTN %ld %ld", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (a < b));
+}
+
+void
+instr_lts(eval_environ_t env)
+{
+ char *a = get_string_arg(env, 1);
+ char *b = get_string_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LTS %s %s", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (strcmp(a, b) < 0));
+}
+
+void
+instr_len(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LEN %ld %ld", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (a <= b));
+}
+
+void
+instr_les(eval_environ_t env)
+{
+ char *a = get_string_arg(env, 1);
+ char *b = get_string_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LES %s %s", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (strcmp(a, b) <= 0));
+}
+
+void
+instr_gtn(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "GTN %ld %ld", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (a > b));
+}
+
+void
+instr_gts(eval_environ_t env)
+{
+ char *a = get_string_arg(env, 1);
+ char *b = get_string_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "GTS %s %s", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (strcmp(a, b) > 0));
+}
+
+void
+instr_gen(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "GEN %ld %ld", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (a >= b));
+}
+
+void
+instr_ges(eval_environ_t env)
+{
+ char *a = get_string_arg(env, 1);
+ char *b = get_string_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "GES %s %s", a, b);
+ adjust_stack(env, 2);
+ push(env, (STKVAL) (strcmp(a, b) >= 0));
+}
+
+/* Jump instructions */
+void
+instr_bz(eval_environ_t env)
+{
+ long v = (long) get_numeric_arg(env, 0);
+ long off = (long) get_immediate(env, 0);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "BZ %ld (%ld)", off, v);
+ adjust_stack(env, 1);
+ if (v == 0)
+ advance_pc(env, off);
+ advance_pc(env, 1);
+}
+
+void
+dump_branch (prog_counter_t i)
+{
+ printf("%ld (%ld)", (long) prog[i], i + (long) prog[i] + 1);
+}
+
+void
+instr_bnz(eval_environ_t env)
+{
+ long v = (long) get_numeric_arg(env, 0);
+ long off = (long) get_immediate(env, 0);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "BNZ %ld (%ld)", off, v);
+ adjust_stack(env, 1);
+ if (v != 0)
+ advance_pc(env, off);
+ advance_pc(env, 1);
+}
+
+void
+instr_jmp(eval_environ_t env)
+{
+ long off = (long) get_immediate(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "JMP %ld", off);
+ advance_pc(env, off+1);
+}
+
+/* Boolean instructions */
+void
+instr_not(eval_environ_t env)
+{
+ long v = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "NOT %ld", v);
+ adjust_stack(env, 1);
+ push(env, (STKVAL) !v);
+}
+
+/* Bitwise arithmetics */
+void
+instr_logand(eval_environ_t env)
+{
+ unsigned long a = (unsigned long) get_numeric_arg(env, 1);
+ unsigned long b = (unsigned long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LOGAND %lu %lu", a, b);
+ push(env, (STKVAL) (a & b));
+}
+
+void
+instr_logor(eval_environ_t env)
+{
+ unsigned long a = (unsigned long) get_numeric_arg(env, 1);
+ unsigned long b = (unsigned long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LOGOR %lu %lu", a, b);
+ push(env, (STKVAL) (a | b));
+}
+
+void
+instr_logxor(eval_environ_t env)
+{
+ unsigned long a = (unsigned long) get_numeric_arg(env, 1);
+ unsigned long b = (unsigned long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LOGXOR %lu %lu", a, b);
+ push(env, (STKVAL) (a ^ b));
+}
+
+void
+instr_lognot(eval_environ_t env)
+{
+ unsigned long v = (unsigned long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "LOGNOT %ld", v);
+ adjust_stack(env, 1);
+ push(env, (STKVAL) ~v);
+}
+
+/* Arithmetic instructions */
+void
+instr_add(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "ADD %ld %ld", a, b);
+ push(env, (STKVAL) (a + b));
+}
+
+void
+instr_sub(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "SUB %ld %ld", a, b);
+ push(env, (STKVAL) (a - b));
+}
+
+void
+instr_mul(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "MUL %ld %ld", a, b);
+ push(env, (STKVAL) (a * b));
+}
+
+void
+instr_div(eval_environ_t env)
+{
+ long a = (long) get_numeric_arg(env, 1);
+ long b = (long) get_numeric_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "DIV %ld %ld", a, b);
+ if (b == 0)
+ env_throw(env, mf_divzero,
+ "Division by zero at %08x", (unsigned int) env->pc);
+ push(env, (STKVAL) (a / b));
+}
+
+void
+instr_neg(eval_environ_t env)
+{
+ long v = (long) get_numeric_arg(env, 0);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "NEG %ld", v);
+ adjust_stack(env, 1);
+ push(env, (STKVAL) -v);
+}
+
+/* Matching and Regular expression instructions */
+
+/* REGEX: Basically it is useful only for debugging. Its effect is
+ the same as that of instr_push */
+void
+instr_regex(eval_environ_t env)
+{
+ char buffer[REGEX_STRING_BUFSIZE];
+ size_t index = (size_t) get_immediate(env, 0);
+ advance_pc(env, 1);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "REGEX (%s) %s",
+ regex_flags_to_string(regtab[index].regflags,
+ buffer,
+ sizeof buffer),
+ (char*) env_data_ref(env, regtab[index].expr));
+ push(env, (STKVAL) index);
+}
+
+void
+dump_regex(prog_counter_t i)
+{
+ size_t index = (size_t) prog[i];
+ char buffer[REGEX_STRING_BUFSIZE];
+ printf("(%s) %s",
+ regex_flags_to_string(regtab[index].regflags,
+ buffer, sizeof buffer),
+ (char*)(dataseg + regtab[index].expr));
+}
+
+void
+instr_regmatch(eval_environ_t env)
+{
+ int v;
+ size_t index = (size_t)get_numeric_arg(env, 0);
+ regex_t *re = &regtab[index].re;
+ char *string = get_string_arg(env, 1);
+ adjust_stack(env, 2);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "REGMATCH %s %s",
+ env_data_ref(env, regtab[index].expr), string);
+
+ env->matchcount = re->re_nsub;
+ if (env->matchsize < env->matchcount + 1) {
+ void *p = realloc(env->matches,
+ sizeof(env->matches[0])
+ * (env->matchcount + 1));
+ if (!p)
+ runtime_error(env, _("Not enough memory"));
+ env->matches = p;
+ env->matchsize = env->matchcount + 1;
+ }
+
+ v = regexec(re, string, env->matchcount + 1, env->matches, 0);
+ env->string = string;
+
+ push(env, (STKVAL) (v == 0));
+}
+
+void
+instr_regcomp(eval_environ_t env)
+{
+ int v;
+ char buffer[REGEX_STRING_BUFSIZE];
+ size_t expr_off = (size_t)get_arg(env, 0);
+ char *expr = get_string_arg(env, 0);
+ size_t index = (size_t) get_immediate(env, 0);
+ struct rt_regex *rtx = &regtab[index];
+
+ advance_pc(env, 1);
+ adjust_stack(env, 1);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "REGCOMP %s %s",
+ regex_flags_to_string(rtx->regflags,
+ buffer, sizeof buffer),
+ expr);
+ if (rtx->compiled) {
+ regfree(&rtx->re);
+ rtx->compiled = 0;
+ }
+ v = regcomp(&rtx->re, expr, rtx->regflags);
+ if (v) {
+ char errbuf[512];
+ regerror(v, &rtx->re, errbuf, sizeof(errbuf));
+ env_throw(env, mf_regcomp,
+ "compiling regex `%s': %s",
+ expr,
+ errbuf);
+ } else {
+ rtx->compiled = 1;
+ rtx->expr = expr_off;
+ }
+ push(env, (STKVAL) index);
+}
+
+void
+dump_regcomp(prog_counter_t i)
+{
+ size_t index = (size_t) prog[i];
+ struct rt_regex *rtx = &regtab[index];
+ char buffer[REGEX_STRING_BUFSIZE];
+ printf("%s", regex_flags_to_string(rtx->regflags,
+ buffer, sizeof buffer));
+}
+
+void
+instr_fnmatch(eval_environ_t env)
+{
+ char *string = get_string_arg(env, 1);
+ char *pattern = get_string_arg(env, 0);
+ adjust_stack(env, 2);
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "FNMATCH %s %s", string, pattern);
+ push(env, (STKVAL) (fnmatch (pattern, string, 0) == 0));
+}
+
+static int
+mx_match(eval_environ_t env, char *string,
+ int (*matcher)(const char *name, void *data), void *data)
+{
+ int rc = 0;
+ mxbuf_t mxbuf;
+ mf_status mxstat;
+
+ char *p = strchr(string, '@');
+ if (p)
+ p++;
+ else
+ p = string;
+ mxstat = getmx(p, mxbuf);
+ rc = 0;
+ if (mxstat == mf_success) {
+ int i;
+
+ for (i = 0; i < MAXMXCOUNT && mxbuf[i]; i++) {
+ if (matcher(mxbuf[i], data)) {
+ rc = 1;
+ break;
+ }
+ }
+ dns_freemx(mxbuf);
+ } else if (mxstat != mf_not_found)
+ env_throw(env, mxstat, "cannot get MXs for %s", p);
+ return rc;
+}
+
+static int
+fn_matcher(const char *string, void *data)
+{
+ return fnmatch (data, string, 0) == 0;
+}
+
+void
+instr_fnmatch_mx(eval_environ_t env)
+{
+ char *string = get_string_arg(env, 1);
+ char *pattern = get_string_arg(env, 0);
+ adjust_stack(env, 2);
+
+ if (PROG_TRACE_ENGINE)
+ prog_trace(env, "FNMATCH,MX %s %s", string, pattern);
+ push(env, (STKVAL) mx_match(env, string, fn_matcher, pattern));
+}
+
+static int
+regex_matcher(const char *string, void *data)
+{
+ return regexec(data, string, 0, NULL, 0) == 0;
+}
+
+void
+instr_regm