
/********************************************
da_bin.c

libmawk binary dump (C) 2012, Tibor 'Igor2' Palinkas;
losely based on mawk code coming with the below copyright:

copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/

#include  "mawk.h"

#include  "code.h"
#include  "bi_funct.h"
#include  "repl.h"
#include  "field.h"
#include  "num.h"
#include  "fin.h"
#include  "vars.h"
#include  "f2d.h"
#include <string.h>

typedef long int da_word;

/* for load sanity checks declare large maximums; without these a malformed
   binary may cause libmawk to allocate a lot of memory */
#define MEG (1024*1024)
#define BIN_CODE_MAXLEN 64*MEG
#define BIN_NAME_MAXLEN 1024
#define BIN_MAXVARS 1024*1024
#define BIN_MAXSTRS BIN_MAXVARS

#define BIN_FILEVER 1

/* SAVE:
	- code
	- constant nums
	- constant str
	- funtion names and pointers (including builtins!)
	(->repl is not saved; regex's are recompiled since regex compiler needs
	 to be in the binary for runtime purposes anyway - save them as string)
*/

static int safe_strcmp(const char *s1, const char *s2)
{
	if ((s1 == NULL) && (s2 == NULL))
		return 0;
	if ((s1 == NULL) || (s2 == NULL))
		return 1;
	return strcmp(s1, s2);
}

static int safe_strlen(const char *s)
{
	if (s == NULL)
		return 0;
	return strlen(s);
}

static mawk_string_t *safe_new_STRING(mawk_state_t *MAWK, const char *s)
{
	if (s == NULL)
		s = "";
	return mawk_new_STRING(MAWK, s);
}

/* link tables are slow linear lists for now; may replace with hashes later */
typedef struct {
	int numsu, strsu, varsu;
	int numsa, strsa, varsa;
	mawk_num_t *nums;
	const char **strs;
	struct {
		int vtype;
		mawk_cell_t *cp;
	} *vars;
} link_t;


static void link_free(link_t *l)
{
	if (l->nums != NULL)
		free(l->nums);
	if (l->strs != NULL)
		free(l->strs);
	if (l->vars != NULL)
		free(l->vars);
}

#define check_grow(l, arr, used, alloced) \
	if (l->used >= l->alloced) { \
		l->alloced += 256; \
		l->arr = realloc(l->arr, sizeof(*l->arr) * l->alloced); \
	}

static int save_num(link_t *l, mawk_num_t *num)
{
	int n;
	for(n = 0; n < l->numsu; n++)
		if (l->nums[n] == *num)
			return n;

	check_grow(l, nums, numsu, numsa);
	l->nums[l->numsu] = *num;
	return l->numsu++;
}


static int save_str(link_t *l, const char *str)
{
	int n;
	for(n = 0; n < l->strsu; n++)
		if (safe_strcmp(l->strs[n], str) == 0) {
			return n;
		}
	check_grow(l, strs, strsu, strsa);
	l->strs[l->strsu] = str; /* no need to copy, strings are constant during dumping */
	return l->strsu++;
}

static int save_var(link_t *l, mawk_cell_t *v, int vtype)
{
	int n;
	for(n = 0; n < l->varsu; n++)
		if ((l->vars[n].cp == v) && (l->vars[n].vtype == vtype))
			return n;

	check_grow(l, vars, varsu, varsa);
	l->vars[l->varsu].vtype = vtype;
	l->vars[l->varsu].cp = v;
	return l->varsu++;
}

static mawk_num_t *load_num(mawk_state_t *MAWK, link_t *l, int id)
{
	mawk_num_t *n;

	if ((id < 0) || (id >= l->numsu))
		return NULL;

	n = mawk_zmalloc(MAWK, sizeof(mawk_num_t));
	*n = l->nums[id];
	return n;
}

static const char *invalid_str = "load_str invalid";
static const char *load_str(link_t *l, int id)
{
	if ((id < 0) || (id >= l->strsu))
		return invalid_str;
	return l->strs[id];
}

static mawk_cell_t *load_var(link_t *l, int id)
{
	if ((id < 0) || (id >= l->varsu))
		return NULL;
	return l->vars[id].cp;
}

