aboutsummaryrefslogtreecommitdiff
path: root/shared/common/props.c
diff options
context:
space:
mode:
authorN.N. <krzyszcz@users.sourceforge.net>2003-09-24 10:46:19 +0000
committerN.N. <krzyszcz@users.sourceforge.net>2003-09-24 10:46:19 +0000
commit9680b47879dfc58f884208f7abf2f945b3b41d25 (patch)
tree93eda19deb61daff60c533a3bbd1efe79e9cdc9b /shared/common/props.c
parentedab184352cd14788a37c76dce147ac19f7464b4 (diff)
adding toxy project
svn path=/trunk/externals/miXed/; revision=1024
Diffstat (limited to 'shared/common/props.c')
-rw-r--r--shared/common/props.c518
1 files changed, 518 insertions, 0 deletions
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);
+}