/*
 *  dlf.c
 *
 *  $Id: dlf.c,v 1.5 2004/11/11 01:52:37 luesang Exp $
 *
 *  Dynamic Library Loader (mapping to SVR4)
 *
 *  The iODBC driver manager.
 *  
 *  Copyright (C) 1995 by Ke Jin <kejin@empress.com> 
 *  Copyright (C) 1996-2002 by OpenLink Software <iodbc@openlinksw.com>
 *  All Rights Reserved.
 *
 *  This software is released under the terms of either of the following
 *  licenses:
 *
 *      - GNU Library General Public License (see LICENSE.LGPL) 
 *      - The BSD License (see LICENSE.BSD).
 *
 *  While not mandated by the BSD license, any patches you make to the
 *  iODBC source code may be contributed back into the iODBC project
 *  at your discretion. Contributions will benefit the Open Source and
 *  Data Access community as a whole. Submissions may be made at:
 *
 *      http://www.iodbc.org
 *
 *
 *  GNU Library Generic Public License Version 2
 *  ============================================
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 *  The BSD License
 *  ===============
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. Neither the name of OpenLink Software Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OPENLINK OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <dlf.h>
#include <errno.h>

#ifdef	DLDAPI_DEFINED
#undef DLDAPI_DEFINED
#endif

#ifdef	DLDAPI_SVR4_DLFCN
#define DLDAPI_DEFINED
static char sccsid[] = "@(#)dynamic load interface -- SVR4 dlfcn";
#endif

/********************************* 
 *
 *	HP/UX 
 *
 *********************************/

#ifdef	DLDAPI_HP_SHL
#define	DLDAPI_DEFINED

#include <dl.h>

static char sccsid[] = "@(#)dynamic load interface -- HP/UX dl(shl)";

void *
dlopen (char *path, int mode)
{
  return (void *) shl_load ((char *) (path), BIND_IMMEDIATE | BIND_NONFATAL, 0L);
}


void *
dlsym (void *hdll, char *sym)
{
  void *symaddr = 0;
  int ret;

#if 0
  /*
   *  iODBC does not need a handle to itself
   */
  if (!hdll)
    hdll = (void *) PROG_HANDLE;
#endif

  /* Remember, a driver may export calls as function pointers 
   * (i.e. with type TYPE_DATA) rather than as functions 
   * (i.e. with type TYPE_PROCEDURE). Thus, to be safe, we 
   * uses TYPE_UNDEFINED to cover all of them. 
   */
  ret = shl_findsym ((shl_t *) & hdll, sym, TYPE_UNDEFINED, &symaddr);

  if (ret == -1)
    return 0;

  return symaddr;
}


char *
dlerror ()
{
  extern char *strerror ();

  return strerror (errno);
}


int
dlclose (void *hdll)
{
  struct shl_descriptor d;

  /*
   *  As HP/UX does not use a reference counter for unloading, 
   *  we can only unload the driver when it is loaded once.
   */
  if (shl_gethandle_r ((shl_t) hdll, &d) < 0 || d.ref_count > 1)
  {
    return 0;
  }

  return shl_unload ((shl_t) hdll);
}
#endif /* end of HP/UX Section */


/*********************************
 *
 *	IBM AIX
 *
 *********************************/

#ifdef	DLDAPI_AIX_LOAD
#define	DLDAPI_DEFINED

#include <sys/types.h>
#include <sys/ldr.h>
#include <sys/stat.h>
#include <nlist.h>

#ifndef	HTAB_SIZE
#define	HTAB_SIZE	256
#endif

#define	FACTOR		0.618039887	/* i.e. (sqrt(5) - 1)/2 */

#ifndef	ENTRY_SYM
#define	ENTRY_SYM	".__start"	/* default entry point for aix */
#endif

typedef struct slot_s
{
  char *sym;
  long fdesc[3];		/* 12 bytes function descriptor */
  struct slot_s *next;
}
slot_t;

/* Note: on AIX, a function pointer actually points to a
 * function descriptor, a 12 bytes data. The first 4 bytes
 * is the virtual address of the function. The next 4 bytes 
 * is the virtual address of TOC (Table of Contents) of the
 * object module the function belong to. The last 4 bytes 
 * are always 0 for C and Fortran functions. Every object 
 * module has an entry point (which can be specified at link
 * time by -e ld option). iODBC driver manager requires ODBC
 * driver shared library always use the default entry point
 * (so you shouldn't use -e ld option when creating a driver
 * share library). load() returns the function descriptor of 
 * a module's entry point. From which we can calculate function
 * descriptors of other functions in the same module by using
 * the fact that the load() doesn't change the relative 
 * offset of functions to their module entry point(i.e the 
 * offset in memory loaded by load() will be as same as in 
 * the module library file). 
 */

typedef slot_t *hent_t;
typedef struct nlist nlist_t;
typedef struct stat stat_t;

