/* ltdl.c -- system independent dlopen wrapper
   Copyright (C) 1998-2000 Free Software Foundation, Inc.
   Originally by Thomas Tanner <tanner@ffii.org>
   This file is part of GNU Libtool.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

As a special exception to the GNU Lesser General Public License,
if you distribute this file as part of a program or library that
is built using GNU libtool, you may include it under the same
distribution terms that you use for the rest of that program.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307  USA

*/

#define _LTDL_COMPILE_

#if HAVE_CONFIG_H
#include <config.h>
#endif

#if HAVE_STRING_H
#include <string.h>
#endif

#if HAVE_STRINGS_H
#include <strings.h>
#endif

#if HAVE_CTYPE_H
#include <ctype.h>
#endif

#if HAVE_MALLOC_H
#include <malloc.h>
#endif

#if HAVE_MEMORY_H
#include <memory.h>
#endif

#if HAVE_STDLIB_H
#include <stdlib.h>
#endif

#if HAVE_STDIO_H
#include <stdio.h>
#endif

#include "ltdl.h"

#ifdef DLL_EXPORT
#  define LTDL_GLOBAL_DATA	__declspec(dllexport)
#else
#  define LTDL_GLOBAL_DATA
#endif

/* max. filename length */
#ifndef LTDL_FILENAME_MAX
#define LTDL_FILENAME_MAX 1024
#endif

#undef	LTDL_READTEXT_MODE
/* fopen() mode flags for reading a text file */
#ifdef _WIN32
#define LTDL_READTEXT_MODE "rt"
#else
#define LTDL_READTEXT_MODE "r"
#endif

#undef	LTDL_SYMBOL_LENGTH
/* This is the maximum symbol size that won't require malloc/free */
#define LTDL_SYMBOL_LENGTH	128

#undef	LTDL_SYMBOL_OVERHEAD
/* This accounts for the _LTX_ separator */
#define LTDL_SYMBOL_OVERHEAD	5

/* NOTE: typedefed in ltdl.h
   This structure is used for the list of registered loaders. */
struct lt_dlloader_t {
	struct lt_dlloader_t *next;
	const char *loader_name; /* identifying name for each loader */
	const char *sym_prefix;	 /* prefix for symbols */
	lt_module_open_t *module_open;
	lt_module_close_t *module_close;
	lt_find_sym_t *find_sym;
	lt_dlloader_exit_t *dlloader_exit;
  	lt_dlloader_data_t dlloader_data;
};

typedef	struct lt_dlhandle_t {
	struct lt_dlhandle_t *next;
	lt_dlloader_t *loader;	/* dlopening interface */
	lt_dlinfo info;
	int	depcount;	/* number of dependencies */
	lt_dlhandle *deplibs;	/* dependencies */
	lt_module_t module;	/* system module handle */
	lt_ptr_t system;	/* system specific data */
	lt_ptr_t app_private;	/* application private data */
} lt_dlhandle_t;


static const char objdir[] = LTDL_OBJDIR;
#ifdef	LTDL_SHLIB_EXT
static const char shlib_ext[] = LTDL_SHLIB_EXT;
#endif
#ifdef	LTDL_SYSSEARCHPATH
static const char sys_search_path[] = LTDL_SYSSEARCHPATH;
#endif

/* Extract the diagnostic strings from the error table macro in the same
   order as the enumberated indices in ltdl.h. */
#define LTDL_ERROR(name, diagnostic)	(diagnostic),
static const char *ltdl_error_strings[] = {
	ltdl_error_table
	0
};
#undef LTDL_ERROR

#ifdef __STDC__
#  define LT_DLSTRERROR(name)	ltdl_error_strings[LTDL_ERROR_##name]
#else
#  define LT_DLSTRERROR(name)	ltdl_error_strings[LTDL_ERROR_/**/name]
#endif

static const char *last_error = 0;

LTDL_GLOBAL_DATA lt_ptr_t (*lt_dlmalloc) LTDL_PARAMS((size_t size)) = (lt_ptr_t(*)LTDL_PARAMS((size_t)))malloc;
LTDL_GLOBAL_DATA void	 (*lt_dlfree)  LTDL_PARAMS((lt_ptr_t ptr)) = (void(*)LTDL_PARAMS((lt_ptr_t)))free;

#undef strdup
#define strdup xstrdup

static inline char *
strdup(str)
	const char *str;
{
	char *tmp;

	if (!str)
		return 0;
	tmp = (char*) lt_dlmalloc(strlen(str)+1);
	if (tmp)
		strcpy(tmp, str);
	return tmp;
}

#if ! HAVE_STRCMP

#undef strcmp
#define strcmp xstrcmp

static inline int
strcmp (str1, str2)
	const char *str1;
	const char *str2;
{
	if (str1 == str2)
		return 0;
	if (str1 == 0)
		return -1;
	if (str2 == 0)
		return 1;
		
	for (;*str1 && *str2; str1++, str2++)
		if (*str1 != *str2)
			break;
	
	return (int)(*str1 - *str2);
}
#endif
		

#if ! HAVE_STRCHR

# if HAVE_INDEX

#  define strchr index

# else

#  define strchr xstrchr

static inline const char*
strchr(str, ch)
	const char *str;
	int ch;
{
	const char *p;

	for (p = str; *p != (char)ch && *p != '\0'; p++)
		/*NOWORK*/;

	return (*p == (char)ch) ? p : 0;
}

# endif

#endif

#if ! HAVE_STRRCHR

# if HAVE_RINDEX

#  define strrchr rindex

# else

#  define strrchr xstrrchr

static inline const char*
strrchr(str, ch)
	const char *str;
	int ch;
{
	const char *p;

	for (p = str; *p != '\0'; p++)
		/*NOWORK*/;

	while (*p != (char)ch && p >= str)
		p--;

	return (*p == (char)ch) ? p : 0;
}

# endif

#endif

/* The Cygwin dlopen implementation prints a spurious error message to
   stderr if its call to LoadLibrary() fails for any reason.  We can
   mitigate this by not using the Cygwin implementation, and falling
   back to our own LoadLibrary() wrapper. */
#if HAVE_LIBDL && !defined(__CYGWIN__)

/* dynamic linking with dlopen/dlsym */

#if HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#ifdef RTLD_GLOBAL
# define LTDL_GLOBAL	RTLD_GLOBAL
#else
# ifdef DL_GLOBAL
#  define LTDL_GLOBAL	DL_GLOBAL
# else
#  define LTDL_GLOBAL	0
# endif
#endif

/* We may have to define LTDL_LAZY_OR_NOW in the command line if we
   find out it does not work in some platform. */
