// the conversion specifiers accepted by wxCRT_VsnprintfW
-enum wxPrintfArgType {
+enum wxPrintfArgType
+{
wxPAT_INVALID = -1,
wxPAT_INT, // %d, %i, %o, %u, %x, %X
#ifdef wxLongLong_t
wxPAT_LONGLONGINT, // %Ld, etc
#endif
- wxPAT_SIZET, // %Zd, etc
+ wxPAT_SIZET, // %zd, etc
wxPAT_DOUBLE, // %e, %E, %f, %g, %G
wxPAT_LONGDOUBLE, // %le, etc
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
+ size_t pad_sizet; // %zd, etc
double pad_double; // %e, %E, %f, %g, %G
long double pad_longdouble; // %le, 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 {};
// it's task of the caller ensure that memory is still valid !
const CharType *m_pArgEnd;
- // a little buffer where formatting flags like #+\.hlqLZ are stored by Parse()
+ // 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];
m_pArgPos = m_pArgEnd = NULL;
m_type = wxPAT_INVALID;
+ memset(m_szFlags, 0, sizeof(m_szFlags));
// this character will never be removed from m_szFlags array and
// is important when calling sprintf() in wxPrintfConvSpec::Process() !
m_szFlags[0] = '%';
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;
// integer conversion specifier for MSVC compatibility
// (it behaves exactly as '%lli' or '%Li' or '%qi')
case wxT('I'):
- if (*(m_pArgEnd+1) != wxT('6') ||
- *(m_pArgEnd+2) != wxT('4'))
- return false; // bad format
-
- m_pArgEnd++;
- m_pArgEnd++;
+ if (*(m_pArgEnd+1) == wxT('6') &&
+ *(m_pArgEnd+2) == wxT('4'))
+ {
+ m_pArgEnd++;
+ m_pArgEnd++;
- ilen = 2;
- CHECK_PREC
- m_szFlags[flagofs++] = char(ch);
- m_szFlags[flagofs++] = '6';
- m_szFlags[flagofs++] = '4';
- break;
+ ilen = 2;
+ CHECK_PREC
+ m_szFlags[flagofs++] = char(ch);
+ m_szFlags[flagofs++] = '6';
+ m_szFlags[flagofs++] = '4';
+ break;
+ }
+ // else: fall-through, 'I' is MSVC equivalent of C99 'z'
#endif // __WXMSW__
+ case wxT('z'):
case wxT('Z'):
+ // 'z' is C99 standard for size_t and ptrdiff_t, 'Z' was used
+ // for this purpose in libc5 and by wx <= 2.8
ilen = 3;
CHECK_PREC
m_szFlags[flagofs++] = char(ch);
case wxT('X'):
CHECK_PREC
m_szFlags[flagofs++] = char(ch);
- m_szFlags[flagofs] = '\0';
if (ilen == 0)
m_type = wxPAT_INT;
else if (ilen == -1)
case wxT('G'):
CHECK_PREC
m_szFlags[flagofs++] = char(ch);
- m_szFlags[flagofs] = '\0';
if (ilen == 2)
m_type = wxPAT_LONGDOUBLE;
else
case wxT('p'):
m_type = wxPAT_POINTER;
m_szFlags[flagofs++] = char(ch);
- m_szFlags[flagofs] = '\0';
done = true;
break;
// 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;
case wxPAT_PCHAR:
case wxPAT_PWCHAR:
{
- wxArgNormalizedString arg(p->pad_str);
- wxString s = arg;
-
- if ( !arg.IsValid() && m_nMaxWidth >= 6 )
- s = wxT("(null)");
+ wxString s;
+ if ( !p->pad_str )
+ {
+ if ( m_nMaxWidth >= 6 )
+ s = wxT("(null)");
+ }
+ else if (m_type == wxPAT_PCHAR)
+ s.assign(static_cast<const char *>(p->pad_str));
+ else // m_type == wxPAT_PWCHAR
+ s.assign(static_cast<const wchar_t *>(p->pad_str));
typename wxPrintfStringHelper<CharType>::ConvertedType strbuf(
wxPrintfStringHelper<CharType>::Convert(s));
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('%') )
+ // skip everything except format specifications
+ if ( *toparse != '%' )
+ continue;
+
+ // also skip escaped percent signs
+ if ( toparse[1] == '%' )
{
- arg[nargs].Init();
+ 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];
+ ConvSpec *spec = &specs[nargs];
+ spec->Init();
- // make toparse point to the end of this specifier
- toparse = current->m_pArgEnd;
+ // attempt to parse this format specification
+ if ( !spec->Parse(toparse) )
+ continue;
- 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;
- }
+ // advance to the end of this specifier
+ toparse = spec->m_pArgEnd;
- // this conversion specifier is tied to the pos-th argument...
- pspec[current->m_pos] = current;
- nargs++;
+ // 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
+ if ( const char *f = strchr(spec->m_szFlags, '*') )
+ {
+ unsigned numAsterisks = 1;
+ if ( strchr(++f, '*') )
+ numAsterisks++;
- 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
+ for ( unsigned n = 0; n < numAsterisks; n++ )
{
- // it's safe to look in the next character of toparse as at
- // worst we'll hit its \0
- if (*(toparse+1) == wxT('%'))
+ 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 )
{
- // 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