diff --git a/pd/src/x_connective.c b/pd/src/x_connective.c
index 2e61671dcb9802030c7657e506a822c148e5c137..eba1be124059507ca84a21608d1f039483224b1d 100644
--- a/pd/src/x_connective.c
+++ b/pd/src/x_connective.c
@@ -1449,50 +1449,251 @@ static void until_setup(void)
 
 static t_class *makefilename_class;
 
+typedef enum {
+    NONE = 0,
+    INT,
+    FLOAT,
+    STRING,
+    POINTER,
+} t_printtype;
+
 typedef struct _makefilename
 {
     t_object x_obj;
     t_symbol *x_format;
-    t_atomtype x_accept;
-    int x_intconvert;
+    t_printtype x_accept;
 } t_makefilename;
 
-static void makefilename_scanformat(t_makefilename *x)
+static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
+    char *errormsg)
 {
-    int infmt=0;
-    char *str;
-    if (!x->x_format) return;
-    x->x_accept = A_NULL;
-    for (str=x->x_format->s_name; *str; str++)
+    int infmt = 0, i, hashflag = 0, zeroflag = 0;
+    for (; *str; str++)
     {
-        if (!infmt && *str=='%')
+        if (!infmt && *str == '%')
         {
-            infmt=1;
+            /* A single '%' character isn't a valid. It needs a conversion
+               specifier ('g', 's', etc.) to follow it */
+            if (*(str+1) == '\0')
+            {
+                str++;
+                sprintf(errormsg, "specifier missing");
+                *typ = NONE;
+                return str;
+            }
+            infmt = 1;
             continue;
         }
         if (infmt)
         {
-            if (strchr("-.#0123456789",*str)!=0)
+            /* 1) flags
+               Check for "%%" which produces a literal '%' in the output */
+            if (*str == '%')
+            {
+                /* Not in a format specifier after all, so let's reset
+                   infmt and continue searching... */
+                infmt = 0;
                 continue;
-            if (*str=='s')
+            }
+            for (i = 0; *str && strchr("-+#0", *str) != 0; str++, i++)
+            {
+                /* Check for flags. While a space is a legal flag, Pd's
+                   parser would split it off into a separate arg. And since
+                   makefilename has always truncated extra args we don't
+                   support spaces here. We also don't support the single
+                   quote flag.
+                   Some flag/specifier combinations can cause undefined
+                   behavior so we need to track them. */
+
+                if (*str == '#') hashflag++;
+                if (*str == '0') zeroflag++;
+
+                /* Since we're dealing with arbitrary input let's keep
+                   the total number of flags sane. */
+                if (i > 15)
+                {
+                    sprintf(errormsg, "too many flags");
+                    *typ = NONE;
+                    return str;
+                }
+            }
+            /* 2) width field
+               Consecutive digits. Technically a width field may also be
+               '*' to use a variable to set the value. But makefilename has
+               never supported that, either generating a memory error or crash
+               upon use. So we exclude it here. */
+            if (*str == '*')
             {
-                x->x_accept = A_SYMBOL;
-                x->x_intconvert = 0;
-                break;
+                sprintf(errormsg, "variable width value not supported");
+                *typ = NONE;
+                return str;
             }
-            if (strchr("fgGeE",*str)!=0)
+            int maxwidth = 3;
+            for (i = 0; *str && strchr("0123456789", *str) != 0; str++, i++)
             {
-                x->x_accept = A_FLOAT;
-                x->x_intconvert = 0;
-                break;
+                /* Limit width length to prevent out-of-memory errors. */
+                if (i >= maxwidth)
+                {
+                    sprintf(errormsg, "width field cannot be greater than "
+                        "%d digits", maxwidth);
+                    *typ = NONE;
+                    return str;
+                }
             }
-            if (strchr("xXdiouc",*str)!=0)
+            /* 3) precision field
+               A '.' followed by consecutive digits. Can also have a '*' for
+               variable, so we need to check and exclude as with the width
+               field above. */
+            if (*str == '.')
             {
-                x->x_accept = A_FLOAT;
-                x->x_intconvert = 1;
-                break;
+                str++;
+                if (*str == '*')
+                {
+                    sprintf(errormsg, "variable precision field not supported");
+                    *typ = NONE;
+                    return str;
+                }
+                int maxwidth = 3;
+                for (i = 0; *str && strchr("0123456789", *str) != 0; str++, i++)
+                {
+                    if (i >= maxwidth)
+                    {
+                        sprintf(errormsg, "precision field cannot be greater "
+                            "than %d digits", maxwidth);
+                        *typ = NONE;
+                        return str;
+                    }
+                }
+                /* precision isn't defined for all conversion specifiers. */
+                if (*str && strchr("diouxXeEfFgGs", *str) == 0)
+                {
+                    sprintf(errormsg, "precision field restricted to "
+                                      "[diouxXeEfFgGs] specifiers");
+                    *typ = NONE;
+                    return str;
+                }
             }
-            infmt=0;
+            /* 4) length field
+               Fairly certain the length field doesn't make any sense
+               here. At least I've never seen it used, so let's skip it. */
+
+            /* 5) conversion specifier
+               The type of value we want to fill the slot with. */
+
+            /* First, check our flags against the allowed specifiers */
+            if (*str && hashflag && strchr("fFgGeExXo", *str) == 0)
+            {
+                sprintf(errormsg, "'#' flag restricted to [fFgGeExXo] "
+                                  "specifiers");
+                *typ = NONE;
+                return str;
+            }
+            if (*str && zeroflag && strchr("diouxXeEfFgG", *str) == 0)
+            {
+                sprintf(errormsg, "'0' flag restricted to [diouxXeEfFgG] "
+                                  "specifiers");
+                *typ = NONE;
+                return str;
+            }
+
+            /* a C string */
+            if (*str == 's')
+            {
+                *typ = STRING;
+                return str;
+            }
+
+            /* a float. There's also 'a' and 'A' from C99, but let's stick
+               to the old school ones for now... */
+            if (*str && strchr("fFgGeE", *str) != 0)
+            {
+                *typ = FLOAT;
+                return str;
+            }
+
+            /* an int */
+            if (*str && strchr("xXdiouc", *str) != 0)
+            {
+                *typ = INT;
+                return str;
+            }
+
+            /* a pointer. We don't suppor this in Purr Data because of the
+               possibility of both undefined and implementation-specific
+               behavior. */
+            if (*str && strchr("p", *str) != 0)
+            {
+                /* *typ = POINTER; */
+                /* return str; */
+                sprintf(errormsg, "p specifier not supported");
+                *typ = NONE;
+                return str;
+            }
+
+            /* if we've gotten here it means we are missing a type field.
+               Undefined behavior would result, so we will bail here */
+            
+            /* First, let's check for any remaining type fields which
+               we don't support. That way we can give a better error message. */
+            if (*str == 'a' || *str == 'A')
+                sprintf(errormsg, "hexfloat specifier not supported. If you "
+                                  "need this feature make a case on the "
+                                  "mailing list and we'll consider adding it");
+            else if (*str == 'n')
+                sprintf(errormsg, "n specifier not supported");
+            else if (*str == 'm')
+                sprintf(errormsg, "m specifier not supported");
+            else if (*str)
+                sprintf(errormsg, "bad specifier type '%c'", *str);
+            else
+                sprintf(errormsg, "specifier missing");
+            *typ = NONE;
+            return str;
+        }
+    }
+    *typ = NONE;
+    return str;
+}
+
+static void makefilename_scanformat(t_makefilename *x)
+{
+    char errorbuf[MAXPDSTRING];
+    errorbuf[0] = '\0';
+    const char *str;
+    t_printtype typ;
+    if (!x->x_format) return;
+    str = x->x_format->s_name;
+    /* First attempt at parsing the format string for the field type. */
+    str = makefilename_doscanformat(str, &typ, errorbuf);
+    /* If we got any errors we will have some content in our errorbuf... */
+    if (*errorbuf)
+    {
+        pd_error(x, "makefilename: invalid format string '%s' "
+                    "(%s). Supressing output.",
+            x->x_format->s_name, errorbuf);
+        /* set the format string to zero. It would be great to refuse to
+           create the object here, but we also have to deal with new format
+           strings with the 'set' message. So for consistency we zero out
+           the format string and make it a runtimer error. */
+        x->x_format = 0;
+        return;
+    }
+    x->x_accept = typ;
+    if (str && (typ != NONE))
+    {
+        /* try again, to see if there's another format specifier
+           (which we forbid) */
+        str = makefilename_doscanformat(str, &typ, errorbuf);
+        /* If we've got a type other than none-- OR if we've got something
+           in our errorbuf-- we've got a syntax error. */
+        if (typ != NONE || *errorbuf)
+        {
+            pd_error(x, "makefilename: invalid format string '%s' "
+                        "(too many specifiers). "
+                        "Suppressing output.",
+                x->x_format->s_name);
+            x->x_format = 0;
+            return;
         }
     }
 }
