#ifndef lint
static char *RCSid = "$Header: /auto/home/flipper/anders/flipper/prosj/rexx/src/RCS/library.c,v 1.2 1993/02/09 18:15:09 anders Exp anders $";
#endif

/*
 *  The Regina Rexx Interpreter
 *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
 *
 *  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.
 */

/*
 * Sigh, let's live dangerously. We need to remove the definition of 
 * _POSIX_SOURCE, in order to get defined some extenstions to POSIX, 
 * since dynamic loading is not a part of POSIX.
 */

#include "rexx.h"
#include <assert.h>
#include <string.h>

#ifdef DYNAMIC
   void *wrapper_load( streng *name ) ;
   struct entrypt *wrapper_getentries( void *handle ) ;
   PFN wrapper_get_addr( struct library *, streng *);
   void *module_handle = NULL ;
   int hashvalue( streng *name )  ;
#endif 

#ifdef DYNAMIC
static struct library *first_library = NULL ;
static struct library_func *libfuncs[133] = { NULL } ;

/* Operations on the library and library_func structures */


struct entrypt *get_addr_of( struct library *lptr, streng *name )
{
   streng *newname=NULL ;
   struct entrypt *pptr=NULL ;

   newname = Str_ify( name ) ;
   for (pptr=lptr->funcs; pptr->name; pptr++ ) 
      if (!strcmp( newname->value, pptr->name ))
         break ;

   if (newname!=name)
      Free_string( newname ) ;

   return pptr ;
}

static void insert_library( struct library *ptr ) 
{
   ptr->prev = NULL ;
   ptr->next = first_library ;
   if ((first_library=ptr)->next)
      ptr->next->prev = ptr ;
}

/*
static void remove_library( struct library *ptr )
{
   if (ptr->next)
      ptr->next->prev = ptr->prev ;

   if (ptr->prev)
      ptr->prev->next = ptr->next ;
   else
      first_library = ptr->next ;

   assert( ptr->name ) ;
   Free_string( ptr->name ) ;
   assert( ptr->first == NULL ) ;
   Free( ptr ) ;
}
*/


static struct library *find_library( streng *name ) 
{
   struct library *lptr=NULL ;

   lptr = first_library ;
   for (;lptr; lptr=lptr->next)
      if (!Str_cmp(name,lptr->name))
          return lptr ;

   return NULL ;
}

static void add_function( struct library_func *fptr )
{
   int hash=0 ;

   hash = hashvalue( fptr->name ) ;
   fptr->next = libfuncs[hash] ;
   libfuncs[hash] = fptr ;
   fptr->prev = NULL ;
   if (fptr->next)
      fptr->next->prev = fptr ;

   fptr->forw = fptr->lib->first ;
   fptr->backw = NULL ;
   fptr->lib->first = fptr ;
   if (fptr->forw)
      fptr->forw->backw = fptr ;
}

static void remove_function( struct library_func *fptr )
{
   if (fptr->next)
      fptr->next->prev = fptr->prev ;
   if (fptr->prev)
      fptr->prev->next = fptr->next ;
   else
      libfuncs[hashvalue(fptr->name)] = fptr->next ;

   if (fptr->forw)
      fptr->forw->backw = fptr->backw ;
   if (fptr->backw)
      fptr->backw->forw = fptr->forw ;
   else
      fptr->lib->first = fptr->forw ;
}

static struct library_func *find_library_func( streng *name ) 
{
   struct library_func *lptr=NULL ;
   int hash=0 ;

   hash = hashvalue( name ) ;
   for (lptr=libfuncs[hash]; lptr; lptr=lptr->next)
      if (!Str_cmp(name,lptr->name))
         return lptr ;

   return NULL ;
}
      

/*
 * Calulates a hashvalue for 'name', for use as index into the array of
 *    functions that have been loaded from external function packages.
 */
int hashvalue( streng *name ) 
{
   char *ptr=name->value, *end=Str_end(name) ;
   int result=0 ;

   for ( ; ptr<end; ptr++ )
      result = result + *ptr ;

   return (result % 133) ;
}


static streng *err_message=NULL ;

void set_err_message( char *message )
{
   if (err_message)
      Free_string( err_message ) ;

   err_message = Str_cre( message ) ;
}

int loadrxfunc( struct library *lptr, streng *rxname, streng *objnam )
{
   int result=1 ;
   PFN addr=NULL ;
   struct library_func *fptr=NULL ;

   if (lptr==NULL) 
     lptr = module_handle ;

   if (lptr)
   {
      fptr = find_library_func( rxname ) ;
      if (!fptr || fptr->lib!=lptr)
      {
         addr = wrapper_get_addr( lptr, objnam ) ;
         if (addr)
           {
            fptr = Malloc( sizeof( struct library_func )) ;
            fptr->name = Str_upper ( Str_dup( rxname ) );
            fptr->addr = addr ;
            fptr->lib = lptr ;
            add_function( fptr ) ;
            result = 0 ;
           }
         else
            result = 1 ;
      }
   }
   return result ;
}



#endif /* DYNAMIC */

streng *rex_rxfuncerrmsg( paramboxptr parms ) 
{
   checkparam(  parms,  0,  0 , "RXFUNCERRMSG" ) ;

#ifdef DYNAMIC
   if (err_message)
      return Str_dup( err_message ) ;
   else
      return nullstringptr() ;
#else
   return Str_cre( "Platform doesn't support dynamic linking" ) ;
#endif
}