static void *load_bifunc(link_t *l, int id)
{
	const char *name;
	name = load_str(l, id);
	if (name == invalid_str)
		return NULL;
	return mawk_f2d(mawk_find_bi_ptr(name));
}

static FBLOCK *bin_new_funct(mawk_state_t *MAWK, const char *name, int add_to_list)
{
	FBLOCK *fbp;
	SYMTAB *s;

	s = mawk_find(MAWK, name, 0);
	if (s != NULL) {
		if (s->type != ST_FUNCT)
			return NULL;

		if (add_to_list)
			mawk_add_to_fdump_list(MAWK, s->stval.fbp);
		return s->stval.fbp;
	}

	s = mawk_find(MAWK, name, 1);
	s->type = ST_FUNCT ;
	fbp = s->stval.fbp = (FBLOCK *) mawk_zmalloc(MAWK, sizeof(FBLOCK)) ;
	fbp->name = mawk_zstrclone(MAWK, name);
	fbp->code = (INST*) 0 ;

	if (add_to_list)
		mawk_add_to_fdump_list(MAWK, fbp);

	return fbp;
}

static void *bin_load_func_ref(mawk_state_t *MAWK, link_t *l, int id)
{
	const char *name;

	name = load_str(l, id);
	if (name == invalid_str)
		return NULL;
	return bin_new_funct(MAWK, name, 0);
}



#define pack_re(p, cp)  p->op = DUMP_RE | save_str(l, mawk_re_uncompile(MAWK, cp->ptr))
#define pack_match(p)  p->op = DUMP_RE | save_str(l, mawk_re_uncompile(MAWK, p->ptr))
#define pack_repl(p, r) p->op = DUMP_REPL | save_str(l, mawk_repl_uncompile(MAWK, r))
#define pack_numptr(p)  p->op = DUMP_NUM | save_num(l, (mawk_num_t *)p->ptr)
#define pack_split_space(p)  p->op = DUMP_SPLIT_SPACE
#define pack_split_null(p)   p->op = DUMP_SPLIT_NULL
#define pack_str(p) \
	do { \
		mawk_string_t *sval = (mawk_string_t *) p->ptr; \
		p->op = DUMP_STR | save_str(l, sval->str); \
	} while(0)

#define pack_var_(p, cp, t)     p->op = DUMP_VAR | save_var(l, p->ptr, t)
#define pack_var(p, cp)         pack_var_(p, cp, ST_VAR)
#define pack_field(p, cp)       pack_var_(p, cp, ST_FIELD)
#define pack_field_0(p)         p->op = DUMP_SPECFIELD | 0
#define pack_field_shadow(p)    p->op = DUMP_SPECFIELD | 1
#define pack_arr(p, cp)         pack_var_(p, cp, ST_ARRAY)
#define pack_index(p, cp)       p->op = DUMP_FIELDIDX | mawk_field_addr_to_index(MAWK, cp)
#define pack_bifunc(p, cp)      p->op = DUMP_CALL | save_str(l, mawk_find_bi_name((PF_CP) mawk_d2f(p->ptr)))
#define pack_call(p)            p->op = DUMP_CALL | save_str(l, ((FBLOCK *) p->ptr)->name)


/* check if op matches flag; return NULL if not or remove flag */
#define chk_mask(op, flag)        do { if ((op & flag) != flag) return NULL; op &= ~(flag); } while(0)

#define unpack_re(p, cp)          do {  \
	const char *s; \
	chk_mask(p->op, DUMP_RE); \
	s = load_str(l, p->op); \
	if (s == invalid_str) \
		return NULL; \
	cp = mawk_zmalloc(MAWK, sizeof(mawk_cell_t)); \
	p->ptr = cp; \
	cp->type = C_RE; \
	cp->ptr = mawk_re_compile(MAWK, mawk_new_STRING(MAWK, s)); \
} while(0)

#define unpack_match(p)          do {  \
	const char *s; \
	chk_mask(p->op, DUMP_RE); \
	s = load_str(l, p->op); \
	if (s == invalid_str) \
		return NULL; \
	p->ptr = mawk_re_compile(MAWK, mawk_new_STRING(MAWK, s)); \
} while(0)

