// ---------------------------------------------------------------------------
#include "wx/crt.h"
+#include "wx/log.h"
+#include "wx/utils.h"
#include <string.h>
// the conversion specifiers accepted by wxCRT_VsnprintfW
-enum wxPrintfArgType {
+enum wxPrintfArgType
+{
wxPAT_INVALID = -1,
wxPAT_INT, // %d, %i, %o, %u, %x, %X
wxPAT_NINT, // %n
wxPAT_NSHORTINT, // %hn
- wxPAT_NLONGINT // %ln
+ wxPAT_NLONGINT, // %ln
+
+ wxPAT_STAR // '*' used for width or precision
};
// an argument passed to wxCRT_VsnprintfW
-typedef union {
+union wxPrintfArg
+{
int pad_int; // %d, %i, %o, %u, %x, %X
long int pad_longint; // %ld, etc
#ifdef wxLongLong_t
- wxLongLong_t pad_longlongint; // %Ld, etc
+ wxLongLong_t pad_longlongint; // %Ld, etc
#endif
size_t pad_sizet; // %Zd, etc
int *pad_nint; // %n
short int *pad_nshortint; // %hn
long int *pad_nlongint; // %ln
-} wxPrintfArg;
+};
-// helper for converting string into either char* or wchar_t* dependening
+// helper for converting string into either char* or wchar_t* depending
// on the type of wxPrintfConvSpec<T> instantiation:
template<typename CharType> struct wxPrintfStringHelper {};
// a little buffer where formatting flags like #+\.hlqLZ are stored by Parse()
// for use in Process()
- // NB: even if this buffer is used only for numeric conversion specifiers
- // and thus could be safely declared as a char[] buffer, we want it to
- // be wchar_t so that in Unicode builds we can avoid to convert its
- // contents to Unicode chars when copying it in user's buffer.
char m_szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
break;
case wxT('.'):
- CHECK_PREC
+ // don't use CHECK_PREC here to avoid warning about the value
+ // assigned to prec_dot inside it being never used (because
+ // overwritten just below) from Borland in release build
+ if (in_prec && !prec_dot)
+ m_szFlags[flagofs++] = '.';
in_prec = true;
prec_dot = false;
m_nMaxWidth = 0;
// find the first * in our flag buffer
char *pwidth = strchr(m_szFlags, '*');
- wxCHECK_RET(pwidth, _T("field width must be specified"));
+ wxCHECK_RET(pwidth, wxT("field width must be specified"));
// save what follows the * (the +1 is to skip the asterisk itself!)
strcpy(temp, pwidth+1);
p->pad_nlongint = va_arg(argptr, long int *);
break;
+ case wxPAT_STAR:
+ // this will be handled as part of the next argument
+ return true;
+
case wxPAT_INVALID:
default:
return false;
if (!m_bAlignLeft)
for (i = 1; i < (size_t)m_nMinWidth; i++)
- APPEND_CH(_T(' '));
+ APPEND_CH(wxT(' '));
APPEND_CH(val);
if (m_bAlignLeft)
for (i = 1; i < (size_t)m_nMinWidth; i++)
- APPEND_CH(_T(' '));
+ APPEND_CH(wxT(' '));
}
break;
if (!m_bAlignLeft)
{
for (i = len; i < m_nMinWidth; i++)
- APPEND_CH(_T(' '));
+ APPEND_CH(wxT(' '));
}
len = wxMin((unsigned int)len, lenMax-lenCur);
if (m_bAlignLeft)
{
for (i = len; i < m_nMinWidth; i++)
- APPEND_CH(_T(' '));
+ APPEND_CH(wxT(' '));
}
}
break;
template<typename CharType>
struct wxPrintfConvSpecParser
{
- wxPrintfConvSpecParser(const CharType *format)
- : posarg_present(false), nonposarg_present(false),
- nargs(0)
+ typedef wxPrintfConvSpec<CharType> ConvSpec;
+
+ wxPrintfConvSpecParser(const CharType *fmt)
{
- memset(pspec, 0, sizeof(pspec));
+ nargs = 0;
+ posarg_present =
+ nonposarg_present = false;
- const CharType *toparse = format;
+ memset(pspec, 0, sizeof(pspec));
// parse the format string
- for (; *toparse != wxT('\0'); toparse++)
+ for ( const CharType *toparse = fmt; *toparse != wxT('\0'); toparse++ )
{
- if (*toparse == wxT('%') )
- {
- arg[nargs].Init();
+ // skip everything except format specifications
+ if ( *toparse != '%' )
+ continue;
- // let's see if this is a (valid) conversion specifier...
- if (arg[nargs].Parse(toparse))
- {
- // ...yes it is
- wxPrintfConvSpec<CharType> *current = &arg[nargs];
+ // also skip escaped percent signs
+ if ( toparse[1] == '%' )
+ {
+ toparse++;
+ continue;
+ }
- // make toparse point to the end of this specifier
- toparse = current->m_pArgEnd;
+ ConvSpec *spec = &specs[nargs];
+ spec->Init();
- if (current->m_pos > 0)
- {
- // the positionals start from number 1... adjust the index
- current->m_pos--;
- posarg_present = true;
- }
- else
- {
- // not a positional argument...
- current->m_pos = nargs;
- nonposarg_present = true;
- }
+ // attempt to parse this format specification
+ if ( !spec->Parse(toparse) )
+ continue;
- // this conversion specifier is tied to the pos-th argument...
- pspec[current->m_pos] = current;
- nargs++;
+ // advance to the end of this specifier
+ toparse = spec->m_pArgEnd;
- if (nargs == wxMAX_SVNPRINTF_ARGUMENTS)
- {
- wxLogDebug(wxT("A single call to wxVsnprintf() has more than %d arguments; ")
- wxT("ignoring all remaining arguments."), wxMAX_SVNPRINTF_ARGUMENTS);
- break; // cannot handle any additional conv spec
- }
- }
- else
+ // special handling for specifications including asterisks: we need
+ // to reserve an extra slot (or two if asterisks were used for both
+ // width and precision) in specs array in this case
+ for ( const char *f = strchr(spec->m_szFlags, '*');
+ f;
+ f = strchr(f + 1, '*') )
+ {
+ if ( nargs++ == wxMAX_SVNPRINTF_ARGUMENTS )
+ break;
+
+ // TODO: we need to support specifiers of the form "%2$*1$s"
+ // (this is the same as "%*s") as if any positional arguments
+ // are used all asterisks must be positional as well but this
+ // requires a lot of changes in this code (basically we'd need
+ // to rewrite Parse() to return "*" and conversion itself as
+ // separate entries)
+ if ( posarg_present )
{
- // it's safe to look in the next character of toparse as at
- // worst we'll hit its \0
- if (*(toparse+1) == wxT('%'))
- {
- // the Parse() returned false because we've found a %%
- toparse++;
- }
+ wxFAIL_MSG
+ (
+ wxString::Format
+ (
+ "Format string \"%s\" uses both positional "
+ "parameters and '*' but this is not currently "
+ "supported by this implementation, sorry.",
+ fmt
+ )
+ );
}
+
+ specs[nargs] = *spec;
+
+ // make an entry for '*' and point to it from pspec
+ spec->Init();
+ spec->m_type = wxPAT_STAR;
+ pspec[nargs - 1] = spec;
+
+ spec = &specs[nargs];
+ }
+
+ // check if this is a positional or normal argument
+ if ( spec->m_pos > 0 )
+ {
+ // the positional arguments start from number 1 so we need
+ // to adjust the index
+ spec->m_pos--;
+ posarg_present = true;
+ }
+ else // not a positional argument...
+ {
+ 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 )
+ break;
+ }
+
+
+ // warn if we lost any arguments (the program probably will crash
+ // anyhow because of stack corruption...)
+ if ( nargs == wxMAX_SVNPRINTF_ARGUMENTS )
+ {
+ wxFAIL_MSG
+ (
+ wxString::Format
+ (
+ "wxVsnprintf() currently supports only %d arguments, "
+ "but format string \"%s\" defines more of them.\n"
+ "You need to change wxMAX_SVNPRINTF_ARGUMENTS and "
+ "recompile if more are really needed.",
+ fmt, wxMAX_SVNPRINTF_ARGUMENTS
+ )
+ );
}
}
- wxPrintfConvSpec<CharType> arg[wxMAX_SVNPRINTF_ARGUMENTS];
- wxPrintfConvSpec<CharType> *pspec[wxMAX_SVNPRINTF_ARGUMENTS];
- bool posarg_present, nonposarg_present;
+ // total number of valid elements in specs
unsigned nargs;
+
+ // all format specifications in this format string in order of their
+ // appearance (which may be different from arguments order)
+ ConvSpec specs[wxMAX_SVNPRINTF_ARGUMENTS];
+
+ // pointer to specs array element for the N-th argument
+ ConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS];
+
+ // true if any positional/non-positional parameters are used
+ bool posarg_present,
+ nonposarg_present;
};
#undef APPEND_CH