/*
 * DL.c
 * (C) Christian Magnusson 1999,2000, Email: d1mag@dtek.chalmers.se
 * This program is copyright, and covered by the Gnu Public License.
 * The Q bot.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "bottypes.h"
#include "globals.h"
#include "hash.h"
#include "dl.h"

DL_HANDLE DL_open(const char *pathname, int mode)
{
#ifdef HAVE_UNIX_DL

	return dlopen(pathname, mode);

#elif defined(HAVE_WIN32_DLL)

	return LoadLibrary(pathname);

#endif /* HAVE_UNIX_DL */

}

void *DL_sym(DL_HANDLE handle, const char *name)
{
#ifdef HAVE_UNIX_DL

	return dlsym(handle, name);

#elif defined(HAVE_WIN32_DLL)

	return (void *) GetProcAddress(handle, name);

#endif /* HAVE_UNIX_DL */

}

int DL_close(DL_HANDLE handle)
{
#ifdef HAVE_UNIX_DL

	return dlclose(handle);

#elif defined(HAVE_WIN32_DLL)

	if (FreeLibrary(handle))
		return 0;

	return -1;

#endif /* HAVE_UNIX_DL */

}

char *DL_error(void)
{
#ifdef HAVE_UNIX_DL

	return dlerror();

#elif defined(HAVE_WIN32_DLL)

	return "Error in WIN32 DL";

#endif /* HAVE_UNIX_DL */

}

int remove_command(char *cmd)
{
	dl_command_entry_t *dl;
	dl = findcommand(cmd);
	if (dl) {
		dl->name[0] = '\0';
		dl->help[0] = '\0';
		dl->summary[0] = '\0';
		dl->level = 0;
		dl->func = NULL;
	}
	return 1;
}


int dll_init_func(ListElem * entry)
{
	dl_command_entry_t *dl;
	char init_func[255];
	char *cp;
	int (*func) (void *, void *);

	if (!entry) {
		return 0;
	}

	dl = (dl_command_entry_t *) (entry->data);

	if (dl && dl->val && dl->name) {
		snprintf(init_func, 200, "_init_%s", dl->name);

		cp = strchr(init_func, '.');

		if (cp)
			*cp = '\0';

		func = (int (*)(void *, void *)) DL_sym(dl->val, init_func);

		if (func) {
			if (!func((void *) dl, NULL)) {
#ifdef DEBUG
				fprintf(stderr, "dll_init_func: Error exec [%s]\n", init_func);
				log("dll_init_func: Error exec [%s]\n", init_func);
#endif
				return 0;
			}
		} else {
#ifdef DEBUG
			fprintf(stderr, "dll_init_func: Couldn't find [%s]\n", init_func);
			log("dll_init_func: Couldn't find [%s]\n", init_func);
#endif
		}
	}
	return 1;
}

int unload_command(char *cmd)
{
	char *derr;
	ListElem *el, *el2;
	dl_command_entry_t *dl;
	if (cmd == NULL) {
		el = GetFirstH(commands);
		while (el) {
			el2 = GetSuccH(commands, el);
			Remove(el);
			dl = (dl_command_entry_t *) el->data;
			DL_close(dl->val);
			FreeElem(el);
			el = el2;
		}
	} else {
		el = GetFirstH(commands);
		while (el) {
			dl = (dl_command_entry_t *) el->data;
			if (strcasecmp(dl->name, cmd) == 0) {
				if (DL_close(dl->val)) {
					derr = DL_error();
					fprintf(stderr, "init_dllib: DL_close [%s] failed [%s]\n", cmd, derr);
					log("init_dllib: DL_close [%s] failed [%s]\n", cmd, derr);
					return -1;
				}
				Remove(el);
				FreeElem(el);
				break;
			}
			el = GetSuccH(commands, el);
		}
		if (!el)
			return 0;									/* command not found */
	}
	/* unloaded successfully */
	return 1;
}

