Commit 1a6b0f5c authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

conform better to spec, remove pointer specifier, add more tests and comments

parent bd5fa90a
......@@ -1467,17 +1467,17 @@ typedef struct _makefilename
static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
char *errormsg)
{
int infmt = 0, i;
int infmt = 0, i, hashflag = 0, zeroflag = 0;
for (; *str; str++)
{
if (!infmt && *str == '%')
{
/* A string consisting of a single '%' character isn't a valid
format specifier. */
/* 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, "field type missing");
sprintf(errormsg, "specifier missing");
*typ = NONE;
return str;
}
......@@ -1486,21 +1486,28 @@ static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
}
if (infmt)
{
/* 1) flag field
Check for a leading '%' after our format specifier. This
will be converted to a single '%' in the output. */
/* 1) flags
Check for "%%" which produces a literal '%' in the output */
if (*str == '%')
{
/* Not in a format specifier after all, so start over... */
/* Not in a format specifier after all, so let's reset
infmt and continue searching... */
infmt = 0;
continue;
}
for (i = 0; *str && strchr("-+#0", *str) != 0; str++, i++)
{
/* Check for flag chars. While a space is a legal part of this
field, Pd's parser would split it off into a separate arg.
Since makefilename has always truncated extra args we don't
support spaces here. */
/* 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)
......@@ -1557,12 +1564,11 @@ static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
return str;
}
}
/* Fairly certain having '.' with no trailing digits or asterisk
is undefined behavior. So let's trigger a syntax error. */
if (i == 0)
/* precision isn't defined for all conversion specifiers. */
if (*str && strchr("diouxXeEfFgGs", *str) == 0)
{
sprintf(errormsg, "precision specifier requires a number "
"following the '.'");
sprintf(errormsg, "precision field restricted to "
"[diouxXeEfFgGs] specifiers");
*typ = NONE;
return str;
}
......@@ -1571,9 +1577,24 @@ static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
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) type field
The type of value we want to fill the slot with. This is the
most complex field we support. */
/* 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')
......@@ -1597,10 +1618,15 @@ static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
return str;
}
/* a pointer */
/* 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;
/* *typ = POINTER; */
/* return str; */
sprintf(errormsg, "p specifier not supported");
*typ = NONE;
return str;
}
......@@ -1610,17 +1636,17 @@ static const char* makefilename_doscanformat(const char *str, t_printtype *typ,
/* 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 type not supported. If you "
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 field type not supported");
sprintf(errormsg, "n specifier not supported");
else if (*str == 'm')
sprintf(errormsg, "m field type not supported");
sprintf(errormsg, "m specifier not supported");
else if (*str)
sprintf(errormsg, "bad field type '%c'", *str);
sprintf(errormsg, "bad specifier type '%c'", *str);
else
sprintf(errormsg, "field type missing");
sprintf(errormsg, "specifier missing");
*typ = NONE;
return str;
}
......@@ -1643,9 +1669,9 @@ static void makefilename_scanformat(t_makefilename *x)
if (*errorbuf)
{
pd_error(x, "makefilename: invalid format string '%s' "
"(%s). Setting to empty string.",
"(%s). Supressing output.",
x->x_format->s_name, errorbuf);
/* set the format string to nothing. It would be great to refuse to
/* 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. */
......@@ -1663,8 +1689,8 @@ static void makefilename_scanformat(t_makefilename *x)
if (typ != NONE || *errorbuf)
{
pd_error(x, "makefilename: invalid format string '%s' "
"(too many format specifiers). "
"Setting to empty string.",
"(too many specifiers). "
"Suppressing output.",
x->x_format->s_name);
x->x_format = 0;
return;
......@@ -1711,25 +1737,30 @@ static void makefilename_float(t_makefilename *x, t_floatarg f)
}
switch(x->x_accept)
{
case NONE:
makefilename_snprintf(x, buf, x->x_format->s_name);
break;
case INT: case POINTER:
makefilename_snprintf(x, buf, x->x_format->s_name, (int)f);
break;
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];
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;
}
default:
makefilename_snprintf(x, buf, "%s", x->x_format->s_name);
/* 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)
if (buf[0] != 0)
outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
}
......@@ -1743,7 +1774,7 @@ static void makefilename_symbol(t_makefilename *x, t_symbol *s)
}
switch(x->x_accept)
{
case STRING: case POINTER:
case STRING:
makefilename_snprintf(x, buf, x->x_format->s_name, s->s_name);
break;
case INT:
......@@ -1756,9 +1787,15 @@ static void makefilename_symbol(t_makefilename *x, t_symbol *s)
makefilename_snprintf(x, buf, x->x_format->s_name);
break;
default:
makefilename_snprintf(x, buf, "%s", x->x_format->s_name);
/* 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)
if (buf[0] != 0)
outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
}
......@@ -1778,13 +1815,18 @@ static void makefilename_bang(t_makefilename *x)
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:
makefilename_snprintf(x, buf, "%s", x->x_format->s_name);
pd_error(x, "cannot convert bang with specifier: %s",
x->x_format->s_name);
return;
}
if (buf[0]!=0)
if (buf[0] != 0)
outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
}
......
......@@ -3,7 +3,7 @@
#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 1 1;
-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;
......@@ -44,9 +44,12 @@ is handy for some binbuf tests.;
#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 875 pd;
#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;
......@@ -80,3 +83,6 @@ is handy for some binbuf tests.;
#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;
#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;
#N canvas 130 60 665 633 12;
#N canvas 96 60 665 633 12;
#X obj 41 8 inlet;
#X msg 41 143 1;
#X obj 43 1059 outlet;
#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
......@@ -10,40 +10,34 @@ substitution variable.;
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 text 117 168 Here \, we trigger every branch of the format specifier
parser to make sure a run-time error results instead of a crash and/or
memory error. We use [unpost] to redirect the error from the console
to the patch.;
#X obj 105 782 makefilename;
#X text 184 664 * todo: add check for [makefilename] with no args to
#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 782 list;
#X obj 43 807 route symbol bang;
#X obj 145 928 list;
#X obj 43 716 trigger bang anything bang anything;
#X obj 43 832 b;
#X obj 43 857 f 1;
#X obj 100 831 f 0;
#X obj 43 882 t a b;
#X obj 43 1018 list append;
#X msg 145 959 code coverage for error handling of poorly formed format
#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 535 unsupported field types;
#X text 154 600 non-existent field type;
#X text 148 585 unsupported field types;
#X text 154 650 non-existent field type;
#X msg 41 265 % \, %0;
#X obj 43 661 list prepend set;
#X obj 43 686 list trim;
#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 msg 83 499 %5.g \, %.g \, %5..g \, %..g;
#X text 132 477 dot with no digit(s) following it;
#X obj 66 746 unpost error;
#X text 168 747 <- if there's an error we'll get it to the left inlet
#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;
......@@ -51,42 +45,68 @@ in the form of a symbol;
#X obj 63 348 r \$0-b;
#X obj 73 409 r \$0-b;
#X obj 83 467 r \$0-b;
#X obj 93 530 r \$0-b;
#X obj 103 584 r \$0-b;
#X msg 93 559 %a \, %A \, %n \, %m;
#X msg 103 610 %z;
#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 34 0;
#X connect 8 0 9 0;
#X connect 9 0 12 0;
#X connect 9 1 14 0;
#X connect 10 0 17 0;
#X connect 11 0 8 0;
#X connect 11 1 32 0;
#X connect 11 2 8 1;
#X connect 11 3 10 1;
#X connect 12 0 13 0;
#X connect 13 0 15 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 15 0 16 0;
#X connect 15 1 10 0;
#X connect 16 0 2 0;
#X connect 17 0 16 1;
#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 26 0;
#X connect 26 0 11 0;
#X connect 27 0 25 0;
#X connect 28 0 25 0;
#X connect 29 0 25 0;
#X connect 30 0 25 0;
#X connect 32 0 8 1;
#X connect 32 1 6 0;
#X connect 35 0 24 0;
#X connect 36 0 27 0;
#X connect 37 0 28 0;
#X connect 38 0 29 0;
#X connect 39 0 30 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 41 0 43 0;
#X connect 42 0 25 0;
#X connect 43 0 25 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;
#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;