#ifndef LTDL_LAZY_OR_NOW
# ifdef RTLD_LAZY
#  define LTDL_LAZY_OR_NOW	RTLD_LAZY
# else
#  ifdef DL_LAZY
#   define LTDL_LAZY_OR_NOW	DL_LAZY
#  else
#   ifdef RTLD_NOW
#    define LTDL_LAZY_OR_NOW	RTLD_NOW
#   else
#    ifdef DL_NOW
#     define LTDL_LAZY_OR_NOW	DL_NOW
#    else
#     define LTDL_LAZY_OR_NOW	0
#    endif
#   endif
#  endif
# endif
#endif

int lt_dlopen_flag = LTDL_LAZY_OR_NOW;

static lt_module_t
sys_dl_open (loader_data, filename)
	lt_dlloader_data_t loader_data;
	const char *filename;
{
	lt_module_t module = dlopen(filename, lt_dlopen_flag);
	if (!module) {
#if HAVE_DLERROR
		last_error = dlerror();
#else
		last_error = LT_DLSTRERROR(CANNOT_OPEN);
#endif
	}
	return module;
}

static int
sys_dl_close (loader_data, module)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
{
	if (dlclose(module) != 0) {
#if HAVE_DLERROR
		last_error = dlerror();
#else
		last_error = LT_DLSTRERROR(CANNOT_CLOSE);
#endif
		return 1;
	}
	return 0;
}

static lt_ptr_t
sys_dl_sym (loader_data, module, symbol)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
	const char *symbol;
{
	lt_ptr_t address = dlsym(module, symbol);
	
	if (!address)
#if HAVE_DLERROR
		last_error = dlerror();
#else
		last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
#endif
	return address;
}

static struct lt_user_dlloader sys_dl = {
#  ifdef NEED_USCORE
	   "_",
#  else
	   0,
#  endif
	   sys_dl_open, sys_dl_close, sys_dl_sym, 0, 0 };

#else
int lt_dlopen_flag = 0;
#endif /* HAVE_LIBDL */

#if HAVE_SHL_LOAD

/* dynamic linking with shl_load (HP-UX) (comments from gmodule) */

#ifdef HAVE_DL_H
#include <dl.h>
#endif

/* some flags are missing on some systems, so we provide
 * harmless defaults.
 *
 * Mandatory:
 * BIND_IMMEDIATE  - Resolve symbol references when the library is loaded.
 * BIND_DEFERRED   - Delay code symbol resolution until actual reference.
 *
 * Optionally:
 * BIND_FIRST	   - Place the library at the head of the symbol search order.
 * BIND_NONFATAL   - The default BIND_IMMEDIATE behavior is to treat all unsatisfied
 *		     symbols as fatal.	This flag allows binding of unsatisfied code
 *		     symbols to be deferred until use.
 *		     [Perl: For certain libraries, like DCE, deferred binding often
 *		     causes run time problems.	Adding BIND_NONFATAL to BIND_IMMEDIATE
 *		     still allows unresolved references in situations like this.]
 * BIND_NOSTART	   - Do not call the initializer for the shared library when the
 *		     library is loaded, nor on a future call to shl_unload().
 * BIND_VERBOSE	   - Print verbose messages concerning possible unsatisfied symbols.
 *
 * hp9000s700/hp9000s800:
 * BIND_RESTRICTED - Restrict symbols visible by the library to those present at
 *		     library load time.
 * DYNAMIC_PATH	   - Allow the loader to dynamically search for the library specified
 *		     by the path argument.
 */

#ifndef	DYNAMIC_PATH
#define	DYNAMIC_PATH	0
#endif	/* DYNAMIC_PATH */
#ifndef	BIND_RESTRICTED
#define	BIND_RESTRICTED	0
#endif	/* BIND_RESTRICTED */

#define	LTDL_BIND_FLAGS	(BIND_IMMEDIATE | BIND_NONFATAL | DYNAMIC_PATH)

static lt_module_t
sys_shl_open (loader_data, filename)
	lt_dlloader_data_t loader_data;
	const char *filename;
{
	lt_module_t module = shl_load(filename, LTDL_BIND_FLAGS, 0L);
	if (!module) {
		last_error = LT_DLSTRERROR(CANNOT_OPEN);
	}
	return module;
}

static int
sys_shl_close (loader_data, module)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
{
	if (shl_unload((shl_t) (module)) != 0) {
		last_error = LT_DLSTRERROR(CANNOT_CLOSE);
		return 1;
	}
	return 0;
}

static lt_ptr_t
sys_shl_sym (loader_data, module, symbol)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
	const char *symbol;
{
	lt_ptr_t address;

	if (module && shl_findsym((shl_t*) &module,
	    symbol, TYPE_UNDEFINED, &address) == 0)
		if (address)
			return address;
	last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
	return 0;
}

static struct lt_user_dlloader
sys_shl = { 0, sys_shl_open, sys_shl_close, sys_shl_sym, 0, 0 };

#undef LTDL_TYPE_TOP
#define LTDL_TYPE_TOP &sys_shl

#endif

#ifdef _WIN32

/* dynamic linking for Win32 */

#include <windows.h>

/* Forward declaration; required to implement handle search below. */
static lt_dlhandle handles;

static lt_module_t
sys_wll_open (loader_data, filename)
	lt_dlloader_data_t loader_data;
	const char *filename;
{
	lt_dlhandle cur;
	lt_module_t module;
	char *searchname = 0;
        char *ext;
        char self_name_buf[MAX_PATH];

	if (!filename) {
		/* Get the name of main module */
		*self_name_buf = 0;
		GetModuleFileName(NULL, self_name_buf, sizeof(self_name_buf));
		filename = ext = self_name_buf;
	}
	else ext = strrchr(filename, '.');

	if (ext) {
		/* FILENAME already has an extension. */
		searchname = strdup(filename);
	} else {
		/* Append a `.' to stop Windows from adding an
		   implicit `.dll' extension. */
		searchname = (char*)lt_dlmalloc(2+ strlen(filename));
		if (!searchname) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 0;
		}
		strcpy(searchname, filename);
		strcat(searchname, ".");
	}

	module = LoadLibrary(searchname);
	lt_dlfree(searchname);
	
	/* libltdl expects this function to fail if it is unable
	   to physically load the library.  Sadly, LoadLibrary
	   will search the loaded libraries for a match and return
	   one of them if the path search load fails.

	   We check whether LoadLibrary is returning a handle to
	   an already loaded module, and simulate failure if we
	   find one. */
	cur = handles;
	while (cur) {
		if (!cur->module) {
			cur = 0;
			break;
		}
		if (cur->module == module)
			break;
		cur = cur->next;
	}

	if (cur || !module) {
		last_error = LT_DLSTRERROR(CANNOT_OPEN);
		return 0;
	}

	return module;
}

