#ifdef UNICODE
#define TLS _T("l")
#else
#define TLS ""
#endif
WCHAR str[256];
_snwprintf(str,NELEM(str),L"%s %s %s",L"ABC",L"DEF",L"GHI"); // result is A D G
_snwprintf(str,NELEM(str),L"%ls %ls %ls",L"ABC",L"DEF",L"GHI"); // results correct
TCHAR tstr[256];
_sntprintf(tstr,NELEM(tstr),_T("%") TLS _T("s %") TLS _T("s %") TLS _T("s"),_T("ABC"),_T("DEF"),_T("GHI"));
// Must I do it this way because Pelles insists on following a bad standard?
Pelles-C help is ambigious but a randomly selected
C Standards Document is clear that the above behavior is according to standard. The trouble with the standard is that extraordinary measures are needed to support <tchar.h> ready source code under Windows. %s functions correctly which is against the standard in every other compiler I have:
MSVC, MinGW, OpenWatcom (see help for wprintf), Borland-C (code tested), and DMC (code tested). Every one broke the standard and Pelles-C should too to be a proper Windows development environment.
It's easy to see why the standard is wrong, not just for Windows but for all systems. Why should %s support any other size than the native character size? On linux should the format string be 32 bit UNICODE and each %s be 8 bit chars? Are those 8-bit Ansi or 8-bit UTF-8? Should %d always be 16 bit just because MS-DOS was a popular programming environment? It seems simple to me: when the native character size changes %s changes with it and let the modifers alter the size for special cases. I've got 5 compilers which encompass 4 separate runtime libraries on my side. Can I have 6 & 5 or must I make my format strings even more unreadable by sprinkling TLS all over or link to a _snwprintf that works correctly?
The second standards issue is that the standard guarantee's that a NUL byte will terminate the ouput but no other Windows compiler does this and Pelles-C in the wild gyrations to try and provide this ends up with a bounds error. Here's a program to test it.
#include <wchar.h>
#if defined(__DMC__) || defined(__MINGW32__) || defined(_MSC_VER) || !defined(__POCC__) || defined(__WATCOMC__)
#define _wmemset wmemset
#define _wmemchr wmemchr
#endif
EXTERNC int my_snprintf(CHAR *szDest,size_t cchDest,const CHAR *szFormat,...) {
va_list ap;
va_start(ap, szFormat);
int Result=_vsnprintf(szDest,cchDest,szFormat,ap);
va_end(ap);
return Result;
}
EXTERNC int my_snwprintf(WCHAR *szDest,size_t cchDest,const WCHAR *szFormat,...) {
va_list ap;
va_start(ap, szFormat);
int Result=_vsnwprintf(szDest,cchDest,szFormat,ap);
va_end(ap);
return Result;
}
EXTERNC void __stdcall sntest(void) {
FILE *fo=fopen("C:\\sntest.txt","wt");
if (fo) {
size_t i;
for(i=2; i<=6; i++) {
size_t j;
{WCHAR szStrW[8];
_wmemset(szStrW,L'#',NELEM(szStrW)); szStrW[NELEM(szStrW)-1]=L'\0';
int cchResultW=_snwprintf(szStrW,i,L"ABCD");
int cchActualW=(WCHAR *)_wmemchr(szStrW,L'#',NELEM(szStrW))-szStrW;
int cchZeroW=(WCHAR *)_wmemchr(szStrW,0,NELEM(szStrW))-szStrW; if (cchZeroW==NELEM(szStrW)-1) cchZeroW=-1;
fprintf(fo,"cchResult:%2d= _snwprintf(szStrW,cch=%d,\"ABCD\"); // ZeroIdx=%2d cchActual=%d ",cchResultW,i,cchZeroW,cchActualW);
for(j=0; j<NELEM(szStrW)-1; j++) fprintf(fo,szStrW[j]<32?"0":"%c",szStrW[j]);
if (cchActualW>i) fputs(" Bounds Error",fo);
fputs("\n",fo);
_wmemset(szStrW,L'#',NELEM(szStrW)); szStrW[NELEM(szStrW)-1]=L'\0';
cchResultW=my_snwprintf(szStrW,i,L"ABCD");
cchActualW=(WCHAR *)_wmemchr(szStrW,L'#',NELEM(szStrW))-szStrW;
cchZeroW=(WCHAR *)_wmemchr(szStrW,0,NELEM(szStrW))-szStrW; if (cchZeroW==NELEM(szStrW)-1) cchZeroW=-1;
fprintf(fo,"cchResult:%2d=_vsnwprintf(szStrW,cch=%d,\"ABCD\"); // ZeroIdx=%2d cchActual=%d ",cchResultW,i,cchZeroW,cchActualW);
for(j=0; j<NELEM(szStrW)-1; j++) fprintf(fo,szStrW[j]<32?"0":"%c",szStrW[j]);
if (cchActualW>i) fputs(" Bounds Error",fo);
fputs("\n",fo);}
{CHAR szStrA[8];
memset(szStrA,'#',NELEM(szStrA)); szStrA[NELEM(szStrA)-1]='\0';
int cchResultA= _snprintf(szStrA,i,"ABCD");
int cchActualA=( CHAR *)memchr(szStrA,'#',NELEM(szStrA))-szStrA;
int cchZeroA=( CHAR *)memchr(szStrA,0,NELEM(szStrA))-szStrA; if (cchZeroA==NELEM(szStrA)-1) cchZeroA=-1;
fprintf(fo,"cchResult:%2d= _snprintf(szStrA,cch=%d,\"ABCD\"); // ZeroIdx=%2d cchActual=%d ",cchResultA,i,cchZeroA,cchActualA);
for(j=0; j<NELEM(szStrA)-1; j++) fprintf(fo,szStrA[j]<32?"0":"%c",szStrA[j]);
if (cchActualA>i) fputs(" Bounds Error",fo);
fputs("\n",fo);
memset(szStrA,'#',NELEM(szStrA)); szStrA[NELEM(szStrA)-1]='\0';
cchResultA=my_snprintf(szStrA,i,"ABCD");
cchActualA=( CHAR *)memchr(szStrA,'#',NELEM(szStrA))-szStrA;
cchZeroA=( CHAR *)memchr(szStrA,0,NELEM(szStrA))-szStrA; if (cchZeroA==NELEM(szStrA)-1) cchZeroA=-1;
fprintf(fo,"cchResult:%2d= _vsnprintf(szStrA,cch=%d,\"ABCD\"); // ZeroIdx=%2d cchActual=%d ",cchResultA,i,cchZeroA,cchActualA);
for(j=0; j<NELEM(szStrA)-1; j++) fprintf(fo,szStrA[j]<32?"0":"%c",szStrA[j]);
if (cchActualA>i) fputs(" Bounds Error",fo);
fputs("\n",fo);}
fputs("\n",fo);
}
fclose(fo);
}
}
I don't think I need to describe the multitude of broken things in Pelles-C output:
cchResult:-1= _snwprintf(szStrW,cch=2,"ABCD"); // ZeroIdx=-1 cchActual=2 AB#####
cchResult: 4=_vsnwprintf(szStrW,cch=2,"ABCD"); // ZeroIdx= 1 cchActual=2 A0#####
cchResult: 4= _snprintf(szStrA,cch=2,"ABCD"); // ZeroIdx= 1 cchActual=2 A0#####
cchResult: 4= _vsnprintf(szStrA,cch=2,"ABCD"); // ZeroIdx= 1 cchActual=2 A0#####
cchResult:-1= _snwprintf(szStrW,cch=3,"ABCD"); // ZeroIdx=-1 cchActual=3 ABC####
cchResult: 4=_vsnwprintf(szStrW,cch=3,"ABCD"); // ZeroIdx= 2 cchActual=3 AB0####
cchResult: 4= _snprintf(szStrA,cch=3,"ABCD"); // ZeroIdx= 2 cchActual=3 AB0####
cchResult: 4= _vsnprintf(szStrA,cch=3,"ABCD"); // ZeroIdx= 2 cchActual=3 AB0####
cchResult: 4= _snwprintf(szStrW,cch=4,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0## Bounds Error
cchResult: 4=_vsnwprintf(szStrW,cch=4,"ABCD"); // ZeroIdx= 3 cchActual=4 ABC0###
cchResult: 4= _snprintf(szStrA,cch=4,"ABCD"); // ZeroIdx= 3 cchActual=4 ABC0###
cchResult: 4= _vsnprintf(szStrA,cch=4,"ABCD"); // ZeroIdx= 3 cchActual=4 ABC0###
cchResult: 4= _snwprintf(szStrW,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4=_vsnwprintf(szStrW,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _snprintf(szStrA,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _vsnprintf(szStrA,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _snwprintf(szStrW,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4=_vsnwprintf(szStrW,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _snprintf(szStrA,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _vsnprintf(szStrA,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
Correct output from Borland, MinGW, MSVC, DMC, and WC:
cchResult:-1= _snwprintf(szStrW,cch=2,"ABCD"); // ZeroIdx=-1 cchActual=2 AB#####
cchResult:-1=_vsnwprintf(szStrW,cch=2,"ABCD"); // ZeroIdx=-1 cchActual=2 AB#####
cchResult:-1= _snprintf(szStrA,cch=2,"ABCD"); // ZeroIdx=-1 cchActual=2 AB#####
cchResult:-1= _vsnprintf(szStrA,cch=2,"ABCD"); // ZeroIdx=-1 cchActual=2 AB#####
cchResult:-1= _snwprintf(szStrW,cch=3,"ABCD"); // ZeroIdx=-1 cchActual=3 ABC####
cchResult:-1=_vsnwprintf(szStrW,cch=3,"ABCD"); // ZeroIdx=-1 cchActual=3 ABC####
cchResult:-1= _snprintf(szStrA,cch=3,"ABCD"); // ZeroIdx=-1 cchActual=3 ABC####
cchResult:-1= _vsnprintf(szStrA,cch=3,"ABCD"); // ZeroIdx=-1 cchActual=3 ABC####
cchResult: 4= _snwprintf(szStrW,cch=4,"ABCD"); // ZeroIdx=-1 cchActual=4 ABCD###
cchResult: 4=_vsnwprintf(szStrW,cch=4,"ABCD"); // ZeroIdx=-1 cchActual=4 ABCD###
cchResult: 4= _snprintf(szStrA,cch=4,"ABCD"); // ZeroIdx=-1 cchActual=4 ABCD###
cchResult: 4= _vsnprintf(szStrA,cch=4,"ABCD"); // ZeroIdx=-1 cchActual=4 ABCD###
cchResult: 4= _snwprintf(szStrW,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4=_vsnwprintf(szStrW,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _snprintf(szStrA,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _vsnprintf(szStrA,cch=5,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _snwprintf(szStrW,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4=_vsnwprintf(szStrW,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _snprintf(szStrA,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##
cchResult: 4= _vsnprintf(szStrA,cch=6,"ABCD"); // ZeroIdx= 4 cchActual=5 ABCD0##