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