Logo Search packages:      
Sourcecode: gambas version File versions  Download package

gbx_library.c

/***************************************************************************

  library.c

  GAMBAS plug-in management routines

  (c) 2000-2004 Benoît Minisini <gambas@users.sourceforge.net>

  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.

***************************************************************************/

#define __GBX_LIBRARY_C

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gb_common.h"
#include "gb_common_buffer.h"
#include "gb_common_case.h"
#include "gb_error.h"
#include "gb_alloc.h"
#include "gbx_list.h"
#include "gb_replace.h"

#include <fcntl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <ctype.h>

#include "gb_error.h"

#include "gbx_class.h"
#include "gbx_exec.h"
#include "gbx_event.h"
#include "gbx_stack.h"
#include "gb_file.h"
#include "gbx_archive.h"
#include "gbx_project.h"
#include "gbx_api.h"

#include "gbx_string.h"
#include "gbx_object.h"

#include "gbx_library.h"


/*#define DEBUG*/
/*#define DEBUG_PRELOAD*/

PUBLIC LIBRARY *LIBRARY_Current;
PUBLIC int LIBRARY_count = 0;

PRIVATE LIBRARY *_library_list = NULL;


#if 0
PRIVATE lt_ptr library_malloc(size_t size)
{
  lt_ptr ptr;
  ALLOC(&ptr, size, "library_malloc");
  printf("library_malloc: %d -> %p\n", size, ptr);
  return ptr;
}


PRIVATE void library_free(lt_ptr ptr)
{
  printf("library_free -> %p\n", ptr);
  FREE(&ptr, "library_free");
}
#endif

PRIVATE void *get_symbol(LIBRARY *lib, const char *symbol, bool err)
{
  void *sym;

  sym = lt_dlsym(lib->handle, symbol);
  if (sym == NULL && err)
  {
    strcpy(COMMON_buffer, lt_dlerror());
    lt_dlclose(lib->handle);
    lib->handle = NULL;
    THROW(E_LIBRARY, lib->path, COMMON_buffer);
  }

  return sym;
}


PRIVATE void copy_interface(long *src, long *dst)
{
  for(;;)
  {
    if (*src == 0)
      return;

    *dst++ = *src++;
  }
}


PUBLIC void LIBRARY_load_all(void)
{
  LIBRARY *lib;

  if (EXEC_debug)
    LIBRARY_create("gb.eval");

  for (lib = _library_list; lib; lib = (LIBRARY *)lib->list.next)
  {
    lib->preload = TRUE;
    LIBRARY_load(lib);
  }
}


PRIVATE bool read_line(int fd, char *dir, int max)
{
  char *p;
  char c;
  ssize_t r;

  p = dir;

  for(;;)
  {
    max--;

    for (;;)
    {
      r = read(fd, &c, 1);
      if (r == 1)
        break;
      if (errno != EINTR)
        return TRUE;
    }

    if (c == '\n' || max == 0)
    {
      *p = 0;
      return FALSE;
    }

    *p++ = c;
  }
}

#if 0
PRIVATE void add_preload(char **env, const char *libdir, const char *lib)
{
  if (*env == COMMON_buffer)
    *env += sprintf(*env, "LD_PRELOAD=");

  *env += sprintf(*env, "%s/lib/lib.%s.so.0", libdir, lib);
}
#endif
PRIVATE void add_preload(char **env, const char *lib)
{
  char *org;

  if (*env == COMMON_buffer)
  {
    org = getenv("LD_PRELOAD");
    if (org && *org)
      *env += sprintf(*env, "%s ", org);
  }

  *env += sprintf(*env, "%s ", lib);
}


