From d0f6986345970955d6390a6953c35babf587c262 Mon Sep 17 00:00:00 2001
From: "N.N." <krzyszcz@users.sourceforge.net>
Date: Thu, 19 Feb 2004 22:23:18 +0000
Subject: many small improvements in toxy, plustot added

svn path=/trunk/externals/miXed/; revision=1321
---
 shared/toxy/Makefile.sources |   3 +-
 shared/toxy/plusbob.c        | 371 +++++++++++++++++++++++++++++++++++++++++++
 shared/toxy/plusbob.h        |  71 +++++++++
 shared/toxy/scriptlet.c      | 223 ++++++++++++++++++--------
 shared/toxy/scriptlet.h      |  12 +-
 5 files changed, 607 insertions(+), 73 deletions(-)
 create mode 100644 shared/toxy/plusbob.c
 create mode 100644 shared/toxy/plusbob.h

(limited to 'shared/toxy')

diff --git a/shared/toxy/Makefile.sources b/shared/toxy/Makefile.sources
index 5f34f42..0613017 100644
--- a/shared/toxy/Makefile.sources
+++ b/shared/toxy/Makefile.sources
@@ -1,2 +1,3 @@
 OTHER_SOURCES = \
-scriptlet.c
+scriptlet.c \
+plusbob.c
diff --git a/shared/toxy/plusbob.c b/shared/toxy/plusbob.c
new file mode 100644
index 0000000..fb587cc
--- /dev/null
+++ b/shared/toxy/plusbob.c
@@ -0,0 +1,371 @@
+/* Copyright (c) 2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */
+
+#include <string.h>
+#include "m_pd.h"
+#include "common/loud.h"
+#include "plusbob.h"
+
+//#define PLUSBOB_DEBUG
+
+/* LATER let there be a choice of using either fake-symbols, or gpointers.
+   The gpointer layout would be such:  gs_un points to a plusbob-like
+   structure (without the bob_tag field), a unique integer code has to be
+   reserved for gs_which, the fields gp_un and gp_valid are ignored.
+   Using bob_refcount instead of gs_refcount is likely to simplify code. */
+
+/* Currently, objects of all +bob types are tagged with the same name: */
+static char plustag_name[] = "+bob";
+
+static void plustag_init(t_symbol *tag)
+{
+    tag->s_name = plustag_name;
+    tag->s_thing = 0;
+    tag->s_next = 0;
+}
+
+/* silent if caller is empty */
+int plustag_isvalid(t_symbol *tag, t_pd *caller)
+{
+    if (tag->s_name == plustag_name)
+	return (1);
+    else if (caller)
+    {
+	if (strcmp(tag->s_name, plustag_name))
+	    loud_error((caller == PLUSBOB_OWNER ? 0 : caller),
+ "does not understand '%s' (check object connections)", tag->s_name);
+	else
+	    loud_error((caller == PLUSBOB_OWNER ? 0 : caller), "confused...");
+    }
+    return (0);
+}
+
+/* +bob is an object tossed around, a bobbing object.  Currently, this is
+   a wrapping for Tcl_Interp, Tcl_Obj, or a tcl variable, but the +bob
+   interface is abstract enough to be suitable for other types of objects.
+   The t_plusbob is kind of a virtual base. */
+
+struct _plustype
+{
+    t_plustype   *tp_base;  /* empty, if directly derived from t_plusbob */
+    t_symbol     *tp_name;
+    size_t        tp_size;
+    /* constructor is to be called explicitly, from derived constructors,
+       or from a public wrapper. */
+    t_plustypefn  tp_deletefn;  /* destructor */
+    t_plustypefn  tp_preservefn;
+    t_plustypefn  tp_releasefn;
+    t_plustypefn  tp_attachfn;
+};
+
+static t_plustype *plustype_default = 0;
+
+t_plustype *plustype_new(t_plustype *base, t_symbol *name, size_t sz,
+			 t_plustypefn deletefn,
+			 t_plustypefn preservefn, t_plustypefn releasefn,
+			 t_plustypefn attachfn)
+{
+    t_plustype *tp = getbytes(sizeof(*tp));
+    tp->tp_base = base;
+    tp->tp_name = name;
+    tp->tp_size = sz;
+    tp->tp_deletefn = deletefn;
+    tp->tp_preservefn = preservefn;
+    tp->tp_releasefn = releasefn;
+    tp->tp_attachfn = attachfn;
+    return (tp);
+}
+
+static void plusbob_doattach(t_plusbob *bob, t_plusbob *parent)
+{
+    if (bob->bob_parent = parent)
+    {
+	/* become the youngest child: */
+	bob->bob_prev = 0;
+	if (bob->bob_next = parent->bob_children)
+	{
+	    if (parent->bob_children->bob_prev)
+		bug("plusbob_doattach 1");
+	    parent->bob_children->bob_prev = bob;
+	}
+	parent->bob_children = bob;
+    }
+    else bug("plusbob_doattach 2");
+}
+
+static void plusbob_dodetach(t_plusbob *bob)
+{
+    if (bob->bob_parent)
+    {
+	if (bob->bob_prev)
+	{
+	    if (bob == bob->bob_parent->bob_children)
+		bug("plusbob_dodetach 1");
+	    bob->bob_prev->bob_next = bob->bob_next;
+	}
+	if (bob->bob_next)
+	    bob->bob_next->bob_prev = bob->bob_prev;
+	if (bob == bob->bob_parent->bob_children)
+	    bob->bob_parent->bob_children = bob->bob_next;
+    }
+    else bug("plusbob_dodetach 2");
+}
+
+/* To be called from derived constructors.
+   Preserving is caller's responsibility. */
+t_plusbob *plusbob_create(t_plustype *tp, t_plusbob *parent)
+{
+    t_plusbob *bob;
+    if (!tp)
+    {
+	if (!plustype_default)
+	    plustype_default = plustype_new(0, 0, sizeof(t_plusbob),
+					    0, 0, 0, 0);
+	tp = plustype_default;
+    }
+    if (bob = getbytes(tp->tp_size))
+    {
+	plustag_init(&bob->bob_tag);
+	bob->bob_type = tp;
+	while (tp->tp_base) tp = tp->tp_base;
+	bob->bob_root = tp;
+	bob->bob_owner = 0;
+	bob->bob_refcount = 0;
+	bob->bob_dorefcount = 1;
+	bob->bob_children = 0;
+	if (parent)
+	    plusbob_doattach(bob, parent);
+	else
+	    bob->bob_parent = 0;
+    }
+    return (bob);
+}
+
+/* Should never be called, but from plusbob_release().
+   Calling from a derived destructor is illegal. */
+static void plusbob_free(t_plusbob *bob)
+{
+    t_plustype *tp;
+    if (bob->bob_parent)
+	plusbob_dodetach(bob);
+    for (tp = bob->bob_type; tp; tp = tp->tp_base)
+	if (tp->tp_deletefn) (*tp->tp_deletefn)(bob);
+    freebytes(bob, (bob->bob_type ? bob->bob_type->tp_size : sizeof(*bob)));
+}
+
+void plusbob_preserve(t_plusbob *bob)
+{
+    if (bob->bob_dorefcount)
+    {
+	t_plustype *tp;
+	for (tp = bob->bob_type; tp; tp = tp->tp_base)
+	    if (tp->tp_preservefn) (*tp->tp_preservefn)(bob);
+	bob->bob_refcount++;
+    }
+}
+
+void plusbob_release(t_plusbob *bob)
+{
+    if (bob->bob_dorefcount)
+    {
+	t_plustype *tp;
+	for (tp = bob->bob_type; tp; tp = tp->tp_base)
+	    if (tp->tp_releasefn) (*tp->tp_releasefn)(bob);
+	if (--bob->bob_refcount <= 0)
+	{
+	    if (bob->bob_refcount == 0)
+		plusbob_free(bob);
+	    else
+		bug("plusbob_release");
+	}
+    }
+}
+
+t_plusbob *plusbob_getparent(t_plusbob *bob)
+{
+    return (bob->bob_parent);
+}
+
+/* To be called for redirection only.  Bobs created as orphans are a special
+   case, and cannot be attached later on.  Likewise, changing non-orphan bobs
+   to orphans is illegal. */
+void plusbob_attach(t_plusbob *bob, t_plusbob *newparent)
+{
+    if (bob->bob_parent && newparent)
+    {
+	t_plustype *tp;
+	plusbob_dodetach(bob);
+	plusbob_doattach(bob, newparent);
+	for (tp = bob->bob_type; tp; tp = tp->tp_base)
+	    if (tp->tp_attachfn) (*tp->tp_attachfn)(bob);
+    }
+    else if (newparent)
+	bug("plusbob_attach 1");
+    else
+	bug("plusbob_attach 2");
+}
+
+t_plusbob *plusbob_getnext(t_plusbob *bob)
+{
+    return (bob->bob_next);
+}
+
+t_plusbob *plusbob_getchildren(t_plusbob *bob)
+{
+    return (bob->bob_children);
+}
+
+/* Redirect all bobs to a replacement parent.
+   Assuming replacement exists. */
+void plusbob_detachchildren(t_plusbob *bob, t_plusbob *newparent)
+{
+    while (bob->bob_children)
+	plusbob_attach(bob->bob_children, newparent);
+}
+
+void plusbob_detachownedchildren(t_plusbob *bob, t_plusbob *newparent,
+				 t_pd *owner)
+{
+    t_plusbob *child = bob->bob_children, *next;
+    while (child)
+    {
+	next = child->bob_next;
+	if (child->bob_owner == owner)
+	    plusbob_attach(child, newparent);
+	child = next;
+    }
+}
+
+void plusbob_setowner(t_plusbob *bob, t_pd *owner)
+{
+    bob->bob_owner = owner;
+}
+
+t_pd *plusbob_getowner(t_plusbob *bob)
+{
+    return (bob->bob_owner);
+}
+
+void outlet_plusbob(t_outlet *o, t_plusbob *bob)
+{
+    outlet_symbol(o, (t_symbol *)bob);
+}
+
+/* silent if caller is empty */
+int plustag_validtype(t_symbol *tag, t_symbol *tname, t_pd *caller)
+{
+    if (tag->s_name == plustag_name)
+    {
+	if (((t_plusbob *)tag)->bob_type->tp_name == tname)
+	    return (1);
+	else if (caller)
+	{
+	    t_symbol *s = ((t_plusbob *)tag)->bob_type->tp_name;
+	    loud_error((caller == PLUSBOB_OWNER ?
+			((t_plusbob *)tag)->bob_owner : caller),
+		       "invalid type '%s' ('%s' expected)",
+		       (s ? s->s_name : "<unknown>"),
+		       (tname ? tname->s_name : "<unknown>"));
+	}
+    }
+    else if (plustag_isvalid(tag, caller))  /* print the error there */
+	bug("plustag_validtype");
+    return (0);
+}
+
+/* silent if caller is empty */
+int plustag_validroot(t_symbol *tag, t_symbol *rname, t_pd *caller)
+{
+    if (tag->s_name == plustag_name)
+    {
+	if (((t_plusbob *)tag)->bob_root->tp_name == rname)
+	    return (1);
+	else if (caller)
+	{
+	    t_symbol *s = ((t_plusbob *)tag)->bob_root->tp_name;
+	    loud_error((caller == PLUSBOB_OWNER ?
+			((t_plusbob *)tag)->bob_owner : caller),
+		       "invalid base type '%s' ('%s' expected)",
+		       (s ? s->s_name : "<unknown>"),
+		       (rname ? rname->s_name : "<unknown>"));
+	}
+    }
+    else if (plustag_isvalid(tag, caller))  /* print the error there */
+	bug("plustag_validroot");
+    return (0);
+}
+
+t_symbol *plustag_typename(t_symbol *tag, int validate, t_pd *caller)
+{
+    if (!validate || tag->s_name == plustag_name)
+	return (((t_plusbob *)tag)->bob_type->tp_name);
+    else if (plustag_isvalid(tag, caller))  /* print the error there */
+	bug("plustag_typename");
+    return (0);
+}
+
+t_symbol *plustag_rootname(t_symbol *tag, int validate, t_pd *caller)
+{
+    if (!validate || tag->s_name == plustag_name)
+	return (((t_plusbob *)tag)->bob_root->tp_name);
+    else if (plustag_isvalid(tag, caller))  /* print the error there */
+	bug("plustag_rootname");
+    return (0);
+}
+
+/* Plusenv (aka +env) is the base for an `environment' +bob.  Environment
+   encapsulates data common for a collection of +bobs.  This is the standard
+   way of grouping +bobs, according to a parent/children relationship. */
+
+static t_plustype *plusenv_type = 0;
+static t_plusbob *plusenv_parent = 0;  /* the parent of all environments */
+
+/* To be called from derived constructors (or, LATER, plusenv's provider). */
+t_plusenv *plusenv_create(t_plustype *tp, t_plusbob *parent, t_symbol *id)
+{
+    t_plusenv *env = 0;
+    if (env = (t_plusenv *)plusbob_create(tp, parent))
+    {
+	if (!id)
+	    /* LATER design a public interface for bob_dorefcount */
+	    ((t_plusbob *)env)->bob_dorefcount = 0;
+	env->env_id = id;  /* LATER rethink */
+    }
+    return (env);
+}
+
+t_plusenv *plusenv_find(t_symbol *id, t_plusenv *defenv)
+{
+    if (plusenv_parent && id)
+    {
+	t_plusbob *bob;
+	for (bob = plusenv_parent->bob_children; bob; bob = bob->bob_next)
+	    if (((t_plusenv *)bob)->env_id == id)
+		break;
+	return ((t_plusenv *)bob);
+    }
+    else return (defenv);
+}
+
+t_symbol *plusenv_getid(t_plusenv *env)
+{
+    return (env->env_id);
+}
+
+/* Type ignored, LATER rethink. */
+t_plusbob *plusenv_getparent(t_plustype *tp)
+{
+    if (!plusenv_parent) plusenv_parent = plusbob_create(0, 0);
+    return (plusenv_parent);
+}
+
+t_plustype *plusenv_setup(void)
+{
+    if (!plusenv_type)
+    {
+	plusenv_type = plustype_new(0, gensym("+env"),
+				    sizeof(t_plusenv), 0, 0, 0, 0);
+    }
+    return (plusenv_type);
+}
diff --git a/shared/toxy/plusbob.h b/shared/toxy/plusbob.h
new file mode 100644
index 0000000..bdfe356
--- /dev/null
+++ b/shared/toxy/plusbob.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */
+
+#ifndef __PLUSBOB_H__
+#define __PLUSBOB_H__
+
+EXTERN_STRUCT _plustype;
+#define t_plustype  struct _plustype
+EXTERN_STRUCT _plusbob;
+#define t_plusbob  struct _plusbob
+EXTERN_STRUCT _plusenv;
+#define t_plusenv  struct _plusenv
+
+struct _plusbob
+{
+    t_symbol     bob_tag;   /* common value for all bob types */
+    t_plustype  *bob_type;  /* our type */
+    t_plustype  *bob_root;  /* our base type directly derived from t_plusbob */
+    t_pd        *bob_owner;
+    int          bob_refcount;
+    int          bob_dorefcount;
+    t_plusbob   *bob_children;  /* empty, unless we are a parent */
+    /* each bob has exactly one parent, unless being a special, `orphan' case */
+    t_plusbob   *bob_parent;
+    t_plusbob   *bob_prev;      /* younger brother */
+    t_plusbob   *bob_next;      /* older sister */
+};
+
+struct _plusenv
+{
+    t_plusbob  env_bob;
+    t_symbol  *env_id;  /* LATER use local symbol namespace */
+};
+
+#define PLUSBOB_OWNER  ((t_pd *)-1)
+
+typedef void (*t_plustypefn)(void *);
+
+int plustag_isvalid(t_symbol *s, t_pd *caller);
+
+t_plustype *plustype_new(t_plustype *base, t_symbol *name, size_t sz,
+			 t_plustypefn deletefn,
+			 t_plustypefn preservefn, t_plustypefn releasefn,
+			 t_plustypefn attachfn);
+
+t_plusbob *plusbob_create(t_plustype *tp, t_plusbob *parent);
+void plusbob_preserve(t_plusbob *bob);
+void plusbob_release(t_plusbob *bob);
+t_plusbob *plusbob_getparent(t_plusbob *bob);
+void plusbob_attach(t_plusbob *bob, t_plusbob *newparent);
+t_plusbob *plusbob_getnext(t_plusbob *bob);
+t_plusbob *plusbob_getchildren(t_plusbob *bob);
+void plusbob_detachchildren(t_plusbob *bob, t_plusbob *newparent);
+void plusbob_detachownedchildren(t_plusbob *bob, t_plusbob *newparent,
+				 t_pd *owner);
+void plusbob_setowner(t_plusbob *bob, t_pd *owner);
+t_pd *plusbob_getowner(t_plusbob *bob);
+void outlet_plusbob(t_outlet *o, t_plusbob *bob);
+int plustag_validtype(t_symbol *tag, t_symbol *tname, t_pd *caller);
+int plustag_validroot(t_symbol *tag, t_symbol *rname, t_pd *caller);
+t_symbol *plustag_typename(t_symbol *tag, int validate, t_pd *caller);
+t_symbol *plustag_rootname(t_symbol *tag, int validate, t_pd *caller);
+
+t_plusenv *plusenv_create(t_plustype *tp, t_plusbob *parent, t_symbol *id);
+t_plusenv *plusenv_find(t_symbol *id, t_plusenv *defenv);
+t_symbol *plusenv_getid(t_plusenv *env);
+t_plusbob *plusenv_getparent(t_plustype *tp);
+t_plustype *plusenv_setup(void);
+
+#endif
diff --git a/shared/toxy/scriptlet.c b/shared/toxy/scriptlet.c
index e2f9883..635c106 100644
--- a/shared/toxy/scriptlet.c
+++ b/shared/toxy/scriptlet.c
@@ -28,14 +28,15 @@ enum { SCRIPTLET_CVOK, SCRIPTLET_CVUNKNOWN, SCRIPTLET_CVMISSING };
 
 struct _scriptlet
 {
-    t_pd               *s_owner;
-    t_glist            *s_glist;     /* containing glist (possibly null) */
-    t_symbol           *s_rptarget;  /* reply target */
-    t_symbol           *s_cbtarget;  /* callback target */
-    t_symbol           *s_item;
-    t_scriptlet_cvfn    s_cvfn;
-    t_canvas           *s_cv;
-    int                 s_cvstate;
+    t_pd             *s_owner;
+    t_glist          *s_glist;     /* containing glist (empty allowed) */
+    t_symbol         *s_rptarget;  /* reply target */
+    t_symbol         *s_cbtarget;  /* callback target */
+    t_symbol         *s_item;
+    t_scriptlet_cvfn  s_cvfn;      /* if empty, passing resolveall is a bug */
+    t_canvas         *s_cv;        /* as returned by cvfn */
+    int               s_cvstate;
+    int     s_locked;  /* append lock, for filtering, when reading from file */
     int     s_size;
     char   *s_buffer;
     char    s_bufini[SCRIPTLET_INISIZE];
@@ -83,7 +84,7 @@ static int scriptlet_ready(t_scriptlet *sp)
 
 static int scriptlet_doappend(t_scriptlet *sp, char *buf)
 {
-    if (buf)
+    if (buf && !sp->s_locked)
     {
 	int nprefix = sp->s_head - sp->s_buffer;
 	int nused = sp->s_tail - sp->s_buffer;
@@ -192,38 +193,68 @@ static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, char *obuf,
 	    len = 1;
 	}
 	break;
-    case '~':
+    case '~':  /* FIXME, the dot-tilde stuff is purely experimental. */
 	if (resolveall)
 	{
 	    t_canvas *cv;
 	    if (cv = scriptlet_canvasvalidate(sp, visedonly))
 	    {
-		/* FIXME */
-		if (!strcmp(&ibuf[1], "x1"))
+		if (!strncmp(&ibuf[1], "tag", 3))
+		{
+		    t_rtext *rt;
+		    if (cv->gl_owner && glist_isvisible(cv->gl_owner) &&
+			cv->gl_owner->gl_editor &&
+			(rt = glist_findrtext(cv->gl_owner, (t_object *)cv)))
+			sprintf(obuf, "%s", rtext_gettag(rt));
+		    else
+			obuf[0] = 0;
+		    len = 4;
+		}
+		else if (!strncmp(&ibuf[1], "owner", 5))
+		{
+		    if (cv->gl_owner && glist_isvisible(cv->gl_owner))
+			sprintf(obuf, ".x%x", (int)cv->gl_owner);
+		    else
+			obuf[0] = 0;
+		    len = 6;
+		}
+		else if (!strncmp(&ibuf[1], "root", 4))
+		{
+		    sprintf(obuf, ".x%x", (int)canvas_getrootfor(cv));
+		    len = 5;
+		}
+		/* LATER find out when gl_<coords> are updated,
+		   think how to better sync them to Tk. */
+		else if (!strncmp(&ibuf[1], "x1", 2))
 		{
 		    sprintf(obuf, "%d", cv->gl_screenx1);
 		    len = 3;
 		}
-		else if (!strcmp(&ibuf[1], "x2"))
+		else if (!strncmp(&ibuf[1], "x2", 2))
 		{
 		    sprintf(obuf, "%d", cv->gl_screenx2);
 		    len = 3;
 		}
-		else if (!strcmp(&ibuf[1], "y1"))
+		else if (!strncmp(&ibuf[1], "y1", 2))
 		{
 		    sprintf(obuf, "%d", cv->gl_screeny1);
 		    len = 3;
 		}
-		else if (!strcmp(&ibuf[1], "y2"))
+		else if (!strncmp(&ibuf[1], "y2", 2))
 		{
 		    sprintf(obuf, "%d", cv->gl_screeny2);
 		    len = 3;
 		}
-		else if (!strcmp(&ibuf[1], "edit"))
+		else if (!strncmp(&ibuf[1], "edit", 4))
 		{
 		    sprintf(obuf, "%d", cv->gl_edit);
 		    len = 5;
 		}
+		else if (!strncmp(&ibuf[1], "gop", 3))
+		{
+		    sprintf(obuf, "%d", glist_isgraph(cv));
+		    len = 4;
+		}
 		else loud_error(sp->s_owner, "bad field '%s'", &ibuf[1]);
 	    }
 	}
@@ -249,19 +280,17 @@ static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, char *obuf,
 	{
 	    if (ibuf[1] == ':')
 	    {
-		sprintf(obuf, "{::toxy::callback ");
+		sprintf(obuf, "{pd [concat ");
 		len = 2;
 	    }
 	    else if (ibuf[1] == '|')
 	    {
-		sprintf(obuf, "{::toxy::callback %s ",
-			sp->s_rptarget->s_name);
+		sprintf(obuf, "{pd [concat %s ", sp->s_rptarget->s_name);
 		len = 2;
 	    }
 	    else
 	    {
-		sprintf(obuf, "{::toxy::callback %s _cb ",
-			sp->s_cbtarget->s_name);
+		sprintf(obuf, "{pd [concat %s _cb ", sp->s_cbtarget->s_name);
 		len = 1;
 	    }
 	}
@@ -269,7 +298,7 @@ static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, char *obuf,
     case '>':
 	if (resolveall)
 	{
-	    sprintf(obuf, "}");
+	    sprintf(obuf, "\\;]}");
 	    len = 1;
 	}
 	break;
@@ -285,6 +314,7 @@ int scriptlet_isempty(t_scriptlet *sp)
 void scriptlet_reset(t_scriptlet *sp)
 {
     sp->s_cvstate = SCRIPTLET_CVUNKNOWN;
+    sp->s_locked = 0;
     sp->s_separator = 0;
     strcpy(sp->s_buffer, "namespace eval ::toxy {\
  proc query {} {set ::toxy::reply [\n");
@@ -400,7 +430,7 @@ void scriptlet_qpush(t_scriptlet *sp)
     }
 }
 
-/* Non-expanding -- LATER think if this is likely to cause any confusion.
+/* Non-substituting -- LATER think if this is likely to cause any confusion.
    Especially, consider the widget_vis() vs. widget_update() case. */
 void scriptlet_vpush(t_scriptlet *sp, char *varname)
 {
@@ -496,53 +526,86 @@ char *scriptlet_nextword(char *buf)
     return (0);
 }
 
-static int scriptlet_doread(t_scriptlet *sp, FILE *fp, char *rc,
-			    t_scriptlet_cmntfn cmntfn)
+static int scriptlet_doread(t_scriptlet *sp, t_pd *caller, FILE *fp,
+			    char *rc, char *builtin, t_scriptlet_cmntfn cmntfn)
 {
     t_scriptlet *outsp = sp, *newsp;
     char buf[MAXPDSTRING];
-    scriptlet_reset(outsp);
-    while (!feof(fp))
+    if (!caller) caller = sp->s_owner;
+    while ((fp && !feof(fp) && fgets(buf, MAXPDSTRING - 1, fp))
+	   || builtin)
     {
-	if (fgets(buf, MAXPDSTRING - 1, fp))
+	char *ptr;
+	if (builtin)
 	{
-	    char *ptr = buf;
-	    while (*ptr == ' ' || *ptr == '\t') ptr++;
-	    if (*ptr == '#')
+	    int i;
+	    for (i = 0, ptr = buf; i < MAXPDSTRING - 1; i++, ptr++)
 	    {
-		if (cmntfn)
+		if ((*ptr = (*builtin ? *builtin : '\n')) == '\n')
 		{
-		    char sel = *++ptr;
-		    if (sel && sel != '\n')
+		    ptr[1] = 0;
+		    if (*builtin) builtin++;
+		    if (!*builtin) builtin = 0;
+		    break;
+		}
+		else builtin++;
+	    }
+	}
+	ptr = buf;
+	while (*ptr == ' ' || *ptr == '\t') ptr++;
+	if (*ptr == '#')
+	{
+	    if (cmntfn)
+	    {
+		char sel = *++ptr;
+		if (sel && sel != '\n')
+		{
+		    ptr++;
+		    while (*ptr == ' ' || *ptr == '\t') ptr++;
+		    if (*ptr == '\n')
+			*ptr = 0;
+		    if (*ptr)
+		    {
+			char *ep = ptr + strlen(ptr) - 1;
+			while (*ep == ' ' || *ep == '\t' || *ep == '\n')
+			    ep--;
+			ep[1] = 0;
+		    }
+		    newsp = cmntfn(caller, rc, sel, ptr);
+		    if (newsp == SCRIPTLET_UNLOCK)
+			outsp->s_locked = 0;
+		    else if (newsp == SCRIPTLET_LOCK)
+			outsp->s_locked = 1;
+		    else if (newsp != outsp)
 		    {
-			ptr++;
-			while (*ptr == ' ' || *ptr == '\t') ptr++;
-			if (*ptr == '\n')
-			    *ptr = 0;
-			if (*ptr)
-			{
-			    char *ep = ptr + strlen(ptr) - 1;
-			    while (*ep == ' ' || *ep == '\t' || *ep == '\n')
-				ep--;
-			    ep[1] = 0;
-			}
-			newsp = cmntfn(sp->s_owner, rc, sel, ptr);
-			if (newsp && newsp != outsp)
-			    scriptlet_reset(outsp = newsp);
+			outsp->s_locked = 0;
+			scriptlet_reset(outsp = newsp);
 		    }
 		}
 	    }
-	    else if (*ptr && *ptr != '\n')
-		scriptlet_doappend(outsp, buf);
 	}
-	else break;
+	else if (*ptr && *ptr != '\n')
+	    scriptlet_doappend(outsp, buf);
     }
+    outsp->s_locked = 0;
     return (SCRIPTLET_OK);
 }
 
-int scriptlet_rcload(t_scriptlet *sp, char *rc, char *ext,
-		     t_scriptlet_cmntfn cmntfn)
+/* Load particular section(s) from buffer (skip up to an unlocking comment,
+   keep appending up to a locking comment, repeat). */
+int scriptlet_rcparse(t_scriptlet *sp, t_pd *caller, char *rc, char *contents,
+		      t_scriptlet_cmntfn cmntfn)
 {
+    int result;
+    sp->s_locked = 1;  /* see scriptlet_doread() above for unlocking scheme */
+    result = scriptlet_doread(sp, caller, 0, rc, contents, cmntfn);
+    return (result);
+}
+
+int scriptlet_rcload(t_scriptlet *sp, t_pd *caller, char *rc, char *ext,
+		     char *builtin, t_scriptlet_cmntfn cmntfn)
+{
+    int result;
     char filename[MAXPDSTRING], buf[MAXPDSTRING], *nameptr, *dir;
     int fd;
     if (sp->s_glist)
@@ -551,32 +614,41 @@ int scriptlet_rcload(t_scriptlet *sp, char *rc, char *ext,
 	dir = "";
     if ((fd = open_via_path(dir, rc, ext, buf, &nameptr, MAXPDSTRING, 0)) < 0)
     {
-    	return (SCRIPTLET_NOFILE);
+	result = SCRIPTLET_NOFILE;
     }
     else
     {
 	FILE *fp;
     	close(fd);
-	strcpy(filename, buf);
-	strcat(filename, "/");
-	strcat(filename, nameptr);
-	sys_bashfilename(filename, filename);
+	if (nameptr != buf)
+	{
+	    strcpy(filename, buf);
+	    strcat(filename, "/");
+	    strcat(filename, nameptr);
+	    sys_bashfilename(filename, filename);
+	}
+	else sys_bashfilename(nameptr, filename);
 	if (fp = fopen(filename, "r"))
 	{
-	    int result = scriptlet_doread(sp, fp, rc, cmntfn);
+	    result = scriptlet_doread(sp, caller, fp, rc, 0, cmntfn);
 	    fclose(fp);
-	    return (result);
 	}
 	else
 	{
 	    bug("scriptlet_rcload");
-	    return (SCRIPTLET_NOFILE);
+	    result = SCRIPTLET_NOFILE;
 	}
     }
+    if (result != SCRIPTLET_OK)
+    {
+	scriptlet_doread(sp, caller, 0, rc, builtin, cmntfn);
+    }
+    return (result);
 }
 
 int scriptlet_read(t_scriptlet *sp, t_symbol *fn)
 {
+    int result;
     FILE *fp;
     char buf[MAXPDSTRING];
     post("loading scriptlet file \"%s\"", fn->s_name);
@@ -587,15 +659,16 @@ int scriptlet_read(t_scriptlet *sp, t_symbol *fn)
     sys_bashfilename(buf, buf);
     if (fp = fopen(buf, "r"))
     {
-	int result = scriptlet_doread(sp, fp, 0, 0);
+	scriptlet_reset(sp);
+	result = scriptlet_doread(sp, 0, fp, 0, 0, 0);
 	fclose(fp);
-	return (result);
     }
     else
     {
 	loud_error(sp->s_owner, "error while loading file \"%s\"", fn->s_name);
-    	return (SCRIPTLET_NOFILE);
+    	result = SCRIPTLET_NOFILE;
     }
+    return (result);
 }
 
 int scriptlet_write(t_scriptlet *sp, t_symbol *fn)
@@ -640,11 +713,22 @@ char *scriptlet_getbuffer(t_scriptlet *sp, int *sizep)
     return (sp->s_buffer);
 }
 
+void scriptlet_setowner(t_scriptlet *sp, t_pd *owner)
+{
+    sp->s_owner = owner;
+}
+
 void scriptlet_clone(t_scriptlet *to, t_scriptlet *from)
 {
     scriptlet_reset(to);
     to->s_separator = ' ';
-    /* LATER use from's buffer with refcount */
+    /* LATER add a flag to optionally use from's buffer with refcount */
+    scriptlet_doappend(to, from->s_head);
+}
+
+void scriptlet_append(t_scriptlet *to, t_scriptlet *from)
+{
+    to->s_separator = ' ';
     scriptlet_doappend(to, from->s_head);
 }
 
@@ -658,8 +742,11 @@ void scriptlet_free(t_scriptlet *sp)
     }
 }
 
