#if !defined(wxVsnprintf_)
+#if !wxUSE_WXVSNPRINTF
+ #error wxUSE_WXVSNPRINTF must be 1 if our wxVsnprintf_ is used
+#endif
+
// wxUSE_STRUTILS says our wxVsnprintf_ implementation to use or not to
// use wxStrlen and wxStrncpy functions over one-char processing loops.
//
#endif
// some limits of our implementation
-#define wxMAX_SVNPRINTF_ARGUMENTS 16
+#define wxMAX_SVNPRINTF_ARGUMENTS 64
#define wxMAX_SVNPRINTF_FLAGBUFFER_LEN 32
#define wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN 512
-
-// wxVsnprintf() needs to use a *system* implementation of swnprintf()
-// in order to perform some internal tasks.
-// NB: we cannot just use wxSnprintf() because for some systems it maybe
-// implemented later in this file using wxVsnprintf() and that would
-// result in an endless recursion and thus in a stack overflow
-#if wxUSE_UNICODE
- #if defined(__WINDOWS__) && !defined(HAVE_SWPRINTF)
- // all compilers under Windows should have swprintf()
- #define HAVE_SWPRINTF
- #endif
-
- // NB: MSVC 6 has only non-standard swprintf() declaration and while MSVC 7
- // and 7.1 do have the standard one, it's completely broken unless
- // /Zc:wchar_t is used while the other one works so use it instead, and
- // only VC8 has a working standard-compliant swprintf()
- #if defined(__WXWINCE__) || \
- (defined(__VISUALC__) && __VISUALC__ < 1400) || \
- defined(__GNUWIN32__) || \
- defined(__BORLANDC__)
- #ifndef HAVE_BROKEN_SWPRINTF_DECL
- #define HAVE_BROKEN_SWPRINTF_DECL
- #endif
- #endif
-
- // problem: on some systems swprintf takes the 'max' argument while on
- // others it doesn't
- #if defined(HAVE_BROKEN_SWPRINTF_DECL)
- // like when using sprintf(), since 'max' is not used, wxVsnprintf()
- // should always ensure that 'buff' is big enough for all common needs
- #define system_sprintf(buff, max, flags, data) \
- ::swprintf(buff, flags, data)
-
- #define SYSTEM_SPRINTF_IS_UNSAFE
- #else
- #if !defined(HAVE_SWPRINTF)
- #error wxVsnprintf() needs a system swprintf() implementation!
- #endif
-
- #define system_sprintf(buff, max, flags, data) \
- ::swprintf(buff, max, flags, data)
- #endif
-#else // !wxUSE_UNICODE
- #if defined(__VISUALC__) || \
- (defined(__BORLANDC__) && __BORLANDC__ >= 0x540)
- #define system_sprintf(buff, max, flags, data) \
- ::_snprintf(buff, max, flags, data)
- #elif defined(HAVE_SNPRINTF)
- #define system_sprintf(buff, max, flags, data) \
- ::snprintf(buff, max, flags, data)
- #else // NB: at least sprintf() should always be available
- // since 'max' is not used in this case, wxVsnprintf() should always
- // ensure that 'buff' is big enough for all common needs
- // (see wxMAX_SVNPRINTF_FLAGBUFFER_LEN and wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN)
- #define system_sprintf(buff, max, flags, data) \
- ::sprintf(buff, flags, data)
-
- #define SYSTEM_SPRINTF_IS_UNSAFE
- #endif
-#endif // wxUSE_UNICODE/!wxUSE_UNICODE
-
-
+// prefer snprintf over sprintf
+#if defined(__VISUALC__) || \
+ (defined(__BORLANDC__) && __BORLANDC__ >= 0x540)
+ #define system_sprintf(buff, max, flags, data) \
+ ::_snprintf(buff, max, flags, data)
+#elif defined(HAVE_SNPRINTF)
+ #define system_sprintf(buff, max, flags, data) \
+ ::snprintf(buff, max, flags, data)
+#else // NB: at least sprintf() should always be available
+ // since 'max' is not used in this case, wxVsnprintf() should always
+ // ensure that 'buff' is big enough for all common needs
+ // (see wxMAX_SVNPRINTF_FLAGBUFFER_LEN and wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN)
+ #define system_sprintf(buff, max, flags, data) \
+ ::sprintf(buff, flags, data)
+
+ #define SYSTEM_SPRINTF_IS_UNSAFE
+#endif
// the conversion specifiers accepted by wxVsnprintf_
enum wxPrintfArgType {
wxPAT_INT, // %d, %i, %o, %u, %x, %X
wxPAT_LONGINT, // %ld, etc
-#if SIZEOF_LONG_LONG
+#ifdef wxLongLong_t
wxPAT_LONGLONGINT, // %Ld, etc
#endif
wxPAT_SIZET, // %Zd, etc
typedef union {
int pad_int; // %d, %i, %o, %u, %x, %X
long int pad_longint; // %ld, etc
-#if SIZEOF_LONG_LONG
- long long int pad_longlongint; // %Ld, etc
+#ifdef wxLongLong_t
+ wxLongLong_t pad_longlongint; // %Ld, etc
#endif
size_t pad_sizet; // %Zd, etc
// thus could be safely declared as a char[] buffer, we want it to be wxChar
// so that in Unicode builds we can avoid to convert its contents to Unicode
// chars when copying it in user's buffer.
- wxChar m_szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
+ char m_szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
public:
// Process this conversion specifier and puts the result in the given
// buffer. Returns the number of characters written in 'buf' or -1 if
// there's not enough space.
- int Process(wxChar *buf, size_t lenMax, wxPrintfArg *p);
+ int Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t written);
// Loads the argument of this conversion specifier from given va_list.
bool LoadArg(wxPrintfArg *p, va_list &argptr);
// this character will never be removed from m_szFlags array and
// is important when calling sprintf() in wxPrintfConvSpec::Process() !
- m_szFlags[0] = wxT('%');
+ m_szFlags[0] = '%';
}
bool wxPrintfConvSpec::Parse(const wxChar *format)
// temporary parse data
size_t flagofs = 1;
- bool in_prec, prec_dot;
+ bool in_prec, // true if we found the dot in some previous iteration
+ prec_dot; // true if the dot has been already added to m_szFlags
int ilen = 0;
m_bAlignLeft = in_prec = prec_dot = false;
#define CHECK_PREC \
if (in_prec && !prec_dot) \
{ \
- m_szFlags[flagofs++] = wxT('.'); \
+ m_szFlags[flagofs++] = '.'; \
prec_dot = true; \
}
case wxT('+'):
case wxT('\''):
CHECK_PREC
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
break;
case wxT('-'):
CHECK_PREC
m_bAlignLeft = true;
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
break;
case wxT('.'):
case wxT('h'):
ilen = -1;
CHECK_PREC
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
break;
case wxT('l'):
else
ilen = 1;
CHECK_PREC
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
break;
case wxT('q'):
case wxT('L'):
ilen = 2;
CHECK_PREC
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
+ break;
+#ifdef __WXMSW__
+ // under Windows we support the special '%I64' notation as longlong
+ // 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++;
+
+ ilen = 2;
+ CHECK_PREC
+ m_szFlags[flagofs++] = char(ch);
+ m_szFlags[flagofs++] = '6';
+ m_szFlags[flagofs++] = '4';
break;
+#endif // __WXMSW__
case wxT('Z'):
ilen = 3;
CHECK_PREC
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
break;
case wxT('*'):
// save the * in our formatting buffer...
// will be replaced later by Process()
- m_szFlags[flagofs++] = ch;
+ m_szFlags[flagofs++] = char(ch);
break;
case wxT('1'): case wxT('2'): case wxT('3'):
while ( (*m_pArgEnd >= wxT('0')) &&
(*m_pArgEnd <= wxT('9')) )
{
- m_szFlags[flagofs++] = (*m_pArgEnd);
+ m_szFlags[flagofs++] = char(*m_pArgEnd);
len = len*10 + (*m_pArgEnd - wxT('0'));
m_pArgEnd++;
}
case wxT('x'):
case wxT('X'):
CHECK_PREC
- m_szFlags[flagofs++] = ch;
- m_szFlags[flagofs] = wxT('\0');
+ m_szFlags[flagofs++] = char(ch);
+ m_szFlags[flagofs] = '\0';
if (ilen == 0)
m_type = wxPAT_INT;
else if (ilen == -1)
else if (ilen == 1)
m_type = wxPAT_LONGINT;
else if (ilen == 2)
-#if SIZEOF_LONG_LONG
+#ifdef wxLongLong_t
m_type = wxPAT_LONGLONGINT;
-#else // !long long
+#else // !wxLongLong_t
m_type = wxPAT_LONGINT;
-#endif // long long/!long long
+#endif // wxLongLong_t/!wxLongLong_t
else if (ilen == 3)
m_type = wxPAT_SIZET;
done = true;
case wxT('g'):
case wxT('G'):
CHECK_PREC
- m_szFlags[flagofs++] = ch;
- m_szFlags[flagofs] = wxT('\0');
+ 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;
void wxPrintfConvSpec::ReplaceAsteriskWith(int width)
{
- wxChar temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
+ char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
// find the first * in our flag buffer
- wxChar *pwidth = wxStrchr(m_szFlags, wxT('*'));
- wxASSERT(pwidth);
+ char *pwidth = strchr(m_szFlags, '*');
+ wxCHECK_RET(pwidth, _T("field width must be specified"));
// save what follows the * (the +1 is to skip the asterisk itself!)
- wxStrcpy(temp, pwidth+1);
+ strcpy(temp, pwidth+1);
if (width < 0)
{
pwidth[0] = wxT('-');
// replace * with the actual integer given as width
#ifndef SYSTEM_SPRINTF_IS_UNSAFE
int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) /
- sizeof(wxChar);
+ sizeof(*m_szFlags);
#endif
- int offset = system_sprintf(pwidth, maxlen, wxT("%d"), abs(width));
+ int offset = system_sprintf(pwidth, maxlen, "%d", abs(width));
// restore after the expanded * what was following it
- wxStrcpy(pwidth+offset, temp);
+ strcpy(pwidth+offset, temp);
}
bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
case wxPAT_LONGINT:
p->pad_longint = va_arg(argptr, long int);
break;
-#if SIZEOF_LONG_LONG
+#ifdef wxLongLong_t
case wxPAT_LONGLONGINT:
- p->pad_longlongint = va_arg(argptr, long long int);
+ p->pad_longlongint = va_arg(argptr, wxLongLong_t);
break;
-#endif
+#endif // wxLongLong_t
case wxPAT_SIZET:
p->pad_sizet = va_arg(argptr, size_t);
break;
return true; // loading was successful
}
-int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
+int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t written)
{
// buffer to avoid dynamic memory allocation each time for small strings;
// note that this buffer is used only to hold results of number formatting,
// %s directly writes user's string in buf, without using szScratch
- wxChar szScratch[wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN];
+ char szScratch[wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN];
size_t lenScratch = 0, lenCur = 0;
#define APPEND_CH(ch) \
lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longint);
break;
-#if SIZEOF_LONG_LONG
+#ifdef wxLongLong_t
case wxPAT_LONGLONGINT:
lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longlongint);
break;
break;
case wxPAT_NINT:
- *p->pad_nint = lenCur;
+ *p->pad_nint = written;
break;
case wxPAT_NSHORTINT:
- *p->pad_nshortint = (short int)lenCur;
+ *p->pad_nshortint = (short int)written;
break;
case wxPAT_NLONGINT:
- *p->pad_nlongint = lenCur;
+ *p->pad_nlongint = written;
break;
case wxPAT_INVALID:
return -1;
}
-#ifdef HAVE_BROKEN_SWPRINTF_DECL
- wxUnusedVar(lenScratch); // avoid dummy warnings
-#endif
-
// if we used system's sprintf() then we now need to append the s_szScratch
// buffer to the given one...
switch (m_type)
{
case wxPAT_INT:
case wxPAT_LONGINT:
-#if SIZEOF_LONG_LONG
+#ifdef wxLongLong_t
case wxPAT_LONGLONGINT:
#endif
case wxPAT_SIZET:
case wxPAT_LONGDOUBLE:
case wxPAT_DOUBLE:
case wxPAT_POINTER:
-#if wxUSE_STRUTILS
+ wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
+#if !wxUSE_UNICODE
{
- wxASSERT(lenScratch >= 0 && lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
if (lenMax < lenScratch)
{
// fill output buffer and then return -1
}
#else
{
- APPEND_STR(szScratch);
+ // Copy the char scratch to the wide output. This requires
+ // conversion, but we can optimise by making use of the fact
+ // that we are formatting numbers, this should mean only 7-bit
+ // ascii characters are involved.
+ wxChar *bufptr = buf;
+ const wxChar *bufend = buf + lenMax;
+ const char *scratchptr = szScratch;
+
+ // Simply copy each char to a wxChar, stopping on the first
+ // null or non-ascii byte. Checking '(signed char)*scratchptr
+ // > 0' is an extra optimisation over '*scratchptr != 0 &&
+ // isascii(*scratchptr)', though it assumes signed char is
+ // 8-bit 2 complement.
+ while ((signed char)*scratchptr > 0 && bufptr != bufend)
+ *bufptr++ = *scratchptr++;
+
+ if (bufptr == bufend)
+ return -1;
+
+ lenCur += bufptr - buf;
+
+ // check if the loop stopped on a non-ascii char, if yes then
+ // fall back to wxMB2WX
+ if (*scratchptr)
+ {
+ size_t len = wxMB2WX(bufptr, scratchptr, bufend - bufptr);
+
+ if (len && len != (size_t)(-1))
+ if (bufptr[len - 1])
+ return -1;
+ else
+ lenCur += len;
+ }
}
#endif
break;
return lenCur;
}
-// differences from standard strncpy:
-// 1) copies everything from 'source' except for '%%' sequence which is copied as '%'
-// 2) returns the number of written characters in 'dest' as it could differ from given 'n'
-// 3) much less optimized, unfortunately...
-static int wxCopyStrWithPercents(wxChar *dest, const wxChar *source, size_t n)
+// Copy chars from source to dest converting '%%' to '%'. Takes at most maxIn
+// chars from source and write at most outMax chars to dest, returns the
+// number of chars actually written. Does not treat null specially.
+//
+static int wxCopyStrWithPercents(
+ size_t maxOut,
+ wxChar *dest,
+ size_t maxIn,
+ const wxChar *source)
{
size_t written = 0;
- if (n == 0)
+ if (maxIn == 0)
return 0;
size_t i;
- for ( i = 0; i < n-1; source++, i++)
+ for ( i = 0; i < maxIn-1 && written < maxOut; source++, i++)
{
dest[written++] = *source;
if (*(source+1) == wxT('%'))
}
}
- if (i < n)
+ if (i < maxIn && written < maxOut)
// copy last character inconditionally
dest[written++] = *source;
}
if (posarg_present && nonposarg_present)
+ {
+ buf[0] = 0;
return -1; // format strings with both positional and
- // non-positional conversion specifier are unsupported !!
+ } // non-positional conversion specifier are unsupported !!
// on platforms where va_list is an array type, it is necessary to make a
// copy to be able to pass it to LoadArg as a reference.
va_end(ap);
// something failed while loading arguments from the variable list...
+ // (e.g. the user repeated twice the same positional argument)
if (!ok)
+ {
+ buf[0] = 0;
return -1;
+ }
// finally, process each conversion specifier with its own argument
toparse = format;
// copy in the output buffer the portion of the format string between
// last specifier and the current one
size_t tocopy = ( arg[i].m_pArgPos - toparse );
- if (lenCur+tocopy >= lenMax)
+
+ lenCur += wxCopyStrWithPercents(lenMax - lenCur, buf + lenCur,
+ tocopy, toparse);
+ if (lenCur == lenMax)
{
- // not enough space in the output buffer !
- // copy until the end of remaining space and then stop
- wxCopyStrWithPercents(buf+lenCur, toparse, lenMax - lenCur - 1);
- buf[lenMax-1] = wxT('\0');
- return -1;
+ buf[lenMax - 1] = 0;
+ return lenMax+1; // not enough space in the output buffer !
}
- lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy);
-
// process this specifier directly in the output buffer
- int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].m_pos]);
+ int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].m_pos], lenCur);
if (n == -1)
{
buf[lenMax-1] = wxT('\0'); // be sure to always NUL-terminate the string
- return -1; // not enough space in the output buffer !
+ return lenMax+1; // not enough space in the output buffer !
}
lenCur += n;
// conversion specifier
// NOTE2: the +1 is because we want to copy also the '\0'
size_t tocopy = wxStrlen(format) + 1 - ( toparse - format ) ;
- if (lenCur+tocopy >= lenMax)
- return -1; // not enough space in the output buffer !
- // the -1 is because of the '\0'
- lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1;
+ lenCur += wxCopyStrWithPercents(lenMax - lenCur, buf + lenCur,
+ tocopy, toparse) - 1;
+ if (buf[lenCur])
+ {
+ buf[lenCur] = 0;
+ return lenMax+1; // not enough space in the output buffer !
+ }
+
+ // Don't do:
+ // wxASSERT(lenCur == wxStrlen(buf));
+ // in fact if we embedded NULLs in the output buffer (using %c with a '\0')
+ // such check would fail
- wxASSERT(lenCur == wxStrlen(buf));
return lenCur;
}
#undef APPEND_STR
#undef CHECK_PREC
-#endif // !wxVsnprintfA
+#else // wxVsnprintf_ is defined
+
+#if wxUSE_WXVSNPRINTF
+ #error wxUSE_WXVSNPRINTF must be 0 if our wxVsnprintf_ is not used
+#endif
+
+#endif // !wxVsnprintf_
#if !defined(wxSnprintf_)
int WXDLLEXPORT wxSnprintf_(wxChar *buf, size_t len, const wxChar *format, ...)
#ifdef wxNEED_FPUTS
int wxFputs(const wchar_t *ws, FILE *stream)
{
+ wxCharBuffer buf(wxConvLibc.cWC2MB(ws));
+ if ( !buf )
+ return -1;
+
// counting the number of wide characters written isn't worth the trouble,
// simply distinguish between ok and error
- return fputs(wxConvLibc.cWC2MB(ws), stream) == -1 ? -1 : 0;
+ return fputs(buf, stream) == -1 ? -1 : 0;
}
#endif // wxNEED_FPUTS