#define unpack_repl(p, r) do { \
	const char *s; \
	chk_mask(p->op, DUMP_REPL); \
	s = load_str(l, p->op); \
	if (s == invalid_str) \
		return NULL; \
	cp = mawk_zmalloc(MAWK, sizeof(mawk_cell_t)); \
	p->ptr = mawk_repl_compile(MAWK, safe_new_STRING(MAWK, s)); \
} while(0)

#define unpack_numptr(p)          do { chk_mask(p->op, DUMP_NUM); p->ptr = load_num(MAWK, l, p->op); if (p->ptr == NULL) return NULL; } while(0)
#define unpack_split_space(p)     do { mawk_cell_t *cp; chk_mask(p->op, DUMP_SPLIT_SPACE); cp = MAWK_ZMALLOC(MAWK, mawk_cell_t); p->ptr = cp; cp->type = C_SPACE; } while(0)
#define unpack_split_null(p)      do { mawk_cell_t *cp; chk_mask(p->op, DUMP_SPLIT_NULL);  cp = MAWK_ZMALLOC(MAWK, mawk_cell_t); p->ptr = cp; cp->type = C_SNULL; } while(0)
#define unpack_str(p) \
	do { \
		const char *s; \
		chk_mask(p->op, DUMP_STR); \
		s = load_str(l, p->op); \
		if (s == invalid_str) \
			return NULL; \
		p->ptr = safe_new_STRING(MAWK, s); \
	} while(0)
#define unpack_var_(p, cp, t)     do { chk_mask(p->op, DUMP_VAR); p->ptr = load_var(l, p->op); if (p->ptr == NULL) return NULL; } while (0)
#define unpack_var(p, cp)         unpack_var_(p, cp, ST_VAR)
#define unpack_field(p, cp)       unpack_var_(p, cp, ST_FIELD)
#define unpack_field_0(p)         do { chk_mask(p->op, DUMP_SPECFIELD); p->ptr = MAWK->field;} while (0)
#define unpack_field_shadow(p)    do { chk_mask(p->op, DUMP_SPECFIELD); p->ptr = &MAWK->fs_shadow; } while (0)
#define unpack_arr(p, cp)         unpack_var_(p, cp, ST_ARRAY)
#define unpack_index(p, cp)       do { chk_mask(p->op, DUMP_FIELDIDX); p->ptr = field_ptr(p->op); } while (0)
#define unpack_bifunc(p, cp)      do { chk_mask(p->op, DUMP_CALL); p->ptr = load_bifunc(l, p->op); if (p->ptr == NULL) return NULL; } while (0)
#define unpack_call(p)            do { chk_mask(p->op, DUMP_CALL); p->ptr = bin_load_func_ref(MAWK, l, p->op); if (p->ptr == NULL) return NULL; } while (0)

/* evaluate macro pack_ or unpack_, depending on pack flag */
#define link_re(p, cp)         do { if (pack) pack_re(p, cp); else unpack_re(p, cp); } while (0)
#define link_match(p)          do { if (pack) pack_match(p); else unpack_match(p); } while (0)
#define link_repl(p, r)        do { if (pack) pack_repl(p, r); else unpack_repl(p, r); } while (0)
#define link_numptr(p)         do { if (pack) pack_numptr(p); else unpack_numptr(p); } while (0)
#define link_split_space(p)    do { if (pack) pack_split_space(p); else unpack_split_space(p); } while (0)
#define link_split_null(p)     do { if (pack) pack_split_null(p); else unpack_split_null(p); } while (0)
#define link_str(p)            do { if (pack) pack_str(p); else unpack_str(p); } while (0)
#define link_var(p, cp)        do { if (pack) pack_var(p, cp); else unpack_var(p, cp); } while (0)
#define link_field(p, cp)      do { if (pack) pack_field(p, cp); else unpack_field(p, cp); } while (0)
#define link_field_0(p)        do { if (pack) pack_field_0(p); else unpack_field_0(p); } while (0)
#define link_field_shadow(p)   do { if (pack) pack_field_shadow(p); else unpack_field_shadow(p); } while (0)
#define link_arr(p, cp)        do { if (pack) pack_arr(p, cp); else unpack_arr(p, cp); } while (0)
#define link_index(p, cp)      do { if (pack) pack_index(p, cp); else unpack_index(p, cp); } while (0)
#define link_bifunc(p, cp)     do { if (pack) pack_bifunc(p, cp); else unpack_bifunc(p, cp); } while (0)
#define link_call(p)           do { if (pack) pack_call(p); else unpack_call(p); } while (0)

