#line 1026 "../../src/builtin/snarf.m4"
/* -*- buffer-read-only: t -*- vi: set ro:
   THIS FILE IS GENERATED AUTOMATICALLY.  PLEASE DO NOT EDIT.
*/
#line 1026
#ifdef HAVE_CONFIG_H
#line 1026
# include <config.h>
#line 1026
#endif
#line 1026
#include <sys/types.h>
#line 1026

#line 1026
#include "mailfromd.h"
#line 1026
#include "prog.h"
#line 1026
#include "builtin.h"
#line 1026

#line 1026

#line 1066 "../../src/builtin/snarf.m4"

/* End of snarf.m4 */
#line 1 "mfmod.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2022-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/>. */


#ifdef WITH_MFMOD
#line 18


#include <mflib/status.h>
#include "global.h"
#include "mfmod/mfmod.h"
#include "msg.h"
#include <dlfcn.h>
#include <mailutils/wordsplit.h>

static size_t nmfmods = MAX_MFMODS;
char *mfmod_path = MFMODDIR;
static struct mu_cfg_param mfmod_cfg_param[] = {
	{ "max-mfmods", mu_c_size, &nmfmods, 0, NULL,
	  N_("Maximum number of dynamically loaded modules.") },
	{ "mfmod-path", mu_c_string, &mfmod_path, 0, NULL,
	  N_("Search path for dynamically loaded modules.") },
	{ NULL }
};

struct modinfo {
	void *handle;
	char *libname;
};

static inline int
mfmod_is_used(struct modinfo *dltab, int i)
{
	return i >= 0 && i < nmfmods && dltab[i].handle != NULL;
}


static void *
alloc_mfmods(void)
{
	return mu_calloc(nmfmods, sizeof(struct modinfo));
}

static struct modinfo *dltab;

#if 0
// See FIXME below

static void
mfmod_close(struct modinfo *mod)
{
	dlclose(mod->handle);
	free(mod->libname);
	memset(mod, 0, sizeof(*mod));
}

static void
destroy_mfmods(void)
{
	if (dltab) {
		size_t i;
		for (i = 0; i < nmfmods; i++) {
			if (mfmod_is_used(dltab, i))
				mfmod_close(&dltab[i]);
		}
		free(dltab);
	}
}
#endif

static struct modinfo *
get_dltab(void)
{
	if (!dltab) {
		dltab = alloc_mfmods();
		/*
		 * FIXME: Used to have atexit(destroy_mfmods) here.
		 * However, shutdown handlers, that are currently run
		 * via mu_onexit (i.e. atexit) as well get executed after
		 * destroy_mfmod, which causes trouble.
		 * I guess it's OK not to close dlhandles, since we're
		 * going to exit anyway.
		 */
	}
	return dltab;
}

static int
find_free_slot(struct modinfo *dltab)
{
	int i;
	for (i = 0; i < nmfmods; i++)
		if (!mfmod_is_used(dltab, i))
			return i;
	return -1;
}

static char *
mfmod_find(char const *name)
{
	char *retval = NULL;

	if (name[0] == '/')
		retval = mu_strdup(name);
	else {
		struct mu_wordsplit ws;
		size_t i;
	
		ws.ws_delim = ":";
		if (mu_wordsplit(mfmod_path, &ws, MU_WRDSF_DELIM|WRDSF_NOCMD|WRDSF_NOVAR)) {
			mu_error("can't split mfmod_path: %s", mu_wordsplit_strerror(&ws));
			mu_wordsplit_free(&ws);
			return NULL;
		}
		
		for (i = 0; i < ws.ws_wordc; i++) {
			char *libname = mu_make_file_name(ws.ws_wordv[0], name);
			if (access(libname, X_OK) == 0) {
				retval = libname;
				break;
			}
			free(libname);
		}
		mu_wordsplit_free(&ws);
	}
	return retval;
}

void
#line 140
bi_dlopen(eval_environ_t env)
#line 140

#line 140

