aboutsummaryrefslogtreecommitdiff
path: root/desiredata/src/kernel.c
diff options
context:
space:
mode:
Diffstat (limited to 'desiredata/src/kernel.c')
-rw-r--r--desiredata/src/kernel.c2348
1 files changed, 2348 insertions, 0 deletions
diff --git a/desiredata/src/kernel.c b/desiredata/src/kernel.c
new file mode 100644
index 00000000..8552c279
--- /dev/null
+++ b/desiredata/src/kernel.c
@@ -0,0 +1,2348 @@
+/* $Id: kernel.c,v 1.1.2.92 2007-09-09 21:34:56 matju Exp $
+ * Copyright 2006-2007 Mathieu Bouchard.
+ * Copyright (c) 1997-2006 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* IOhannes :
+ * changed the canvas_restore in "g_canvas.c", so that it might accept $args as well (like "pd $0_test")
+ * so you can make multiple & distinguishable templates
+ * 1511:forum::für::umläute:2001
+ * change marked with IOhannes
+ */
+
+#define PD_PLUSPLUS_FACE
+#include "desire.h"
+#include "m_simd.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sstream>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pthread.h>
+#ifdef UNISTD
+#include <unistd.h>
+#endif
+#ifdef MSW
+#include <io.h>
+#endif
+
+#define a_float a_w.w_float
+#define a_symbol a_w.w_symbol
+#define a_gpointer a_w.w_gpointer
+#define a_index a_w.w_index
+
+using namespace std;
+
+/* T.Grill - bit alignment for signal vectors (must be a multiple of 8!) */
+/* if undefined no alignment occurs */
+#ifdef SIMD_BYTEALIGN
+ #define VECTORALIGNMENT (SIMD_BYTEALIGN*8)
+#else
+ #define VECTORALIGNMENT 128
+#endif
+
+void *getbytes(size_t nbytes) {
+ if (nbytes < 1) nbytes = 1;
+ void *ret = (void *)calloc(nbytes, 1);
+ if (!ret) error("pd: getbytes() failed -- out of memory");
+ return ret;
+}
+
+void *copybytes(void *src, size_t nbytes) {
+ void *ret = getbytes(nbytes);
+ if (nbytes) memcpy(ret, src, nbytes);
+ return ret;
+}
+
+void *resizebytes(void *old, size_t oldsize, size_t newsize) {
+ if (newsize < 1) newsize = 1;
+ if (oldsize < 1) oldsize = 1;
+ void *ret = (void *)realloc((char *)old, newsize);
+ if (newsize > oldsize && ret) memset(((char *)ret) + oldsize, 0, newsize - oldsize);
+ if (!ret) error("pd: resizebytes() failed -- out of memory");
+ return ret;
+}
+
+void freebytes(void *old, size_t nbytes) {free(old);}
+
+/* in the following size_t is assumed to have the same size as a pointer type !!! */
+
+/* T.Grill - get aligned memory */
+void *getalignedbytes(size_t nbytes) {
+ /* to align the region we also need some extra memory to save the original pointer location
+ it is saved immediately before the aligned vector memory */
+ void *vec = getbytes(nbytes+(VECTORALIGNMENT/8-1)+sizeof(void *));
+ if (!vec) return 0;
+ t_int alignment = ((t_int)vec+sizeof(void *))&(VECTORALIGNMENT/8-1);
+ void *ret = (unsigned char *)vec+sizeof(void *)+(alignment == 0?0:VECTORALIGNMENT/8-alignment);
+ *(void **)((unsigned char *)ret-sizeof(void *)) = vec;
+ return ret;
+}
+
+/* T.Grill - free aligned vector memory */
+void freealignedbytes(void *ptr,size_t nbytes) {
+ free(*(void **)((unsigned char *)ptr-sizeof(void *)));
+}
+
+/* T.Grill - resize aligned vector memory */
+void *resizealignedbytes(void *ptr,size_t oldsize, size_t newsize) {
+ if (newsize<1) newsize=1;
+ void *ori = *(void **)((unsigned char *)ptr-sizeof(void *));
+ void *vec = realloc(ori,newsize+(VECTORALIGNMENT/8-1)+sizeof(void *));
+ t_int alignment = ((t_int)vec+sizeof(void *))&(VECTORALIGNMENT/8-1);
+ void *ret = (unsigned char *)vec+sizeof(void *)+(alignment == 0?0:VECTORALIGNMENT/8-alignment);
+ *(void **)((unsigned char *)ret-sizeof(void *)) = vec;
+ return ret;
+}
+
+/* TB: copy to aligned vector memory */
+void *copyalignedbytes(void *src, size_t nbytes) {
+ void *ret = getalignedbytes(nbytes);
+ if (nbytes) memcpy(ret, src, nbytes);
+ return ret;
+}
+
+t_class *hash_class;
+
+/*extern "C"*/ void hash_setup () {
+ hash_class = class_new(gensym("#V"), (t_newmethod)0 /*hash_new*/,
+ 0 /*(t_method)hash_free*/, sizeof(t_object), CLASS_PD, A_GIMME, 0);
+}
+
+/* convenience routines for checking and getting values of atoms.
+ There's no "pointer" version since there's nothing safe to return if there's an error. */
+
+t_float atom_getfloat( t_atom *a) {return a->a_type==A_FLOAT ? a->a_float : 0;}
+t_int atom_getint( t_atom *a) {return (t_int)atom_getfloat(a);}
+t_symbol * atom_getsymbol(t_atom *a) {return a->a_type==A_SYMBOL ? a->a_symbol : &s_symbol;}
+const char *atom_getstring(t_atom *a) {return atom_getsymbol(a)->name;}
+
+t_symbol *atom_gensym(t_atom *a) { /* this works better for graph labels */
+ if (a->a_type == A_SYMBOL) return a->a_symbol;
+ if (a->a_type == A_FLOAT) {char buf[30]; sprintf(buf, "%g", a->a_float); return gensym(buf);}
+ return gensym("???");
+}
+
+t_float atom_getfloatarg(int which, int argc, t_atom *argv) {
+ if (argc <= which) return 0;
+ argv += which;
+ return argv->a_type==A_FLOAT ? argv->a_float : 0;
+}
+
+t_int atom_getintarg(int which, int argc, t_atom *argv)
+{return (t_int)atom_getfloatarg(which, argc, argv);}
+
+t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv) {
+ if (argc <= which) return &s_;
+ argv += which;
+ return argv->a_type==A_SYMBOL ? argv->a_symbol : &s_;
+}
+
+const char *atom_getstringarg(int which, int argc, t_atom *argv) {
+ return atom_getsymbolarg(which,argc,argv)->name;
+}
+
+/* convert an atom into a string, in the reverse sense of binbuf_text (q.v.)
+ special attention is paid to symbols containing the special characters
+ ';', ',', '$', and '\'; these are quoted with a preceding '\', except that
+ the '$' only gets quoted at the beginning of the string. */
+
+//static int should_quote(char *s) {return strchr(";,\\{}\"",*s) || isspace(*s) || (*s=='$' && isdigit(s[1]));}
+static int should_quote(char *s) {return strchr(";,\\{}\" ",*s) || (*s=='$' && isdigit(s[1]));}
+
+void atom_ostream(t_atom *a, ostream &buf) {
+ switch(a->a_type) {
+ case A_SEMI: buf << ";"; break;
+ case A_COMMA: buf << ","; break;
+ case A_POINTER: buf << "(pointer)"; break;
+ case A_FLOAT: buf << a->a_float; break;
+ case A_SYMBOL: {
+ bool quote=0;
+ for (char *sp = a->a_symbol->name; *sp; sp++) if (should_quote(sp)) {quote = 1; break;}
+ if (quote) {
+ for (char *sp = a->a_symbol->name; *sp; sp++) {
+ if (should_quote(sp)) buf << '\\';
+ buf << *sp;
+ }
+ } else buf << a->a_symbol->name;
+ } break;
+ case A_DOLLAR: buf << "$" << a->a_index; break;
+ case A_DOLLSYM: buf << a->a_symbol->name; break;
+ default: bug("%s",__PRETTY_FUNCTION__);
+ }
+}
+
+/* this is not completely compatible with Miller's, as it won't do anything special for short bufsizes. */
+void atom_string(t_atom *a, char *buf, unsigned int bufsize) {
+ ostringstream b;
+ atom_ostream(a,b);
+ strncpy(buf,b.str().data(),bufsize);
+ buf[bufsize-1]=0;
+}
+
+void atom_init(t_atom *a, size_t n) {
+ for (size_t i=0; i<n; i++) {
+ a[i].a_type = A_FLOAT;
+ a[i].a_float = 0.0;
+ }
+}
+
+void atom_copy(t_atom *a, t_atom *b, size_t n) {
+ memcpy(a,b,n*sizeof(t_atom));
+ /* here I should handle incref */
+}
+
+void atom_delete(t_atom *a, size_t n) {
+ /* here I should handle decref */
+}
+
+/* in which the value has bit 0 set if the key object is not a zombie,
+ and has bit 1 set if the object has been uploaded to the client */
+t_hash<t_pd *,long> *object_table;
+
+t_pd *pd_new(t_class *c) {
+ if (!c) bug("pd_new: apparently called before setup routine");
+ t_pd *x = (t_pd *)getbytes(c->size);
+ x->_class = c;
+ object_table->set(x,1);
+ if (c->gobj) ((t_gobj *)x)->g_adix = appendix_new((t_gobj *)x);
+ if (c->patchable) {
+ ((t_object *)x)->inlet = 0;
+ ((t_object *)x)->outlet = 0;
+ }
+ return x;
+}
+
+void pd_free_zombie(t_pd *x) {
+ t_class *c = x->_class;
+ if (c->gobj) appendix_free((t_gobj *)x);
+ if (c->size) free(x);
+ object_table->del(x);
+}
+
+void pd_free(t_pd *x) {
+ t_class *c = x->_class;
+ if (c->freemethod) ((t_gotfn)(c->freemethod))(x);
+ if (c->patchable) {
+ t_object *y = (t_object *)x;
+ while (y->outlet) outlet_free(y->outlet);
+ while (y->inlet) inlet_free(y->inlet);
+ if (y->binbuf) binbuf_free(y->binbuf);
+ }
+ /* schedule for deletion if need to keep the allocation around */
+ if (c->gobj && (object_table->get(x)&2)) {
+ object_table->set(x,object_table->get(x)&~1);
+ gobj_changed((t_gobj *)x,"");
+ //char *xx = (char *)x; for (int i=0; i<c->size; i++) xx[i]="\xde\xad\xbe\xef"[i&3];
+ } else pd_free_zombie(x);
+}
+
+void gobj_save(t_gobj *x, t_binbuf *b) {
+ t_class *c = x->g_pd;
+ if (c->savefn) c->savefn(x, b);
+}
+
+/* deal with several objects bound to the same symbol. If more than one,
+ we actually bind a collection object to the symbol, which forwards messages sent to the symbol. */
+
+static t_class *bindlist_class;
+
+struct t_bindelem {
+ t_pd *who;
+ t_bindelem *next;
+};
+
+struct t_bindlist : t_pd {
+ t_bindelem *list;
+};
+
+#define bind_each(e,x) for (t_bindelem *e = x->list; e; e = e->next)
+static void bindlist_bang (t_bindlist *x) {bind_each(e,x) pd_bang(e->who);}
+static void bindlist_float (t_bindlist *x, t_float f) {bind_each(e,x) pd_float(e->who,f);}
+static void bindlist_symbol (t_bindlist *x, t_symbol *s) {bind_each(e,x) pd_symbol(e->who,s);}
+static void bindlist_pointer (t_bindlist *x, t_gpointer *gp) {bind_each(e,x) pd_pointer(e->who, gp);}
+static void bindlist_list (t_bindlist *x, t_symbol *s, int argc, t_atom *argv) {bind_each(e,x) pd_list(e->who, s,argc,argv);}
+static void bindlist_anything(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) {bind_each(e,x) pd_typedmess(e->who, s,argc,argv);}
+
+static t_bindelem *bindelem_new(t_pd *who, t_bindelem *next) {
+ t_bindelem *self = (t_bindelem *)malloc(sizeof(t_bindelem));
+ self->who = who;
+ self->next = next;
+ return self;
+}
+
+void pd_bind(t_pd *x, t_symbol *s) {
+ if (s->thing) {
+ if (s->thing->_class == bindlist_class) {
+ t_bindlist *b = (t_bindlist *)s->thing;
+ b->list = bindelem_new(x,b->list);
+ } else {
+ t_bindlist *b = (t_bindlist *)pd_new(bindlist_class);
+ b->list = bindelem_new(x,bindelem_new(s->thing,0));
+ s->thing = b;
+ }
+ } else s->thing = x;
+}
+
+/* bindlists always have at least two elements... if the number
+ goes down to one, get rid of the bindlist and bind the symbol
+ straight to the remaining element. */
+void pd_unbind(t_pd *x, t_symbol *s) {
+ if (s->thing == x) {s->thing = 0; return;}
+ if (s->thing && s->thing->_class == bindlist_class) {
+ t_bindlist *b = (t_bindlist *)s->thing;
+ t_bindelem *e, *e2;
+ if ((e = b->list)->who == x) {
+ b->list = e->next;
+ free(e);
+ } else for (e = b->list; (e2=e->next); e = e2) if (e2->who == x) {
+ e->next = e2->next;
+ free(e2);
+ break;
+ }
+ if (!b->list->next) {
+ s->thing = b->list->who;
+ free(b->list);
+ pd_free(b);
+ }
+ } else error("%s: couldn't unbind", s->name);
+}
+
+t_pd *pd_findbyclass(t_symbol *s, t_class *c) {
+ t_pd *x = 0;
+ if (!s->thing) return 0;
+ if (s->thing->_class == c) return s->thing;
+ if (s->thing->_class == bindlist_class) {
+ t_bindlist *b = (t_bindlist *)s->thing;
+ int warned = 0;
+ bind_each(e,b) if (e->who->_class == c) {
+ if (x && !warned) {post("warning: %s: multiply defined", s->name); warned = 1;}
+ x = e->who;
+ }
+ }
+ return x;
+}
+
+/* stack for maintaining bindings for the #X symbol during nestable loads. */
+
+#undef g_next
+
+struct t_gstack {
+ t_pd *what;
+ t_symbol *loading_abstr;
+ t_gstack *next;
+ long base_o_index;
+};
+
+static t_gstack *gstack_head = 0;
+static t_pd *lastpopped;
+static t_symbol *pd_loading_abstr;
+
+int pd_setloadingabstraction(t_symbol *sym) {
+ t_gstack *foo = gstack_head;
+ for (foo = gstack_head; foo; foo = foo->next) if (foo->loading_abstr == sym) return 1;
+ pd_loading_abstr = sym;
+ return 0;
+}
+
+int gstack_empty() {return !gstack_head;}
+
+long canvas_base_o_index() {
+ return gstack_head ? gstack_head->base_o_index : 0;
+}
+
+void pd_pushsym(t_pd *x) {
+ t_gstack *y = (t_gstack *)malloc(sizeof(*y));
+ y->what = s__X.thing;
+ y->next = gstack_head;
+ y->loading_abstr = pd_loading_abstr;
+ y->base_o_index = x->_class == canvas_class ? ((t_canvas *)x)->next_o_index : -666;
+ pd_loading_abstr = 0;
+ gstack_head = y;
+ s__X.thing = x;
+}
+
+void pd_popsym(t_pd *x) {
+ if (!gstack_head || s__X.thing != x) {bug("gstack_pop"); return;}
+ t_gstack *headwas = gstack_head;
+ s__X.thing = headwas->what;
+ gstack_head = headwas->next;
+ free(headwas);
+ lastpopped = x;
+}
+
+static void stackerror(t_pd *x) {error("stack overflow");}
+
+/* to enable multithreading, make those variables "thread-local". this means that they have to go in
+ a thread-specific place instead of plain global. do not ever use tim's atomic counters for this,
+ as they count all threads together as if they're one, and they're especially incompatible with
+ use of the desiredata-specific stack[] variable. */
+int pd_stackn = 0; /* how much of the stack is in use */
+t_call pd_stack[STACKSIZE];
+
+static inline uint64 rdtsc() {uint64 x; __asm__ volatile (".byte 0x0f, 0x31":"=A"(x)); return x;}
+
+//#define PROFILER
+#ifdef PROFILER
+#define ENTER_PROF uint64 t = rdtsc();
+#define LEAVE_PROF if (x->_class->gobj && ((t_gobj *)x)->dix) ((t_gobj *)x)->dix->elapsed += rdtsc() - t;
+#else
+#define ENTER_PROF
+#define LEAVE_PROF
+#endif
+
+#define ENTER(SELECTOR) if(pd_stackn >= STACKSIZE) {stackerror(x); return;} \
+ pd_stack[pd_stackn].self = x; pd_stack[pd_stackn].s = SELECTOR; pd_stackn++; ENTER_PROF
+#define LEAVE pd_stackn--; LEAVE_PROF
+
+/* matju's 2007.07.14 inlet-based stack check needs to be implemented in:
+ pd_bang pd_float pd_pointer pd_symbol pd_string pd_list pd_typedmess */
+void pd_bang(t_pd *x) {ENTER(&s_bang); x->_class->bangmethod(x); LEAVE;}
+void pd_float(t_pd *x, t_float f) {ENTER(&s_float); x->_class->floatmethod(x,f); LEAVE;}
+void pd_pointer(t_pd *x, t_gpointer *gp) {ENTER(&s_pointer); x->_class->pointermethod(x,gp); LEAVE;}
+void pd_symbol(t_pd *x, t_symbol *s) {ENTER(&s_symbol); x->_class->symbolmethod(x,s); LEAVE;}
+/* void pd_string(t_pd *x, const char *s){ENTER(&s_symbol); x->_class->stringmethod(x,s); LEAVE;} future use */
+void pd_list(t_pd *x, t_symbol *s, int ac, t_atom *av) {ENTER(s); x->_class->listmethod(x,&s_list,ac,av); LEAVE;}
+
+/* this file handles Max-style patchable objects, i.e., objects which
+can interconnect via inlets and outlets; also, the (terse) generic
+behavior for "gobjs" appears at the end of this file. */
+
+union inletunion {
+ t_symbol *symto;
+ t_gpointer *pointerslot;
+ t_float *floatslot;
+ t_symbol **symslot;
+ t_sample floatsignalvalue;
+};
+
+struct _inlet : t_pd {
+ struct _inlet *next;
+ t_object *owner;
+ t_pd *dest;
+ t_symbol *symfrom;
+ union inletunion u;
+ t_symbol* tip;
+};
+
+static t_class *inlet_class, *pointerinlet_class, *floatinlet_class, *symbolinlet_class;
+
+#define ISINLET(pd) ( \
+ pd->_class == inlet_class || \
+ pd->_class == pointerinlet_class || \
+ pd->_class == floatinlet_class || \
+ pd->_class == symbolinlet_class)
+
+/* --------------------- generic inlets ala max ------------------ */
+
+static void object_append_inlet(t_object *owner, t_inlet *x) {
+ t_inlet *y = owner->inlet, *y2;
+ if (y) {
+ while ((y2 = y->next)) y = y2;
+ y->next = x;
+ } else owner->inlet = x;
+}
+
+t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2) {
+ t_inlet *x = (t_inlet *)pd_new(inlet_class);
+ x->owner = owner;
+ x->dest = dest;
+ if (s1 == &s_signal) x->u.floatsignalvalue = 0; else x->u.symto = s2;
+ x->symfrom = s1;
+ x->next = 0;
+ x->tip = gensym("?");
+ object_append_inlet(owner,x);
+ return x;
+}
+
+t_inlet *signalinlet_new(t_object *owner, t_float f) {
+ t_inlet *x = inlet_new(owner, owner, &s_signal, &s_signal);
+ x->u.floatsignalvalue = f;
+ return x;
+}
+
+static void inlet_wrong(t_inlet *x, t_symbol *s) {
+ error("inlet: expected '%s' but got '%s'", x->symfrom->name, s->name);
+}
+
+void inlet_settip(t_inlet* i,t_symbol* s) {i->tip = s;}
+
+char* inlet_tip(t_inlet* i,int num) {
+ if (num < 0) return "???";
+ while (num-- && i) i = i->next;
+ if (i && i->tip) return i->tip->name;
+ return "?";
+}
+
+/* LATER figure out how to make these efficient: */
+static void inlet_bang(t_inlet *x) {
+ if (x->symfrom == &s_bang) pd_vmess(x->dest, x->u.symto, "");
+ else if (!x->symfrom) pd_bang(x->dest);
+ else inlet_wrong(x, &s_bang);
+}
+static void inlet_pointer(t_inlet *x, t_gpointer *gp) {
+ if (x->symfrom == &s_pointer) pd_vmess(x->dest, x->u.symto, "p", gp);
+ else if (!x->symfrom) pd_pointer(x->dest, gp);
+ else inlet_wrong(x, &s_pointer);
+}
+static void inlet_float(t_inlet *x, t_float f) {
+ if (x->symfrom == &s_float) pd_vmess(x->dest, x->u.symto, "f", (t_floatarg)f);
+ else if (x->symfrom == &s_signal) x->u.floatsignalvalue = f;
+ else if (!x->symfrom) pd_float(x->dest, f);
+ else inlet_wrong(x, &s_float);
+}
+
+static void inlet_symbol(t_inlet *x, t_symbol *s) {
+ if (x->symfrom == &s_symbol) pd_vmess(x->dest, x->u.symto, "s", s);
+ else if (!x->symfrom) pd_symbol(x->dest, s);
+ else inlet_wrong(x, &s_symbol);
+}
+
+static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv) {
+ if (x->symfrom == &s_list || x->symfrom == &s_float || x->symfrom == &s_symbol || x->symfrom == &s_pointer)
+ typedmess(x->dest, x->u.symto, argc, argv);
+ else if (!x->symfrom) pd_list(x->dest, s, argc, argv);
+ else inlet_wrong(x, &s_list);
+}
+
+static void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv) {
+ if (x->symfrom == s) typedmess(x->dest, x->u.symto, argc, argv);
+ else if (!x->symfrom) typedmess(x->dest, s, argc, argv);
+ else inlet_wrong(x, s);
+}
+
+void inlet_free(t_inlet *x) {
+ t_object *y = x->owner;
+ if (y->inlet == x) y->inlet = x->next;
+ else for (t_inlet *x2 = y->inlet; x2; x2 = x2->next) if (x2->next == x) {
+ x2->next = x->next;
+ break;
+ }
+ pd_free(x);
+}
+
+/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */
+
+static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp) {
+ gpointer_unset(x->u.pointerslot);
+ *(x->u.pointerslot) = *gp;
+ if (gp->o) gp->o->refcount++;
+}
+
+static void floatinlet_float( t_inlet *x, t_float f) { *(x->u.floatslot) = f; }
+static void symbolinlet_symbol(t_inlet *x, t_symbol *s) { *(x->u.symslot) = s; }
+
+#define COMMON \
+ x->owner = owner; \
+ x->dest = 0; \
+ x->next = 0; \
+ object_append_inlet(owner,x); \
+ return x;
+
+t_inlet *floatinlet_new(t_object *owner, t_float *fp) {
+ t_inlet *x = (t_inlet *)pd_new(floatinlet_class);
+ x->symfrom = &s_float; x->u.floatslot = fp; COMMON
+}
+t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp) {
+ t_inlet *x = (t_inlet *)pd_new(symbolinlet_class);
+ x->symfrom = &s_symbol; x->u.symslot = sp; COMMON
+}
+t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp) {
+ t_inlet *x = (t_inlet *)pd_new(pointerinlet_class);
+ x->symfrom = &s_pointer; x->u.pointerslot = gp; COMMON
+}
+#undef COMMON
+
+/* ---------------------- routine to handle lists ---------------------- */
+
+/* objects interpret lists by feeding them to the individual inlets. Before you call this,
+ check that the object doesn't have a more specific way to handle lists. */
+void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv) {
+ t_atom *ap;
+ int count;
+ t_inlet *ip = ((t_object *)x)->inlet;
+ if (!argc) return;
+ for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->next) {
+ if (ap->a_type == A_POINTER) pd_pointer(ip,ap->a_gpointer);
+ else if (ap->a_type == A_FLOAT) pd_float(ip,ap->a_float);
+ else pd_symbol(ip,ap->a_symbol);
+ }
+ if (argv->a_type == A_POINTER) pd_pointer(x, argv->a_gpointer);
+ else if (argv->a_type == A_FLOAT) pd_float(x, argv->a_float);
+ else pd_symbol(x, argv->a_symbol);
+}
+
+void obj_init () {
+ inlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0);
+ floatinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0);
+ symbolinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0);
+ pointerinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0);
+ class_addbang(inlet_class, inlet_bang);
+ class_addpointer(inlet_class, inlet_pointer);
+ class_addfloat(inlet_class, inlet_float);
+ class_addsymbol(inlet_class, inlet_symbol);
+ class_addlist(inlet_class, inlet_list);
+ class_addanything(inlet_class, inlet_anything);
+ class_addfloat( floatinlet_class, floatinlet_float);
+ class_addsymbol( symbolinlet_class, symbolinlet_symbol);
+ class_addpointer(pointerinlet_class, pointerinlet_pointer);
+}
+
+/* --------------------------- outlets ------------------------------ */
+
+/* this is fairly obsolete stuff, I think */
+static int outlet_eventno;
+void outlet_setstacklim () {outlet_eventno++;}
+int sched_geteventno( void) {return outlet_eventno;}
+
+struct _outlet {
+ t_object *owner;
+ struct _outlet *next;
+ t_outconnect *connections;
+ t_symbol *sym;
+};
+
+t_inlet *t_object:: in(int n) {t_inlet *i= inlet; while(n--) i=i->next; return i;}
+t_outlet *t_object::out(int n) {t_outlet *o=outlet; while(n--) o=o->next; return o;}
+
+t_class *wire_class;
+t_wire *wire_new (t_symbol *s, int argc, t_atom *argv) {
+ t_wire *self = (t_wire *)pd_new(wire_class);
+ self->g_adix = appendix_new((t_gobj *)self);
+ return self;
+}
+void wire_free (t_wire *self) {/* nothing here */}
+
+/* this is only used for pd_upload yet, right? so, it can use the new indices instead already */
+void wire_save (t_wire *self, t_binbuf *b) {
+// t_canvas *c = self->dix->canvas;
+ binbuf_addv(b,"ttiiii;","#X","connect",
+// canvas_getindex(c,self->from), self->outlet,
+// canvas_getindex(c,self->to ), self-> inlet);
+ self->from->dix->index, self->outlet,
+ self->to ->dix->index, self-> inlet);
+ appendix_save((t_gobj *)self,b);
+}
+
+t_outlet *outlet_new(t_object *owner, t_symbol *s) {
+ t_outlet *x = (t_outlet *)malloc(sizeof(*x)), *y, *y2;
+ x->owner = owner;
+ x->next = 0;
+ y = owner->outlet;
+ if (y) {
+ while ((y2 = y->next)) y = y2;
+ y->next = x;
+ } else owner->outlet = x;
+ x->connections = 0;
+ x->sym = s;
+ return x;
+}
+
+#define each_connect(oc,x) for (t_outconnect *oc = x->connections; oc; oc = oc->next)
+void outlet_bang(t_outlet *x) {each_connect(oc,x) pd_bang(oc->oc_to);}
+void outlet_pointer(t_outlet *x, t_gpointer *gp) {t_gpointer gpointer = *gp; each_connect(oc,x) pd_pointer(oc->oc_to, &gpointer);}
+void outlet_float(t_outlet *x, t_float f) {each_connect(oc,x) pd_float(oc->oc_to, f);}
+void outlet_symbol(t_outlet *x, t_symbol *s) {each_connect(oc,x) pd_symbol(oc->oc_to, s);}
+void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv) {each_connect(oc,x) pd_list( oc->oc_to,s,argc,argv);}
+void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv) {each_connect(oc,x) typedmess(oc->oc_to,s,argc,argv);}
+
+void outlet_atom(t_outlet *x, t_atom *a) {
+ if (a->a_type == A_FLOAT ) outlet_float( x,a->a_float);
+ else if (a->a_type == A_SYMBOL ) outlet_symbol( x,a->a_symbol);
+ else if (a->a_type == A_POINTER) outlet_pointer(x,a->a_gpointer);
+ else error("can't send atom whose type is %d",a->a_type);
+}
+
+/* get the outlet's declared symbol */
+t_symbol *outlet_getsymbol(t_outlet *x) {return x->sym;}
+
+void outlet_free(t_outlet *x) {
+ t_object *y = x->owner;
+ if (y->outlet == x) y->outlet = x->next;
+ else for (t_outlet *x2 = y->outlet; x2; x2 = x2->next) if (x2->next == x) {
+ x2->next = x->next;
+ break;
+ }
+ free(x);
+}
+
+#define each_inlet(i,obj) for ( t_inlet *i=obj->inlet; i; i=i->next)
+#define each_outlet(o,obj) for (t_outlet *o=obj->outlet; o; o=o->next)
+
+static t_pd *find_inlet(t_object *to, int inlet) {
+ if (to->_class->firstin) {if (inlet) inlet--; else return (t_pd *)to;}
+ each_inlet(i,to) if (inlet) inlet--; else return (t_pd *)i;
+ return 0;
+}
+
+static t_outlet *find_outlet(t_object *from, int outlet) {
+ each_outlet(o,from) if (outlet) outlet--; else return o;
+ return 0;
+}
+
+t_outconnect *obj_connect(t_object *from, int outlet, t_object *to, int inlet) {
+ t_outlet *o = find_outlet(from,outlet);
+ t_pd *i = find_inlet(to,inlet);
+ if (!o||!i) return 0;
+ t_outconnect *oc = wire_new(0,0,0), *oc2;
+ oc->next = 0;
+ oc->oc_to = i;
+ oc->from = from; oc->outlet = outlet;
+ oc->to = to; oc->inlet = inlet;
+ /* append it to the end of the list */
+ /* LATER we might cache the last "oc" to make this faster. */
+ if ((oc2 = o->connections)) {
+ while (oc2->next) oc2 = oc2->next;
+ oc2->next = oc;
+ } else o->connections = oc;
+ if (o->sym == &s_signal) canvas_update_dsp();
+ return oc;
+}
+
+void obj_disconnect(t_object *from, int outlet, t_object *to, int inlet) {
+ t_outlet *o = find_outlet(from,outlet); if (!o) {post("outlet does not exist"); return;}
+ t_pd *i = find_inlet(to, inlet); if (!i) {post( "inlet does not exist"); return;}
+ t_outconnect *oc = o->connections, *oc2;
+ if (!oc) {post("outlet has no connections"); return;}
+ if (oc->oc_to == i) {
+ o->connections = oc->next;
+ pd_free(oc);
+ goto done;
+ }
+ while ((oc2 = oc->next)) {
+ if (oc2->oc_to == i) {
+ oc->next = oc2->next;
+ pd_free(oc2);
+ goto done;
+ }
+ oc = oc2;
+ }
+ post("connection not found");
+done:
+ if (o->sym == &s_signal) canvas_update_dsp();
+}
+
+/* ------ traversal routines for code that can't see our structures ------ */
+
+int obj_noutlets(t_object *x) {
+ int n=0;
+ each_outlet(o,x) n++;
+ return n;
+}
+
+int obj_ninlets(t_object *x) {
+ int n=!!x->_class->firstin;
+ each_inlet(i,x) n++;
+ return n;
+}
+
+t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout) {
+ t_outlet *o = x->outlet;
+ while (nout-- && o) o = o->next;
+ *op = o;
+ return o ? o->connections : 0;
+}
+
+t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, t_object **destp, t_inlet **inletp, int *whichp) {
+ t_pd *y = lastconnect->oc_to;
+ if (ISINLET(y)) {
+ t_inlet *i = (t_inlet *)y;
+ t_object *dest = i->owner;
+ int n = dest->_class->firstin;
+ each_inlet(i2,dest) if (i2==i) break; else n++;
+ *whichp = n;
+ *destp = dest;
+ *inletp = i;
+ } else {
+ *whichp = 0;
+ *inletp = 0;
+ *destp = (t_object *)y;
+ }
+ return lastconnect->next;
+}
+
+/* this one checks that a pd is indeed a patchable object, and returns it,
+ correctly typed, or zero if the check failed. */
+t_object *pd_checkobject(t_pd *x) {
+ return x->_class->patchable ? (t_object *)x : 0;
+}
+
+/* move an inlet or outlet to the head of the list. this code is not safe with the latest additions in t_outconnect ! */
+void obj_moveinletfirst( t_object *x, t_inlet *i) {
+ if (x->inlet == i) return;
+ each_inlet( i2,x) if (i2->next == i) {i2->next = i->next; i->next = x-> inlet; x-> inlet = i; return;}}
+void obj_moveoutletfirst(t_object *x, t_outlet *o) {
+ if (x->outlet == o) return;
+ each_outlet(o2,x) if (o2->next == o) {o2->next = o->next; o->next = x->outlet; x->outlet = o; return;}}
+
+/* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */
+/* LATER try to consolidate all the slightly different routines. */
+
+int obj_nsiginlets(t_object *x) {
+ int n=0;
+ each_inlet(i,x) if (i->symfrom == &s_signal) n++;
+ if (x->_class->firstin && x->_class->floatsignalin) n++;
+ return n;
+}
+int obj_nsigoutlets(t_object *x) {
+ int n=0;
+ each_outlet(o,x) if (o->sym == &s_signal) n++;
+ return n;
+}
+
+/* get the index, among signal inlets, of the mth inlet overall */
+int obj_siginletindex(t_object *x, int m) {
+ int n=0;
+ if (x->_class->firstin && x->_class->floatsignalin) {if (!m--) return 0; else n++;}
+ each_inlet(i,x) if (i->symfrom == &s_signal) {if (!m) return n; else {n++; m--;}}
+ return -1;
+}
+int obj_sigoutletindex(t_object *x, int m) {
+ int n=0;
+ each_outlet(o,x) if (o->sym == &s_signal) {if (!m) return n; else {n++; m--;}}
+ return -1;
+}
+
+int obj_issignalinlet(t_object *x, int m) {
+ if (x->_class->firstin) {if (!m) return x->_class->floatsignalin; else m--;}
+ t_inlet *i;
+ for (i = x->inlet; i && m; i = i->next, m--) {}
+ return i && i->symfrom==&s_signal;
+}
+int obj_issignaloutlet(t_object *x, int m) {
+ t_outlet *o2;
+ for (o2 = x->outlet; o2 && m--; o2 = o2->next) {}
+ return o2 && o2->sym==&s_signal;
+}
+
+t_sample *obj_findsignalscalar(t_object *x, int m) {
+ int n = 0;
+ t_inlet *i;
+ if (x->_class->firstin && x->_class->floatsignalin) {
+ if (!m--) return x->_class->floatsignalin > 0 ? (t_sample *)(((char *)x) + x->_class->floatsignalin) : 0;
+ n++;
+ }
+ for (i = x->inlet; i; i = i->next, m--) if (i->symfrom == &s_signal) {
+ if (m == 0) return &i->u.floatsignalvalue;
+ n++;
+ }
+ return 0;
+}
+
+/* and those two are only used in desire.c... */
+int inlet_getsignalindex(t_inlet *x) {
+ int n=0; for ( t_inlet *i = x->owner-> inlet; i; i = i->next) if (i==x) return n; else if (i->symfrom == &s_signal) n++;
+ return -1;}
+int outlet_getsignalindex(t_outlet *x) {
+ int n=0; for (t_outlet *o = x->owner->outlet; o; o = o->next) if (o==x) return n; else if (o->sym == &s_signal) n++;
+ return -1;}
+
+#ifdef QUALIFIED_NAME
+static char *pd_library_name = 0;
+void pd_set_library_name(char *libname){
+ pd_library_name=libname;
+}
+#endif
+
+t_hash<t_symbol *, t_class *> *class_table=0;
+static t_symbol *class_loadsym; /* name under which an extern is invoked */
+static void pd_defaultfloat(t_pd *x, t_float f);
+static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+t_pd pd_objectmaker; /* factory for creating "object" boxes */
+t_pd pd_canvasmaker; /* factory for creating canvases */
+
+static t_symbol *class_extern_dir = &s_;
+
+static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv) {
+ error("%s: no method for '%s'", x->_class->name->name, s->name);
+}
+
+static void pd_defaultbang(t_pd *x) {
+ t_class *c = pd_class(x);
+ if (c->listmethod != pd_defaultlist) c->listmethod(x,0,0,0);
+ else c->anymethod(x,&s_bang,0,0);
+}
+
+static void pd_defaultfloat(t_pd *x, t_float f) {
+ t_class *c = pd_class(x); t_atom at; SETFLOAT(&at, f);
+ if (c->listmethod != pd_defaultlist) c->listmethod(x,0,1,&at); else c->anymethod(x,&s_float,1,&at);
+}
+static void pd_defaultsymbol(t_pd *x, t_symbol *s) {
+ t_class *c = pd_class(x); t_atom at; SETSYMBOL(&at, s);
+ if (c->listmethod != pd_defaultlist) c->listmethod(x,0,1,&at); else c->anymethod(x,&s_symbol,1,&at);
+}
+static void pd_defaultpointer(t_pd *x, t_gpointer *gp) {
+ t_class *c = pd_class(x); t_atom at; SETPOINTER(&at, gp);
+ if (c->listmethod != pd_defaultlist) c->listmethod(x,0,1,&at); else c->anymethod(x,&s_pointer,1,&at);
+}
+
+void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);
+static void class_nosavefn(t_gobj *z, t_binbuf *b);
+
+/* handle "list" messages to Pds without explicit list methods defined. */
+static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv) {
+ t_class *c = pd_class(x);
+ /* a list with no elements is handled by the 'bang' method if one exists. */
+ if (argc == 0 && c->bangmethod != pd_defaultbang) {c->bangmethod(x); return;}
+ /* a list with one element which is a number can be handled by a
+ "float" method if any is defined; same for "symbol", "pointer". */
+ if (argc == 1) {
+#define HANDLE(A,M,D,F) if (argv->a_type==A && c->M != D) {c->M(x, argv->a_w.F); return;}
+ HANDLE(A_FLOAT ,floatmethod ,pd_defaultfloat ,w_float)
+ HANDLE(A_SYMBOL ,symbolmethod ,pd_defaultsymbol ,w_symbol)
+ HANDLE(A_POINTER,pointermethod,pd_defaultpointer,w_gpointer)
+ }
+ /* Next try for an "anything" method; if the object is patchable (i.e.,
+ can have proper inlets) send it on to obj_list which will unpack the
+ list into the inlets. otherwise gove up and complain. */
+ if (c->anymethod != pd_defaultanything) c->anymethod(x,&s_list,argc,argv);
+ else if (c->patchable) obj_list((t_object *)x, s, argc, argv);
+ else pd_defaultanything(x, &s_list, argc, argv);
+}
+
+t_symbol *qualified_name(t_symbol *s) {
+ char *buf;
+ asprintf(&buf, "%s%s%s", pd_library_name, QUALIFIED_NAME, s->name);
+ t_symbol *sym = gensym(buf);
+ free(buf);
+ return sym;
+}
+
+#undef class_new2
+#undef class_addcreator2
+#undef class_addmethod2
+
+/* Note that some classes such as "select", are actually two classes of the same name,
+ one for the single-argument form, one for the multiple one; see select_setup() to
+ find out how this is handled. */
+t_class *class_new2(const char *ss, t_newmethod newmethod, t_method freemethod,
+size_t size, int flags, const char *sig) {
+ t_symbol *s = gensym(ss);
+ int typeflag = flags & CLASS_TYPEMASK;
+ if (!typeflag) typeflag = CLASS_PATCHABLE;
+#ifdef QUALIFIED_NAME
+ if (pd_library_name) s = qualified_name(s);
+#endif
+ if (pd_objectmaker._class && newmethod) {
+ /* add a "new" method by the name specified by the object */
+ class_addmethod2(pd_objectmaker._class, (t_method)newmethod, s->name, sig);
+ if (class_loadsym) {
+ /* if we're loading an extern it might have been invoked by a
+ longer file name; in this case, make this an admissible name too. */
+ char *loadstring = class_loadsym->name, l1 = strlen(s->name), l2 = strlen(loadstring);
+ if (l2 > l1 && !strcmp(s->name, loadstring + (l2 - l1)))
+ class_addmethod2(pd_objectmaker._class, (t_method)newmethod, class_loadsym->name, sig);
+ }
+ }
+ t_class *c = (t_class *)malloc(sizeof(*c));
+ c->name = c->helpname = s;
+ c->size = size;
+ c->methods = (t_methodentry *)malloc(1);
+ c->nmethod = 0;
+ c->freemethod = (t_method)freemethod;
+ c->bangmethod = pd_defaultbang;
+ c->pointermethod = pd_defaultpointer;
+ c->floatmethod = pd_defaultfloat;
+ c->symbolmethod = pd_defaultsymbol;
+ c->listmethod = pd_defaultlist;
+ c->anymethod = pd_defaultanything;
+ c->firstin = ((flags & CLASS_NOINLET) == 0);
+ c->firsttip = gensym("?");
+ c->fields = (t_symbol **)malloc(sizeof(t_symbol *)*31);
+ c->nfields = 0;
+ c->patchable = (typeflag == CLASS_PATCHABLE);
+ c->gobj = (typeflag >= CLASS_GOBJ);
+ c->drawcommand = 0;
+ c->floatsignalin = 0;
+ c->externdir = class_extern_dir;
+ c->savefn = (typeflag == CLASS_PATCHABLE ? text_save : class_nosavefn);
+#ifdef QUALIFIED_NAME
+ c->helpname = gensym(ss);
+ // like a class_addcreator
+ if (pd_library_name && newmethod)
+ class_addmethod2(pd_objectmaker._class, (t_method)newmethod, ss, sig);
+#endif
+ c->onsubscribe = gobj_onsubscribe;
+ class_table->set(c->name, c);
+ return c;
+}
+
+/* add a creation method, which is a function that returns a Pd object
+ suitable for putting in an object box. We presume you've got a class it
+ can belong to, but this won't be used until the newmethod is actually
+ called back (and the new method explicitly takes care of this.) */
+void class_addcreator2(const char *ss, t_newmethod newmethod, const char *sig) {
+ t_symbol *s = gensym(ss);
+ class_addmethod2(pd_objectmaker._class, (t_method)newmethod, ss, sig);
+#ifdef QUALIFIED_NAME
+ class_addmethod2(pd_objectmaker._class, (t_method)newmethod, pd_library_name ? qualified_name(s)->name : ss, sig);
+#endif
+ class_table->set(s,0);
+}
+
+void class_addmethod2(t_class *c, t_method fn, const char *ss, const char *fmt) {
+ t_symbol *sel = gensym(ss);
+ t_methodentry *m;
+ int argtype = *fmt++;
+ /* "signal" method specifies that we take audio signals but
+ that we don't want automatic float to signal conversion. This
+ is obsolete; you should now use the CLASS_MAINSIGNALIN macro. */
+ if (sel == &s_signal) {
+ if (c->floatsignalin) post("warning: signal method overrides class_mainsignalin");
+ c->floatsignalin = -1;
+ }
+ /* check for special cases. "Pointer" is missing here so that
+ pd_objectmaker's pointer method can be typechecked differently. */
+ /* is anyone actually using those five cases? */
+ if (sel==&s_bang) {if (argtype) goto phooey; class_addbang( c,fn);}
+ else if (sel==&s_float) {if (argtype!='f'||*fmt) goto phooey; class_doaddfloat( c,fn);}
+ else if (sel==&s_symbol) {if (argtype!='s'||*fmt) goto phooey; class_addsymbol( c,fn);}
+ else if (sel==&s_list) {if (argtype!='*') goto phooey; class_addlist( c,fn);}
+ else if (sel==&s_anything) {if (argtype!='*') goto phooey; class_addanything(c,fn);}
+ else {
+ /* SLOW, especially for [objectmaker] */
+ c->methods = (t_methodentry *)realloc(c->methods, (c->nmethod+1) * sizeof(*c->methods));
+ m = c->methods + c->nmethod;
+ c->nmethod++;
+ m->me_name = sel;
+ m->me_fun = (t_gotfn)fn;
+ int nargs = 0;
+ while (argtype && nargs < MAXPDARG) {
+ t_atomtype t;
+ switch(argtype) {
+ case 'f': t=A_FLOAT; break;
+ case 's': t=A_SYMBOL; break;
+ case 'p': t=A_POINTER; break;
+ case ';': t=A_SEMI; break;
+ case ',': t=A_COMMA; break;
+ case 'F': t=A_DEFFLOAT; break;
+ case 'S': t=A_DEFSYMBOL;break;
+ case '$': t=A_DOLLAR; break;
+ case '@': t=A_DOLLSYM; break;
+ case '*': t=A_GIMME; break;
+ case '!': t=A_CANT; break;
+ default: goto phooey;
+ };
+ m->me_arg[nargs++] = t;
+ argtype = *fmt++;
+ }
+ if (argtype) error("%s_%s: only 5 arguments are typecheckable; use A_GIMME aka '*'", c->name->name, sel->name);
+ m->me_arg[nargs] = A_NULL;
+ }
+ return;
+phooey:
+ bug("class_addmethod: %s_%s: bad argument types", c->name->name, sel->name);
+}
+
+t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod,
+size_t size, int flags, t_atomtypearg arg1, ...) {
+ char fmt[42],*f=fmt; va_list ap; va_start(ap,arg1); int t=arg1;
+ while(t) {
+ if (t>A_CANT) {error("class_new: ARRGH! t=%d",t); return 0;}
+ *f++ = " fsp;,FS$@*!"[t];
+ t=(t_atomtype)va_arg(ap,int);
+ }
+ *f=0; va_end(ap); return class_new2(s->name,newmethod,freemethod,size,flags,fmt);
+}
+void class_addcreator(t_newmethod newmethod, t_symbol *s, t_atomtypearg arg1, ...) {
+ char fmt[42],*f=fmt; va_list ap; va_start(ap,arg1); int t=arg1;
+ while(t) {
+ if (t>A_CANT) {error("class_addcreator: ARRGH! t=%d",t); return;}
+ *f++ = " fsp;,FS$@*!"[t];
+ t=(t_atomtype)va_arg(ap,int);
+ }
+ *f=0; va_end(ap); class_addcreator2(s->name,newmethod,fmt);
+}
+void class_addmethod(t_class *c, t_method fn, t_symbol *sel, t_atomtypearg arg1, ...) {
+ char fmt[42],*f=fmt; va_list ap; va_start(ap,arg1); int t=arg1;
+ while(t) {
+ if (t>A_CANT) {error("class_addmethod: ARRGH! t=%d",t); return;}
+ *f++ = " fsp;,FS$@*!"[t];
+ t=(t_atomtype)va_arg(ap,int);
+ }
+ *f=0; va_end(ap); class_addmethod2(c,fn,sel->name,fmt);
+}
+
+/* see also the "class_addfloat", etc., macros in m_pd.h */
+#undef class_addbang
+#undef class_addpointer
+#undef class_addsymbol
+#undef class_addlist
+#undef class_addanything
+void class_addbang( t_class *c, t_method fn) {c-> bangmethod = (t_bangmethod)fn;}
+void class_addpointer( t_class *c, t_method fn) {c->pointermethod = (t_pointermethod)fn;}
+void class_doaddfloat( t_class *c, t_method fn) {c-> floatmethod = (t_floatmethod)fn;}
+void class_addsymbol( t_class *c, t_method fn) {c-> symbolmethod = (t_symbolmethod)fn;}
+void class_addlist( t_class *c, t_method fn) {c-> listmethod = (t_listmethod)fn;}
+void class_addanything(t_class *c, t_method fn) {c-> anymethod = (t_anymethod)fn;}
+
+char *class_getname(t_class *c) {return c->name->name;}
+char *class_gethelpname(t_class *c) {return c->helpname->name;}
+void class_sethelpsymbol(t_class *c, t_symbol *s) {c->helpname = s;}
+void class_setdrawcommand(t_class *c) {c->drawcommand = 1;}
+int class_isdrawcommand( t_class *c) {return c->drawcommand;}
+void class_setnotice( t_class *c, t_notice notice ) {c->notice = notice ;}
+void class_setonsubscribe(t_class *c, t_onsubscribe onsubscribe) {c->onsubscribe = onsubscribe;}
+
+static void pd_floatforsignal(t_pd *x, t_float f) {
+ int offset = x->_class->floatsignalin;
+ if (offset > 0)
+ *(t_sample *)(((char *)x) + offset) = f;
+ else
+ error("%s: float unexpected for signal input", x->_class->name->name);
+}
+
+void class_domainsignalin(t_class *c, int onset) {
+ if (onset <= 0) onset = -1;
+ else {
+ if (c->floatmethod != pd_defaultfloat)
+ post("warning: %s: float method overwritten", c->name->name);
+ c->floatmethod = (t_floatmethod)pd_floatforsignal;
+ }
+ c->floatsignalin = onset;
+}
+
+void class_set_extern_dir(t_symbol *s) {class_extern_dir = s;}
+char *class_gethelpdir(t_class *c) {return c->externdir->name;}
+
+static void class_nosavefn(t_gobj *z, t_binbuf *b) {
+ bug("save function called but not defined");
+}
+
+void class_setsavefn(t_class *c, t_savefn f) {c->savefn = f;}
+t_savefn class_getsavefn(t_class *c) {return c->savefn;}
+
+/* ---------------- the symbol table ------------------------ */
+
+/* tb: new 16 bit hash table: multiplication hash */
+#ifndef NEWHASH
+#define HASHSIZE 1024
+#else
+#define HASHSIZE 65536
+#define HASHFACTOR 40503 /* donald knuth: (sqrt(5) - 1)/2*pow(2,16) */
+#endif
+
+#ifdef NEWHASH
+static short hash(const char *s, size_t n) {
+ unsigned short hash1 = 0, hash2 = 0;
+#else
+static int hash(const char *s, size_t n) {
+ unsigned int hash1 = 0, hash2 = 0;
+#endif
+ const char *s2 = s;
+ while (n) {
+ hash1 += *s2;
+ hash2 += hash1;
+ s2++;
+ n--;
+ }
+ return hash2;
+}
+
+/* tb: made dogensym() threadsafe
+ * supported by vibrez.net */
+t_symbol *dogensym(const char *s, size_t n, t_symbol *oldsym) {
+ static t_symbol *symhash[HASHSIZE];
+#ifdef THREADSAFE_GENSYM
+ static pthread_mutex_t hash_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+ t_symbol **sym1, *sym2;
+#ifdef NEWHASH
+ unsigned short hash2 = hash(s,n);
+#else
+ unsigned int hash2 = hash(s,n);
+#endif
+#ifdef NEWHASH
+ hash2 = hash2 * HASHFACTOR;
+ sym1 = symhash + hash2;
+#else
+ sym1 = symhash + (hash2 & (HASHSIZE-1));
+#endif
+ while ((sym2 = *sym1)) {
+ if (!strcmp(sym2->name, s)) return sym2;
+ sym1 = &sym2->next;
+ }
+#ifdef THREADSAFE_GENSYM
+ pthread_mutex_lock(&hash_lock);
+ /* tb: maybe another thread added the symbol to the hash table; double check */
+ while (sym2 = *sym1) {
+ if (!strcmp(sym2->name, s)) {
+ pthread_mutex_unlock(&hash_lock);
+ return sym2;
+ }
+ sym1 = &sym2->next;
+ }
+#endif
+
+ if (oldsym) sym2 = oldsym;
+ else {
+ sym2 = (t_symbol *)malloc(sizeof(*sym2));
+ sym2->name = (char *)malloc(n+1);
+ sym2->next = 0;
+ sym2->thing = 0;
+ memcpy(sym2->name, s, n);
+ sym2->name[n]=0;
+ sym2->n=n;
+ }
+ *sym1 = sym2;
+#ifdef THREADSAFE_GENSYM
+ pthread_mutex_unlock(&hash_lock);
+#endif
+ return sym2;
+}
+
+t_symbol *gensym( const char *s) {return dogensym(s,strlen(s),0);}
+t_symbol *gensym2(const char *s, size_t n) {return dogensym(s,n,0);}
+extern "C" t_symbol *symprintf(const char *s, ...) {
+ char *buf;
+ va_list args;
+ va_start(args,s);
+ vasprintf(&buf,s,args);
+ va_end(args);
+ t_symbol *r = gensym(buf);
+ free(buf);
+ return r;
+}
+
+static int tryingalready;
+extern "C" void canvas_popabstraction(t_canvas *x);
+extern t_pd *newest;
+t_symbol* pathsearch(t_symbol *s,char* ext);
+int pd_setloadingabstraction(t_symbol *sym);
+
+/* this routine is called when a new "object" is requested whose class Pd
+ 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) {
+ int fd;
+ char *dirbuf, *nameptr;
+ if (tryingalready) return;
+ newest = 0;
+ class_loadsym = s;
+ if (sys_load_lib(canvas_getcurrent(), s->name)) {
+ tryingalready = 1;
+ typedmess((t_pd *)dummy, s, argc, argv);
+ tryingalready = 0;
+ return;
+ }
+ class_loadsym = 0;
+ t_pd *current = s__X.thing;
+ if ((fd = canvas_open2(canvas_getcurrent(), s->name, ".pd", &dirbuf, &nameptr, 0)) >= 0 ||
+ (fd = canvas_open2(canvas_getcurrent(), s->name, ".pat", &dirbuf, &nameptr, 0)) >= 0) {
+ close(fd);
+ if (!pd_setloadingabstraction(s)) {
+ canvas_setargs(argc, argv); /* bug fix by Krzysztof Czaja */
+ binbuf_evalfile(gensym(nameptr), gensym(dirbuf));
+ if (s__X.thing != current) canvas_popabstraction((t_canvas *)s__X.thing);
+ canvas_setargs(0, 0);
+ } else error("%s: can't load abstraction within itself", s->name);
+ free(dirbuf);
+ } else newest = 0;
+}
+
+#define MAKESYM(CSYM,S) t_symbol CSYM = {S,0,0,1,0xdeadbeef};
+MAKESYM(s_pointer ,"pointer")
+MAKESYM(s_float ,"float")
+MAKESYM(s_symbol ,"symbol")
+MAKESYM(s_bang ,"bang")
+MAKESYM(s_list ,"list")
+MAKESYM(s_anything,"anything")
+MAKESYM(s_signal ,"signal")
+MAKESYM(s__N ,"#N")
+MAKESYM(s__X ,"#X")
+MAKESYM(s_x ,"x")
+MAKESYM(s_y ,"y")
+MAKESYM(s_ ,"")
+
+static t_symbol *symlist[] = { &s_pointer, &s_float, &s_symbol, &s_bang,
+ &s_list, &s_anything, &s_signal, &s__N, &s__X, &s_x, &s_y, &s_};
+
+t_pd *newest;
+
+/* This is externally available, but note that it might later disappear; the
+whole "newest" thing is a hack which needs to be redesigned. */
+t_pd *pd_newest () {return newest;}
+
+ /* horribly, we need prototypes for each of the artificial function
+ calls in typedmess(), to keep the compiler quiet. */
+typedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv);
+typedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+
+#define REST t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5
+typedef t_pd *(*t_fun0)(REST);
+typedef t_pd *(*t_fun1)(t_int i1, REST);
+typedef t_pd *(*t_fun2)(t_int i1, t_int i2, REST);
+typedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3, REST);
+typedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4, REST);
+typedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, REST);
+typedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6, REST);
+#undef REST
+
+void pd_typedmess_2(t_pd *x, t_symbol *s, int argc, t_atom *argv) {
+ t_class *c = x->_class;
+ t_atomtype *wp, wanttype;
+ t_int ai[MAXPDARG+1], *ap = ai;
+ t_floatarg ad[MAXPDARG+1], *dp = ad;
+ int narg = 0;
+ /* check for messages that are handled by fixed slots in the class structure. We don't catch "pointer"
+ though so that sending "pointer" to pd_objectmaker doesn't require that we supply a pointer value. */
+ if (s == &s_float) {
+ if (!argc) c->floatmethod(x, 0.);
+ else if (argv->a_type == A_FLOAT) c->floatmethod(x, argv->a_float);
+ else error("expected one float, in class [%s]", c->name->name);
+ return;
+ }
+ if (s == &s_bang) {c->bangmethod(x); return;}
+ if (s == &s_list) {c->listmethod(x,s,argc,argv); return;}
+ if (s == &s_symbol) {c->symbolmethod(x, argc && argv->a_type==A_SYMBOL ? argv->a_symbol : &s_); return;}
+ t_methodentry *m = c->methods;
+ for (int i = c->nmethod; i--; m++) if (m->me_name == s) {
+ wp = m->me_arg;
+ if (*wp == A_GIMME) {
+ if (x == &pd_objectmaker) pd_set_newest(((t_newgimme)(m->me_fun))( s,argc,argv));
+ else ((t_messgimme)(m->me_fun))(x,s,argc,argv);
+ return;
+ }
+ if (argc > MAXPDARG) argc = MAXPDARG;
+ if (x != &pd_objectmaker) *(ap++) = (t_int)x, narg++;
+ while ((wanttype = *wp++)) {
+ switch (wanttype) {
+ case A_POINTER:
+ if (!argc) goto badarg;
+ if (argv->a_type!=A_POINTER) goto badarg;
+ *ap = t_int(argv->a_gpointer);
+ argc--; argv++;
+ narg++;
+ ap++;
+ break;
+ case A_FLOAT: if (!argc) goto badarg;
+ case A_DEFFLOAT: if (!argc) *dp = 0;
+ else {
+ if (argv->a_type!=A_FLOAT) goto badarg;
+ *dp = argv->a_float;
+ argc--; argv++;
+ }
+ dp++;
+ break;
+ case A_SYMBOL: if (!argc) goto badarg;
+ case A_DEFSYM: if (!argc) *ap = t_int(&s_);
+ else {
+ if (argv->a_type == A_SYMBOL) *ap = t_int(argv->a_symbol);
+ /* if it's an unfilled "dollar" argument it appears as zero here; cheat and bash it to the null
+ symbol. Unfortunately, this lets real zeros pass as symbols too, which seems wrong... */
+ else if (x == &pd_objectmaker && argv->a_type == A_FLOAT && argv->a_float == 0)
+ *ap = t_int(&s_);
+ else goto badarg;
+ argc--; argv++;
+ }
+ narg++;
+ ap++;
+ default: {}
+ }
+ }
+ t_pd *bonzo;
+ switch (narg) {
+#define REST ad[0],ad[1],ad[2],ad[3],ad[4]
+ case 0 : bonzo = ((t_fun0)(m->me_fun))( REST); break;
+ case 1 : bonzo = ((t_fun1)(m->me_fun))(ai[0], REST); break;
+ case 2 : bonzo = ((t_fun2)(m->me_fun))(ai[0],ai[1], REST); break;
+ case 3 : bonzo = ((t_fun3)(m->me_fun))(ai[0],ai[1],ai[2], REST); break;
+ case 4 : bonzo = ((t_fun4)(m->me_fun))(ai[0],ai[1],ai[2],ai[3], REST); break;
+ case 5 : bonzo = ((t_fun5)(m->me_fun))(ai[0],ai[1],ai[2],ai[3],ai[4], REST); break;
+ case 6 : bonzo = ((t_fun6)(m->me_fun))(ai[0],ai[1],ai[2],ai[3],ai[4],ai[5],REST); break;
+ default: bonzo = 0;
+ }
+ if (x == &pd_objectmaker) pd_set_newest(bonzo);
+ return;
+ }
+ c->anymethod(x, s, argc, argv);
+ return;
+badarg:
+ error("Bad arguments for message '%s' to object '%s'", s->name, c->name->name);
+}
+
+void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv) {
+ ENTER(s); pd_typedmess_2(x,s,argc,argv); LEAVE;
+}
+
+void pd_vmess(t_pd *x, t_symbol *sel, char *fmt, ...) {
+ va_list ap;
+ t_atom arg[MAXPDARG], *at =arg;
+ int nargs = 0;
+ char *fp = fmt;
+ va_start(ap, fmt);
+ while (1) {
+ if (nargs > MAXPDARG) {
+ error("pd_vmess: only %d allowed", MAXPDARG);
+ break;
+ }
+ switch(*fp++) {
+ case 'f': SETFLOAT(at, va_arg(ap, double)); break;
+ case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break;
+ case 'i': SETFLOAT(at, va_arg(ap, t_int)); break;
+ case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break;
+ default: goto done;
+ }
+ at++;
+ nargs++;
+ }
+done:
+ va_end(ap);
+ typedmess(x, sel, nargs, arg);
+}
+
+void pd_forwardmess(t_pd *x, int argc, t_atom *argv) {
+ if (argc) {
+ t_atomtype t = argv->a_type;
+ if (t == A_SYMBOL) pd_typedmess(x, argv->a_symbol, argc-1, argv+1);
+ else if (t == A_POINTER) {if (argc==1) pd_pointer(x, argv->a_gpointer); else pd_list(x, &s_list, argc, argv);}
+ else if (t == A_FLOAT) {if (argc==1) pd_float( x, argv->a_float); else pd_list(x, &s_list, argc, argv);}
+ else bug("pd_forwardmess");
+ }
+}
+
+void nullfn () {}
+
+t_gotfn getfn(t_pd *x, t_symbol *s) {
+ t_class *c = x->_class;
+ t_methodentry *m = c->methods;
+ for (int i=c->nmethod; i--; m++) if (m->me_name == s) return m->me_fun;
+ error("%s: no method for message '%s'", c->name->name, s->name);
+ return (t_gotfn)nullfn;
+}
+
+t_gotfn zgetfn(t_pd *x, t_symbol *s) {
+ t_class *c = x->_class;
+ t_methodentry *m = c->methods;
+ for (int i=c->nmethod; i--; m++) if (m->me_name == s) return m->me_fun;
+ return 0;
+}
+
+void class_settip(t_class *x,t_symbol* s) {x->firsttip = s;}
+
+/* must be called only once */
+void class_setfieldnames(t_class *x, const char *s) {
+ char foo[64];
+ while (*s) {
+ char *t = strchr(s,' ');
+ int i = t-s;
+ if (!t) return;
+ memcpy(foo,s,i);
+ foo[i]=0;
+ x->fields[x->nfields++] = gensym(foo);
+ s=s+i+1;
+ }
+}
+
+int class_getfieldindex(t_class *x, const char *s) {
+ t_symbol *sy = gensym((char *)s);
+ for (int i=0; i<x->nfields; i++) if (x->fields[i]==sy) return i;
+ return -1;
+}
+
+/* O(n) asymptotic time :-} */
+/* only looks for already loaded classes though. */
+
+t_class *class_find (t_symbol *s) {return (t_class *)class_table->get(s);}
+
+void glob_update_class_info (t_pd *bogus, t_symbol *s, t_symbol *cb_recv, t_symbol *cb_sel) {
+ t_class *c = class_find(s);
+ if (!c) { post("class not found!"); return; }
+ sys_vgui("global class_info; set class_info(%s) [list "
+ "helpname \"%s\" externdir \"%s\" size \"%d\" "
+/*
+ t_methodentry *c_methods; int c_nmethod;
+ t_method c_freemethod;
+ t_savefn c_savefn;
+ int c_floatsignalin;
+*/
+ "gobj \"%d\" patchable \"%d\" firstin \"%d\" "
+ "firsttip \"%s\" methods {",s->name,c->helpname->name,c->externdir->name,
+ c->size,c->gobj,c->patchable,c->firstin,c->firsttip->name);
+ if (c-> bangmethod != pd_defaultbang) sys_vgui("<bang> ");
+ if (c->pointermethod != pd_defaultpointer) sys_vgui("<pointer> ");
+ if (c-> floatmethod != pd_defaultfloat) sys_vgui("<float> ");
+ if (c-> symbolmethod != pd_defaultsymbol) sys_vgui("<symbol> ");
+ if (c-> listmethod != pd_defaultlist) sys_vgui("<list> ");
+ if (c-> anymethod != pd_defaultanything) sys_vgui("<any> ");
+ for (int i=0; i<c->nmethod; i++) sys_vgui("%s ",c->methods[i].me_name->name);
+ sys_vgui("}]; %s %s %s\n",cb_recv->name, cb_sel->name, s->name);
+}
+
+t_class *binbuf_class;
+
+t_binbuf *binbuf_new () {
+ t_binbuf *x = (t_binbuf *)pd_new(binbuf_class);
+ x->n = 0;
+ x->capa = 1;
+ x->v = (t_atom *)malloc(1*sizeof(t_atom));
+ return x;
+}
+
+/* caution: capa >= x->n and capa >= 1 too */
+static void binbuf_capa(t_binbuf *x, int capa) {
+ x->v = (t_atom *)realloc(x->v, capa*sizeof(*x->v));
+ x->capa = capa;
+}
+
+void binbuf_free(t_binbuf *x) {pd_free(x);}
+void binbuf_free2(t_binbuf *x) {free(x->v);}
+
+t_binbuf *binbuf_duplicate(t_binbuf *y) {
+ t_binbuf *x = (t_binbuf *)malloc(sizeof(*x));
+ x->capa = x->n = y->n;
+ x->v = (t_atom *)malloc(x->n * sizeof(*x->v));
+ memcpy(x->v,y->v,x->n*sizeof(*x->v));
+ return x;
+}
+
+void binbuf_clear(t_binbuf *x) {
+ x->n = 0;
+ x->v = (t_atom *)realloc(x->v,4);
+ x->capa = 4;
+}
+
+/* called just after a doublequote in version 1 parsing */
+char *binbuf_text_quoted(t_binbuf *x, char *t, char *end) {
+ ostringstream buf;
+ while (t!=end) {
+ char c = *t++;
+ if (c=='"') break;
+ if (c!='\\') {buf << c; continue;}
+ c = *t++;
+ if (c=='a') {buf << '\a'; continue;}
+ if (c=='b') {buf << '\b'; continue;}
+ if (c=='f') {buf << '\f'; continue;}
+ if (c=='n') {buf << '\n'; continue;}
+ if (c=='r') {buf << '\r'; continue;}
+ if (c=='v') {buf << '\v'; continue;}
+ if (c=='t') {buf << '\t'; continue;}
+ if (c=='"') {buf << '\"'; continue;}
+ if (c=='\\'){buf << '\\'; continue;}
+ if (c=='\n'){continue;}
+ /* if (c=='u') ... */
+ /* if (c=='x') ... */
+ /* if (isdigit(c)) ... */
+ buf << c; /* ignore syntax error (should it?) */
+ }
+ binbuf_addv(x,"t",buf.str().data());
+ return t; /* ignore syntax error (should it?) */
+}
+
+/* find the first atom in text, in any, and add it to this binbuf;
+ returns pointer to end of atom text */
+/* this one is for pd format version 1 */
+/* TODO: double-quotes, braces, test backslashes&dollars */
+char *binbuf_text_matju(t_binbuf *x, char *t, char *end) {
+ int doll=0;
+ while (t!=end && isspace(*t)) t++;
+ if (t==end) return t;
+ if (*t==';') {binbuf_addv(x,";"); return t+1;}
+ if (*t==',') {binbuf_addv(x,","); return t+1;}
+ /* if (*t=='"') return binbuf_text_quoted(x,t,end); */
+ if (*t=='+' || *t=='-' || *t=='.' || isdigit(*t)) {
+ char *token;
+ double v = strtod(t,&token);
+ if (t==end || isspace(*token)) {binbuf_addv(x,"f",v); return token;}
+ }
+ ostringstream buf;
+ for (; t!=end && *t!=',' && *t!=';' && !isspace(*t); ) {
+ doll |= t[0]=='$' && t+1!=end && isdigit(t[1]);
+ if (*t=='\\') t++;
+ if (t!=end) buf << *t++;
+ }
+ if (doll) {
+ const char *b = buf.str().data();
+ if (b[0]!='$') doll=0;
+ for (b++; *b; b++) if (!isdigit(*b)) doll=0;
+ if (doll) binbuf_addv(x,"$",atoi(buf.str().data()+1));
+ else binbuf_addv(x,"&",gensym(buf.str().data()));
+ } else binbuf_addv(x,"t",buf.str().data());
+ return t;
+}
+
+/* this one is for pd format version 0 */
+char *binbuf_text_miller(t_binbuf *x, char *t, char *end) {
+ ostringstream buf;
+ /* it's an atom other than a comma or semi */
+ int q = 0, slash = 0, lastslash = 0, dollar = 0;
+ /* skip leading space */
+ while (t!=end && isspace(*t)) t++;
+ if (t==end) return t;
+ if (*t==';') {binbuf_addv(x,";"); return t+1;}
+ if (*t==',') {binbuf_addv(x,","); return t+1;}
+ do {
+ char c = *t++;
+ lastslash = slash;
+ slash = c=='\\';
+ if (q >= 0) {
+ int digit = isdigit(c), dot=c=='.', minus=c=='-', plusminus=minus||c=='+', expon=c=='e'||c=='E';
+ if (q==0) { /* beginning */ if (minus) q=1; else if (digit) q=2; else if (dot) q=3; else q=-1;}
+ else if (q==1) { /* got minus */ if (digit) q=2; else if (dot) q=3; else q=-1;}
+ else if (q==2) { /* got digits */ if (dot) q=4; else if (expon) q=6; else if (!digit) q=-1;}
+ else if (q==3) { /* got '.' without digits */ if (digit) q=5; else q=-1;}
+ else if (q==4) { /* got '.' after digits */ if (digit) q=5; else if (expon) q=6; else q=-1;}
+ else if (q==5) { /* got digits after . */ if (expon) q=6; else if (!digit) q=-1;}
+ else if (q==6) { /* got 'e' */ if (plusminus) q=7; else if (digit) q=8; else q=-1;}
+ else if (q==7) { /* got plus or minus */ if (digit) q=8; else q=-1;}
+ else if (q==8) { /* got digits */ if (!digit) q=-1;}
+ }
+ if (!lastslash && c == '$' && t!=end && isdigit(*t)) dollar = 1;
+#if 1
+ if (slash&&lastslash) slash=0;
+#endif
+ if (!slash) buf << c;
+ } while (t!=end && (slash || !strchr(" \n\r\t,;",*t)));
+ if (q == 2 || q == 4 || q == 5 || q == 8) {binbuf_addv(x,"f",atof(buf.str().data())); return t;}
+ /* LATER try to figure out how to mix "$" and "\$" correctly; here, the backslashes were already
+ stripped so we assume all "$" chars are real dollars. In fact, we only know at least one was. */
+ if (dollar) {
+ const char *b = buf.str().data();
+ if (*b != '$') dollar = 0;
+ for (b++; *b; b++) if (!isdigit(*b)) dollar = 0;
+ if (dollar) binbuf_addv(x,"$",atoi(buf.str().data()+1));
+ else binbuf_addv(x,"&",gensym(buf.str().data()));
+ } else binbuf_addv(x,"t",buf.str().data());
+ return t;
+}
+
+int sys_syntax = 0;
+
+void binbuf_text(t_binbuf *x, char *t, size_t size) {
+ char *end=t+size;
+ binbuf_clear(x);
+ while (t!=end) t = sys_syntax ? binbuf_text_matju(x,t,end) : binbuf_text_miller(x,t,end);
+ binbuf_capa(x,x->n);
+}
+
+void pd_eval_text(char *t, size_t size) {
+ t_binbuf *x = binbuf_new();
+ char *end = t+size;
+ while (t!=end) {
+ t = sys_syntax ? binbuf_text_matju(x,t,end) : binbuf_text_miller(x,t,end);
+ if (x->n && x->v[x->n-1].a_type == A_SEMI) {
+ binbuf_eval(x,0,0,0);
+ binbuf_clear(x);
+ }
+ }
+ binbuf_free(x);
+}
+
+void voprintf(ostream &buf, const char *s, va_list args) {
+ char *b;
+ vasprintf(&b,s,args);
+ buf << b;
+ free(b);
+}
+void oprintf(ostream &buf, const char *s, ...) {
+ va_list args;
+ va_start(args,s);
+ voprintf(buf,s,args);
+ va_end(args);
+}
+
+/* convert a binbuf to text; no null termination. */
+void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp) {
+ ostringstream buf;
+ t_atom *ap = x->v;
+ char nextdelim=0;
+ for (int i=x->n; i--; ap++) {
+ if (ap->a_type != A_SEMI && ap->a_type != A_COMMA && nextdelim) buf << (char)nextdelim;
+ atom_ostream(ap,buf);
+ nextdelim = ap->a_type == A_SEMI ? '\n' : ' ';
+ }
+ //if (nextdelim) buf << (char)nextdelim;
+ *bufp = strdup(buf.str().data());
+ *lengthp = buf.str().size();// - (nextdelim == ' ');
+}
+
+/* convert a binbuf to text with null termination, as return value */
+char *binbuf_gettext2(t_binbuf *x) {
+ char *buf; int n;
+ binbuf_gettext(x,&buf,&n);
+ buf[n] = 0;
+ return (char *)realloc(buf,n+1);
+}
+
+/* Miller said: fix this so that writing to file doesn't buffer everything together. */
+/* matju said: make this use vector size doubling as it used to be in binbuf_text */
+void binbuf_add(t_binbuf *x, int argc, t_atom *argv) {
+ int newsize = x->n + argc;
+ t_atom *ap = (t_atom *)realloc(x->v,newsize*sizeof(*x->v));
+ x->v = ap;
+ ap += x->n;
+ for (int i = argc; i--; ap++) *ap = *(argv++);
+ x->capa = x->n = newsize;
+}
+
+#define MAXADDMESSV 100
+void binbuf_addv(t_binbuf *x, char *fmt, ...) {
+ va_list ap;
+ t_atom arg[MAXADDMESSV], *at =arg;
+ int nargs = 0;
+ char *fp = fmt;
+ va_start(ap, fmt);
+ while (1) {
+ if (nargs >= MAXADDMESSV) {
+ error("binbuf_addmessv: only %d allowed", MAXADDMESSV);
+ break;
+ }
+ switch(*fp++) {
+ case 'i': SETFLOAT(at, va_arg(ap, int)); break;
+ case 'f': SETFLOAT(at, va_arg(ap, double)); break;
+ case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break;
+ case 't': SETSYMBOL(at, gensym(va_arg(ap, char *))); break;
+ case ';': SETSEMI(at); break;
+ case ',': SETCOMMA(at); break;
+ case '$': SETDOLLAR(at, va_arg(ap, int)); break;
+ case '&': SETDOLLSYM(at, va_arg(ap, t_symbol *)); break;
+ default: goto done;
+ }
+ at++;
+ nargs++;
+ }
+done:
+ va_end(ap);
+ binbuf_add(x, nargs, arg);
+}
+
+/* add a binbuf to another one for saving. Semicolons and commas go to
+symbols ";", "'",; the symbol ";" goes to "\;", etc. */
+
+void binbuf_addbinbuf(t_binbuf *x, t_binbuf *y) {
+ t_binbuf *z = binbuf_new();
+ binbuf_add(z, y->n, y->v);
+ t_atom *ap = z->v;
+ for (size_t i=0; i < z->n; i++, ap++) {
+ switch (ap->a_type) {
+ case A_FLOAT: break;
+ case A_SEMI: SETSYMBOL(ap, gensym(";")); break;
+ case A_COMMA: SETSYMBOL(ap, gensym(",")); break;
+ case A_DOLLAR: SETSYMBOL(ap, symprintf("$%ld", ap->a_index)); break;
+ case A_DOLLSYM: {
+ ostringstream b;
+ atom_ostream(ap,b);
+ SETSYMBOL(ap, gensym(b.str().data()));} break;
+ case A_SYMBOL:
+ /* FIXME make this general */
+ if (!strcmp(ap->a_symbol->name, ";")) SETSYMBOL(ap, gensym(";"));
+ else if (!strcmp(ap->a_symbol->name, ",")) SETSYMBOL(ap, gensym(","));
+ break;
+ default:
+ //bug("binbuf_addbinbuf: stray atom of type %d",ap->a_type);
+ //abort();
+ ;
+ }
+ }
+ binbuf_add(x, z->n, z->v);
+}
+
+void binbuf_addsemi(t_binbuf *x) {
+ t_atom a;
+ SETSEMI(&a);
+ binbuf_add(x, 1, &a);
+}
+
+/* Supply atoms to a binbuf from a message, making the opposite changes
+from binbuf_addbinbuf. The symbol ";" goes to a semicolon, etc. */
+
+void binbuf_restore(t_binbuf *x, int argc, t_atom *argv) {
+ int newsize = x->n + argc;
+ t_atom *ap = (t_atom *)realloc(x->v,(newsize+1)*sizeof(*x->v));
+ if (!ap) {error("binbuf_addmessage: out of space"); return;}
+ x->v = ap;
+ ap = x->v + x->n;
+ for (int i = argc; i--; ap++) {
+ if (argv->a_type == A_SYMBOL) {
+ char *str = argv->a_symbol->name, *str2;
+ if (!strcmp(str, ";")) SETSEMI(ap);
+ else if (!strcmp(str, ",")) SETCOMMA(ap);
+ else if ((str2 = strchr(str, '$')) && isdigit(str2[1])) {
+ int dollsym = 0;
+ if (*str != '$') dollsym = 1;
+ else for (str2 = str + 1; *str2; str2++) if (!isdigit(*str2)) {
+ dollsym = 1;
+ break;
+ }
+ if (dollsym) SETDOLLSYM(ap, gensym(str));
+ else {
+ int dollar = 0;
+ sscanf(argv->a_symbol->name + 1, "%d", &dollar);
+ SETDOLLAR(ap, dollar);
+ }
+ } else *ap = *argv;
+ argv++;
+ } else *ap = *(argv++);
+ }
+ x->n = newsize;
+}
+
+#define MSTACKSIZE 2048
+
+void binbuf_print(t_binbuf *x) {
+ int startedpost = 0, newline = 1;
+ for (size_t i=0; i < x->n; i++) {
+ if (newline) {
+ if (startedpost) endpost();
+ startpost("");
+ startedpost = 1;
+ }
+ postatom(1, x->v + i);
+ newline = !! x->v[i].a_type == A_SEMI;
+ }
+ if (startedpost) endpost();
+}
+
+int binbuf_getnatom(t_binbuf *x) {return x->n;}
+t_atom *binbuf_getvec(t_binbuf *x) {return x->v;}
+
+int canvas_getdollarzero ();
+
+/* JMZ:
+ * s points to the first character after the $
+ * (e.g. if the org.symbol is "$1-bla", then s will point to "1-bla")
+ * (e.g. org.symbol="hu-$1mu", s="1mu")
+ * LATER: think about more complex $args, like ${$1+3}
+ *
+ * the return value holds the length of the $arg (in most cases: 1)
+ * buf holds the expanded $arg
+ *
+ * if some error occurred, "-1" is returned
+ *
+ * e.g. "$1-bla" with list "10 20 30"
+ * s="1-bla"
+ * buf="10"
+ * return value = 1; (s+1=="-bla")
+ */
+static int binbuf_expanddollsym(char *s, std::ostream &buf, t_atom dollar0, int ac, t_atom *av, int tonew) {
+ int argno=atol(s);
+ int arglen=0;
+ char*cs=s;
+ char c=*cs;
+ while (c && isdigit(c)) {
+ c=*cs++;
+ arglen++;
+ }
+ /* invalid $-expansion (like "$bla") */
+ if (cs==s) {buf << "$"; return 0;}
+ if (argno < 0 || argno > ac) { /* undefined argument */
+ if(!tonew) return 0;
+ buf << "$" << argno;
+ } else if (argno == 0) { /* $0 */
+ atom_ostream(&dollar0, buf);
+ } else { /* fine! */
+ atom_ostream(av+(argno-1), buf);
+ }
+ return arglen-1;
+}
+
+/* LATER remove the dependence on the current canvas for $0; should be another argument. */
+t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) {
+ ostringstream buf2;
+ char *str=s->name;
+ t_atom dollarnull;
+ SETFLOAT(&dollarnull, canvas_getdollarzero());
+ /* JMZ: currently, a symbol is detected to be A_DOLLSYM if it starts with '$'
+ * the leading $ is stripped and the rest stored in "s". i would suggest to NOT strip the leading $
+ * and make everything a A_DOLLSYM that contains(!) a $ whenever this happened, enable this code */
+ char *substr=strchr(str, '$');
+ if(!substr) return s;
+ oprintf(buf2,"%.*s",substr-str,str);
+ str=substr+1;
+ for (;;) {
+ std::ostringstream buf;
+ int next = binbuf_expanddollsym(str, buf, dollarnull, ac, av, tonew);
+ if (next<0) break;
+ /* JMZ: i am not sure what this means, so i might have broken it. it seems like that if "tonew" is
+ set and the $arg cannot be expanded (or the dollarsym is in reality a A_DOLLAR).
+ 0 is returned from binbuf_realizedollsym; this happens when expanding in a message-box,
+ but does not happen when the A_DOLLSYM is the name of a subpatch */
+ /* JMZ: this should mimick the original behaviour */
+ if(!tonew && !next && buf.str().size()==0) return 0;
+ buf2 << buf;
+ str+=next;
+ substr=strchr(str, '$');
+ if(substr) {
+ oprintf(buf2,"%.*s",substr-str,str);
+ str=substr+1;
+ } else {
+ buf2 << str;
+ return gensym(buf2.str().data());
+ }
+ }
+ return gensym(buf2.str().data());
+}
+
+void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv) {
+ static t_atom mstack[MSTACKSIZE], *msp = mstack, *ems = mstack+MSTACKSIZE;
+ t_atom *stackwas = msp;
+ t_atom *at = x->v;
+ int ac = x->n;
+ int nargs;
+ while (1) {
+ t_pd *nexttarget;
+ while (!target) {
+ t_symbol *s;
+ while (ac && (at->a_type == A_SEMI || at->a_type == A_COMMA)) {ac--; at++;}
+ if (!ac) break;
+ if (at->a_type == A_DOLLAR) {
+ if (at->a_index <= 0 || at->a_index > argc) {error("$%d: not enough arguments supplied", at->a_index); goto cleanup;}
+ else if (argv[at->a_index-1].a_type != A_SYMBOL) {error("$%d: symbol needed as receiver", at->a_index); goto cleanup;}
+ else s = argv[at->a_index-1].a_symbol;
+ } else if (at->a_type == A_DOLLSYM) {
+ s = binbuf_realizedollsym(at->a_symbol, argc, argv, 0);
+ if (!s) {error("$%s: not enough arguments supplied", at->a_symbol->name); goto cleanup;}
+ } else s = atom_getsymbol(at);
+ target = s->thing;
+ /* IMPD: allows messages to unbound objects, via pointers */
+ if (!target) {
+ if (!sscanf(s->name,".x%lx",(long*)&target)) target=0;
+ if (target) {
+ if (!object_table->exists(target) || !object_table->get(target)) {
+ error("%s target is not a currently valid pointer",s->name);
+ return;
+ }
+ }
+ }
+ if (!target) {error("%s: no such object", s->name); goto cleanup;}
+ at++;
+ ac--;
+ break;
+ cleanup:
+ do {at++; ac--;} while (ac && at->a_type != A_SEMI); /* is this the correct thing to do? */
+ continue;
+ }
+ if (!ac) break;
+ nargs = 0;
+ nexttarget = target;
+ while (1) {
+ if (!ac) goto gotmess;
+ if (msp >= ems) {error("message too long"); goto broken;}
+ switch (at->a_type) {
+ /* semis and commas in new message just get bashed to a symbol. This is needed so you can pass them to "expr." */
+ case A_SEMI: if (target == &pd_objectmaker) {SETSYMBOL(msp, gensym(";")); break;} else {nexttarget = 0; goto gotmess;}
+ case A_COMMA: if (target == &pd_objectmaker) {SETSYMBOL(msp, gensym(",")); break;} else goto gotmess;
+ case A_FLOAT:
+ case A_SYMBOL:
+ *msp = *at;
+ break;
+ case A_DOLLAR:
+ if (at->a_index > 0 && at->a_index <= argc) *msp = argv[at->a_index-1];
+ else if (at->a_index == 0) SETFLOAT(msp, canvas_getdollarzero());
+ else {
+ SETFLOAT(msp, 0);
+ if (target != &pd_objectmaker) error("$%d: argument number out of range", at->a_index);
+ }
+ break;
+ case A_DOLLSYM: {
+ t_symbol *s9 = binbuf_realizedollsym(at->a_symbol, argc, argv, target == &pd_objectmaker);
+ if (!s9) {
+ error("%s: argument number out of range", at->a_symbol->name);
+ SETSYMBOL(msp, at->a_symbol);
+ } else SETSYMBOL(msp, s9);
+ break;}
+ default:
+ bug("bad item in binbuf");
+ goto broken;
+ }
+ msp++;
+ ac--;
+ at++;
+ nargs++;
+ }
+ gotmess:
+ if (nargs) {
+ switch (stackwas->a_type) {
+ case A_SYMBOL: typedmess(target, stackwas->a_symbol, nargs-1, stackwas+1); break;
+ case A_FLOAT: if (nargs == 1) pd_float(target, stackwas->a_float); else pd_list(target, 0, nargs, stackwas); break;
+ default: {}
+ }
+ }
+ msp = stackwas;
+ if (!ac) break;
+ target = nexttarget;
+ at++;
+ ac--;
+ }
+ return;
+broken:
+ msp = stackwas;
+}
+
+static int binbuf_doopen(char *s, int mode) {
+ char namebuf[strlen(s)+1];
+#ifdef MSW
+ mode |= O_BINARY;
+#endif
+ sys_bashfilename(s, namebuf);
+ return open(namebuf, mode);
+}
+
+static FILE *binbuf_dofopen(const char *s, char *mode) {
+ char namebuf[strlen(s)+1];
+ sys_bashfilename(s, namebuf);
+ return fopen(namebuf, mode);
+}
+
+int binbuf_read(t_binbuf *b, char *filename, char *dirname, int flags) {
+ long length;
+ char *buf;
+ char *namebuf=0;
+ if (*dirname) asprintf(&namebuf,"%s/%s",dirname,filename);
+ else asprintf(&namebuf, "%s", filename);
+ int fd = binbuf_doopen(namebuf, 0);
+ if (fd < 0) {error("open: %s: %s",namebuf,strerror(errno)); return 1;}
+ if ((length = lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0 || !(buf = (char *)malloc(length))) {
+ error("lseek: %s: %s",namebuf,strerror(errno));
+ close(fd); free(namebuf);
+ return 1;
+ }
+ int readret = read(fd, buf, length);
+ if (readret < length) {
+ error("read (%d %ld) -> %d; %s: %s", fd, length, readret, namebuf, strerror(errno));
+ close(fd); free(namebuf); free(buf);
+ return 1;
+ }
+ if (flags&1) for (int i=0; i<length; i++) if (buf[i]=='\n') buf[i] = ';';
+ if (flags&2) pd_eval_text(buf,length); else binbuf_text(b, buf, length);
+ close(fd); free(namebuf); free(buf);
+ return 0;
+}
+
+/* read a binbuf from a file, via the search patch of a canvas */
+int binbuf_read_via_canvas(t_binbuf *b, char *filename, t_canvas *canvas, int flags) {
+ char *buf, *bufptr;
+ int fd = canvas_open2(canvas, filename, "", &buf, &bufptr, 0);
+ if (fd<0) {error("%s: can't open", filename); return 1;}
+ close(fd); free(buf);
+ return !!binbuf_read(b, bufptr, buf, flags);
+}
+
+/* old version */
+int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname, int flags) {
+ char *buf, *bufptr;
+ int fd = open_via_path2(dirname, filename, "", &buf, &bufptr, 0);
+ if (fd<0) {error("%s: can't open", filename); return 1;}
+ close(fd);
+ bool r = binbuf_read(b, bufptr, buf, flags);
+ free(buf);
+ return r;
+}
+
+#define WBUFSIZE 4096
+static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd);
+
+/* write a binbuf to a text file. If "crflag" is set we suppress semicolons. */
+int binbuf_write(t_binbuf *x, char *filename, char *dir, int crflag) {
+ char sbuf[WBUFSIZE];
+ ostringstream fbuf;
+ char *bp = sbuf, *ep = sbuf + WBUFSIZE;
+ int indx; bool deleteit = 0;
+ int ncolumn = 0;
+ if (*dir) fbuf << dir << "/";
+ fbuf << filename;
+ if (!strcmp(filename + strlen(filename) - 4, ".pat")) {
+ x = binbuf_convert(x, 0);
+ deleteit = 1;
+ }
+ FILE *f = binbuf_dofopen(fbuf.str().data(), "w");
+ if (!f) {error("open: %s: %s",fbuf.str().data(),strerror(errno)); goto fail;}
+ indx = x->n;
+ for (t_atom *ap = x->v; indx--; ap++) {
+ /* estimate how many characters will be needed. Printing out symbols may need extra characters for inserting backslashes. */
+ int length = (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM) ? 80 + strlen(ap->a_symbol->name) : 40;
+ if (ep - bp < length) {
+ if (fwrite(sbuf, bp-sbuf, 1, f) < 1) {error("write: %s: %s",fbuf.str().data(),strerror(errno)); goto fail;}
+ bp = sbuf;
+ }
+ if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && bp > sbuf && bp[-1] == ' ') bp--;
+ if (!crflag || ap->a_type != A_SEMI) {
+ atom_string(ap, bp, (ep-bp)-2);
+ length = strlen(bp);
+ bp += length;
+ ncolumn += length;
+ }
+ if (ap->a_type == A_SEMI || (!crflag && ncolumn > 65)) {
+ *bp++ = '\n';
+ ncolumn = 0;
+ } else {
+ *bp++ = ' ';
+ ncolumn++;
+ }
+ }
+ if (fwrite(sbuf, bp-sbuf, 1, f) < 1) {error("write: %s: %s",fbuf.str().data(),strerror(errno)); goto fail;}
+ if (deleteit) binbuf_free(x);
+ fclose(f);
+ return 0;
+fail:
+ if (deleteit) binbuf_free(x);
+ if (f) fclose(f);
+ return 1;
+}
+
+/* The following routine attempts to convert from max to pd or back. The max to pd direction is working OK
+ but you will need to make lots of abstractions for objects like "gate" which don't exist in Pd. Conversion
+ from Pd to Max hasn't been tested for patches with subpatches yet! */
+#define MAXSTACK 1000
+#define ISSYMBOL(a, b) ((a)->a_type == A_SYMBOL && !strcmp((a)->a_symbol->name, (b)))
+#define GETF(i) atom_getfloatarg(i,natom,nextmess)
+static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd) {
+ t_binbuf *newb = binbuf_new();
+ t_atom *vec = oldb->v;
+ t_int n = oldb->n, nextindex, stackdepth = 0, stack[MAXSTACK], nobj = 0;
+ t_atom outmess[MAXSTACK], *nextmess;
+ if (!maxtopd) binbuf_addv(newb,"tt;","max","v2");
+ for (nextindex = 0; nextindex < n; ) {
+ int endmess, natom;
+ for (endmess = nextindex; endmess < n && vec[endmess].a_type != A_SEMI; endmess++) {}
+ if (endmess == n) break;
+ if (endmess == nextindex || endmess == nextindex + 1
+ || vec[nextindex].a_type != A_SYMBOL || vec[nextindex+1].a_type != A_SYMBOL) {
+ nextindex = endmess + 1;
+ continue;
+ }
+ natom = endmess - nextindex;
+ if (natom > MAXSTACK-10) natom = MAXSTACK-10;
+ nextmess = vec + nextindex;
+ char *first = nextmess ->a_symbol->name;
+ char *second = (nextmess+1)->a_symbol->name;
+ if (maxtopd) { /* case 1: importing a ".pat" file into Pd. */
+ /* dollar signs in file translate to symbols */
+ for (int i=0; i<natom; i++) {
+ if (nextmess[i].a_type == A_DOLLAR) {
+ SETSYMBOL(nextmess+i, symprintf("$%ld",nextmess[i].a_index));
+ } else if (nextmess[i].a_type == A_DOLLSYM) {
+ SETSYMBOL(nextmess+i, gensym(nextmess[i].a_symbol->name));
+ }
+ }
+ if (!strcmp(first, "#N")) {
+ if (!strcmp(second, "vpatcher")) {
+ if (stackdepth >= MAXSTACK) {
+ post("too many embedded patches");
+ return newb;
+ }
+ stack[stackdepth] = nobj;
+ stackdepth++;
+ nobj = 0;
+ binbuf_addv(newb,"ttfffff;","#N","canvas", GETF(2), GETF(3), GETF(4)-GETF(2), GETF(5)-GETF(3), 10.);
+ }
+ }
+ if (!strcmp(first, "#P")) {
+ /* drop initial "hidden" flag */
+ if (!strcmp(second, "hidden")) {
+ nextmess++;
+ natom--;
+ second = (nextmess+1)->a_symbol->name;
+ }
+ if (natom >= 7 && !strcmp(second, "newobj")
+ && (ISSYMBOL(&nextmess[6], "patcher") || ISSYMBOL(&nextmess[6], "p"))) {
+ binbuf_addv(newb,"ttffts;","#X","restore", GETF(2), GETF(3),
+ "pd", atom_getsymbolarg(7, natom, nextmess));
+ if (stackdepth) stackdepth--;
+ nobj = stack[stackdepth];
+ nobj++;
+ } else if (!strcmp(second, "newex") || !strcmp(second, "newobj")) {
+ t_symbol *classname = atom_getsymbolarg(6, natom, nextmess);
+ if (classname == gensym("trigger") || classname == gensym("t")) {
+ for (int i=7; i<natom; i++)
+ if (nextmess[i].a_type == A_SYMBOL && nextmess[i].a_symbol == gensym("i"))
+ nextmess[i].a_symbol = gensym("f");
+ }
+ if (classname == gensym("table")) classname = gensym("TABLE");
+ SETSYMBOL(outmess, gensym("#X"));
+ SETSYMBOL(outmess + 1, gensym("obj"));
+ outmess[2] = nextmess[2];
+ outmess[3] = nextmess[3];
+ SETSYMBOL(outmess+4, classname);
+ for (int i=7; i<natom; i++) outmess[i-2] = nextmess[i];
+ SETSEMI(outmess + natom - 2);
+ binbuf_add(newb, natom - 1, outmess);
+ nobj++;
+ } else if (!strcmp(second, "message") || !strcmp(second, "comment")) {
+ SETSYMBOL(outmess, gensym("#X"));
+ SETSYMBOL(outmess + 1, gensym((char *)(strcmp(second, "message") ? "text" : "msg")));
+ outmess[2] = nextmess[2];
+ outmess[3] = nextmess[3];
+ for (int i=6; i<natom; i++) outmess[i-2] = nextmess[i];
+ SETSEMI(outmess + natom - 2);
+ binbuf_add(newb, natom - 1, outmess);
+ nobj++;
+ } else if (!strcmp(second, "button")) {
+ binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3),"bng");
+ nobj++;
+ } else if (!strcmp(second, "number") || !strcmp(second, "flonum")) {
+ binbuf_addv(newb,"ttff;","#X","floatatom",GETF(2),GETF(3));
+ nobj++;
+ } else if (!strcmp(second, "slider")) {
+ float inc = GETF(7);
+ if (inc <= 0) inc = 1;
+ binbuf_addv(newb, "ttfftfffffftttfffffffff;","#X","obj",
+ GETF(2), GETF(3), "vsl", GETF(4), GETF(5), GETF(6), GETF(6)+(GETF(5)-1)*inc,
+ 0., 0., "empty", "empty", "empty", 0., -8., 0., 8., -262144., -1., -1., 0., 1.);
+ nobj++;
+ } else if (!strcmp(second, "toggle")) {
+ binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3),"tgl");
+ nobj++;
+ } else if (!strcmp(second, "inlet")) {
+ binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3), natom > 5 ? "inlet~" : "inlet");
+ nobj++;
+ } else if (!strcmp(second, "outlet")) {
+ binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3), natom > 5 ? "outlet~" : "outlet");
+ nobj++;
+ } else if (!strcmp(second, "user")) {
+ binbuf_addv(newb,"ttffs;","#X","obj", GETF(3), GETF(4), atom_getsymbolarg(2, natom, nextmess));
+ nobj++;
+ } else if (!strcmp(second, "connect") || !strcmp(second, "fasten")) {
+ binbuf_addv(newb,"ttffff;","#X","connect", nobj-GETF(2)-1, GETF(3), nobj-GETF(4)-1, GETF(5));
+ }
+ }
+ } else { /* Pd to Max */
+ if (!strcmp(first, "#N")) {
+ if (!strcmp(second, "canvas")) {
+ if (stackdepth >= MAXSTACK) {
+ post("too many embedded patches");
+ return newb;
+ }
+ stack[stackdepth] = nobj;
+ stackdepth++;
+ nobj = 0;
+ binbuf_addv(newb,"ttffff;","#N","vpatcher", GETF(2), GETF(3), GETF(4), GETF(5));
+ }
+ }
+ if (!strcmp(first, "#X")) {
+ if (natom >= 5 && !strcmp(second, "restore") && (ISSYMBOL (&nextmess[4], "pd"))) {
+ binbuf_addv(newb,"tt;","#P","pop");
+ binbuf_addv(newb,"ttffffts;","#P","newobj", GETF(2), GETF(3), 50., 1.,
+ "patcher", atom_getsymbolarg(5, natom, nextmess));
+ if (stackdepth) stackdepth--;
+ nobj = stack[stackdepth];
+ nobj++;
+ } else if (!strcmp(second, "obj")) {
+ t_symbol *classname = atom_getsymbolarg(4, natom, nextmess);
+ if (classname == gensym("inlet")) binbuf_addv(newb,"ttfff;","#P","inlet", GETF(2), GETF(3), 15.);
+ else if (classname == gensym("inlet~")) binbuf_addv(newb,"ttffff;","#P","inlet", GETF(2), GETF(3), 15., 1.);
+ else if (classname == gensym("outlet")) binbuf_addv(newb,"ttfff;","#P","outlet", GETF(2), GETF(3), 15.);
+ else if (classname == gensym("outlet~")) binbuf_addv(newb,"ttffff;","#P","outlet", GETF(2), GETF(3), 15., 1.);
+ else if (classname == gensym("bng")) binbuf_addv(newb,"ttffff;","#P","button", GETF(2), GETF(3), GETF(5), 0.);
+ else if (classname == gensym("tgl")) binbuf_addv(newb,"ttffff;","#P","toggle", GETF(2), GETF(3), GETF(5), 0.);
+ else if (classname == gensym("vsl")) binbuf_addv(newb,"ttffffff;","#P","slider",
+ GETF(2), GETF(3), GETF(5), GETF(6), (GETF(8)-GETF(7)) / (GETF(6)==1?1:GETF(6)-1), GETF(7));
+ else {
+ binbuf_addv(newb,"ttffff","#P","newex", GETF(2), GETF(3), 50., 1.);
+ for (int i=4; i<natom; i++) outmess[i-4] = nextmess[i];
+ binbuf_add(newb, natom-4, outmess);
+ binbuf_addv(newb,";");
+ }
+ nobj++;
+ } else if (!strcmp(second, "msg") || !strcmp(second, "text")) {
+ binbuf_addv(newb,"ttffff","#P",strcmp(second, "msg") ? "comment" : "message",GETF(2),GETF(3),50.,1.);
+ for (int i=4; i<natom; i++) outmess[i-4] = nextmess[i];
+ binbuf_add(newb, natom-4, outmess);
+ binbuf_addv(newb,";");
+ nobj++;
+ } else if (!strcmp(second, "floatatom")) {
+ binbuf_addv(newb, "ttfff;", "#P", "flonum", GETF(2), GETF(3), 35);
+ nobj++;
+ } else if (!strcmp(second, "connect")) {
+ binbuf_addv(newb, "ttffff;", "#P", "connect", nobj-GETF(2)-1, GETF(3), nobj-GETF(4)-1, GETF(5));
+ }
+ }
+ }
+ nextindex = endmess + 1;
+ }
+ if (!maxtopd) binbuf_addv(newb, "tt;", "#P", "pop");
+#if 0
+ binbuf_write(newb, "import-result.pd", "/tmp", 0);
+#endif
+ return newb;
+}
+
+/* function to support searching */
+int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf) {
+ for (size_t indexin = 0; indexin <= inbuf->n - searchbuf->n; indexin++) {
+ for (size_t nmatched = 0; nmatched < searchbuf->n; nmatched++) {
+ t_atom *a1 = &inbuf->v[indexin + nmatched], *a2 = &searchbuf->v[nmatched];
+ if (a1->a_type != a2->a_type ||
+ a1->a_type == A_SYMBOL && a1->a_symbol != a2->a_symbol ||
+ a1->a_type == A_FLOAT && a1->a_float != a2->a_float ||
+ a1->a_type == A_DOLLAR && a1->a_index != a2->a_index ||
+ a1->a_type == A_DOLLSYM && a1->a_symbol != a2->a_symbol) goto nomatch;
+ }
+ return 1;
+ nomatch: ;
+ }
+ return 0;
+}
+
+/* LATER figure out how to log errors */
+void binbuf_evalfile(t_symbol *name, t_symbol *dir) {
+ t_binbuf *b = binbuf_new();
+ int import = !strcmp(name->name + strlen(name->name) - 4, ".pat");
+ /* set filename so that new canvases can pick them up */
+ int dspstate = canvas_suspend_dsp();
+ glob_setfilename(0, name, dir);
+ if (import) {
+ if (binbuf_read(b, name->name, dir->name, 0)) {perror(name->name); goto bye;}
+ t_binbuf *newb = binbuf_convert(b, 1);
+ binbuf_free(b);
+ b = newb;
+ } else {
+ if (binbuf_read(b, name->name, dir->name, 2)) perror(name->name);
+ }
+bye:
+ glob_setfilename(0, &s_, &s_); /* bug fix by Krzysztof Czaja */
+ binbuf_free(b);
+ canvas_resume_dsp(dspstate);
+}
+
+void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir) {
+ /* even though binbuf_evalfile appears to take care of dspstate, we have to do it again here, because
+ canvas_startdsp() assumes that all toplevel canvases are visible. LATER: check if this is still necessary (probably not) */
+ int dspstate = canvas_suspend_dsp();
+ binbuf_evalfile(name, dir);
+ t_pd *x = 0;
+ while ((x != s__X.thing) && (x = s__X.thing)) vmess(x, gensym("pop"), "i", 1);
+ if (lastpopped) pd_vmess(lastpopped, gensym("loadbang"), "");
+ lastpopped = 0;
+ canvas_resume_dsp(dspstate);
+}
+
+//copied from m_pd.h
+#define class_new2(NAME,NU,FREE,SIZE,FLAGS,SIG) class_new2(NAME,(t_newmethod)NU,(t_method)FREE,SIZE,FLAGS,SIG)
+
+extern "C" {
+void conf_init();
+void glob_init();
+void boxes_init();
+void garray_init();
+void pd_init() {
+ object_table = new t_hash<t_pd *,long>(127);
+ bindlist_class = class_new(gensym("bindlist"), 0, 0, sizeof(t_bindlist), CLASS_PD, 0);
+ class_addbang(bindlist_class, (t_method)bindlist_bang);
+ class_addfloat(bindlist_class, (t_method)bindlist_float);
+ class_addsymbol(bindlist_class, (t_method)bindlist_symbol);
+ class_addpointer(bindlist_class, (t_method)bindlist_pointer);
+ class_addlist(bindlist_class, (t_method)bindlist_list);
+ class_addanything(bindlist_class, (t_method)bindlist_anything);
+ binbuf_class = class_new2("__list", binbuf_new, binbuf_free2, sizeof(t_binbuf), CLASS_PD, "*");
+ wire_class = class_new2("__wire", wire_new, wire_free, sizeof(t_wire), CLASS_GOBJ, "*");
+ class_setsavefn(wire_class,(t_savefn)wire_save);
+ if (pd_objectmaker._class) bug("ARGH");
+ for (size_t i=0; i<sizeof(symlist)/sizeof(*symlist); i++) {
+ symlist[i]->n = strlen(symlist[i]->name);
+ dogensym(symlist[i]->name, symlist[i]->n, symlist[i]); /* why does this take three args? */
+ }
+ pd_objectmaker._class = class_new2("objectmaker", 0, 0, sizeof(t_pd), CLASS_DEFAULT, "");
+ pd_canvasmaker._class = class_new2("canvasmaker", 0, 0, sizeof(t_pd), CLASS_DEFAULT, "");
+ pd_bind(&pd_canvasmaker, &s__N);
+ class_addanything(pd_objectmaker._class, (t_method)new_anything);
+ obj_init();
+ conf_init();
+ glob_init();
+ boxes_init();
+ garray_init();
+}
+};