INST *mawk_da_bin(mawk_state_t *MAWK, INST *start, link_t *l, int pack)
{
	mawk_cell_t *cp;
	register INST *p = start;

/*	fprintf(stderr, "-\n"); */
	do {
/*				fprintf(stderr, "%d: %d\n", p-start, p->op);*/

		switch (p++->op) {

		case _PUSHC:
			cp = (mawk_cell_t *) p->ptr;
			if (pack) {
				switch (cp->type) {
					case C_RE:
						link_re(p, cp);
						break;
					case C_REPL:
					case C_REPLV:
						link_repl(p, cp);
						break;
					case C_SPACE:
						link_split_space(p);
						break;
					case C_SNULL:
						link_split_null(p);
						break;
					default:
						/* nothing else to save */
						break;
				}
			}
			else {
				if (p->op == DUMP_SPLIT_SPACE)
					link_split_space(p);
				else if (p->op == DUMP_SPLIT_NULL)
					link_split_null(p);
				else if ((p->op & DUMP_RE) == DUMP_RE)
					link_re(p, cp);
				else if ((p->op & DUMP_REPL) == DUMP_REPL)
					link_repl(p, cp);
			}
			p++;
			break;

		case _PUSHD:
			link_numptr(p);
			p++;
			break;
		case _PUSHS:
			link_str(p);
			p++;
			break;
		case _MATCH0:
		case _MATCH1:
			link_match(p);
			p++;
			break;

		case _PUSHA:
			cp = (mawk_cell_t *) p->ptr;
			link_var(p, cp);
			p++;
			break;

		case _PUSHI:
			cp = (mawk_cell_t *) p->ptr;
			if (pack) {
				if (cp == MAWK->field)
					link_field_0(p);
				else if (cp == &MAWK->fs_shadow)
					link_field_shadow(p);
				else {
					if (cp > MAWK_NF && cp <= LAST_PFIELD)
						link_field(p, cp);
					else
						link_var(p, cp);
				}
			}
			else {
				if (p->op == (DUMP_SPECFIELD | 0))
					link_field_0(p);
				else if (p->op == (DUMP_SPECFIELD | 1))
					link_field_shadow(p);
				else
					link_var(p, cp); /* fields are just variables */
			}
			p++;
			break;

		case L_PUSHA:
		case L_PUSHI:
		case LAE_PUSHI:
		case LAE_PUSHA:
		case LAE_PUSHA_WRARR:
		case LA_PUSHA:
			/* address is (p+1)->op */
			p++;
			break;

		case F_PUSHI:
			/* address is (p+1)->op */
			p++;
			p++;
			break;


		case F_PUSHA:
			cp = (mawk_cell_t *) p->ptr;
			if (pack) {
				if (cp >= MAWK_NF && cp <= LAST_PFIELD)
					link_field(p, cp);
				else
					link_index(p, cp);
			}
			else {
				if ((p->op & DUMP_FIELDIDX) == DUMP_FIELDIDX)
					link_index(p, cp);
				else
					link_field(p, cp);
			}
			p++;
			break;


		case AE_PUSHA:
		case AE_PUSHA_WRARR:
		case AE_PUSHI:
		case A_PUSHA:
			cp = (mawk_cell_t *) p->ptr;
			link_arr(p, cp);
			p++;
			break;

		case _PUSHINT:
			/* integer operand is (p+1)->op */
			p++;
			break;

		case _BUILTIN:
		case _PRINT:
			cp = (mawk_cell_t *) p->ptr;
			link_bifunc(p, cp);
			p++;
			break;

		case _JMP:
		case _JNZ:
		case _JZ:
		case _LJZ:
		case _LJNZ:
		case SET_ALOOP:
		case ALOOP:
		case A_CAT:
			/* address is (p+1)->op */
			p++;
			break;

		case _CALL:
			link_call(p);
			p += 2;
			break;

		case _HALT:
			break;

		case _RANGE_CHK:
			p += 4;
			break;

		case LOCATION:
			p++;
			break;

		default:
			/* simple code */
			break;
		}
	} while (p->op != _HALT);
	p++;
	return p;
}

