Ticket #3011: 0013-Remove-sys_vswprintf.patch

File 0013-Remove-sys_vswprintf.patch, 22.1 KB (added by Philip Taylor, 9 years ago)
  • source/lib/secure_crt.cpp

    From 0fb4fb20359788b8f4b327d2ea3d1541c56cfbd9 Mon Sep 17 00:00:00 2001
    From: Philip Taylor <philip@zaynar.co.uk>
    Date: Tue, 20 Jan 2015 23:16:24 +0000
    Subject: [PATCH 13/13] Remove sys_vswprintf.
    
    The implementation on Windows was gross. The only user was CLogger, which no longer uses it.
    
    Also fix vswprintf_s to handle truncated output correctly (by returning "") on Linux, now that CLogger is no longer relying on the buggy behaviour.
    ---
     source/lib/secure_crt.cpp             |   2 +-
     source/lib/sysdep/os/unix/printf.cpp  |  39 ---
     source/lib/sysdep/os/win/wprintf.cpp  | 526 ----------------------------------
     source/lib/sysdep/sysdep.h            |  11 -
     source/lib/sysdep/tests/test_printf.h |  99 -------
     source/lib/tests/test_secure_crt.h    |  29 +-
     source/ps/CLogger.cpp                 |   1 -
     7 files changed, 29 insertions(+), 678 deletions(-)
     delete mode 100644 source/lib/sysdep/os/unix/printf.cpp
     delete mode 100644 source/lib/sysdep/os/win/wprintf.cpp
     delete mode 100644 source/lib/sysdep/tests/test_printf.h
    
    diff --git a/source/lib/secure_crt.cpp b/source/lib/secure_crt.cpp
    index 849c76e..40c0771 100644
    a b int tvsprintf_s(tchar* dst, size_t max_dst_chars, const tchar* fmt, va_list ap)  
    237237    }
    238238
    239239    const int ret = tvsnprintf(dst, max_dst_chars, fmt, ap);
    240     if(ret >= int(max_dst_chars))   // not enough space
     240    if(ret < 0 || ret >= int(max_dst_chars))    // not enough space
    241241    {
    242242        dst[0] = '\0';
    243243        return -1;
  • deleted file source/lib/sysdep/os/unix/printf.cpp

    diff --git a/source/lib/sysdep/os/unix/printf.cpp b/source/lib/sysdep/os/unix/printf.cpp
    deleted file mode 100644
    index 6c5a8be..0000000
    + -  
    1 /* Copyright (c) 2010 Wildfire Games
    2  *
    3  * Permission is hereby granted, free of charge, to any person obtaining
    4  * a copy of this software and associated documentation files (the
    5  * "Software"), to deal in the Software without restriction, including
    6  * without limitation the rights to use, copy, modify, merge, publish,
    7  * distribute, sublicense, and/or sell copies of the Software, and to
    8  * permit persons to whom the Software is furnished to do so, subject to
    9  * the following conditions:
    10  *
    11  * The above copyright notice and this permission notice shall be included
    12  * in all copies or substantial portions of the Software.
    13  *
    14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  */
    22 
    23 #include "precompiled.h"
    24 
    25 #include <cstdio>
    26 #include <cstdarg>
    27 
    28 // See declaration in sysdep.h for explanation of need
    29 
    30 int sys_vswprintf(wchar_t* buffer, size_t count, const wchar_t* format, va_list argptr)
    31 {
    32     int ret = vswprintf(buffer, count, format, argptr);
    33 
    34     // Guarantee the buffer is null terminated on error
    35     if (ret < 0 && count > 0)
    36         buffer[count-1] = '\0';
    37 
    38     return ret;
    39 }
  • deleted file source/lib/sysdep/os/win/wprintf.cpp

    diff --git a/source/lib/sysdep/os/win/wprintf.cpp b/source/lib/sysdep/os/win/wprintf.cpp
    deleted file mode 100644
    index dce843c..0000000
    + -  
    1 /* Copyright (c) 2010 Wildfire Games
    2  *
    3  * Permission is hereby granted, free of charge, to any person obtaining
    4  * a copy of this software and associated documentation files (the
    5  * "Software"), to deal in the Software without restriction, including
    6  * without limitation the rights to use, copy, modify, merge, publish,
    7  * distribute, sublicense, and/or sell copies of the Software, and to
    8  * permit persons to whom the Software is furnished to do so, subject to
    9  * the following conditions:
    10  *
    11  * The above copyright notice and this permission notice shall be included
    12  * in all copies or substantial portions of the Software.
    13  *
    14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  */
    22 
    23 /*
    24  * implementation of sys_vswprintf.
    25  */
    26 
    27 #include "precompiled.h"
    28 
    29 #if ARCH_IA32
    30 
    31 /*
    32     See http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html
    33     for the specification (apparently an extension to ISO C) that was used
    34     when creating this code.
    35 */
    36 
    37 /*
    38     Added features (compared to MSVC's printf):
    39         Positional parameters (e.g. "%1$d", where '1' means '1st in the parameter list')
    40         %lld (equivalent to %I64d in MSVC7.1, though it's supported natively by MSVC8)
    41 
    42     Unsupported features (compared to a perfect implementation):
    43         '   <-- because MSVC doesn't support it
    44         %S  }
    45         %C  } because they're unnecessary and can cause confusion
    46         *   <-- probably works in some situations, but don't expect it to
    47         *m$ <-- positional precision parameters, because they're not worthwhile
    48         portability <-- just use GCC
    49         efficiency  <-- nothing in the spec says it should be as fast as possible
    50                         (the code could be made a lot faster if speed mattered more
    51                         than non-fixed size buffers)
    52 
    53 */
    54 
    55 #ifndef _UNICODE
    56 #define _UNICODE
    57 #endif
    58 #include <tchar.h>
    59 #ifdef _UNICODE
    60 # define tstring wstring
    61 # define tstringstream wstringstream
    62 #else
    63 # define tstring string
    64 # define tstringstream stringstream
    65 #endif
    66 #include <string>
    67 #include <vector>
    68 
    69 #include <stdio.h>
    70 #include <sstream>
    71 #include <stdarg.h>
    72 
    73 #if MSC_VERSION < 1400
    74 # define USE_I64_FORMAT 1
    75 #else
    76 # define USE_I64_FORMAT 0
    77 #endif
    78 
    79 enum
    80 {
    81     SPECFLAG_THOUSANDS      = 1,    // '
    82     SPECFLAG_LEFTJUSTIFIED  = 2,    // -
    83     SPECFLAG_SIGNED         = 4,    // +
    84     SPECFLAG_SPACEPREFIX    = 8,    // <space>
    85     SPECFLAG_ALTERNATE      = 16,   // #
    86     SPECFLAG_ZEROPAD        = 32    // 0
    87 };
    88 
    89 struct FormatChunk
    90 {
    91     virtual ~FormatChunk() { }
    92     virtual int ChunkType() = 0; // 0 = FormatSpecification, 1 = FormatString
    93 };
    94 
    95 struct FormatVariable : public FormatChunk
    96 {
    97     int ChunkType() { return 0; }
    98     int position;   // undefined if the format includes no positional elements
    99     char flags;     // ['-+ #0]
    100     int width;      // -1 for *, 0 for unspecified 
    101     int precision;  // -1 for *
    102     int length;     // "\0\0\0l", "\0\0hh", etc
    103     char type;      // 'd', etc
    104 };
    105 
    106 struct FormatString : public FormatChunk
    107 {
    108     int ChunkType() { return 1; }
    109     FormatString(std::tstring t) : text(t) {}
    110     std::tstring text;
    111 };
    112 
    113 
    114 int get_flag(TCHAR c)
    115 {
    116     switch (c)
    117     {
    118     case _T('\''): return SPECFLAG_THOUSANDS;
    119     case _T('-'):  return SPECFLAG_LEFTJUSTIFIED;
    120     case _T('+'):  return SPECFLAG_SIGNED;
    121     case _T(' '):  return SPECFLAG_SPACEPREFIX;
    122     case _T('#'):  return SPECFLAG_ALTERNATE;
    123     case _T('0'):  return SPECFLAG_ZEROPAD;
    124     }
    125     return 0;
    126 }
    127 
    128 std::tstring flags_to_string(char flags)
    129 {
    130     std::tstring s;
    131     const char* c = "\'-+ #0";
    132     for (int i=0; i<6; ++i)
    133         if (flags & (1<<i))
    134             s += c[i];
    135     return s;
    136 }
    137 
    138 template<typename T>
    139 std::tstring to_string(T n)
    140 {
    141     std::tstring s;
    142     std::tstringstream str;
    143     str << n;
    144     str >> s;
    145     return s;
    146 }
    147 
    148 int is_lengthmod(TCHAR c)
    149 {
    150     return
    151         c == _T('h') ||
    152         c == _T('l') ||
    153         c == _T('j') ||
    154         c == _T('z') ||
    155         c == _T('t') ||
    156         c == _T('L');
    157 }
    158 
    159 // _T2('l') == 'll'
    160 #define _T2(a) ((a<<8) | a)
    161 
    162 int type_size(TCHAR type, int length)
    163 {
    164     switch (type)
    165     {
    166     case 'd':
    167     case 'i':
    168         switch (length)
    169         {
    170         case _T ('l'):  return sizeof(long);
    171         case _T2('l'):  return sizeof(long long);
    172         case _T ('h'):  return sizeof(short);
    173         case _T2('h'):  return sizeof(char);
    174         default: return sizeof(int);
    175         }
    176     case 'o':
    177     case 'u':
    178     case 'x':
    179     case 'X': return sizeof(unsigned);
    180     case 'f':
    181     case 'F':
    182     case 'e':
    183     case 'E':
    184     case 'g':
    185     case 'G':
    186     case 'a':
    187     case 'A':
    188         if (length == _T('L'))
    189             return sizeof(long double);
    190         else
    191             return sizeof(double);
    192        
    193     case 'c':
    194         // "%lc" is a wide character, passed as a wint_t (ushort)
    195         if (length == _T('l'))
    196             return sizeof(wint_t);
    197         // "%c" is an int, apparently
    198         else
    199             return sizeof(int);
    200 
    201     case 's':
    202         if (length == _T('l'))
    203             return sizeof(wchar_t*);
    204         else
    205             return sizeof(char*);
    206 
    207     case 'p':
    208         return sizeof(void*);
    209 
    210     case 'n':
    211         return sizeof(int*);
    212 
    213     }
    214     return 0;
    215 }
    216 
    217 int sys_vswprintf(TCHAR* buffer, size_t count, const TCHAR* format, va_list argptr)
    218 {
    219     // To help quickly detect incorrect 'count' values, fill the buffer with 0s
    220     memset(buffer, 0, count*sizeof(TCHAR));
    221 
    222     /*
    223    
    224     Format 'variable' specifications are (in pseudo-Perl regexp syntax):
    225 
    226     % (\d+$)? ['-+ #0]? (* | \d+)?  (. (* | \d*) )? (hh|h|l|ll|j|z|t|L)? [diouxXfFeEgGaAcspnCS%]
    227       position  flags     width       precision          length                   type
    228 
    229     */
    230 
    231     /**** Parse the format string into constant/variable chunks ****/
    232 
    233     std::vector<FormatChunk*> specs;
    234     std::tstring stringchunk;
    235 
    236     TCHAR chr;
    237 
    238 #define readchar(x) if ((x = *format++) == '\0') { delete s; goto finished_reading; }
    239 
    240     while ((chr = *format++) != '\0')
    241     {
    242         if (chr == _T('%'))
    243         {
    244             // Handle %% correctly
    245             if (*format == _T('%'))
    246             {
    247                 stringchunk += _T('%');
    248                 continue;
    249             }
    250 
    251             // End the current string and start a new spec chunk
    252             if (stringchunk.length())
    253             {
    254                 specs.push_back(new FormatString(stringchunk));
    255                 stringchunk = _T("");
    256             }
    257 
    258             FormatVariable *s = new FormatVariable;
    259             s->position = -1;
    260             s->flags = 0;
    261             s->width = 0;
    262             s->precision = 0;
    263             s->length = 0;
    264             s->type = 0;
    265 
    266             // Read either the position or the width
    267             int number = 0;
    268 
    269             while (1)
    270             {
    271                 readchar(chr);
    272 
    273                 // Read flags (but not if it's a 0 appearing after other digits)
    274                 if (!number && get_flag(chr))
    275                     s->flags = (char)(s->flags|get_flag(chr));
    276 
    277                 // Read decimal numbers (position or width)
    278                 else if (isdigit(chr))
    279                     number = number*10 + (chr-'0');
    280 
    281                 // If we've reached a $, 'number' was the position,
    282                 // so remember it and start getting the width
    283                 else if (chr == _T('$'))
    284                 {
    285                     s->position = number;
    286                     number = 0;
    287                 }
    288 
    289                 // End of the number section
    290                 else
    291                 {
    292                     // Remember the width
    293                     s->width = number;
    294 
    295                     // Start looking for a precision
    296                     if (chr == _T('.'))
    297                     {
    298                         // Found a precision: read the digits
    299 
    300                         number = 0;
    301 
    302                         while (1)
    303                         {
    304                             readchar(chr);
    305 
    306                             if (isdigit(chr))
    307                                 number = number*10 + (chr-'0');
    308                             else
    309                             {
    310                                 s->precision = number;
    311                                 break;
    312                             }
    313                         }
    314                     }
    315 
    316                     // Finished dealing with any precision.
    317 
    318                     // Now check for length and type codes.
    319 
    320                     if (chr == _T('I'))
    321                     {
    322                         DEBUG_WARN_ERR(ERR::LOGIC); // MSVC-style \"%I64\" is not allowed!
    323                     }
    324 
    325                     if (is_lengthmod(chr))
    326                     {
    327                         s->length = chr;
    328 
    329                         // Check for ll and hh
    330                         if (chr == _T('l') || chr == _T('h'))
    331                         {
    332                             if (*format == chr)
    333                             {
    334                                 s->length |= (chr << 8);
    335                                 ++format;
    336                             }
    337                         }
    338 
    339                         readchar(chr);
    340                     }
    341 
    342                     s->type = (char)chr;
    343 
    344                     specs.push_back(s);
    345 
    346                     break;
    347                 }
    348             }
    349         }
    350         else
    351         {
    352             stringchunk += chr;
    353         }
    354     }
    355 
    356 #undef readchar
    357 
    358 finished_reading:
    359 
    360     if (stringchunk.length())
    361     {
    362         specs.push_back(new FormatString(stringchunk));
    363         stringchunk = _T("");
    364     }
    365 
    366     /**** Build a new format string (to pass to the system printf) ****/
    367 
    368     std::tstring newformat;
    369 
    370     std::vector<int> varsizes; // stores the size of each variable type, to allow stack twiddling
    371 
    372     typedef std::vector<FormatChunk*>::iterator ChunkIt;
    373 
    374     for (ChunkIt it = specs.begin(); it != specs.end(); ++it)
    375     {
    376         if ((*it)->ChunkType() == 0)
    377         {
    378             FormatVariable* s = static_cast<FormatVariable*>(*it);
    379 
    380             if (s->position > 0)
    381             {
    382                 // Grow if necessary
    383                 if (s->position >= (int)varsizes.size())
    384                     varsizes.resize(s->position+1, -1);
    385                 // Store the size of the current type
    386                 varsizes[s->position] = type_size(s->type, s->length);
    387             }
    388 
    389 
    390             newformat += _T("%");
    391 
    392             if (s->flags)
    393                 newformat += flags_to_string(s->flags);
    394            
    395             if (s->width == -1)
    396                 newformat += '*';
    397             else if (s->width)
    398                 newformat += to_string(s->width);
    399 
    400             if (s->precision)
    401             {
    402                 newformat += '.';
    403                 if (s->precision == -1)
    404                     newformat += '*';
    405                 else
    406                     newformat += to_string(s->precision);
    407             }
    408 
    409             if (s->length)
    410             {
    411                 if (s->length > 256)
    412                 {
    413                     if (s->length == 0x00006c6c)
    414                         #if USE_I64_FORMAT
    415                          newformat += "I64";
    416                         #else
    417                          newformat += _T("ll");
    418                         #endif
    419                     else if (s->length == 0x00006868)
    420                         newformat += _T("hh");
    421                 }
    422                 else
    423                 {
    424                     newformat += (char) s->length;
    425                 }
    426             }
    427 
    428             newformat += s->type;
    429         }
    430         else
    431         {
    432             FormatString* s = static_cast<FormatString*>(*it);
    433             newformat += s->text;
    434         }
    435     }
    436 
    437 
    438 /*
    439     varargs on x86:
    440 
    441     All types are padded to 4 bytes, so size-in-stack == _INTSIZEOF(type).
    442     No special alignment is required.
    443 
    444     first+_INTSIZEOF(first) == first item in stack
    445 
    446     Keep adding _INTSIZEOF(item) to get the next item
    447 */
    448 
    449 // Because of those dangerous assumptions about varargs:
    450 #if !ARCH_IA32
    451 #error SLIGHTLY FATAL ERROR: Only x86 is supported!
    452 #endif
    453 
    454     // Highly efficient buffer to store the rearranged copy of the stack
    455     std::string newstack;
    456 
    457     std::vector< std::pair<char*, char*> > stackitems;
    458 
    459     va_list arglist = argptr;
    460     //va_start(arglist, format);
    461 
    462     const u8* newstackptr;
    463 
    464     if (varsizes.size())
    465     {
    466 
    467         for (size_t i = 1; i < varsizes.size(); ++i)
    468         {
    469             if (varsizes[i] <= 0)
    470             {
    471                 DEBUG_WARN_ERR(ERR::LOGIC); // Invalid variable type somewhere - make sure all variable things are positional and defined
    472                 return -1;
    473             }
    474 
    475             // Based on _INTSIZEOF in stdarg.h:
    476             // (Warning - slightly non-portable. But we use gcc's default printf
    477             // when portability matters.)
    478             #define INTSIZE(n)   ( (n + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    479 
    480             size_t size = INTSIZE(varsizes[i]);
    481             stackitems.push_back( std::pair<char*, char*>( arglist, arglist+size ));
    482             arglist += size;
    483         }
    484 
    485 
    486         for (ChunkIt it = specs.begin(); it != specs.end(); ++it)
    487         {
    488             FormatChunk* chunk = *it;
    489             if (chunk->ChunkType() == 0)
    490             {
    491                 FormatVariable* s = static_cast<FormatVariable*>(chunk);
    492                 if (s->position <= 0)
    493                 {
    494                     DEBUG_WARN_ERR(ERR::LOGIC); // Invalid use of positional elements - make sure all variable things are positional and defined
    495                     return -1;
    496                 }
    497                 newstack += std::string( stackitems[s->position-1].first, stackitems[s->position-1].second );
    498             }
    499         }
    500        
    501         newstackptr = (const u8*)newstack.c_str();
    502     }
    503     else
    504     {
    505         newstackptr = (const u8*)arglist;
    506     }
    507 
    508     for (ChunkIt it = specs.begin(); it != specs.end(); ++it)
    509         if ((*it)->ChunkType() == 0)
    510             delete static_cast<FormatVariable*>(*it);
    511         else
    512             delete static_cast<FormatString*>(*it);
    513 
    514     int ret = _vsntprintf(buffer, count, newformat.c_str(), (va_list)newstackptr);
    515 
    516     // For consistency with GCC's vsnprintf, make sure the buffer is null-terminated
    517     // and return an error if that truncates the output
    518     if(count > 0)
    519         buffer[count-1] = '\0';
    520     if (ret == (int)count)
    521         return -1;
    522 
    523     return ret;
    524 }
    525 
    526 #endif
  • source/lib/sysdep/sysdep.h

    diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h
    index bbb032d..2517309 100644
    a b  
    3030#include "lib/debug.h"  // ErrorReactionInternal
    3131#include "lib/os_path.h"
    3232
    33 #include <cstdarg>  // needed for sys_vswprintf
    34 
    3533
    3634//
    3735// output
    LIB_API bool sys_IsDebuggerPresent();  
    8179LIB_API std::wstring sys_WideFromArgv(const char* argv_i);
    8280
    8381/**
    84  * sys_vswprintf: doesn't quite follow the standard for vswprintf, but works
    85  * better across compilers:
    86  * - handles positional parameters and %lld
    87  * - always null-terminates the buffer, if count > 0
    88  * - returns -1 on overflow (if the output string (including null) does not fit in the buffer)
    89  **/
    90 extern int sys_vswprintf(wchar_t* buffer, size_t count, const wchar_t* format, va_list argptr);
    91 
    92 /**
    9382 * describe the current OS error state.
    9483 *
    9584 * @param err: if not 0, use that as the error code to translate; otherwise,
  • deleted file source/lib/sysdep/tests/test_printf.h

    diff --git a/source/lib/sysdep/tests/test_printf.h b/source/lib/sysdep/tests/test_printf.h
    deleted file mode 100644
    index 341a81f..0000000
    + -  
    1 /* Copyright (c) 2010 Wildfire Games
    2  *
    3  * Permission is hereby granted, free of charge, to any person obtaining
    4  * a copy of this software and associated documentation files (the
    5  * "Software"), to deal in the Software without restriction, including
    6  * without limitation the rights to use, copy, modify, merge, publish,
    7  * distribute, sublicense, and/or sell copies of the Software, and to
    8  * permit persons to whom the Software is furnished to do so, subject to
    9  * the following conditions:
    10  *
    11  * The above copyright notice and this permission notice shall be included
    12  * in all copies or substantial portions of the Software.
    13  *
    14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  */
    22 
    23 #include "lib/self_test.h"
    24 
    25 #include "lib/sysdep/sysdep.h"
    26 
    27 class TestPrintf : public CxxTest::TestSuite
    28 {
    29     // Split some bits into separate functions, so we can get
    30     // a legitimate va_list to pass to sys_vswprintf:
    31 
    32     void _test_truncate(int buffer_size, const wchar_t* expected_output, int expected_return, /* wchar_t* input_string */...)
    33     {
    34         wchar_t buf[17] = L"................"; // fill with dots so null-termination is made obvious
    35 
    36         va_list ap;
    37         va_start(ap, expected_return);
    38 
    39         int ret = sys_vswprintf(buf, buffer_size, L"%ls", ap);
    40 
    41         TS_ASSERT_WSTR_EQUALS(buf, expected_output);
    42         TS_ASSERT_EQUALS(ret, expected_return);
    43 
    44         std::wstring past_buffer(buf + buffer_size);
    45         TS_ASSERT(past_buffer.find_first_not_of('.') == past_buffer.npos);
    46 
    47         va_end(ap);
    48     }
    49 
    50     void _test_sprintf(const wchar_t* expected_output, const wchar_t* format, ...)
    51     {
    52         wchar_t buf[256];
    53 
    54         va_list ap;
    55         va_start(ap, format);
    56 
    57         sys_vswprintf(buf, ARRAY_SIZE(buf), format, ap);
    58         TS_ASSERT_WSTR_EQUALS(buf, expected_output);
    59 
    60         va_end(ap);
    61     }
    62 
    63 public:
    64     void test_truncate()
    65     {
    66         _test_truncate(0, L"................", -1, L"1234");
    67         _test_truncate(1, L"",                 -1, L"1234");
    68 
    69         _test_truncate(8, L"1234",     4, L"1234");
    70         _test_truncate(8, L"1234567",  7, L"1234567");
    71         _test_truncate(8, L"1234567", -1, L"12345678");
    72         _test_truncate(8, L"1234567", -1, L"123456789");
    73         _test_truncate(8, L"1234567", -1, L"123456789abcdef");
    74     }
    75 
    76     void test_lld()
    77     {
    78         i64 z = 0;
    79         i64 n = 65536;
    80         _test_sprintf(L"0", L"%lld", z);
    81         _test_sprintf(L"65536", L"%lld", n);
    82         _test_sprintf(L"4294967296", L"%lld", n*n);
    83         _test_sprintf(L"281474976710656", L"%lld", n*n*n);
    84         _test_sprintf(L"-281474976710656", L"%lld", -n*n*n);
    85         _test_sprintf(L"123 456 281474976710656 789", L"%d %d %lld %d", 123, 456, n*n*n, 789);
    86     }
    87 
    88     void test_pos()
    89     {
    90         _test_sprintf(L"a b", L"%1$c %2$c", 'a', 'b');
    91         _test_sprintf(L"b a", L"%2$c %1$c", 'a', 'b');
    92     }
    93 
    94     void test_pos_lld()
    95     {
    96         _test_sprintf(L"1 2 3", L"%1$d %2$lld %3$d", 1, (i64)2, 3);
    97         _test_sprintf(L"2 1 3", L"%2$lld %1$d %3$d", 1, (i64)2, 3);
    98     }
    99 };
  • source/lib/tests/test_secure_crt.h

    diff --git a/source/lib/tests/test_secure_crt.h b/source/lib/tests/test_secure_crt.h
    index b52480d..422bea1 100644
    a b class TestString_s : public CxxTest::TestSuite  
    8181    const char* const s1;
    8282    const char* const s5;
    8383    const char* const s10;
     84    const wchar_t* const ws10;
    8485
    8586    char d1[1];
    8687    char d2[2];
    class TestString_s : public CxxTest::TestSuite  
    8889    char d5[5];
    8990    char d6[6];
    9091    char d10[10];
     92    wchar_t wd10[10];
    9193    char d11[11];
    9294
    9395    char no_null[7];
    class TestString_s : public CxxTest::TestSuite  
    157159
    158160public:
    159161    TestString_s()
    160         : s0(""), s1("a"), s5("abcde"), s10("abcdefghij")
     162        : s0(""), s1("a"), s5("abcde"), s10("abcdefghij"), ws10(L"abcdefghij")
    161163    {
    162164        const char no_null_tmp[] = { 'n','o','_','n','u','l','l'};
    163165        memcpy(no_null, no_null_tmp, sizeof(no_null));
    public:  
    303305        if (dst) TS_ASSERT_STR_EQUALS(dst, expected_dst);
    304306    }
    305307
     308    static void TEST_WPRINTF(wchar_t* dst, size_t max_dst_chars, const wchar_t* dst_val,
     309        int expected_ret, const wchar_t* expected_dst, const wchar_t* fmt, ...)
     310    {
     311        if (dst) wcscpy(dst, dst_val);
     312        va_list ap;
     313        va_start(ap, fmt);
     314        int ret = vswprintf_s(dst, max_dst_chars, fmt, ap);
     315        va_end(ap);
     316        TS_ASSERT_EQUALS(ret, expected_ret);
     317        if (dst) TS_ASSERT_WSTR_EQUALS(dst, expected_dst);
     318    }
     319
    306320    void test_printf()
    307321    {
    308322        TEST_PRINTF(d10,10, s10, 4, "1234", "%d", 1234);
    public:  
    315329        TEST_PRINTF(NULL,0, NULL, -1, "", "%d", 1234);
    316330        TEST_PRINTF(d10,10, s10, -1, "abcdefghij", NULL);
    317331    }
     332
     333    void test_wprintf()
     334    {
     335        TEST_WPRINTF(wd10,10, ws10, 4, L"1234", L"%d", 1234);
     336        TEST_WPRINTF(wd10,5, ws10, 4, L"1234", L"%d", 1234);
     337
     338        SuppressErrors suppress;
     339        TEST_WPRINTF(wd10,4, ws10, -1, L"", L"%d", 1234);
     340        TEST_WPRINTF(wd10,3, ws10, -1, L"", L"%d", 1234);
     341        TEST_WPRINTF(wd10,0, ws10, -1, L"abcdefghij", L"%d", 1234);
     342        TEST_WPRINTF(NULL,0, NULL, -1, L"", L"%d", 1234);
     343        TEST_WPRINTF(wd10,10, ws10, -1, L"abcdefghij", NULL);
     344    }
    318345};
  • source/ps/CLogger.cpp

    diff --git a/source/ps/CLogger.cpp b/source/ps/CLogger.cpp
    index 16c8ff0..58cf7c2 100644
    a b  
    2525#include "lib/ogl.h"
    2626#include "lib/timer.h"
    2727#include "lib/utf8.h"
    28 #include "lib/sysdep/sysdep.h"
    2928#include "ps/Profile.h"
    3029#include "renderer/Renderer.h"
    3130