static int
sys_wll_close (loader_data, module)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
{
	if (FreeLibrary(module) == 0) {
		last_error = LT_DLSTRERROR(CANNOT_CLOSE);
		return 1;
	}
	return 0;
}

static lt_ptr_t
sys_wll_sym (loader_data, module, symbol)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
	const char *symbol;
{
	lt_ptr_t address = GetProcAddress(module, symbol);
	
	if (!address)
		last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
	return address;
}

static struct lt_user_dlloader
sys_wll = { 0, sys_wll_open, sys_wll_close, sys_wll_sym, 0, 0 };

#endif

#ifdef __BEOS__

/* dynamic linking for BeOS */

#include <kernel/image.h>

static lt_module_t
sys_bedl_open (loader_data, filename)
	lt_dlloader_data_t loader_data;
	const char *filename;
{
	image_id image = 0;
	
	if (filename) {
		image = load_add_on(filename);
	} else {
		image_info info; 
		int32 cookie = 0; 
		if (get_next_image_info(0, &cookie, &info) == B_OK)
			image = load_add_on(info.name);
	}
	if (image <= 0) {
		last_error = LT_DLSTRERROR(CANNOT_OPEN);
		return 0;
	}

	return (lt_module_t) image;
}

static int
sys_bedl_close (loader_data, module)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
{
	if (unload_add_on((image_id)module) != B_OK) {
		last_error = LT_DLSTRERROR(CANNOT_CLOSE);
		return 1;
	}
	return 0;
}

static lt_ptr_t
sys_bedl_sym (loader_data, module, symbol)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
	const char *symbol;
{
	lt_ptr_t address = 0;
	image_id image = (image_id)module;
   
	if (get_image_symbol(image, symbol, B_SYMBOL_TYPE_ANY,
		&address) != B_OK) {
		last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
		return 0;
	}
	return address;
}

static struct lt_user_dlloader
sys_bedl = { 0, sys_bedl_open, sys_bedl_close, sys_bedl_sym, 0, 0 };

#endif

#if HAVE_DLD

/* dynamic linking with dld */

#if HAVE_DLD_H
#include <dld.h>
#endif

static lt_module_t
sys_dld_open (loader_data, filename)
	lt_dlloader_data_t loader_data;
	const char *filename;
{
	lt_module_t module = strdup(filename);
	if (!module) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		return 0;
	}
	if (dld_link(filename) != 0) {
		last_error = LT_DLSTRERROR(CANNOT_OPEN);
		lt_dlfree(module);
		return 0;
	}
	return module;
}

static int
sys_dld_close (loader_data, module)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
{
	if (dld_unlink_by_file((char*)(module), 1) != 0) {
		last_error = LT_DLSTRERROR(CANNOT_CLOSE);
		return 1;
	}
	lt_dlfree(module);
	return 0;
}

static lt_ptr_t
sys_dld_sym (loader_data, module, symbol)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
	const char *symbol;
{
	lt_ptr_t address = dld_get_func(symbol);

	if (!address)
		last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
	return address;
}

static struct lt_user_dlloader
sys_dld = { 0, sys_dld_open, sys_dld_close, sys_dld_sym, 0, 0 };

#endif

/* emulate dynamic linking using preloaded_symbols */

typedef struct lt_dlsymlists_t {
	struct lt_dlsymlists_t *next;
	const lt_dlsymlist *syms;
} lt_dlsymlists_t;

static const lt_dlsymlist *default_preloaded_symbols = 0;
static lt_dlsymlists_t *preloaded_symbols = 0;

static int
presym_init (loader_data)
	lt_dlloader_data_t loader_data;
{
	preloaded_symbols = 0;
	if (default_preloaded_symbols)
		return lt_dlpreload(default_preloaded_symbols);
	return 0;
}

static int
presym_free_symlists LTDL_PARAMS((void))
{
	lt_dlsymlists_t	*lists = preloaded_symbols;
	
	while (lists) {
		lt_dlsymlists_t	*tmp = lists;
		
		lists = lists->next;
		lt_dlfree(tmp);
	}
	preloaded_symbols = 0;
	return 0;
}

static int
presym_exit (loader_data)
	lt_dlloader_data_t loader_data;
{
	presym_free_symlists();
	return 0;
}

static int
presym_add_symlist (preloaded)
	const lt_dlsymlist *preloaded;
{
	lt_dlsymlists_t *tmp;
	lt_dlsymlists_t *lists = preloaded_symbols;
	
	while (lists) {
		if (lists->syms == preloaded)
			return 0;
		lists = lists->next;
	}

	tmp = (lt_dlsymlists_t*) lt_dlmalloc(sizeof(lt_dlsymlists_t));
	if (!tmp) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		return 1;
	}
	tmp->syms = preloaded;
	tmp->next = preloaded_symbols;
	preloaded_symbols = tmp;
	return 0;
}

static lt_module_t
presym_open (loader_data, filename)
	lt_dlloader_data_t loader_data;
	const char *filename;
{
	lt_dlsymlists_t *lists = preloaded_symbols;

	if (!lists) {
		last_error = LT_DLSTRERROR(NO_SYMBOLS);
		return 0;
	}
	if (!filename)
		filename = "@PROGRAM@";
	while (lists) {
		const lt_dlsymlist *syms = lists->syms;
	
		while (syms->name) {
			if (!syms->address &&
			    strcmp(syms->name, filename) == 0) {
				return (lt_module_t) syms;
			}
			syms++;
		}
		lists = lists->next;
	}
	last_error = LT_DLSTRERROR(FILE_NOT_FOUND);
	return 0;
}

static int
presym_close (loader_data, module)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
{
	/* Just to silence gcc -Wall */
	module = 0;
	return 0;
}

static lt_ptr_t
presym_sym (loader_data, module, symbol)
	lt_dlloader_data_t loader_data;
	lt_module_t module;
	const char *symbol;
{
	lt_dlsymlist *syms = (lt_dlsymlist*)(module);

	syms++;
	while (syms->address) {
		if (strcmp(syms->name, symbol) == 0)
			return syms->address;
		syms++;
	}
	last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
	return 0;
}

static struct lt_user_dlloader
presym = { 0, presym_open, presym_close, presym_sym, presym_exit, 0 };


static char *user_search_path = 0;
static lt_dlloader_t *loaders = 0;
static lt_dlhandle handles = 0;
static int initialized = 0;

