diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/Makefile.dirs | 2 | ||||
-rw-r--r-- | shared/common/props.c | 518 | ||||
-rw-r--r-- | shared/common/props.h | 26 | ||||
-rw-r--r-- | shared/toxy/Makefile | 4 | ||||
-rw-r--r-- | shared/toxy/Makefile.objects | 0 | ||||
-rw-r--r-- | shared/toxy/Makefile.sources | 2 | ||||
-rw-r--r-- | shared/toxy/scriptlet.c | 666 | ||||
-rw-r--r-- | shared/toxy/scriptlet.h | 42 |
8 files changed, 1259 insertions, 1 deletions
diff --git a/shared/Makefile.dirs b/shared/Makefile.dirs index d9be5aa..5764f41 100644 --- a/shared/Makefile.dirs +++ b/shared/Makefile.dirs @@ -1 +1 @@ -MIXED_DIRS = common hammer sickle toys unstable +MIXED_DIRS = common hammer sickle toxy unstable diff --git a/shared/common/props.c b/shared/common/props.c new file mode 100644 index 0000000..6b6181a --- /dev/null +++ b/shared/common/props.c @@ -0,0 +1,518 @@ +/* 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/grow.h" +#include "common/props.h" + +//#define PROPS_DEBUG + +#define PROPS_INISIZE 32 /* LATER rethink */ +#define PROPS_MAXOTHERS 32 + +enum { PROPS_NONE = 0, PROPS_THIS, PROPS_OTHER }; + +typedef struct _propelem +{ + char *e_key; + char *e_value; + struct _propelem *e_next; +} t_propelem; + +struct _props +{ + char p_thisescape; + char *p_thisinitial; + char *p_name; + int p_size; /* as allocated */ + int p_natoms; /* as used */ + t_atom *p_buffer; + t_atom p_bufini[PROPS_INISIZE]; + t_pd *p_owner; + t_propsresolver p_resolver; + t_propelem *p_dict; + t_propelem *p_nextelem; + int p_badupdate; + char p_otherescapes[PROPS_MAXOTHERS]; + t_props *p_otherprops; /* props list's head */ + t_props *p_next; +}; + +/* Dictionary of properties, p_dict, meant to be nothing more, but an + optimalization detail, is handled implicitly, through its owning t_props. + This optimalization has to be enabled by passing a nonzero 'resolver' + argument to props_new(). + Since p_dict stores resolved strings, it is a secondary, `shallow' storage, + which has to be synced to its master, p_buffer of atoms. + Currently, p_dict is implemented as an unsorted linked list, which should + be fine in most cases (but might need revisiting LATER). */ + +static t_propelem *propelem_new(char *key, char *value) +{ + t_propelem *ep = (t_propelem *)getbytes(sizeof(*ep)); + ep->e_key = getbytes(strlen(key) + 1); + strcpy(ep->e_key, key); + ep->e_value = getbytes(strlen(value) + 1); + strcpy(ep->e_value, value); + ep->e_next = 0; + return (ep); +} + +static void propelem_free(t_propelem *ep) +{ + if (ep->e_key) freebytes(ep->e_key, strlen(ep->e_key) + 1); + if (ep->e_value) freebytes(ep->e_value, strlen(ep->e_value) + 1); + freebytes(ep, sizeof(*ep)); +} + +/* Returns zero if the key was found (and value replaced), + nonzero if a new element was added. */ +static t_propelem *propelem_add(t_propelem *ep, char *key, char *value) +{ + while (ep) + { + if (strcmp(ep->e_key, key)) + ep = ep->e_next; + else + break; + } + if (ep) + { + if (strcmp(ep->e_value, value)) + { + if (ep->e_value) + ep->e_value = resizebytes(ep->e_value, strlen(ep->e_value) + 1, + strlen(value) + 1); + else + ep->e_value = getbytes(strlen(value) + 1); + strcpy(ep->e_value, value); + } + return (0); + } + else return (propelem_new(key, value)); +} + +static void props_dictadd(t_props *pp, t_symbol *s, int ac, t_atom *av) +{ + if (s && *s->s_name && s->s_name[1] && ac) + { + t_propelem *ep; + char *value = pp->p_resolver(pp->p_owner, ac, av); + if (value && + (ep = propelem_add(pp->p_dict, s->s_name + 1, value))) + { + ep->e_next = pp->p_dict; + pp->p_dict = ep; + } + } +} + +static int props_atstart(t_props *pp, char *buf) +{ + if (*buf == pp->p_thisescape) + { + char c = buf[1]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || (pp->p_thisinitial && strchr(pp->p_thisinitial, c))) + return (PROPS_THIS); + } + return (PROPS_NONE); +} + +static char *props_otherinitial(t_props *pp, char c) +{ + t_props *pp1 = pp->p_otherprops; + while (pp1) + { + if (pp1 != pp && pp1->p_thisescape == c) + return (pp1->p_thisinitial); + pp1 = pp1->p_next; + } + bug("props_otherinitial"); + post("(%c \"%s\")", c, pp->p_otherescapes); + return (0); +} + +static int props_atnext(t_props *pp, char *buf) +{ + char *otherinitial; + if (*buf == pp->p_thisescape) + { + char c = buf[1]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || (pp->p_thisinitial && strchr(pp->p_thisinitial, c))) + return (PROPS_THIS); + } + else if (*pp->p_otherescapes && strchr(pp->p_otherescapes, *buf)) + { + char c = buf[1]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || ((otherinitial = props_otherinitial(pp, *buf)) + && *otherinitial && strchr(otherinitial, c))) + return (PROPS_OTHER); + } + return (PROPS_NONE); +} + +/* Search for a property, replace its value if found, otherwise add. + Assuming s is valid. Returning nafter - nbefore. */ +static int props_update(t_props *pp, t_symbol *s, int ac, t_atom *av, int doit) +{ + int nadd, ndiff, ibeg, iend = 0; + t_atom *ap; + for (nadd = 0, ap = av; nadd < ac; nadd++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(pp, ap->a_w.w_symbol->s_name)) + break; + if (!nadd) + { + pp->p_badupdate = 1; + return (0); + } + pp->p_badupdate = 0; + nadd++; + for (ibeg = 0, ap = pp->p_buffer; ibeg < pp->p_natoms; ibeg++, ap++) + { + if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == s) + { + for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(pp, ap->a_w.w_symbol->s_name)) + break; + break; + } + } + ndiff = (iend > ibeg ? nadd - (iend - ibeg) : nadd); + if (doit) + { + int i, newnatoms = pp->p_natoms + ndiff; + if (newnatoms > pp->p_size) + { + bug("props_update"); + return (0); + } +#ifdef PROPS_DEBUG + post("%s %s, [%d..%d), ndiff %d", + (iend > ibeg ? "replacing" : "adding"), s->s_name, + ibeg, iend, ndiff); +#endif + if (iend > ibeg) + { + if (ndiff > 0) + { + t_atom *ap2 = pp->p_buffer + newnatoms; + t_atom *ap1 = ap2 - ndiff; + for (i = iend; i < pp->p_natoms; i++) *--ap2 = *--ap1; + } + else if (ndiff < 0) + { + t_atom *ap2 = pp->p_buffer + iend; + t_atom *ap1 = ap2 + ndiff; + for (i = iend; i < pp->p_natoms; i++) *ap1++ = *ap2++; + } + ap = pp->p_buffer + ibeg; + } + else + { + ap = pp->p_buffer + pp->p_natoms; + SETSYMBOL(ap, s); + } + ap++; + nadd--; + if (pp->p_resolver) props_dictadd(pp, s, nadd, av); + for (i = 0; i < nadd; i++) *ap++ = *av++; + pp->p_natoms = newnatoms; + } + return (ndiff); +} + +/* If there is an empty property, do not parse beyond. + Return the offending switch, if any. */ +t_symbol *props_add(t_props *pp, t_symbol *s, int ac, t_atom *av) +{ + t_symbol *empty = 0; + t_atom *av1, *ap; + int ac1, i, ngrown = 0; + if (s && props_atstart(pp, s->s_name)) + ngrown += props_update(pp, s, ac, av, 0); + if (pp->p_badupdate) + empty = s; + else for (i = 0, ap = av; i < ac; i++, ap++) + { + if (ap->a_type == A_SYMBOL + && props_atstart(pp, ap->a_w.w_symbol->s_name)) + { + ngrown += props_update(pp, ap->a_w.w_symbol, ac - i - 1, ap + 1, 0); + if (pp->p_badupdate) + { + empty = ap->a_w.w_symbol; + break; + } + } + } + ngrown += pp->p_natoms; + if (ngrown > pp->p_size) + { + int nrequested = ngrown; + pp->p_buffer = grow_withdata(&nrequested, &pp->p_natoms, + &pp->p_size, pp->p_buffer, + PROPS_INISIZE, pp->p_bufini, + sizeof(*pp->p_buffer)); + if (nrequested != ngrown) + goto done; + } + ac1 = (s ? ac + 1 : ac); + if (!(av1 = getbytes(ac1 * sizeof(*av1)))) + goto done; + ap = av1; + if (s) + { + SETSYMBOL(ap, s); + ap++; + } + while (ac--) *ap++ = *av++; + ac = ac1; + av = av1; + for (i = 0, ap = av; i < ac; i++, ap++) + { + if (ap->a_type == A_SYMBOL + && props_atstart(pp, ap->a_w.w_symbol->s_name)) + { + props_update(pp, ap->a_w.w_symbol, ac - i - 1, ap + 1, 1); + if (pp->p_badupdate) + { + empty = ap->a_w.w_symbol; + break; + } + } + } + freebytes(av1, ac1 * sizeof(*av1)); +done: + return (empty); +} + +/* FIXME remove from p_dict */ +int props_remove(t_props *pp, t_symbol *s) +{ + int ac; + t_atom *av = props_getone(pp, s, &ac); + if (av) + { + int i; + t_atom *ap = av + ac; + t_atom *guard = pp->p_buffer + pp->p_natoms; + while (ap < guard) *av++ = *ap++; + pp->p_natoms -= ac; + return (1); + } + else return (0); +} + +/* LATER think about 'deep' cloning, i.e. propagating source atoms into + the destination buffer. Since cloning, unless requested by the user, + should never be persistent (source atoms should not stick to the + destination object in a .pd file), deep cloning requires introducing + a two-buffer scheme. There is no reason for deep cloning of arguments, + or handlers, but options could benefit. */ + +void props_clone(t_props *to, t_props *from) +{ + if (to->p_resolver) + { + /* LATER make this into a generic traversing method */ + int ibeg = 0, iend = 0; + t_atom *abeg = from->p_buffer; + t_atom *ap = abeg; + while (ibeg < from->p_natoms) + { + if (ap->a_type == A_SYMBOL && + props_atstart(from, ap->a_w.w_symbol->s_name)) + { + for (iend = ibeg + 1, ap++; iend < from->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(from, ap->a_w.w_symbol->s_name)) + break; + props_dictadd(to, abeg->a_w.w_symbol, + iend - ibeg - 1, abeg + 1); + if (iend < from->p_natoms) + { + ibeg = iend; + abeg = ap; + } + else break; + } + else + { + ibeg++; + ap++; + } + } + } + else + { + /* LATER */ + } +} + +/* only dictionary-enabled properties handle props_...value() calls */ + +char *props_getvalue(t_props *pp, char *key) +{ + if (pp->p_resolver) + { + t_propelem *ep = pp->p_dict; + while (ep) + { + if (strcmp(ep->e_key, key)) + ep = ep->e_next; + else + return (ep->e_value); + } + } + return (0); +} + +char *props_nextvalue(t_props *pp, char **keyp) +{ + if (pp->p_nextelem) + { + char *value = pp->p_nextelem->e_value; + *keyp = pp->p_nextelem->e_key; + pp->p_nextelem = pp->p_nextelem->e_next; + return (value); + } + return (0); +} + +char *props_firstvalue(t_props *pp, char **keyp) +{ + if (pp->p_resolver) + pp->p_nextelem = pp->p_dict; + return (props_nextvalue(pp, keyp)); +} + +t_atom *props_getone(t_props *pp, t_symbol *s, int *npp) +{ + int ibeg, iend = 0; + t_atom *ap; + if (!(s && props_atstart(pp, s->s_name))) + return (0); + for (ibeg = 0, ap = pp->p_buffer; ibeg < pp->p_natoms; ibeg++, ap++) + { + if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == s) + { + for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL + && props_atnext(pp, ap->a_w.w_symbol->s_name)) + break; + break; + } + } + if (iend > ibeg) + { + *npp = iend - ibeg; + return (pp->p_buffer + ibeg); + } + else return (0); +} + +t_atom *props_getall(t_props *pp, int *npp) +{ + *npp = pp->p_natoms; + return (pp->p_buffer); +} + +char *props_getname(t_props *pp) +{ + return (pp ? pp->p_name : "property"); +} + +static void props_freeone(t_props *pp) +{ + if (pp->p_buffer != pp->p_bufini) + freebytes(pp->p_buffer, pp->p_size * sizeof(*pp->p_buffer)); + while (pp->p_dict) + { + t_propelem *ep = pp->p_dict->e_next; + propelem_free(pp->p_dict); + pp->p_dict = ep; + } + freebytes(pp, sizeof(*pp)); +} + +void props_freeall(t_props *pp) +{ + if (pp && (pp = pp->p_otherprops)) + { + while (pp) + { + t_props *pp1 = pp->p_next; + props_freeone(pp); + pp = pp1; + } + } + else bug("props_freeall"); +} + +void props_setupothers(t_props *pp, t_props *otherprops) +{ + t_props *pp1; + pp->p_next = (otherprops ? otherprops->p_otherprops : 0); + for (pp1 = pp; pp1; pp1 = pp1->p_next) + { + t_props *pp2; + char *bp = pp1->p_otherescapes; + int i; + pp1->p_otherprops = pp; + for (pp2 = pp, i = 1; pp2 && i < PROPS_MAXOTHERS; + pp2 = pp2->p_next, i++) + if (pp2 != pp1) + *bp++ = pp2->p_thisescape; + *bp = 0; +#ifdef PROPS_DEBUG + startpost("%c \"%s\" ", pp1->p_thisescape, pp1->p_otherescapes); +#endif + } +#ifdef PROPS_DEBUG + endpost(); +#endif +} + +/* nonzero resolver requires the owner to be nonzero */ +t_props *props_new(t_pd *owner, char *name, char *thisdelim, + t_props *otherprops, t_propsresolver resolver) +{ + t_props *pp = getbytes(sizeof(*pp)); + if (pp) + { + pp->p_name = name; + if (thisdelim && *thisdelim) + { + pp->p_thisescape = *thisdelim++; + pp->p_thisinitial = (*thisdelim ? thisdelim : 0); + } + else + { + bug("props_new (no escape)"); + pp->p_thisescape = '-'; + pp->p_thisinitial = 0; + } + props_setupothers(pp, otherprops); + pp->p_size = PROPS_INISIZE; + pp->p_natoms = 0; + pp->p_buffer = pp->p_bufini; + if (pp->p_owner = owner) + pp->p_resolver = resolver; + else + { + if (resolver) + bug("props_new (no owner)"); + pp->p_resolver = 0; + } + pp->p_dict = 0; + pp->p_nextelem = 0; + } + return (pp); +} diff --git a/shared/common/props.h b/shared/common/props.h new file mode 100644 index 0000000..0eef345 --- /dev/null +++ b/shared/common/props.h @@ -0,0 +1,26 @@ +/* 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 __PROPS_H__ +#define __PROPS_H__ + +EXTERN_STRUCT _props; +#define t_props struct _props + +typedef char *(*t_propsresolver)(t_pd *, int, t_atom *); + +t_symbol *props_add(t_props *pp, t_symbol *s, int ac, t_atom *av); +int props_remove(t_props *pp, t_symbol *s); +void props_clone(t_props *to, t_props *from); +char *props_getvalue(t_props *pp, char *key); +char *props_firstvalue(t_props *pp, char **keyp); +char *props_nextvalue(t_props *pp, char **keyp); +t_atom *props_getone(t_props *pp, t_symbol *s, int *npp); +t_atom *props_getall(t_props *pp, int *npp); +char *props_getname(t_props *pp); +void props_freeall(t_props *pp); +t_props *props_new(t_pd *owner, char *name, char *thisdelim, + t_props *otherprops, t_propsresolver resolver); + +#endif diff --git a/shared/toxy/Makefile b/shared/toxy/Makefile new file mode 100644 index 0000000..5dcb2c8 --- /dev/null +++ b/shared/toxy/Makefile @@ -0,0 +1,4 @@ +ROOT_DIR = ../.. +include $(ROOT_DIR)/Makefile.common + +all: $(OBJECTS) diff --git a/shared/toxy/Makefile.objects b/shared/toxy/Makefile.objects new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/shared/toxy/Makefile.objects diff --git a/shared/toxy/Makefile.sources b/shared/toxy/Makefile.sources new file mode 100644 index 0000000..5f34f42 --- /dev/null +++ b/shared/toxy/Makefile.sources @@ -0,0 +1,2 @@ +OTHER_SOURCES = \ +scriptlet.c diff --git a/shared/toxy/scriptlet.c b/shared/toxy/scriptlet.c new file mode 100644 index 0000000..2592cee --- /dev/null +++ b/shared/toxy/scriptlet.c @@ -0,0 +1,666 @@ +/* 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 <stdio.h> +#include <string.h> +#ifdef UNIX +#include <unistd.h> +#endif +#ifdef NT +#include <io.h> +#endif +#include "m_pd.h" +#include "g_canvas.h" +#include "common/loud.h" +#include "common/grow.h" +#include "common/props.h" +#include "scriptlet.h" + +//#define SCRIPTLET_DEBUG + +#define SCRIPTLET_INISIZE 1024 +#define SCRIPTLET_MARGIN 64 +#define SCRIPTLET_MAXARGS 9 /* do not increase (parser's constraint) */ +#define SCRIPTLET_MAXPUSH 20000 /* Tcl limit? LATER investigate */ + +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; + int s_size; + char *s_buffer; + char s_bufini[SCRIPTLET_INISIZE]; + char *s_head; /* ptr to the command part of a scriptlet */ + char *s_tail; + char s_separator; /* current separator, set before a new token */ + int s_ac; /* the actual count */ + t_atom s_av[SCRIPTLET_MAXARGS]; /* always padded with zeros (if used) */ +}; + +static t_canvas *scriptlet_canvasvalidate(t_scriptlet *sp, int visedonly) +{ + t_canvas *cv; + if (sp->s_cvstate == SCRIPTLET_CVUNKNOWN) + { + if (sp->s_cvfn) + cv = sp->s_cv = sp->s_cvfn(sp->s_owner); + else + { + bug("scriptlet_canvasvalidate"); + return (0); + } + if (cv && (!visedonly || glist_isvisible(cv))) + sp->s_cvstate = SCRIPTLET_CVOK; + else + sp->s_cvstate = SCRIPTLET_CVMISSING; + } + else cv = sp->s_cv; + return (sp->s_cvstate == SCRIPTLET_CVOK ? cv : 0); +} + +static int scriptlet_ready(t_scriptlet *sp) +{ + int len = sp->s_tail - sp->s_head; + if (len > 0 && *sp->s_head && sp->s_cvstate != SCRIPTLET_CVMISSING) + { + if (len < SCRIPTLET_MAXPUSH) + return (1); + else + loud_error(sp->s_owner, + "scriptlet too long to be pushed (%d bytes)", len); + } + return (0); +} + +static int scriptlet_doappend(t_scriptlet *sp, char *buf) +{ + if (buf) + { + int nprefix = sp->s_head - sp->s_buffer; + int nused = sp->s_tail - sp->s_buffer; + int newsize = nused + strlen(buf) + SCRIPTLET_MARGIN; + if (newsize > sp->s_size) + { + int nrequested = newsize; + sp->s_buffer = grow_withdata(&nrequested, &nused, + &sp->s_size, sp->s_buffer, + SCRIPTLET_INISIZE, sp->s_bufini, + sizeof(*sp->s_buffer)); + if (nrequested != newsize) + { + scriptlet_reset(sp); + return (0); + } + sp->s_head = sp->s_buffer + nprefix; + sp->s_tail = sp->s_buffer + nused; + } + if (sp->s_separator && sp->s_tail > sp->s_head) + *sp->s_tail++ = sp->s_separator; + *sp->s_tail = 0; + strcpy(sp->s_tail, buf); + sp->s_tail += strlen(sp->s_tail); + } + sp->s_separator = 0; + return (1); +} + +static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, char *obuf, + int resolveall, int visedonly, + int ac, t_atom *av, t_props *argprops) +{ + int len = 0; + switch (*ibuf) + { + case '#': + /* ac is ignored -- assuming av is padded to SCRIPTLET_MAXARGS atoms */ + if (resolveall) + { + int which = ibuf[1] - '1'; + if (which >= 0 && which < SCRIPTLET_MAXARGS) + { + if (av) + { + if (av[which].a_type == A_FLOAT) + { + sprintf(obuf, "%g", av[which].a_w.w_float); + len = 2; + } + else if (av[which].a_type == A_SYMBOL) + { + strcpy(obuf, av[which].a_w.w_symbol->s_name); + len = 2; + } + } + } + else if (argprops) + { + char *ptr; + int cnt; + for (ptr = ibuf + 1, cnt = 1; *ptr; ptr++, cnt++) + { + char c = *ptr; + if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) + { + cnt = 0; + break; + } + } + if (cnt && (ptr = props_getvalue(argprops, ibuf + 1))) + { + strcpy(obuf, ptr); + len = cnt; + } + } + } + break; + case '-': + if (resolveall && sp->s_item) + { + t_canvas *cv; + if (cv = scriptlet_canvasvalidate(sp, visedonly)) + { + sprintf(obuf, ".x%x.c.%s%x", (int)cv, sp->s_item->s_name, + (int)sp->s_owner); + len = 1; + } + } + break; + case '^': + if (resolveall) + { + t_canvas *cv; + if (cv = scriptlet_canvasvalidate(sp, visedonly)) + { + sprintf(obuf, ".x%x", (int)cv); + len = 1; + } + } + break; + case '|': + if (resolveall) + { + strcpy(obuf, sp->s_cbtarget->s_name); + len = 1; + } + break; + case '~': + if (resolveall) + { + t_canvas *cv; + if (cv = scriptlet_canvasvalidate(sp, visedonly)) + { + /* FIXME */ + if (!strcmp(&ibuf[1], "x1")) + { + sprintf(obuf, "%d", cv->gl_screenx1); + len = 3; + } + else if (!strcmp(&ibuf[1], "x2")) + { + sprintf(obuf, "%d", cv->gl_screenx2); + len = 3; + } + else if (!strcmp(&ibuf[1], "y1")) + { + sprintf(obuf, "%d", cv->gl_screeny1); + len = 3; + } + else if (!strcmp(&ibuf[1], "y2")) + { + sprintf(obuf, "%d", cv->gl_screeny2); + len = 3; + } + else if (!strcmp(&ibuf[1], "edit")) + { + sprintf(obuf, "%d", cv->gl_edit); + len = 5; + } + else loud_error(sp->s_owner, "bad field '%s'", &ibuf[1]); + } + } + break; + case '`': + sprintf(obuf, "\\"); + len = 1; + break; + case ':': + sprintf(obuf, ";"); + len = 1; + break; + case '(': + sprintf(obuf, "{"); + len = 1; + break; + case ')': + sprintf(obuf, "}"); + len = 1; + break; + case '<': + if (resolveall) + { + if (ibuf[1] == ':') + { + sprintf(obuf, "{::toxy::callback "); + len = 2; + } + else if (ibuf[1] == '|') + { + sprintf(obuf, "{::toxy::callback %s ", + sp->s_rptarget->s_name); + len = 2; + } + else + { + sprintf(obuf, "{::toxy::callback %s _cb ", + sp->s_cbtarget->s_name); + len = 1; + } + } + break; + case '>': + if (resolveall) + { + sprintf(obuf, "}"); + len = 1; + } + break; + } + return (len ? ibuf + len : 0); +} + +void scriptlet_reset(t_scriptlet *sp) +{ + sp->s_cvstate = SCRIPTLET_CVUNKNOWN; + sp->s_separator = 0; + strcpy(sp->s_buffer, "namespace eval ::toxy {\ + proc query {} {set ::toxy::reply [\n"); + sp->s_head = sp->s_tail = sp->s_buffer + strlen(sp->s_buffer); +} + +void scriptlet_prealloc(t_scriptlet *sp, int sz, int mayshrink) +{ + if (sz < SCRIPTLET_INISIZE) + sz = SCRIPTLET_INISIZE; + if (sz < sp->s_size && mayshrink) + { + if (sp->s_buffer != sp->s_bufini) + freebytes(sp->s_buffer, sp->s_size * sizeof(*sp->s_buffer)); + else + bug("scriptlet_prealloc"); + sp->s_size = SCRIPTLET_INISIZE; + sp->s_buffer = sp->s_bufini; + } + if (sz > sp->s_size) + sp->s_buffer = grow_nodata(&sz, &sp->s_size, sp->s_buffer, + SCRIPTLET_INISIZE, sp->s_bufini, + sizeof(*sp->s_buffer)); + scriptlet_reset(sp); +} + +int scriptlet_addstring(t_scriptlet *sp, char *ibuf, + int resolveall, int visedonly, + int ac, t_atom *av, t_props *argprops) +{ + int result = 1; + char *bp = ibuf, *ep = ibuf, *ep1; + char dotbuf[64]; /* LATER reestimation */ + if (!sp->s_separator) + sp->s_separator = ' '; + while (*ep) + { + if (*ep == '.' + && (ep1 = scriptlet_dedot(sp, ep + 1, dotbuf, + resolveall, visedonly, ac, av, argprops))) + { + *ep = 0; + if (!(result = scriptlet_doappend(sp, bp))) + break; + *ep = '.'; + if (!(result = scriptlet_doappend(sp, dotbuf))) + break; + bp = ep = ep1; + } + else ep++; + } + if (result) + result = scriptlet_doappend(sp, bp); + sp->s_separator = 0; + return (result); +} + +int scriptlet_addfloat(t_scriptlet *sp, t_float f) +{ + char buf[64]; + if (!sp->s_separator) + sp->s_separator = ' '; + sprintf(buf, "%g ", f); + return (scriptlet_doappend(sp, buf)); +} + +int scriptlet_add(t_scriptlet *sp, + int resolveall, int visedonly, int ac, t_atom *av) +{ + while (ac--) + { + int result = 1; + if (av->a_type == A_SYMBOL) + result = scriptlet_addstring(sp, av->a_w.w_symbol->s_name, + resolveall, visedonly, 0, 0, 0); + else if (av->a_type == A_FLOAT) + result = scriptlet_addfloat(sp, av->a_w.w_float); + if (!result) + return (0); + av++; + } + return (1); +} + +void scriptlet_setseparator(t_scriptlet *sp, char c) +{ + sp->s_separator = c; +} + +void scriptlet_push(t_scriptlet *sp) +{ + if (scriptlet_ready(sp)) + { + char *tail = sp->s_tail; + strcpy(tail, "\n"); + sys_gui(sp->s_head); + *tail = 0; + } +} + +void scriptlet_qpush(t_scriptlet *sp) +{ + if (scriptlet_ready(sp)) + { + char buf[MAXPDSTRING]; + char *tail = sp->s_tail; + strcpy(tail, "]}}\n"); + sys_gui(sp->s_buffer); + *tail = 0; + sprintf(buf, "after 0 {::toxy::query}\nvwait ::toxy::reply\n\ + pd [concat %s _rp $::toxy::reply \\;]\n", sp->s_rptarget->s_name); + sys_gui(buf); + } +} + +int scriptlet_evaluate(t_scriptlet *insp, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops) +{ + if (scriptlet_ready(insp)) + { + t_atom *ap; + int i; + char *bp; + char separator = 0; + insp->s_ac = ac; + for (i = 0, ap = insp->s_av; i < SCRIPTLET_MAXARGS; i++, ap++) + { + if (ac) + { + if (av->a_type == A_FLOAT || + (av->a_type == A_SYMBOL && av->a_w.w_symbol)) + *ap = *av; + else + SETFLOAT(ap, 0); + ac--; av++; + } + else SETFLOAT(ap, 0); + } + /* FIXME pregrowing of the transient scriptlet */ + scriptlet_reset(outsp); + /* LATER abstract this into scriptlet_parse() */ + bp = insp->s_head; + while (*bp) + { + if (*bp == '\n') + separator = '\n'; + else if (*bp == ' ' || *bp == '\t') + { + if (!separator) separator = ' '; + } + else + { + int done = 1; + char *ep = bp; + char c = ' '; + while (*++ep) + { + if (*ep == ' ' || *bp == '\t' || *ep == '\n') + { + done = 0; + c = *ep; + *ep = 0; + break; + } + } + outsp->s_separator = separator; + scriptlet_addstring(outsp, bp, 1, visedonly, + ac, insp->s_av, argprops); + if (done) + break; + *ep = c; + bp = ep; + separator = (c == '\t' ? ' ' : c); + } + bp++; + } + return (outsp->s_cvstate != SCRIPTLET_CVMISSING); + } + else return (0); +} + +/* utility function to be used in a comment-parsing callback */ +char *scriptlet_nextword(char *buf) +{ + while (*++buf) + { + if (*buf == ' ' || *buf == '\t') + { + char *ptr = buf + 1; + while (*ptr == ' ' || *ptr == '\t') ptr++; + *buf = 0; + return (*ptr ? ptr : 0); + } + } + return (0); +} + +static int scriptlet_doread(t_scriptlet *sp, FILE *fp, char *rc, + t_scriptlet_cmntfn cmntfn) +{ + t_scriptlet *outsp = sp, *newsp; + char buf[MAXPDSTRING]; + scriptlet_reset(outsp); + while (!feof(fp)) + { + if (fgets(buf, MAXPDSTRING - 1, fp)) + { + char *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(sp->s_owner, rc, sel, ptr); + if (newsp && newsp != outsp) + scriptlet_reset(outsp = newsp); + } + } + } + else if (*ptr && *ptr != '\n') + scriptlet_doappend(outsp, buf); + } + else break; + } + return (SCRIPTLET_OK); +} + +int scriptlet_rcload(t_scriptlet *sp, char *rc, char *ext, + t_scriptlet_cmntfn cmntfn) +{ + char filename[MAXPDSTRING], buf[MAXPDSTRING], *nameptr, *dir; + int fd; + if (sp->s_glist) + dir = canvas_getdir(sp->s_glist)->s_name; + else + dir = ""; + if ((fd = open_via_path(dir, rc, ext, buf, &nameptr, MAXPDSTRING, 0)) < 0) + { + return (SCRIPTLET_NOFILE); + } + else + { + FILE *fp; + close(fd); + strcpy(filename, buf); + strcat(filename, "/"); + strcat(filename, nameptr); + sys_bashfilename(filename, filename); + if (fp = fopen(filename, "r")) + { + int result = scriptlet_doread(sp, fp, rc, cmntfn); + fclose(fp); + return (result); + } + else + { + bug("scriptlet_rcload"); + return (SCRIPTLET_NOFILE); + } + } +} + +int scriptlet_read(t_scriptlet *sp, t_symbol *fn) +{ + FILE *fp; + char buf[MAXPDSTRING]; + post("loading scriptlet file \"%s\"", fn->s_name); + if (sp->s_glist) + canvas_makefilename(sp->s_glist, fn->s_name, buf, MAXPDSTRING); + else + strncpy(buf, fn->s_name, MAXPDSTRING); + sys_bashfilename(buf, buf); + if (fp = fopen(buf, "r")) + { + int result = scriptlet_doread(sp, fp, 0, 0); + fclose(fp); + return (result); + } + else + { + loud_error(sp->s_owner, "error while loading file \"%s\"", fn->s_name); + return (SCRIPTLET_NOFILE); + } +} + +int scriptlet_write(t_scriptlet *sp, t_symbol *fn) +{ + int size = sp->s_tail - sp->s_head; + if (size > 0 && *sp->s_head) + { + FILE *fp; + char buf[MAXPDSTRING]; + post("saving scriptlet file \"%s\"", fn->s_name); + if (sp->s_glist) + canvas_makefilename(sp->s_glist, fn->s_name, buf, MAXPDSTRING); + else + strncpy(buf, fn->s_name, MAXPDSTRING); + sys_bashfilename(buf, buf); + if (fp = fopen(buf, "w")) + { + int result = fwrite(sp->s_head, 1, size, fp); + fclose(fp); + if (result == size) + return (SCRIPTLET_OK); + } + loud_error(sp->s_owner, "error while saving file \"%s\"", fn->s_name); + return (fp ? SCRIPTLET_BADFILE : SCRIPTLET_NOFILE); + } + else + { + loud_warning(sp->s_owner, "empty scriptlet not written"); + return (SCRIPTLET_IGNORED); + } +} + +char *scriptlet_getcontents(t_scriptlet *sp, int *lenp) +{ + *lenp = sp->s_tail - sp->s_head; + return (sp->s_head); +} + +char *scriptlet_getbuffer(t_scriptlet *sp, int *sizep) +{ + *sizep = sp->s_size; + return (sp->s_buffer); +} + +void scriptlet_clone(t_scriptlet *to, t_scriptlet *from) +{ + scriptlet_reset(to); + to->s_separator = ' '; + /* LATER use from's buffer with refcount */ + scriptlet_doappend(to, from->s_head); +} + +void scriptlet_free(t_scriptlet *sp) +{ + if (sp) + { + if (sp->s_buffer != sp->s_bufini) + freebytes(sp->s_buffer, sp->s_size * sizeof(*sp->s_buffer)); + freebytes(sp, sizeof(*sp)); + } +} + +t_scriptlet *scriptlet_new(t_pd *owner, t_symbol *rptarget, t_symbol *cbtarget, + t_symbol *item, t_scriptlet_cvfn cvfn) +{ + t_scriptlet *sp = getbytes(sizeof(*sp)); + if (sp) + { + 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_rptarget = rptarget; + sp->s_cbtarget = cbtarget; + sp->s_item = item; + sp->s_cvfn = cvfn; + sp->s_size = SCRIPTLET_INISIZE; + sp->s_buffer = sp->s_bufini; + scriptlet_reset(sp); + } + return (sp); +} diff --git a/shared/toxy/scriptlet.h b/shared/toxy/scriptlet.h new file mode 100644 index 0000000..4b057b9 --- /dev/null +++ b/shared/toxy/scriptlet.h @@ -0,0 +1,42 @@ +/* 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 __SCRIPTLET_H__ +#define __SCRIPTLET_H__ + +enum { SCRIPTLET_OK = 0, SCRIPTLET_NOFILE, SCRIPTLET_BADFILE, + SCRIPTLET_IGNORED }; + +EXTERN_STRUCT _scriptlet; +#define t_scriptlet struct _scriptlet + +typedef t_canvas *(*t_scriptlet_cvfn)(t_pd *); +typedef t_scriptlet *(*t_scriptlet_cmntfn)(t_pd *, char *, char, char *); + +void scriptlet_reset(t_scriptlet *sp); +void scriptlet_prealloc(t_scriptlet *sp, int sz, int mayshrink); +int scriptlet_add(t_scriptlet *sp, + int resolveall, int visedonly, int ac, t_atom *av); +int scriptlet_addstring(t_scriptlet *sp, char *ibuf, + int resolveall, int visedonly, + int ac, t_atom *av, t_props *argprops); +int scriptlet_addfloat(t_scriptlet *sp, t_float f); +void scriptlet_setseparator(t_scriptlet *sp, char c); +void scriptlet_push(t_scriptlet *sp); +void scriptlet_qpush(t_scriptlet *sp); +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_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_clone(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); + +#endif |