/*

    GEO, Postgres GIS (Geografic Information System) frontend

    Copyright (C) 1991  TNO Institute for Perception, The Netherlands
			Written by: Tom Vijlbrief

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


Note that parts of this distribution are taken from the ET++
distribution. See the et2.2/ subdirectories for details.

===============================================================================
Tom Vijlbrief
TNO Institute for Perception		Phone: +31 34 63 562 11
P.O. Box 23				Fax:   +31 34 63 539 77
3769 ZG  Soesterberg			E-mail: tom@izf.tno.nl
The Netherlands				or: uunet!hp4nl.nluug.nl!tnosoes!tom
===============================================================================

*/

static char *sccs_id= (sccs_id, "@(#)dynload.c	3.8 7/28/92");

#undef DLOPEN

#include	<stdio.h>

#include	"dynload.h"

#ifndef DLOPEN
extern "C" {

int		dld_init(char *path);
char *		dld_find_executable(char *command);
int		dld_link(char *filename);
unsigned long	dld_get_symbol(char *id);
unsigned long	dld_get_func(char *func);
int		dld_unlink_by_file(char *path, int hard);
int		dld_unlink_by_symbol(char *id, int hard);
int		dld_function_executable_p(char *func);
char **		dld_list_undefined_sym();
int		dld_create_reference(char *name);
int		dld_define_sym(char *name, unsigned int size);
void		dld_remove_defined_symbol(char *name);
void		dld_perror(char *user_msg);

extern int	dld_undefined_sym_count;

void		free(void *);
}

#ifndef DYNAMIC
extern char	*dld_prog_name;
#else
static char	*dld_prog_name;

extern "C"	call_ctors();
extern "C"	dld_load_more();
#endif

#else // DLOPEN

extern "C" {
#include <dlfcn.h>
}

#endif // DLOPEN


// #define	DEBUG

int patch_vtable(void *the_object, void *org_vf, void *new_vf,
  int copy_from_ro_segment)
{
  long *vt= * (((long **) the_object) + 1);
  int  n_entries= vt[1];
  long *new_vt;

#ifdef DEBUG
fprintf(stderr, "Nr Entries: %d\n", n_entries);
#endif

  if (copy_from_ro_segment) {
    int nlongs= 2 * (n_entries+1);

    new_vt= new long[nlongs];
    bcopy(vt, new_vt, nlongs * sizeof(long));
    vt= new_vt;
  }

  for (vt+= 3; n_entries > 0; n_entries--, vt+= 2) {
    if ((void*) *vt == org_vf) {
      *vt= (long) new_vf;
      if (copy_from_ro_segment)
        *(((long **) the_object) + 1)= new_vt;
      return 1;
    }
  }

  if (copy_from_ro_segment)
    delete new_vt;

  return 0;
}


void show_unresolved()
{
#ifndef DLOPEN
#ifndef DYNAMIC
  if (dld_undefined_sym_count) {
      char **un= dld_list_undefined_sym();

      fprintf(stderr, "Undefined symbols:\n");
      for (int i= 0; i < dld_undefined_sym_count; i++)
        fprintf(stderr, "%s\n", un[i]);
      free(un);
  }
#endif
#endif
}


#ifndef DYNAMIC
int geo_dld_init()
{
#ifndef DLOPEN
  static int must_init= 1;

  if (must_init) {
    must_init= 0;
    if (dld_init(dld_prog_name)) {
	dld_perror("dld_init");
	return 0;
    }
  }
#endif
  return 1;
}
#endif


#ifdef DLOPEN
static int	dld_undefined_sym_count;

static void	*dl_handle;

int dld_link(char *file)
{
  dl_handle= dlopen(file, 1);
  return dl_handle == 0 ? -1: 0;
}

void *dld_get_func(char *func)
{
  return dlsym(dl_handle, func);
}

void dld_perror(char *es)
{
  char *de= dlerror();
  if (!de)
    de= "";
  fprintf(stderr, "%s: %s\n", es, de);
}
#endif


pfcp dyn_load(char *ofile, char *function, int dont_load)
{
#ifndef DYNAMIC
  if (!geo_dld_init())
	return 0;
#endif

  if (!dont_load) {
    fprintf(stderr, "Dyn loading: %s\n", ofile);
    if (dld_link(ofile)) {
	dld_perror("dld_link");
	return 0;
    }

    if (dld_undefined_sym_count) {
      show_unresolved();
      fprintf(stderr, "Linking /usr/lib/libc.a\n");
      if (dld_link("/usr/lib/libc.a")) {
	dld_perror("dld_link /usr/lib/libc.a");
	return 0;
      }
      fprintf(stderr, "Linking /usr/lib/libm.a\n");
      if (dld_link("/usr/lib/libm.a")) {
	dld_perror("dld_link /usr/lib/libm.a");
	return 0;
      }

      if (dld_undefined_sym_count) {
        fprintf(stderr, "Unresolved references remain !!!\n");
        show_unresolved();
        return 0;
      }
#ifdef DYNAMIC
      fprintf(stderr, "Done\nCalling global constructors...\n");
      call_ctors();
#endif
    }
  }

  pfcp		the_fp;

  if (the_fp= (pfcp) dld_get_func(function))
    return the_fp;
  else
    dld_perror(function);
  
  return 0;
}



#ifdef DYNAMIC
static char	GEO_OBJECTS[]= "geo.objects";

main(int argc, char *argv[])
{
  FILE	*f;
  char	*gs;

  dld_prog_name= argv[0];
#if 0
  geo_dld_init();
#else
  dld_load_more(dld_prog_name, 0, 0);
#endif

  if ((gs= getenv("GEO_OBJECTS")) == NULL)
    gs= GEO_OBJECTS;

  if ((f= fopen(gs, "r")) == NULL) {
    perror(gs);
    exit(1);
  }

  char	fn[100];
  while (fscanf(f, "%s", fn) == 1) {
    fprintf(stderr, "Loading: %s\n", fn);
    dld_link(fn);
  }
  if (dld_undefined_sym_count)
    show_unresolved();

#if 1
  call_ctors();
#endif

  typedef int	(*ptgeo)(int, char *[]);
  ptgeo		the_fp;

  if (the_fp= (ptgeo) dld_get_func("geo_go"))
    _exit((*the_fp)(argc, argv));

  dld_perror("geo_go");
  return(1);
}

extern "C" void dl_repl(char *f)
{
  dld_unlink_by_file(f, 1);
  dld_link(f);
#if 1
  call_ctors();
#endif

  if (dld_undefined_sym_count)
    dld_link("/usr/lib/libm.a");
  if (dld_undefined_sym_count)
    dld_link("/usr/lib/libc.a");
  if (dld_undefined_sym_count)
    show_unresolved();
}
#endif