typedef struct obj
  {
    int dev;			/* device id */
    int ino;			/* inode number */
    char *path;			/* file name */
    int (*pentry) ();		/* entry point of this share library */
    int refn;			/* number of reference */
    hent_t htab[HTAB_SIZE];
    struct obj *next;
  }
obj_t;

static char *errmsg = 0;

static void
init_htab (hent_t * ht)
/* initialize a hashing table */
{
  int i;

  for (i = 0; i < HTAB_SIZE; i++)
    ht[i] = (slot_t *) 0;

  return;
}


static void
clean_htab (hent_t * ht)
/* free all slots */
{
  int i;
  slot_t *ent;
  slot_t *tent;

  for (i = 0; i < HTAB_SIZE; i++)
    {
      for (ent = ht[i]; ent;)
	{
	  tent = ent->next;

	  free (ent->sym);
	  free (ent);

	  ent = tent;
	}

      ht[i] = 0;
    }

  return;
}


static int
hash (char *sym)
{
  int a, key;
  double f;

  if (!sym || !*sym)
    return 0;

  for (key = *sym; *sym; sym++)
    {
      key += *sym;
      a = key;

      key = (int) ((a << 8) + (key >> 8));
      key = (key > 0) ? key : -key;
    }

  f = key * FACTOR;
  a = (int) f;

  return (int) ((HTAB_SIZE - 1) * (f - a));
}


static hent_t
search (hent_t * htab, char *sym)
/* search hashing table to find a matched slot */
{
  int key;
  slot_t *ent;

  key = hash (sym);

  for (ent = htab[key]; ent; ent = ent->next)
    {
      if (!strcmp (ent->sym, sym))
	return ent;
    }

  return 0;			/* no match */
}


static void
insert (hent_t * htab, slot_t * ent)
/* insert a new slot to hashing table */
{
  int key;

  key = hash (ent->sym);

  ent->next = htab[key];
  htab[key] = ent;

  return;
}


static slot_t *
slot_alloc (char *sym)
/* allocate a new slot with symbol */
{
  slot_t *ent;

  ent = (slot_t *) malloc (sizeof (slot_t));

  ent->sym = (char *) malloc (STRLEN (sym) + 1);

  if (!ent->sym)
    {
      free (ent);
      return 0;
    }

  STRCPY (ent->sym, sym);

  return ent;
}


static obj_t *obj_list = 0;

void *
dlopen (char *file, int mode)
{
  stat_t st;
  obj_t *pobj;
  char buf[1024];

  if (!file || !*file)
    {
      errno = EINVAL;
      return 0;
    }

  errno = 0;
  errmsg = 0;

  if (stat (file, &st))
    return 0;

  for (pobj = obj_list; pobj; pobj = pobj->next)
    /* find a match object */
    {
      if (pobj->ino == st.st_ino
	  && pobj->dev == st.st_dev)
	{
	  /* found a match. increase its
	   * reference count and return 
	   * its address */
	  pobj->refn++;
	  return pobj;
	}
    }

  pobj = (obj_t *) malloc (sizeof (obj_t));

  if (!pobj)
    return 0;

  pobj->path = (char *) malloc (STRLEN (file) + 1);

  if (!pobj->path)
    {
      free (pobj);
      return 0;
    }

  STRCPY (pobj->path, file);

  pobj->dev = st.st_dev;
  pobj->ino = st.st_ino;
  pobj->refn = 1;

  pobj->pentry = (int (*)()) load (file, 0, 0);

  if (!pobj->pentry)
    {
      free (pobj->path);
      free (pobj);
      return 0;
    }

  init_htab (pobj->htab);

  pobj->next = obj_list;
  obj_list = pobj;

  return pobj;
}


int
dlclose (void *hobj)
{
  obj_t *pobj = (obj_t *) hobj;
  obj_t *tpobj;
  int match = 0;

  if (!hobj)
    {
      errno = EINVAL;
      return -1;
    }

  errno = 0;
  errmsg = 0;

  if (pobj == obj_list)
    {
      pobj->refn--;

      if (pobj->refn)
	return 0;

      match = 1;
      obj_list = pobj->next;
    }

  for (tpobj = obj_list; !match && tpobj; tpobj = tpobj->next)
    {
      if (tpobj->next == pobj)
	{
	  pobj->refn--;

	  if (pobj->refn)
	    return 0;

	  match = 1;
	  tpobj->next = pobj->next;
	}
    }

  if (match)
    {
      unload ((void *) (pobj->pentry));
      clean_htab (pobj->htab);
      free (pobj->path);
      free (pobj);
    }

  return 0;
}


char *
dlerror ()
{
  extern char *sys_errlist[];

  if (!errmsg || !errmsg[0])
    {
      if (errno >= 0)
	return sys_errlist[errno];

      return "";
    }

  return errmsg;
}


