diff options
Diffstat (limited to 'src/pal/src/misc/fmtmessage.cpp')
-rw-r--r-- | src/pal/src/misc/fmtmessage.cpp | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/src/pal/src/misc/fmtmessage.cpp b/src/pal/src/misc/fmtmessage.cpp new file mode 100644 index 0000000000..46e0af6e0d --- /dev/null +++ b/src/pal/src/misc/fmtmessage.cpp @@ -0,0 +1,701 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + + + +Module Name: + + fmtmessage.c + +Abstract: + + Implementation of FormatMessage function. + +Revision History: + + + +--*/ + +#include "pal/palinternal.h" +#include "pal/dbgmsg.h" +#include "pal/unicode_data.h" +#include "pal/critsect.h" +#include "pal/module.h" +#include "pal/misc.h" + +#include "pal/printfcpp.hpp" + +#include "errorstrings.h" + +#include <stdarg.h> +#if NEED_DLCOMPAT +#include "dlcompat.h" +#else // NEED_DLCOMPAT +#include <dlfcn.h> +#endif // NEED_DLCOMPAT +#include <errno.h> + +SET_DEFAULT_DEBUG_CHANNEL(MISC); + +/* Defines */ + +#define MAX_ERROR_STRING_LENGTH 32 + +/*++ +Function: + + FMTMSG_GetMessageString + +Returns the message as a wide string. +--*/ +static LPWSTR FMTMSG_GetMessageString( DWORD dwErrCode ) +{ + TRACE("Entered FMTMSG_GetMessageString\n"); + + LPCWSTR lpErrorString = GetPalErrorString(dwErrCode); + int allocChars; + + if (lpErrorString != NULL) + { + allocChars = PAL_wcslen(lpErrorString) + 1; + } + else + { + allocChars = MAX_ERROR_STRING_LENGTH + 1; + } + + LPWSTR lpRetVal = (LPWSTR)LocalAlloc(LMEM_FIXED, allocChars * sizeof(WCHAR)); + + if (lpRetVal) + { + if (lpErrorString != NULL) + { + PAL_wcscpy(lpRetVal, lpErrorString); + } + else + { + swprintf_s(lpRetVal, MAX_ERROR_STRING_LENGTH, W("Error %u"), dwErrCode); + } + } + else + { + ERROR("Unable to allocate memory.\n"); + } + + return lpRetVal; +} + +/*++ + +Function : + + FMTMSG__watoi + + Converts a wide string repersentation of an integer number + into a interger number. + + Returns a integer number, or 0 on failure. 0 is not a valid number + for FormatMessage inserts. + +--*/ +static INT FMTMSG__watoi( LPWSTR str ) +{ + CONST UINT MAX_NUMBER_LENGTH = 3; + CHAR buf[ MAX_NUMBER_LENGTH ]; + INT nRetVal = 0; + + nRetVal = WideCharToMultiByte( CP_ACP, 0, str, -1, buf, + MAX_NUMBER_LENGTH, NULL, 0 ); + + if ( nRetVal != 0 ) + { + return atoi( buf ); + } + else + { + ERROR( "Unable to convert the string to a number.\n" ); + return 0; + } +} + +/* Adds the character to the working string. */ +#define _ADD_TO_STRING( c ) \ +{\ + TRACE( "Adding %c to the string.\n", (CHAR)c );\ + *lpWorkingString = c;\ + lpWorkingString++;\ + nCount++;\ +} + +/* Grows the buffer. */ +#define _GROW_BUFFER() \ +{\ + if ( bIsLocalAlloced ) \ + { \ + LPWSTR lpTemp = NULL; \ + UINT NumOfBytes = 0; \ + nSize *= 2; \ + NumOfBytes = nSize * sizeof( WCHAR ); \ + lpTemp = static_cast<WCHAR *>( LocalAlloc( LMEM_FIXED, NumOfBytes ) ); \ + TRACE( "Growing the buffer.\n" );\ + \ + if ( !lpTemp ) \ + { \ + ERROR( "Out of buffer\n" ); \ + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); \ + nCount = 0; \ + lpWorkingString = NULL; \ + goto exit; \ + } \ + \ + *lpWorkingString = '\0';\ + PAL_wcscpy( lpTemp, lpReturnString );\ + LocalFree( lpReturnString ); \ + lpWorkingString = lpReturnString = lpTemp; \ + lpWorkingString += nCount; \ + } \ + else \ + { \ + WARN( "Out of buffer.\n" ); \ + SetLastError( ERROR_INSUFFICIENT_BUFFER ); \ + nCount = 0; \ + lpWorkingString = NULL; \ + goto exit; \ + } \ +} +/* Adds a character to the working string. This is a safer version +of _ADD_TO_STRING, as we will resize the buffer if necessary. */ +#define _CHECKED_ADD_TO_STRING( c ) \ +{\ + if ( nCount+1 == nSize ) \ + {\ + _GROW_BUFFER();\ + } \ + _ADD_TO_STRING( c );\ +} + + +/*++ +Function : + + FMTMSG_ProcessPrintf + + Processes the printf formatters based on the format. + + Returns the LPWSTR string, or NULL on failure. +*/ + +static LPWSTR FMTMSG_ProcessPrintf( wchar_t c , + LPWSTR lpPrintfString, + LPWSTR lpInsertString) +{ + LPWSTR lpBuffer = NULL; + LPWSTR lpBuffer2 = NULL; + LPWSTR lpFormat = NULL; +#if _DEBUG + // small size for _DEBUG to exercise buffer reallocation logic + int tmpSize = 4; +#else + int tmpSize = 64; +#endif + UINT nFormatLength = 0; + int nBufferLength = 0; + + TRACE( "FMTMSG_ProcessPrintf( %C, %S, %S )\n", c, + lpPrintfString, lpInsertString ); + + switch ( c ) + { + case 'e' : + /* Fall through */ + case 'E' : + /* Fall through */ + case 'f' : + /* Fall through */ + case 'g' : + /* Fall through */ + case 'G' : + ERROR( "%%%c is not supported by FormatMessage.\n", c ); + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + + nFormatLength = PAL_wcslen( lpPrintfString ) + 2; /* Need to count % AND NULL */ + lpFormat = (LPWSTR)PAL_malloc( nFormatLength * sizeof( WCHAR ) ); + if ( !lpFormat ) + { + ERROR( "Unable to allocate memory.\n" ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return NULL; + } + /* Create the format string. */ + memset( lpFormat, 0, nFormatLength * sizeof(WCHAR) ); + *lpFormat = '%'; + + PAL_wcscat( lpFormat, lpPrintfString ); + + lpBuffer = (LPWSTR) PAL_malloc(tmpSize*sizeof(WCHAR)); + + /* try until the buffer is big enough */ + while (TRUE) + { + if (!lpBuffer) + { + ERROR("Unable to allocate memory\n"); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + PAL_free(lpFormat); + return NULL; + } + nBufferLength = _snwprintf_s( lpBuffer, tmpSize, tmpSize, + lpFormat, lpInsertString); + + if ((nBufferLength >= 0) && (nBufferLength != tmpSize)) + { + break; /* succeeded */ + } + else + { + tmpSize *= 2; + lpBuffer2 = static_cast<WCHAR *>( + PAL_realloc(lpBuffer, tmpSize*sizeof(WCHAR))); + if (lpBuffer2 == NULL) + PAL_free(lpBuffer); + lpBuffer = lpBuffer2; + } + } + + PAL_free( lpFormat ); + lpFormat = NULL; + + return lpBuffer; +} + +/*++ +Function: + FormatMessageW + +See MSDN doc. +--*/ +DWORD +PALAPI +FormatMessageW( + IN DWORD dwFlags, + IN LPCVOID lpSource, + IN DWORD dwMessageId, + IN DWORD dwLanguageId, + OUT LPWSTR lpBuffer, + IN DWORD nSize, + IN va_list *Arguments) +{ + BOOL bIgnoreInserts = FALSE; + BOOL bIsVaList = TRUE; + BOOL bIsLocalAlloced = FALSE; + LPWSTR lpSourceString = NULL; + UINT nCount = 0; + LPWSTR lpReturnString = NULL; + LPWSTR lpWorkingString = NULL; + + + PERF_ENTRY(FormatMessageW); + ENTRY( "FormatMessageW(dwFlags=%#x, lpSource=%p, dwMessageId=%#x, " + "dwLanguageId=%#x, lpBuffer=%p, nSize=%u, va_list=%p)\n", + dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, + Arguments); + + /* Sanity checks. */ + if ( dwFlags & FORMAT_MESSAGE_FROM_STRING && !lpSource ) + { + /* This behavior is different then in Windows. + Windows would just crash.*/ + ERROR( "lpSource cannot be NULL.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + goto exit; + } + + if ( !(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER ) && !lpBuffer ) + { + /* This behavior is different then in Windows. + Windows would just crash.*/ + ERROR( "lpBuffer cannot be NULL, if " + " FORMAT_MESSAGE_ALLOCATE_BUFFER is not specified.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + goto exit; + } + + if ( ( dwFlags & FORMAT_MESSAGE_FROM_STRING ) && + ( dwFlags & FORMAT_MESSAGE_FROM_SYSTEM ) ) + { + ERROR( "These flags cannot co-exist. You can either " + "specify FORMAT_MESSAGE_FROM_STRING, or " + "FORMAT_MESSAGE_FROM_SYSTEM.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + goto exit; + } + + if ( !( dwFlags & FORMAT_MESSAGE_FROM_STRING ) && + ( dwLanguageId != 0 +#if ENABLE_DOWNLEVEL_FOR_NLS + && dwLanguageId != MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ) +#endif + ) ) + { + ERROR( "Invalid language indentifier.\n" ); + SetLastError( ERROR_RESOURCE_LANG_NOT_FOUND ); + goto exit; + } + + /* Parameter processing. */ + if ( dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER ) + { + TRACE( "Allocated %d TCHARs. Don't forget to call LocalFree to " + "free the memory when done.\n", nSize ); + bIsLocalAlloced = TRUE; + } + + if ( dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS ) + { + bIgnoreInserts = TRUE; + } + + if ( dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY ) + { + if ( !Arguments && !bIgnoreInserts ) + { + ERROR( "The va_list cannot be NULL.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + goto exit; + } + else + { + bIsVaList = FALSE; + } + } + + if ( dwFlags & FORMAT_MESSAGE_FROM_STRING ) + { + lpSourceString = (LPWSTR)lpSource; + } + else if ( dwFlags & FORMAT_MESSAGE_FROM_SYSTEM ) + { + if ((dwMessageId & 0xFFFF0000) == 0x80070000) + { + // This message has been produced by HRESULT_FROM_WIN32. Undo its work. + dwMessageId &= 0xFFFF; + } + + lpWorkingString = lpReturnString = + FMTMSG_GetMessageString( dwMessageId ); + + if ( !lpWorkingString ) + { + ERROR( "Unable to find the message %d.\n", dwMessageId ); + SetLastError( ERROR_INTERNAL_ERROR ); + nCount = 0; + goto exit; + } + + nCount = PAL_wcslen( lpWorkingString ); + + if ( !bIsLocalAlloced && nCount > nSize ) + { + ERROR( "Insufficient buffer.\n" ); + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + lpWorkingString = NULL; + nCount = 0; + goto exit; + } + if ( !lpWorkingString ) + { + ERROR( "Invalid error indentifier.\n" ); + SetLastError( ERROR_INVALID_ADDRESS ); + } + goto exit; + } + else + { + ERROR( "Unknown flag.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + goto exit; + } + + if ( nSize == 0 && bIsLocalAlloced ) + { + nSize = 1; + } + + lpWorkingString = static_cast<WCHAR *>( + LocalAlloc( LMEM_FIXED, nSize * sizeof( WCHAR ) ) ); + if ( !lpWorkingString ) + { + ERROR( "Unable to allocate memory for the working string.\n" ); + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + goto exit; + } + + + /* Process the string. */ + lpReturnString = lpWorkingString; + while ( *lpSourceString ) + { + if ( *lpSourceString == '%' && !bIgnoreInserts ) + { + lpSourceString++; + /* Escape sequences. */ + if ( *lpSourceString == '0' ) + { + /* Terminates a message without a newline character. */ + *lpWorkingString = '\0'; + goto exit; + } + else if ( PAL_iswdigit( *lpSourceString ) ) + { + /* Get the insert number. */ + WCHAR Number[] = { '\0', '\0', '\0' }; + SIZE_T Index = 0; + + Number[ 0 ] = *lpSourceString; + lpSourceString++; + + if ( PAL_iswdigit( *lpSourceString ) ) + { + Number[ 1 ] = *lpSourceString; + lpSourceString++; + if ( PAL_iswdigit( *lpSourceString ) ) + { + ERROR( "Invalid insert indentifier.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + lpWorkingString = NULL; + nCount = 0; + goto exit; + } + } + Index = FMTMSG__watoi( Number ); + if ( Index == 0 ) + { + ERROR( "Invalid insert indentifier.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + lpWorkingString = NULL; + nCount = 0; + goto exit; + } + if ( *lpSourceString == '!' ) + { + LPWSTR lpInsertString = NULL; + LPWSTR lpPrintfString = NULL; + LPWSTR lpStartOfFormattedString = NULL; + UINT nPrintfLength = 0; + LPWSTR lpFormattedString = NULL; + UINT nFormattedLength = 0; + + if ( !bIsVaList ) + { + lpInsertString = ((LPWSTR*)Arguments)[ Index - 1 ]; + } + else + { + va_list TheArgs; + + va_copy(TheArgs, *Arguments); + UINT i = 0; + for ( ; i < Index; i++ ) + { + lpInsertString = va_arg( TheArgs, LPWSTR ); + } + } + + /* Calculate the length, and extract the printf string.*/ + lpSourceString++; + { + LPWSTR p = PAL_wcschr( lpSourceString, '!' ); + + if ( NULL == p ) + { + nPrintfLength = 0; + } + else + { + nPrintfLength = p - lpSourceString; + } + } + + lpPrintfString = + (LPWSTR)PAL_malloc( ( nPrintfLength + 1 ) * sizeof( WCHAR ) ); + + if ( !lpPrintfString ) + { + ERROR( "Unable to allocate memory.\n" ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + lpWorkingString = NULL; + nCount = 0; + goto exit; + } + + PAL_wcsncpy( lpPrintfString, lpSourceString, nPrintfLength ); + *( lpPrintfString + nPrintfLength ) = '\0'; + + lpStartOfFormattedString = lpFormattedString = + FMTMSG_ProcessPrintf( *lpPrintfString, + lpPrintfString, + lpInsertString); + + if ( !lpFormattedString ) + { + ERROR( "Unable to process the format string.\n" ); + /* Function will set the error code. */ + PAL_free( lpPrintfString ); + lpWorkingString = NULL; + goto exit; + } + + + nFormattedLength = PAL_wcslen( lpFormattedString ); + + /* Append the processed printf string into the working string */ + while ( *lpFormattedString ) + { + _CHECKED_ADD_TO_STRING( *lpFormattedString ); + lpFormattedString++; + } + + lpSourceString += nPrintfLength + 1; + PAL_free( lpPrintfString ); + PAL_free( lpStartOfFormattedString ); + lpPrintfString = lpFormattedString = NULL; + } + else + { + /* The printf format string defaults to 's'.*/ + LPWSTR lpInsert = NULL; + + if ( !bIsVaList ) + { + lpInsert = ((LPWSTR*)Arguments)[Index - 1]; + } + else + { + va_list TheArgs; + va_copy(TheArgs, *Arguments); + UINT i = 0; + for ( ; i < Index; i++ ) + { + lpInsert = va_arg( TheArgs, LPWSTR ); + } + } + + while ( *lpInsert ) + { + _CHECKED_ADD_TO_STRING( *lpInsert ); + lpInsert++; + } + } + } + /* Format specifiers. */ + else if ( *lpSourceString == '%' ) + { + _CHECKED_ADD_TO_STRING( '%' ); + lpSourceString++; + } + else if ( *lpSourceString == 'n' ) + { + /* Hard line break. */ + _CHECKED_ADD_TO_STRING( '\n' ); + lpSourceString++; + } + else if ( *lpSourceString == '.' ) + { + _CHECKED_ADD_TO_STRING( '.' ); + lpSourceString++; + } + else if ( *lpSourceString == '!' ) + { + _CHECKED_ADD_TO_STRING( '!' ); + lpSourceString++; + } + else if ( !*lpSourceString ) + { + ERROR( "Invalid parameter.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + lpWorkingString = NULL; + nCount = 0; + goto exit; + } + else /* Append the character. */ + { + _CHECKED_ADD_TO_STRING( *lpSourceString ); + lpSourceString++; + } + }/* END if ( *lpSourceString == '%' ) */ + else + { + /* In Windows if FormatMessage is called with ignore inserts, + then FormatMessage strips %1!s! down to %1, since string is the + default. */ + if ( bIgnoreInserts && *lpSourceString == '!' && + *( lpSourceString + 1 ) == 's' ) + { + LPWSTR lpLastBang = PAL_wcschr( lpSourceString + 1, '!' ); + + if ( lpLastBang && ( 2 == lpLastBang - lpSourceString ) ) + { + lpSourceString = lpLastBang + 1; + } + else + { + ERROR( "Mal-formed string\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + lpWorkingString = NULL; + nCount = 0; + goto exit; + } + } + else + { + /* Append to the string. */ + _CHECKED_ADD_TO_STRING( *lpSourceString ); + lpSourceString++; + } + } + } + + /* Terminate the message. */ + _CHECKED_ADD_TO_STRING( '\0' ); + /* NULL does not count. */ + nCount--; + +exit: /* Function clean-up and exit. */ + if ( lpWorkingString ) + { + if ( bIsLocalAlloced ) + { + TRACE( "Assigning the buffer to the pointer.\n" ); + // when FORMAT_MESSAGE_ALLOCATE_BUFFER is specified, nSize + // does not specify the size of lpBuffer, rather it specifies + // the minimum size of the string + // as such we have to blindly assume that lpBuffer has enough space to + // store PVOID + // might cause a prefast warning, but there is no good way to suppress it yet + _ASSERTE(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER); + *((LPVOID*)lpBuffer) = (LPVOID)lpReturnString; + } + else /* Only delete lpReturnString if the caller has their own buffer.*/ + { + TRACE( "Copying the string into the buffer.\n" ); + PAL_wcsncpy( lpBuffer, lpReturnString, nCount + 1 ); + LocalFree( lpReturnString ); + } + } + else /* Error, something occurred. */ + { + if ( lpReturnString ) + { + LocalFree( lpReturnString ); + } + } + LOGEXIT( "FormatMessageW returns %d.\n", nCount ); + PERF_EXIT(FormatMessageW); + return nCount; +} |