/**
 * Copyright (C) 2021 Daniel-Constantin Mierla (asipto.com)
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * This file 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 2 of the License, or
 * (at your option) any later version
 *
 *
 * This file 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "../../core/sr_module.h"
#include "../../core/dprint.h"
#include "../../core/mod_fix.h"
#include "../../core/data_lump.h"
#include "../../core/kemi.h"


MODULE_VERSION

static int mod_init(void);
static int child_init(int);
static void mod_destroy(void);

static int w_posops_pos_append(sip_msg_t* msg, char* p1idx, char* p2val);
static int w_posops_pos_insert(sip_msg_t* msg, char* p1idx, char* p2val);
static int w_posops_pos_rm(sip_msg_t* msg, char* p1idx, char* p2len);
static int w_posops_pos_headers_start(sip_msg_t* msg, char* p1, char* p2);
static int w_posops_pos_headers_end(sip_msg_t* msg, char* p1, char* p2);
static int w_posops_pos_body_start(sip_msg_t* msg, char* p1, char* p2);
static int w_posops_pos_body_end(sip_msg_t* msg, char* p1, char* p2);
static int w_posops_pos_find_str(sip_msg_t* msg, char* p1idx, char* p2val);
static int w_posops_pos_findi_str(sip_msg_t* msg, char* p1idx, char* p2val);
static int w_posops_pos_rfind_str(sip_msg_t* msg, char* p1idx, char* p2val);
static int w_posops_pos_rfindi_str(sip_msg_t* msg, char* p1idx, char* p2val);
static int w_posops_pos_search(sip_msg_t* msg, char* p1idx, char* p2re);

typedef struct posops_data {
	int ret;
	int idx;
} posops_data_t;

static int posops_idx0 = -255;

static int pv_posops_get_pos(sip_msg_t *msg, pv_param_t *param, pv_value_t *res);
static int pv_posops_parse_pos_name(pv_spec_t *sp, str *in);

static pv_export_t mod_pvs[] = {
	{ {"pos", (sizeof("pos")-1)}, PVT_OTHER, pv_posops_get_pos, 0,
		pv_posops_parse_pos_name, 0, 0, 0 },

	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
};

/**
 *
 */
static posops_data_t _posops_data = {0};

/* clang-format off */
static cmd_export_t cmds[]={
	{"pos_append", (cmd_function)w_posops_pos_append, 2, fixup_igp_spve,
		fixup_free_igp_spve, ANY_ROUTE},
	{"pos_insert", (cmd_function)w_posops_pos_insert, 2, fixup_igp_spve,
		fixup_free_igp_spve, ANY_ROUTE},
	{"pos_rm", (cmd_function)w_posops_pos_rm, 2, fixup_igp_igp,
		fixup_free_igp_igp, ANY_ROUTE},
	{"pos_headers_start", (cmd_function)w_posops_pos_headers_start, 0, 0,
		0, ANY_ROUTE},
	{"pos_headers_end", (cmd_function)w_posops_pos_headers_end, 0, 0,
		0, ANY_ROUTE},
	{"pos_body_start", (cmd_function)w_posops_pos_body_start, 0, 0,
		0, ANY_ROUTE},
	{"pos_body_end", (cmd_function)w_posops_pos_body_end, 0, 0,
		0, ANY_ROUTE},
	{"pos_find_str", (cmd_function)w_posops_pos_find_str, 2, fixup_igp_spve,
		fixup_free_igp_spve, ANY_ROUTE},
	{"pos_findi_str", (cmd_function)w_posops_pos_findi_str, 2, fixup_igp_spve,
		fixup_free_igp_spve, ANY_ROUTE},
	{"pos_rfind_str", (cmd_function)w_posops_pos_rfind_str, 2, fixup_igp_spve,
		fixup_free_igp_spve, ANY_ROUTE},
	{"pos_rfindi_str", (cmd_function)w_posops_pos_rfindi_str, 2, fixup_igp_spve,
		fixup_free_igp_spve, ANY_ROUTE},
	{"pos_search",    (cmd_function)w_posops_pos_search, 2, fixup_igp_regexp,
		fixup_free_igp_regexp, ANY_ROUTE},

	{0, 0, 0, 0, 0, 0}
};

static param_export_t params[]={
	{"idx0",     PARAM_INT,   &posops_idx0},

	{0, 0, 0}
};

struct module_exports exports = {
	"posops",        /* module name */
	DEFAULT_DLFLAGS, /* dlopen flags */
	cmds,            /* cmd (cfg function) exports */
	params,          /* param exports */
	0,               /* exported RPC methods */
	mod_pvs,         /* exported pseudo-variables */
	0,               /* response function */
	mod_init,        /* module initialization function */
	child_init,      /* per child init function */
	mod_destroy      /* destroy function */
};
/* clang-format on */


