diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2008-02-10 14:08:36 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2008-02-10 14:08:36 +0000 |
commit | a1871918823166cac9f0c12164ba27cb3b1f0c2c (patch) | |
tree | 7260d118aa5af6a8f006f9b2dac6cabde782db8c /mfd/prog.c | |
parent | 455b645247bdc0f01239b2352a9d8f07f446024f (diff) | |
download | mailfromd-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.c | 2316 |
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(®stk); + r.compiled = 0; /* Will be compiled later */ + r.expr = rp->lit ? rp->lit->off : 0; + r.regflags = rp->regflags; + rp->index = regcount++; + obstack_grow(®stk, &r, sizeof r); +} + +void +finalize_regex() +{ + if (regcount) + regtab = obstack_finish(®stk); +} + + +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 = ®tab[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 = ®tab[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 = ®tab[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 |