void *
dlsym (void *hdl, char *sym)
{
  nlist_t nl[3];
  obj_t *pobj = (obj_t *) hdl;
  slot_t *ent;
  int (*fp) ();
  long lbuf[3];

  if (!hdl || !(pobj->htab) || !sym || !*sym)
    {
      errno = EINVAL;
      return 0;
    }

  errno = 0;
  errmsg = 0;

  ent = search (pobj->htab, sym);

  if (ent)
    return ent->fdesc;

#define	n_name	_n._n_name

  nl[0].n_name = ENTRY_SYM;
  nl[1].n_name = sym;
  nl[2].n_name = 0;

  /* There is a potential problem here. If application
   * did not pass a full path name, and changed the
   * working directory after the load(), then nlist()
   * will be unable to open the original shared library
   * file to resolve the symbols. there are 3 ways to working 
   * round this: 1. convert to full pathname in driver
   * manager. 2. applications always pass driver's full 
   * path name. 3. if driver itself don't support 
   * SQLGetFunctions(), call it with SQL_ALL_FUNCTIONS
   * as flag immediately after SQLConnect(), SQLDriverConnect()
   * and SQLBrowseConnect() to force the driver manager
   * resolving all will be used symbols. 
   */
  if (nlist (pobj->path, nl) == -1)
    return 0;

  if (!nl[0].n_type && !nl[0].n_value)
    {
      errmsg = "can't locate module entry symbol";
      return 0;
    }

  /* Note: On AIX 3.x if the object library is not
   * built with -g compiling option, .n_type field
   * is always 0. While on 4.x it will be 32. 
   * On AIX 4.x, if the symbol is a entry point,
   * n_value will be 0. However, one thing is for sure 
   * that if a symbol does not exists in the file,
   * both .n_type and .n_value would be 0.
   */

  if (!nl[1].n_type && !nl[1].n_value)
    {
      errmsg = "symbol does not exist in this module";
      return 0;
    }

  ent = slot_alloc (sym);

  if (!ent)
    return 0;

  /* catch it with a slot in the hashing table */
  insert (pobj->htab, ent);

  memcpy (ent->fdesc, pobj->pentry, sizeof (ent->fdesc));

  /* now ent->fdesc[0] is the virtual address of entry point
   * and ent->fdesc[1] is the TOC of the module
   */

  /* let's calculate the virtual address of the symbol 
   * by adding a relative offset getting from the module 
   * file symbol table, i.e
   *
   *  function virtual address = entry point virtual address +
   *     + ( function offset in file - entry point offset in file )
   */

  (ent->fdesc)[0] = (ent->fdesc)[0] +
      (nl[1].n_value - nl[0].n_value);

  /* return the function descriptor */
  return ent->fdesc;
}
#endif /* end of IBM AIX Section */


/********************************* 
 *
 *	Windows 3.x, 95, NT
 *
 *********************************/

#ifdef	DLDAPI_WINDOWS
#define	DLDAPI_DEFINED

#include <windows.h>

void *
dlopen (char * dll, int mode)
{
  HINSTANCE hint;

  if (dll == NULL)
    {
      return GetWindowWord (NULL, GWW_HINSTANCE);
    }

  hint = LoadLibrary (dll);

  if (hint < HINSTANCE_ERROR)
    {
      return NULL;
    }

  return (void *) hint;
}


void *
dlsym (void * hdll, char * sym)
{
  return (void *) GetProcAddress (hdll, sym);
}


char *
dlerror ()
{
  return 0L;			/* unimplemented yet */
}


int
dlclose (void * hdll)
{
  FreeLibrary ((HINSTANCE) hdll);
}
#endif /* end of Windows family */


/***********************************
 *
 * 	VMS
 *
 ***********************************/

#ifdef VMS
#define	DLDAPI_DEFINED
#ifdef DLDAPI_VMS_IODBC

#include <stdio.h>
#include <descrip.h>
#include <starlet.h>
#include <ssdef.h>
#include <libdef.h>
#include <lib$routines>
#include <rmsdef.h>
#include <fabdef.h>
#include <namdef.h>

#ifndef LIB$M_FIS_MIXCASE
#define LIB$M_FIS_MIXCASE 1<<4
#endif

typedef struct
{
  struct dsc$descriptor_s filename_d;
  struct dsc$descriptor_s image_d;
  char filename[NAM$C_MAXRSS];	/* $PARSEd image name */
}
dll_t;

/*
 *  The following static int contains the last VMS error returned. It is kept
 *  static so that dlerror() can get it. This method is dangerous if you have
 *  threaded applications, but this is the way the UNIX dlopen() etc
 *  is defined.
 */
static int saved_status = SS$_NORMAL;
static char dlerror_buf[256];