/**
 * init module function
 */
static int mod_init(void)
{
	return 0;
}

/**
 * @brief Initialize async module children
 */
static int child_init(int rank)
{
	return 0;
}

/**
 * destroy module function
 */
static void mod_destroy(void)
{
	return;
}

/**
 *
 */
static int ki_posops_pos_append(sip_msg_t *msg, int idx, str *val)
{
	int offset;
	sr_lump_t *anchor = NULL;

	if(val==NULL || val->s==NULL || val->len<=0) {
		LM_ERR("invalid val parameter\n");
		return -1;
	}

	if(idx<0) {
		offset = msg->len + idx;
	} else {
		offset = idx;
	}
	if(offset>msg->len) {
		LM_ERR("offset invalid: %d (msg-len: %d)\n", offset, msg->len);
		return -1;
	}

	anchor = anchor_lump(msg, offset, 0, 0);
	if (insert_new_lump_after(anchor, val->s, val->len, 0) == 0) {
		LM_ERR("unable to add lump\n");
		return -1;
	}

	return 1;
}

/**
 *
 */
static int w_posops_pos_append(sip_msg_t* msg, char* p1idx, char* p2val)
{
	int idx = 0;
	str val = STR_NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}

	if(fixup_get_svalue(msg, (gparam_t*)p2val, &val)!=0) {
		LM_ERR("unable to get val parameter\n");
		return -1;
	}

	return ki_posops_pos_append(msg, idx, &val);
}

/**
 *
 */
static int ki_posops_pos_insert(sip_msg_t *msg, int idx, str *val)
{
	int offset;
	sr_lump_t *anchor = NULL;

	if(val==NULL || val->s==NULL || val->len<=0) {
		LM_ERR("invalid val parameter\n");
		return -1;
	}

	if(idx<0) {
		offset = msg->len + idx;
	} else {
		offset = idx;
	}
	if(offset>msg->len) {
		LM_ERR("offset invalid: %d (msg-len: %d)\n", offset, msg->len);
		return -1;
	}

	anchor = anchor_lump(msg, offset, 0, 0);
	if (insert_new_lump_before(anchor, val->s, val->len, 0) == 0) {
		LM_ERR("unable to add lump\n");
		return -1;
	}

	return 1;
}

/**
 *
 */
static int w_posops_pos_insert(sip_msg_t* msg, char* p1idx, char* p2val)
{
	int idx = 0;
	str val = STR_NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}

	if(fixup_get_svalue(msg, (gparam_t*)p2val, &val)!=0) {
		LM_ERR("unable to get val parameter\n");
		return -1;
	}

	return ki_posops_pos_insert(msg, idx, &val);
}


/**
 *
 */
static int ki_posops_pos_rm(sip_msg_t *msg, int idx, int len)
{
	int offset;
	sr_lump_t *anchor = NULL;

	if(len<=0) {
		LM_ERR("length invalid: %d (msg-len: %d)\n", len, msg->len);
		return -1;
	}
	if(idx<0) {
		offset = msg->len + idx;
	} else {
		offset = idx;
	}
	if(offset>msg->len) {
		LM_ERR("offset invalid: %d (msg-len: %d)\n", offset, msg->len);
		return -1;
	}
	if(offset==msg->len) {
		LM_WARN("offset at the end of message: %d (msg-len: %d)\n",
				offset, msg->len);
		return 1;
	}
	if(offset + len > msg->len) {
		LM_WARN("offset + len over the end of message: %d + %d (msg-len: %d)\n",
				offset, len, msg->len);
		return -1;
	}
	anchor=del_lump(msg, offset, len, 0);
	if (anchor==0) {
		LM_ERR("cannot remove - offset: %d - len: %d - msg-len: %d\n",
				offset, len, msg->len);
		return -1;
	}
	return 1;
}

/**
 *
 */
static int w_posops_pos_rm(sip_msg_t* msg, char* p1idx, char* p2len)
{
	int idx = 0;
	int len = 0;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}
	if(fixup_get_ivalue(msg, (gparam_t*)p2len, &len)!=0) {
		LM_ERR("unable to get len parameter\n");
		return -1;
	}

	return ki_posops_pos_rm(msg, idx, len);
}

/**
 *
 */
static int ki_posops_pos_headers_start(sip_msg_t* msg)
{
	memset(&_posops_data, 0, sizeof(posops_data_t));
	if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
		LM_ERR("failed to parse headers\n");
		return -1;
	}

	_posops_data.idx = msg->first_line.len;
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_headers_start(sip_msg_t* msg, char* p1, char* p2)
{
	return ki_posops_pos_headers_start(msg);
}

/**
 *
 */
static int ki_posops_pos_headers_end(sip_msg_t* msg)
{
	memset(&_posops_data, 0, sizeof(posops_data_t));
	if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
		LM_ERR("failed to parse headers\n");
		return -1;
	}

	_posops_data.idx = msg->unparsed - msg->buf;
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_headers_end(sip_msg_t* msg, char* p1, char* p2)
{
	return ki_posops_pos_headers_end(msg);
}

/**
 *
 */
static int ki_posops_pos_body_start(sip_msg_t* msg)
{
	char *body = 0;

	memset(&_posops_data, 0, sizeof(posops_data_t));
	if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
		LM_ERR("failed to parse headers\n");
		return -1;
	}

	body = get_body(msg);
	if(body == NULL) {
		LM_DBG("no body\n");
		return -1;
	}
	_posops_data.idx = body - msg->buf;

	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_body_start(sip_msg_t* msg, char* p1, char* p2)
{
	return ki_posops_pos_body_start(msg);
}

/**
 *
 */
static int ki_posops_pos_body_end(sip_msg_t* msg)
{
	memset(&_posops_data, 0, sizeof(posops_data_t));
	if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
		LM_ERR("failed to parse headers\n");
		return -1;
	}

	if(get_body(msg) == NULL) {
		LM_DBG("no body\n");
		return -1;
	}

	_posops_data.idx = msg->len;
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_body_end(sip_msg_t* msg, char* p1, char* p2)
{
	return  ki_posops_pos_body_end(msg);
}

/**
 *
 */
static int ki_posops_pos_find_str(sip_msg_t *msg, int idx, str *val)
{
	char *p;
	str text;

	if(val==NULL || val->s==NULL || val->len<=0) {
		return -1;
	}
	if(idx<0) {
		idx += msg->len;
	}
	if(idx<0 || idx > msg->len - val->len) {
		return -1;
	}
	text.s = msg->buf + idx;
	text.len = msg->len - idx;
	p = str_search(&text, val);
	if(p==NULL) {
		return -1;
	}

	_posops_data.idx = (int)(p - msg->buf);
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_find_str(sip_msg_t* msg, char* p1idx, char* p2val)
{
	int idx = 0;
	str val = STR_NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}

	if(fixup_get_svalue(msg, (gparam_t*)p2val, &val)!=0) {
		LM_ERR("unable to get val parameter\n");
		return -1;
	}

	return ki_posops_pos_find_str(msg, idx, &val);
}

/**
 *
 */
static int ki_posops_pos_findi_str(sip_msg_t *msg, int idx, str *val)
{
	char *p;
	str text;

	if(val==NULL || val->s==NULL || val->len<=0) {
		return -1;
	}
	if(idx<0) {
		idx += msg->len;
	}
	if(idx<0 || idx > msg->len - val->len) {
		return -1;
	}

	text.s = msg->buf + idx;
	text.len = msg->len - idx;
	p = str_casesearch(&text, val);
	if(p==NULL) {
		return -1;
	}

	_posops_data.idx = (int)(p - msg->buf);
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_findi_str(sip_msg_t* msg, char* p1idx, char* p2val)
{
	int idx = 0;
	str val = STR_NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}

	if(fixup_get_svalue(msg, (gparam_t*)p2val, &val)!=0) {
		LM_ERR("unable to get val parameter\n");
		return -1;
	}

	return ki_posops_pos_findi_str(msg, idx, &val);
}



/**
 *
 */
static int ki_posops_pos_rfind_str(sip_msg_t *msg, int idx, str *val)
{
	char *p;
	str text;

	if(val==NULL || val->s==NULL || val->len<=0) {
		return -1;
	}
	if(idx<0) {
		idx += msg->len;
	}
	if(idx<0 || idx > msg->len - val->len) {
		return -1;
	}
	text.s = msg->buf + idx;
	text.len = msg->len - idx;
	p = str_rsearch(&text, val);
	if(p==NULL) {
		return -1;
	}

	_posops_data.idx = (int)(p - msg->buf);
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_rfind_str(sip_msg_t* msg, char* p1idx, char* p2val)
{
	int idx = 0;
	str val = STR_NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}

	if(fixup_get_svalue(msg, (gparam_t*)p2val, &val)!=0) {
		LM_ERR("unable to get val parameter\n");
		return -1;
	}

	return ki_posops_pos_rfind_str(msg, idx, &val);
}

/**
 *
 */
