Index: generic/nsfError.c =================================================================== diff -u -rcf9bbe07158ccf6d77685f42eb0dce5117a225cf -r626a121f43a32c15b458dd3f4ec1387ecc20a712 --- generic/nsfError.c (.../nsfError.c) (revision cf9bbe07158ccf6d77685f42eb0dce5117a225cf) +++ generic/nsfError.c (.../nsfError.c) (revision 626a121f43a32c15b458dd3f4ec1387ecc20a712) @@ -68,35 +68,43 @@ void NsfDStringVPrintf(Tcl_DString *dsPtr, const char *fmt, va_list vargs) { - int result, failure, offset = dsPtr->length, avail = dsPtr->spaceAvl; + int result, failure, offset, avail; va_list vargsCopy; + /* Calculate the DString's anatomy */ + offset = Tcl_DStringLength(dsPtr); /* current length *without* null terminating character (NTC)*/ + +#if defined(_MSC_VER) + avail = dsPtr->spaceAvl - offset - 1; /* Pre-C99: currently free storage, excluding NTC */ +#else + avail = dsPtr->spaceAvl - offset; /* C99: currently free storage, including NTC */ +#endif + /* - * Work on a copy of the va_list so that the caller's copy is untouched + * 1) Copy va_list so that the caller's copy is untouched. + * 2) Run vsnprintf() eagerly. */ - avail -= offset; va_copy(vargsCopy, vargs); - result = vsnprintf(dsPtr->string + offset, avail, fmt, vargsCopy); + result = vsnprintf(dsPtr->string + offset, avail ,fmt, vargsCopy); va_end(vargsCopy); - /* - * Trap C99+ incompatabilities of certain vsnprintf() implementations - * w.r.t. the result value: For example, old *nix implementations of - * vsnprintf() as well as C89 implementations (as current MS Visual Compiler - * runtimes) return -1 (or another negative number) upon overflowing the - * buffer (rather than the number of required bytes as required by C99) and - * upon other error conditions. This should not happen for the above size - * estimation, however. Also, for MS VC runtimes, we use the vendor-specific - * _vscprintf() - * - * Note: For MinGW and MinGW-w64, we assume that their ANSI-compliant - * version of vsnprintf() is used. See __USE_MINGW_ANSI_STDIO in nsfInt.h - */ - #if defined(_MSC_VER) - failure = (result == -1 && errno == ERANGE) || (result == avail) /* VC 12 */; + /* + vs*printf() in pre-C99 runtimes (MSVC up to VS13, VS15 and newer in + backward-compat mode) return -1 upon overflowing the buffer. + + Note: Tcl via tclInt.h precludes the use of pre-C99 mode even in VS15 and + newer (vsnprintf points to backward-compat, pre-C99 _vsnprintf). + */ + failure = (result == -1); #else - assert(result > -1); + /* + vs*printf() in C99 compliant runtimes (GCC, CLANG, MSVC in VS15 and + newer, MinGW/MinGW-w64 with __USE_MINGW_ANSI_STDIO) returns the number of + chars to be written if the buffer would be sufficiently large (excluding + NTC). A return value of -1 signals an encoding error. + */ + assert(result > -1); /* no encoding error */ failure = (result >= avail); #endif @@ -125,9 +133,20 @@ Tcl_DStringSetLength(dsPtr, offset + addedStringLength); +#if defined(_MSC_VER) + avail = dsPtr->spaceAvl - offset - 1; /* Pre-C99: currently free storage, excluding NTC */ +#else + avail = dsPtr->spaceAvl - offset; /* C99: currently free storage, including NTC */ +#endif + va_copy(vargsCopy, vargs); - result = vsnprintf(dsPtr->string + offset, dsPtr->spaceAvl - offset, fmt, vargsCopy); + result = vsnprintf(dsPtr->string + offset, avail, fmt, vargsCopy); +#if defined(_MSC_VER) assert(result > -1); +#else + assert(result > -1); /* no encoding error */ + assert(result < avail); /* no overflow */ +#endif va_end(vargsCopy); } }