aboutsummaryrefslogtreecommitdiff
path: root/cyclone/hammer/sprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'cyclone/hammer/sprintf.c')
-rw-r--r--cyclone/hammer/sprintf.c634
1 files changed, 634 insertions, 0 deletions
diff --git a/cyclone/hammer/sprintf.c b/cyclone/hammer/sprintf.c
new file mode 100644
index 0000000..aebfed3
--- /dev/null
+++ b/cyclone/hammer/sprintf.c
@@ -0,0 +1,634 @@
+/* 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. */
+
+/* FIXME empty string as a default for %s */
+
+#include <stdio.h>
+#include <string.h>
+#include "m_pd.h"
+#include "common/loud.h"
+
+#define SPRINTF_DEBUG 0
+
+/* Pattern types. These are the parsing routine's return values.
+ If returned value is >= SPRINTF_MINSLOTTYPE, then another slot
+ is created (i.e. an inlet, and a proxy handling it). */
+#define SPRINTF_UNSUPPORTED 0
+#define SPRINTF_LITERAL 1
+#define SPRINTF_MINSLOTTYPE 2
+#define SPRINTF_INT 2
+#define SPRINTF_FLOAT 3
+#define SPRINTF_CHAR 4
+#define SPRINTF_STRING 5
+
+/* Numbers: assuming max 62 digits preceding a decimal point in any
+ fixed-point representation of a t_float (39 in my system)
+ -- need to be sure, that using max precision would never produce
+ a representation longer than max width. If this is so, then no number
+ representation would exceed max width (presumably...).
+ Strings: for the time being, any string longer than max width would
+ be truncated (somehow compatible with Str256, but LATER warn-and-allow). */
+/* LATER rethink it all */
+#define SPRINTF_MAXPRECISION 192
+#define SPRINTF_MAXWIDTH 256
+
+typedef struct _sprintf
+{
+ t_object x_ob;
+ int x_nslots;
+ int x_nproxies; /* as requested (and allocated) */
+ t_pd **x_proxies;
+ int x_fsize; /* as allocated (i.e. including a terminating 0) */
+ char *x_fstring;
+} t_sprintf;
+
+typedef struct _sprintf_proxy
+{
+ t_object p_ob;
+ t_sprintf *p_master;
+ int p_id;
+ int p_type; /* a value #defined above */
+ char *p_pattern;
+ char *p_pattend;
+ t_atom p_atom; /* current input */
+ int p_size; /* also an input validation flag */
+} t_sprintf_proxy;
+
+static t_class *sprintf_class;
+static t_class *sprintf_proxy_class;
+
+/* CHECKED: 'symout' argument has no special meaning in max4.07,
+ LATER investigate */
+
+/* LATER use snprintf, if it is available on other systems (should be...) */
+static void sprintf_proxy_checkit(t_sprintf_proxy *x, char *buf, int checkin)
+{
+ int result = 0;
+ char *pattend = x->p_pattend;
+ if (pattend)
+ {
+ char tmp = *pattend;
+ *pattend = 0;
+ if (x->p_atom.a_type == A_FLOAT)
+ {
+ t_float f = x->p_atom.a_w.w_float;
+ if (x->p_type == SPRINTF_INT)
+ /* CHECKME large/negative values */
+ result = sprintf(buf, x->p_pattern, (int)f);
+ else if (x->p_type == SPRINTF_FLOAT)
+ result = sprintf(buf, x->p_pattern, f);
+ else if (x->p_type == SPRINTF_CHAR)
+ /* CHECKED: if 0 is input into a %c-slot, the whole output
+ string is null-terminated */
+ /* CHECKED: float into a %c-slot is truncated,
+ but CHECKME large/negative values */
+ result = sprintf(buf, x->p_pattern, (unsigned char)f);
+ else if (x->p_type == SPRINTF_STRING)
+ {
+ /* CHECKED: any number input into a %s-slot is ok */
+ char tmp[64]; /* LATER rethink */
+ sprintf(tmp, "%g", f);
+ result = sprintf(buf, x->p_pattern, tmp);
+ }
+ else /* LATER consider calling it a bug(), rather than error? */
+ loud_error((t_pd *)x->p_master,
+ "can't convert float to type of argument %d",
+ x->p_id + 1);
+ }
+ else if (x->p_atom.a_type == A_SYMBOL)
+ {
+ t_symbol *s = x->p_atom.a_w.w_symbol;
+ if (x->p_type == SPRINTF_STRING)
+ {
+ if (strlen(s->s_name) > SPRINTF_MAXWIDTH)
+ {
+ strncpy(buf, s->s_name, SPRINTF_MAXWIDTH);
+ buf[SPRINTF_MAXWIDTH] = 0;
+ result = SPRINTF_MAXWIDTH;
+ }
+ else result = sprintf(buf, x->p_pattern, s->s_name);
+ }
+ else /* CHECKED */
+ loud_error((t_pd *)x->p_master,
+ "can't convert symbol to type of argument %d",
+ x->p_id + 1);
+ }
+ *pattend = tmp;
+ }
+ else bug("sprintf_proxy_checkit");
+ if (result > 0)
+ {
+#if SPRINTF_DEBUG
+ if (checkin) post("[%d in \"%s\"]", result, buf);
+#endif
+ x->p_size = result;
+ }
+ else
+ {
+#if SPRINTF_DEBUG
+ if (checkin) post("checkit failed");
+#endif
+ x->p_size = 0;
+ }
+}
+
+static void sprintf_dooutput(t_sprintf *x)
+{
+ int i, outsize;
+ char *outstring;
+ outsize = x->x_fsize; /* this is strlen() + 1 */
+ /* LATER consider subtracting format pattern sizes */
+ for (i = 0; i < x->x_nslots; i++)
+ {
+ t_sprintf_proxy *y = (t_sprintf_proxy *)x->x_proxies[i];
+ if (y->p_size)
+ outsize += y->p_size;
+ else
+ {
+ /* slot i has received an invalid input -- CHECKME if this
+ condition blocks all subsequent output requests? */
+ return;
+ }
+ }
+ if (outsize > 0 && (outstring = getbytes(outsize)))
+ {
+ char *inp = x->x_fstring;
+ char *outp = outstring;
+ for (i = 0; i < x->x_nslots; i++)
+ {
+ t_sprintf_proxy *y = (t_sprintf_proxy *)x->x_proxies[i];
+ int len = y->p_pattern - inp;
+ if (len > 0)
+ {
+ strncpy(outp, inp, len);
+ outp += len;
+ }
+ sprintf_proxy_checkit(y, outp, 0);
+ outp += y->p_size; /* p_size is never negative */
+ inp = y->p_pattend;
+ }
+ strcpy(outp, inp);
+
+ outp = outstring;
+ while (*outp == ' ' || *outp == '\t'
+ || *outp == '\n' || *outp == '\r') outp++;
+ if (*outp)
+ {
+ t_binbuf *bb = binbuf_new();
+ int ac;
+ t_atom *av;
+ binbuf_text(bb, outp, strlen(outp));
+ ac = binbuf_getnatom(bb);
+ av = binbuf_getvec(bb);
+ if (ac)
+ {
+ if (av->a_type == A_SYMBOL)
+ outlet_anything(((t_object *)x)->ob_outlet,
+ av->a_w.w_symbol, ac - 1, av + 1);
+ else if (av->a_type == A_FLOAT)
+ {
+ if (ac > 1)
+ outlet_list(((t_object *)x)->ob_outlet,
+ &s_list, ac, av);
+ else
+ outlet_float(((t_object *)x)->ob_outlet,
+ av->a_w.w_float);
+ }
+ }
+ binbuf_free(bb);
+ }
+ freebytes(outstring, outsize);
+ }
+}
+
+static void sprintf_proxy_bang(t_sprintf_proxy *x)
+{
+ sprintf_dooutput(x->p_master); /* CHECKED (in any inlet) */
+}
+
+static void sprintf_proxy_float(t_sprintf_proxy *x, t_float f)
+{
+ char buf[SPRINTF_MAXWIDTH + 1]; /* LATER rethink */
+ SETFLOAT(&x->p_atom, f);
+ sprintf_proxy_checkit(x, buf, 1);
+ if (x->p_id == 0 && x->p_size)
+ sprintf_dooutput(x->p_master); /* CHECKED: only first inlet */
+}
+
+static void sprintf_proxy_symbol(t_sprintf_proxy *x, t_symbol *s)
+{
+ char buf[SPRINTF_MAXWIDTH + 1]; /* LATER rethink */
+ if (s && *s->s_name)
+ SETSYMBOL(&x->p_atom, s);
+ else
+ SETFLOAT(&x->p_atom, 0);
+ sprintf_proxy_checkit(x, buf, 1);
+ if (x->p_id == 0 && x->p_size)
+ sprintf_dooutput(x->p_master); /* CHECKED: only first inlet */
+}
+
+static void sprintf_dolist(t_sprintf *x,
+ t_symbol *s, int ac, t_atom *av, int startid)
+{
+ int cnt = x->x_nslots - startid;
+ if (ac > cnt)
+ ac = cnt;
+ if (ac-- > 0)
+ {
+ int id;
+ for (id = startid + ac, av += ac; id >= startid; id--, av--)
+ {
+ if (av->a_type == A_FLOAT)
+ sprintf_proxy_float((t_sprintf_proxy *)x->x_proxies[id],
+ av->a_w.w_float);
+ else if (av->a_type == A_SYMBOL)
+ sprintf_proxy_symbol((t_sprintf_proxy *)x->x_proxies[id],
+ av->a_w.w_symbol);
+ }
+ }
+}
+
+static void sprintf_doanything(t_sprintf *x,
+ t_symbol *s, int ac, t_atom *av, int startid)
+{
+ if (s && s != &s_)
+ {
+ sprintf_dolist(x, 0, ac, av, startid + 1);
+ sprintf_proxy_symbol((t_sprintf_proxy *)x->x_proxies[startid], s);
+ }
+ else sprintf_dolist(x, 0, ac, av, startid);
+}
+
+static void sprintf_proxy_list(t_sprintf_proxy *x,
+ t_symbol *s, int ac, t_atom *av)
+{
+ sprintf_dolist(x->p_master, s, ac, av, x->p_id);
+}
+
+static void sprintf_proxy_anything(t_sprintf_proxy *x,
+ t_symbol *s, int ac, t_atom *av)
+{
+ sprintf_doanything(x->p_master, s, ac, av, x->p_id);
+}
+
+static void sprintf_bang(t_sprintf *x)
+{
+ if (x->x_nslots)
+ sprintf_proxy_bang((t_sprintf_proxy *)x->x_proxies[0]);
+}
+
+static void sprintf_float(t_sprintf *x, t_float f)
+{
+ if (x->x_nslots)
+ sprintf_proxy_float((t_sprintf_proxy *)x->x_proxies[0], f);
+}
+
+static void sprintf_symbol(t_sprintf *x, t_symbol *s)
+{
+ if (x->x_nslots)
+ sprintf_proxy_symbol((t_sprintf_proxy *)x->x_proxies[0], s);
+}
+
+static void sprintf_list(t_sprintf *x, t_symbol *s, int ac, t_atom *av)
+{
+ if (x->x_nslots)
+ sprintf_dolist(x, s, ac, av, 0);
+}
+
+static void sprintf_anything(t_sprintf *x, t_symbol *s, int ac, t_atom *av)
+{
+ if (x->x_nslots)
+ sprintf_doanything(x, s, ac, av, 0);
+}
+
+/* adjusted binbuf_gettext(), LATER do it right */
+static char *hammer_gettext(int ac, t_atom *av, int *sizep)
+{
+ char *buf = getbytes(1);
+ int size = 1;
+ char atomtext[MAXPDSTRING];
+ while (ac--)
+ {
+ char *newbuf;
+ int newsize;
+ if (buf[size-1] == 0 || av->a_type == A_SEMI || av->a_type == A_COMMA)
+ size--;
+ atom_string(av, atomtext, MAXPDSTRING);
+ newsize = size + strlen(atomtext) + 1;
+ if (!(newbuf = resizebytes(buf, size, newsize)))
+ {
+ *sizep = 1;
+ return (getbytes(1));
+ }
+ buf = newbuf;
+ strcpy(buf + size, atomtext);
+ size = newsize;
+ buf[size-1] = ' ';
+ av++;
+ }
+ buf[size-1] = 0;
+ *sizep = size;
+ return (buf);
+}
+
+/* Called twice: 1st pass (with x == 0) is used for counting valid patterns;
+ 2nd pass (after object allocation) -- for initializing the proxies.
+ If there is a "%%" pattern, then the buffer is shrinked in the second pass
+ (LATER rethink). */
+static int sprintf_parsepattern(t_sprintf *x, char **patternp)
+{
+ int type = SPRINTF_UNSUPPORTED;
+ char errstring[MAXPDSTRING];
+ char *ptr;
+ char modifier = 0;
+ int width = 0;
+ int precision = 0;
+ int *numfield = &width;
+ int dotseen = 0;
+ *errstring = 0;
+ for (ptr = *patternp; *ptr; ptr++)
+ {
+ if (*ptr >= '0' && *ptr <= '9')
+ {
+ if (!numfield)
+ {
+ if (x) sprintf(errstring, "extra number field");
+ break;
+ }
+ *numfield = 10 * *numfield + *ptr - '0';
+ if (dotseen)
+ {
+ if (precision > SPRINTF_MAXPRECISION)
+ {
+ if (x) sprintf(errstring, "precision field too large");
+ break;
+ }
+ }
+ else
+ {
+ if (width > SPRINTF_MAXWIDTH)
+ {
+ if (x) sprintf(errstring, "width field too large");
+ break;
+ }
+ }
+ continue;
+ }
+ if (*numfield)
+ numfield = 0;
+
+ if (strchr("diouxX", *ptr))
+ {
+ type = SPRINTF_INT;
+ break;
+ }
+ else if (strchr("aAeEfgG", *ptr))
+ {
+ if (modifier)
+ {
+ if (x) sprintf(errstring,
+ "\'%c\' modifier not supported", modifier);
+ break;
+ }
+ type = SPRINTF_FLOAT;
+ break;
+ }
+ else if (strchr("c", *ptr))
+ {
+ if (modifier)
+ {
+ if (x) sprintf(errstring,
+ "\'%c\' modifier not supported", modifier);
+ break;
+ }
+ type = SPRINTF_CHAR;
+ break;
+ }
+ else if (strchr("s", *ptr))
+ {
+ if (modifier)
+ {
+ if (x) sprintf(errstring,
+ "\'%c\' modifier not supported", modifier);
+ break;
+ }
+ type = SPRINTF_STRING;
+ break;
+ }
+ else if (*ptr == '%')
+ {
+ type = SPRINTF_LITERAL;
+ if (x)
+ { /* buffer-shrinking hack, LATER rethink */
+ char *p1 = ptr, *p2 = ptr + 1;
+ do
+ *p1++ = *p2;
+ while (*p2++);
+ ptr--;
+ }
+ break;
+ }
+ else if (strchr("CSnm", *ptr))
+ {
+ if (x) sprintf(errstring, "\'%c\' type not supported", *ptr);
+ break;
+ }
+ else if (strchr("l", *ptr))
+ {
+ if (modifier)
+ {
+ if (x) sprintf(errstring, "only single modifier is supported");
+ break;
+ }
+ modifier = *ptr;
+ }
+ else if (strchr("hjLqtzZ", *ptr))
+ {
+ if (x) sprintf(errstring, "\'%c\' modifier not supported", *ptr);
+ break;
+ }
+ else if (*ptr == '.')
+ {
+ if (dotseen)
+ {
+ if (x) sprintf(errstring, "multiple dots");
+ break;
+ }
+ numfield = &precision;
+ dotseen = 1;
+ }
+ else if (*ptr == '$')
+ {
+ if (x) sprintf(errstring, "parameter number field not supported");
+ break;
+ }
+ else if (*ptr == '*')
+ {
+ if (x) sprintf(errstring, "%s parameter not supported",
+ (dotseen ? "precision" : "width"));
+ break;
+ }
+ else if (!strchr("-+ #\'", *ptr))
+ {
+ if (x) sprintf(errstring,
+ "\'%c\' format character not supported", *ptr);
+ break;
+ }
+ }
+ if (*ptr)
+ ptr++; /* LATER rethink */
+ else
+ if (x) sprintf(errstring, "type not specified");
+ if (x && type == SPRINTF_UNSUPPORTED)
+ {
+ if (*errstring)
+ loud_error((t_pd *)x, "slot skipped (%s %s)",
+ errstring, "in a format pattern");
+ else
+ loud_error((t_pd *)x, "slot skipped");
+ }
+ *patternp = ptr;
+ return (type);
+}
+
+static void sprintf_free(t_sprintf *x)
+{
+ if (x->x_proxies)
+ {
+ int i = x->x_nslots;
+ while (i--)
+ {
+ t_sprintf_proxy *y = (t_sprintf_proxy *)x->x_proxies[i];
+ pd_free((t_pd *)y);
+ }
+ freebytes(x->x_proxies, x->x_nproxies * sizeof(*x->x_proxies));
+ }
+ if (x->x_fstring)
+ freebytes(x->x_fstring, x->x_fsize);
+}
+
+static void *sprintf_new(t_symbol *s, int ac, t_atom *av)
+{
+ t_sprintf *x;
+ int fsize;
+ char *fstring;
+ char *p1, *p2;
+ int i, nslots, nproxies = 0;
+ t_pd **proxies;
+ fstring = hammer_gettext(ac, av, &fsize);
+ p1 = fstring;
+ while (p2 = strchr(p1, '%'))
+ {
+ int type;
+ p1 = p2 + 1;
+ type = sprintf_parsepattern(0, &p1);
+ if (type >= SPRINTF_MINSLOTTYPE)
+ nproxies++;
+ }
+ if (!nproxies)
+ {
+ /* CHECKED: an object without arguments, if created in the editor,
+ has no inlets/outlets, but it would have one inlet (no outlets)
+ upon loading. Error message is printed in either case. */
+ x = (t_sprintf *)pd_new(sprintf_class);
+ x->x_nslots = 0;
+ x->x_nproxies = 0;
+ x->x_proxies = 0;
+ x->x_fsize = fsize;
+ x->x_fstring = fstring;
+ p1 = fstring;
+ while (p2 = strchr(p1, '%'))
+ {
+ p1 = p2 + 1;
+ sprintf_parsepattern(x, &p1);
+ }
+ loud_error((t_pd *)x,
+ "an object created without valid format patterns...");
+ return (x);
+ }
+#if SPRINTF_DEBUG
+ post("%d slots:", nproxies);
+#endif
+ /* CHECKED: max creates as many inlets, as there are %-signs, no matter
+ if they are valid, or not -- if not, it prints ``can't convert'' errors
+ for any input... */
+ if (!(proxies = (t_pd **)getbytes(nproxies * sizeof(*proxies))))
+ {
+ freebytes(fstring, fsize);
+ return (0);
+ }
+ for (nslots = 0; nslots < nproxies; nslots++)
+ if (!(proxies[nslots] = pd_new(sprintf_proxy_class))) break;
+ if (!nslots)
+ {
+ freebytes(fstring, fsize);
+ freebytes(proxies, nproxies * sizeof(*proxies));
+ return (0);
+ }
+ x = (t_sprintf *)pd_new(sprintf_class);
+ x->x_nslots = nslots;
+ x->x_nproxies = nproxies;
+ x->x_proxies = proxies;
+ x->x_fsize = fsize;
+ x->x_fstring = fstring;
+ p1 = fstring;
+ i = 0;
+ while (p2 = strchr(p1, '%'))
+ {
+ int type;
+ p1 = p2 + 1;
+ type = sprintf_parsepattern(x, &p1);
+ if (type >= SPRINTF_MINSLOTTYPE)
+ {
+#if SPRINTF_DEBUG
+ char tmp = *++p1;
+ *p1 = 0;
+ poststring(p2);
+ endpost();
+ *p1 = tmp;
+#endif
+ if (i < nslots)
+ {
+ char buf[SPRINTF_MAXWIDTH + 1]; /* LATER rethink */
+ t_sprintf_proxy *y = (t_sprintf_proxy *)proxies[i];
+ y->p_master = x;
+ y->p_id = i;
+ y->p_type = type;
+ y->p_pattern = p2;
+ y->p_pattend = p1;
+ SETFLOAT(&y->p_atom, 0);
+ y->p_size = 0;
+ if (i) inlet_new((t_object *)x, (t_pd *)y, 0, 0);
+ sprintf_proxy_checkit(y, buf, 1);
+ i++;
+ }
+ }
+ }
+#if SPRINTF_DEBUG
+ post("printf(\"%s\", ...)", fstring);
+#endif
+ outlet_new((t_object *)x, &s_anything);
+ return (x);
+}
+
+void sprintf_setup(void)
+{
+ sprintf_class = class_new(gensym("sprintf"),
+ (t_newmethod)sprintf_new,
+ (t_method)sprintf_free,
+ sizeof(t_sprintf), 0, A_GIMME, 0);
+ class_addbang(sprintf_class, sprintf_bang);
+ class_addfloat(sprintf_class, sprintf_float);
+ class_addsymbol(sprintf_class, sprintf_symbol);
+ class_addlist(sprintf_class, sprintf_list);
+ class_addanything(sprintf_class, sprintf_anything);
+ sprintf_proxy_class = class_new(gensym("_sprintf_proxy"), 0, 0,
+ sizeof(t_sprintf_proxy),
+ CLASS_PD | CLASS_NOINLET, 0);
+ class_addbang(sprintf_proxy_class, sprintf_proxy_bang);
+ class_addfloat(sprintf_proxy_class, sprintf_proxy_float);
+ class_addsymbol(sprintf_proxy_class, sprintf_proxy_symbol);
+ class_addlist(sprintf_proxy_class, sprintf_proxy_list);
+ class_addanything(sprintf_proxy_class, sprintf_proxy_anything);
+}