static int
iodbc_find_image_symbol (
    struct dsc$descriptor_s *filename_d,
    struct dsc$descriptor_s *symbol_d,
    void **rp,
    struct dsc$descriptor_s *image_d, int flag)
{
  lib$establish (lib$sig_to_ret);
  return lib$find_image_symbol (filename_d, symbol_d, rp, image_d, flag);
}


void *
iodbc_dlopen (char *path, int unused_flag)
{
  int status;
  dll_t *dll;
  struct FAB imgfab;
  struct NAM imgnam;
  static char defimg[] = "SYS$SHARE:.EXE";

  if (path == NULL)
    {
      saved_status = SS$_UNSUPPORTED;
      return NULL;
    }

  dll = malloc (sizeof (dll_t));
  if (dll == NULL)
    {
      saved_status = SS$_INSFMEM;
      return NULL;
    }

  imgfab = cc$rms_fab;
  imgfab.fab$l_fna = path;
  imgfab.fab$b_fns = STRLEN (path);
  imgfab.fab$w_ifi = 0;
  imgfab.fab$l_dna = defimg;
  imgfab.fab$b_dns = sizeof (defimg);
  imgfab.fab$l_fop = FAB$M_NAM;
  imgfab.fab$l_nam = &imgnam;
  imgnam = cc$rms_nam;
  imgnam.nam$l_esa = dll->filename;
  imgnam.nam$b_ess = NAM$C_MAXRSS;
  status = sys$parse (&imgfab);
  if (!(status & 1))
    {
      free (dll);
      saved_status = status;
      return NULL;
    }

  dll->filename_d.dsc$b_dtype = DSC$K_DTYPE_T;
  dll->filename_d.dsc$b_class = DSC$K_CLASS_S;
  dll->filename_d.dsc$a_pointer = imgnam.nam$l_name;
  dll->filename_d.dsc$w_length = imgnam.nam$b_name;
  dll->image_d.dsc$b_dtype = DSC$K_DTYPE_T;
  dll->image_d.dsc$b_class = DSC$K_CLASS_S;
  dll->image_d.dsc$a_pointer = dll->filename;
  dll->image_d.dsc$w_length = imgnam.nam$b_esl;

  /*
   *  VMS does not have the concept of first opening a shared library and then
   *  asking for symbols; the LIB$FIND_IMAGE_SYMBOL routine does both.
   *  Since I want my implementation of dlopen() to return an error if the
   *  shared library can not be loaded, I try to find a dummy symbol in the
   *  library.
   */
  iodbc_dlsym (dll, "THIS_ROUTINE_MIGHT_NOT_EXIST");
  if (!((saved_status ^ LIB$_KEYNOTFOU) & ~7))
    {
      saved_status = SS$_NORMAL;
    }
  if (saved_status & 1)
    {
      return dll;
    }
  else
    {
      free (dll);
      return NULL;
    }
}


void *
iodbc_dlsym (void *hdll, char *sym)
{
  int status;
  dll_t *dll;
  struct dsc$descriptor_s symbol_d;
  void *rp;

  dll = hdll;
  if (dll == NULL)
    return NULL;

  symbol_d.dsc$b_dtype = DSC$K_DTYPE_T;
  symbol_d.dsc$b_class = DSC$K_CLASS_S;
  symbol_d.dsc$a_pointer = sym;
  symbol_d.dsc$w_length = STRLEN (sym);
  status = iodbc_find_image_symbol (&dll->filename_d, &symbol_d, &rp,
      &dll->image_d, 0);
  if (!((saved_status ^ LIB$_KEYNOTFOU) & ~7))
    {
      status = iodbc_find_image_symbol (&dll->filename_d, &symbol_d, &rp,
	  &dll->image_d, LIB$M_FIS_MIXCASE);
    }
  if (status & 1)
    {
      return rp;
    }
  else
    {
      saved_status = status;
      return NULL;
    }
}


char *
iodbc_dlerror ()
{
  struct dsc$descriptor desc;
  short outlen;
  int status;

  if (saved_status & 1)
    {
      return NULL;
    }

  desc.dsc$b_dtype = DSC$K_DTYPE_T;
  desc.dsc$b_class = DSC$K_CLASS_S;
  desc.dsc$a_pointer = dlerror_buf;
  desc.dsc$w_length = sizeof (dlerror_buf);
  status = sys$getmsg (saved_status, &outlen, &desc, 15, 0);
  if (status & 1)
    {
      dlerror_buf[outlen] = '\0';
    }
  else
    {
      sprintf (dlerror_buf, "Message number %8X", saved_status);
    }
  saved_status = SS$_NORMAL;
  return (dlerror_buf);
}


int
iodbc_dlclose (void *hdll)
{
  /*
   *  Not really implemented since VMS doesn't support unloading images.
   *  The hdll pointer is released though.
   */
  free (hdll);
  return 0;
}
#endif /* DLDAPI_VMS_IODBC */
#endif /* VMS */