+/* The parameter 'gl' (null accepted) is necessary, because the 's_glist'
+   field, if implicitly set, would be dangerous (after a glist is gone)
+   and confusing (current directory used for i/o of a global scriptlet). */
 t_scriptlet *scriptlet_new(t_pd *owner, t_symbol *rptarget, t_symbol *cbtarget,
-			   t_symbol *item, t_scriptlet_cvfn cvfn)
+			   t_symbol *item, t_glist *gl, t_scriptlet_cvfn cvfn)
 {
     t_scriptlet *sp = getbytes(sizeof(*sp));
     if (sp)
@@ -667,12 +754,10 @@ t_scriptlet *scriptlet_new(t_pd *owner, t_symbol *rptarget, t_symbol *cbtarget,
 	static int configured = 0;
 	if (!configured)
 	{
-	    sys_gui("namespace eval ::toxy {\
- proc callback {args} {pd $args \\;}}\n");
 	    sys_gui("image create bitmap ::toxy::img::empty -data {}\n");
 	}
 	sp->s_owner = owner;
-	sp->s_glist = canvas_getcurrent();
+	sp->s_glist = gl;
 	sp->s_rptarget = rptarget;
 	sp->s_cbtarget = cbtarget;
 	sp->s_item = item;
diff --git a/shared/toxy/scriptlet.h b/shared/toxy/scriptlet.h
index 336d729..b284797 100644
--- a/shared/toxy/scriptlet.h
+++ b/shared/toxy/scriptlet.h
@@ -7,6 +7,8 @@
 
 enum { SCRIPTLET_OK = 0, SCRIPTLET_NOFILE, SCRIPTLET_BADFILE,
        SCRIPTLET_IGNORED };
+#define SCRIPTLET_UNLOCK  ((t_scriptlet *)0)
+#define SCRIPTLET_LOCK    ((t_scriptlet *)1)
 
 EXTERN_STRUCT _scriptlet;
 #define t_scriptlet  struct _scriptlet
@@ -30,15 +32,19 @@ void scriptlet_vpush(t_scriptlet *sp, char *varname);
 int scriptlet_evaluate(t_scriptlet *insp, t_scriptlet *outsp,
 		       int visedonly, int ac, t_atom *av, t_props *argprops);
 char *scriptlet_nextword(char *buf);
-int scriptlet_rcload(t_scriptlet *sp, char *rc, char *ext,
-		     t_scriptlet_cmntfn cmntfn);
+int scriptlet_rcparse(t_scriptlet *sp, t_pd *caller, char *rc, char *contents,
+		      t_scriptlet_cmntfn cmntfn);
+int scriptlet_rcload(t_scriptlet *sp, t_pd *caller, char *rc, char *ext,
+		     char *builtin, t_scriptlet_cmntfn cmntfn);
 int scriptlet_read(t_scriptlet *sp, t_symbol *fn);
 int scriptlet_write(t_scriptlet *sp, t_symbol *fn);
 char *scriptlet_getcontents(t_scriptlet *sp, int *lenp);
 char *scriptlet_getbuffer(t_scriptlet *sp, int *sizep);
+void scriptlet_setowner(t_scriptlet *sp, t_pd *owner);
 void scriptlet_clone(t_scriptlet *to, t_scriptlet *from);
+void scriptlet_append(t_scriptlet *to, t_scriptlet *from);
 void scriptlet_free(t_scriptlet *sp);
 t_scriptlet *scriptlet_new(t_pd *owner, t_symbol *rptarget, t_symbol *cbtarget,
-			   t_symbol *item, t_scriptlet_cvfn cvfn);
+			   t_symbol *item, t_glist *gl, t_scriptlet_cvfn cvfn);
 
 #endif
-- 
cgit v1.2.1