This msvcrt-A?? series patch implements the full vfprintf (ASCII) and vfwprintf (Unicode) functions. License: LGPL Changelog: * dlls/msvcrt/vfprintf.c: Jaco Greeff <jaco@puxedo.org> - Implementation of the vfprintf/vfwprintf functions to allow for the Microsoft format specifier extentions, %C and %S - Full implementation of vfwprintf to actually output Unicode strings, instead of the ASCII strings printed before --[ inline patch ]-- diff -aurN msvcrt-A01/dlls/msvcrt/vfprintf.c msvcrt-A02/dlls/msvcrt/vfprintf.c --- msvcrt-A01/dlls/msvcrt/vfprintf.c 2002-11-03 11:23:45.000000000 +0000 +++ msvcrt-A02/dlls/msvcrt/vfprintf.c 2002-11-03 11:43:18.000000000 +0000 @@ -38,60 +38,946 @@ WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); +#define VFPRINTF_LEFTALIGN 0x0001 /* Align output on the left ('-' prefix) */ +#define VFPRINTF_PREFIX_HEX 0x0002 /* Prefix hex with 0x ('#' prefix) */ +#define VFPRINTF_ZEROPAD 0x0004 /* Pad with zeros ('0' prefix) */ +#define VFPRINTF_LONG 0x0008 /* Long arg ('l' prefix) */ +#define VFPRINTF_SHORT 0x0010 /* Short arg ('h' prefix) */ +#define VFPRINTF_UPPER 0x0020 /* Upper-case hex ('X' specifier) */ +#define VFPRINTF_WIDE 0x0040 /* Wide arg ('w' prefix) */ + +typedef struct +{ + MSVCRT_FILE *fIOFile; + INT nBufLen; + INT nTotLen; + INT nThisLen; +} VFPRINTF_FILE; + +typedef enum +{ + VFMT_UNKNOWN, + VFMT_CHAR, + VFMT_WCHAR, + VFMT_STRING, + VFMT_WSTRING, + VFMT_INTEGER, + VFMT_UNSIGNED, + VFMT_DOUBLE, + VFMT_INT_LENGTH, + VFMT_POINTER +} VFPRINTF_FORMAT_TYPE; + +typedef struct +{ + CHAR szFmt[128]; + UINT nFlags; + UINT nWidth; + UINT nPrecision; + VFPRINTF_FORMAT_TYPE nType; +} VFPRINTF_FORMAT, *VFPRINTF_PFORMAT; + + /********************************************************************* - * vfprintf (MSVCRT.@) + * VFPRINTF_printCharA + * (internal, non-exported) + * + * Prints a single ASCII char to the required file/buffer + */ +static inline INT VFPRINTF_printCharA(VFPRINTF_FILE *fOut, CHAR ch) +{ + if (fOut->fIOFile) + { + if ((fOut->nBufLen > 1) || (fOut->nBufLen == -1)) + MSVCRT(fwrite)(&ch, sizeof(CHAR), 1, fOut->fIOFile); + if (fOut->nBufLen != -1) + fOut->nBufLen--; + fOut->nTotLen++; + } + fOut->nThisLen++; + + return 1; +} + + +/********************************************************************* + * VFPRINTF_printCharW + * (internal, non-exported) + * + * Prints a single Unicode char to the required file/buffer + */ +static inline INT VFPRINTF_printCharW(VFPRINTF_FILE *fOut, WCHAR wch) +{ + if (fOut->fIOFile) + { + if ((fOut->nBufLen > 1) || (fOut->nBufLen == -1)) + MSVCRT(fwrite)(&wch, sizeof(WCHAR), 1, fOut->fIOFile); + if (fOut->nBufLen != -1) + fOut->nBufLen--; + fOut->nTotLen++; + } + fOut->nThisLen++; + + return 1; +} + + +/********************************************************************* + * VFPRINTF_LPCWSTRToLPSTR + * (internal, non-exported) + * + * Wrapper to allocate enough memory and convert a LPCWSTR to a normal + * LPSTR + */ +static inline CHAR *VFPRINTF_LPCWSTRToLPSTR(LPCWSTR lpwszIn, INT nIn) +{ + INT nLen; + CHAR *szOut; + + nLen = WideCharToMultiByte(CP_ACP, 0, lpwszIn, nIn, NULL, 0, NULL, NULL); + if ((szOut = (CHAR *)MSVCRT_malloc((nLen+1)*sizeof(CHAR)))) + { + WideCharToMultiByte(CP_ACP, 0, lpwszIn, nIn, szOut, nLen+1, NULL, NULL); + szOut[nLen] = '\0'; + } + + return szOut; +} + + +/********************************************************************* + * VFPRINTF_LPCSTRToLPWSTR + * (internal, non-exported) + * + * Wrapper to allocate enough memory and convert a LPCSTR to a normal + * LPWSTR + */ +static inline WCHAR *VFPRINTF_LPCSTRToLPWSTR(LPCSTR lpszIn, INT nIn) +{ + INT nLen; + WCHAR *szOut; + + nLen = MultiByteToWideChar(CP_ACP, 0, lpszIn, nIn, NULL, 0); + if ((szOut = (WCHAR *)MSVCRT_malloc((nLen+1)*sizeof(WCHAR)))) + { + MultiByteToWideChar(CP_ACP, 0, lpszIn, nIn, szOut, nLen+1); + szOut[nLen] = 0; + } + + return szOut; +} + + +/********************************************************************* + * VFPRINTF_printStringA + * (internal, non-exported) + * + * Prints a ASCII string to the required file/buffer + */ +static inline INT VFPRINTF_printStringA(VFPRINTF_FILE *fOut, LPCSTR szStr) +{ + INT nLen = 0; + + while (szStr && *szStr) + { + VFPRINTF_printCharA(fOut, *szStr); + nLen++; + szStr++; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_printStringW + * (internal, non-exported) + * + * Prints a Unicode string to the required file/buffer + */ +static inline INT VFPRINTF_printStringW(VFPRINTF_FILE *fOut, LPCWSTR szStr) +{ + INT nLen = 0; + + while (szStr && *szStr) + { + VFPRINTF_printCharW(fOut, *szStr); + nLen++; + szStr++; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_nprintStringW + * (internal, non-exported) + * + * Prints a Unicode string (num characters) to the required file/buffer + */ +static INT VFPRINTF_nprintStringW(VFPRINTF_FILE *fOut, LPCWSTR szStr, INT nWidth) +{ + INT nLen = 0; + + while (nWidth && szStr && *szStr) + { + VFPRINTF_printCharW(fOut, *szStr); + nLen++; + szStr++; + nWidth--; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_printFormatArgA + * (internal, non-exported) + * + * Prints a single argument using the ASCII format string to + * the required file/buffer + */ +static INT VFPRINTF_vprintFormatArgA(VFPRINTF_FILE *fOut, VFPRINTF_PFORMAT pFormat, ...) +{ + INT nLen = 2048; + CHAR szScratch[2048+1]; + CHAR *szBuffer = szScratch; + INT nWritten = 0; + va_list vaList; + + va_start(vaList, pFormat); + while (((nWritten = vsnprintf(szBuffer, nLen, pFormat->szFmt, vaList)) == -1) || + (nWritten >= nLen)) + { + if (szBuffer != szScratch) + MSVCRT_free(szBuffer); + nLen = (nWritten == -1) ? nLen*2 : nWritten+1; + szBuffer = (CHAR *)MSVCRT_malloc(nLen*sizeof(*szBuffer)); + } + nWritten = VFPRINTF_printStringA(fOut, szBuffer); + if (szBuffer != szScratch) + MSVCRT_free(szBuffer); + va_end(vaList); + + return nWritten; +} + + +/********************************************************************* + * VFPRINTF_printFormatArgW + * (internal, non-exported) + * + * Prints a single argument using the Unicode format string to + * the required file/buffer + */ +static INT VFPRINTF_vprintFormatArgW(VFPRINTF_FILE *fOut, VFPRINTF_PFORMAT pFormat, ...) +{ + INT nLen = 2048; + CHAR szScratch[2048+1]; + CHAR *szBuffer = szScratch; + WCHAR *wszBuffer; + INT nWritten = 0; + va_list vaList; + + va_start(vaList, pFormat); + while (((nWritten = vsnprintf(szBuffer, nLen, pFormat->szFmt, vaList)) == -1) || + (nWritten >= nLen)) + { + if (szBuffer != szScratch) + MSVCRT_free(szBuffer); + nLen = (nWritten == -1) ? nLen*2 : nWritten+1; + szBuffer = (CHAR *)MSVCRT_malloc(nLen*sizeof(*szBuffer)); + } + if ((wszBuffer = VFPRINTF_LPCSTRToLPWSTR(szBuffer, nWritten))) + { + nWritten = VFPRINTF_printStringW(fOut, wszBuffer); + MSVCRT_free(wszBuffer); + } + else + nWritten = 0; + if (szBuffer != szScratch) + MSVCRT_free(szBuffer); + va_end(vaList); + + return nWritten; +} + + +/********************************************************************* + * VFPRINTF_printFormatA + * (internal, non-exported) + * + * Prints a full ASCII format string to the required file/buffer + */ +static va_list VFPRINTF_printFormatA(VFPRINTF_FILE *fOut, VFPRINTF_PFORMAT pFormat, va_list vaList) +{ + switch (pFormat->nType) + { + case VFMT_POINTER: + VFPRINTF_vprintFormatArgA(fOut, pFormat, (VOID *)va_arg(vaList, VOID *)); + break; + case VFMT_INT_LENGTH: + { + INT *p = va_arg(vaList, INT *); + *p = fOut->nThisLen; + break; + } + case VFMT_INTEGER: + VFPRINTF_vprintFormatArgA(fOut, pFormat, va_arg(vaList, INT)); + break; + case VFMT_DOUBLE: + VFPRINTF_vprintFormatArgA(fOut, pFormat, va_arg(vaList, double)); + break; + case VFMT_CHAR: + VFPRINTF_vprintFormatArgA(fOut, pFormat, (CHAR)va_arg(vaList, INT)); + break; + case VFMT_STRING: + VFPRINTF_vprintFormatArgA(fOut, pFormat, (CHAR *)va_arg(vaList, CHAR *)); + break; + case VFMT_WCHAR: + { + WCHAR wch = (WCHAR)va_arg(vaList, INT); + CHAR *szArg = VFPRINTF_LPCWSTRToLPSTR(&wch, 1); + if (szArg) + { + VFPRINTF_vprintFormatArgA(fOut, pFormat, szArg); + MSVCRT_free(szArg); + } + break; + } + case VFMT_WSTRING: + { + CHAR *szArg = VFPRINTF_LPCWSTRToLPSTR((WCHAR *)va_arg(vaList, WCHAR *), -1); + if (szArg) + { + VFPRINTF_vprintFormatArgA(fOut, pFormat, szArg); + MSVCRT_free(szArg); + } + break; + } + default: + break; + } + + return vaList; +} + + +/********************************************************************* + * VFPRINTF_printNoLeftAlignW + * (internal, non-exported) + * + * Prints characters to a Unicode string that is not left-aligned + */ +static inline void VFPRINTF_printNoLeftAlignW(VFPRINTF_FILE *fOut, VFPRINTF_PFORMAT pFormat) +{ + int i; + + if (!(pFormat->nFlags & VFPRINTF_LEFTALIGN)) + for (i = pFormat->nPrecision; i < pFormat->nWidth; i++) + VFPRINTF_printCharW(fOut, ' '); +} + + +/********************************************************************* + * VFPRINTF_printLeftAlignW + * (internal, non-exported) + * + * Prints characters to left-align a Unicode string + */ +static inline void VFPRINTF_printLeftAlignW(VFPRINTF_FILE *fOut, VFPRINTF_PFORMAT pFormat) +{ + int i; + + if (pFormat->nFlags & VFPRINTF_LEFTALIGN) + for (i = pFormat->nPrecision; i < pFormat->nWidth; i++) + VFPRINTF_printCharW(fOut, ' '); +} + + +/********************************************************************* + * VFPRINTF_printFormatW + * (internal, non-exported) + * + * Prints a full Unicode format string to the required file/buffer + */ +static va_list VFPRINTF_printFormatW(VFPRINTF_FILE *fOut, VFPRINTF_PFORMAT pFormat, va_list vaList) +{ + switch (pFormat->nType) + { + case VFMT_POINTER: + VFPRINTF_vprintFormatArgW(fOut, pFormat, (VOID *)va_arg(vaList, VOID *)); + break; + case VFMT_INT_LENGTH: + { + INT *p = va_arg(vaList, INT *); + *p = fOut->nThisLen; + break; + } + case VFMT_INTEGER: + VFPRINTF_vprintFormatArgW(fOut, pFormat, va_arg(vaList, INT)); + break; + case VFMT_DOUBLE: + VFPRINTF_vprintFormatArgW(fOut, pFormat, va_arg(vaList, double)); + break; + case VFMT_CHAR: + VFPRINTF_vprintFormatArgW(fOut, pFormat, (CHAR)va_arg(vaList, INT)); + break; + case VFMT_STRING: + VFPRINTF_vprintFormatArgW(fOut, pFormat, (CHAR *)va_arg(vaList, CHAR *)); + break; + case VFMT_WCHAR: + pFormat->nPrecision = 1; + VFPRINTF_printNoLeftAlignW(fOut, pFormat); + VFPRINTF_printCharW(fOut, (WCHAR)va_arg(vaList, INT)); + VFPRINTF_printLeftAlignW(fOut, pFormat); + break; + case VFMT_WSTRING: + { + INT i; + INT nWidth = 0; + WCHAR *wszStr = (WCHAR *)va_arg(vaList, WCHAR *); + + for (i = 0; !pFormat->nPrecision || (i < pFormat->nPrecision); i++) + if (!*(wszStr + i)) + break; + nWidth = (pFormat->nPrecision = i); + VFPRINTF_printNoLeftAlignW(fOut, pFormat); + VFPRINTF_nprintStringW(fOut, wszStr, nWidth); + VFPRINTF_printLeftAlignW(fOut, pFormat); + break; + } + default: + break; + } + + return vaList; +} + + +/********************************************************************* + * VFPRINTF_parseFormatPrefixA + * (internal, non-exported) * - * FIXME: We don't handle the Microsoft format flag extentions - * %C and %S at all + * Parses part of a ASCII format string + */ +static inline INT VFPRINTF_parseFormatPrefixA(LPCSTR szFmt, VFPRINTF_PFORMAT pFormat) +{ + INT nLen = 0; + + if (*szFmt == '-') + { + strcat(pFormat->szFmt, "-"); + pFormat->nFlags |= VFPRINTF_LEFTALIGN; + szFmt++; + nLen++; + } + if (*szFmt == '#') + { + strcat(pFormat->szFmt, "#"); + pFormat->nFlags |= VFPRINTF_PREFIX_HEX; + szFmt++; + nLen++; + } + if (*szFmt == '0') + { + strcat(pFormat->szFmt, "0"); + pFormat->nFlags |= VFPRINTF_ZEROPAD; + szFmt++; + nLen++; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_parseFormatPrefixW + * (internal, non-exported) + * + * Parses part of a Unicode format string + */ +static inline INT VFPRINTF_parseFormatPrefixW(LPCWSTR szFmt, VFPRINTF_PFORMAT pFormat) +{ + INT nLen = 0; + + if (*szFmt == '-') + { + strcat(pFormat->szFmt, "-"); + pFormat->nFlags |= VFPRINTF_LEFTALIGN; + szFmt++; + nLen++; + } + if (*szFmt == '#') + { + strcat(pFormat->szFmt, "#"); + pFormat->nFlags |= VFPRINTF_PREFIX_HEX; + szFmt++; + nLen++; + } + if (*szFmt == '0') + { + strcat(pFormat->szFmt, "0"); + pFormat->nFlags |= VFPRINTF_ZEROPAD; + szFmt++; + nLen++; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_parseFormatWidthA + * (internal, non-exported) + * + * Parses part of a format string for width specifiers + */ +static inline INT VFPRINTF_parseFormatWidthA(LPCSTR szFmt, UINT *pNum, VFPRINTF_PFORMAT pFormat) +{ + INT nLen = 0; + + while ((*szFmt >= '0') && (*szFmt <= '9')) + { + CHAR cDigit = (CHAR)(*szFmt); + strncat(pFormat->szFmt, &cDigit, 1); + *pNum = (*pNum)*10 + cDigit - '0'; + szFmt++; + nLen++; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_parseFormatWidthA + * (internal, non-exported) + * + * Parses part of a format string for width specifiers + */ +static inline INT VFPRINTF_parseFormatWidthW(LPCWSTR szFmt, UINT *pNum, VFPRINTF_PFORMAT pFormat) +{ + INT nLen = 0; + + while ((*szFmt >= '0') && (*szFmt <= '9')) + { + CHAR cDigit = (CHAR)(*szFmt); + strncat(pFormat->szFmt, &cDigit, 1); + *pNum = (*pNum)*10 + cDigit - '0'; + szFmt++; + nLen++; + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_parseFormatPrecisionA + * (internal, non-exported) + * + * Parses part of a format string for precision specifiers + */ +static inline INT VFPRINTF_parseFormatPrecisionA(LPCSTR szFmt, UINT *pNum, VFPRINTF_PFORMAT pFormat) +{ + INT nLen = 0; + + if (*szFmt == '.') + { + strcat(pFormat->szFmt, "."); + nLen++; + szFmt++; + nLen += VFPRINTF_parseFormatWidthA(szFmt, pNum, pFormat); + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_parseFormatPrecisionW + * (internal, non-exported) + * + * Parses part of a format string for precision specifiers + */ +static inline INT VFPRINTF_parseFormatPrecisionW(LPCWSTR szFmt, UINT *pNum, VFPRINTF_PFORMAT pFormat) +{ + INT nLen = 0; + + if (*szFmt == '.') + { + strcat(pFormat->szFmt, "."); + nLen++; + szFmt++; + nLen += VFPRINTF_parseFormatWidthW(szFmt, pNum, pFormat); + } + + return nLen; +} + + +/********************************************************************* + * VFPRINTF_parseFormatFlagsA + * (internal, non-exported) + * + * Parses part of a format string for flags + */ +static inline INT VFPRINTF_parseFormatFlagsA(LPCSTR szFmt, VFPRINTF_PFORMAT pFormat) +{ + switch (*szFmt) + { + case 'l': + pFormat->nFlags |= VFPRINTF_LONG; + return 1; + case 'h': + pFormat->nFlags |= VFPRINTF_SHORT; + return 1; + case 'w': + pFormat->nFlags |= VFPRINTF_WIDE; + return 1; + default: + return 0; + } +} + + +/********************************************************************* + * VFPRINTF_parseFormatFlagsW + * (internal, non-exported) + * + * Parses part of a format string for flags + */ +static inline INT VFPRINTF_parseFormatFlagsW(LPCWSTR szFmt, VFPRINTF_PFORMAT pFormat) +{ + switch (*szFmt) + { + case 'l': + pFormat->nFlags |= VFPRINTF_LONG; + return 1; + case 'h': + pFormat->nFlags |= VFPRINTF_SHORT; + return 1; + case 'w': + pFormat->nFlags |= VFPRINTF_WIDE; + return 1; + default: + return 0; + } +} + + +/********************************************************************* + * VFPRINTF_parseFormatTypeA + * (internal, non-exported) + * + * Parses part of an ASCII format string for type specifiers + */ +inline INT VFPRINTF_parseFormatTypeA(LPCSTR szFmt, VFPRINTF_PFORMAT pFormat) +{ + switch (*szFmt) + { + case 'c': + pFormat->nType = (pFormat->nFlags & VFPRINTF_LONG) ? VFMT_WCHAR : VFMT_CHAR; + strcat(pFormat->szFmt, (pFormat->nType == VFMT_WCHAR) ? "s" : "c"); + break; + case 'C': + pFormat->nType = (pFormat->nFlags & VFPRINTF_SHORT) ? VFMT_CHAR : VFMT_WCHAR; + strcat(pFormat->szFmt, (pFormat->nType == VFMT_WCHAR) ? "s" : "c"); + break; + case 's': + pFormat->nType = (pFormat->nFlags & VFPRINTF_LONG) ? VFMT_WSTRING : VFMT_STRING; + strcat(pFormat->szFmt, "s"); + break; + case 'S': + pFormat->nType = (pFormat->nFlags & VFPRINTF_SHORT) ? VFMT_STRING : VFMT_WSTRING; + strcat(pFormat->szFmt, "s"); + break; + case 'd': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "d"); + break; + case 'i': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "i"); + break; + case 'o': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "o"); + break; + case 'u': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "u"); + break; + case 'X': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "X"); + break; + case 'x': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "x"); + break; + case 'E': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "E"); + break; + case 'e': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "e"); + break; + case 'f': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "f"); + break; + case 'G': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "G"); + break; + case 'g': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "g"); + break; + case 'n': + pFormat->nType = VFMT_INT_LENGTH; + strcat(pFormat->szFmt, "n"); + break; + case 'p': + pFormat->nType = VFMT_POINTER; + strcat(pFormat->szFmt, "p"); + break; + case 'I': + if ((szFmt[1] == '6') && (szFmt[2] == '4')) + { + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "i"); + FIXME("I64 format type prefix is not fully supported\n"); + break; + } + default: + pFormat->nType = VFMT_UNKNOWN; + TRACE("Unhandled format specifier %%[...]%c\n", *szFmt); + break; + } + + return 1; +} + + +/********************************************************************* + * VFPRINTF_parseFormatTypeW + * (internal, non-exported) + * + * Parses part of a Unicode format string for type specifiers + */ +inline INT VFPRINTF_parseFormatTypeW(LPCWSTR szFmt, VFPRINTF_PFORMAT pFormat) +{ + switch (*szFmt) + { + case 'c': + pFormat->nType = (pFormat->nFlags & VFPRINTF_SHORT) ? VFMT_CHAR : VFMT_WCHAR; + strcat(pFormat->szFmt, (pFormat->nType == VFMT_WCHAR) ? "s" : "c"); + break; + case 'C': + pFormat->nType = (pFormat->nFlags & VFPRINTF_LONG) ? VFMT_WCHAR : VFMT_CHAR; + strcat(pFormat->szFmt, (pFormat->nType == VFMT_WCHAR) ? "s" : "c"); + break; + case 's': + pFormat->nType = (pFormat->nFlags & VFPRINTF_SHORT) ? VFMT_STRING : VFMT_WSTRING; + strcat(pFormat->szFmt, "s"); + break; + case 'S': + pFormat->nType = (pFormat->nFlags & VFPRINTF_LONG) ? VFMT_WSTRING : VFMT_STRING; + strcat(pFormat->szFmt, "s"); + break; + case 'd': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "d"); + break; + case 'i': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "i"); + break; + case 'o': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "o"); + break; + case 'u': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "u"); + break; + case 'X': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "X"); + break; + case 'x': + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "x"); + break; + case 'E': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "E"); + break; + case 'e': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "e"); + break; + case 'f': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "f"); + break; + case 'G': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "G"); + break; + case 'g': + pFormat->nType = VFMT_DOUBLE; + strcat(pFormat->szFmt, "g"); + break; + case 'n': + pFormat->nType = VFMT_INT_LENGTH; + strcat(pFormat->szFmt, "n"); + break; + case 'p': + pFormat->nType = VFMT_POINTER; + strcat(pFormat->szFmt, "p"); + break; + case 'I': + if ((szFmt[1] == '6') && (szFmt[2] == '4')) + { + pFormat->nType = VFMT_INTEGER; + strcat(pFormat->szFmt, "i"); + FIXME("I64 format type prefix is not fully supported\n"); + break; + } + default: + pFormat->nType = VFMT_UNKNOWN; + TRACE("Unhandled format specifier %%[...]%c\n", *szFmt); + break; + } + + return 1; +} + + +/********************************************************************* + * VFPRINTF_parseFormatA + * (internal, non-exported) + * + * Parses a ASCII format string + */ +static INT VFPRINTF_parseFormatA(LPCSTR szSpec, VFPRINTF_PFORMAT pFormat) +{ + LPCSTR szFmt = szSpec; + + pFormat->szFmt[0] = '%'; + pFormat->szFmt[1] = '\0'; + pFormat->nFlags = 0; + pFormat->nWidth = 0; + pFormat->nPrecision = 0; + + szFmt += VFPRINTF_parseFormatPrefixA(szFmt, pFormat); + szFmt += VFPRINTF_parseFormatWidthA(szFmt, &(pFormat->nWidth), pFormat); + szFmt += VFPRINTF_parseFormatPrecisionA(szFmt, &(pFormat->nPrecision), pFormat); + szFmt += VFPRINTF_parseFormatFlagsA(szFmt, pFormat); + szFmt += VFPRINTF_parseFormatTypeA(szFmt, pFormat); + + return (INT)(szFmt - szSpec); +} + + +/********************************************************************* + * VFPRINTF_parseFormatW + * (internal, non-exported) + * + * Parses a Unicode format string + */ +static INT VFPRINTF_parseFormatW(LPCWSTR szSpec, VFPRINTF_PFORMAT pFormat) +{ + LPCWSTR szFmt = szSpec; + + pFormat->szFmt[0] = '%'; + pFormat->szFmt[1] = '\0'; + pFormat->nFlags = 0; + pFormat->nWidth = 0; + pFormat->nPrecision = 0; + + szFmt += VFPRINTF_parseFormatPrefixW(szFmt, pFormat); + szFmt += VFPRINTF_parseFormatWidthW(szFmt, &(pFormat->nWidth), pFormat); + szFmt += VFPRINTF_parseFormatPrecisionW(szFmt, &(pFormat->nPrecision), pFormat); + szFmt += VFPRINTF_parseFormatFlagsW(szFmt, pFormat); + szFmt += VFPRINTF_parseFormatTypeW(szFmt, pFormat); + + return (INT)(szFmt - szSpec); +} + + +/********************************************************************* + * vfprintf (MSVCRT.@) */ int MSVCRT_vfprintf(MSVCRT_FILE *fFile, const char *szFormat, va_list vaList) { - char buf[2048], *mem = buf; - int written, resize = sizeof(buf), retval; + VFPRINTF_FILE fOut; + + fOut.fIOFile = fFile; + fOut.nBufLen = -1; + fOut.nTotLen = 0; + fOut.nThisLen = 0; + + TRACE("(fFile == %p, szFormat == %s, ...)\n", fFile, debugstr_a(szFormat)); + + while (*szFormat) + { + if (*szFormat == '%') + { + if (*(++szFormat) != '%') + { + VFPRINTF_FORMAT fSpec; + szFormat += VFPRINTF_parseFormatA(szFormat, &fSpec); + vaList = VFPRINTF_printFormatA(&fOut, &fSpec, vaList); + } + else + szFormat += VFPRINTF_printCharA(&fOut, '%'); + } + else + szFormat += VFPRINTF_printCharA(&fOut, *szFormat); + } - /* There are two conventions for vsnprintf failing: - * Return -1 if we truncated, or - * Return the number of bytes that would have been written - * The code below handles both cases - */ - while ((written = vsnprintf(mem, resize, szFormat, vaList)) == -1 || - written > resize) - { - resize = (written == -1 ? resize * 2 : written + 1); - if (mem != buf) - MSVCRT_free (mem); - if (!(mem = (char *)MSVCRT_malloc(resize))) - return MSVCRT_EOF; - } - retval = MSVCRT_fwrite(mem, sizeof(*mem), written, fFile); - if (mem != buf) - MSVCRT_free (mem); - return retval; + return fOut.nTotLen; } + /********************************************************************* * vfwprintf (MSVCRT.@) - * - * FIXME: We don't handle the Microsoft format flag extentions - * %C and %S at all */ int MSVCRT_vfwprintf(MSVCRT_FILE *fFile, const WCHAR *szFormat, va_list vaList) { - WCHAR buf[2048], *mem = buf; - int written, resize = sizeof(buf) / sizeof(WCHAR), retval; + VFPRINTF_FILE fOut; + + fOut.fIOFile = fFile; + fOut.nBufLen = -1; + fOut.nTotLen = 0; + fOut.nThisLen = 0; + + TRACE("(fFile == %p, szFormat == %s, ...)\n", fFile, debugstr_w(szFormat)); + + while (*szFormat) + { + if (*szFormat == '%') + { + if (*(++szFormat) != '%') + { + VFPRINTF_FORMAT fSpec; + szFormat += VFPRINTF_parseFormatW(szFormat, &fSpec); + vaList = VFPRINTF_printFormatW(&fOut, &fSpec, vaList); + } + else + szFormat += VFPRINTF_printCharW(&fOut, '%'); + } + else + szFormat += VFPRINTF_printCharW(&fOut, *szFormat); + } - /* See vfprintf comments */ - while ((written = _vsnwprintf(mem, resize, szFormat, vaList)) == -1 || - written > resize) - { - resize = (written == -1 ? resize * 2 : written + sizeof(WCHAR)); - if (mem != buf) - MSVCRT_free (mem); - if (!(mem = (WCHAR *)MSVCRT_malloc(resize*sizeof(*mem)))) - return MSVCRT_EOF; - } - retval = MSVCRT_fwrite(mem, sizeof(*mem), written, fFile); - if (mem != buf) - MSVCRT_free (mem); - return retval; + return fOut.nTotLen; }