/**
 * @file      nstdc_dl.c
 * @brief     動的リンクライブラリを扱う.
 * @author    Nomura Kei
 * @copyright 2003 - 2017  Nomura Kei
 * License: New BSD License (3-cclause BSD license)
 */
#include <nstdc_dlfcn.h>

#if (nstdc_is_windows())
#include <nstdc_errno.h>
#else
#include <dlfcn.h>
#endif


/* -----------------------------------------------------------------------------
 *  内部変数
 * -----------------------------------------------------------------------------
 */
#if (nstdc_is_windows())
#define MAX_DL_MSGBUF		(256)
static int  nstdc_dl_last_errno = 0;
static char nstdc_dl_errmsg[MAX_DL_MSGBUF];
#endif

/**
 * 指定された filename のライブラリをロードします.
 *
 * @param filename ライブラリ名
 * @return ライブラリのハンドル
 */
nstdc_dl_handle_t nstdc_dlopen(const char* filename)
{
	nstdc_dl_handle_t handle;
#if (nstdc_is_windows())
	handle = LoadLibraryEx(filename, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
	nstdc_dl_last_errno = nstdc_get_errno();
#else
	handle = dlopen(filename, RTLD_LAZY);
#endif
	return handle;
}


/**
 * nstdc_dlopen, nstdc_dlsym, nstdc_dlclose のいずれかで
 * 最後に発生したエラーについての説明メッセージを返します.
 *
 * @return エラーについての説明メッセージ
 */
char* nstdc_dlerror(void)
{
#if (nstdc_is_windows())
	/* エラー番号よりエラーメッセージを取得して返す.	*/
	bool ret = nstdc_get_errmsg(nstdc_dl_last_errno,
			nstdc_dl_errmsg, sizeof(nstdc_dl_errmsg));
	if (!ret)
	{
		nstdc_dl_errmsg[0] = '\0';
	}
	return nstdc_dl_errmsg;
#else
	return dlerror();
#endif
}


/**
 * 指定されたシンボル(関数名)のポインタを返します.
 *
 * @param handle  ライブラリのハンドル
 * @param symbol  シンボル
 * @return シンボルのアドレス
 */
nstdc_dl_func_t nstdc_dlsym(nstdc_dl_handle_t handle, const char*	symbol)
{
	nstdc_dl_func_t func;
#if (nstdc_is_windows())
	func = GetProcAddress(handle, symbol);
	nstdc_dl_last_errno = nstdc_get_errno();
#else
	func = dlsym(handle, symbol);
#endif
	return func;
}


/**
 * 指定されたロード済みライブラリの参照カウントを1減らします.
 * 参照カウントが0になり, 他でロードされているライブラリによって
 * シンボルが使われていなければ, そのダイナミックライブラリは
 * アンロードされます.
 *
 * @param handle ライブラリのハンドル
 */
bool nstdc_dlclose(nstdc_dl_handle_t handle)
{
	bool result;
#if (nstdc_is_windows())
	BOOL ret = FreeLibrary(handle);
	nstdc_dl_last_errno = nstdc_get_errno();
	result = (ret != 0);
	
#else
	int ret = dlclose(handle);
	result = (ret == 0);

#endif
	return result;
}

