Files
wxWidgets/src/common/wxprintf.cpp
Vadim Zeitlin 8fb4ab99f1 Always return -1 but set errno in our wxVsnprintf() on error
This makes it more compatible with the standard behaviour of vswprintf()
and allows to use the same logic in the builds using our version of this
function and the normal ones in DoStringPrintfV(), simplifying its
(already rather hairy) logic.

Update the tests to not require any particular return value in case of
buffer overflow, as this differs between Unicode and non-Unicode builds.
When we finally drop the latter, we should just check that it always
returns -1 in this case.

Note that ideal would be to return the actually needed size of the
buffer in case of the error due to buffer being too small, but this
isn't that simple to do and it's probably not worth spending time on
improving this code as long as we still need to use the buffer doubling
strategy in DoStringPrintfV() when using the standard vswprintf().
2020-12-01 21:53:55 +01:00

251 lines
7.9 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/wxprintf.cpp
// Purpose: wxWidgets wxPrintf() implementation
// Author: Ove Kaven
// Modified by: Ron Lee, Francesco Montorsi
// Created: 09/04/99
// Copyright: (c) wxWidgets copyright
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ===========================================================================
// headers, declarations, constants
// ===========================================================================
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/string.h"
#include "wx/hash.h"
#include "wx/utils.h" // for wxMin and wxMax
#include "wx/log.h"
#endif
#include "wx/private/wxprintf.h"
#include <errno.h>
// ============================================================================
// printf() implementation
// ============================================================================
// special test mode: define all functions below even if we don't really need
// them to be able to test them
#ifdef wxTEST_PRINTF
#undef wxCRT_VsnprintfW
#undef wxCRT_VsnprintfA
#endif
// ----------------------------------------------------------------------------
// implement [v]snprintf() if the system doesn't provide a safe one
// or if the system's one does not support positional parameters
// (very useful for i18n purposes)
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// common code for both ANSI and Unicode versions
// ----------------------------------------------------------------------------
#if !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
// 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.
template<typename CharType>
static int wxCopyStrWithPercents(
size_t maxOut,
CharType *dest,
size_t maxIn,
const CharType *source)
{
size_t written = 0;
if (maxIn == 0)
return 0;
size_t i;
for ( i = 0; i < maxIn-1 && written < maxOut; source++, i++)
{
dest[written++] = *source;
if (*(source+1) == wxT('%'))
{
// skip this additional '%' character
source++;
i++;
}
}
if (i < maxIn && written < maxOut)
// copy last character inconditionally
dest[written++] = *source;
return written;
}
template<typename CharType>
static int wxDoVsnprintf(CharType *buf, size_t lenMax,
const CharType *format, va_list argptr)
{
// useful for debugging, to understand if we are really using this function
// rather than the system implementation
#if 0
wprintf(L"Using wxCRT_VsnprintfW\n");
#endif
wxPrintfConvSpecParser<CharType> parser(format);
wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
size_t i;
// number of characters in the buffer so far, must be less than lenMax
size_t lenCur = 0;
if (parser.posarg_present && parser.nonposarg_present)
{
buf[0] = 0;
// Indicate to the caller that it's an unrecoverable error and not just
// due to the buffer being too small.
errno = EINVAL;
return -1; // format strings with both positional and
} // 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.
bool ok = true;
va_list ap;
wxVaCopy(ap, argptr);
// now load arguments from stack
for (i=0; i < parser.nargs && ok; i++)
{
// !pspec[i] means that the user forgot a positional parameter (e.g. %$1s %$3s);
// LoadArg == false means that wxPrintfConvSpec::Parse failed to set the
// conversion specifier 'type' to a valid value...
ok = parser.pspec[i] && parser.pspec[i]->LoadArg(&argdata[i], ap);
}
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;
errno = EINVAL;
return -1;
}
// finally, process each conversion specifier with its own argument
const CharType *toparse = format;
for (i=0; i < parser.nspecs; i++)
{
wxPrintfConvSpec<CharType>& spec = parser.specs[i];
// skip any asterisks, they're processed as part of the conversion they
// apply to
if ( spec.m_type == wxPAT_STAR )
continue;
// copy in the output buffer the portion of the format string between
// last specifier and the current one
size_t tocopy = ( spec.m_pArgPos - toparse );
lenCur += wxCopyStrWithPercents(lenMax - lenCur, buf + lenCur,
tocopy, toparse);
if (lenCur == lenMax)
{
buf[lenMax - 1] = 0;
return -1; // not enough space in the output buffer !
}
// process this specifier directly in the output buffer
int n = spec.Process(buf+lenCur, lenMax - lenCur,
&argdata[spec.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 !
}
lenCur += n;
// the +1 is because wxPrintfConvSpec::m_pArgEnd points to the last character
// of the format specifier, but we are not interested to it...
toparse = spec.m_pArgEnd + 1;
}
// copy portion of the format string after last specifier
// NOTE: toparse is pointing to the character just after the last processed
// conversion specifier
// NOTE2: the +1 is because we want to copy also the '\0'
size_t tocopy = wxStrlen(format) + 1 - ( toparse - format ) ;
lenCur += wxCopyStrWithPercents(lenMax - lenCur, buf + lenCur,
tocopy, toparse) - 1;
if (buf[lenCur])
{
buf[lenCur] = 0;
return -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
return lenCur;
}
#endif // !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
// ----------------------------------------------------------------------------
// wxCRT_VsnprintfW
// ----------------------------------------------------------------------------
#if !defined(wxCRT_VsnprintfW)
#if !wxUSE_WXVSNPRINTFW
#error "wxUSE_WXVSNPRINTFW must be 1 if our wxCRT_VsnprintfW is used"
#endif
int wxCRT_VsnprintfW(wchar_t *buf, size_t len,
const wchar_t *format, va_list argptr)
{
return wxDoVsnprintf(buf, len, format, argptr);
}
#else // wxCRT_VsnprintfW is defined
#if wxUSE_WXVSNPRINTFW
#error "wxUSE_WXVSNPRINTFW must be 0 if our wxCRT_VsnprintfW is not used"
#endif
#endif // !wxCRT_VsnprintfW
// ----------------------------------------------------------------------------
// wxCRT_VsnprintfA
// ----------------------------------------------------------------------------
#ifndef wxCRT_VsnprintfA
#if !wxUSE_WXVSNPRINTFA
#error "wxUSE_WXVSNPRINTFA must be 1 if our wxCRT_VsnprintfA is used"
#endif
int wxCRT_VsnprintfA(char *buf, size_t len,
const char *format, va_list argptr)
{
return wxDoVsnprintf(buf, len, format, argptr);
}
#else // wxCRT_VsnprintfA is defined
#if wxUSE_WXVSNPRINTFA
#error "wxUSE_WXVSNPRINTFA must be 0 if our wxCRT_VsnprintfA is not used"
#endif
#endif // !wxCRT_VsnprintfA