@@ -1504,40 +1705,129 @@ static void *makefilename_new(t_symbol *s)
         s = gensym("file.%d");
     outlet_new(&x->x_obj, &s_symbol);
     x->x_format = s;
-    x->x_accept = A_NULL;
-    x->x_intconvert = 0;
+    x->x_accept = NONE;
     makefilename_scanformat(x);
     return (x);
 }
 
+static void makefilename_snprintf(t_makefilename *x, char *buf, char *fmt, ...)
+{
+    int length_minus_null_terminator;
+    va_list ap;
+    va_start(ap, fmt);
+    length_minus_null_terminator =
+        vsnprintf(buf, MAXPDSTRING, fmt, ap);
+    va_end(ap);
+    if (length_minus_null_terminator >= MAXPDSTRING)
+    {
+        /* Just don't trust snprintf... */
+        buf[MAXPDSTRING-1] = '\0';
+        pd_error(x, "makefilename: output truncated to %d characters",
+            MAXPDSTRING);
+    }
+}
+
 static void makefilename_float(t_makefilename *x, t_floatarg f)
 {
     char buf[MAXPDSTRING];
-    if (x->x_accept == A_FLOAT)
+    if (!x->x_format)
     {
-        if (x->x_intconvert)
-            sprintf(buf, x->x_format->s_name, (int)f);
-        else sprintf(buf, x->x_format->s_name, f);
+        pd_error(x, "makefilename: no format specifier given");
+        return;
     }
-    else
+    switch(x->x_accept)
     {
+    case FLOAT:
+        makefilename_snprintf(x, buf, x->x_format->s_name, f);
+        break;
+    case INT:
+        makefilename_snprintf(x, buf, x->x_format->s_name, (int)f);
+        break;
+    case STRING: {
         char buf2[MAXPDSTRING];
-        sprintf(buf2, "%g", f);
-        sprintf(buf, x->x_format->s_name, buf2);
+        makefilename_snprintf(x, buf2, "%g", f);
+        makefilename_snprintf(x, buf, x->x_format->s_name, buf2);
+        break;
+    case NONE:
+        makefilename_snprintf(x, buf, x->x_format->s_name);
+        break;
     }
-    if (buf[0]!=0)
-    outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+    default:
+        /* POINTER type would fall here. We probably don't want to expose
+           implementation-specific output for whatever rando float values
+           the user wants to hurl at makefilename. */
+        pd_error(x, "cannot convert float with specifier: %s",
+            x->x_format->s_name);
+        return;
+    }
+    if (buf[0] != 0)
+        outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
 }
 
 static void makefilename_symbol(t_makefilename *x, t_symbol *s)
 {
     char buf[MAXPDSTRING];
-    if (x->x_accept == A_SYMBOL)
-    sprintf(buf, x->x_format->s_name, s->s_name);
-    else
-        sprintf(buf, x->x_format->s_name, 0);
-    if (buf[0]!=0)
-    outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+    if (!x->x_format)
+    {
+        pd_error(x, "makefilename: no format specifier given");
+        return;
+    }
+    switch(x->x_accept)
+    {
+    case STRING:
+        makefilename_snprintf(x, buf, x->x_format->s_name, s->s_name);
+        break;
+    case INT:
+        makefilename_snprintf(x, buf, x->x_format->s_name, 0);
+        break;
+    case FLOAT:
+        makefilename_snprintf(x, buf, x->x_format->s_name, 0.);
+        break;
+    case NONE:
+        makefilename_snprintf(x, buf, x->x_format->s_name);
+        break;
+    default:
+         /* POINTER case falls here. Technically we could print out
+            symbol addys using whatever the compiler's implementation-specific
+            output format happens to be. But that probably shouldn't be exposed
+            within the patch like this. */
+        pd_error(x, "cannot convert symbol with specifier: %s",
+            x->x_format->s_name);
+        return;
+    }
+    if (buf[0] != 0)
+        outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+}
+
+static void makefilename_bang(t_makefilename *x)
+{
+    char buf[MAXPDSTRING];
+    if(!x->x_format)
+    {
+        pd_error(x, "makefilename: no format specifier given");
+        return;
+    }
+    switch(x->x_accept)
+    {
+    case INT:
+        makefilename_snprintf(x, buf, x->x_format->s_name, 0);
+        break;
+    case FLOAT:
+        makefilename_snprintf(x, buf, x->x_format->s_name, 0.);
+        break;
+    case STRING:
+        makefilename_snprintf(x, buf, x->x_format->s_name, "");
+        break;
+    case NONE:
+        makefilename_snprintf(x, buf, x->x_format->s_name);
+        break;
+    default:
+        pd_error(x, "cannot convert bang with specifier: %s",
+            x->x_format->s_name);
+        return;
+    }
+    if (buf[0] != 0)
+        outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
 }
 
 static void makefilename_set(t_makefilename *x, t_symbol *s)
@@ -1553,6 +1843,7 @@ static void makefilename_setup(void)
         sizeof(t_makefilename), 0, A_DEFSYM, 0);
     class_addfloat(makefilename_class, makefilename_float);
     class_addsymbol(makefilename_class, makefilename_symbol);
+    class_addbang(makefilename_class, makefilename_bang);
     class_addmethod(makefilename_class, (t_method)makefilename_set,
         gensym("set"), A_SYMBOL, 0);
 }
diff --git a/scripts/regression_tests.pd b/scripts/regression_tests.pd
index 37ba956bc54cf02415e3bdfc8e49648c182384dd..a7566b0ccbbac8d2ae964b9c575aa9ce590f260b 100644
--- a/scripts/regression_tests.pd
+++ b/scripts/regression_tests.pd
@@ -1,29 +1,29 @@
 #N canvas 3 60 749 617 12;
-#X obj 345 301 r \$0-result;
-#X obj 345 326 route 0;
-#X obj 453 470 print failure;
-#X obj 430 336 tgl 28 0 empty empty Print_All_Results 31 11 0 12 -262144
--1 -1 1 1;
+#X obj 475 301 r \$0-result;
+#X obj 475 326 route 0;
+#X obj 583 470 print failure;
+#X obj 560 336 tgl 28 0 empty empty Print_All_Results 31 11 0 12 -262144
+-1 -1 0 1;
 #X obj 159 149 bng 31 250 50 0 empty empty Run_all 39 13 0 12 -262144
 -1 -1;
 #X obj 56 25 r init;
 #X obj 345 191 route dollarzero;
-#X obj 345 411 t b a;
-#X obj 345 541 s pd;
+#X obj 475 411 t b a;
+#X obj 475 541 s pd;
 #X obj 56 120 trigger bang bang anything;
 #X msg 56 145 gui;
 #X obj 56 170 pdinfo;
 #X obj 56 195 sel 0;
 #X obj 56 245 s pd;
-#X msg 345 516 quit 1;
+#X msg 475 516 quit 1;
 #X msg 56 220 quit;
 #X obj 145 191 rtest msg_dollarzero;
 #X obj 145 246 rtest msg_dollarzero_semi;
 #X obj 145 302 rtest msg_click;
 #X obj 345 216 rtest binbuf_dollarzero;
-#X msg 345 440 gui;
-#X obj 345 465 pdinfo;
-#X obj 345 490 sel 0;
+#X msg 475 440 gui;
+#X obj 475 465 pdinfo;
+#X obj 475 490 sel 0;
 #X text 117 25 <- we start Pd with the -send "init etc." flag. This
 will automatically start the tests and allow us to send a comma-separated
 list of messages which will be evaluated by Pd without a target. This