/********************************* 
 *
 *	Apple MacOS X Rhapsody
 *
 *********************************/
#ifdef	DLDAPI_DYLD
#define	DLDAPI_DEFINED
#include <stdio.h>
#include <mach-o/dyld.h>

static char sccsid[] = "@(#)dynamic load interface -- MacOS X dl(dyld)";


static void
undefined_symbol_handler (const char *symbolName)
{
  fprintf (stderr, "dyld found undefined symbol: %s\n", symbolName);

  abort ();
}


static NSModule 
multiple_symbol_handler (NSSymbol s, NSModule old, NSModule new)
{
  /*
   *  Since we can't unload symbols, we're going to run into this
   *  every time we reload a module. Workaround here is to just
   *  rebind to the new symbol, and forget about the old one.
   *  This is crummy, because it's basically a memory leak.
   */

  return (new);
}


static void
linkEdit_symbol_handler (NSLinkEditErrors c, int errorNumber,
    const char *fileName, const char *errorString)
{
  fprintf (stderr, "dyld errors during link edit for file %s\n%s\n",
      fileName, errorString);

  abort ();
}


void *
dlopen (char *path, int mode)
{
  NSObjectFileImage image;
  NSLinkEditErrorHandlers handlers;
  NSModule handle = NULL;
  int i;

  /*
   *  Install error handler 
   */
  handlers.undefined = undefined_symbol_handler;
#if !defined (NSLINKMODULE_OPTION_PRIVATE)
  handlers.multiple = multiple_symbol_handler;
#endif
  handlers.linkEdit = linkEdit_symbol_handler;

  NSInstallLinkEditErrorHandlers (&handlers);

  /*
   *  Load object
   */
  i = NSCreateObjectFileImageFromFile (path, &image);
  if (i != NSObjectFileImageSuccess)
    {
      static char *ErrorStrings[] =
      {
	  "%s(%d): Object Image Load Failure\n",
	  "%s(%d): Object Image Load Success\n",
	  "%s(%d): Not an recognizable object file\n",
	  "%s(%d): No valid architecture\n",
	  "%s(%d): Object image has an invalid format\n",
	  "%s(%d): Invalid access (permissions?)\n",
	  "%s(%d): Unknown error code from NSCreateObjectFileImageFromFile\n",
      };

      if (i < 0 || i > 6)
	i = 6;

      fprintf (stderr, ErrorStrings[i], path, i);
    }
  else
    {
#if !defined (NSLINKMODULE_OPTION_PRIVATE)
      handle = NSLinkModule (image, path, TRUE);
#else
      handle = NSLinkModule (image, path, NSLINKMODULE_OPTION_PRIVATE);
#endif
    }

  return (void *) handle;
}


void *
dlsym (void *hdll, char *sym)
{
  NSSymbol symbol;

#if !defined (NSLINKMODULE_OPTION_PRIVATE)
  if (NSIsSymbolNameDefined (sym))
    {
      symbol = NSLookupAndBindSymbol (sym);

      return NSAddressOfSymbol (symbol);
    }

  return NULL;
#else
  symbol = NSLookupSymbolInModule ((NSModule) hdll, sym);

  return NSAddressOfSymbol (symbol);   
#endif
}


char *
dlerror ()
{
  return NULL;
}


int 
dlclose (void *hdll)
{
  NSUnLinkModule (hdll, FALSE);
  return 0;
}
#endif	/* end of Rhapsody Section */


/********************************* 
 *
 *	Apple MacOS X Rhapsody
 *
 *********************************/
#ifdef	DLDAPI_MACX
static struct dlopen_handle *dlopen_handles = NULL;
static const struct dlopen_handle main_program_handle = { NULL };
static char *dlerror_pointer = NULL;

  enum bool
  { false, true };

/*
 * NSMakePrivateModulePublic() is not part of the public dyld API so we define
 * it here.  The internal dyld function pointer for
 * __dyld_NSMakePrivateModulePublic is returned so thats all that matters to get
 * the functionality need to implement the dlopen() interfaces.
 */
static enum bool
NSMakePrivateModulePublic (NSModule module)
{
  static enum bool (*p) (NSModule module) = NULL;

  if (p == NULL)
    _dyld_func_lookup ("__dyld_NSMakePrivateModulePublic",
	(unsigned long *) &p);
  if (p == NULL)
    {
#ifdef DEBUG
      printf ("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic "
	  "failed\n");
#endif
      return (FALSE);
    }
  return (p (module));
}


/*
 * dlopen() the MacOS X version of the FreeBSD dlopen() interface.
 */
