/* * FormatMessage implementation * * Copyright 1996 Marcus Meissner * * 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include #include #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winternl.h" #include "winuser.h" #include "winnls.h" #include "wine/unicode.h" #include "kernel_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(resource); /* Messages...used by FormatMessage (KERNEL32.something) * * They can be specified either directly or using a message ID and * loading them from the resource. * * The resourcedata has following format: * start: * 0: DWORD nrofentries * nrofentries * subentry: * 0: DWORD firstentry * 4: DWORD lastentry * 8: DWORD offset from start to the stringentries * * (lastentry-firstentry) * stringentry: * 0: WORD len (0 marks end) [ includes the 4 byte header length ] * 2: WORD flags * 4: CHAR[len-4] * (stringentry i of a subentry refers to the ID 'firstentry+i') * * Yes, ANSI strings in win32 resources. Go figure. */ static const WCHAR PCNTFMTWSTR[] = { '%','%','%','s',0 }; static const WCHAR FMTWSTR[] = { '%','s',0 }; /********************************************************************** * load_messageW (internal) */ static LPWSTR load_messageW( HMODULE module, UINT id, WORD lang ) { const MESSAGE_RESOURCE_ENTRY *mre; WCHAR *buffer; TRACE("module = %p, id = %08x\n", module, id ); if (!module) module = GetModuleHandleW( NULL ); if (RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre ) != STATUS_SUCCESS) return NULL; if (mre->Flags & MESSAGE_RESOURCE_UNICODE) { int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR); if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL; memcpy( buffer, mre->Text, len ); } else { int len = MultiByteToWideChar( CP_ACP, 0, (const char *)mre->Text, -1, NULL, 0 ); if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL; MultiByteToWideChar( CP_ACP, 0, (const char*)mre->Text, -1, buffer, len ); } TRACE("returning %s\n", wine_dbgstr_w(buffer)); return buffer; } /********************************************************************** * load_messageA (internal) */ static LPSTR load_messageA( HMODULE module, UINT id, WORD lang ) { const MESSAGE_RESOURCE_ENTRY *mre; char *buffer; TRACE("module = %p, id = %08x\n", module, id ); if (!module) module = GetModuleHandleW( NULL ); if (RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre ) != STATUS_SUCCESS) return NULL; if (mre->Flags & MESSAGE_RESOURCE_UNICODE) { int len = WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, NULL, 0, NULL, NULL ); if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL; WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, buffer, len, NULL, NULL ); } else { int len = strlen((const char*)mre->Text) + 1; if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL; memcpy( buffer, mre->Text, len ); } TRACE("returning %s\n", wine_dbgstr_a(buffer)); return buffer; } /*********************************************************************** * FormatMessageA (KERNEL32.@) * FIXME: missing wrap, */ DWORD WINAPI FormatMessageA( DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list* _args ) { LPDWORD args=(LPDWORD)_args; DWORD ret = 0; #if defined(__i386__) || defined(__sparc__) /* This implementation is completely dependent on the format of the va_list on x86 CPUs */ LPSTR target,t; DWORD talloced; LPSTR from,f; DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK; BOOL eos = FALSE; CHAR ch; TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n", dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args); if ((dwFlags & FORMAT_MESSAGE_FROM_STRING) &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0; if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK) FIXME("line wrapping (%lu) not supported.\n", width); from = NULL; if (dwFlags & FORMAT_MESSAGE_FROM_STRING) { from = HeapAlloc( GetProcessHeap(), 0, strlen((LPCSTR)lpSource)+1 ); strcpy( from, (LPCSTR)lpSource ); } else { from = NULL; if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) from = load_messageA( (HMODULE)lpSource, dwMessageId, dwLanguageId ); if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)) from = load_messageA( kernel32_handle, dwMessageId, dwLanguageId ); if (!from) { SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND); return 0; } } target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100); t = target; talloced= 100; #define ADD_TO_T(c) do { \ *t++=c;\ if (t-target == talloced) {\ target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\ t = target+talloced;\ talloced*=2;\ }\ } while (0) if (from) { f=from; if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) { while (*f && !eos) ADD_TO_T(*f++); } else { while (*f && !eos) { if (*f=='%') { int insertnr; char *fmtstr,*x,*lastf; DWORD *argliststart; fmtstr = NULL; lastf = f; f++; if (!*f) { ADD_TO_T('%'); continue; } switch (*f) { case '1':case '2':case '3':case '4':case '5': case '6':case '7':case '8':case '9': insertnr=*f-'0'; switch (f[1]) { case '0':case '1':case '2':case '3': case '4':case '5':case '6':case '7': case '8':case '9': f++; insertnr=insertnr*10+*f-'0'; f++; break; default: f++; break; } if (*f=='!') { f++; if (NULL!=(x=strchr(f,'!'))) { *x='\0'; fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2); sprintf(fmtstr,"%%%s",f); f=x+1; } else { fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2); sprintf(fmtstr,"%%%s",f); f+=strlen(f); /*at \0*/ } } else { if(!args) break; fmtstr = HeapAlloc(GetProcessHeap(),0,3); strcpy( fmtstr, "%s" ); } if (args) { int sz; LPSTR b; if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) argliststart=args+insertnr-1; else argliststart=(*(DWORD**)args)+insertnr-1; /* FIXME: precision and width components are not handled correctly */ if ( (strcmp(fmtstr, "%ls") == 0) || (strcmp(fmtstr,"%S") == 0) ) { sz = WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, NULL, 0, NULL, NULL); b = HeapAlloc(GetProcessHeap(), 0, sz); WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, b, sz, NULL, NULL); } else { b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 1000); /* CMF - This makes a BIG assumption about va_list */ TRACE("A BIG assumption\n"); vsnprintf(b, sz, fmtstr, (va_list) argliststart); } for (x=b; *x; x++) ADD_TO_T(*x); HeapFree(GetProcessHeap(),0,b); } else { /* NULL args - copy formatstr * (probably wrong) */ while ((lastf