/* Copyright (c) 2002-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 #include "m_pd.h" #include "common/grow.h" #define SUBSTITUTE_INISIZE 32 /* LATER rethink */ #define SUBSTITUTE_MAXSIZE 256 typedef struct _substitute { t_object x_ob; t_pd *x_proxy; t_atom x_match; t_atom x_repl; int x_size; /* as allocated */ t_atom *x_message; t_atom x_messini[SUBSTITUTE_INISIZE]; int x_entered; t_atom x_auxmatch; t_atom x_auxrepl; t_outlet *x_passout; } t_substitute; typedef struct _substitute_proxy { t_object p_ob; t_atom *p_match; /* pointing to parent's (aux)match */ t_atom *p_repl; } t_substitute_proxy; static t_class *substitute_class; static t_class *substitute_proxy_class; /* LATER rethink */ static void substitute_dooutput(t_substitute *x, t_symbol *s, int ac, t_atom *av, int pass) { t_outlet *out = (pass ? x->x_passout : ((t_object *)x)->ob_outlet); if (s == &s_float) { if (ac > 1) outlet_list(out, &s_list, ac, av); else outlet_float(out, av->a_w.w_float); } else if (s == &s_bang && !ac) /* CHECKED */ outlet_bang(out); else if (s == &s_symbol && ac == 1 && av->a_type == A_SYMBOL) outlet_symbol(out, av->a_w.w_symbol); else if (s) outlet_anything(out, s, ac, av); else if (ac) outlet_list(out, &s_list, ac, av); } static int substitute_check(t_substitute *x, t_symbol *s, int ac, t_atom *av) { if (x->x_repl.a_type == A_NULL) return (-2); /* see substitute_proxy_validate() for possible types and values */ if (x->x_match.a_type == A_FLOAT) { t_float f = x->x_match.a_w.w_float; int i; for (i = 0; i < ac; i++, av++) if (av->a_type == A_FLOAT && av->a_w.w_float == f) return (i); } else if (x->x_match.a_type == A_SYMBOL) { /* match symbol is validated -- never null */ t_symbol *match = x->x_match.a_w.w_symbol; int i; if (s == match) return (-1); for (i = 0; i < ac; i++, av++) if (av->a_type == A_SYMBOL && av->a_w.w_symbol == match) return (i); } return (-2); } static void substitute_doit(t_substitute *x, t_symbol *s, int ac, t_atom *av, int startndx) { int cnt = ac - startndx; if (cnt > 0) { t_atom *ap = av + startndx; if (x->x_match.a_type == A_FLOAT) { t_float f = x->x_match.a_w.w_float; while (cnt--) { if (ap->a_type == A_FLOAT && ap->a_w.w_float == f) *ap = x->x_repl; ap++; } } else if (x->x_match.a_type == A_SYMBOL) { t_symbol *match = x->x_match.a_w.w_symbol; while (cnt--) { if (ap->a_type == A_SYMBOL && ap->a_w.w_symbol == match) *ap = x->x_repl; ap++; } } } substitute_dooutput(x, s, ac, av, 0); } static void substitute_anything(t_substitute *x, t_symbol *s, int ac, t_atom *av) { int matchndx = substitute_check(x, s, ac, av); if (matchndx < -1) substitute_dooutput(x, s, ac, av, 1); else { int reentered = x->x_entered; int prealloc = !reentered; int ntotal = ac; t_atom *buf; t_substitute_proxy *proxy = (t_substitute_proxy *)x->x_proxy; x->x_entered = 1; proxy->p_match = &x->x_auxmatch; proxy->p_repl = &x->x_auxrepl; if (s == &s_) s = 0; if (matchndx == -1) { if (x->x_repl.a_type == A_FLOAT) { ntotal++; if (ac) s = &s_list; else s = &s_float; } else if (x->x_repl.a_type == A_SYMBOL) { s = x->x_repl.a_w.w_symbol; matchndx = 0; } } else if (matchndx == 0 && (!s || s == &s_list || s == &s_float) && av->a_type == A_FLOAT && x->x_repl.a_type == A_SYMBOL) { s = x->x_repl.a_w.w_symbol; ac--; av++; ntotal = ac; } if (prealloc && ac > x->x_size) { if (ntotal > SUBSTITUTE_MAXSIZE) prealloc = 0; else x->x_message = grow_nodata(&ntotal, &x->x_size, x->x_message, SUBSTITUTE_INISIZE, x->x_messini, sizeof(*x->x_message)); } if (prealloc) buf = x->x_message; else /* LATER consider using the stack if ntotal <= MAXSTACK */ buf = getbytes(ntotal * sizeof(*buf)); if (buf) { int ncopy = ntotal; t_atom *bp = buf; if (matchndx == -1) { SETFLOAT(bp++, x->x_repl.a_w.w_float); ncopy--; } if (ncopy) memcpy(bp, av, ncopy * sizeof(*buf)); substitute_doit(x, s, ntotal, buf, matchndx); if (buf != x->x_message) freebytes(buf, ntotal * sizeof(*buf)); } if (!reentered) { x->x_entered = 0; if (x->x_auxmatch.a_type != A_NULL) { x->x_match = x->x_auxmatch; x->x_auxmatch.a_type = A_NULL; } if (x->x_auxrepl.a_type != A_NULL) { x->x_repl = x->x_auxrepl; x->x_auxrepl.a_type = A_NULL; } proxy->p_match = &x->x_match; proxy->p_repl = &x->x_repl; } } } static void substitute_bang(t_substitute *x) { substitute_anything(x, &s_bang, 0, 0); } static void substitute_float(t_substitute *x, t_float f) { t_atom at; SETFLOAT(&at, f); substitute_anything(x, 0, 1, &at); } /* CHECKED (but LATER rethink) */ static void substitute_symbol(t_substitute *x, t_symbol *s) { t_atom at; SETSYMBOL(&at, s); substitute_anything(x, &s_symbol, 1, &at); } /* LATER gpointer */ static void substitute_list(t_substitute *x, t_symbol *s, int ac, t_atom *av) { substitute_anything(x, 0, ac, av); } static int substitute_atomvalidate(t_atom *ap) { return (ap->a_type == A_FLOAT || (ap->a_type == A_SYMBOL && ap->a_w.w_symbol && ap->a_w.w_symbol != &s_)); } /* CHECKED: 'set' is ignored, single '' does not modify a replacement */ static void substitute_proxy_anything(t_substitute_proxy *x, t_symbol *s, int ac, t_atom *av) { if (s == &s_) s = 0; if (s) { SETSYMBOL(x->p_match, s); if (ac && substitute_atomvalidate(av)) *x->p_repl = *av; } else if (ac && substitute_atomvalidate(av)) { *x->p_match = *av++; if (ac > 1 && substitute_atomvalidate(av)) *x->p_repl = *av; } } static void substitute_proxy_bang(t_substitute_proxy *x) { SETSYMBOL(x->p_match, &s_bang); } static void substitute_proxy_float(t_substitute_proxy *x, t_float f) { SETFLOAT(x->p_match, f); } /* CHECKED (but LATER rethink) */ static void substitute_proxy_symbol(t_substitute_proxy *x, t_symbol *s) { SETSYMBOL(x->p_match, &s_symbol); SETSYMBOL(x->p_repl, s); } /* LATER gpointer */ static void substitute_proxy_list(t_substitute_proxy *x, t_symbol *s, int ac, t_atom *av) { substitute_proxy_anything(x, 0, ac, av); } static void substitute_free(t_substitute *x) { if (x->x_proxy) pd_free(x->x_proxy); if (x->x_message != x->x_messini) freebytes(x->x_message, x->x_size * sizeof(*x->x_message)); } static void *substitute_new(t_symbol *s, int ac, t_atom *av) { t_substitute *x = 0; t_substitute_proxy *proxy = (t_substitute_proxy *)pd_new(substitute_proxy_class); if (proxy) { x = (t_substitute *)pd_new(substitute_class); proxy->p_match = &x->x_match; proxy->p_repl = &x->x_repl; x->x_proxy = (t_pd *)proxy; x->x_size = SUBSTITUTE_INISIZE; x->x_message = x->x_messini; x->x_entered = 0; /* CHECKED: everything is to be passed unchanged, until both are set */ /* CHECKED: max crashes if a match has been set, but not a replacement, and there is a match */ x->x_match.a_type = x->x_repl.a_type = A_NULL; x->x_auxmatch.a_type = x->x_auxrepl.a_type = A_NULL; inlet_new((t_object *)x, (t_pd *)proxy, 0, 0); outlet_new((t_object *)x, &s_anything); /* CHECKED (refman error: 'a bang is sent') */ x->x_passout = outlet_new((t_object *)x, &s_anything); substitute_proxy_anything(proxy, 0, ac, av); } return (x); } void substitute_setup(void) { substitute_class = class_new(gensym("substitute"), (t_newmethod)substitute_new, (t_method)substitute_free, sizeof(t_substitute), 0, A_GIMME, 0); class_addbang(substitute_class, substitute_bang); class_addfloat(substitute_class, substitute_float); class_addsymbol(substitute_class, substitute_symbol); class_addlist(substitute_class, substitute_list); class_addanything(substitute_class, substitute_anything); substitute_proxy_class = class_new(gensym("_substitute_proxy"), 0, 0, sizeof(t_substitute_proxy), CLASS_PD | CLASS_NOINLET, 0); class_addbang(substitute_proxy_class, substitute_proxy_bang); class_addfloat(substitute_proxy_class, substitute_proxy_float); class_addsymbol(substitute_proxy_class, substitute_proxy_symbol); class_addlist(substitute_proxy_class, substitute_proxy_list); class_addanything(substitute_proxy_class, substitute_proxy_anything); class_addmethod(substitute_proxy_class, (t_method)substitute_proxy_list, gensym("set"), A_GIMME, 0); }