@@ -34,13 +34,22 @@ is handy for some binbuf tests.;
 #X text 536 150 <- we have to escape the arg;
 #X text 556 190 escape it in a comment.;
 #X text 556 170 in bash but we can't;
-#X obj 391 374 spigot;
-#X obj 407 440 route 1;
-#X obj 407 495 print success;
+#X obj 521 374 spigot;
+#X obj 537 440 route 1;
+#X obj 537 495 print success;
 #X obj 145 358 rtest unpost_sanity;
 #X obj 145 414 rtest unpost_error;
 #X obj 145 465 rtest unpost_print;
 #X obj 145 516 rtest unpost_long_message;
+#X obj 145 569 rtest makefilename_double_percent;
+#X obj 145 620 rtest makefilename_code_coverage;
+#N canvas 461 242 450 323 (subpatch) 0;
+#X restore 148 1105 pd;
+#X obj 145 671 rtest makefilename_default;
+#X obj 145 722 rtest makefilename_default_bang;
+#X obj 145 773 rtest makefilename_float;
+#X obj 145 824 rtest makefilename_symbol;
+#X obj 145 875 rtest makefilename_bang;
 #X connect 0 0 1 0;
 #X connect 1 0 7 0;
 #X connect 1 1 29 0;
@@ -70,3 +79,10 @@ is handy for some binbuf tests.;
 #X connect 32 0 33 0;
 #X connect 33 0 34 0;
 #X connect 34 0 35 0;
