Commit 81cd906b authored by Albert Gräf's avatar Albert Gräf
Browse files

Backport IOhannes' and Miller's loader fixes from vanilla. Fixes #266.

parent 5f809c02
......@@ -613,9 +613,6 @@ int pd_setloadingabstraction(t_symbol *sym);
doesn't know. Pd tries to load it as an extern, then as an abstraction. */
void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv)
{
t_pd *current;
int fd;
char dirbuf[FILENAME_MAX], *nameptr;
if (tryingalready) return;
newest = 0;
class_loadsym = s;
......@@ -627,25 +624,6 @@ void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv)
return;
}
class_loadsym = 0;
current = s__X.s_thing;
if ((fd = canvas_open(canvas_getcurrent(), s->s_name, ".pd",
dirbuf, &nameptr, FILENAME_MAX, 0)) >= 0 ||
(fd = canvas_open(canvas_getcurrent(), s->s_name, ".pat",
dirbuf, &nameptr, FILENAME_MAX, 0)) >= 0)
{
close (fd);
if (!pd_setloadingabstraction(s))
{
canvas_setargs(argc, argv);
binbuf_evalfile(gensym(nameptr), gensym(dirbuf));
canvas_initbang((t_canvas *)(s__X.s_thing));/* JMZ*/
if (s__X.s_thing != current)
canvas_popabstraction((t_canvas *)(s__X.s_thing));
canvas_setargs(0, 0);
}
else error("%s: can't load abstraction within itself\n", s->s_name);
}
else newest = 0;
}
t_symbol s_pointer = {"pointer", 0, 0};
......
......@@ -2,20 +2,20 @@
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
#ifdef HAVE_LIBDL
#if defined(HAVE_LIBDL) || defined(__FreeBSD__)
#include <dlfcn.h>
#endif
#ifdef UNISTD
#ifdef HAVE_UNISTD_H
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#endif
#ifdef MSW
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <mach-o/dyld.h>
#endif
#include <string.h>
#include "m_pd.h"
......@@ -24,6 +24,7 @@
#include <sys/stat.h>
#ifdef _MSC_VER /* This is only for Microsoft's compiler, not cygwin, e.g. */
#define snprintf sprintf_s
#define stat _stat
#endif
typedef void (*t_xxx)(void);
......@@ -36,9 +37,7 @@ objects. The specific name is the letter b, l, d, or m for BSD, linux,
darwin, or microsoft, followed by a more specific string, either "fat" for
a fat binary or an indication of the instruction set. */
#ifdef __FreeBSD__
static char sys_dllextent[] = ".b_i386", sys_dllextent2[] = ".pd_freebsd";
#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__)
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__) || defined(__FreeBSD__)
static char sys_dllextent2[] = ".pd_linux";
# ifdef __x86_64__
static char sys_dllextent[] = ".l_ia64"; // this should be .l_x86_64 or .l_amd64
......@@ -69,7 +68,7 @@ typedef struct _loadedlist
} t_loadlist;
static t_loadlist *sys_loaded;
int sys_onloadlist(char *classname) /* return true if already loaded */
int sys_onloadlist(const char *classname) /* return true if already loaded */
{
t_symbol *s = gensym(classname);
t_loadlist *ll;
......@@ -79,7 +78,8 @@ int sys_onloadlist(char *classname) /* return true if already loaded */
return (0);
}
void sys_putonloadlist(char *classname) /* add to list of loaded modules */
/* add to list of loaded modules */
void sys_putonloadlist(const char *classname)
{
t_loadlist *ll = (t_loadlist *)getbytes(sizeof(*ll));
ll->ll_name = gensym(classname);
......@@ -90,27 +90,31 @@ void sys_putonloadlist(char *classname) /* add to list of loaded modules */
void class_set_extern_dir(t_symbol *s);
static int sys_do_load_lib(t_canvas *canvas, char *objectname)
static int sys_do_load_abs(t_canvas *canvas, const char *objectname,
const char *path);
static int sys_do_load_lib(t_canvas *canvas, const char *objectname,
const char *path)
{
char symname[MAXPDSTRING], filename[FILENAME_MAX], dirbuf[FILENAME_MAX],
*classname, *nameptr;
char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING],
*nameptr, altsymname[MAXPDSTRING];
const char *classname, *cnameptr;
void *dlobj;
t_xxx makeout = NULL;
int i, hexmunge = 0, fd;
#ifdef MSW
#ifdef _WIN32
HINSTANCE ntdll;
#endif
if (classname = strrchr(objectname, '/'))
/* NULL-path is only used as a last resort,
but we have already tried all paths */
if(!path)return (0);
if ((classname = strrchr(objectname, '/')))
classname++;
else classname = objectname;
if (sys_onloadlist(objectname))
for (i = 0, cnameptr = classname; i < MAXPDSTRING-7 && *cnameptr;
cnameptr++)
{
post("%s: already loaded", objectname);
return (1);
}
for (i = 0, nameptr = classname; i < MAXPDSTRING-7 && *nameptr; nameptr++)
{
char c = *nameptr;
char c = *cnameptr;
if ((c>='0' && c<='9') || (c>='A' && c<='Z')||
(c>='a' && c<='z' )|| c == '_')
{
......@@ -118,7 +122,7 @@ static int sys_do_load_lib(t_canvas *canvas, char *objectname)
i++;
}
/* trailing tilde becomes "_tilde" */
else if (c == '~' && nameptr[1] == 0)
else if (c == '~' && cnameptr[1] == 0)
{
strcpy(symname+i, "_tilde");
i += strlen(symname+i);
......@@ -137,76 +141,112 @@ static int sys_do_load_lib(t_canvas *canvas, char *objectname)
strncpy(symname, "setup_", 6);
}
else strcat(symname, "_setup");
#if 0
fprintf(stderr, "lib: %s %s\n", classname, symname);
fprintf(stderr, "lib: %s\n", classname);
#endif
/* try looking in the path for (objectname).(sys_dllextent) ... */
if ((fd = canvas_open(canvas, objectname, sys_dllextent,
dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0)
if ((fd = sys_trytoopenone(path, objectname, sys_dllextent,
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)
goto gotone;
/* same, with the more generic sys_dllextent2 */
if ((fd = canvas_open(canvas, objectname, sys_dllextent2,
dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0)
if ((fd = sys_trytoopenone(path, objectname, sys_dllextent2,
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)
goto gotone;
/* next try (objectname)/(classname).(sys_dllextent) ... */
strncpy(filename, objectname, FILENAME_MAX);
strncpy(filename, objectname, MAXPDSTRING);
filename[MAXPDSTRING-2] = 0;
strcat(filename, "/");
strncat(filename, classname, FILENAME_MAX-strlen(filename));
filename[FILENAME_MAX-1] = 0;
if ((fd = canvas_open(canvas, filename, sys_dllextent,
dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0)
strncat(filename, classname, MAXPDSTRING-strlen(filename));
filename[MAXPDSTRING-1] = 0;
if ((fd = sys_trytoopenone(path, filename, sys_dllextent,
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)
goto gotone;
if ((fd = canvas_open(canvas, filename, sys_dllextent2,
dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0)
if ((fd = sys_trytoopenone(path, filename, sys_dllextent2,
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)
goto gotone;
#ifdef ANDROID
/* Android libs have a 'lib' prefix, '.so' suffix and don't allow ~ */
char libname[MAXPDSTRING] = "lib";
strncat(libname, objectname, MAXPDSTRING - 4);
int len = strlen(libname);
if (libname[len-1] == '~' && len < MAXPDSTRING - 6) {
strcpy(libname+len-1, "_tilde");
}
if ((fd = sys_trytoopenone(path, libname, ".so",
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)
goto gotone;
#endif
return (0);
gotone:
close(fd);
class_set_extern_dir(gensym(dirbuf));
/* rebuild the absolute pathname */
strncpy(filename, dirbuf, FILENAME_MAX);
filename[FILENAME_MAX-2] = 0;
strncpy(filename, dirbuf, MAXPDSTRING);
filename[MAXPDSTRING-2] = 0;
strcat(filename, "/");
strncat(filename, nameptr, FILENAME_MAX-strlen(filename));
filename[FILENAME_MAX-1] = 0;
strncat(filename, nameptr, MAXPDSTRING-strlen(filename));
filename[MAXPDSTRING-1] = 0;
#ifdef HAVE_LIBDL
#ifdef _WIN32
{
char dirname[MAXPDSTRING], *s, *basename;
sys_bashfilename(filename, filename);
/* set the dirname as DllDirectory, meaning in the path for
loading other DLLs so that dependent libraries can be included
in the same folder as the external. SetDllDirectory() needs a
minimum supported version of Windows XP SP1 for
SetDllDirectory, so WINVER must be 0x0502 */
strncpy(dirname, filename, MAXPDSTRING);
s = strrchr(dirname, '\\');
basename = s;
if (s && *s)
*s = '\0';
if (!SetDllDirectory(dirname))
error("Could not set '%s' as DllDirectory(), '%s' might not load.",
dirname, basename);
/* now load the DLL for the external */
ntdll = LoadLibrary(filename);
if (!ntdll)
{
verbose(1, "%s: couldn't load", filename);
class_set_extern_dir(&s_);
return (0);
}
makeout = (t_xxx)GetProcAddress(ntdll, symname);
if (!makeout)
makeout = (t_xxx)GetProcAddress(ntdll, "setup");
SetDllDirectory(NULL); /* reset DLL dir to nothing */
}
#elif defined(HAVE_LIBDL) || defined(__FreeBSD__)
dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (!dlobj)
{
post("%s: %s", filename, dlerror());
verbose(1, "%s: %s", filename, dlerror());
class_set_extern_dir(&s_);
return (0);
}
makeout = (t_xxx)dlsym(dlobj, symname);
#endif
#ifdef MSW
sys_bashfilename(filename, filename);
ntdll = LoadLibrary(filename);
if (!ntdll)
{
post("%s: couldn't load", filename);
class_set_extern_dir(&s_);
return (0);
}
makeout = (t_xxx)GetProcAddress(ntdll, symname);
if(!makeout)
makeout = (t_xxx)dlsym(dlobj, "setup");
#else
#warning "No dynamic loading mechanism specified, \
libdl or WIN32 required for loading externals!"
#endif
if (!makeout)
{
post("load_object: Symbol \"%s\" not found", symname);
verbose(1, "load_object: Symbol \"%s\" not found", symname);
class_set_extern_dir(&s_);
return 0;
}
(*makeout)();
class_set_extern_dir(&s_);
sys_putonloadlist(objectname);
return (1);
}
/* linked list of loaders */
typedef struct loader_queue {
loader_t loader;
......@@ -221,7 +261,9 @@ void sys_register_loader(loader_t loader)
loader_queue_t *q = &loaders;
while (1)
{
if (q->next)
if (q->loader == loader) /* already loaded - nothing to do */
return;
else if (q->next)
q = q->next;
else
{
......@@ -230,18 +272,78 @@ void sys_register_loader(loader_t loader)
q->next->next = NULL;
break;
}
}
}
}
int sys_load_lib(t_canvas *canvas, char *classname)
#include "g_canvas.h"
/* the data passed to the iter-function */
struct _loadlib_data
{
t_canvas *canvas;
const char *classname;
int ok;
};
int sys_loadlib_iter(const char *path, struct _loadlib_data *data)
{
int dspstate = canvas_suspend_dsp();
int ok = 0;
loader_queue_t *q;
for(q = &loaders; q; q = q->next)
if (ok = q->loader(canvas, classname)) break;
if ((ok = q->loader(data->canvas, data->classname, path)))
break;
/* if all loaders failed, try to load as abstraction */
if (!ok)
ok = sys_do_load_abs(data->canvas, data->classname, path);
data->ok = ok;
return (ok == 0);
}
int sys_load_lib(t_canvas *canvas, const char *classname)
{
int dspstate = canvas_suspend_dsp();
struct _loadlib_data data;
data.canvas = canvas;
data.ok = 0;
if (sys_onloadlist(classname))
{
verbose(1, "%s: already loaded", classname);
return (1);
}
/* if classname is absolute, try this first */
if (sys_isabsolutepath(classname))
{
/* this is just copied from sys_open_absolute()
LATER avoid code duplication */
char dirbuf[MAXPDSTRING], *z = strrchr(classname, '/');
int dirlen;
if (!z)
return (0);
dirlen = z - classname;
if (dirlen > MAXPDSTRING-1)
dirlen = MAXPDSTRING-1;
strncpy(dirbuf, classname, dirlen);
dirbuf[dirlen] = 0;
data.classname=classname+(dirlen+1);
sys_loadlib_iter(dirbuf, &data);
}
data.classname = classname;
if(!data.ok)
canvas_path_iterate(canvas, (t_canvas_path_iterator)sys_loadlib_iter,
&data);
/* if loaders failed so far, we try a last time without a PATH
* let the loaders search wherever they want */
if (!data.ok)
sys_loadlib_iter(0, &data);
if(data.ok)
sys_putonloadlist(classname);
canvas_resume_dsp(dspstate);
return ok;
return data.ok;
}
int sys_run_scheduler(const char *externalschedlibname,
......@@ -250,41 +352,142 @@ int sys_run_scheduler(const char *externalschedlibname,
typedef int (*t_externalschedlibmain)(const char *);
t_externalschedlibmain externalmainfunc;
char filename[MAXPDSTRING];
struct stat statbuf;
snprintf(filename, sizeof(filename), "%s%s", externalschedlibname,
sys_dllextent);
sys_bashfilename(filename, filename);
#ifdef MSW
/* if first-choice file extent can't 'stat', go for second */
if (stat(filename, &statbuf) < 0)
{
snprintf(filename, sizeof(filename), "%s%s", externalschedlibname,
sys_dllextent2);
sys_bashfilename(filename, filename);
}
#ifdef _WIN32
{
HINSTANCE ntdll = LoadLibrary(filename);
if (!ntdll)
{
post("%s: couldn't load external scheduler lib ", filename);
fprintf(stderr, "%s: couldn't load external scheduler\n", filename);
error("%s: couldn't load external scheduler", filename);
return (1);
}
externalmainfunc =
(t_externalschedlibmain)GetProcAddress(ntdll, "main");
(t_externalschedlibmain)GetProcAddress(ntdll, "pd_extern_sched");
if (!externalmainfunc)
externalmainfunc =
(t_externalschedlibmain)GetProcAddress(ntdll, "main");
}
#else
#elif defined HAVE_LIBDL
{
void *dlobj;
struct stat statbuf;
/* if first-choice file extent can't 'stat', go for second */
if (stat(filename, &statbuf) < 0)
{
snprintf(filename, sizeof(filename), "%s%s", externalschedlibname,
sys_dllextent2);
sys_bashfilename(filename, filename);
}
dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (!dlobj)
{
post("%s: %s", filename, dlerror());
error("%s: %s", filename, dlerror());
fprintf(stderr, "dlopen failed for %s: %s\n", filename, dlerror());
return (1);
}
externalmainfunc = (t_externalschedlibmain)dlsym(dlobj,
"pd_extern_sched");
}
#else
return (0);
#endif
return((*externalmainfunc)(sys_extraflagsstring));
if (externalmainfunc)
return((*externalmainfunc)(sys_extraflagsstring));
else
{
fprintf(stderr, "%s: couldn't find pd_extern_sched() or main()\n",
filename);
return (0);
}
}
/* abstraction loading */
void canvas_popabstraction(t_canvas *x);
int pd_setloadingabstraction(t_symbol *sym);
extern t_pd *newest;
static t_pd *do_create_abstraction(t_symbol*s, int argc, t_atom *argv)
{
/*
* TODO: check if the there is a binbuf cached for <canvas::symbol>
and use that instead. We'll have to invalidate the cache once we
are done (either with a clock_delay(0) or something else)
*/
if (!pd_setloadingabstraction(s))
{
const char *objectname = s->s_name;
char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr;
t_glist *glist = (t_glist *)canvas_getcurrent();
t_canvas *canvas = (t_canvas*)glist_getcanvas(glist);
int fd = -1;
t_pd *was = s__X.s_thing;
snprintf(classslashclass, MAXPDSTRING, "%s/%s", objectname, objectname);
if ((fd = canvas_open(canvas, objectname, ".pd",
dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 ||
(fd = canvas_open(canvas, objectname, ".pat",
dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 ||
(fd = canvas_open(canvas, classslashclass, ".pd",
dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0)
{
close(fd);
canvas_setargs(argc, argv);
binbuf_evalfile(gensym(nameptr), gensym(dirbuf));
if (s__X.s_thing && was != s__X.s_thing)
canvas_popabstraction((t_canvas *)(s__X.s_thing));
else s__X.s_thing = was;
canvas_setargs(0, 0);
return (newest);
}
/* otherwise we couldn't do it; just return 0 */
}
else error("%s: can't load abstraction within itself\n", s->s_name);
newest = 0;
return (0);
}
/* search for abstraction; register a creator if found */
static int sys_do_load_abs(t_canvas *canvas, const char *objectname,
const char *path)
{
int fd;
static t_gobj*abstraction_classes = 0;
char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr;
/* NULL-path is only used as a last resort,
but we have already tried all paths */
if (!path) return (0);
snprintf(classslashclass, MAXPDSTRING, "%s/%s", objectname, objectname);
if ((fd = sys_trytoopenone(path, objectname, ".pd",
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 ||
(fd = sys_trytoopenone(path, objectname, ".pat",
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 ||
(fd = sys_trytoopenone(path, classslashclass, ".pd",
dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)
{
t_class*c=0;
close(fd);
/* found an abstraction, now register it as a new pseudo-class */
class_set_extern_dir(gensym(dirbuf));
if((c=class_new(gensym(objectname),
(t_newmethod)do_create_abstraction, 0,
0, 0, A_GIMME, 0)))
{
/* store away the newly created class, maybe we will need it one day */
t_gobj*absclass=0;
absclass=t_getbytes(sizeof(*absclass));
absclass->g_pd=c;
absclass->g_next=abstraction_classes;
abstraction_classes=absclass;
}
class_set_extern_dir(&s_);
return (1);
}
return (0);
}
......@@ -71,8 +71,8 @@ extern t_symbol *sys_libdir; /* library directory for auxilliary files */
extern t_symbol *sys_guidir; /* directory holding pd_gui, u_pdsend, etc */
/* s_loader.c */
typedef int (*loader_t)(t_canvas *canvas, char *classname); /* callback type */
EXTERN int sys_load_lib(t_canvas *canvas, char *filename);
typedef int (*loader_t)(t_canvas *canvas, const char *classname, const char *path); /* callback type */
EXTERN int sys_load_lib(t_canvas *canvas, const char *classname);
EXTERN void sys_register_loader(loader_t loader);
/* s_audio.c */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment