X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/412a5c570d18aa64f0077a3278ca232776f3f259..855f31ebe72bef834a32df2c274b41fb282ad265:/src/common/wxchar.cpp diff --git a/src/common/wxchar.cpp b/src/common/wxchar.cpp index c77dc9cec3..cf4e0a5be5 100644 --- a/src/common/wxchar.cpp +++ b/src/common/wxchar.cpp @@ -20,6 +20,8 @@ #pragma hdrstop #endif +#include "wx/wxchar.h" + #define _ISOC9X_SOURCE 1 // to get vsscanf() #define _BSD_SOURCE 1 // to still get strdup() @@ -35,11 +37,11 @@ #endif #ifndef WX_PRECOMP - #include "wx/wxchar.h" #include "wx/string.h" #include "wx/hash.h" + #include "wx/utils.h" // for wxMin and wxMax + #include "wx/log.h" #endif - #include "wx/utils.h" // for wxMin and wxMax #if defined(__WIN32__) && defined(wxNEED_WX_CTYPE_H) #include @@ -165,6 +167,10 @@ bool WXDLLEXPORT wxOKlibc() #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. // @@ -183,8 +189,27 @@ bool WXDLLEXPORT wxOKlibc() #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 + +// 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 { @@ -192,7 +217,7 @@ 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 @@ -217,8 +242,8 @@ enum wxPrintfArgType { 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 @@ -278,7 +303,7 @@ public: // 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: @@ -296,7 +321,7 @@ 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); @@ -317,7 +342,7 @@ void wxPrintfConvSpec::Init() // 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) @@ -326,7 +351,8 @@ 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; @@ -356,13 +382,13 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) 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('.'): @@ -377,7 +403,7 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('h'): ilen = -1; CHECK_PREC - m_szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = char(ch); break; case wxT('l'): @@ -387,20 +413,39 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) 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('*'): @@ -421,7 +466,7 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) // 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'): @@ -433,7 +478,7 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) 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++; } @@ -475,7 +520,7 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('x'): case wxT('X'): CHECK_PREC - m_szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = char(ch); m_szFlags[flagofs] = '\0'; if (ilen == 0) m_type = wxPAT_INT; @@ -487,11 +532,11 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) 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; @@ -503,7 +548,7 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('g'): case wxT('G'): CHECK_PREC - m_szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = char(ch); m_szFlags[flagofs] = '\0'; if (ilen == 2) m_type = wxPAT_LONGDOUBLE; @@ -514,6 +559,8 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('p'): m_type = wxPAT_POINTER; + m_szFlags[flagofs++] = char(ch); + m_szFlags[flagofs] = '\0'; done = true; break; @@ -597,14 +644,14 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) 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('-'); @@ -612,15 +659,14 @@ void wxPrintfConvSpec::ReplaceAsteriskWith(int width) } // replace * with the actual integer given as width -#if wxUSE_UNICODE - int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) / sizeof(wxChar); - int offset = ::swprintf(pwidth, maxlen, L"%d", abs(width)); -#else - int offset = ::sprintf(pwidth, "%d", abs(width)); +#ifndef SYSTEM_SPRINTF_IS_UNSAFE + int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) / + sizeof(*m_szFlags); #endif + 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) @@ -656,11 +702,11 @@ 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; @@ -675,7 +721,7 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr) break; case wxPAT_CHAR: - p->pad_char = va_arg(argptr, int); // char is promoted to int when passed through '...' + p->pad_char = (char)va_arg(argptr, int); // char is promoted to int when passed through '...' break; case wxPAT_WCHAR: p->pad_wchar = (wchar_t)va_arg(argptr, int); // char is promoted to int when passed through '...' @@ -706,22 +752,14 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr) 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 -#define wxSCRATCH_BUFFER_SIZE 512 - - wxChar szScratch[wxSCRATCH_BUFFER_SIZE]; + char szScratch[wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN]; size_t lenScratch = 0, lenCur = 0; -#if wxUSE_UNICODE -#define system_sprintf(buff, flags, data) ::swprintf(buff, wxSCRATCH_BUFFER_SIZE, flags, data) -#else -#define system_sprintf ::sprintf -#endif - #define APPEND_CH(ch) \ { \ if ( lenCur == lenMax ) \ @@ -741,33 +779,33 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) switch ( m_type ) { case wxPAT_INT: - lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_int); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_int); break; case wxPAT_LONGINT: - lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longint); + 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, m_szFlags, p->pad_longlongint); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longlongint); break; #endif // SIZEOF_LONG_LONG case wxPAT_SIZET: - lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_sizet); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_sizet); break; case wxPAT_LONGDOUBLE: - lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longdouble); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longdouble); break; case wxPAT_DOUBLE: - lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_double); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_double); break; case wxPAT_POINTER: - lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_pointer); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_pointer); break; case wxPAT_CHAR: @@ -892,15 +930,15 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) 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: @@ -914,16 +952,16 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) { 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 < wxSCRATCH_BUFFER_SIZE); if (lenMax < lenScratch) { // fill output buffer and then return -1 @@ -935,7 +973,39 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) } #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; @@ -947,19 +1017,23 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) 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('%')) @@ -970,7 +1044,7 @@ static int wxCopyStrWithPercents(wxChar *dest, const wxChar *source, size_t n) } } - if (i < n) + if (i < maxIn && written < maxOut) // copy last character inconditionally dest[written++] = *source; @@ -1051,8 +1125,10 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, } 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. @@ -1072,8 +1148,12 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, 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; @@ -1082,23 +1162,21 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, // 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; @@ -1112,13 +1190,20 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, // 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; } @@ -1126,7 +1211,13 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, #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, ...) @@ -1166,9 +1257,13 @@ 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