int
lt_dlinit LTDL_PARAMS((void))
{
	/* initialize libltdl */
	int errors = 0;

	if (initialized) {	/* Initialize only at first call. */
		initialized++;
		return 0;
	}
	handles = 0;
	user_search_path = 0; /* empty search path */
	
#if HAVE_LIBDL && !defined(__CYGWIN__)
	errors += lt_dlloader_add (lt_dlloader_next(0), &sys_dl, "dlopen");
#endif	
#if HAVE_SHL_LOAD
	errors += lt_dlloader_add (lt_dlloader_next(0), &sys_shl, "dlopen");
#endif
#ifdef _WIN32
	errors += lt_dlloader_add (lt_dlloader_next(0), &sys_wll, "dlopen");
#endif
#ifdef __BEOS__
	errors += lt_dlloader_add (lt_dlloader_next(0), &sys_bedl, "dlopen");
#endif
#if HAVE_DLD
	errors += lt_dlloader_add (lt_dlloader_next(0), &sys_dld, "dld");
#endif
	errors += lt_dlloader_add (lt_dlloader_next(0), &presym, "dlpreload");
	if (presym_init(presym.dlloader_data)) {
		last_error = LT_DLSTRERROR(INIT_LOADER);
			return 1;
	}

	if (errors != 0) {
		last_error = LT_DLSTRERROR(DLOPEN_NOT_SUPPORTED);
		return 1;
	}
	last_error = 0;
	initialized = 1;
	return 0;
}

int
lt_dlpreload (preloaded)
	const lt_dlsymlist *preloaded;
{
	if (preloaded)
		return presym_add_symlist(preloaded);
	presym_free_symlists();
	if (default_preloaded_symbols)
		return lt_dlpreload(default_preloaded_symbols);
	return 0;
}

int
lt_dlpreload_default (preloaded)
	const lt_dlsymlist *preloaded;
{
	default_preloaded_symbols = preloaded;
	return 0;
}

int
lt_dlexit LTDL_PARAMS((void))
{
	/* shut down libltdl */
	lt_dlloader_t *loader = loaders;
	int	errors, level;
	
	if (!initialized) {
		last_error = LT_DLSTRERROR(SHUTDOWN);
		return 1;
	}
	if (initialized != 1) { /* shut down only at last call. */
		initialized--;
		return 0;
	}
	/* close all modules */
	errors = 0;
	for (level = 1; handles; level++) {
		lt_dlhandle cur = handles;
		while (cur) {
			lt_dlhandle tmp = cur;
			cur = cur->next;
			if (tmp->info.ref_count <= level)
				if (lt_dlclose(tmp))
					errors++;
		}
	}
	/* close all loaders */
	while (loader) {
	  	lt_dlloader_t *next = loader->next;
		lt_dlloader_data_t data = loader->dlloader_data;
		if (loader->dlloader_exit && loader->dlloader_exit(data))
			errors++;
		lt_dlfree (loader);
		loader = next;
	}

	loaders = 0;
	initialized = 0;
	return errors;
}

static int
tryall_dlopen (handle, filename)
	lt_dlhandle *handle;
	const char *filename;
{
	lt_dlhandle cur = handles;
	lt_dlloader_t *loader = loaders;
	const char *saved_error = last_error;
	
	/* check whether the module was already opened */
	while (cur) {
		/* try to dlopen the program itself? */
		if (!cur->info.filename && !filename)
			break;
		if (cur->info.filename && filename && 
		    strcmp(cur->info.filename, filename) == 0)
			break;
		cur = cur->next;
	}

	if (cur) {
		cur->info.ref_count++;
		*handle = cur;
		return 0;
	}
	
	cur = *handle;
	if (filename) {
		cur->info.filename = strdup(filename);
		if (!cur->info.filename) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 1;
		}
	} else
		cur->info.filename = 0;
	while (loader) {
		lt_dlloader_data_t data = loader->dlloader_data;
		cur->module = loader->module_open(data, filename);
		if (cur->module != 0)
			break;
		loader = loader->next;
	}
	if (!loader) {
		if (cur->info.filename)
			lt_dlfree(cur->info.filename);
		return 1;
	}
	cur->loader = loader;
	last_error = saved_error;
	return 0;
}

static int
find_module (handle, dir, libdir, dlname, old_name, installed)
	lt_dlhandle *handle;
	const char *dir;
	const char *libdir;
	const char *dlname;
	const char *old_name;
	int installed;
{
	int	error;
	char	*filename;
	/* try to open the old library first; if it was dlpreopened, 
	   we want the preopened version of it, even if a dlopenable
	   module is available */
	if (old_name && tryall_dlopen(handle, old_name) == 0)
		return 0;
	/* try to open the dynamic library */
	if (dlname) {
		/* try to open the installed module */
		if (installed && libdir) {
			filename = (char*)
				lt_dlmalloc(strlen(libdir)+1+strlen(dlname)+1);
			if (!filename) {
				last_error = LT_DLSTRERROR(NO_MEMORY);
				return 1;
			}
			sprintf (filename, "%s/%s", libdir, dlname);
			error = tryall_dlopen(handle, filename) != 0;
			lt_dlfree(filename);
			if (!error)
				return 0;
		}
		/* try to open the not-installed module */
		if (!installed) {
			filename = (char*)
				lt_dlmalloc((dir ? strlen(dir) : 0)
				       + strlen(objdir)	+ strlen(dlname) + 1);
			if (!filename) {
				last_error = LT_DLSTRERROR(NO_MEMORY);
				return 1;
			}
			if (dir)
				strcpy(filename, dir);
			else
				*filename = 0;
			strcat(filename, objdir);
			strcat(filename, dlname);

			error = tryall_dlopen(handle, filename) != 0;
			lt_dlfree(filename);
			if (!error)
				return 0;
		}
		/* maybe it was moved to another directory */
		{
			filename = (char*)
				lt_dlmalloc((dir ? strlen(dir) : 0)
				       + strlen(dlname) + 1);
			if (dir)
				strcpy(filename, dir);
			else
				*filename = 0;
			strcat(filename, dlname);
			error = tryall_dlopen(handle, filename) != 0;
			lt_dlfree(filename);
			if (!error)
				return 0;
		}
	}
	return 1;
}

static char*
canonicalize_path (path)
	const char *path;
{
	char *canonical = 0;
	
	if (path && *path) {
		char *ptr = strdup (path);
		canonical = ptr;
#ifdef LTDL_DIRSEP_CHAR
		/* Avoid this overhead where '/' is the only separator. */
		while (ptr = strchr (ptr, LTDL_DIRSEP_CHAR))
			*ptr++ = '/';
#endif
	}

	return canonical;
}

