Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
nerrons
purr-data
Commits
d7a46093
Commit
d7a46093
authored
Dec 25, 2018
by
Jonathan Wilkes
Browse files
Merge branch 'fix-makefilename'
parents
731f1a77
1a6b0f5c
Changes
9
Hide whitespace changes
Inline
Side-by-side
pd/src/x_connective.c
View file @
d7a46093
...
...
@@ -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
;
b
re
ak
;
sprintf
(
errormsg
,
"variable width value not supported"
)
;
*
typ
=
NONE
;
re
turn
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
);
}
...
...
scripts/regression_tests.pd
View file @
d7a46093
#N canvas 3 60 749 617 12;
#X obj
3
45 301 r \$0-result;
#X obj
3
45 326 route 0;
#X obj
4
53 470 print failure;
#X obj
43
0 336 tgl 28 0 empty empty Print_All_Results 31 11 0 12 -262144
-1 -1
1
1;
#X obj 4
7
5 301 r \$0-result;
#X obj 4
7
5 326 route 0;
#X obj 5
8
3 470 print failure;
#X obj
56
0 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
3
45 411 t b a;
#X obj
3
45 541 s pd;
#X obj 4
7
5 411 t b a;
#X obj 4
7
5 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
3
45 516 quit 1;
#X msg 4
7
5 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
3
45 440 gui;
#X obj
3
45 465 pdinfo;
#X obj
3
45 490 sel 0;
#X msg 4
7
5 440 gui;
#X obj 4
7
5 465 pdinfo;
#X obj 4
7
5 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
39
1 374 spigot;
#X obj
40
7 440 route 1;
#X obj
40
7 495 print success;
#X obj
52
1 374 spigot;
#X obj
53
7 440 route 1;
#X obj
53
7 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;
scripts/regression_tests/makefilename_bang.pd
0 → 100644
View file @
d7a46093
#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;