diff options
Diffstat (limited to 'shared/common/props.c')
-rw-r--r-- | shared/common/props.c | 487 |
1 files changed, 311 insertions, 176 deletions
diff --git a/shared/common/props.c b/shared/common/props.c index 4dfe113..4445bf2 100644 --- a/shared/common/props.c +++ b/shared/common/props.c @@ -1,9 +1,10 @@ -/* Copyright (c) 2003 krzYszcz and others. +/* Copyright (c) 2003-2005 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 "common/grow.h" #include "props.h" @@ -12,9 +13,12 @@ #endif #define PROPS_INISIZE 32 /* LATER rethink */ -#define PROPS_MAXOTHERS 32 +#define PROPS_MAXMIXUPS 32 -enum { PROPS_NONE = 0, PROPS_THIS, PROPS_OTHER }; +/* return values of props_iskey() */ +enum { PROPS_NONE = 0, PROPS_THIS, PROPS_MIXUP }; + +/* 'mode' argument values of props_iskey() and props_update() */ enum { PROPS_SINGLEMODE = 0, PROPS_MULTIMODE }; typedef struct _propelem @@ -29,17 +33,18 @@ struct _props char p_thisescape; char *p_thisinitial; char *p_name; - int p_size; /* as allocated */ - int p_natoms; /* as used */ + int p_size; /* as allocated */ + int p_natoms; /* as used */ t_atom *p_buffer; t_atom p_bufini[PROPS_INISIZE]; + int p_nextindex; 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 */ + char p_mixupescapes[PROPS_MAXMIXUPS]; + t_props *p_firstmixup; /* points to the props list's head */ t_props *p_next; }; @@ -112,23 +117,124 @@ static void props_dictadd(t_props *pp, t_symbol *s, int ac, t_atom *av) } } -static char *props_otherinitial(t_props *pp, char c) +/* API calls for lookup (getvalue) and traversal (firstvalue, nextvalue), and + non-api calls (removevalue) of resolved properties. Only dictionary-enabled + properties handle these calls. Plain 'key', without escape, is expected. + Traversal is not thread-safe (will we need threaded props, LATER?) */ + +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_nextelem) + loudbug_bug("props_firstvalue"); + if (pp->p_resolver) + pp->p_nextelem = pp->p_dict; + return (props_nextvalue(pp, keyp)); +} + +static void props_removevalue(t_props *pp, char *key) +{ + if (pp->p_resolver && *key) + { + t_propelem *ep = pp->p_dict, *epp = 0; + while (ep) + { + if (strcmp(ep->e_key, key)) + { + epp = ep; + ep = ep->e_next; + } + else + { + if (epp) + epp->e_next = ep->e_next; + else + pp->p_dict = ep->e_next; + propelem_free(ep); + break; + } + } + } +} + +void props_clearvalues(t_props *pp) { - t_props *pp1 = pp->p_otherprops; + while (pp->p_dict) + { + t_propelem *ep = pp->p_dict->e_next; + propelem_free(pp->p_dict); + pp->p_dict = ep; + } +} + +/* 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_clonevalues(t_props *to, t_props *from) +{ + if (to->p_resolver) + { + int ac; + t_atom *ap = props_getfirst(from, &ac); + while (ap) + { + props_dictadd(to, ap->a_w.w_symbol, ac - 1, ap + 1); + ap = props_getnext(from, &ac); + } + } +} + +static char *props_mixupinitial(t_props *pp, char c) +{ + t_props *pp1 = pp->p_firstmixup; 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); + loudbug_bug("props_mixupinitial"); + loudbug_post("(%c \"%s\")", c, pp->p_mixupescapes); return (0); } -static int props_atstart(t_props *pp, int mode, char *buf) +/* If buf is pp's key, returns PROPS_THIS; otherwise, if PROPS_MULTIMODE + was passed and buf is a key of one of pp's mixups, returns PROPS_MIXUP; + otherwise, returns PROPS_NONE. */ +static int props_iskey(t_props *pp, int mode, char *buf) { - char *otherinitial; + char *mixupinitial; if (*buf == pp->p_thisescape) { char c = buf[1]; @@ -137,27 +243,113 @@ static int props_atstart(t_props *pp, int mode, char *buf) return (PROPS_THIS); } else if (mode == PROPS_MULTIMODE && - *pp->p_otherescapes && strchr(pp->p_otherescapes, *buf)) + *pp->p_mixupescapes && strchr(pp->p_mixupescapes, *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); + || ((mixupinitial = props_mixupinitial(pp, *buf)) + && *mixupinitial && strchr(mixupinitial, c))) + return (PROPS_MIXUP); } return (PROPS_NONE); } +/* Lookup (getone) and traversal (getfirst, getnext) of unresolved properties. + These calls return a pointer to the key atom (the contents follows it), + unlike the get/first/nextvalue calls, which return the (resolved) value. + Traversal is not thread-safe (will we need threaded props, LATER?) */ + +t_atom *props_getone(t_props *pp, t_symbol *keysym, int *npp) +{ + if (keysym && + props_iskey(pp, PROPS_SINGLEMODE, keysym->s_name) != PROPS_NONE) + { + int ibeg, iend = 0; + t_atom *ap; + for (ibeg = 0, ap = pp->p_buffer; ibeg < pp->p_natoms; ibeg++, ap++) + { + if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == keysym) + { + for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, PROPS_SINGLEMODE, + ap->a_w.w_symbol->s_name) != PROPS_NONE) + break; + break; + } + } + if (iend > ibeg) + { + *npp = iend - ibeg; + return (pp->p_buffer + ibeg); + } + } + *npp = 0; + return (0); +} + +t_atom *props_getnext(t_props *pp, int *npp) +{ + if (pp->p_nextindex >= 0) + { + int ibeg = pp->p_nextindex; + t_atom *ap; + for (ap = pp->p_buffer + ibeg; ibeg < pp->p_natoms; ibeg++, ap++) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, PROPS_SINGLEMODE, + ap->a_w.w_symbol->s_name) != PROPS_NONE) + break; + if (ibeg < pp->p_natoms) + { + int iend; + for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, PROPS_SINGLEMODE, + ap->a_w.w_symbol->s_name) != PROPS_NONE) + break; + if (iend < pp->p_natoms) + pp->p_nextindex = iend; + else + pp->p_nextindex = -1; + *npp = iend - ibeg; + return (pp->p_buffer + ibeg); + } + else pp->p_nextindex = -1; + } + *npp = 0; + return (0); +} + +t_atom *props_getfirst(t_props *pp, int *npp) +{ + if (pp->p_nextindex >= 0) + loudbug_bug("props_getfirst"); + pp->p_nextindex = 0; + return (props_getnext(pp, npp)); +} + +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"); +} + /* 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, int mode, - t_symbol *s, int ac, t_atom *av, int doit) + If 'filter' contains an exact copy, do nothing. Assuming 'keysym' + is valid. Returning nafter - nbefore. */ +static int props_update(t_props *pp, int mode, t_props *filter, + t_symbol *keysym, 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_atstart(pp, mode, ap->a_w.w_symbol->s_name)) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, mode, ap->a_w.w_symbol->s_name) != PROPS_NONE) break; if (!nadd) { @@ -166,14 +358,43 @@ static int props_update(t_props *pp, int mode, } pp->p_badupdate = 0; nadd++; + + if (filter) + { + int acf; + t_atom *apf = props_getone(filter, keysym, &acf); + if (acf == nadd) + { + int i; +#ifdef PROPS_DEBUG + loudbug_startpost("checking %s", keysym->s_name); + loudbug_postatom(nadd - 1, av); +#endif + for (i = 1, ap = av, apf++; i < nadd; i++, ap++, apf++) + if (ap->a_type != apf->a_type || + ap->a_w.w_symbol != apf->a_w.w_symbol) + break; + if (i == nadd) +#ifndef PROPS_DEBUG + return (0); +#else + { + loudbug_post(" ... filtered"); + return (0); + } + else loudbug_post(" ... updated"); +#endif + } + } + 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) + if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == keysym) { for (iend = ibeg + 1, ap++; iend < pp->p_natoms; iend++, ap++) - if (ap->a_type == A_SYMBOL - && props_atstart(pp, PROPS_SINGLEMODE, - ap->a_w.w_symbol->s_name)) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, PROPS_SINGLEMODE, + ap->a_w.w_symbol->s_name) != PROPS_NONE) break; break; } @@ -184,13 +405,13 @@ static int props_update(t_props *pp, int mode, int i, newnatoms = pp->p_natoms + ndiff; if (newnatoms > pp->p_size) { - bug("props_update"); + loudbug_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); + loudbug_post("%s %s, [%d..%d), ndiff %d", + (iend > ibeg ? "replacing" : "adding"), keysym->s_name, + ibeg, iend, ndiff); #endif if (iend > ibeg) { @@ -211,34 +432,38 @@ static int props_update(t_props *pp, int mode, else { ap = pp->p_buffer + pp->p_natoms; - SETSYMBOL(ap, s); + SETSYMBOL(ap, keysym); } ap++; nadd--; - if (pp->p_resolver) props_dictadd(pp, s, nadd, av); + if (pp->p_resolver) + props_dictadd(pp, keysym, nadd, av); for (i = 0; i < nadd; i++) *ap++ = *av++; pp->p_natoms = newnatoms; } return (ndiff); } -/* If in a single mode, ignore `other' properties (their switches are parsed - through as values). If there is an empty property, which is not to be - ignored, do not parse beyond. Return an offending switch, if any. */ -t_symbol *props_add(t_props *pp, int single, t_symbol *s, int ac, t_atom *av) +/* Carve out all properties of a given kind from a message. If in a single + mode, ignore `mixup' properties -- their keys are parsed through as values. + If there is an empty property, which is not to be ignored, do not parse + beyond. Return an offending key symbol, if any. */ +t_symbol *props_add(t_props *pp, int single, t_props *filter, + t_symbol *s, int ac, t_atom *av) { t_symbol *empty = 0; t_atom *av1, *ap; int mode = (single ? PROPS_SINGLEMODE : PROPS_MULTIMODE); int ac1, i, ngrown = 0; - if (!s || !props_atstart(pp, PROPS_SINGLEMODE, s->s_name)) + if (!s || props_iskey(pp, PROPS_SINGLEMODE, s->s_name) == PROPS_NONE) { s = 0; while (ac) { s = (av->a_type == A_SYMBOL ? av->a_w.w_symbol : 0); ac--; av++; - if (s && props_atstart(pp, PROPS_SINGLEMODE, s->s_name)) + if (s && + props_iskey(pp, PROPS_SINGLEMODE, s->s_name) != PROPS_NONE) break; s = 0; } @@ -248,16 +473,16 @@ t_symbol *props_add(t_props *pp, int single, t_symbol *s, int ac, t_atom *av) empty = s; goto done; } - ngrown += props_update(pp, mode, s, ac, av, 0); + ngrown += props_update(pp, mode, filter, 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, PROPS_SINGLEMODE, - ap->a_w.w_symbol->s_name)) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, PROPS_SINGLEMODE, + ap->a_w.w_symbol->s_name) != PROPS_NONE) { - ngrown += props_update(pp, mode, ap->a_w.w_symbol, + ngrown += props_update(pp, mode, filter, ap->a_w.w_symbol, ac - i - 1, ap + 1, 0); if (pp->p_badupdate) { @@ -277,16 +502,16 @@ t_symbol *props_add(t_props *pp, int single, t_symbol *s, int ac, t_atom *av) if (nrequested != ngrown) goto done; } - props_update(pp, mode, s, ac, av, 1); + props_update(pp, mode, filter, s, ac, av, 1); 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, PROPS_SINGLEMODE, - ap->a_w.w_symbol->s_name)) + if (ap->a_type == A_SYMBOL && + props_iskey(pp, PROPS_SINGLEMODE, + ap->a_w.w_symbol->s_name) != PROPS_NONE) { - props_update(pp, mode, ap->a_w.w_symbol, + props_update(pp, mode, filter, ap->a_w.w_symbol, ac - i - 1, ap + 1, 1); if (pp->p_badupdate) { @@ -299,11 +524,12 @@ done: return (empty); } -/* FIXME remove from p_dict */ -int props_remove(t_props *pp, t_symbol *s) +int props_remove(t_props *pp, t_symbol *keysym) { int ac; - t_atom *av = props_getone(pp, s, &ac); + t_atom *av = props_getone(pp, keysym, &ac); + if (keysym && *keysym->s_name) + props_removevalue(pp, keysym->s_name + 1); if (av) { int i; @@ -316,143 +542,51 @@ int props_remove(t_props *pp, t_symbol *s) 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) +static void props_clearone(t_props *pp) { - 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, PROPS_SINGLEMODE, 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_atstart(from, PROPS_MULTIMODE, - 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 */ - } + pp->p_natoms = 0; + props_clearvalues(pp); } -/* only dictionary-enabled properties handle props_...value() calls */ - -char *props_getvalue(t_props *pp, char *key) +void props_clearall(t_props *pp) { - if (pp->p_resolver) + if (pp && (pp = pp->p_firstmixup)) { - t_propelem *ep = pp->p_dict; - while (ep) + while (pp) { - if (strcmp(ep->e_key, key)) - ep = ep->e_next; - else - return (ep->e_value); + props_clearone(pp); + pp = pp->p_next; } } - return (0); + else loudbug_bug("props_clearall"); } -char *props_nextvalue(t_props *pp, char **keyp) +/* Compute pp0 = pp1 - pp2, using key-only equivalence. */ +void props_diff(t_props *pp0, t_props *pp1, t_props *pp2) { - if (pp->p_nextelem) + int ac1; + t_atom *ap1 = props_getfirst(pp1, &ac1); + props_clearone(pp0); + while (ap1) { - char *value = pp->p_nextelem->e_value; - *keyp = pp->p_nextelem->e_key; - pp->p_nextelem = pp->p_nextelem->e_next; - return (value); + int ac2; + if (!props_getone(pp2, ap1->a_w.w_symbol, &ac2)) + props_add(pp0, 0, 0, 0, ac1, ap1); + ap1 = props_getnext(pp1, &ac1); } - 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, PROPS_SINGLEMODE, 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_atstart(pp, PROPS_MULTIMODE, - 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; - } + props_clearvalues(pp); freebytes(pp, sizeof(*pp)); } void props_freeall(t_props *pp) { - if (pp && (pp = pp->p_otherprops)) + if (pp && (pp = pp->p_firstmixup)) { while (pp) { @@ -461,36 +595,36 @@ void props_freeall(t_props *pp) pp = pp1; } } - else bug("props_freeall"); + else loudbug_bug("props_freeall"); } -void props_setupothers(t_props *pp, t_props *otherprops) +static void props_setupmixups(t_props *pp, t_props *mixup) { t_props *pp1; - pp->p_next = (otherprops ? otherprops->p_otherprops : 0); + pp->p_next = (mixup ? mixup->p_firstmixup : 0); for (pp1 = pp; pp1; pp1 = pp1->p_next) { t_props *pp2; - char *bp = pp1->p_otherescapes; + char *bp = pp1->p_mixupescapes; int i; - pp1->p_otherprops = pp; - for (pp2 = pp, i = 1; pp2 && i < PROPS_MAXOTHERS; + pp1->p_firstmixup = pp; + for (pp2 = pp, i = 1; pp2 && i < PROPS_MAXMIXUPS; 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); + loudbug_startpost("%c \"%s\" ", pp1->p_thisescape, pp1->p_mixupescapes); #endif } #ifdef PROPS_DEBUG - endpost(); + loudbug_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 *mixup, t_propsresolver resolver) { t_props *pp = getbytes(sizeof(*pp)); if (pp) @@ -503,20 +637,21 @@ t_props *props_new(t_pd *owner, char *name, char *thisdelim, } else { - bug("props_new (no escape)"); + loudbug_bug("props_new (no escape)"); pp->p_thisescape = '-'; pp->p_thisinitial = 0; } - props_setupothers(pp, otherprops); + props_setupmixups(pp, mixup); pp->p_size = PROPS_INISIZE; pp->p_natoms = 0; pp->p_buffer = pp->p_bufini; + pp->p_nextindex = -1; if (pp->p_owner = owner) pp->p_resolver = resolver; else { if (resolver) - bug("props_new (no owner)"); + loudbug_bug("props_new (no owner)"); pp->p_resolver = 0; } pp->p_dict = 0; |