void mawk_fdump_bin(mawk_state_t * MAWK, link_t *l)
{
	register struct mawk_fdump *p, *q = MAWK->fdump_list;

	while (q) {
		p = q;
		q = p->link;
/*		fprintf(stdout, "function %s\n", p->fbp->name); */
		mawk_da_bin(MAWK, p->fbp->code, l, 1);
	}
}


static const char *f_id = "libmawk\n";
typedef struct header_s {
	char f_id[8];
	char numeric; /* see mawk_num_id in num_*.c */
	char filever; /* should be BIN_FILEVER */
	union {
		short int s;
		unsigned char c;
	} byteorder;
	char inst_size;
	char num_size;
	char pad[50]; /* reserved for future use - header size should be 64 bytes long */
} header_t;

#define chk_hdr_size(h) \
	if (sizeof(h) != 64) { \
		mawk_errmsg(MAWK, 0, "Internal libmawk error: da_bin: header struct size is not 64 bytes but %d bytes", sizeof(h)); \
		abort(); \
	}

#ifndef MAWK_NO_COMP
static int link_bin_sect(mawk_state_t * MAWK, INST *b, link_t *l)
{
	INST *p;

	if (b != NULL) {
		p = mawk_da_bin(MAWK, b, l, 1);
		return p - b;
	}
	return 0;
}

static int save_bin_sect(mawk_state_t * MAWK, void *fd, int (*write)(void *fd, const void *buff, size_t len), INST *b, da_word len)
{
	if (write(fd, &len, sizeof(da_word)) != sizeof(da_word))
		return -1;
	if (b != NULL) {
		int l;
		l = len * sizeof(INST);
		if (write(fd, b, l) != l)
			return -1;
	}
	return 0;
}