static lt_ptr_t
find_file (basename, search_path, pdir, handle)
	const char *basename;
	const char *search_path;
	char **pdir;
	lt_dlhandle *handle;
{
	/* when handle != NULL search a library, otherwise a file */
	/* return NULL on failure, otherwise the file/handle */

	lt_ptr_t result = 0;
	char	*filename = 0;
	int     filenamesize = 0;
	int	lenbase = strlen(basename);
	char	*canonical = 0, *next = 0;
	
	if (!search_path || !*search_path) {
		last_error = LT_DLSTRERROR(FILE_NOT_FOUND);
		return 0;
	}
	canonical = canonicalize_path (search_path);
	if (!canonical) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		goto cleanup;
	}
	next = canonical;
	while (next) {
		int lendir;
		char *cur = next;

		next = strchr(cur, LTDL_PATHSEP_CHAR);
		if (!next)
			next = cur + strlen(cur);
		lendir = next - cur;
		if (*next == LTDL_PATHSEP_CHAR)
			++next;
		else
			next = 0;
		if (lendir == 0)
			continue;
		if (lendir + 1 + lenbase >= filenamesize) {
			if (filename)
				lt_dlfree(filename);
			filenamesize = lendir + 1 + lenbase + 1;
			filename = (char*) lt_dlmalloc(filenamesize);
			if (!filename) {
				last_error = LT_DLSTRERROR(NO_MEMORY);
				goto cleanup;
			}
		}
		strncpy(filename, cur, lendir);
		if (filename[lendir-1] != '/')
			filename[lendir++] = '/';
		strcpy(filename+lendir, basename);
		if (handle) {
			if (tryall_dlopen(handle, filename) == 0) {
				result = (lt_ptr_t) handle;
				goto cleanup;
			}
		} else {
			FILE *file = fopen(filename, LTDL_READTEXT_MODE);
			if (file) {
				if (*pdir)
					lt_dlfree(*pdir);
				filename[lendir] = '\0';
				*pdir = strdup(filename);
				if (!*pdir) {
					/* We could have even avoided the
					   strdup, but there would be some
					   memory overhead. */
					*pdir = filename;
					filename = 0;
				}
				result = (lt_ptr_t) file;
				goto cleanup;
			}
		}
	}
	last_error = LT_DLSTRERROR(FILE_NOT_FOUND);
cleanup:
	if (filename)
		lt_dlfree(filename);
	if (canonical)
		lt_dlfree(canonical);
	return result;
}

static int
load_deplibs(handle, deplibs)
	lt_dlhandle handle;
	char *deplibs;
{
	char	*p, *save_search_path;
	int	i;
	int	ret = 1, depcount = 0;
	char	**names = 0;
	lt_dlhandle *handles = 0;

	handle->depcount = 0;
	if (!deplibs)
		return 0;
	save_search_path = strdup(user_search_path);
	if (user_search_path && !save_search_path) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		return 1;
	}
	p = deplibs;
	/* extract search paths and count deplibs */
	while (*p) {
		if (!isspace(*p)) {
			char *end = p+1;
			while (*end && !isspace(*end)) end++;
			if (strncmp(p, "-L", 2) == 0 ||
			    strncmp(p, "-R", 2) == 0) {
				char save = *end;
				*end = 0; /* set a temporary string terminator */
				if (lt_dladdsearchdir(p+2))
					goto cleanup;
				*end = save;
			} else
				depcount++;
			p = end;
		} else
			p++;
	}
	if (!depcount) {
		ret = 0;
		goto cleanup;
	}
	names = lt_dlmalloc(depcount * sizeof(char*));
	if (!names)
		goto cleanup;
	handles = lt_dlmalloc(depcount * sizeof(lt_dlhandle*));
	if (!handles)
		goto cleanup;
	depcount = 0;
	/* now only extract the actual deplibs */
	p = deplibs;
	while (*p) {
		if (!isspace(*p)) {
			char *end = p+1;
			while (*end && !isspace(*end)) end++;
			if (strncmp(p, "-L", 2) != 0 &&
			    strncmp(p, "-R", 2) != 0) {
				char *name;
				char save = *end;
				*end = 0; /* set a temporary string terminator */
				if (strncmp(p, "-l", 2) == 0) {
					name = lt_dlmalloc(3+ /* "lib" */
					 strlen(p+2)+strlen(shlib_ext)+1);
					if (name)
						sprintf(name, "lib%s%s", p+2, shlib_ext);
				} else
					name = strdup(p);
				if (name)
					names[depcount++] = name;
				else
					goto cleanup_names;
				*end = save;
			}
			p = end;
		} else
			p++;
	}
	/* load the deplibs (in reverse order) */
	for (i = 0; i < depcount; i++) {
		lt_dlhandle handle = lt_dlopen(names[depcount-1-i]);
		if (!handle) {
			int j;
			for (j = 0; j < i; j++)
				lt_dlclose(handles[j]);
			last_error = LT_DLSTRERROR(DEPLIB_NOT_FOUND);
			goto cleanup_names;
		}
		handles[i] = handle;	
	}
	handle->depcount = depcount;
	handle->deplibs = handles;
	handles = 0;
	ret = 0;
cleanup_names:
	for (i = 0; i < depcount; i++)
		lt_dlfree(names[i]);
cleanup:
	if (names)
		lt_dlfree(names);
	if (handles)
		lt_dlfree(handles);
	/* restore the old search path */
	if (user_search_path)
		lt_dlfree(user_search_path);
	user_search_path = save_search_path;
	return ret;
}

static int
unload_deplibs(handle)
	lt_dlhandle handle;
{
	int i;
	int errors = 0;
	
	if (!handle->depcount)
		return 0;
	for (i = 0; i < handle->depcount; i++)
		errors += lt_dlclose(handle->deplibs[i]);		
	return errors;
}

static inline int
trim (dest, str)
	char **dest;
	const char *str;
{
	/* remove the leading and trailing "'" from str 
	   and store the result in dest */
	char *tmp;
	const char *end = strrchr(str, '\'');
	int len = strlen(str);

	if (*dest)
		lt_dlfree(*dest);
	if (len > 3 && str[0] == '\'') {
		tmp = (char*) lt_dlmalloc(end - str);
		if (!tmp) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 1;
		}
		strncpy(tmp, &str[1], (end - str) - 1);
		tmp[len-3] = '\0';
		*dest = tmp;
	} else
		*dest = 0;
	return 0;
}