void *
dlopen (char * path, int mode)
{
  void *retval;
  struct stat stat_buf;
  NSObjectFileImage objectFileImage;
  NSObjectFileImageReturnCode ofile_result_code;
  NSModule module;
  struct dlopen_handle *p;
  unsigned long options;
  NSSymbol NSSymbol;
  void (*init) (void);

  dlerror_pointer = NULL;

  /*
   * A NULL path is to indicate the caller wants a handle for the
   * main program.
   */
  if (path == NULL)
    {
      retval = (void *) &main_program_handle;
      return (retval);
    }

  /* see if the path exists and if so get the device and inode number */
  if (stat (path, &stat_buf) == -1)
    {
      dlerror_pointer = strerror (errno);
      return (NULL);
    }

  /*
   * If we don't want an unshared handle see if we already have a handle
   * for this path.
   */
  if ((mode & RTLD_UNSHARED) != RTLD_UNSHARED)
    {
      p = dlopen_handles;
      while (p != NULL)
	{
	  if (p->dev == stat_buf.st_dev && p->ino == stat_buf.st_ino)
	    {
	      /* skip unshared handles */
	      if ((p->dlopen_mode & RTLD_UNSHARED) == RTLD_UNSHARED)
		continue;
	      /*
	       * We have already created a handle for this path.  The
	       * caller might be trying to promote an RTLD_LOCAL handle
	       * to a RTLD_GLOBAL.  Or just looking it up with
	       * RTLD_NOLOAD.
	       */
	      if ((p->dlopen_mode & RTLD_LOCAL) == RTLD_LOCAL &&
		  (mode & RTLD_GLOBAL) == RTLD_GLOBAL)
		{
		  /* promote the handle */
		  if (NSMakePrivateModulePublic (p->module) == TRUE)
		    {
		      p->dlopen_mode &= ~RTLD_LOCAL;
		      p->dlopen_mode |= RTLD_GLOBAL;
		      p->dlopen_count++;
		      return (p);
		    }
		  else
		    {
		      dlerror_pointer = "can't promote handle from "
			  "RTLD_LOCAL to RTLD_GLOBAL";
		      return (NULL);
		    }
		}
	      p->dlopen_count++;
	      return (p);
	    }
	  p = p->next;
	}
    }

  /*
   * We do not have a handle for this path if we were just trying to
   * look it up return NULL to indicate we don't have it.
   */
  if ((mode & RTLD_NOLOAD) == RTLD_NOLOAD)
    {
      dlerror_pointer = "no existing handle for path RTLD_NOLOAD test";
      return (NULL);
    }

  /* try to create an object file image from this path */
  ofile_result_code = NSCreateObjectFileImageFromFile (path,
      &objectFileImage);
  if (ofile_result_code != NSObjectFileImageSuccess)
    {
      switch (ofile_result_code)
	{
	case NSObjectFileImageFailure:
	  dlerror_pointer = "object file setup failure";
	  return (NULL);
	case NSObjectFileImageInappropriateFile:
	  dlerror_pointer = "not a Mach-O MH_BUNDLE file type";
	  return (NULL);
	case NSObjectFileImageArch:
	  dlerror_pointer = "no object for this architecture";
	  return (NULL);
	case NSObjectFileImageFormat:
	  dlerror_pointer = "bad object file format";
	  return (NULL);
	case NSObjectFileImageAccess:
	  dlerror_pointer = "can't read object file";
	  return (NULL);
	default:
	  dlerror_pointer = "unknown error from "
	      "NSCreateObjectFileImageFromFile()";
	  return (NULL);
	}
    }

  /* try to link in this object file image */
  options = NSLINKMODULE_OPTION_NONE |
    NSLINKMODULE_OPTION_PRIVATE |
    NSLINKMODULE_OPTION_RETURN_ON_ERROR;
  if ((mode & RTLD_NOW) == RTLD_NOW)
    options |= NSLINKMODULE_OPTION_BINDNOW;
  module = NSLinkModule (objectFileImage, path, options);
  NSDestroyObjectFileImage (objectFileImage);
  if (module == NULL)
    {
      dlerror_pointer = "NSLinkModule() failed for dlopen()";
      return (NULL);
    }

  /*
   * If the handle is to be global promote the handle.  It is done this
   * way to avoid multiply defined symbols.
   */
  if ((mode & RTLD_GLOBAL) == RTLD_GLOBAL)
    {
      if (NSMakePrivateModulePublic (module) == FALSE)
	{
	  dlerror_pointer = "can't promote handle from RTLD_LOCAL to "
	      "RTLD_GLOBAL";
	  return (NULL);
	}
    }

  p = malloc (sizeof (struct dlopen_handle));
  if (p == NULL)
    {
      dlerror_pointer = "can't allocate memory for the dlopen handle";
      return (NULL);
    }

  /* fill in the handle */
  p->dev = stat_buf.st_dev;
  p->ino = stat_buf.st_ino;
  if (mode & RTLD_GLOBAL)
    p->dlopen_mode = RTLD_GLOBAL;
  else
    p->dlopen_mode = RTLD_LOCAL;
  p->dlopen_mode |= (mode & RTLD_UNSHARED) |
      (mode & RTLD_NODELETE) | (mode & RTLD_LAZY_UNDEF);
  p->dlopen_count = 1;
  p->module = module;
  p->prev = NULL;
  p->next = dlopen_handles;
  if (dlopen_handles != NULL)
    dlopen_handles->prev = p;
  dlopen_handles = p;

  /* call the init function if one exists */
  NSSymbol = NSLookupSymbolInModule (p->module, "__init");
  if (NSSymbol != NULL)
    {
      init = NSAddressOfSymbol (NSSymbol);
      init ();
    }

  return (p);
}