int mawk_save_code_bin_(mawk_state_t * MAWK, void *fd, int (*write)(void *fd, const void *buff, size_t len))
{
	header_t h;
	link_t l;
	int n;
	long int begin_len, end_len, main_len;
	da_word num_func, len;
	long int *flen;
	struct mawk_fdump **fps;
	struct mawk_fdump *fp;

	/* create header */
	chk_hdr_size(h);
	memcpy(h.f_id, f_id, sizeof(h.f_id));
	h.numeric     = mawk_num_id;
	h.byteorder.s = 0x626F; /* bo in MSB */
	h.filever     = BIN_FILEVER;
	h.inst_size   = sizeof(INST);
	h.num_size    = sizeof(mawk_num_t);
	memset(h.pad, 0, sizeof(h.pad));
	write(fd, &h, sizeof(h));

	/* prepare for linking */
	memset(&l, 0, sizeof(l));

	/* count functions */
	num_func = 0;
	for(fp = MAWK->fdump_list; fp != NULL; fp = fp->link)
		num_func++;
	if (num_func > 0) {
		flen = malloc(sizeof(long int) * num_func);
		fps  = malloc(sizeof(struct mawk_fdump *) * num_func);
	}
	for(n = 0, fp = MAWK->fdump_list; fp != NULL; n++, fp = fp->link) {
		fps[n] = fp;
	}


/*	fprintf(stderr, "Funcs: %d\n", num_func); */

	begin_len = link_bin_sect(MAWK, MAWK->begin_start, &l);
/*	fprintf(stderr, "BEGIN: total %d instructions\n", begin_len); */
	end_len = link_bin_sect(MAWK, MAWK->end_start, &l);
/*	fprintf(stderr, "END: total %d instructions\n", end_len); */
	main_len = link_bin_sect(MAWK, MAWK->main_start, &l);
/*	fprintf(stderr, "MAIN: total %d instructions\n", main_len); */

	for(n = 0, fp = MAWK->fdump_list; fp != NULL; n++, fp = fp->link)
		flen[n] = link_bin_sect(MAWK, fp->fbp->code, &l);

	/* L1: save nums */
	len = l.numsu;
	write(fd, &len, sizeof(da_word));
	write(fd, l.nums, sizeof(mawk_num_t) * l.numsu);
	write(fd, &num_func, sizeof(da_word));

	/* L2: save strings */
	len = l.strsu;
	write(fd, &len, sizeof(da_word));
	for(n = 0; n < l.strsu; n++) {
		len = safe_strlen(l.strs[n]);
		write(fd, &len, sizeof(da_word));
		if (len > 0)
			write(fd, l.strs[n], len);
	}

	/* L3: save vars */
	len = l.varsu;
	write(fd, &len, sizeof(da_word));
	for(n = 0; n < l.varsu; n++) {
		const char *name;
		SYMTAB *stp;

		name = mawk_reverse_find(MAWK, l.vars[n].vtype, &(l.vars[n].cp));
		if (name == mawk_reverse_uk) {
			mawk_errmsg(MAWK, 0, "da_bin: Variable name lookup error");
			return -1;
		}
		stp = mawk_find(MAWK, name, 0);

		/* save types */
		write(fd, &(l.vars[n].vtype), 2);
		write(fd, &(stp->type), 1);

		/* save name len and name*/
		len = strlen(name);
		write(fd, &len, sizeof(da_word));
		write(fd, name, len);

		/* save value len and content - empty for now */
		len = 0;
		write(fd, &len, sizeof(da_word));
	}

	/* L4: save begin/end/main sections */
	save_bin_sect(MAWK, fd, write, MAWK->begin_start, begin_len);
	save_bin_sect(MAWK, fd, write, MAWK->end_start, end_len);
	save_bin_sect(MAWK, fd, write, MAWK->main_start, main_len);


	/* L5: save user functions */
	
	for(n = num_func-1; n >= 0; n--) {
		int len;

		fp = fps[n];
		len = strlen(fp->fbp->name);
/*		fprintf(stderr, "da_bin function %s/%d len=%d\n", fp->fbp->name, len,flen[n]); */
		write(fd, &len, sizeof(da_word));
		write(fd, fp->fbp->name, len);
/*		fprintf(stderr, "NARGS=%d %d\n", fp->fbp->nargs, fp->fbp->typev[0]); */
		write(fd, &fp->fbp->nargs, 2);
		if (fp->fbp->nargs > 0)
			write(fd, fp->fbp->typev, fp->fbp->nargs);
		save_bin_sect(MAWK, fd, write, fp->fbp->code, flen[n]);
	}
	if (num_func > 0) {
		free(flen);
		free(fps);
	}

	link_free(&l);
	return 0;
}
#endif

#ifndef MAWK_NO_EXEC
static int bin_load_func(mawk_state_t *MAWK, void *fd, int (*read)(void *fd, void *buff, size_t len), FBLOCK *fbp)
{
	da_word len;
	read(fd, &len, sizeof(da_word));
	if (len > 0) {
		if (len > BIN_CODE_MAXLEN)
			return -1;

		fbp->code = mawk_zmalloc(MAWK, len  * sizeof(INST));
		read(fd, fbp->code, len  * sizeof(INST));
	}
	return 0;
}

static int bin_load_sect(mawk_state_t *MAWK, void *fd, int (*read)(void *fd, void *buff, size_t len), int scope)
{
	da_word len;

	read(fd, &len, sizeof(da_word));
	if (len > 0) {
		MAWK->scope = scope;
		if ((scope == SCOPE_BEGIN) || (scope == SCOPE_END))
			mawk_be_setup(MAWK, MAWK->scope);
		mawk_code_reset_size(MAWK, len * sizeof(INST));
		read(fd, MAWK->active_code.base, len * sizeof(INST));
		MAWK->active_code.ptr = MAWK->active_code.base + len;
		switch(scope) {
			case SCOPE_BEGIN: *MAWK->begin_code_p = MAWK->active_code; break;
			case SCOPE_END:   *MAWK->end_code_p = MAWK->active_code; break;
			case SCOPE_MAIN:  *MAWK->main_code_p = MAWK->active_code; break;
			/* avoid using default so later when scope is enum compiler can give warnings on missing scopes */
		}
		MAWK->active_code = *MAWK->main_code_p;
	}
	return 0;
}

