/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the license, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. You should have received a copy * of the GNU Lesser General Public License along with this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Authors: Kyle Stemen */ /* -*- mode: c; c-basic-offset: 4 -*- */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #pragma warning( disable : 4996 ) #include #define va_copy(a, b) ((a) = (b)) #endif // % * ? ? ? // flag may be one of: #define FLAG_ALT_FORM 1 // '#' #define FLAG_ZERO_PAD 2 // '0' #define FLAG_LEFT_JUSTIFY 4 // '-' #define FLAG_SPACE_FOR_POSITIVE 8 // ' ' #define FLAG_PLUS_FOR_POSITIVE 16 // '+' #define FLAG_COMMA_SEPARATE 32 // '\'' #define FLAG_LOCALE_DIGITS 64 // 'I' // #define FLAG_UPPER_CASE 128 // This one isn't accessible from the format specifier, but X, E, G, etc. // are converted to their lower case counterparts with this flag set. // // field width: // \* | [0-9]+ // // precision: // \. ( \* | [0-9]* ) // // length modifier: #define LENGTH_UNSET 0 #define LENGTH_CHAR 1 // hh #define LENGTH_SHORT 2 // h #define LENGTH_LONG 3 // l #define LENGTH_LONG_LONG 4 // ll,q #define LENGTH_LONG_DOUBLE 5 // L #define LENGTH_INTMAX_T 6 // j #define LENGTH_SIZE_T 7 // z #define LENGTH_PTRDIFF_T 8 // t #define LENGTH_W16CHAR_T 9 // w // // conversion specifier: #define TYPE_INTEGER 1 // d,i #define TYPE_OCTAL 2 // o #define TYPE_UNSIGNED_INTEGER 3 // u #define TYPE_HEX 4 // x,X #define TYPE_SCIENTIFIC 6 // e,E #define TYPE_DOUBLE 8 // f,F #define TYPE_AUTO_DOUBLE 10 // g,G #define TYPE_HEX_DOUBLE 11 // a,A #define TYPE_CHAR 13 // c #define TYPE_STRING 14 // s #define TYPE_CHAR 13 // C #define TYPE_STRING 14 // S #define TYPE_POINTER 15 // p #define TYPE_WRITTEN_COUNT 16 // n #define TYPE_STRERROR 17 // m #define TYPE_PERCENT 18 // % #define TYPE_INVALID 19 typedef struct _PRINTF_BUFFER PRINTF_BUFFER, *PPRINTF_BUFFER; typedef void (*PFNBUFFER_WRITE_WCS)( PPRINTF_BUFFER pBuffer, const wchar_t *pwszBuffer, size_t cchBuffer); typedef void (*PFNBUFFER_WRITE_WC16S)( PPRINTF_BUFFER pBuffer, const wchar16_t *pwszBuffer, size_t cchBuffer); typedef void (*PFNBUFFER_WRITE_MBS)( PPRINTF_BUFFER pBuffer, const char *pszBuffer, size_t cchBuffer); struct _PRINTF_BUFFER { size_t sWrittenCount; PFNBUFFER_WRITE_WCS pfnWriteWcs; PFNBUFFER_WRITE_WC16S pfnWriteWc16s; PFNBUFFER_WRITE_MBS pfnWriteMbs; }; void WriteSpaces( PPRINTF_BUFFER pBuffer, ssize_t sCount ) { wchar16_t space = ' '; while (sCount > 0) { pBuffer->pfnWriteWc16s(pBuffer, &space, 1); sCount--; } } unsigned int ParseFlags( const wchar16_t** ppwszPos ) { const wchar16_t* pwszPos = *ppwszPos; int bFoundOption = 1; unsigned int dwFlags = 0; while (bFoundOption) { switch (*pwszPos++) { case '#': dwFlags |= FLAG_ALT_FORM; break; case '0': dwFlags |= FLAG_ZERO_PAD; break; case '-': dwFlags |= FLAG_LEFT_JUSTIFY; break; case ' ': dwFlags |= FLAG_SPACE_FOR_POSITIVE; break; case '+': dwFlags |= FLAG_PLUS_FOR_POSITIVE; break; case '\'': dwFlags |= FLAG_COMMA_SEPARATE; break; case 'I': dwFlags |= FLAG_LOCALE_DIGITS; break; default: bFoundOption = 0; pwszPos--; break; } } *ppwszPos = pwszPos; return dwFlags; } unsigned int ParseLengthModifier( const wchar16_t** ppwszPos ) { const wchar16_t* pwszPos = *ppwszPos; unsigned int dwLengthModifier = LENGTH_UNSET; switch (*pwszPos) { case 'h': if (pwszPos[1] == 'h') { dwLengthModifier = LENGTH_CHAR; pwszPos += 2; } else { dwLengthModifier = LENGTH_SHORT; pwszPos++; } break; case 'l': if (pwszPos[1] == 'l') { dwLengthModifier = LENGTH_LONG_LONG; pwszPos += 2; } else { dwLengthModifier = LENGTH_LONG; pwszPos++; } break; case 'q': dwLengthModifier = LENGTH_LONG_LONG; pwszPos++; break; case 'L': dwLengthModifier = LENGTH_LONG_DOUBLE; pwszPos++; break; case 'j': dwLengthModifier = LENGTH_INTMAX_T; pwszPos++; break; case 'z': dwLengthModifier = LENGTH_SIZE_T; pwszPos++; break; case 't': dwLengthModifier = LENGTH_PTRDIFF_T; pwszPos++; break; case 'w': dwLengthModifier = LENGTH_W16CHAR_T; pwszPos++; break; } *ppwszPos = pwszPos; return dwLengthModifier; } unsigned int ParseType( const wchar16_t** ppwszPos, unsigned int *pdwFlags ) { const wchar16_t* pwszPos = *ppwszPos; unsigned int dwType = TYPE_INVALID; switch (*pwszPos++) { case 'd': case 'i': dwType = TYPE_INTEGER; break; case 'o': dwType = TYPE_OCTAL; break; case 'u': dwType = TYPE_UNSIGNED_INTEGER; break; case 'X': *pdwFlags |= FLAG_UPPER_CASE; case 'x': dwType = TYPE_HEX; break; case 'E': *pdwFlags |= FLAG_UPPER_CASE; case 'e': dwType = TYPE_SCIENTIFIC; break; case 'F': // Upper case F doesn't do anything special case 'f': dwType = TYPE_DOUBLE; break; case 'G': *pdwFlags |= FLAG_UPPER_CASE; case 'g': dwType = TYPE_AUTO_DOUBLE; break; case 'A': *pdwFlags |= FLAG_UPPER_CASE; case 'a': dwType = TYPE_HEX_DOUBLE; break; case 'C': *pdwFlags |= FLAG_UPPER_CASE; case 'c': dwType = TYPE_CHAR; break; case 'S': *pdwFlags |= FLAG_UPPER_CASE; case 's': dwType = TYPE_STRING; break; case 'p': dwType = TYPE_POINTER; break; case 'n': dwType = TYPE_WRITTEN_COUNT; break; case 'm': dwType = TYPE_STRERROR; break; case '%': dwType = TYPE_PERCENT; break; default: pwszPos--; dwType = TYPE_INVALID; break; } *ppwszPos = pwszPos; return dwType; } int W16PrintfCore( PPRINTF_BUFFER pBuffer, int bMSCompat, const wchar16_t* pwszFormat, va_list args ) { size_t sLen = 0; const wchar16_t* pwszPos = pwszFormat; const wchar16_t* pwszTokenStart = pwszFormat; unsigned int dwFlags = 0; unsigned int dwLengthModifier = 0; unsigned int dwType = 0; size_t sWidth = 0; ssize_t iPrecision = -1; // Enough room for all supported fixed sized types char szArgBuffer[100]; wchar_t wszArgBuffer[1]; wchar16_t w16szArgBuffer[100]; // Enough room for all supported format specifiers char szFormatBuffer[20]; char *pszFormatBufferPos = NULL; while (*pwszPos) { if (*pwszPos == '%') { pBuffer->pfnWriteWc16s( pBuffer, pwszTokenStart, pwszPos - pwszTokenStart); pwszPos++; // Put the format specifier (% included) into a multibyte // string incase regular printf needs to be called pszFormatBufferPos = szFormatBuffer; *pszFormatBufferPos++ = '%'; pwszTokenStart = pwszPos; dwFlags = ParseFlags(&pwszPos); // update multibyte format // subtract for % * . * null if (pwszPos - pwszTokenStart > sizeof(szFormatBuffer) - 5) { errno = ENOMEM; return -1; } sLen = wc16stombs( pszFormatBufferPos, pwszTokenStart, pwszPos - pwszTokenStart); if (sLen == (size_t)-1) { return -1; } pszFormatBufferPos += sLen; if (*pwszPos == '*') { pwszPos++; sWidth = va_arg(args, size_t); } else { sWidth = (size_t)wc16stoull(pwszPos, &pwszPos, 10); } *pszFormatBufferPos++ = '*'; iPrecision = -1; if (*pwszPos == '.') { pwszPos++; if (*pwszPos == '*') { pwszPos++; iPrecision = va_arg(args, ssize_t); } else if (*pwszPos != '-') { iPrecision = (ssize_t)wc16stoull(pwszPos, &pwszPos, 10); } } *pszFormatBufferPos++ = '.'; *pszFormatBufferPos++ = '*'; pwszTokenStart = pwszPos; dwLengthModifier = ParseLengthModifier(&pwszPos); dwType = ParseType(&pwszPos, &dwType); if (pszFormatBufferPos - szFormatBuffer + pwszPos - pwszTokenStart > sizeof(szFormatBuffer) - 1) { errno = ENOMEM; return -1; } sLen = wc16stombs( pszFormatBufferPos, pwszTokenStart, pwszPos - pwszTokenStart); if (sLen == (size_t)-1) { return -1; } pszFormatBufferPos += sLen; *pszFormatBufferPos = 0; if (bMSCompat && (dwType == TYPE_STRING || dwType == TYPE_CHAR)) { if (dwLengthModifier == LENGTH_LONG) { dwLengthModifier = LENGTH_W16CHAR_T; } else if (dwLengthModifier == LENGTH_SHORT) { dwLengthModifier = LENGTH_CHAR; } else { if (dwFlags & FLAG_UPPER_CASE) { dwLengthModifier = LENGTH_CHAR; } else { dwLengthModifier = LENGTH_W16CHAR_T; } } } switch (dwType) { case TYPE_OCTAL: case TYPE_UNSIGNED_INTEGER: case TYPE_HEX: case TYPE_INTEGER: if (dwLengthModifier == LENGTH_LONG_LONG) { long long llArg = va_arg(args, long long); sLen = snprintf( szArgBuffer, sizeof(szArgBuffer), szFormatBuffer, sWidth, iPrecision, llArg); } else { long lArg = va_arg(args, long); sLen = snprintf( szArgBuffer, sizeof(szArgBuffer), szFormatBuffer, sWidth, iPrecision, lArg); } if (sLen > sizeof(szArgBuffer) || (ssize_t) sLen < 0) { errno = ENOMEM; return -1; } pBuffer->pfnWriteMbs(pBuffer, szArgBuffer, sLen); break; case TYPE_DOUBLE: case TYPE_AUTO_DOUBLE: case TYPE_HEX_DOUBLE: case TYPE_SCIENTIFIC: if (dwLengthModifier == LENGTH_LONG_DOUBLE) { long double ldArg = va_arg(args, long double); sLen = snprintf( szArgBuffer, sizeof(szArgBuffer), szFormatBuffer, sWidth, iPrecision, ldArg); } else { double dArg = va_arg(args, double); sLen = snprintf( szArgBuffer, sizeof(szArgBuffer), szFormatBuffer, sWidth, iPrecision, dArg); } if (sLen > sizeof(szArgBuffer) || (ssize_t) sLen < 0) { errno = ENOMEM; return -1; } pBuffer->pfnWriteMbs(pBuffer, szArgBuffer, sLen); break; case TYPE_CHAR: { wint_t iArg = va_arg(args, wint_t); if (!(dwFlags & FLAG_LEFT_JUSTIFY)) { WriteSpaces(pBuffer, (ssize_t)sWidth - 1); } switch (dwLengthModifier) { case LENGTH_UNSET: case LENGTH_CHAR: szArgBuffer[0] = (char)iArg; pBuffer->pfnWriteMbs( pBuffer, szArgBuffer, 1); break; case LENGTH_SHORT: case LENGTH_W16CHAR_T: w16szArgBuffer[0] = iArg; pBuffer->pfnWriteWc16s( pBuffer, w16szArgBuffer, 1); break; case LENGTH_LONG: wszArgBuffer[0] = iArg; pBuffer->pfnWriteWcs( pBuffer, wszArgBuffer, 1); break; case LENGTH_LONG_LONG: case LENGTH_LONG_DOUBLE: case LENGTH_INTMAX_T: case LENGTH_SIZE_T: case LENGTH_PTRDIFF_T: default: errno = EINVAL; return -1; } if (dwFlags & FLAG_LEFT_JUSTIFY) { WriteSpaces(pBuffer, (ssize_t)sWidth - 1); } } break; case TYPE_STRING: { void* pvArg = va_arg(args, void *); if (pvArg == NULL) { pvArg = "(null)"; dwLengthModifier = LENGTH_CHAR; } switch (dwLengthModifier) { case LENGTH_UNSET: case LENGTH_CHAR: sLen = strlen((char *)pvArg); break; case LENGTH_SHORT: case LENGTH_W16CHAR_T: sLen = wc16slen((wchar16_t *)pvArg); break; case LENGTH_LONG: sLen = wcslen((wchar_t *)pvArg); break; case LENGTH_LONG_LONG: case LENGTH_LONG_DOUBLE: case LENGTH_INTMAX_T: case LENGTH_SIZE_T: case LENGTH_PTRDIFF_T: default: errno = EINVAL; return -1; } if (iPrecision >= 0 && sLen > (size_t)iPrecision) { sLen = iPrecision; } if (!(dwFlags & FLAG_LEFT_JUSTIFY)) { WriteSpaces(pBuffer, (ssize_t)sWidth - (ssize_t)sLen); } switch (dwLengthModifier) { case LENGTH_UNSET: case LENGTH_CHAR: pBuffer->pfnWriteMbs( pBuffer, (char *)pvArg, sLen); break; case LENGTH_SHORT: case LENGTH_W16CHAR_T: pBuffer->pfnWriteWc16s( pBuffer, (wchar16_t *)pvArg, sLen); break; case LENGTH_LONG: pBuffer->pfnWriteWcs( pBuffer, (wchar_t *)pvArg, sLen); break; //Invalid cases have already been checked for } if (dwFlags & FLAG_LEFT_JUSTIFY) { WriteSpaces(pBuffer, (ssize_t)(sWidth - sLen)); } } break; case TYPE_POINTER: { void *pvArg = va_arg(args, void *); sLen = snprintf( szArgBuffer, sizeof(szArgBuffer), szFormatBuffer, sWidth, iPrecision, pvArg); if (sLen > sizeof(szArgBuffer) || (ssize_t) sLen < 0) { errno = ENOMEM; return -1; } pBuffer->pfnWriteMbs(pBuffer, szArgBuffer, sLen); } break; case TYPE_WRITTEN_COUNT: { size_t *psArg = va_arg(args, size_t *); if (psArg == NULL) { errno = EINVAL; return -1; } *psArg = pBuffer->sWrittenCount; } break; case TYPE_STRERROR: { sLen = snprintf( szArgBuffer, sizeof(szArgBuffer), szFormatBuffer, sWidth, iPrecision); if (sLen > sizeof(szArgBuffer) || (ssize_t) sLen < 0) { errno = ENOMEM; return -1; } pBuffer->pfnWriteMbs(pBuffer, szArgBuffer, sLen); } break; case TYPE_PERCENT: w16szArgBuffer[0] = '%'; pBuffer->pfnWriteWc16s(pBuffer, w16szArgBuffer, 1); break; case TYPE_INVALID: errno = EINVAL; return -1; } pwszTokenStart = pwszPos; } else { pwszPos++; } } pBuffer->pfnWriteWc16s(pBuffer, pwszTokenStart, pwszPos - pwszTokenStart); return 0; } typedef struct _STRING_PRINTF_BUFFER { PRINTF_BUFFER parent; wchar16_t* pwszBuffer; size_t sAvailable; unsigned int dwError; } STRING_PRINTF_BUFFER, *PSTRING_PRINTF_BUFFER; void StringPrintfWriteWcs( PSTRING_PRINTF_BUFFER pBuffer, const wchar_t *pwszWrite, size_t cchWrite) { size_t sConverted; if (pBuffer->dwError) { goto error; } if (pBuffer->pwszBuffer) { if (cchWrite > pBuffer->sAvailable) { pBuffer->dwError = ENOMEM; goto error; } sConverted = wcstowc16s(pBuffer->pwszBuffer, pwszWrite, cchWrite); if (sConverted != cchWrite) { pBuffer->dwError = errno; goto error; } pBuffer->pwszBuffer += sConverted; pBuffer->sAvailable -= sConverted; } pBuffer->parent.sWrittenCount += cchWrite; error: ; } void StringPrintfWriteMbs( PSTRING_PRINTF_BUFFER pBuffer, const char *pszWrite, size_t cchWrite) { size_t sConverted; if (pBuffer->dwError) { goto error; } if (pBuffer->pwszBuffer) { if (cchWrite > pBuffer->sAvailable) { pBuffer->dwError = ENOMEM; goto error; } sConverted = mbstowc16s(pBuffer->pwszBuffer, pszWrite, cchWrite); if (sConverted != cchWrite) { pBuffer->dwError = errno; goto error; } pBuffer->pwszBuffer += sConverted; pBuffer->sAvailable -= sConverted; } pBuffer->parent.sWrittenCount += cchWrite; error: ; } void StringPrintfWriteWc16s( PSTRING_PRINTF_BUFFER pBuffer, const wchar16_t *pwszWrite, size_t cchWrite) { ssize_t sConverted; if (pBuffer->dwError) { goto error; } if (pBuffer->pwszBuffer && cchWrite) { if (cchWrite > pBuffer->sAvailable) { pBuffer->dwError = ENOMEM; goto error; } sConverted = wc16pncpy(pBuffer->pwszBuffer, pwszWrite, cchWrite) - pBuffer->pwszBuffer + 1; if (sConverted != cchWrite) { pBuffer->dwError = errno; goto error; } pBuffer->pwszBuffer += sConverted; pBuffer->sAvailable -= sConverted; } pBuffer->parent.sWrittenCount += cchWrite; error: ; } ssize_t _vsw16printf( wchar16_t *out, size_t maxchars, const wchar16_t *format, va_list args ) { int error = 0; STRING_PRINTF_BUFFER output; memset(&output, 0, sizeof(output)); output.parent.pfnWriteWcs = (PFNBUFFER_WRITE_WCS)StringPrintfWriteWcs; output.parent.pfnWriteWc16s = (PFNBUFFER_WRITE_WC16S)StringPrintfWriteWc16s; output.parent.pfnWriteMbs = (PFNBUFFER_WRITE_MBS)StringPrintfWriteMbs; output.pwszBuffer = out; output.sAvailable = maxchars; if (W16PrintfCore( &output.parent, 0, format, args) < 0) { error = errno; } if (output.dwError) { // This error takes precedence over an error from W16PrintfCore error = output.dwError; } if (error) { goto error; } if (!output.pwszBuffer) { /* This is only calculating the maximum size. Do not include the null * in this calculation, and do not write anything. */ } else if (output.sAvailable) { /* terminate the resulting string */ *output.pwszBuffer = 0; } else { error = ENOMEM; goto error; } cleanup: if (error) { return -1; } else { return output.parent.sWrittenCount; } error: goto cleanup; } //TODO: rename this once the deprecated sw16printf is removed ssize_t _sw16printf_new(wchar16_t *out, size_t maxchars, const wchar16_t *format, ...) { ssize_t sResult = 0; va_list ap; va_start(ap, format); sResult = _vsw16printf( out, maxchars, format, ap); if (sResult < 0) { goto error; } cleanup: va_end(ap); return sResult; error: goto cleanup; } ssize_t _sw16printfw(wchar16_t *out, size_t maxchars, const wchar_t *format, ...) { int bFreeFormat = 0; wchar16_t *pwszFormat = NULL; ssize_t sResult = 0; va_list ap; va_start(ap, format); pwszFormat = awcstowc16s( format, &bFreeFormat); if (pwszFormat == NULL) { errno = ENOMEM; goto error; } sResult = _vsw16printf( out, maxchars, pwszFormat, ap); if (sResult < 0) { goto error; } cleanup: if (bFreeFormat) { free(pwszFormat); } va_end(ap); return sResult; error: goto cleanup; } wchar16_t * asw16printfwv(const wchar_t *format, va_list args) { int bFreeFormat = 0; wchar16_t *pwszFormat = NULL; wchar16_t *pwszOut = NULL; ssize_t sLen = 0; va_list args2; va_copy(args2, args); pwszFormat = awcstowc16s( format, &bFreeFormat); if (pwszFormat == NULL) { errno = ENOMEM; goto error; } sLen = vsw16printf( NULL, 0, pwszFormat, args); if (sLen < 0) { goto error; } pwszOut = malloc((sLen + 1) * sizeof(*pwszOut)); sLen = vsw16printf( pwszOut, sLen + 1, pwszFormat, args2); if (sLen < 0) { goto error; } cleanup: if (bFreeFormat) { free(pwszFormat); } va_end(args2); return pwszOut; error: if (pwszOut != NULL) { free(pwszOut); pwszOut = NULL; } goto cleanup; } wchar16_t * asw16printfw(const wchar_t *format, ...) { va_list ap; wchar16_t *result = NULL; va_start(ap, format); result = asw16printfwv(format, ap); va_end(ap); return result; } typedef struct _FILE_PRINTF_BUFFER { PRINTF_BUFFER parent; FILE* pFile; unsigned int dwError; } FILE_PRINTF_BUFFER, *PFILE_PRINTF_BUFFER; void FilePrintfWriteWcs( PFILE_PRINTF_BUFFER pBuffer, const wchar_t *pwszWrite, size_t cchWrite) { if (pBuffer->dwError) { // Something already went wrong. Don't try to write anything else return; } if (cchWrite > INT_MAX) { pBuffer->dwError = ERANGE; return; } // This is the number of characters that would be written if the output // string had unlimited space. pBuffer->parent.sWrittenCount += cchWrite; if (fprintf(pBuffer->pFile, "%.*ls", (int)cchWrite, pwszWrite) < 0) { pBuffer->dwError = errno; } } void FilePrintfWriteWc16s( PFILE_PRINTF_BUFFER pBuffer, const wchar16_t *pw16szWrite, size_t cch16Write) { wchar_t* pwszWrite = NULL; size_t cchWrite = 0; if (pBuffer->dwError) { // Something already went wrong. Don't try to write anything else goto error; } pwszWrite = malloc((cch16Write + 1) * sizeof(*pwszWrite)); if (pwszWrite == NULL) { goto error; } cchWrite = wc16stowcs(pwszWrite, pw16szWrite, cch16Write); if (cchWrite == (size_t)-1) { goto error; } if (cchWrite > INT_MAX) { pBuffer->dwError = ERANGE; goto error; } // This is the number of characters that would be written if the output // string had unlimited space. pBuffer->parent.sWrittenCount += cchWrite; if (fprintf(pBuffer->pFile, "%.*ls", (int)cchWrite, pwszWrite) < 0) { pBuffer->dwError = errno; } cleanup: if (pwszWrite) { free(pwszWrite); } return; error: goto cleanup; } void FilePrintfWriteMbs( PFILE_PRINTF_BUFFER pBuffer, const char *pszWrite, size_t cchWrite) { size_t cbWrite; if (pBuffer->dwError) { // Something already went wrong. Don't try to write anything else return; } // This is the number of characters that would be written if the output // string had unlimited space. pBuffer->parent.sWrittenCount += cchWrite; cbWrite = mbsnbcnt(pszWrite, cchWrite); if (cbWrite > INT_MAX) { pBuffer->dwError = ERANGE; return; } if (fprintf(pBuffer->pFile, "%.*s", (int)cbWrite, pszWrite) < 0) { pBuffer->dwError = errno; } } ssize_t _vfw16printf( FILE *pFile, const wchar16_t *format, va_list args ) { int error = 0; FILE_PRINTF_BUFFER output; memset(&output, 0, sizeof(output)); output.parent.pfnWriteWcs = (PFNBUFFER_WRITE_WCS)FilePrintfWriteWcs; output.parent.pfnWriteWc16s = (PFNBUFFER_WRITE_WC16S)FilePrintfWriteWc16s; output.parent.pfnWriteMbs = (PFNBUFFER_WRITE_MBS)FilePrintfWriteMbs; output.pFile = pFile; if (W16PrintfCore( &output.parent, 0, format, args) < 0) { error = errno; } if (output.dwError) { // This error takes precedence over an error from W16PrintfCore error = output.dwError; } if (error) { goto error; } cleanup: if (error) { return -1; } else { return output.parent.sWrittenCount; } error: goto cleanup; } ssize_t _fw16printf(FILE *pFile, const wchar16_t *format, ...) { ssize_t sResult = 0; va_list ap; va_start(ap, format); sResult = _vfw16printf( pFile, format, ap); if (sResult < 0) { goto error; } cleanup: va_end(ap); return sResult; error: goto cleanup; } ssize_t _fw16printfw(FILE *pFile, const wchar_t *format, ...) { int bFreeFormat = 0; wchar16_t *pwszFormat = NULL; ssize_t sResult = 0; va_list ap; va_start(ap, format); pwszFormat = awcstowc16s( format, &bFreeFormat); if (pwszFormat == NULL) { errno = ENOMEM; goto error; } sResult = _vfw16printf( pFile, pwszFormat, ap); if (sResult < 0) { goto error; } cleanup: if (bFreeFormat) { free(pwszFormat); } va_end(ap); return sResult; error: goto cleanup; } ssize_t _w16printfw(const wchar_t *format, ...) { int bFreeFormat = 0; wchar16_t *pwszFormat = NULL; ssize_t sResult = 0; va_list ap; va_start(ap, format); pwszFormat = awcstowc16s( format, &bFreeFormat); if (pwszFormat == NULL) { errno = ENOMEM; goto error; } sResult = _vfw16printf( stdout, pwszFormat, ap); if (sResult < 0) { goto error; } cleanup: if (bFreeFormat) { free(pwszFormat); } va_end(ap); return sResult; error: goto cleanup; } //Deprecated int sw16printf(wchar16_t *out, const char *format, ...) { va_list ap; char c; size_t len; char *s; wchar16_t *S; wchar_t *W; wchar16_t *buf = out; char *fmt, *f; size_t spec_len; char *spec_end, spec[64]; char spec_out[64]; /* larger than any number "printf-ed" */ char conv_string[2]; len = 0; fmt = strdup(format); if (fmt == NULL) return -1; f = fmt; va_start(ap, format); while (*fmt) { if ((*fmt) != '%') { conv_string[0] = *fmt; conv_string[1] = 0; mbstowc16s(buf++, conv_string, 1); fmt++; continue; } /* get the next character */ c = *(fmt + 1); switch (c) { case 's': /* byte character string */ s = va_arg(ap, char *); len = strlen(s); mbstowc16s(buf, s, len); fmt += 2; break; case 'S': /* 16-bit character string */ S = va_arg(ap, wchar16_t *); len = wc16slen(S); wc16scpy(buf, S); fmt += 2; break; case 'W': /* 32-bit character string */ W = va_arg(ap, wchar_t *); len = wcslen(W); wcstowc16s(buf, W, len); fmt += 2; break; default: /* all other format strings */ /* extract single specifier (space ends it) */ spec_end = strchr(fmt, ' '); if (spec_end == NULL) { spec_end = strchr(fmt + 1, '%'); } if (spec_end == NULL) { spec_len = strlen(fmt); } else { spec_len = spec_end - fmt; } memset(spec, 0, sizeof(spec)); strncpy(spec, fmt, spec_len); if (strchr(spec, 'd') || strchr(spec, 'i') || strchr(spec, 'c')) { int arg = va_arg(ap, int); snprintf(spec_out, sizeof(spec_out), spec, arg); } else if (strchr(spec, 'u') || strchr(spec, 'x') || strchr(spec, 'X') || strchr(spec, 'o') ) { unsigned int arg = va_arg(ap, unsigned int); snprintf(spec_out, sizeof(spec_out), spec, arg); } else if (strchr(spec, 'f') || strchr(spec, 'F') || strchr(spec, 'e') || strchr(spec, 'E') || strchr(spec, 'g') || strchr(spec, 'G') || strchr(spec, 'a') || strchr(spec, 'A')) { double arg = va_arg(ap, double); snprintf(spec_out, sizeof(spec_out), spec, arg); } len = strlen(spec_out); mbstowc16s(buf, spec_out, len); fmt += spec_len; } buf += len; } va_end(ap); /* terminate the resulting string */ *buf = (wchar16_t) 0; /* free what's been duplicated as fmt */ if (f) free(f); return (int)(buf - out); } //Deprecated int printfw16(const char *format, ...) { va_list ap; char c; size_t len, total; char *s; wchar16_t *S; wchar_t *W; char *fmt, *f, *out; size_t spec_len; char *spec_end, spec[64]; char spec_out[64]; /* larger than any number "printf-ed" */ len = 0; total = 0; fmt = strdup(format); if (fmt == NULL) return -1; f = fmt; va_start(ap, format); while (*fmt) { if ((*fmt) != '%') { c = *fmt; printf("%c", c); fmt++; continue; } /* get the next character */ c = *(fmt + 1); switch (c) { case 's': /* byte character string */ s = va_arg(ap, char *); len = strlen(s); printf("%s", s); fmt += 2; break; case 'S': /* 16-bit character string */ S = va_arg(ap, wchar16_t *); len = wc16slen(S); out = (char*) malloc(len + 1); wc16stombs(out, S, len + 1); printf("%s", out); free(out); fmt += 2; break; case 'W': /* 32-bit character string */ W = va_arg(ap, wchar_t *); len = wcslen(W); out = (char*) malloc(len + 1); wcstombs(out, W, len + 1); printf("%s", out); free(out); fmt += 2; break; default: /* all other format strings */ /* extract single specifier (space ends it) */ spec_end = strchr(fmt, ' '); if (spec_end == NULL) { spec_end = strchr(fmt + 1, '%'); } if (spec_end == NULL) { spec_len = strlen(fmt); } else { spec_len = spec_end - fmt; } memset(spec, 0, sizeof(spec)); strncpy(spec, fmt, spec_len); if (strchr(spec, 'd') || strchr(spec, 'i') || strchr(spec, 'c')) { int arg = va_arg(ap, int); snprintf(spec_out, sizeof(spec_out), spec, arg); printf("%s", spec_out); len = strlen(spec_out); } else if (strchr(spec, 'u') || strchr(spec, 'x') || strchr(spec, 'X') || strchr(spec, 'o') ) { unsigned int arg = va_arg(ap, unsigned int); snprintf(spec_out, sizeof(spec_out), spec, arg); printf("%s", spec_out); len = strlen(spec_out); } else if (strchr(spec, 'f') || strchr(spec, 'F') || strchr(spec, 'e') || strchr(spec, 'E') || strchr(spec, 'g') || strchr(spec, 'G') || strchr(spec, 'a') || strchr(spec, 'A')) { double arg = va_arg(ap, double); snprintf(spec_out, sizeof(spec_out), spec, arg); printf("%s", spec_out); len = strlen(spec_out); } fmt += spec_len; } total += len; } va_end(ap); /* free what's been duplicated as fmt */ if (f) free(f); return (int)total; }