static int ki_posops_pos_rfindi_str(sip_msg_t *msg, int idx, str *val)
{
	char *p;
	str text;

	if(val==NULL || val->s==NULL || val->len<=0) {
		return -1;
	}
	if(idx<0) {
		idx += msg->len;
	}
	if(idx<0 || idx > msg->len - val->len) {
		return -1;
	}

	text.s = msg->buf + idx;
	text.len = msg->len - idx;
	p = str_rcasesearch(&text, val);
	if(p==NULL) {
		return -1;
	}

	_posops_data.idx = (int)(p - msg->buf);
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

/**
 *
 */
static int w_posops_pos_rfindi_str(sip_msg_t* msg, char* p1idx, char* p2val)
{
	int idx = 0;
	str val = STR_NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}

	if(fixup_get_svalue(msg, (gparam_t*)p2val, &val)!=0) {
		LM_ERR("unable to get val parameter\n");
		return -1;
	}

	return ki_posops_pos_rfindi_str(msg, idx, &val);
}

/**
 *
 */
static int ki_posops_pos_search_helper(sip_msg_t *msg, int idx, regex_t *re)
{
	regmatch_t pmatch;

	if(idx<0) {
		idx += msg->len;
	}
	if(idx<0 || idx >= msg->len) {
		return -1;
	}

	if (regexec(re, msg->buf + idx, 1, &pmatch, 0)!=0) {
		return -1;
	}
	if (pmatch.rm_so==-1) {
		return -1;
	}

	_posops_data.idx = (int)(msg->buf + idx + pmatch.rm_so);
	_posops_data.ret = (_posops_data.idx==0)?posops_idx0:_posops_data.idx;

	return _posops_data.ret;
}

static int ki_posops_pos_search(sip_msg_t* msg, int idx, str* sre)
{
	regex_t mre;
	int ret;

	memset(&mre, 0, sizeof(regex_t));
	if (regcomp(&mre, sre->s, REG_EXTENDED|REG_ICASE|REG_NEWLINE)!=0) {
		LM_ERR("failed to compile regex: %.*s\n", sre->len, sre->s);
		return -1;
	}

	ret = ki_posops_pos_search_helper(msg, idx, &mre);

	regfree(&mre);

	return ret;
}

/**
 *
 */
static int w_posops_pos_search(sip_msg_t* msg, char* p1idx, char* p2re)
{
	int idx = 0;
	regex_t *re = NULL;

	if(fixup_get_ivalue(msg, (gparam_t*)p1idx, &idx)!=0) {
		LM_ERR("unable to get idx parameter\n");
		return -1;
	}
	re = (regex_t*)p2re;

	return ki_posops_pos_search_helper(msg, idx, re);
}

/**
 *
 */
static int pv_posops_get_pos(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
{
	switch(param->pvn.u.isname.name.n) {
		case 0: /* idx */
			return pv_get_sintval(msg, param, res, _posops_data.idx);
		case 1: /* ret */
			return pv_get_sintval(msg, param, res, _posops_data.ret);
	}
	return pv_get_null(msg, param, res);
}

/**
 *
 */
static int pv_posops_parse_pos_name(pv_spec_t *sp, str *in)
{
	switch(in->len) {
		case 3:
			if(strncmp(in->s, "idx", 3)==0)
				sp->pvp.pvn.u.isname.name.n = 0;
			else if(strncmp(in->s, "ret", 3)==0)
				sp->pvp.pvn.u.isname.name.n = 1;
			else goto error;
		break;

		default:
			goto error;
	}
	sp->pvp.pvn.type = PV_NAME_INTSTR;
	sp->pvp.pvn.u.isname.type = 0;

	return 0;

error:
	LM_ERR("unknown pv pos key: %.*s\n", in->len, in->s);
	return -1;
}


/**
 *
 */
/* clang-format off */
static sr_kemi_t sr_kemi_posops_exports[] = {
	{ str_init("posops"), str_init("pos_append"),
		SR_KEMIP_INT, ki_posops_pos_append,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_insert"),
		SR_KEMIP_INT, ki_posops_pos_insert,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_rm"),
		SR_KEMIP_INT, ki_posops_pos_rm,
		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_headers_start"),
		SR_KEMIP_INT, ki_posops_pos_headers_start,
		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_headers_end"),
		SR_KEMIP_INT, ki_posops_pos_headers_end,
		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_body_start"),
		SR_KEMIP_INT, ki_posops_pos_body_start,
		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_body_end"),
		SR_KEMIP_INT, ki_posops_pos_body_end,
		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_find_str"),
		SR_KEMIP_INT, ki_posops_pos_find_str,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_findi_str"),
		SR_KEMIP_INT, ki_posops_pos_findi_str,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_rfind_str"),
		SR_KEMIP_INT, ki_posops_pos_rfind_str,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_rfindi_str"),
		SR_KEMIP_INT, ki_posops_pos_rfindi_str,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("posops"), str_init("pos_search"),
		SR_KEMIP_INT, ki_posops_pos_search,
		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},

	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
};
/* clang-format on */

/**
 *
 */
int mod_register(char *path, int *dlflags, void *p1, void *p2)
{
	sr_kemi_modules_add(sr_kemi_posops_exports);
	return 0;
}