int load_command(char *name)
{
	dl_command_entry_t *dl;
	char file[255];
	int dir_len, namel;
	char *derr;
	ListElem *el;

	if (!name) {
		return 0;
	}


	dir_len = strlen(q_commands_dir);
	(void) memcpy(file, q_commands_dir, dir_len);
	file[dir_len] = '/';

	dl = Malloc(sizeof(dl_command_entry_t));
	strncpy(dl->name, name, MAX_COMMAND_LEN);
	dl->name[MAX_COMMAND_LEN] = '\0';
	namel = strlen(dl->name);

	(void) memcpy(file + dir_len + 1, name, namel);
	file[dir_len + namel + 1] = '\0';
	strcat(file, ".so");

	if (!(dl->val = DL_open(file, RTLD_LAZY))) {
		/* Couldn't open that lib, but continue anyway */
		derr = DL_error();
		fprintf(stderr, "init_dllib: DL_open [%s]  failed [%s]\n", file, derr);
		log("init_dllib: DL_open [%s]  failed [%s]\n", file, derr);
		Free(dl);
		return 0;
	} else {
		el = NewElem(dl);
		AddElem(commands, el);
		if (!dll_init_func(el)) {
			fprintf(stderr, "init_dllib: error dll_init_func [%s]\n", file);
			log("init_dllib: error dll_init_func [%s]\n", file);
			Remove(el);
			FreeElem(el);
			return 0;
		}
	}
	return 1;
}

int init_dllib(void)
{
	FILE *fp;
	int namel, dir_len, ret = 0;
	char name[100];
	char file[512 + 1];
	char *derr;
	ListElem *el;
	dl_command_entry_t *dl;

	fp = fopen(COMMANDS_FILE, "r");
	if (fp == NULL) {
		fprintf(stderr, "Couldn't find [%s]\a\n", COMMANDS_FILE);
		return 0;
	}

	dir_len = strlen(q_commands_dir);
	(void) memcpy(file, q_commands_dir, dir_len);
	file[dir_len] = '/';

	while (!feof(fp)) {
		if (fscanf(fp, "%s\n", name) != 1)
			break;

		dl = Malloc(sizeof(dl_command_entry_t));

		strncpy(dl->name, name, MAX_COMMAND_LEN);
		dl->name[MAX_COMMAND_LEN] = '\0';
		namel = strlen(dl->name);

		(void) memcpy(file + dir_len + 1, name, namel);
		file[dir_len + namel + 1] = '\0';
		strcat(file, ".so");

		if (!(dl->val = DL_open(file, RTLD_LAZY))) {
			/* Couldn't open that lib, but continue anyway */
			derr = DL_error();
			fprintf(stderr, "init_dllib: DL_open [%s]  failed [%s]\n", file, derr);
			Free(dl);
			ret = -1;
		} else {
			el = NewElem(dl);
			AddElem(commands, el);
			if (!dll_init_func(el)) {
				log("init_dllib: error dll_init_func [%s]\n", file);
				Remove(el);
				FreeElem(el);
			}
		}
	}
	fclose(fp);
	return 1;
}

#if 0
int save_dllib(void)
{
	FILE *fp;
	int namel, dir_len, ret = 0;
	char name[100];
	char file[512 + 1];
	char *derr;
	ListElem *el;
	dl_command_entry_t *dl;

	fp = fopen(COMMANDS_FILE, "w");
	if (fp == NULL) {
		fprintf(stderr, "Couldn't find [%s]\a\n", COMMANDS_FILE);
		return 0;
	}

	dir_len = strlen(q_commands_dir);
	(void) memcpy(file, q_commands_dir, dir_len);
	file[dir_len] = '/';

	while (!feof(fp)) {
		if (fscanf(fp, "%s\n", name) != 1)
			break;

		dl = Malloc(sizeof(dl_command_entry_t));

		strncpy(dl->name, name, MAX_COMMAND_LEN);
		dl->name[MAX_COMMAND_LEN] = '\0';
		namel = strlen(dl->name);
#ifdef DEBUG
		fprintf(stderr, "found [%s]\n", dl->name);
#endif

		(void) memcpy(file + dir_len + 1, name, namel);
		file[dir_len + namel + 1] = '\0';
		strcat(file, ".so");

		if (!(dl->val = DL_open(file, RTLD_LAZY))) {
			/* Couldn't open that lib, but continue anyway */
			derr = DL_error();
			fprintf(stderr, "init_dllib: DL_open [%s]  failed [%s]\n", file, derr);
			Free(dl);
			ret = -1;
		} else {
#ifdef DEBUG
			fprintf(stderr, "init_dllib: found lib [%s]\n", file);
#endif
			el = NewElem(dl);
			AddElem(commands, el);
			if (!dll_init_func(el)) {
				log("init_dllib: error dll_init_func [%s]\n", file);
				Remove(el);
				FreeElem(el);
			}
		}
	}
	fclose(fp);
	return 1;
}
#endif
