/* Copyright 1989-93 GROUPE BULL -- See license conditions in file COPYRIGHT */
/*****************************************************************************\
*                                                                             *
* 				     KLDLOAD                                  *
* 			   Dynamic Load Functions                             *
*                                                                             *
\*****************************************************************************/
/* low-level interface. high-level, autoloadable layer must be written upon it
 * dynamic loading is implemented on top of the dlopen, dlsym, dlclose, dlerror
 * set of functions (SUN, SGI)
 * may be implemented on top of load, unload, loadbind, loadquery on AIX
 * the module name is the C-legal identifier prefix of the file name (cutting 
 * after first dot or minus for instance
 * and KlDLOpen calls at load time the <modulename>Init() C func.
 */

#ifdef KLDLOAD

/* To create a loadable module foo.so from foo.c:
 * cc -pic -c foo.c
 * ld -o foo.so foo.o <add-needed-libraries-here>
 */

/*****************************************************************************\
* 				 declarations                                 *
\*****************************************************************************/
#include <dlfcn.h>
#include <ctype.h>

#include "EXTERN.h"
#include "klone.h"
#include "kl_number.h"
#include "kl_string.h"
#include "kl_atom.h"
#include "kl_func.h"
#include "klgeneric.h"

DECLARE_strrchr;

typedef struct _KlDlHandle {
    KlKLONE_HEADER;
    void *handle;			/* opaque type, loaded object */
    char *name;
}        *KlDlHandle;

#define KlIsADlHandle(obj)  ((obj)->type == KlDlHandleType)
#define KlMustBeDlHandle(obj, n)  \
    if (obj->type != KlDlHandleType || !(((KlDlHandle) obj)->handle)) \
        KlBadArgument(obj, n, "open DlHandle")

KlType KlDlHandleType;
KlAtom KlDlErrorTag;

typedef void (*func_returning_void_ptr)(); 

/*****************************************************************************\
* 				  functions                                   *
\*****************************************************************************/

KlDlHandle
KlDlmake(handle, name)
    void *handle;
    char *name;
{
    KlDlHandle obj = (KlDlHandle) KlOMake(KlDlHandleType);
    obj->handle = handle;
    obj->name = KlStrdup(name);
    return obj;
} 

KlO
KlDlfree(obj)
    KlDlHandle obj;
{
    if (obj->handle)
	dlclose(obj->handle);
    Free(obj->name);
    Free(obj);
    return (KlO) obj;
} 

KlO
KlDlError(where, what)
    char *where;
    KlO what;
{
    const char *err = dlerror();
    return KlError3(KlDlErrorTag, KlStringMake(where),
		    KlStringMake(err ? err : "unknown"), what);
}

/* dlopen loads the module, and calls its function KloneModuleInit() if exists 
 * returns a DlHandle that MUST be stored, as the module is freed as soon as
 * the Klone handle is garbaged!
 * init function is name of file (first part, truncated at first 
 * non-alphanumeric or underscore char) with Init postpended:
 * filename = Tk_fileselect-1.0  ==> function is Tk_fileselectInit();
 */

KlO
KlDlopen(filename)
    KlString filename;
{
    void *handle;

    KlMustBeString(filename, 0);
    if (handle = dlopen(KlExpandTildeForFilesC(filename->string), 1)) {
	func_returning_void_ptr init_func;
	char modulename[256], *p, *q;
	if (p = strrchr(filename->string, '/')) {
	    p++;
	} else {
	    p = filename->string;
	}
	for (q = p; isalnum(*q) || (*q == '_'); q++) /* upto non-alphanum or _ */
	    ;
	bcopy(p, modulename, q - p);
	strcpy(modulename + (q - p), "Init");
	if (init_func = (func_returning_void_ptr)
	    dlsym(handle, modulename)) {
	    (*init_func)();
	}
	return (KlO) KlDlmake(handle, modulename);
    } else {				/* error */
	return KlDlError("dlopen", filename);
    }
}

/* dlclose unloads the module, and calls its C function KloneModuleFini() 
 * if it exists 
 */
KlO
KlDlclose(kldlhandle)
    KlDlHandle kldlhandle;
{
    func_returning_void_ptr fini_func;
    char modulename[256];
    KlMustBeDlHandle(kldlhandle, 0);

    strcpy(stpcpy(modulename, kldlhandle->name), "Fini");
    if (fini_func = (func_returning_void_ptr)
	dlsym(kldlhandle->handle, modulename)) {
	(*fini_func)();
    }

    if (dlclose(kldlhandle->handle)) {	/* error */
	return KlDlError("dlclose", kldlhandle);
    } else {
	kldlhandle->handle = 0;
	return NIL;
    }
}

/* dlsym declares (binds) a C symbol 
 */
KlO
KlDlsym(kldlhandle, name, type, arity)
    KlDlHandle kldlhandle;
    KlString name;
    KlType type;			/* type of symbol: Subr, FSubr */
    KlNumber arity;			/* arity of Subrs */
{
    void *Cobj;
    KlMustBeDlHandle(kldlhandle, 0);
    KlMustBeString(name, 1);
    KlMustBeType(type, 2);
    KlMustBeNumber(arity, 3);

    if (Cobj = dlsym(kldlhandle->handle, name->string)) {
	if (type != KlFSubrType && type != KlSubrType) {
	    KlBadArgument(type, 2, KlTypeErr_SubrOrFSubr);
	}
	if (arity->number < NARY || arity->number >= KlSelectorsPropsSize) {
	    char tmp[60];
	    sprintf(tmp, "integer between 0 and %d", KlSelectorsPropsSize - 1);
	    KlBadArgument(arity, 3, tmp);
	}
	return (KlO) KlSubrMake(type, Cobj, arity->number);
    } else {
	return KlDlError("dlsym", name);
    }
}

/*****************************************************************************\
* 				     Init                                     *
\*****************************************************************************/

KlDlInit()
{
    KlDeclareType(&KlDlHandleType, "DlHandle", sizeof(struct _KlDlHandle));
    KlDeclareMethod1(KlDlHandleType, KlSelFree, KlDlfree);

    KlDeclareSubr(KlDlopen, "Dl:open", 1);
    KlDeclareSubr(KlDlclose, "Dl:close", 1);
    KlDeclareSubr(KlDlsym, "Dl:symbol", 4);

					/* error */
    KlDlErrorTag = KlErrorCodeMakeS("Errors:Dl", 
				    "Dynamic loader error: %0: %2: %1");

}

#endif /* KLDLOAD */


syntax highlighted by Code2HTML, v. 0.9.1