static int bin_link_sect(mawk_state_t *MAWK, link_t *l, INST *blk, const char *sectname)
{
	if (blk == NULL)
		return 0;
	if (mawk_da_bin(MAWK, blk, l, 0) == NULL) {
		mawk_errmsg(MAWK, 0, "da_bin: Error: failed to link binary in section %s", sectname);
		return 1;
	}
	return 0;
}


int mawk_load_code_bin_(mawk_state_t *MAWK, void *fd, int (*read)(void *fd, void *buff, size_t len))
{
	SYMTAB *stp;
	header_t h;
	link_t l;
	int n;
	da_word num_func, len;
	int ret;

	memset(&l, 0, sizeof(l));

	/* read header */
	chk_hdr_size(h);

	read(fd, &h, sizeof(h));

	ret = 0;
	if (memcmp(h.f_id, f_id, sizeof(h.f_id)) != 0) {
		mawk_errmsg(MAWK, 0, "da_bin: header ID mismatch");
		ret = MAWK_EHDRSIZE;
		goto err;
	}
	if (h.numeric != mawk_num_id) {
		mawk_errmsg(MAWK, 0, "da_bin: magic mismatch");
		ret = MAWK_EFILEMAGIC;
		goto err;
	}
	if (h.byteorder.s != 0x626F) { /* bo in MSB */
		mawk_errmsg(MAWK, 0, "da_bin: byte order mismatch");
		ret = MAWK_EBYTEORDER;
		goto err;
	}
	if (h.filever != BIN_FILEVER) {
		mawk_errmsg(MAWK, 0, "da_bin: file version mismatch: produced by %d, can load %d", h.filever, BIN_FILEVER);
		ret = MAWK_EVERSION;
		goto err;
	}
	if (h.inst_size != sizeof(INST)) {
		mawk_errmsg(MAWK, 0, "da_bin: instruction size mismatch: expected %d, got %d", sizeof(INST), h.inst_size);
		ret = MAWK_EINSTSIZE;
		goto err;
	}
	if (h.num_size != sizeof(mawk_num_t)) {
		mawk_errmsg(MAWK, 0, "da_bin: numeric size mismatch: expected %d, got %d", sizeof(mawk_num_t), h.num_size);
		ret = MAWK_ENUMSIZE;
		goto err;
	}

	/* prepare for linking */
	memset(&l, 0, sizeof(l));


	/* L1: load nums */
	len = 0;
	read(fd, &len, sizeof(da_word));
	l.numsa = l.numsu = len;
	len = sizeof(*l.nums) * l.numsa;
	if (len > BIN_MAXVARS)
		goto err;
	l.nums = malloc(len);
	read(fd, l.nums, len);
	read(fd, &num_func, sizeof(da_word));

	/* L2: load strings */
	read(fd, &len, sizeof(da_word));
	l.strsa = l.strsu = len;
	len = sizeof(*l.strs) * l.strsa;
	if (len > BIN_MAXSTRS)
			goto err;
	l.strs = malloc(len);
	for(n = 0; n < l.strsu; n++) {
		char *name;

		read(fd, &len, sizeof(da_word));
		if (len > BIN_NAME_MAXLEN)
			goto err;
		if (len > 0) {
			l.strs[n] = mawk_zmalloc(MAWK, len+1);
			if (l.strs[n] == NULL) {
				ret = MAWK_EALLOC;
				goto err;
			}
			read(fd, (char *)l.strs[n], len);
			name = (char *)l.strs[n];
			name[len] = '\0';
		}
		else
			l.strs[n] = NULL;
/*		fprintf(stderr, "LOAD str: [%d] '%s'\n", n, l.strs[n]); */
	}

	/* L3: load vars */
	read(fd, &len, sizeof(da_word));
	l.varsa = l.varsu = len;
	len = sizeof(*l.vars) * l.varsa;
	if (l.varsa > BIN_MAXVARS)
			goto err;
	l.vars = malloc(len);
/*	fprintf(stderr, "numvars: %d allocated=%d\n", l.varsu, len); */
	for(n = 0; n < l.varsu; n++) {
		char *name;
		short type;
		char stype;
		/* load type */
		read(fd, &type, 2);
		read(fd, &stype, 1);

		/* load name len and name*/
		read(fd, &len, sizeof(da_word));
		if (len > BIN_NAME_MAXLEN)
			goto err;
		name = mawk_zmalloc(MAWK, len+1);
		read(fd, name, len);
		name[len] = '\0';

		/* go for existing vars first (because ARGV and builtins) */
		l.vars[n].cp = mawk_get_var(MAWK, name);

		if (l.vars[n].cp == NULL) {
			if (type == ST_ARRAY) {
				SYMTAB *stp;

				stp = mawk_insert(MAWK, name);
				stp->stval.array = mawk_array_new(MAWK, NULL);
				stp->type = ST_ARRAY;
				l.vars[n].cp = (mawk_cell_t *)stp->stval.array;
			}
			else {
				l.vars[n].cp = mawk_create_var(MAWK, name, NULL);
				if (l.vars[n].cp == NULL) {
					mawk_errmsg(MAWK, 0, "da_bin: load error: can not create variable %s", name);
					goto err;
				}
			}
		}
		l.vars[n].vtype = type;
		stp = mawk_find(MAWK, name, 0);
		if (stp->type != stype) {
			mawk_errmsg(MAWK, 0, "da_bin warning: type of variable %s changed from %d to %d", name, stp->type, stype);
		}
		stp->type = stype;

/*		fprintf(stderr, "LOAD var name='%s' type=%d len=%d: %d -> %p\n", name, type, len, n, l.vars[n].cp); */
		/* load value len and content - empty for now */
		read(fd, &len, sizeof(da_word));
		if (len != 0) {
			ret = MAWK_EWRONGVAL;
			goto err2;
		}
	}


	/* L4: load begin/end/main sections */
	mawk_code_init(MAWK);
/*	printf("D1: %p %p %p\n", MAWK->main_code_p, MAWK->begin_code_p, MAWK->end_code_p); */
	if (bin_load_sect(MAWK, fd, read, SCOPE_BEGIN))
		goto err;
	if (bin_load_sect(MAWK, fd, read, SCOPE_END))
		goto err;
	if (bin_load_sect(MAWK, fd, read, SCOPE_MAIN))
		goto err;

	/* L5: load functions */
	for(n = 0; n < num_func; n++) {
		int len;
		char *name;
		FBLOCK *fbp;

		read(fd, &len, sizeof(da_word));
/*		fprintf(stderr, "LOAD function len=%d\n", len); */
		if (len > BIN_NAME_MAXLEN)
				goto err;
		name = mawk_zmalloc(MAWK, len+1);
		read(fd, name, len);
		name[len] = '\0';
/*		fprintf(stderr, "LOAD function name=%s\n", name); */
		fbp = bin_new_funct(MAWK, name, 1);

		read(fd, &fbp->nargs, 2);
/*		fprintf(stderr, "LOAD nargs=%d\n", fbp->nargs); */
		if (fbp->nargs > 0) {
			fbp->typev = mawk_zmalloc(MAWK, fbp->nargs);
			read(fd, fbp->typev, fbp->nargs);
		}
		else
			fbp->typev = NULL;

		bin_load_func(MAWK, fd, read, fbp);
		if (mawk_da_bin(MAWK, fbp->code, &l, 0) == NULL) {
			mawk_errmsg(MAWK, 0, "Can't link function %s", name);
			goto err2;
		}
	}

	mawk_set_code(MAWK);

	if (bin_link_sect(MAWK, &l, MAWK->begin_start, "BEGIN"))
		goto err;
	if (bin_link_sect(MAWK, &l, MAWK->end_start, "END"))
		goto err;
	if (bin_link_sect(MAWK, &l, MAWK->main_start, "main"))
		goto err;

	goto out;
	err2:;
	err:;
	out:;

	link_free(&l);

	return ret;
}
#endif

