Fix built-in wxPrintf() for repeated positional parameters

Allow wxPrintf("%1$s %1$s", "foo") to work.

Take into account the possibility that the number of format specifiers
and the number of actual arguments can be different.

Closes #9367.
This commit is contained in:
Vadim Zeitlin
2017-11-25 11:02:52 +01:00
parent dd8155980b
commit 471a771382
3 changed files with 36 additions and 10 deletions

View File

@@ -797,6 +797,7 @@ struct wxPrintfConvSpecParser
wxPrintfConvSpecParser(const CharType *fmt)
{
nspecs =
nargs = 0;
posarg_present =
nonposarg_present = false;
@@ -817,7 +818,7 @@ struct wxPrintfConvSpecParser
continue;
}
ConvSpec *spec = &specs[nargs];
ConvSpec *spec = &specs[nspecs];
spec->Init();
// attempt to parse this format specification
@@ -838,7 +839,7 @@ struct wxPrintfConvSpecParser
for ( unsigned n = 0; n < numAsterisks; n++ )
{
if ( ++nargs == wxMAX_SVNPRINTF_ARGUMENTS )
if ( ++nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
break;
// TODO: we need to support specifiers of the form "%2$*1$s"
@@ -861,19 +862,19 @@ struct wxPrintfConvSpecParser
);
}
specs[nargs] = *spec;
specs[nspecs] = *spec;
// make an entry for '*' and point to it from pspec
spec->Init();
spec->m_type = wxPAT_STAR;
pspec[nargs - 1] = spec;
pspec[nargs++] = spec;
spec = &specs[nargs];
spec = &specs[nspecs];
}
// If we hit the maximal number of arguments inside the inner
// loop, break out of the outer one as well.
if ( nargs == wxMAX_SVNPRINTF_ARGUMENTS )
if ( nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
break;
}
@@ -884,25 +885,44 @@ struct wxPrintfConvSpecParser
// the positional arguments start from number 1 so we need
// to adjust the index
spec->m_pos--;
// We could be reusing an already existing argument, only
// increment their number if it's really a new one.
if ( spec->m_pos >= nargs )
{
nargs = spec->m_pos + 1;
}
else if ( pspec[spec->m_pos] ) // Had we seen it before?
{
// Check that the type specified this time is compatible
// with the previously-specified type.
wxASSERT_MSG
(
pspec[spec->m_pos]->m_type == spec->m_type,
"Positional parameter specified multiple times "
"with incompatible types."
);
}
posarg_present = true;
}
else // not a positional argument...
{
spec->m_pos = nargs;
spec->m_pos = nargs++;
nonposarg_present = true;
}
// this conversion specifier is tied to the pos-th argument...
pspec[spec->m_pos] = spec;
if ( ++nargs == wxMAX_SVNPRINTF_ARGUMENTS )
if ( ++nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
break;
}
// warn if we lost any arguments (the program probably will crash
// anyhow because of stack corruption...)
if ( nargs == wxMAX_SVNPRINTF_ARGUMENTS )
if ( nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
{
wxFAIL_MSG
(
@@ -919,6 +939,10 @@ struct wxPrintfConvSpecParser
}
// total number of valid elements in specs
unsigned nspecs;
// total number of arguments, also number of valid elements in pspec, and
// always less than or (usually) equal to nspecs
unsigned nargs;
// all format specifications in this format string in order of their

View File

@@ -139,7 +139,7 @@ static int wxDoVsnprintf(CharType *buf, size_t lenMax,
// finally, process each conversion specifier with its own argument
const CharType *toparse = format;
for (i=0; i < parser.nargs; i++)
for (i=0; i < parser.nspecs; i++)
{
wxPrintfConvSpec<CharType>& spec = parser.specs[i];

View File

@@ -188,6 +188,8 @@ void StringTestCase::Format()
"4 world hello world 3",
wxString::Format("%4$d %2$s %1$s %2$s %3$d", "hello", "world", 3, 4)
);
CHECK( wxString::Format("%1$o %1$d %1$x", 20) == "24 20 14" );
}
void StringTestCase::FormatUnicode()