+#X connect 35 0 36 0;
+#X connect 36 0 37 0;
+#X connect 37 0 39 0;
+#X connect 39 0 40 0;
+#X connect 40 0 41 0;
+#X connect 41 0 42 0;
+#X connect 42 0 43 0;
diff --git a/scripts/regression_tests/makefilename_bang.pd b/scripts/regression_tests/makefilename_bang.pd
new file mode 100644
index 0000000000000000000000000000000000000000..be59a3b13e5c625efa54e2abc158deccde11d05b
--- /dev/null
+++ b/scripts/regression_tests/makefilename_bang.pd
@@ -0,0 +1,134 @@
+#N canvas 3 60 1105 621 12;
+#X obj 41 8 inlet;
+#X obj 41 794 outlet;
+#X msg 908 211 set %5.5c;
+#X obj 876 183 t a b;
+#X obj 795 157 unpost error;
+#X obj 876 246 makefilename;
+#X text 890 157 supress error;
+#X obj 761 283 list;
+#X obj 761 132 t b a;
+#X obj 761 308 route bang symbol;
+#X obj 761 333 f 1;
+#X obj 819 333 b;
+#X msg 819 358 0;
+#X text 325 138 "INT" branch;
+#X obj 391 327 symbol;
+#X obj 391 168 t b a b;
+#X obj 437 201 symbol;
+#X obj 414 271 makefilename %i_test;
+#X obj 475 407 b;
+#X msg 475 432 0;
+#X obj 391 399 f 1;
+#X obj 41 337 symbol;
+#X obj 41 178 t b a b;
+#X obj 87 211 symbol;
+#X obj 125 417 b;
+#X msg 125 442 0;
+#X obj 41 409 f 1;
+#X obj 64 281 makefilename %g_test;
+#X text 56 136 "FLOAT" branch;
+#X text 789 109 trigger a type "NONE" branch with broken specifier
+;
+#X obj 571 327 symbol;
+#X obj 571 168 t b a b;
+#X obj 617 201 symbol;
+#X obj 655 407 b;
+#X msg 655 432 0;
+#X obj 571 399 f 1;
+#X text 563 141 well-formed "NONE" branch;
+#X obj 594 271 makefilename test;
+#X obj 571 374 select test;
+#X obj 41 66 trigger anything anything anything anything anything;
+#X obj 221 367 symbol;
+#X obj 221 208 t b a b;
+#X obj 267 241 symbol;
+#X obj 305 447 b;
+#X msg 305 472 0;
+#X obj 221 439 f 1;
+#X text 185 168 "STRING" branch;
+#X obj 391 374 select 0_test;
+#X obj 41 384 select 0_test;
+#X obj 571 492 list append incoming symbol correctly triggers constant
+string;
+#X obj 391 602 list append incoming symbol correctly converted to int
+;
+#X obj 221 642 list append incoming symbol correctly converted to symbol
+;
+#X obj 41 692 list append incoming symbol correctly formatted in outgoing
+symbol;
+#X obj 761 395 list append broken format string should suppress output
+for incoming bang;
+#X msg 41 36 bang;
+#X obj 244 311 makefilename %s_test;
+#X obj 221 414 select _test;
+#X connect 0 0 54 0;
+#X connect 2 0 5 0;
+#X connect 3 0 5 0;
+#X connect 3 1 2 0;
+#X connect 3 1 7 1;
+#X connect 4 1 3 0;
+#X connect 5 0 7 1;
+#X connect 7 0 9 0;
+#X connect 8 0 7 0;
+#X connect 8 1 4 0;
+#X connect 9 0 10 0;
+#X connect 9 1 11 0;
+#X connect 10 0 53 0;
+#X connect 11 0 12 0;
+#X connect 12 0 53 0;
+#X connect 14 0 47 0;
+#X connect 15 0 14 0;
+#X connect 15 1 17 0;
+#X connect 15 2 16 0;
+#X connect 16 0 14 1;
+#X connect 17 0 14 1;
+#X connect 18 0 19 0;
+#X connect 19 0 50 0;
+#X connect 20 0 50 0;
+#X connect 21 0 48 0;
+#X connect 22 0 21 0;
+#X connect 22 1 27 0;
+#X connect 22 2 23 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 0;
+#X connect 25 0 52 0;
+#X connect 26 0 52 0;
+#X connect 27 0 21 1;
+#X connect 30 0 38 0;
+#X connect 31 0 30 0;
+#X connect 31 1 37 0;
+#X connect 31 2 32 0;
+#X connect 32 0 30 1;
+#X connect 33 0 34 0;
+#X connect 34 0 49 0;
+#X connect 35 0 49 0;
+#X connect 37 0 30 1;
+#X connect 38 0 35 0;
+#X connect 38 1 33 0;
+#X connect 39 0 22 0;
+#X connect 39 1 41 0;
+#X connect 39 2 15 0;
+#X connect 39 3 31 0;
+#X connect 39 4 8 0;
+#X connect 40 0 56 0;
+#X connect 41 0 40 0;
+#X connect 41 1 55 0;
+#X connect 41 2 42 0;
+#X connect 42 0 40 1;
+#X connect 43 0 44 0;
+#X connect 44 0 51 0;
+#X connect 45 0 51 0;
+#X connect 47 0 20 0;
+#X connect 47 1 18 0;
+#X connect 48 0 26 0;
+#X connect 48 1 24 0;
+#X connect 49 0 1 0;
+#X connect 50 0 1 0;
+#X connect 51 0 1 0;
+#X connect 52 0 1 0;
+#X connect 53 0 1 0;
+#X connect 54 0 39 0;
+#X connect 55 0 40 1;
+#X connect 56 0 45 0;
+#X connect 56 1 43 0;
diff --git a/scripts/regression_tests/makefilename_code_coverage.pd b/scripts/regression_tests/makefilename_code_coverage.pd
new file mode 100644
index 0000000000000000000000000000000000000000..33fff49e55a510289d7e402f3e0ed15d4f24320b
--- /dev/null
+++ b/scripts/regression_tests/makefilename_code_coverage.pd
@@ -0,0 +1,112 @@
+#N canvas 96 60 665 633 12;
+#X obj 41 8 inlet;
+#X msg 41 143 1;
+#X obj 43 1229 outlet;
+#X text 117 8 makefilename is a strange combination of the already
+complicated C format string format specification and Pd's syntax limitations.
+This is especially unfortunate given the object only accepts a single
+substitution variable.;
+#X text 117 88 As a consequence we get an extremely error-prone interface
+subject to all kinds of broken format specifiers settable at runtime
+with arbitrary user-input. All that complexity for an object of impressively
+limited usefulness.;
+#X obj 105 982 makefilename;
+#X text 184 864 * todo: add check for [makefilename] with no args to
+make sure it ouputs file%d;
+#X obj 43 982 list;
+#X obj 43 1007 route symbol bang;
+#X obj 145 1118 list;
+#X obj 43 916 trigger bang anything bang anything;
+#X obj 43 1032 b;
+#X obj 43 1057 f 1;
+#X obj 100 1031 f 0;
+#X obj 43 1082 t a b;
+#X obj 43 1203 list append;
+#X msg 145 1149 code coverage for error handling of poorly formed format
+specifier: \$@;
+#X text 143 292 too many flags;
+#X text 50 240 no field type;
+#X text 129 414 max width/precision;
+#X text 115 354 variable width or precision field not supported;
+#X text 148 585 unsupported field types;
+#X text 154 650 non-existent field type;
+#X msg 41 265 % \, %0;
+#X obj 43 861 list prepend set;
+#X obj 43 886 list trim;
+#X msg 53 317 %-+#0-+#0-+#0-+#0- \, %-+#0-+#0-+#0-+#0-g;
+#X msg 63 379 %* \, %*s \, %.*s \, %*.*s;
+#X msg 73 439 %9999g \, %.9999g \, %9999.9999g;
+#X obj 66 946 unpost error;
+#X text 168 947 <- if there's an error we'll get it to the left inlet
+in the form of a symbol;
+#X obj 41 168 s \$0-b;
+#X obj 41 213 r \$0-b;
+#X obj 54 291 r \$0-b;
+#X obj 63 348 r \$0-b;
+#X obj 73 409 r \$0-b;
+#X obj 83 467 r \$0-b;
+#X obj 93 580 r \$0-b;
+#X obj 103 634 r \$0-b;
+#X msg 103 660 %z;
+#X obj 110 684 r \$0-b;
+#X text 185 697 more than one format specifier;
+#X msg 110 710 %g%g \, %g%;
+#X text 132 477 double dots;
+#X msg 83 499 %5..g \, %..g;
+#X obj 93 523 r \$0-b;
+#X text 142 533 precision field/specifier mismatch;
+#X msg 93 555 %5.3c;
+#X msg 93 609 %a \, %A \, %n \, %m \, %p;
+#X text 117 168 Here \, we trigger every branch of the format specifier
+parser to make sure our run-time errors prevent crashes and/or memory
+errors. We use [unpost] to redirect the error from the console to the
+patch.;
+#X obj 120 734 r \$0-b;
+#X msg 120 760 %#d \, %#i;
+#X obj 120 794 r \$0-b;
+#X text 195 747 hash flag mismatch;
+#X text 195 807 zero flag mismatch;
+#X msg 120 820 %0c \, %0s;
+#X connect 0 0 1 0;
+#X connect 1 0 31 0;
+#X connect 7 0 8 0;
+#X connect 8 0 11 0;
+#X connect 8 1 13 0;
+#X connect 9 0 16 0;
+#X connect 10 0 7 0;
+#X connect 10 1 29 0;
+#X connect 10 2 7 1;
+#X connect 10 3 9 1;
+#X connect 11 0 12 0;
+#X connect 12 0 14 0;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 14 1 9 0;
+#X connect 15 0 2 0;
+#X connect 16 0 15 1;
+#X connect 23 0 24 0;
+#X connect 24 0 25 0;
+#X connect 25 0 10 0;
+#X connect 26 0 24 0;
+#X connect 27 0 24 0;
+#X connect 28 0 24 0;
+#X connect 29 0 7 1;
+#X connect 29 1 5 0;
+#X connect 32 0 23 0;
+#X connect 33 0 26 0;
+#X connect 34 0 27 0;
+#X connect 35 0 28 0;
+#X connect 36 0 44 0;
+#X connect 37 0 48 0;
+#X connect 38 0 39 0;
+#X connect 39 0 24 0;
+#X connect 40 0 42 0;
+#X connect 42 0 24 0;
+#X connect 44 0 24 0;
+#X connect 45 0 47 0;
+#X connect 47 0 24 0;
+#X connect 48 0 24 0;
+#X connect 50 0 51 0;
+#X connect 51 0 24 0;
+#X connect 52 0 55 0;
+#X connect 55 0 24 0;
diff --git a/scripts/regression_tests/makefilename_default.pd b/scripts/regression_tests/makefilename_default.pd
new file mode 100644
index 0000000000000000000000000000000000000000..ebbb0da0fbf54e276b90bec7ce3513502845b205
--- /dev/null
+++ b/scripts/regression_tests/makefilename_default.pd
@@ -0,0 +1,21 @@
+#N canvas 51 212 538 283 12;
+#X obj 41 28 inlet;
+#X obj 136 153 list prepend 0;
+#X obj 41 249 outlet;
+#X obj 41 98 makefilename;
+#X text 117 28 [makefilename] with no arguments should output the symbol
+`file.` concatenated with the number from the input. The incoming float
+should be cast to an int before concatenation.;
+#X msg 41 73 42.3;
+#X obj 41 123 select file.42;
+#X msg 41 153 1 file.42;
+#X msg 41 187 \$1 default format specifier 'file.%d' with incoming
+float cast to int. Expected output: 'file.42'. Actual output: \$2;
+#X connect 0 0 5 0;
+#X connect 1 0 8 0;
+#X connect 3 0 6 0;
+#X connect 5 0 3 0;
+#X connect 6 0 7 0;
+#X connect 6 1 1 0;
+#X connect 7 0 8 0;
+#X connect 8 0 2 0;
diff --git a/scripts/regression_tests/makefilename_default_bang.pd b/scripts/regression_tests/makefilename_default_bang.pd
new file mode 100644
index 0000000000000000000000000000000000000000..80dd5bf704f25642a74de41f60557205efddc81d
--- /dev/null
+++ b/scripts/regression_tests/makefilename_default_bang.pd
@@ -0,0 +1,21 @@
+#N canvas 72 248 589 329 12;
+#X obj 41 28 inlet;
+#X obj 136 153 list prepend 0;
+#X obj 41 229 outlet;
+#X obj 41 98 makefilename;
+#X text 117 28 [makefilename] in Pd Vanilla apparently converts an
+incoming bang to zero. Not sure why \, but we check for that default
+here.;
+#X msg 41 73 bang;
+#X obj 41 123 select file.0;
+#X msg 41 153 1 file.0;
+#X msg 41 187 \$1 a bang should bash to zero. Expected output: 'file.0'.
+Actual output: \$2;
+#X connect 0 0 5 0;
+#X connect 1 0 8 0;
+#X connect 3 0 6 0;
+#X connect 5 0 3 0;
+#X connect 6 0 7 0;
+#X connect 6 1 1 0;
+#X connect 7 0 8 0;
+#X connect 8 0 2 0;
diff --git a/scripts/regression_tests/makefilename_double_percent.pd b/scripts/regression_tests/makefilename_double_percent.pd
new file mode 100644
index 0000000000000000000000000000000000000000..e510160146183a7eb97710cc244949ad7d2a7b7e
--- /dev/null
+++ b/scripts/regression_tests/makefilename_double_percent.pd
@@ -0,0 +1,22 @@
+#N canvas 176 225 538 369 12;
+#X obj 41 28 inlet;
+#X msg 41 73 1;
+#X obj 41 98 makefilename %%s%g;
+#X obj 41 123 select %s1;
+#X obj 108 153 list prepend 0;
+#X msg 41 153 1 %s1;
+#X obj 41 229 outlet;
+#X text 117 28 Double percent signs map to a single literal '%'. Some
+old patches use this feature in conjunction with the 'set' message
+to do string concatenation. So we want to make sure it continues to
+work going forward.;
+#X msg 41 187 \$1 format specifier '%%' should get converted to a single
+'%'. Expected output: '%s1'. Actual output: \$2;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X connect 2 0 3 0;
+#X connect 3 0 5 0;
+#X connect 3 1 4 0;
+#X connect 4 0 8 0;
+#X connect 5 0 8 0;
+#X connect 8 0 6 0;
diff --git a/scripts/regression_tests/makefilename_float.pd b/scripts/regression_tests/makefilename_float.pd
new file mode 100644
index 0000000000000000000000000000000000000000..f9f9dbaa0fc19c1c8e2ac774dba4cbc62689b81d
--- /dev/null
+++ b/scripts/regression_tests/makefilename_float.pd
@@ -0,0 +1,134 @@
+#N canvas 3 60 1105 621 12;
+#X obj 41 8 inlet;
+#X obj 41 794 outlet;
+#X msg 908 211 set %5.5c;
+#X obj 876 183 t a b;
+#X obj 795 157 unpost error;
+#X obj 876 246 makefilename;
+#X text 890 157 supress error;
+#X obj 761 283 list;
+#X obj 761 132 t b a;
+#X obj 761 308 route bang symbol;
+#X obj 761 333 f 1;
+#X obj 819 333 b;
+#X msg 819 358 0;
+#X text 315 148 "INT" branch;
+#X obj 391 327 symbol;
+#X obj 391 168 t b a b;
+#X obj 437 201 symbol;
+#X msg 41 36 42.3;
+#X obj 414 271 makefilename %i_test;
+#X obj 391 374 select 42_test;
+#X obj 475 407 b;
+#X msg 475 432 0;
+#X obj 391 399 f 1;
+#X obj 391 602 list append incoming float correctly converted to int
+;
+#X obj 41 337 symbol;
+#X obj 41 178 t b a b;
+#X obj 87 211 symbol;
+#X obj 125 417 b;
+#X msg 125 442 0;
+#X obj 41 409 f 1;
+#X obj 64 281 makefilename %g_test;
+#X obj 41 384 select 42.3_test;
+#X obj 41 692 list append incoming float correctly formatted in outgoing
+symbol;
+#X text 56 136 "FLOAT" branch;
+#X text 789 109 trigger a type "NONE" branch with broken specifier
+;
+#X obj 761 395 list append broken format string should suppress output
+for incoming float;
+#X obj 571 327 symbol;
+#X obj 571 168 t b a b;
+#X obj 617 201 symbol;
+#X obj 655 407 b;
+#X msg 655 432 0;
+#X obj 571 399 f 1;
+#X text 563 141 well-formed "NONE" branch;
+#X obj 571 492 list append incoming float correctly triggers constant
+string;
+#X obj 594 271 makefilename test;
+#X obj 571 374 select test;
+#X obj 41 66 trigger anything anything anything anything anything;
+#X obj 221 367 symbol;
+#X obj 221 208 t b a b;
+#X obj 267 241 symbol;
+#X obj 305 447 b;
+#X msg 305 472 0;
+#X obj 221 439 f 1;
+#X text 185 168 "STRING" branch;
+#X obj 244 311 makefilename %s_test;
+#X obj 221 414 select 42.3_test;
+#X obj 221 642 list append incoming float correctly converted to symbol
+;
+#X connect 0 0 17 0;
+#X connect 2 0 5 0;
+#X connect 3 0 5 0;
+#X connect 3 1 2 0;
+#X connect 3 1 7 1;
+#X connect 4 1 3 0;
+#X connect 5 0 7 1;
+#X connect 7 0 9 0;
+#X connect 8 0 7 0;
+#X connect 8 1 4 0;
+#X connect 9 0 10 0;
+#X connect 9 1 11 0;
+#X connect 10 0 35 0;
+#X connect 11 0 12 0;
+#X connect 12 0 35 0;
+#X connect 14 0 19 0;
+#X connect 15 0 14 0;
+#X connect 15 1 18 0;
+#X connect 15 2 16 0;
+#X connect 16 0 14 1;
+#X connect 17 0 46 0;
+#X connect 18 0 14 1;
+#X connect 19 0 22 0;
+#X connect 19 1 20 0;
+#X connect 20 0 21 0;
+#X connect 21 0 23 0;
+#X connect 22 0 23 0;
+#X connect 23 0 1 0;
+#X connect 24 0 31 0;
+#X connect 25 0 24 0;
+#X connect 25 1 30 0;
+#X connect 25 2 26 0;
+#X connect 26 0 24 1;
+#X connect 27 0 28 0;
+#X connect 28 0 32 0;
+#X connect 29 0 32 0;
+#X connect 30 0 24 1;
+#X connect 31 0 29 0;
+#X connect 31 1 27 0;
+#X connect 32 0 1 0;
+#X connect 35 0 1 0;
+#X connect 36 0 45 0;
+#X connect 37 0 36 0;
+#X connect 37 1 44 0;
+#X connect 37 2 38 0;
+#X connect 38 0 36 1;
+#X connect 39 0 40 0;
+#X connect 40 0 43 0;
+#X connect 41 0 43 0;
+#X connect 43 0 1 0;
+#X connect 44 0 36 1;
+#X connect 45 0 41 0;
+#X connect 45 1 39 0;
+#X connect 46 0 25 0;
+#X connect 46 1 48 0;
+#X connect 46 2 15 0;
+#X connect 46 3 37 0;
+#X connect 46 4 8 0;
+#X connect 47 0 55 0;
+#X connect 48 0 47 0;
+#X connect 48 1 54 0;
+#X connect 48 2 49 0;
+#X connect 49 0 47 1;
+#X connect 50 0 51 0;
+#X connect 51 0 56 0;
+#X connect 52 0 56 0;
+#X connect 54 0 47 1;
+#X connect 55 0 52 0;
+#X connect 55 1 50 0;
+#X connect 56 0 1 0;
diff --git a/scripts/regression_tests/makefilename_symbol.pd b/scripts/regression_tests/makefilename_symbol.pd
new file mode 100644
index 0000000000000000000000000000000000000000..23c97ceecc0f40dc46cd44e0163fdbbfb7ba7a75
--- /dev/null
+++ b/scripts/regression_tests/makefilename_symbol.pd
@@ -0,0 +1,134 @@
+#N canvas 3 60 1105 621 12;
+#X obj 41 8 inlet;
+#X obj 41 794 outlet;
+#X msg 908 211 set %5.5c;
+#X obj 876 183 t a b;
+#X obj 795 157 unpost error;
+#X obj 876 246 makefilename;
+#X text 890 157 supress error;
+#X obj 761 283 list;
+#X obj 761 132 t b a;
+#X obj 761 308 route bang symbol;
+#X obj 761 333 f 1;
+#X obj 819 333 b;
+#X msg 819 358 0;
+#X text 325 138 "INT" branch;
+#X obj 391 327 symbol;
+#X obj 391 168 t b a b;
+#X obj 437 201 symbol;
+#X obj 414 271 makefilename %i_test;
+#X obj 475 407 b;
+#X msg 475 432 0;
+#X obj 391 399 f 1;
+#X obj 41 337 symbol;
+#X obj 41 178 t b a b;
+#X obj 87 211 symbol;
+#X obj 125 417 b;
+#X msg 125 442 0;
+#X obj 41 409 f 1;
+#X obj 64 281 makefilename %g_test;
+#X text 56 136 "FLOAT" branch;
+#X text 789 109 trigger a type "NONE" branch with broken specifier
+;
+#X obj 571 327 symbol;
+#X obj 571 168 t b a b;
+#X obj 617 201 symbol;
+#X obj 655 407 b;
+#X msg 655 432 0;
+#X obj 571 399 f 1;
+#X text 563 141 well-formed "NONE" branch;
+#X obj 594 271 makefilename test;
+#X obj 571 374 select test;
+#X obj 41 66 trigger anything anything anything anything anything;
+#X obj 221 367 symbol;
+#X obj 221 208 t b a b;
+#X obj 267 241 symbol;
+#X obj 305 447 b;
+#X msg 305 472 0;
+#X obj 221 439 f 1;
+#X text 185 168 "STRING" branch;
+#X msg 41 36 symbol test;
+#X obj 391 374 select 0_test;
+#X obj 41 384 select 0_test;
+#X obj 244 311 makefilename %s;
+#X obj 221 414 select test;
+#X obj 761 395 list append broken format string should suppress output
+for incoming symbol;
+#X obj 571 492 list append incoming symbol correctly triggers constant
+string;
+#X obj 391 602 list append incoming symbol correctly converted to int
+;
+#X obj 221 642 list append incoming symbol correctly converted to symbol
+;
+#X obj 41 692 list append incoming symbol correctly formatted in outgoing
+symbol;
+#X connect 0 0 47 0;
+#X connect 2 0 5 0;
+#X connect 3 0 5 0;
+#X connect 3 1 2 0;
+#X connect 3 1 7 1;
+#X connect 4 1 3 0;
+#X connect 5 0 7 1;
+#X connect 7 0 9 0;
+#X connect 8 0 7 0;
+#X connect 8 1 4 0;
+#X connect 9 0 10 0;
+#X connect 9 1 11 0;
+#X connect 10 0 52 0;
+#X connect 11 0 12 0;
+#X connect 12 0 52 0;
+#X connect 14 0 48 0;
+#X connect 15 0 14 0;
+#X connect 15 1 17 0;
+#X connect 15 2 16 0;
+#X connect 16 0 14 1;
+#X connect 17 0 14 1;
+#X connect 18 0 19 0;
+#X connect 19 0 54 0;
+#X connect 20 0 54 0;
+#X connect 21 0 49 0;
+#X connect 22 0 21 0;
+#X connect 22 1 27 0;
+#X connect 22 2 23 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 0;
+#X connect 25 0 56 0;
+#X connect 26 0 56 0;
+#X connect 27 0 21 1;
+#X connect 30 0 38 0;
+#X connect 31 0 30 0;
+#X connect 31 1 37 0;
+#X connect 31 2 32 0;
+#X connect 32 0 30 1;
+#X connect 33 0 34 0;
+#X connect 34 0 53 0;
+#X connect 35 0 53 0;
+#X connect 37 0 30 1;
+#X connect 38 0 35 0;
+#X connect 38 1 33 0;
+#X connect 39 0 22 0;
+#X connect 39 1 41 0;
+#X connect 39 2 15 0;
+#X connect 39 3 31 0;
+#X connect 39 4 8 0;
+#X connect 40 0 51 0;
+#X connect 41 0 40 0;
+#X connect 41 1 50 0;
+#X connect 41 2 42 0;
+#X connect 42 0 40 1;
+#X connect 43 0 44 0;
+#X connect 44 0 55 0;
+#X connect 45 0 55 0;
+#X connect 47 0 39 0;
+#X connect 48 0 20 0;
+#X connect 48 1 18 0;
+#X connect 49 0 26 0;
+#X connect 49 1 24 0;
+#X connect 50 0 40 1;
+#X connect 51 0 45 0;
+#X connect 51 1 43 0;
+#X connect 52 0 1 0;
+#X connect 53 0 1 0;
+#X connect 54 0 1 0;
+#X connect 55 0 1 0;
+#X connect 56 0 1 0;