PUBLIC void LIBRARY_preload(const char *file, char **argv)
{
#if DO_PRELOADING
  const char *path;
  char dir[MAX_PATH];
  /*char libdir[MAX_PATH];*/
  int fd;
  char *p;
  char *env;
  bool preload;
  bool stop;

  env = getenv("GAMBAS_PRELOAD");

  if (env)
  {
    #ifdef DEBUG_PRELOAD
    fprintf(stderr, "Preloading done.\nLD_PRELOAD=%s\nGAMBAS_PRELOAD=%s\n", getenv("LD_PRELOAD"), getenv("GAMBAS_PRELOAD"));
    #endif

    if (*env)
      setenv("LD_PRELOAD", env, TRUE);
    else
      unsetenv("LD_PRELOAD");

    unsetenv("GAMBAS_PRELOAD");

    return;
  }

  preload = FALSE;

  /*
  path = FILE_readlink("/usr/bin/gbx");
  if (!path)
    goto _PANIC;

  path = FILE_get_dir(FILE_get_dir(path));
  strcpy(libdir, path);
  */

  if (!EXEC_arch)
  {
    if (file == NULL)
      file = ".";

    if (*file == '/')
    {
      strcpy(dir, file);
    }
    else
    {
      if (*file == '.' && file[1] == '/')
        file += 2;

      path = FILE_getcwd(file);
      if (path == NULL)
        goto _PANIC;

      chdir(path);

      path = FILE_getcwd(NULL);
      if (path == NULL)
        goto _PANIC;

      strcpy(dir, path);
    }

    file = FILE_cat(dir, ".project", NULL);
  }

  fd = open(file, O_RDONLY);
  /* Silently ignored */
  if (fd < 0)
    return;

  if (EXEC_arch)
    lseek(fd, 32 + sizeof(ARCH_HEADER), SEEK_SET);

  read_line(fd, dir, MAX_PATH);

  if (strcasecmp(dir, "# Gambas Project File 1.0"))
  {
    fprintf(stderr, "Syntax error in project file.\n");
    goto _PANIC;
  }

  env = COMMON_buffer;
  stop = FALSE;

  for(;;)
  {
    if (read_line(fd, dir, MAX_PATH))
      break;

    if (strncasecmp(dir, "library=", 8) == 0)
    {
      stop = TRUE;
      p = &dir[8];

      if (strcasecmp(p, "gb.qt") == 0)
      {
        add_preload(&env, "libqt-mt.so.3");
        preload = TRUE;
      }
      #if HAVE_KDE_COMPONENT
      else if (strcasecmp(p, "gb.qt.kde") == 0)
      {
        /*add_preload(&env, libdir, p);*/
        add_preload(&env, "libkdecore.so.4");
        add_preload(&env, "libkdeui.so.4");
        add_preload(&env, "libDCOP.so.4");
        add_preload(&env, "libkio.so.4");
        /*fprintf(stderr, "Warning: preloading KDE libraries\n");*/
        preload = TRUE;
      }
      #endif
    }
    else
    {
      if (stop)
        break;
    }
  }

  close(fd);

  if (preload)
  {
    env = getenv("LD_PRELOAD");
    if (env && *env)
      setenv("GAMBAS_PRELOAD", env, TRUE);
    else
      setenv("GAMBAS_PRELOAD", "", TRUE);

    setenv("LD_PRELOAD", COMMON_buffer, TRUE);

    #ifdef DEBUG_PRELOAD
    fprintf(stderr, "Preloading...\nLD_PRELOAD=%s\nGAMBAS_PRELOAD=%s\n", getenv("LD_PRELOAD"), getenv("GAMBAS_PRELOAD"));
    #endif

    execv("/usr/bin/gbx", argv);
  }

  return;

_PANIC:

  fprintf(stderr, "Cannot preload libraries: %s\n", strerror(errno));
#endif
}


PUBLIC void LIBRARY_init(void)
{
  /*if (putenv("LD_BIND_NOW=true"))
    ERROR_panic("Cannot set LD_BIND_NOW: &1", strerror(errno));

  if (putenv("KDE_MALLOC=0"))
    ERROR_panic("Cannot set KDE_MALLOC: &1", strerror(errno));*/

  /*lt_dlmalloc = library_malloc;
  lt_dlfree = library_free;*/

  #ifdef USE_LTDL
  if (lt_dlinit())
    ERROR_panic("Cannot initialize plug-in management: %s", lt_dlerror());
  #endif
}


PUBLIC void LIBRARY_exit(void)
{
  LIBRARY *lib;

  /*for (lib = _library_list; lib; lib = (LIBRARY *)lib->list.next)*/
  LIST_for_each(lib, _library_list)
    LIBRARY_unload(lib);

  while (_library_list)
    LIBRARY_delete(_library_list);

  #ifdef USE_LTDL
  lt_dlexit();
  #endif
}


PUBLIC LIBRARY *LIBRARY_find(const char *name)
{
  LIBRARY *lib;

  /*for (lib = _library_list; lib; lib = (LIBRARY *)lib->list.next)*/
  LIST_for_each(lib, _library_list)
  {
    if (strcmp(lib->name, name) == 0)
      return lib;
  }

  return NULL;
}


PUBLIC void LIBRARY_get_interface(LIBRARY *lib, long version, void *iface)
{
  char symbol[32];
  int i, len;
  char c;

  len = strlen(lib->name);
  for (i = 0; i < len; i++)
  {
    c = toupper(lib->name[i]);
    if (!isalnum(c))
      c = '_';

    symbol[i] = c;
  }

  sprintf(&symbol[len], "_%ld", version);

  copy_interface((long *)get_symbol(lib, symbol, TRUE), (long *)iface);
}


PUBLIC boolean LIBRARY_get_interface_by_name(const char *name, long version, void *iface)
{
  LIBRARY *lib;

  lib = LIBRARY_find(name);
  if (lib == NULL)
    return TRUE;

  LIBRARY_get_interface(lib, version, iface);
  return FALSE;
}



