From bc88edeb5009472edc01a448c80e11292c035558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 19 Apr 2007 13:31:19 +0000 Subject: hexloader initial version svn path=/trunk/externals/loaders/hexloader/; revision=7582 --- Makefile | 153 ++++++++++++++++++ hexloader.c | 508 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 661 insertions(+) create mode 100644 Makefile create mode 100644 hexloader.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a3d66a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,153 @@ +# Makefile +# (c) 2007 IOhannes m zmölnig + +# path to pd +## change this according to your setup! +PDROOT=../../../pd + +# here we find the sources of pd (and evtl. the pd.lib) +PDSRCDIR=$(PDROOT)/src +PDLIBDIR=$(PDROOT)/bin + +# this is the filename-extension +# people have to specify it at the cmdline: eg "make pd_linux" +EXTENSION=$(MAKECMDGOALS) + +# if no filename-extension is supplied by the user +# try to guess one, based on what "uname" tells us +UNAME := $(shell uname -s) +ifeq ($(UNAME),Linux) + DEFAULTEXTENSION= pd_linux +else + ifeq ($(UNAME),Darwin) + DEFAULTEXTENSION= pd_darwin + else + ifeq (MINGW,$(findstring MINGW,$(UNAME))) + DEFAULTEXTENSION= pd_nt + else + ifeq ($(UNAME),IRIX) + UNAMEV := $(shell uname -R) + ifeq (6.,$(findstring 6.,$(UNAMEV))) + DEFAULTEXTENSION= pd_irix6 + else + DEFAULTEXTENSION= pd_irix5 + endif + else + DEFAULTEXTENSION=help + endif + endif + endif +endif + +# if no extension is given, call "make" again with a guessed extension +auto: + make $(DEFAULTEXTENSION) + +# just a stupid fallback +help: + @echo "choose one command: make pd_linux (linux), make pd_darwin (osX), make pd_irix5 (IRIX5), make pd_irix6 (IRIX6), make dll (MSVC), make pd_nt (MinWG)" + +# delete old build files +clean: + -rm -f *.dll *.pd_* *.o *.obj *~ + +# we want to compile all C-files we find in the current directory +SOURCES=$(sort $(filter %.c, $(wildcard *.c))) +# each C-files maps will become an external with the given filename-extension +TARGETS=$(SOURCES:.c=.$(EXTENSION)) + + +# ----------------------- Linux ----------------------- + +pd_linux: $(TARGETS) + +LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -fPIC \ + -Wall -W -Wshadow -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch + +LINUXLDFLAGS = -export_dynamic -shared -lc -lm + +LINUXINCLUDE = -I$(PDSRCDIR) + +%.pd_linux: %.c + $(CC) $(LINUXLDFLAGS) $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.pd_linux $*.c + strip --strip-unneeded $*.pd_linux + + + +# ----------------------- Mac OSX ----------------------- + +pd_darwin: $(TARGETS) + +DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \ + -Wno-unused -Wno-parentheses -Wno-switch + +DARWININCLUDE = -I$(PDSRCDIR) + +DARWINLDFLAGS = -bundle -undefined suppress -flat_namespace + +%.pd_darwin: %.c + $(CC) $(DARWINCFLAGS) $(DARWININCLUDE) $(DARWINLDFLAGS) -o $*.pd_darwin $*.c + + +# ----------------------- IRIX 5.x ----------------------- +pd_irix5: $(TARGETS) + +SGICFLAGS5 = -o32 -DPD -DSGI -O2 + +SGIINCLUDE = -I$(PDSRCDIR) + +SGILDFLAGS = -elf -shared -rdata_shared + +%.pd_irix5: %.c + $(CC) $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c + $(LD) $(SGILDFLAGS) -o $*.pd_irix5 $*.o + rm $*.o + + +# ----------------------- IRIX 6.x ----------------------- +pd_irix6: $(TARGETS) + +SGICFLAGS6 = -DPD -DSGI -n32 \ + -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \ + -Ofast=ip32 + +%.pd_irix6: %.c + $(CC) $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c + $(LD) $(SGILDFLAGS) -o $*.pd_irix6 $*.o + rm $*.o + + +# ----------------------- NT ----------------------- +dll: $(TARGETS) + +PDNTCFLAGS = /W3 /WX /DPD /DNT /D__WIN32__ /DMSW /nologo + +VC="C:\Programme\Microsoft Visual Studio\Vc98" + +PDNTINCLUDE = /I. /I$(PDROOT)\tcl\include /I$(PDSRCDIR)\src /I$(VC)\include + +PDNTLDIR = $(VC)\lib + +PDNTLIB = $(PDNTLDIR)\libc.lib \ + $(PDNTLDIR)\oldnames.lib \ + $(PDNTLDIR)\kernel32.lib \ + $(PDLIBDIR)\pd.lib + +%.dll: %.c + cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c + link /dll /export:$*_setup $*.obj $(PDNTLIB) + + +pd_nt: $(TARGETS) + +MINGWCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \ + -Wall -W -Wshadow -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch -mms-bitfields + +MINGWLDFLAGS = -export_dynamic -shared -lm -lkernel32 -lcoldname -lcrtdll -lpd -L$(PDLIBDIR) + +MINGWINCLUDE = -I$(PDSRCDIR) + +%.pd_nt: %.c + $(CC) $(MINGWLDFLAGS) $(MINGWCFLAGS) $(MINGWINCLUDE) -o $*.dll $*.c diff --git a/hexloader.c b/hexloader.c new file mode 100644 index 0000000..a027e15 --- /dev/null +++ b/hexloader.c @@ -0,0 +1,508 @@ +#ifdef __WIN32__ +# define MSW +#endif + +#define DL_OPEN + +#include "m_pd.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include +#include +#include + +#ifdef DL_OPEN +# include +#endif +#ifdef UNISTD +# include +# include +#endif +#ifdef __WIN32__ +# include +# include +#endif +#ifdef __APPLE__ +# include +#endif + + +typedef void (*t_hexloader_setup)(void); + +/* definitions taken from s_loader.c */ +typedef int (*loader_t)(t_canvas *canvas, char *classname); +void sys_register_loader(loader_t loader); +void class_set_extern_dir(t_symbol *s); + +/* ==================================================== */ + +typedef struct _hexloader +{ + t_object x_obj; +} t_hexloader; +static t_class *hexloader_class; + +static char *version = "$Revision: 1.1 $"; + + +static char*hex_dllextent[] = { +#ifdef __FreeBSD__ + ".b_i386", ".pd_freebsd", +#endif +#ifdef __linux__ +# ifdef __x86_64__ + ".l_ia64", +# else + ".l_i386", +# endif + ".pd_linux", +#endif /* linux */ +#ifdef __APPLE__ +# ifndef MACOSX3 + ".d_fat", +# else + ".d_ppc", +# endif + ".pd_darwin", +#endif +#ifdef __WIN32__ + ".m_i386", ".dll", +#endif + 0}; /* zero-terminated list of extensions */ + + + +static char *patch_extent[]={ + ".pd", + ".pat", + 0}; + + +/** + * object-names containing non-alphanumerics like [||~] + * can (sometimes) be not represented on filesystems (e.g. + * "|" is a forbidden character) and more often they + * cannot be used to construct a valid setup-function + * ("||~_setup" or "||_tilde_setup" are really bad). + * the way the "~" is handled, is non-generic and thus + * sub-optimal. + * + * as a solution me and hcs proposed an encoding into + * alphanumeric-values, using a hexadecimal representation + * of all characters but [0-9A-Za-z_] (e.g. "+" is ascii + * 43 and is thus represented by "0x2b" (hex-value all + * lowercase and prepended with "0x") + * + * e.g. if we have a new class "mtx_||", pd first attempts + * to find a file called "mtx_||.dll". if it succeeds, it + * will try to call the "mtx_||_setup()" function. + * if that fails we suggest to try and call a function + * "setup_mtx_0x7c0x7c()" (the keyword setup is now at the + * beginning of the function-name, in order to prevent the + * names starting with numbers and in order to distinguish + * between the normal setup-methods). + * if no "mtx_||.dll" can be found, pd should then search + * for a file "mtx_0x7c0x7c.dll" (guaranteed to be + * representable on any filesystem); search this file for + * the 2 setup-functions. + * if all fails try to find "mtx_||.pd" and then + * "mtx_0x7c0x7c.pd" + */ + +/* + * ideally this loader could somehow call the object-instantiator recursively + * but with changed classnames; + * this would allow us to use the hexloader stuff for all kinds of other loaders + * including abstractions + */ + + +/** + * replace everything but [a-zA-Z0-9_] by "0x%x" + * @return the normalized version of org + */ +static char*hexloader_normalize(char*org) +{ + char*orgname=org; + char altname[MAXPDSTRING]; + t_symbol*s=0; + + int count=0; + int i=0; + + for(i=0; i=48 && c<=57)|| /* [0-9] */ + (c>=65 && c<=90)|| /* [A-Z] */ + (c>=97 && c<=122)||/* [a-z] */ + (c==95)) /* [_] */ + { + altname[i]=c; + i++; + } + else /* a "bad" character */ + { + sprintf(altname+i, "0x%02x", c); + i+=4; + count++; + } + orgname++; + } + + s=gensym(altname); + return s->s_name; +} + +/** + * replace only / \ : * ? " < > | by 0x%x since these are forbidden on some filesystems + * @return the normalized version of org + */ +static char*hexloader_fsnormalize(char*org) +{ + char*orgname=org; + char altname[MAXPDSTRING]; + t_symbol*s=0; + + char forbiddenchars[]={ + '/', '\\', ':', '*', '?', '"', '<', '>', '|', + 0}; + + int count=0; + int i=0; + + for(i=0; is_name; +} + + +/** + * replace everything but [a-zA-Z0-9_] by "0x%x" + * @return a 0-terminated array of all versions we consider to be names + */ + +/* linked list of loaders */ +typedef struct namelist_ { + char* name; + struct namelist_ *next; +} namelist_t; + +static namelist_t*namelist_add(namelist_t*names, char*name) { + namelist_t*dummy=names; + if(name==0)return names; + + if(!dummy) { + dummy=(namelist_t*)getbytes(sizeof(namelist_t)); + dummy->next=0; + dummy->name=name; + return dummy; + } + + while(dummy->next) { + if (!strncmp(name, dummy->name, MAXPDSTRING)) { + // we already have this entry! + return names; + } + + dummy=(dummy->next); + } + + dummy->next=(namelist_t*)getbytes(sizeof(namelist_t)); + dummy=dummy->next; + dummy->next=0; + dummy->name=name; + + return names; +} + +static void namelist_clear(namelist_t*names) { + namelist_t*dummy=0; + + while(names) { + dummy=names->next; + names->next=0; + names->name=0; /* we dont care since the names are allocated in the symboltable anyhow */ + freebytes(names, sizeof(namelist_t)); + names=dummy; + } +} + +static namelist_t*hexloader_getalternatives(char*org) { + namelist_t*names=0; + names=namelist_add(names, org); + names=namelist_add(names, hexloader_normalize(org)); + names=namelist_add(names, hexloader_fsnormalize(org)); + +#if 0 + { + namelist_t*dummy=names; + while(dummy) { + post("alternatives=%s",dummy->name); + dummy=dummy->next; + } + } +#endif + + return names; +} + + +static int hexloader_doload(char*filename, char*setupfun) { +#ifdef __WIN32__ + HINSTANCE ntdll; +#endif + t_hexloader_setup makeout=0; + +#ifdef DL_OPEN + void *dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + if (!dlobj) + { + verbose(2, "%s: %s", filename, dlerror()); + class_set_extern_dir(&s_); + return (0); + } + makeout = (t_hexloader_setup)dlsym(dlobj, setupfun); +#endif +#ifdef __WIN32__ + sys_bashfilename(filename, filename); + ntdll = LoadLibrary(filename); + if (!ntdll) + { + verbose(2, "%s: couldn't load", filename); + class_set_extern_dir(&s_); + return (0); + } + makeout = (t_hexloader_setup)GetProcAddress(ntdll, setupfun); +#endif + + if (!makeout) + { + verbose(2, "hexload object: Symbol \"%s\" not found", setupfun); + class_set_extern_dir(&s_); + return 0; + } + (*makeout)(); + return (1); +} + +/** + * open a file (given via pathname+filename) as dll and call various + * setupfunctions (as can be calculated from the altnames array + * @param pathname the path of the file + * @param filename the name (without path) of the file + * @param altnames a zero-terminated array of possible non-generic parts of the setup-function + * @return 1 on success, 0 otherwise + */ +static int hexloader_loadfile(char*pathname, char*filename, namelist_t*altnames) +{ + char setupfun[MAXPDSTRING]; + char fullfile[MAXPDSTRING]; + namelist_t*altname=altnames; + + sprintf(fullfile, "%s/%s", pathname, filename); + + while(altname) { + sprintf(setupfun, "%s_setup", altname->name); + if(hexloader_doload(fullfile, setupfun)) + return 1; + + sprintf(setupfun, "setup_%s", altname->name); + if(hexloader_doload(fullfile, setupfun)) + return 1; + + altname=altname->next; + } + + return 0; +} + +/** + * try to open a file (given via pathname+filename) as a patcher + * TODO: make this work.... + * @param pathname the path of the file + * @param filename the name (without path) of the file + * @param altclassname the alternative classname we currently use... + * @return 1 on success, 0 otherwise + */ +t_pd pd_objectmaker; /* factory for creating "object" boxes */ +static int hexloader_loadpatch(char*pathname, char*filename, char*altclassname) +{ + char fullfile[MAXPDSTRING]; + sprintf(fullfile, "%s/%s", pathname, filename); + +#if 0 + { + t_symbol*s=gensym(altclassname); + if(!pd_objectmaker) { + post("BUG: no pd_objectmaker found"); + return 0; + } + post("hexloader: typedmess %s", s->s_name); + new_anything((void*)pd_objectmaker, s, 0, 0); + return 1; + } +#endif + + post("BUG: hexloader not loading patch: %s", fullfile); + return 0; +} +/** + * the actual loader: + * example-nomenclature: + * "class": the original class-name (e.g. containing weird characters) + * "CLASS": the normalized class-name (with all weirdness replaced by hex-representations + * "ext" : the external-extension (e.g. "dll" or "pd_linux" or...) + * example: + * trying to create an object [class] (and pd fails for whatever reasons, and thus callsus) + * - search for a file "class.ext" in our search-path + * + if found + * try to call "class_setup()" function + * if fails, try to call "setup_CLASS()" function + * (if fails, try to call "CLASS_setup()" function) + * - "class.ext" file not found + * - search for a file "CLASS.ext" in our search-path + * + if found + * try to call "class_setup()" function + * if fails, try to call "setup_CLASS()" function + * (if fails, try to call "CLASS_setup()" function) + * - if everything fails, return... + * + * @param canvas the context of the object to be created + * @param classname the name of the object (external, library) to be created + * @return 1 on success, 0 on failure + */ +static int hexloader_doloader(t_canvas *canvas, namelist_t*altnames0) +{ + int fd = -1; + char dirbuf[MAXPDSTRING]; + char*nameptr; + namelist_t*altnames=altnames0; + + /* try binaries */ + while(altnames) { + char*altname=altnames->name; + int dll_index=0; + char*dllextent=hex_dllextent[dll_index]; + + while(dllextent!=0) { + if ((fd = open_via_path(".", altname, dllextent, dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0) { + close (fd); + if(hexloader_loadfile(dirbuf, nameptr, altnames0)) { + return 1; + } + } + dll_index++; + dllextent=hex_dllextent[dll_index]; + } + + altnames=altnames->next; + } + + /* try patches */ + altnames=altnames0; + while(altnames) { + char*altname=altnames->name; + int extindex=0; + char*extent=patch_extent[extindex]; + while(extent!=0) { + if ((fd = open_via_path(".", altname, extent, dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0) { + close (fd); + if(hexloader_loadpatch(dirbuf, nameptr, altname)) { + return 1; + } + } + + extindex++; + extent=patch_extent[extindex]; + } + altnames=altnames->next; + } + return 0; +} + +/** + * the loader + * + * @param canvas the context of the object to be created + * @param classname the name of the object (external, library) to be created + * @return 1 on success, 0 on failure + */ +static int hexloader_loader(t_canvas *canvas, char *classname) +{ + namelist_t*altnames=0; + int result=0; + + static int already_loading=0; + if(already_loading)return 0; + already_loading=1; + + /* get alternatives */ + altnames=hexloader_getalternatives(classname); + + /* do the loading */ + result=hexloader_doloader(canvas, altnames); + + /* clean up */ + namelist_clear(altnames); + + already_loading=0; + return result; +} + + + +static void*hexloader_new(void) +{ + t_hexloader*x = (t_hexloader*)pd_new(hexloader_class); + return x; +} + +void hexloader_setup(void) +{ + /* relies on t.grill's loader functionality, fully added in 0.40 */ + post("hex loader %s",version); + post("\twritten by IOhannes m zmölnig, IEM "); + post("\tcompiled on "__DATE__" at "__TIME__ " "); + post("\tcompiled against Pd version %d.%d.%d.%s", PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); + +#if (PD_MINOR_VERSION >= 40) + sys_register_loader(hexloader_loader); +#else + error("to function, this needs to be compiled against Pd 0.40 or higher,\n"); + error("\tor a version that has sys_register_loader()"); +#endif + + hexloader_class = class_new(gensym("hexloader"), (t_newmethod)hexloader_new, 0, sizeof(t_hexloader), CLASS_NOINLET, 0); +} -- cgit v1.2.1