static inline int
free_vars( dlname, oldname, libdir, deplibs)
	char *dlname;
	char *oldname;
	char *libdir;
	char *deplibs;
{
	if (dlname)
		lt_dlfree(dlname);
	if (oldname)
		lt_dlfree(oldname);
	if (libdir)
		lt_dlfree(libdir);
	if (deplibs)
		lt_dlfree(deplibs);
	return 0;
}

lt_dlhandle
lt_dlopen (filename)
	const char *filename;
{
	lt_dlhandle handle = 0, newhandle;
	const char *ext;
	const char *saved_error = last_error;
	char	*canonical = 0, *basename = 0, *dir = 0, *name = 0;
	
	if (!filename) {
		handle = (lt_dlhandle) lt_dlmalloc(sizeof(lt_dlhandle_t));
		if (!handle) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 0;
		}
		handle->info.ref_count = 0;
		handle->depcount = 0;
		handle->deplibs = 0;
		newhandle = handle;
		if (tryall_dlopen(&newhandle, 0) != 0) {
			lt_dlfree(handle);
			return 0;
		}
		goto register_handle;
	}
	canonical = canonicalize_path (filename);
	if (!canonical) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		if (handle)
			lt_dlfree(handle);
		return 0;
	}
	basename = strrchr(canonical, '/');
	if (basename) {
		basename++;
		dir = (char*) lt_dlmalloc(basename - canonical + 1);
		if (!dir) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			handle = 0;
			goto cleanup;
		}
		strncpy(dir, canonical, basename - canonical);
		dir[basename - canonical] = '\0';
	} else
		basename = canonical;
	/* check whether we open a libtool module (.la extension) */
	ext = strrchr(basename, '.');
	if (ext && strcmp(ext, ".la") == 0) {
		/* this seems to be a libtool module */
		FILE	*file;
		int	i;
		char	*dlname = 0, *old_name = 0;
		char	*libdir = 0, *deplibs = 0;
		char	*line;
		int	error = 0;
		/* if we can't find the installed flag, it is probably an
		   installed libtool archive, produced with an old version
		   of libtool */
		int     installed = 1; 

		/* extract the module name from the file name */
		name = (char*) lt_dlmalloc(ext - basename + 1);
		if (!name) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			handle = 0;
			goto cleanup;
		}
		/* canonicalize the module name */
		for (i = 0; i < ext - basename; i++)
			if (isalnum((int)(basename[i])))
				name[i] = basename[i];
			else
				name[i] = '_';
		name[ext - basename] = '\0';
		/* now try to open the .la file */
		file = fopen(filename, LTDL_READTEXT_MODE);
		if (!file)
			last_error = LT_DLSTRERROR(FILE_NOT_FOUND);
		if (!file && !dir) {
			/* try other directories */
			file = (FILE*) find_file(basename, 
						 user_search_path,
						 &dir, 0);
			if (!file)
				file = (FILE*) find_file(basename,
						 getenv("LTDL_LIBRARY_PATH"),
						 &dir, 0);
#ifdef LTDL_SHLIBPATH_VAR
			if (!file)
				file = (FILE*) find_file(basename,
						 getenv(LTDL_SHLIBPATH_VAR),
						 &dir, 0);
#endif
#ifdef LTDL_SYSSEARCHPATH
			if (!file)
				file = (FILE*) find_file(basename,
						 sys_search_path,
						 &dir, 0);
#endif
		}
		if (!file) {
			handle = 0;
			goto cleanup;
		}
		line = (char*) lt_dlmalloc(LTDL_FILENAME_MAX);
		if (!line) {
			fclose(file);
			last_error = LT_DLSTRERROR(NO_MEMORY);
			handle = 0;
			goto cleanup;
		}
		/* read the .la file */
		while (!feof(file)) {
			if (!fgets(line, LTDL_FILENAME_MAX, file))
				break;
			if (line[0] == '\n' || line[0] == '#')
				continue;
#			undef  STR_DLNAME
#			define STR_DLNAME	"dlname="
			if (strncmp(line, STR_DLNAME,
				sizeof(STR_DLNAME) - 1) == 0)
				error = trim(&dlname,
					&line[sizeof(STR_DLNAME) - 1]);
			else
#			undef  STR_OLD_LIBRARY
#			define STR_OLD_LIBRARY	"old_library="
			if (strncmp(line, STR_OLD_LIBRARY,
				sizeof(STR_OLD_LIBRARY) - 1) == 0)
				error = trim(&old_name,
					&line[sizeof(STR_OLD_LIBRARY) - 1]);
			else
#			undef  STR_LIBDIR
#			define STR_LIBDIR	"libdir="
			if (strncmp(line, STR_LIBDIR,
				sizeof(STR_LIBDIR) - 1) == 0)
				error = trim(&libdir,
					&line[sizeof(STR_LIBDIR) - 1]);
			else
#			undef  STR_DL_DEPLIBS
#			define STR_DL_DEPLIBS	"dl_dependency_libs="
			if (strncmp(line, STR_DL_DEPLIBS,
				sizeof(STR_DL_DEPLIBS) - 1) == 0)
				error = trim(&deplibs,
					&line[sizeof(STR_DL_DEPLIBS) - 1]);
			else
			if (strcmp(line, "installed=yes\n") == 0)
				installed = 1;
			else
			if (strcmp(line, "installed=no\n") == 0)
				installed = 0;
			if (error)
				break;
		}
		fclose(file);
		lt_dlfree(line);
		/* allocate the handle */
		handle = (lt_dlhandle) lt_dlmalloc(sizeof(lt_dlhandle_t));
		if (!handle || error) {
			if (handle)
				lt_dlfree(handle);
			if (!error)
				last_error = LT_DLSTRERROR(NO_MEMORY);
			free_vars(dlname, old_name, libdir, deplibs);
			/* handle is already set to 0 */
			goto cleanup;
		}
		handle->info.ref_count = 0;
		if (load_deplibs(handle, deplibs) == 0) {
			newhandle = handle;
			/* find_module may replace newhandle */
			if (find_module(&newhandle, dir, libdir, 
					dlname, old_name, installed)) {
				unload_deplibs(handle);
				error = 1;
			}
		} else
			error = 1;
		free_vars(dlname, old_name, libdir, deplibs);
		if (error) {
			lt_dlfree(handle);
			handle = 0;
			goto cleanup;
		}
		if (handle != newhandle)
			unload_deplibs(handle);
	} else {
		/* not a libtool module */
		handle = (lt_dlhandle) lt_dlmalloc(sizeof(lt_dlhandle_t));
		if (!handle) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			/* handle is already set to 0 */
			goto cleanup;
		}
		handle->info.ref_count = 0;
		/* non-libtool modules don't have dependencies */
		handle->depcount = 0;
		handle->deplibs = 0;
		newhandle = handle;
		if (tryall_dlopen(&newhandle, filename)
		    && (dir
			|| (!find_file(basename, user_search_path,
					  0, &newhandle)
			    && !find_file(basename,
					  getenv("LTDL_LIBRARY_PATH"),
					  0, &newhandle)
#ifdef LTDL_SHLIBPATH_VAR
			    && !find_file(basename,
					  getenv(LTDL_SHLIBPATH_VAR),
					  0, &newhandle)
#endif
#ifdef LTDL_SYSSEARCHPATH
			    && !find_file(basename, sys_search_path,
					  0, &newhandle)
#endif
				))) {
			lt_dlfree(handle);
			handle = 0;
			goto cleanup;
		}
	}
