/* This file is part of Mailfromd.
   Copyright (C) 2006-2025 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 MFL_DATASEG volatile

typedef void *mft_ptr;
typedef char *mft_str;
typedef int mft_int;
typedef unsigned int mft_uint;
typedef long mft_long;
typedef unsigned long mft_ulong;
typedef size_t mft_size;

typedef mft_str mft_string;
typedef mft_long mft_number;

union mf_stkval {
	mft_ptr   v_ptr;
	mft_size  v_size;
	mft_long  v_long;
	mft_ulong v_ulong;
	mft_int   v_int;
	mft_uint  v_uint;
	mft_str   v_str;
};
typedef union mf_stkval STKVAL;

#define mf_cat(a,b) a ## b
#define mf_c_val(x,t) ((x).mf_cat(v_,t))

#define mf_c_cast(x,t) ((STKVAL)(mf_cat(mft_,t))(x))

typedef void (*instr_t) (eval_environ_t env); /* Instruction */
union mf_code_cell {
	instr_t c_instr;
	STKVAL c_value;
};
typedef union mf_code_cell mf_code_cell_t;
#define mf_cell_instr(p) ((p).c_instr)
#define mf_cell_value(p) ((p).c_value)
#define mf_cell_c_value(p,t) mf_c_val(mf_cell_value(p),t)

struct optab {
	char *name;
	instr_t instr;
	void (*dump) (prog_counter_t i);
	unsigned length;
};

/* Code generation and diagnostics functions */
void code_put_stkval(prog_counter_t pos, STKVAL val);
#define code_put(p,v,t) code_put_stkval(p,mf_c_cast(v,t))
mf_code_cell_t code_peek(prog_counter_t pos);
prog_counter_t code_reserve(size_t count);

prog_counter_t code_immediate_stkval(STKVAL value);
#define code_immediate(v,t) code_immediate_stkval(mf_c_cast(v,t))
prog_counter_t code_op(unsigned code);
prog_counter_t code_instr(const instr_t ptr);
prog_counter_t code_exmask(struct exmask *exmask);
prog_counter_t code_get_counter();
void dump_code(prog_counter_t start, prog_counter_t end);
void fixup_code(void);
void run_startup(void);
void run_shutdown(void);

/* Runtime evaluation functions */
struct eval_environ;

#define B2STACK(s) (s + sizeof(STKVAL) - 1) / sizeof(STKVAL)

void runtime_warning(eval_environ_t env, const char *fmt, ...)
	MU_PRINTFLIKE(2, 3);
void runtime_error(eval_environ_t env, const char *fmt, ...)
	MU_PRINTFLIKE(2, 3) ATTRIBUTE_NORETURN;
STKVAL get_arg(eval_environ_t env, unsigned n);
void get_pointer_arg(eval_environ_t env, unsigned n, void * MFL_DATASEG *p);
void get_string_arg(eval_environ_t env, unsigned n, char *MFL_DATASEG *ptr);
void get_numeric_arg(eval_environ_t env, unsigned n, long *np);
void push(eval_environ_t env, STKVAL val);
STKVAL pop(eval_environ_t env);
STKVAL *env_data_ref(eval_environ_t env, size_t off);
size_t heap_reserve(eval_environ_t env, size_t size);
size_t heap_reserve_words(eval_environ_t env, size_t size);
STKVAL heap_tempspace(eval_environ_t env, size_t size);
void heap_obstack_begin(eval_environ_t env);
STKVAL heap_obstack_finish(eval_environ_t env);
void heap_obstack_cancel(eval_environ_t env);
void *heap_obstack_grow(eval_environ_t env, void *MFL_DATASEG ptr, size_t size);
void heap_obstack_reclaim(eval_environ_t env, size_t size);
void heap_obstack_truncate(eval_environ_t env, size_t size);
void *heap_obstack_base(eval_environ_t env);
size_t heap_obstack_length(eval_environ_t env);
void heap_obstack_vsprintf(eval_environ_t env, const char *fmt, va_list ap);
void heap_obstack_sprintf(eval_environ_t env, const char *fmt, ...);

void pushs(eval_environ_t env, const char * MFL_DATASEG s);
void advance_pc(eval_environ_t env, long cnt);
void adjust_stack(eval_environ_t env, unsigned cnt);
void unroll_stack(eval_environ_t env, unsigned cnt);
void prog_trace(eval_environ_t env, const char *fmt, ...)
	MU_PRINTFLIKE(2, 3);
void runtime_stack_trace(eval_environ_t env);
void env_var_inc(eval_environ_t env, size_t off);
void env_get_locus(eval_environ_t env, struct mu_locus_range *loc);
STKVAL env_get_reg(eval_environ_t env);
prog_counter_t env_register_read(eval_environ_t env, int what);
char *env_vaptr(eval_environ_t env, size_t off);
int expand_dataseg(eval_environ_t env, size_t count);
void env_save_socket_family(eval_environ_t env, int family);
int env_socket_family(eval_environ_t env);

void *env_get_builtin_priv(eval_environ_t env, int id);
int builtin_priv_register(void *(*init)(void), void (*destroy)(void*),
			  void (*free_capture));
void env_free_captured(eval_environ_t env);


enum cleanup_when {
	CLEANUP_THROW,        /* Cleanup on exception. */
	CLEANUP_RETURN,       /* Cleanup on return from function. */
	CLEANUP_ALWAYS        /* Cleanup always. */
};

void env_function_cleanup_add(eval_environ_t env, int when,
			      void *data,
			      void (*func)(void *));
void env_function_cleanup_del(eval_environ_t env, int when, void *data);
void env_function_cleanup_flush(eval_environ_t env, int when);


void ensure_initialized_variable(const char *name, struct value *val);
void ds_init_variable(const char *name, void *data);

void scan_code(prog_counter_t start, prog_counter_t end,
	       void (*fun)(prog_counter_t pc, struct optab *op, void *d),
	       void *data);

int within_loop(struct literal *lit);
void enter_loop(struct literal *lit,
		prog_counter_t *begptr, prog_counter_t *endptr);
void leave_loop(void);

NODE *alloc_node(enum node_type type, const struct mu_locus_range *locus);
NODE *create_node_variable(struct variable *var, const struct mu_locus_range *locus);
NODE *create_node_argcount(const struct mu_locus_range *locus);
NODE *create_node_arg(long num, const struct mu_locus_range *locus);
NODE *create_node_string(char const *str, struct mu_locus_range const *locus);
NODE *create_node_symbol(struct literal *lit, const struct mu_locus_range *loc);
NODE *create_node_backref(long num, const struct mu_locus_range *locus);
NODE *cast_to(data_type_t type, NODE *node);
void add_xref(struct variable *var, const struct mu_locus_range *locus);

extern mf_code_cell_t *prog;
extern unsigned error_count; 
extern unsigned long prog_trace_option;

extern STKVAL *dataseg;
extern size_t datasize;
extern size_t dvarsize;
#define EXTABIND (datasize - exception_count)

extern size_t *dataseg_reloc;
extern size_t dataseg_reloc_count;

extern size_t exception_count;

extern mu_stream_t mf_strecho;