streng *rex_rxfuncquery( paramboxptr parms )
{
   streng *name=NULL;
#ifdef DYNAMIC
   struct library_func *fptr=NULL ;
#endif

   checkparam(  parms,  1,  1 , "RXFUNCQUERY" ) ;
   name = Str_upper( parms->value ) ;
#ifdef DYNAMIC
   fptr = find_library_func( name ) ;

   if (fptr)
      return int_to_streng( 0 ) ;
   /*
    *... otherwise fall through and try to find the function
    * loaded via RexxRegisterFunctionExe()
    */
#endif
   return int_to_streng((external_func( name ) ) ? 0 : 1);
}


/*
 * parameters:
 *   1) name of the function to be added (in Rexx)
 *   2) name of object file to link in
 *   3) name of the function to be added (in the object file)
 */
streng *rex_rxfuncadd( paramboxptr parms )
{
#ifdef DYNAMIC
   streng *rxname=NULL ;
   streng *module=NULL, *objnam=NULL ;
 
   checkparam(  parms,  3,  3 , "RXFUNCADD" ) ;

   rxname = Str_upper (Str_dup (parms->value) ) ;
   module = (parms=parms->next)->value ;
   objnam = parms->next->value ;

   return int_to_streng( rex_rxfuncdlladd( rxname, module, objnam ));
#else
   checkparam(  parms,  3,  3 , "RXFUNCADD" ) ;
   return int_to_streng( 1 ) ;
#endif
}

int rexxsaa_rxfuncdlladd( char* rxname, char* module, char* objnam )
{
#ifdef DYNAMIC
   streng *ext = Str_upper(Str_cre(rxname));
   streng *lib = Str_cre(module);
   streng *intr = Str_cre(objnam);

   return(rex_rxfuncdlladd(ext,lib,intr));
#else
   return 1;
#endif
}
/*
 * This loads a module as a result of a RexxRegisterFunctionDll() call
 *
 * parameters:
 *   1) name of the function to be added (in Rexx)
 *   2) name of object file to link in
 *   3) name of the function to be added (in the object file)
 */
int rex_rxfuncdlladd( streng* rxname, streng* module, streng* objnam )
{
#ifdef DYNAMIC
   struct library *lptr=NULL ;
   void *handle=NULL ;
   int rc=0;
 
   if (!(lptr=find_library(module)))
   {
      handle = wrapper_load( module ) ;
      if (handle)
        {
         lptr = Malloc( sizeof( struct library )) ;
         lptr->name = Str_dup( module ) ;
         lptr->handle = handle ;
         lptr->funcs = NULL ;
         lptr->first = NULL ;
        }
      else
         return 1;
      insert_library( lptr ) ;
   }
   rc = loadrxfunc( lptr, rxname, objnam ) ;
   return ( rc );
#else
   return 1;
#endif
}

streng *rex_rxfuncdrop( paramboxptr parms )
{
   streng *name=NULL;
   int rc=0;
#ifdef DYNAMIC
   struct library_func *fptr=NULL ;
#endif

   checkparam(  parms,  1,  1 , "RXFUNCDROP" ) ;
   name = Str_upper( parms->value ) ;
#ifdef DYNAMIC
   fptr = find_library_func( name ) ;

   if (fptr)
     {
      /*
       * if found OK, remove the function...
       */
      remove_function( fptr ) ;
      return int_to_streng( 0 ) ;
     }
   /*
    *... otherwise fall through and try to remove from function
    * loaded via RexxRegisterFunctionExe()
    */
#endif
   if (external_func( name ) )
      rc = delfunc ( name ) ;
   else
      rc = 1;
   return int_to_streng( rc ) ; /* value of 1 indicates failure */
}

int rex_rxfuncdlldrop( streng* objnam )
{
   int rc=0;
   streng *name=Str_upper( objnam );
#ifdef DYNAMIC
   struct library_func *fptr=NULL ;
   /*
    * try to find the function loaded from a dynamic library
    */
   fptr=find_library_func(name);
   if (fptr)
     {
      /*
       * if found OK, remove the function...
       */
      remove_function(fptr);
      return(0);
     }
   /*
    *... otherwise fall through and try to remove from function
    * loaded via RexxRegisterFunctionExe()
    */
#endif
   if (external_func( name ) )
      rc = delfunc ( name );
   else
      rc = 1;
   return rc;                   /* value of 0 indicates failure */
}
                                         
int rex_rxfuncdllquery( streng* objnam )
{
   streng *name=Str_upper( objnam );
#ifdef DYNAMIC
   struct library_func *fptr=NULL ;
   /*
    * try to find the function loaded from a dynamic library
    */
   fptr=find_library_func(name);
   if (fptr)
      return(0);
   /*
    *... otherwise fall through and try to find the function
    * loaded via RexxRegisterFunctionExe()
    */
#endif
   return((external_func( name )) ? 0 : 1 );
}

void *loaded_lib_func( streng *name ) 
{
#ifdef DYNAMIC
   struct library_func *box=NULL ;

   box = find_library_func( name ) ;
   return (void*)(box) ;
#else
   return NULL ;
#endif
}


