#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 ) ;
   void *module_handle = NULL ;
   int hashvalue( streng *name )  ;
#endif 


struct library_func {
   streng *name ;
   streng *((*addr)()) ;
   struct library *lib ;
   struct library_func *next, *prev ;
   struct library_func *forw, *backw ;  
} ;

#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 ;
   struct entrypt *pptr ;

   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 ;

   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 ;

   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 ;
   int hash ;

   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 ;
   struct entrypt *addr ;
   struct library_func *fptr ;

   result = 1 ;

   if (lptr==NULL) 
     lptr = module_handle ;

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



#endif /* DYNAMIC */

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

#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 ) 
{
   checkparam( parms, 1, 1 ) ;
#ifdef DYNAMIC
   return int_to_streng( find_library_func( parms->value ) == NULL ) ;
#else
   return int_to_streng( 1 ) ;
#endif
}   

streng *rex_rxfuncdrop( paramboxptr parms )
{
#ifdef DYNAMIC
   struct library_func *box ;

   checkparam( parms, 1, 1 ) ;
   box = find_library_func( parms->value ) ;

   if (box)
      remove_function( box ) ;

   return int_to_streng( box!=NULL ) ;
#else
   checkparam( parms, 1, 1 ) ;
   return int_to_streng( 1 ) ;
#endif
}


/*
 * 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
   struct library *lptr ;
   void *handle ;
   struct entrypt *res ;
   streng *rxname ;
   streng *module, *objnam ;
 
   checkparam( parms, 3, 3 ) ;

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

   if (!(lptr=find_library(module)))
   {
      handle = wrapper_load( module ) ;
   
      res = NULL ;
      if (handle)
         res = wrapper_getentries( handle ) ;
    
      if (res && handle) 
      {
         lptr = Malloc( sizeof( struct library )) ;
         lptr->name = Str_dup( rxname ) ;
         lptr->handle = handle ;
         lptr->funcs = res ;
         lptr->first = NULL ;

         insert_library( lptr ) ;
      }   
   }
   return int_to_streng( loadrxfunc( lptr, rxname, objnam )) ;
#else
   checkparam( parms, 3, 3 ) ;
   return int_to_streng( 1 ) ;
#endif
}


streng *do_loaded_func( void *vbox, paramboxptr parms )
{
#ifdef DYNAMIC
   struct library_func *box ;

   box = (struct library_func*)(vbox) ;

   assert( box ) ;
   module_handle = box->lib ;
   return (*(box->addr))( parms ) ;
#else
# ifndef NDEBUG
   exiterror( ERR_INTERPRETER_FAILURE ) ;
# endif
   return nullstringptr() ;
#endif
}

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

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