PUBLIC LIBRARY *LIBRARY_create(const char *path)
{
  LIBRARY *lib;
  int len;

  lib = LIBRARY_find(path);
  if (lib)
    return lib;

  ALLOC_ZERO(&lib, sizeof(LIBRARY), "LIBRARY_create");

  lib->class = CLASS_Library;
  lib->ref = 1;
  lib->handle = NULL;

  if (path)
  {
    STRING_new(&lib->name, FILE_get_name(path), 0);
    lib->free_name = TRUE;

    if (!FILE_is_relative(path))
      ERROR_panic("Absolute path are not allowed in libraries: %s", path);

    len = strlen(path) + strlen(PROJECT_lib_path) + 8;
    STRING_new(&lib->path, NULL, len);
    sprintf(lib->path, LIB_PATTERN, PROJECT_lib_path, path);

    lib->persistent = FALSE;
    lib->preload = FALSE;
  }
  else
  {
    lib->name = "gb";
    lib->free_name = FALSE;
    lib->path = NULL;
    lib->persistent = TRUE;
    lib->preload = TRUE;
  }

  LIST_insert((void **)&_library_list, lib, &lib->list);
  LIBRARY_count++;

  return lib;
}


PUBLIC void LIBRARY_delete(LIBRARY *lib)
{
  LIBRARY_unload(lib);
  LIST_remove((void **)&_library_list, lib, &lib->list);
  LIBRARY_count--;

  if (lib->free_name)
    STRING_free(&lib->name);

  FREE(&lib, "LIBRARY_delete");
}


PUBLIC void LIBRARY_load(LIBRARY *lib)
{
  int (*func)();
  GB_INTERFACE *iface;
  GB_DESC **desc;

#ifdef DEBUG
  clock_t t = clock();
  fprintf(stderr, "Loading library %s\n", lib->path);
#endif

  if (lib->path == NULL)
    return;

  if (lib->handle)
    return;

  #ifdef USE_LTDL
    lt_dlopen_flag = RTLD_LAZY;
    lib->handle = lt_dlopenext(lib->path);
  #else
    lib->handle = dlopen(lib->path, RTLD_LAZY);
  #endif

  if (lib->handle == NULL)
    THROW(E_LIBRARY, lib->path, lt_dlerror());

  func = get_symbol(lib, LIB_INIT, TRUE);

  /* Interface de Gambas */

  iface = get_symbol(lib, LIB_GAMBAS, TRUE);
  copy_interface((long *)GAMBAS_Api, (long *)iface);

  /* Initialisation */

  LIBRARY_Current = lib;
  lib->persistent = (boolean)(*func)();

  /* Déclaration des classes */
  desc = get_symbol(lib, LIB_CLASS, FALSE);
  if (desc)
    LIBRARY_declare(desc);

#ifdef DEBUG
  fprintf(stderr, "Library %s loaded ", lib->path);
  fprintf(stderr, "in %g s\n", ((double)(clock() - t) / CLOCKS_PER_SEC));
#endif
}


PUBLIC void LIBRARY_declare(GB_DESC **desc)
{
  while (*desc != NULL)
  {
    if (CLASS_register(*desc) == NULL)
      THROW(E_REGISTER, *((char **)desc));

    desc++;
  }
}


PUBLIC void LIBRARY_unload(LIBRARY *lib)
{
  void (*gambas_exit)();

  if (lib->path == NULL)
    return;

  STRING_free(&lib->path);

  if (lib->handle == NULL)
    return;

  /* Pas de libération des classes préchargée ! */

  /* Vérification qu'aucune classe de la librairie n'est instanciée ! */

  gambas_exit = lt_dlsym(lib->handle, LIB_EXIT);
  if (gambas_exit != NULL)
    (*gambas_exit)();

  if (lib->persistent)
  {
    gambas_exit = lt_dlsym(lib->handle, "_fini");
    if (gambas_exit != NULL)
      (*gambas_exit)();
  }
  else
    lt_dlclose(lib->handle);

  lib->handle = NULL;

#ifdef DEBUG
  printf("Unloading library %s\n", lib->name);
#endif
}


PUBLIC LIBRARY *LIBRARY_next(LIBRARY *lib)
{
  if (lib)
    return (LIBRARY *)(lib->list.next);
  else
    return _library_list;
}


#if 0
PUBLIC const char *LIBRARY_get_control_list(LIBRARY *lib)
{
  void (*gambas_info)();
  const char *result = NULL;

  gambas_info = lt_dlsym(lib->handle, LIB_INFO);
  if (gambas_info == NULL)
    return NULL;

  (*gambas_info)(GB_INFO_CONTROL, &result);
  return result;
}
#endif


Generated by  Doxygen 1.6.0   Back to index