/*
 * dlsym() the MacOS X version of the FreeBSD dlopen() interface.
 */
void *
dlsym (void * handle, char * symbol)
{
  struct dlopen_handle *dlopen_handle, *p;
  char symbol2[1024];
  NSSymbol NSSymbol;
  void *address;

  symbol2[0] = '_';
  strcpy (symbol2 + 1, symbol);

  dlopen_handle = (struct dlopen_handle *) handle;

  /*
   * If this is the handle for the main program do a global lookup.
   */
  if (dlopen_handle == (struct dlopen_handle *) &main_program_handle)
    {
      if (NSIsSymbolNameDefined (symbol2) == TRUE)
	{
	  NSSymbol = NSLookupAndBindSymbol (symbol2);
	  address = NSAddressOfSymbol (NSSymbol);
	  dlerror_pointer = NULL;
	  return (address);
	}
      else
	{
	  dlerror_pointer = "symbol not found";
	  return (NULL);
	}
    }

  /*
   * Find this handle and do a lookup in just this module.
   */
  p = dlopen_handles;
  while (p != NULL)
    {
      if (dlopen_handle == p)
	{
	  NSSymbol = NSLookupSymbolInModule (p->module, symbol2);
	  if (NSSymbol != NULL)
	    {
	      address = NSAddressOfSymbol (NSSymbol);
	      dlerror_pointer = NULL;
	      return (address);
	    }
	  else
	    {
	      dlerror_pointer = "symbol not found";
	      return (NULL);
	    }
	}
      p = p->next;
    }

  dlerror_pointer = "bad handle passed to dlsym()";
  return (NULL);
}


/*
 * dlerror() the MacOS X version of the FreeBSD dlopen() interface.
 */
char *
dlerror (void)
{
  const char *p;

  p = (const char *) dlerror_pointer;
  dlerror_pointer = NULL;
  return (char *)(p);
}


/*
 * dlclose() the MacOS X version of the FreeBSD dlopen() interface.
 */
int
dlclose (void * handle)
{
  struct dlopen_handle *p, *q;
  unsigned long options;
  NSSymbol NSSymbol;
  void (*fini) (void);

  dlerror_pointer = NULL;
  q = (struct dlopen_handle *) handle;
  p = dlopen_handles;
  while (p != NULL)
    {
      if (p == q)
	{
	  /* if the dlopen() count is not zero we are done */
	  p->dlopen_count--;
	  if (p->dlopen_count != 0)
	    return (0);

	  /* call the fini function if one exists */
	  NSSymbol = NSLookupSymbolInModule (p->module, "__fini");
	  if (NSSymbol != NULL)
	    {
	      fini = NSAddressOfSymbol (NSSymbol);
	      fini ();
	    }

	  /* unlink the module for this handle */
	  options = 0;
	  if (p->dlopen_mode & RTLD_NODELETE)
	    options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
	  if (p->dlopen_mode & RTLD_LAZY_UNDEF)
	    options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
	  if (NSUnLinkModule (p->module, options) == FALSE)
	    {
	      dlerror_pointer = "NSUnLinkModule() failed for dlclose()";
	      return (-1);
	    }
	  if (p->prev != NULL)
	    p->prev->next = p->next;
	  if (p->next != NULL)
	    p->next->prev = p->prev;
	  if (dlopen_handles == p)
	    dlopen_handles = p->next;
	  free (p);
	  return (0);
	}
      p = p->next;
    }
  dlerror_pointer = "invalid handle passed to dlclose()";
  return (-1);
}

#define	DLDAPI_DEFINED
#endif /* end of Rhapsody Section */

/********************************* 
 *
 *	Macintosh
 *
 *********************************/
#ifdef	DLDAPI_MAC

#include <CodeFragments.h>
#include <strconv.h>

static char *msg_error = NULL;