register_handle:
	if (newhandle != handle) {
		lt_dlfree(handle);
		handle = newhandle;
	}
	if (!handle->info.ref_count) {
		handle->info.ref_count = 1;
		handle->info.name = name;
		handle->next = handles;
		handles = handle;
		name = 0;	/* don't free this during `cleanup' */
	}
	last_error = saved_error;
cleanup:
	if (dir)
		lt_dlfree(dir);
	if (name)
		lt_dlfree(name);
	if (canonical)
		lt_dlfree(canonical);
	return handle;
}

lt_dlhandle
lt_dlopenext (filename)
	const char *filename;
{
	lt_dlhandle handle;
	char	*tmp;
	int	len;
	const char *saved_error = last_error;
	
	if (!filename)
		return lt_dlopen(filename);
	len = strlen(filename);
	if (!len) {
		last_error = LT_DLSTRERROR(FILE_NOT_FOUND);
		return 0;
	}
	/* try the normal file name */
	handle = lt_dlopen(filename);
	if (handle)
		return handle;
	/* try "filename.la" */
	tmp = (char*) lt_dlmalloc(len+4);
	if (!tmp) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		return 0;
	}
	strcpy(tmp, filename);
	strcat(tmp, ".la");
	handle = lt_dlopen(tmp);
	if (handle) {
		last_error = saved_error;
		lt_dlfree(tmp);
		return handle;
	}
#ifdef LTDL_SHLIB_EXT
	/* try "filename.EXT" */
	if (strlen(shlib_ext) > 3) {
		lt_dlfree(tmp);
		tmp = (char*) lt_dlmalloc(len + strlen(shlib_ext) + 1);
		if (!tmp) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 0;
		}
		strcpy(tmp, filename);
	} else
		tmp[len] = '\0';
	strcat(tmp, shlib_ext);
	handle = lt_dlopen(tmp);
	if (handle) {
		last_error = saved_error;
		lt_dlfree(tmp);
		return handle;
	}
#endif	
	last_error = LT_DLSTRERROR(FILE_NOT_FOUND);
	lt_dlfree(tmp);
	return 0;
}

int
lt_dlclose (handle)
	lt_dlhandle handle;
{
	lt_dlhandle cur, last;
	
	/* check whether the handle is valid */
	last = cur = handles;
	while (cur && handle != cur) {
		last = cur;
		cur = cur->next;
	}
	if (!cur) {
		last_error = LT_DLSTRERROR(INVALID_HANDLE);
		return 1;
	}
	handle->info.ref_count--;
	if (!handle->info.ref_count) {
		int	error;
	  	lt_dlloader_data_t data = handle->loader->dlloader_data;
	
		if (handle != handles)
			last->next = handle->next;
		else
			handles = handle->next;
		error = handle->loader->module_close(data, handle->module);
		error += unload_deplibs(handle);
		if (handle->info.filename)
			lt_dlfree(handle->info.filename);
		if (handle->info.name)
			lt_dlfree(handle->info.name);
		lt_dlfree(handle);
		return error;
	}
	return 0;
}

lt_ptr_t
lt_dlsym (handle, symbol)
	lt_dlhandle handle;
	const char *symbol;
{
	int	lensym;
	char	lsym[LTDL_SYMBOL_LENGTH];
	char	*sym;
	lt_ptr_t address;
	lt_dlloader_data_t data;

	if (!handle) {
		last_error = LT_DLSTRERROR(INVALID_HANDLE);
		return 0;
	}
	if (!symbol) {
		last_error = LT_DLSTRERROR(SYMBOL_NOT_FOUND);
		return 0;
	}
	lensym = strlen(symbol);
	if (handle->loader->sym_prefix)
		lensym += strlen(handle->loader->sym_prefix);
	if (handle->info.name)
		lensym += strlen(handle->info.name);
	if (lensym + LTDL_SYMBOL_OVERHEAD < LTDL_SYMBOL_LENGTH)
		sym = lsym;
	else
		sym = (char*) lt_dlmalloc(lensym + LTDL_SYMBOL_OVERHEAD + 1);
	if (!sym) {
		last_error = LT_DLSTRERROR(BUFFER_OVERFLOW);
		return 0;
	}
	data = handle->loader->dlloader_data;
	if (handle->info.name) {
		const char *saved_error = last_error;
		
		/* this is a libtool module */
		if (handle->loader->sym_prefix) {
			strcpy(sym, handle->loader->sym_prefix);
			strcat(sym, handle->info.name);
		} else
			strcpy(sym, handle->info.name);
		strcat(sym, "_LTX_");
		strcat(sym, symbol);
		/* try "modulename_LTX_symbol" */
		address = handle->loader->find_sym(data, handle->module, sym);
		if (address) {
			if (sym != lsym)
				lt_dlfree(sym);
			return address;
		}
		last_error = saved_error;
	}
	/* otherwise try "symbol" */
	if (handle->loader->sym_prefix) {
		strcpy(sym, handle->loader->sym_prefix);
		strcat(sym, symbol);
	} else
		strcpy(sym, symbol);
	address = handle->loader->find_sym(data, handle->module, sym);
	if (sym != lsym)
		lt_dlfree(sym);
	return address;
}

const char *
lt_dlerror LTDL_PARAMS((void))
{
	const char *error = last_error;
	
	last_error = 0;
	return error;
}

int
lt_dladdsearchdir (search_dir)
	const char *search_dir;
{
	if (!search_dir || !strlen(search_dir))
		return 0;
	if (!user_search_path) {
		user_search_path = strdup(search_dir);
		if (!user_search_path) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 1;
		}
	} else {
		char	*new_search_path = (char*)
			lt_dlmalloc(strlen(user_search_path) + 
				strlen(search_dir) + 2); /* ':' + '\0' == 2 */
		if (!new_search_path) {
			last_error = LT_DLSTRERROR(NO_MEMORY);
			return 1;
		}
		sprintf (new_search_path, "%s%c%s", user_search_path,
			 LTDL_PATHSEP_CHAR, search_dir);
		lt_dlfree(user_search_path);
		user_search_path = new_search_path;
	}
	return 0;
}

