diff --git a/pd/src/m_class.c b/pd/src/m_class.c index 3db693bbb80cc29615b8cb4cc54dc2b9774c337d..85ca523491fa773cf446c07667331079063f4c08 100644 --- a/pd/src/m_class.c +++ b/pd/src/m_class.c @@ -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}; diff --git a/pd/src/s_loader.c b/pd/src/s_loader.c index 23140ccd94a80c13b4bbfe66c1fb1fe5a3fb890f..fe1304c63155578b5f1be4f2ee34de2b454be482 100644 --- a/pd/src/s_loader.c +++ b/pd/src/s_loader.c @@ -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); } diff --git a/pd/src/s_stuff.h.in b/pd/src/s_stuff.h.in index a5b72e89aaa8b8ac2ff2cbdb43df9dc0655d9a0b..0fb85a58a9024504c115fb3d5655f23cad1b2890 100644 --- a/pd/src/s_stuff.h.in +++ b/pd/src/s_stuff.h.in @@ -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 */