#line 140 "mfmod.bi"
{
#line 140
	
#line 140

#line 140
        char *  libname;
#line 140
        
#line 140

#line 140
        get_string_arg(env, 0, &libname);
#line 140
        
#line 140
        adjust_stack(env, 1);
#line 140

#line 140

#line 140
	if (builtin_module_trace(BUILTIN_IDX_mfmod))
#line 140
		prog_trace(env, "dlopen %s",libname);;
#line 140

{
	struct modinfo *dltab = get_dltab();
	long n = find_free_slot(dltab);
	void *dlh;
	char *filename;
	
		if (!(n != -1))
#line 147
		(
#line 147
	env_throw_bi(env, mfe_failure, "dlopen", _("no more library slots available"))
#line 147
)
;
	filename = mfmod_find(libname);
		if (!(filename != NULL))
#line 150
		(
#line 150
	env_throw_bi(env, mfe_failure, "dlopen", _("module file %s not found in mfmod_path"),libname)
#line 150
)
#line 152
;
	env_function_cleanup_add(env, CLEANUP_ALWAYS, filename, free);
	dlh = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
		if (!(dlh != NULL))
#line 155
		(
#line 155
	env_throw_bi(env, mfe_failure, "dlopen", _("can't load %s: %s"),libname,dlerror())
#line 155
)
;
	dltab[n].handle = dlh;
	dltab[n].libname = mu_strdup(libname);
	
#line 159
do {
#line 159
  push(env, (STKVAL)(mft_number)(n));
#line 159
  goto endlab;
#line 159
} while (0);
}
endlab:
#line 161
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 161
	return;
#line 161
}

static void
_cleanup_vparam(void *ptr)
{
	free(ptr);
}

static void
_cleanup_retval(void *ptr)
{
	MFMOD_PARAM *p = ptr;
	free(p->string);
}

void
#line 176
bi_dlcall(eval_environ_t env)
#line 176

#line 176

#line 176 "mfmod.bi"
{
#line 176
	
#line 176

#line 176
        long  dlh;
#line 176
        char *  symname;
#line 176
        char *  types;
#line 176
        
#line 176
        long __bi_argcnt;
#line 176
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 176
        get_numeric_arg(env, 1, &dlh);
#line 176
        get_string_arg(env, 2, &symname);
#line 176
        get_string_arg(env, 3, &types);
#line 176
        
#line 176
        adjust_stack(env, __bi_argcnt + 1);
#line 176

#line 176

#line 176
	if (builtin_module_trace(BUILTIN_IDX_mfmod))
#line 176
		prog_trace(env, "dlcall %lu %s %s",dlh, symname, types);;

{
	long i;
	MFMOD_FUNC func;
	struct modinfo *dltab = get_dltab();
	struct modinfo *mod;
	size_t nparam, ntypes;
	MFMOD_PARAM *vparam, retval;
	int rc;
	int varargs;
	
		if (!(mfmod_is_used(dltab, dlh)))
#line 188
		(
#line 188
	env_throw_bi(env, mfe_failure, "dlcall", _("invalid dynamic library handle"))
#line 188
)
#line 190
;
	mod = &dltab[dlh];

	dlerror(); /* Clear error state */
	func = dlsym(mod->handle, symname);
	if (!func) {
		char *err = dlerror();
		if (err)
			(
#line 198
	env_throw_bi(env, mfe_failure, "dlcall", _("can't find symbol %s in %s: %s"),symname,mod->libname,err)
#line 198
);
#line 200
		else
			(
#line 201
	env_throw_bi(env, mfe_failure, "dlcall", _("%s in module %s has NULL value"),symname,mod->libname)
#line 201
);
#line 203
	}

	nparam = (__bi_argcnt - 3);
	ntypes = strlen(types);
	varargs = 0;
	if (ntypes > 1) {
		switch (types[ntypes-1]) {
		case '+':
			--ntypes;
			varargs = nparam > ntypes;
			break;
		case '*':
			--ntypes;
			varargs = nparam >= ntypes-1;
			break;
		}
	}
		
		if (!(nparam == ntypes || varargs))
#line 221
		(
#line 221
	env_throw_bi(env, mfe_failure, "dlcall", _("number of arguments doesn't satisfy type string"))
#line 221
)
#line 223
;

	vparam = mu_calloc(nparam, sizeof(vparam[0]));
	env_function_cleanup_add(env, CLEANUP_ALWAYS, vparam, _cleanup_vparam);

	
#line 228
unroll_stack(env, __bi_argcnt + 1);
	for (i = 0; i < nparam; i++) {
		switch (types[(varargs && i >= ntypes) ? ntypes-1 : i]) {
		case 'n':
		case 'i':
		case 'd':
			vparam[i].type = mfmod_number;
			
#line 235
 ((__bi_argcnt > (i+3)) ?   get_numeric_arg(env, (i+3) + 1, &vparam[i].number) :   ((
#line 235
	env_throw_bi(env, mfe_range, "dlcall", "Argument %u is not supplied",(unsigned) (i+3))
#line 235
),(long ) 0));
			break;

		case 's':
			vparam[i].type = mfmod_string;
			
#line 240
 ((__bi_argcnt > (i+3)) ?   get_string_arg(env, (i+3) + 1, &vparam[i].string) :   ((
#line 240
	env_throw_bi(env, mfe_range, "dlcall", "Argument %u is not supplied",(unsigned) (i+3))
#line 240
),(char * ) 0));
			break;

		case 'm': {
			long n;
			mu_message_t msg;
			
			
#line 247
 ((__bi_argcnt > (i+3)) ?   get_numeric_arg(env, (i+3) + 1, &n) :   ((
#line 247
	env_throw_bi(env, mfe_range, "dlcall", "Argument %u is not supplied",(unsigned) (i+3))
#line 247
),(long ) 0));
			msg = bi_message_from_descr(env, n);
			vparam[i].type = mfmod_message;
			vparam[i].message = msg;
			break;
		}
				
		default:
			(
#line 255
	env_throw_bi(env, mfe_failure, "dlcall", "unrecognized data type in %s at position: %ld",types,i)
#line 255
);
#line 258
		}
	}
	adjust_stack(env, __bi_argcnt + 1);

	rc = func(nparam, vparam, &retval);
	if (rc < 0) {
		(
#line 264
	env_throw_bi(env, mfe_failure, "dlcall", _("call to %s in module %s failed"),symname,mod->libname)
#line 264
);
#line 267
	} else if (rc) {
		if (retval.type == mfmod_string && retval.string) {
			env_function_cleanup_add(env, CLEANUP_THROW, &retval, _cleanup_retval);
			(
#line 270
	env_throw_bi(env, rc, "dlcall", _("call to %s in module %s failed: %s"),symname,mod->libname,retval.string)
#line 270
);
#line 273
		} else {
			(
#line 274
	env_throw_bi(env, rc, "dlcall", _("call to %s in module %s failed"),symname,mod->libname)
#line 274
);
#line 277
		}
	}

	switch (retval.type) {
	case mfmod_number:
		push(env, (STKVAL)(mft_number)retval.number);
		break;

	case mfmod_string:
		pushs(env, retval.string);
		break;

	case mfmod_message:
		rc = bi_message_register(env, NULL, retval.message, MF_MSG_STANDALONE);
		if (rc < 0) {
			mu_message_destroy(&retval.message, mu_message_get_owner(retval.message));
			(
#line 293
	env_throw_bi(env, mfe_failure, "dlcall", _("no more message slots available"))
#line 293
);
#line 295
		}
		push(env, (STKVAL)(mft_number)rc);
		break;

	default:
		(
#line 300
	env_throw_bi(env, mfe_failure, "dlcall", _("unsupported return type %d in call to %s, module %s failed"),retval.type,symname,mod->libname)
#line 300
);
#line 303
	}
}

#line 305
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 305
	return;
#line 305
}

int
mfmod_error(MFMOD_PARAM *r, int errcode, char const *fmt, ...)
{
	va_list ap;
	char *buf = NULL;
	size_t size = 0;
	int rc;

	va_start(ap, fmt);
	rc = mu_vasnprintf (&buf, &size, fmt, ap);
	va_end(ap);

	if (rc)
		return -1;
	r->type = mfmod_string;
	r->string = buf;
	return errcode;
}

char const *
mfmod_data_type_str(int t)
{
	static char const *type_str[] = {
		[mfmod_number] = "number",
		[mfmod_string] = "string",
		[mfmod_message] = "message"
	};
	if (t >= 0 && t < sizeof(type_str)/sizeof(type_str[0]) && type_str[t])
		return type_str[t];
	return "UNKNOWN";
}

int
mfmod_error_argtype(MFMOD_PARAM *p, MFMOD_PARAM *r, int n, int exptype)
{
	return mfmod_error(r, mfe_inval,
			   "bad type of argument #%d: expected %s, but given %s",
			   n+1, mfmod_data_type_str(exptype),
			   mfmod_data_type_str(p[n].type));
}

 
#line 1026 "../../src/builtin/snarf.m4"

#line 1026

#line 1026
#endif /* WITH_MFMOD */
#line 1026

#line 1026
void
#line 1026
mfmod_init_builtin(void)
#line 1026
{
#line 1026
	
#line 1026
#ifdef WITH_MFMOD
#line 1026
	pp_define("WITH_MFMOD");
#line 1026
	#line 140 "mfmod.bi"
va_builtin_install_ex("dlopen", bi_dlopen, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 176 "mfmod.bi"
va_builtin_install_ex("dlcall", bi_dlcall, 0, dtype_any, 3, 0, 0|MFD_BUILTIN_VARIADIC|MFD_BUILTIN_NO_PROMOTE, dtype_number, dtype_string, dtype_string);

#line 1026 "../../src/builtin/snarf.m4"
	
#line 1026
	 mf_add_runtime_params(mfmod_cfg_param);
#line 1026
	 
#line 1026
#endif /* WITH_MFMOD */
#line 1026
}
#line 1026 "../../src/builtin/snarf.m4"