void *
dlopen (char *dll, int mode)
{
#ifdef __POWERPC__
  CFragConnectionID conn_id;
  Ptr main_addr;
  Str255 name;
  OSErr err;

  if (dll == NULL)
    {
      msg_error = "Library name not valid.";
      return NULL;
    }

  if ((err = GetSharedLibrary ((unsigned char *) str_to_Str255 (dll),
	      kPowerPCCFragArch, kLoadCFrag, &conn_id, &main_addr,
	      name)) != noErr)
    {
      msg_error = "Library cannot be loaded.";
      return NULL;
    }

  msg_error = NULL;
  return (void *) conn_id;
#else
  CFragConnectionID conn_id;
  Ptr main_addr;
  Str255 name;
  OSErr err;

  if (dll == NULL)
    {
      msg_error = "Library name not valid.";
      return NULL;
    }

  if ((err = GetSharedLibrary ((unsigned char *) str_to_Str255 (dll),
	      kMotorola68KCFragArch, kLoadCFrag, &conn_id, &main_addr,
	      name)) != noErr)
    {
      msg_error = "Library cannot be loaded.";
      return NULL;
    }

  msg_error = NULL;
  return (void *) conn_id;
#endif
}


void *
dlsym (void *hdll, char *sym)
{
#ifdef __POWERPC__
  Ptr symbol;
  CFragSymbolClass symbol_type;
  OSErr err;

  if (sym == NULL)
    {
      msg_error = "Symbol name not valid.";
      return NULL;
    }

  if ((err =
	  FindSymbol ((CFragConnectionID) hdll,
	   (unsigned char *) str_to_Str255 (sym), &symbol,
	      &symbol_type)) != noErr)
    {
      msg_error = "Symbol cannot be loaded.";
      return NULL;
    }

  msg_error = NULL;
  return symbol;
#else
  Ptr symbol;
  CFragSymbolClass symbol_type;

  if (sym == NULL)
    {
      msg_error = "Symbol name not valid.";
      return NULL;
    }

  if (FindSymbol ((CFragConnectionID) hdll,
	  (unsigned char *) str_to_Str255 (sym), &symbol,
	  &symbol_type) != noErr)
    {
      msg_error = "Symbol cannot be loaded.";
      return NULL;
    }

  msg_error = NULL;
  return symbol;
#endif
}


char *
dlerror ()
{
  return (msg_error) ? msg_error : "No error detected.";
}


int
dlclose (void *hdll)
{
  /* It should be something like that ....  */
  /* but some applications like Office 2001 */
  /* have a problem with that ... just let  */
  /* the Mac unload the library when the    */
  /* application stop ...                   */
#ifdef __POWERPC__
/*		if( CloseConnection((CFragConnectionID*)hdll) )
		{
			msg_error = "Library cannot be unloaded.";
			return 1;
		}
		msg_error = NULL;	
		return 0;*/
#else
  if (CloseConnection ((CFragConnectionID *) hdll))
    {
      msg_error = "Library cannot be unloaded.";
      return 1;
    }

  msg_error = NULL;
  return 0;
#endif
}

#define	DLDAPI_DEFINED
#endif /* end of Macintosh family */


/********************************* 
 *
 *	BeOS
 *
 *********************************/
#ifdef	DLDAPI_BE
#define	DLDAPI_DEFINED

#include <kernel/image.h>
#include <be/support/Errors.h>

static char *msg_error = NULL;

void *
dlopen (char *dll, int mode)
{
  image_id dll_id;

  if (dll == NULL)
    {
      msg_error = "Library name not valid.";
      return NULL;
    }

  dll_id = load_add_on (dll);

  if (dll_id == B_ERROR)
    {
      msg_error = "Library cannot be loaded.";
      return NULL;
    }

  msg_error = NULL;
  return (void *) dll_id;
}


void *
dlsym (void *hdll, char *sym)
{
  void *address = NULL;

  if (sym == NULL)
    {
      msg_error = "Symbol name not valid.";
      return NULL;
    }

  if (get_image_symbol ((image_id) hdll, sym, B_SYMBOL_TYPE_ANY,
	  &address) != B_OK)
    {
      msg_error = "Symbol cannot be loaded.";
      return NULL;
    }

  msg_error = NULL;
  return address;
}


char *
dlerror ()
{
  return (msg_error) ? msg_error : "No error detected.";
}


int
dlclose (void *hdll)
{
  if (unload_add_on ((image_id) hdll) != B_OK)
    {
      msg_error = "Library cannot be unloaded.";
      return 1;
    }

  msg_error = NULL;
  return 0;
}

#endif /* end of BeOS */



/***********************************
 *
 * 	other platforms
 *
 ***********************************/

#ifdef DLDAPI_OS2
#define	DLDAPI_DEFINED
/*
 *    DosLoadModule(), DosQueryProcAddress(), DosFreeModule(), ...
 */
#endif

#ifdef DLDAPI_NEXT
#define	DLDAPI_DEFINED
#endif

#ifndef DLDAPI_DEFINED
#error	"dynamic load editor undefined"
#endif
