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:
@@ -797,6 +797,7 @@ struct wxPrintfConvSpecParser
|
|||||||
|
|
||||||
wxPrintfConvSpecParser(const CharType *fmt)
|
wxPrintfConvSpecParser(const CharType *fmt)
|
||||||
{
|
{
|
||||||
|
nspecs =
|
||||||
nargs = 0;
|
nargs = 0;
|
||||||
posarg_present =
|
posarg_present =
|
||||||
nonposarg_present = false;
|
nonposarg_present = false;
|
||||||
@@ -817,7 +818,7 @@ struct wxPrintfConvSpecParser
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvSpec *spec = &specs[nargs];
|
ConvSpec *spec = &specs[nspecs];
|
||||||
spec->Init();
|
spec->Init();
|
||||||
|
|
||||||
// attempt to parse this format specification
|
// attempt to parse this format specification
|
||||||
@@ -838,7 +839,7 @@ struct wxPrintfConvSpecParser
|
|||||||
|
|
||||||
for ( unsigned n = 0; n < numAsterisks; n++ )
|
for ( unsigned n = 0; n < numAsterisks; n++ )
|
||||||
{
|
{
|
||||||
if ( ++nargs == wxMAX_SVNPRINTF_ARGUMENTS )
|
if ( ++nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// TODO: we need to support specifiers of the form "%2$*1$s"
|
// 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
|
// make an entry for '*' and point to it from pspec
|
||||||
spec->Init();
|
spec->Init();
|
||||||
spec->m_type = wxPAT_STAR;
|
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
|
// If we hit the maximal number of arguments inside the inner
|
||||||
// loop, break out of the outer one as well.
|
// loop, break out of the outer one as well.
|
||||||
if ( nargs == wxMAX_SVNPRINTF_ARGUMENTS )
|
if ( nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -884,25 +885,44 @@ struct wxPrintfConvSpecParser
|
|||||||
// the positional arguments start from number 1 so we need
|
// the positional arguments start from number 1 so we need
|
||||||
// to adjust the index
|
// to adjust the index
|
||||||
spec->m_pos--;
|
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;
|
posarg_present = true;
|
||||||
}
|
}
|
||||||
else // not a positional argument...
|
else // not a positional argument...
|
||||||
{
|
{
|
||||||
spec->m_pos = nargs;
|
spec->m_pos = nargs++;
|
||||||
nonposarg_present = true;
|
nonposarg_present = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this conversion specifier is tied to the pos-th argument...
|
// this conversion specifier is tied to the pos-th argument...
|
||||||
pspec[spec->m_pos] = spec;
|
pspec[spec->m_pos] = spec;
|
||||||
|
|
||||||
if ( ++nargs == wxMAX_SVNPRINTF_ARGUMENTS )
|
if ( ++nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// warn if we lost any arguments (the program probably will crash
|
// warn if we lost any arguments (the program probably will crash
|
||||||
// anyhow because of stack corruption...)
|
// anyhow because of stack corruption...)
|
||||||
if ( nargs == wxMAX_SVNPRINTF_ARGUMENTS )
|
if ( nspecs == wxMAX_SVNPRINTF_ARGUMENTS )
|
||||||
{
|
{
|
||||||
wxFAIL_MSG
|
wxFAIL_MSG
|
||||||
(
|
(
|
||||||
@@ -919,6 +939,10 @@ struct wxPrintfConvSpecParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// total number of valid elements in specs
|
// 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;
|
unsigned nargs;
|
||||||
|
|
||||||
// all format specifications in this format string in order of their
|
// all format specifications in this format string in order of their
|
||||||
|
@@ -139,7 +139,7 @@ static int wxDoVsnprintf(CharType *buf, size_t lenMax,
|
|||||||
|
|
||||||
// finally, process each conversion specifier with its own argument
|
// finally, process each conversion specifier with its own argument
|
||||||
const CharType *toparse = format;
|
const CharType *toparse = format;
|
||||||
for (i=0; i < parser.nargs; i++)
|
for (i=0; i < parser.nspecs; i++)
|
||||||
{
|
{
|
||||||
wxPrintfConvSpec<CharType>& spec = parser.specs[i];
|
wxPrintfConvSpec<CharType>& spec = parser.specs[i];
|
||||||
|
|
||||||
|
@@ -188,6 +188,8 @@ void StringTestCase::Format()
|
|||||||
"4 world hello world 3",
|
"4 world hello world 3",
|
||||||
wxString::Format("%4$d %2$s %1$s %2$s %3$d", "hello", "world", 3, 4)
|
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()
|
void StringTestCase::FormatUnicode()
|
||||||
|
Reference in New Issue
Block a user