int
lt_dlsetsearchpath (search_path)
	const char *search_path;
{
	if (user_search_path)
		lt_dlfree(user_search_path);
	user_search_path = 0; /* reset the search path */
	if (!search_path || !strlen(search_path))
		return 0;
	user_search_path = strdup(search_path);
	if (!user_search_path)
		return 1;
	return 0;
}

const char *
lt_dlgetsearchpath LTDL_PARAMS((void))
{
	return user_search_path;
}

const lt_dlinfo *
lt_dlgetinfo (handle)
	lt_dlhandle handle;
{
	if (!handle) {
		last_error = LT_DLSTRERROR(INVALID_HANDLE);
		return 0;
	}
	return &(handle->info);
}

int
lt_dlforeach (func, data)
	int (*func) LTDL_PARAMS((lt_dlhandle handle, lt_ptr_t data));
	lt_ptr_t data;
{
	lt_dlhandle cur = handles;
	while (cur) {
		lt_dlhandle tmp = cur;
		cur = cur->next;
		if (func(tmp, data))
			return 1;
	}
	return 0;
}

int
lt_dlloader_add (place, dlloader, loader_name)
	lt_dlloader_t *place;
	const struct lt_user_dlloader *dlloader;
	const char *loader_name;
{
	lt_dlloader_t *node = 0, *ptr = 0;
	
	if ((dlloader == 0)	/* diagnose null parameters */
	    || (dlloader->module_open == 0)
	    || (dlloader->module_close == 0)
	    || (dlloader->find_sym == 0)) {
		last_error = LT_DLSTRERROR(INVALID_LOADER);
		return 1;
	}

	/* Create a new dlloader node with copies of the user callbacks.  */
	node = (lt_dlloader_t *) lt_dlmalloc (sizeof (lt_dlloader_t));
	if (node == 0) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		return 1;
	}
	node->next = 0;
	node->loader_name = loader_name;
	node->sym_prefix = dlloader->sym_prefix;
	node->dlloader_exit = dlloader->dlloader_exit;
	node->module_open = dlloader->module_open;
	node->module_close = dlloader->module_close;
	node->find_sym = dlloader->find_sym;
	node->dlloader_data = dlloader->dlloader_data;
	
	if (!loaders)
		/* If there are no loaders, NODE becomes the list! */
		loaders = node;
	else if (!place) {
		/* If PLACE is not set, add NODE to the end of the
		   LOADERS list. */
		for (ptr = loaders; ptr->next; ptr = ptr->next)
			/*NOWORK*/;
		ptr->next = node;
	} else if (loaders == place) {
		/* If PLACE is the first loader, NODE goes first. */
		node->next = place;
		loaders = node;
	} else {
		/* Find the node immediately preceding PLACE. */
		for (ptr = loaders; ptr->next != place; ptr = ptr->next)
			/*NOWORK*/;

		if (ptr->next != place) {
			last_error = LT_DLSTRERROR(INVALID_LOADER);
			return 1;
		}

		/* Insert NODE between PTR and PLACE. */
		node->next = place;
		ptr->next = node;
	}

	return 0;
}

int
lt_dlloader_remove (loader_name)
	const char *loader_name;
{
	lt_dlloader_t *place = lt_dlloader_find (loader_name);
	lt_dlhandle handle;
	int result = 0;

	if (!place) {
		last_error = LT_DLSTRERROR(INVALID_LOADER);
		return 1;
	}

	/* Fail if there are any open modules which use this loader. */
	for  (handle = handles; handle; handle = handle->next)
		if (handle->loader == place) {
			last_error = LT_DLSTRERROR(REMOVE_LOADER);
			return 1;
		}
	
	if (place == loaders)
		/* PLACE is the first loader in the list. */
		loaders = loaders->next;
	else {
		/* Find the loader before the one being removed. */
		lt_dlloader_t *prev;
		for (prev = loaders; prev->next; prev = prev->next)
			if (!strcmp (prev->next->loader_name, loader_name))
				break;

		place = prev->next;
		prev->next = prev->next->next;
	}
	if (place->dlloader_exit)
		result = place->dlloader_exit (place->dlloader_data);
	lt_dlfree (place);

	return result;
}

lt_dlloader_t *
lt_dlloader_next (place)
	lt_dlloader_t *place;
{
	return place ? place->next : loaders;
}
	
const char *
lt_dlloader_name (place)
	lt_dlloader_t *place;
{
	if (!place)
		last_error =  LT_DLSTRERROR(INVALID_LOADER);
	return place ? place->loader_name : 0;
}	

lt_dlloader_data_t *
lt_dlloader_data (place)
	lt_dlloader_t *place;
{
	if (!place)
		last_error =  LT_DLSTRERROR(INVALID_LOADER);
	return place ? &(place->dlloader_data) : 0;
}	

lt_dlloader_t *
lt_dlloader_find (loader_name)
	const char *loader_name;
{
	lt_dlloader_t *place = 0;

	for (place = loaders; place; place = place->next)
		if (strcmp (place->loader_name, loader_name) == 0)
			break;

	return place;
}

static const char **user_error_strings = 0;
static int errorcode = LTDL_ERROR_MAX;

int
lt_dladderror (diagnostic)
	const char *diagnostic;
{
	int index = errorcode - LTDL_ERROR_MAX;
	const char **temp = 0;

	/* realloc is not entirely portable, so simulate it using
	   lt_dlmalloc and lt_dlfree. */
	temp = (const char **) lt_dlmalloc ((1+index) * sizeof(const char*));
	if (temp == 0) {
		last_error = LT_DLSTRERROR(NO_MEMORY);
		return -1;
	}

	/* Build the new vector in the memory addressed by temp. */
	temp[index] = diagnostic;
	while (--index >= 0)
		temp[index] = user_error_strings[index];

	lt_dlfree (user_error_strings);
	user_error_strings = temp;
	return errorcode++;
}

int
lt_dlseterror (index)
	int index;
{
	if (index >= errorcode || index < 0) {
		last_error = LT_DLSTRERROR(INVALID_ERRORCODE);
		return 1;
	}
	
	if (index < LTDL_ERROR_MAX)
		last_error = ltdl_error_strings[errorcode];
	else
		last_error = user_error_strings[errorcode - LTDL_ERROR_MAX];

	return 0;
}
