e945a9faa6
* NEW: Requesting a component interface automatically loads the component now. * NEW: The Image and Picture functions in the interpreter API were removed. [GB.DRAW] * NEW: A new function in the Draw interface to get the size of a picture. This function replaces the removed Picture interpreter API. [GB.IMAGE] * NEW: This is a new component that manages images in memory. It implements the Image class, can create images of many formats (RGB, RGBA, BGRA...) and convert image data between different formats. This component replaces the previous interpreter Image API. All components were ported to this new image management system by loading this component automatically. Beware that the Image constructor has changed! The transparent property has been removed, and there is an optional color argument that is used for initializing the image data. Moreover, many classes (Webcam, PdfDocument...) that have an Image property lost their Picture property. Now to get a Picture, you must use the Image property and then convert the Image to a Picture. [GB.QT] * BUG: As now the Image class overrides the one located in gb.image, it must be declared early, at least earlier than the Picture class. git-svn-id: svn://localhost/gambas/trunk@1803 867c0c6c-44f3-4631-809d-bfa615b0a4ec
553 lines
10 KiB
C
553 lines
10 KiB
C
/***************************************************************************
|
||
|
||
library.c
|
||
|
||
GAMBAS plug-in management routines
|
||
|
||
(c) 2000-2007 Benoit 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 "gb_replace.h"
|
||
#include "gb_magic.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_component.h"
|
||
|
||
#include "gbx_library.h"
|
||
|
||
|
||
/*#define DEBUG*/
|
||
//#define DEBUG_PRELOAD
|
||
|
||
// Maximum size of a project or startup file
|
||
// This avoids reading too much in the archive file!
|
||
|
||
#define MAX_SIZE 2048
|
||
|
||
int _bytes_read = 0;
|
||
|
||
#if 0
|
||
static 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;
|
||
}
|
||
|
||
|
||
static void library_free(lt_ptr ptr)
|
||
{
|
||
printf("library_free -> %p\n", ptr);
|
||
FREE(&ptr, "library_free");
|
||
}
|
||
#endif
|
||
|
||
static 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->name, COMMON_buffer);
|
||
}
|
||
|
||
return sym;
|
||
}
|
||
|
||
|
||
static void copy_interface(intptr_t *src, intptr_t *dst)
|
||
{
|
||
for(;;)
|
||
{
|
||
if (*src == (intptr_t)0)
|
||
return;
|
||
|
||
*dst++ = *src++;
|
||
}
|
||
}
|
||
|
||
|
||
static bool read_line(FILE *fd, char *dir, int max)
|
||
{
|
||
char *p;
|
||
int c;
|
||
//ssize_t r;
|
||
|
||
p = dir;
|
||
|
||
for(;;)
|
||
{
|
||
_bytes_read++;
|
||
if (_bytes_read > MAX_SIZE)
|
||
return TRUE;
|
||
|
||
for (;;)
|
||
{
|
||
c = fgetc(fd); //read(fd, &c, 1);
|
||
if (c != EOF)
|
||
break;
|
||
if (errno != EINTR)
|
||
return TRUE;
|
||
}
|
||
|
||
if (c == '\n')
|
||
{
|
||
*p = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
max--;
|
||
if (max > 0)
|
||
*p++ = c;
|
||
}
|
||
}
|
||
|
||
static 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);
|
||
}
|
||
|
||
/*
|
||
static const char *start_with(const char *str, const char *begin)
|
||
{
|
||
int len = strlen(begin);
|
||
if (!strncasecmp(str, begin, len))
|
||
return &str[len];
|
||
else
|
||
return NULL;
|
||
}
|
||
*/
|
||
|
||
static bool check_preload(const char *lib, char **penv)
|
||
{
|
||
if (strcasecmp(lib, "gb.qt") == 0)
|
||
{
|
||
add_preload(penv, "libqt-mt.so.3");
|
||
#ifdef DEBUG_PRELOAD
|
||
fprintf(stderr, "found %s\n", lib);
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
//#if HAVE_KDE_COMPONENT
|
||
else if (strcasecmp(lib, "gb.qt.kde") == 0)
|
||
{
|
||
/*add_preload(&env, libdir, p);*/
|
||
add_preload(penv, "libkdecore.so.4");
|
||
add_preload(penv, "libkdeui.so.4");
|
||
add_preload(penv, "libDCOP.so.4");
|
||
add_preload(penv, "libkio.so.4");
|
||
/*fprintf(stderr, "Warning: preloading KDE libraries\n");*/
|
||
#ifdef DEBUG_PRELOAD
|
||
fprintf(stderr, "found %s\n", lib);
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
void LIBRARY_preload(const char *file, char **argv)
|
||
{
|
||
#if DO_PRELOADING
|
||
const char *path;
|
||
char dir[MAX_PATH];
|
||
/*char libdir[MAX_PATH];*/
|
||
FILE *fd;
|
||
char *env;
|
||
bool preload;
|
||
char *err = NULL;
|
||
bool startup = FALSE;
|
||
|
||
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;
|
||
}
|
||
|
||
_bytes_read = 0;
|
||
preload = FALSE;
|
||
|
||
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, ".startup", NULL);
|
||
/*if (!FILE_exist(file))
|
||
file = FILE_cat(dir, ".project", NULL);*/
|
||
}
|
||
|
||
fd = fopen(file, "r");
|
||
/* Silently ignored */
|
||
if (!fd)
|
||
return;
|
||
|
||
if (EXEC_arch)
|
||
fseek(fd, 32 + sizeof(ARCH_HEADER), SEEK_SET);
|
||
|
||
read_line(fd, dir, MAX_PATH);
|
||
|
||
env = COMMON_buffer;
|
||
|
||
//if (!start_with(dir, PROJECT_MAGIC))
|
||
//{
|
||
// We suppose that we have a .startup file.
|
||
startup = TRUE;
|
||
while (*dir)
|
||
read_line(fd, dir, MAX_PATH);
|
||
|
||
for(;;)
|
||
{
|
||
if (read_line(fd, dir, MAX_PATH))
|
||
break;
|
||
if (!*dir)
|
||
break;
|
||
preload |= check_preload(dir, &env);
|
||
}
|
||
/*
|
||
}
|
||
else
|
||
{
|
||
stop = FALSE;
|
||
|
||
for(;;)
|
||
{
|
||
if (read_line(fd, dir, MAX_PATH))
|
||
break;
|
||
|
||
if ((p = start_with(dir, "library=")) || (p = start_with(dir, "component=")))
|
||
{
|
||
preload |= check_preload(p, &env);
|
||
stop = TRUE;
|
||
}
|
||
else if (stop)
|
||
break;
|
||
}
|
||
}
|
||
*/
|
||
|
||
fclose(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(GAMBAS_LINK_PATH, argv);
|
||
}
|
||
|
||
return;
|
||
|
||
_PANIC:
|
||
|
||
fprintf(stderr, "Cannot preload libraries: %s\n", err ? err : strerror(errno));
|
||
#endif
|
||
}
|
||
|
||
|
||
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;*/
|
||
|
||
#ifndef DONT_USE_LTDL
|
||
if (lt_dlinit())
|
||
ERROR_panic("Cannot initialize plug-in management: %s", lt_dlerror());
|
||
#endif
|
||
}
|
||
|
||
|
||
void LIBRARY_exit(void)
|
||
{
|
||
#ifndef DONT_USE_LTDL
|
||
lt_dlexit();
|
||
#endif
|
||
}
|
||
|
||
|
||
void LIBRARY_get_interface(LIBRARY *lib, int 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], "_%d", version);
|
||
|
||
copy_interface((intptr_t *)get_symbol(lib, symbol, TRUE), (intptr_t *)iface);
|
||
}
|
||
|
||
|
||
boolean LIBRARY_get_interface_by_name(const char *name, int version, void *iface)
|
||
{
|
||
COMPONENT *comp;
|
||
|
||
comp = COMPONENT_find(name);
|
||
if (!comp || !comp->library)
|
||
return TRUE;
|
||
|
||
LIBRARY_get_interface(comp->library, version, iface);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
LIBRARY *LIBRARY_create(const char *name)
|
||
{
|
||
LIBRARY *lib;
|
||
|
||
ALLOC_ZERO(&lib, sizeof(LIBRARY), "LIBRARY_create");
|
||
|
||
lib->handle = NULL;
|
||
lib->name = name;
|
||
|
||
/*if (name)
|
||
{
|
||
lib->persistent = FALSE;
|
||
lib->preload = FALSE;
|
||
}
|
||
else
|
||
{
|
||
lib->persistent = TRUE;
|
||
lib->preload = TRUE;
|
||
}*/
|
||
|
||
return lib;
|
||
}
|
||
|
||
|
||
void LIBRARY_delete(LIBRARY *lib)
|
||
{
|
||
LIBRARY_unload(lib);
|
||
FREE(&lib, "LIBRARY_delete");
|
||
}
|
||
|
||
|
||
void LIBRARY_load(LIBRARY *lib)
|
||
{
|
||
int (*func)();
|
||
GB_INTERFACE *iface;
|
||
GB_DESC **desc;
|
||
char *path;
|
||
|
||
#ifdef DEBUG
|
||
clock_t t = clock();
|
||
fprintf(stderr, "Loading library %s\n", lib->name);
|
||
#endif
|
||
|
||
if (lib->handle)
|
||
return;
|
||
|
||
path = FILE_buffer();
|
||
sprintf(path, LIB_PATTERN, COMPONENT_path, lib->name);
|
||
|
||
if (!FILE_exist(path))
|
||
sprintf(path, LIB_PATTERN, COMPONENT_user_path, lib->name);
|
||
|
||
#ifndef DONT_USE_LTDL
|
||
/* no more available in libltld ?
|
||
lt_dlopen_flag = RTLD_LAZY;
|
||
*/
|
||
lib->handle = lt_dlopenext(path);
|
||
#else
|
||
lib->handle = dlopen(path, RTLD_LAZY);
|
||
#endif
|
||
|
||
if (lib->handle == NULL)
|
||
THROW(E_LIBRARY, path, lt_dlerror());
|
||
|
||
func = get_symbol(lib, LIB_INIT, TRUE);
|
||
|
||
/* Interface de Gambas */
|
||
|
||
iface = get_symbol(lib, LIB_GAMBAS, TRUE);
|
||
copy_interface((intptr_t *)(void *)GAMBAS_Api, (intptr_t *)(void *)iface);
|
||
|
||
/* Signal function */
|
||
lib->signal = (void(*)())get_symbol(lib, LIB_SIGNAL, FALSE);
|
||
lib->info = (int(*)())get_symbol(lib, LIB_INFO, FALSE);
|
||
|
||
/* Initialisation */
|
||
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->name);
|
||
fprintf(stderr, "in %g s\n", ((double)(clock() - t) / CLOCKS_PER_SEC));
|
||
#endif
|
||
}
|
||
|
||
|
||
void LIBRARY_declare(GB_DESC **desc)
|
||
{
|
||
GB_DESC **p;
|
||
|
||
p = desc;
|
||
while (*p != NULL)
|
||
{
|
||
CLASS_find_global((*p)->name);
|
||
p++;
|
||
}
|
||
|
||
p = desc;
|
||
while (*p != NULL)
|
||
{
|
||
if (CLASS_register(*p) == NULL)
|
||
THROW(E_REGISTER, (*p)->name);
|
||
|
||
p++;
|
||
}
|
||
}
|
||
|
||
|
||
void LIBRARY_unload(LIBRARY *lib)
|
||
{
|
||
void (*gambas_exit)();
|
||
|
||
if (lib->handle == NULL)
|
||
return;
|
||
|
||
/* Pas de lib<69>ation des classes pr<70>harg<72> ! */
|
||
|
||
/* V<>ification qu'aucune classe de la librairie n'est instanci<63> ! */
|
||
|
||
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
|
||
}
|
||
|