2002-12-08 16:07:18 +00:00
|
|
|
/*
|
|
|
|
* FormatMessage implementation
|
|
|
|
*
|
|
|
|
* Copyright 1996 Marcus Meissner
|
2009-12-03 18:57:57 +00:00
|
|
|
* Copyright 2009 Alexandre Julliard
|
2002-12-08 16:07:18 +00:00
|
|
|
*
|
|
|
|
* 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
|
2009-12-03 18:57:57 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2002-12-08 16:07:18 +00:00
|
|
|
*/
|
|
|
|
|
2013-12-22 18:10:41 +00:00
|
|
|
#include <k32.h>
|
[KERNEL32]: Winesync all there is to Winesync in ReactOS' kernel32. This mainly affects LZ*, Comm*, *ProfileString* (INI), and *Resource* APIs, however the changes in there are relatively minor. More substantial changes affect the locale/NLS/language functions, many which were bitrotting for 6+ years.
In theory, this code is "better" than before, and it is closer to Wine (which arguably has better compatibility). It also resets things in sync with Wine however, and may lose and "fixes" ReactOS may have added over the years. But this is a good thing, since these fixes have been "lost" (they obviously never made it into Wine), and if regressions are now found due to this, actual upstream patches can be sent and picked up on the next sync. This avoids maintaining duplicate code, at the expenses of some potential short-term regressions in i18n.
Finally, note that much of /string seems to be taken from Wine's Unicode library (which a host "unicode" already exists in ReactOS' tools/. It may be better (for someone with more experience as to these wine-isms) to simply just pull-in whatever winelib files are not currently present in ReactOS, and have kernel32 and tools/unicode use winelib, instead of having 2 or 3 copies of the code.
svn path=/trunk/; revision=52754
2011-07-21 05:24:59 +00:00
|
|
|
|
2013-12-22 18:10:41 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
DEBUG_CHANNEL(resource);
|
[KERNEL32]: Winesync all there is to Winesync in ReactOS' kernel32. This mainly affects LZ*, Comm*, *ProfileString* (INI), and *Resource* APIs, however the changes in there are relatively minor. More substantial changes affect the locale/NLS/language functions, many which were bitrotting for 6+ years.
In theory, this code is "better" than before, and it is closer to Wine (which arguably has better compatibility). It also resets things in sync with Wine however, and may lose and "fixes" ReactOS may have added over the years. But this is a good thing, since these fixes have been "lost" (they obviously never made it into Wine), and if regressions are now found due to this, actual upstream patches can be sent and picked up on the next sync. This avoids maintaining duplicate code, at the expenses of some potential short-term regressions in i18n.
Finally, note that much of /string seems to be taken from Wine's Unicode library (which a host "unicode" already exists in ReactOS' tools/. It may be better (for someone with more experience as to these wine-isms) to simply just pull-in whatever winelib files are not currently present in ReactOS, and have kernel32 and tools/unicode use winelib, instead of having 2 or 3 copies of the code.
svn path=/trunk/; revision=52754
2011-07-21 05:24:59 +00:00
|
|
|
|
|
|
|
extern HMODULE kernel32_handle;
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
struct format_args
|
2003-11-05 20:42:02 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
ULONG_PTR *args;
|
|
|
|
__ms_va_list *list;
|
|
|
|
int last;
|
|
|
|
};
|
2003-11-19 22:19:17 +00:00
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
/* Messages used by FormatMessage
|
2002-12-08 16:07:18 +00:00
|
|
|
*
|
|
|
|
* 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:
|
2009-12-03 18:57:57 +00:00
|
|
|
* 0: DWORD firstentry
|
|
|
|
* 4: DWORD lastentry
|
2002-12-08 16:07:18 +00:00
|
|
|
* 8: DWORD offset from start to the stringentries
|
|
|
|
*
|
|
|
|
* (lastentry-firstentry) * stringentry:
|
2009-12-03 18:57:57 +00:00
|
|
|
* 0: WORD len (0 marks end) [ includes the 4 byte header length ]
|
2002-12-08 16:07:18 +00:00
|
|
|
* 2: WORD flags
|
|
|
|
* 4: CHAR[len-4]
|
2009-12-03 18:57:57 +00:00
|
|
|
* (stringentry i of a subentry refers to the ID 'firstentry+i')
|
2002-12-08 16:07:18 +00:00
|
|
|
*
|
|
|
|
* Yes, ANSI strings in win32 resources. Go figure.
|
|
|
|
*/
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
static const WCHAR FMTWSTR[] = { '%','s',0 };
|
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
/**********************************************************************
|
2010-07-08 15:20:46 +00:00
|
|
|
* load_message (internal)
|
2002-12-08 16:07:18 +00:00
|
|
|
*/
|
2010-07-08 15:20:46 +00:00
|
|
|
static LPWSTR load_message( HMODULE module, UINT id, WORD lang )
|
2002-12-08 16:07:18 +00:00
|
|
|
{
|
2013-12-22 18:10:41 +00:00
|
|
|
MESSAGE_RESOURCE_ENTRY *mre;
|
2009-11-16 01:37:12 +00:00
|
|
|
WCHAR *buffer;
|
2009-12-03 18:57:57 +00:00
|
|
|
NTSTATUS status;
|
2005-11-14 21:53:21 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("module = %p, id = %08x\n", module, id );
|
2005-11-14 21:53:21 +00:00
|
|
|
|
|
|
|
if (!module) module = GetModuleHandleW( NULL );
|
2009-12-03 18:57:57 +00:00
|
|
|
if ((status = RtlFindMessage( module, (ULONG)RT_MESSAGETABLE, lang, id, &mre )) != STATUS_SUCCESS)
|
2009-11-16 01:37:12 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
SetLastError( RtlNtStatusToDosError(status) );
|
2005-11-14 21:53:21 +00:00
|
|
|
return NULL;
|
2009-11-16 01:37:12 +00:00
|
|
|
}
|
2005-11-14 21:53:21 +00:00
|
|
|
|
|
|
|
if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
|
|
|
|
{
|
2009-11-16 01:37:12 +00:00
|
|
|
int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR);
|
2005-11-14 21:53:21 +00:00
|
|
|
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
|
2009-11-16 01:37:12 +00:00
|
|
|
memcpy( buffer, mre->Text, len );
|
2003-11-05 20:42:02 +00:00
|
|
|
}
|
2005-11-14 21:53:21 +00:00
|
|
|
else
|
|
|
|
{
|
2009-11-16 01:37:12 +00:00
|
|
|
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 );
|
2003-01-07 17:29:09 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("returning %s\n", wine_dbgstr_w(buffer));
|
2005-11-14 21:53:21 +00:00
|
|
|
return buffer;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
/**********************************************************************
|
|
|
|
* get_arg (internal)
|
|
|
|
*/
|
|
|
|
static ULONG_PTR get_arg( int nr, DWORD flags, struct format_args *args )
|
|
|
|
{
|
|
|
|
if (nr == -1) nr = args->last + 1;
|
|
|
|
if (args->list)
|
|
|
|
{
|
|
|
|
if (!args->args) args->args = HeapAlloc( GetProcessHeap(), 0, 99 * sizeof(ULONG_PTR) );
|
|
|
|
while (nr > args->last)
|
|
|
|
args->args[args->last++] = va_arg( *args->list, ULONG_PTR );
|
|
|
|
}
|
|
|
|
if (nr > args->last) args->last = nr;
|
|
|
|
return args->args[nr - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************
|
2010-07-08 15:20:46 +00:00
|
|
|
* format_insert (internal)
|
2009-12-03 18:57:57 +00:00
|
|
|
*/
|
2010-07-08 15:20:46 +00:00
|
|
|
static LPCWSTR format_insert( BOOL unicode_caller, int insert, LPCWSTR format,
|
|
|
|
DWORD flags, struct format_args *args,
|
|
|
|
LPWSTR *result )
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
static const WCHAR fmt_u[] = {'%','u',0};
|
2010-07-08 15:20:46 +00:00
|
|
|
WCHAR *wstring = NULL, *p, fmt[256];
|
2009-12-03 18:57:57 +00:00
|
|
|
ULONG_PTR arg;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
if (*format != '!') /* simple string */
|
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
arg = get_arg( insert, flags, args );
|
2012-08-03 06:38:44 +00:00
|
|
|
if (unicode_caller || !arg)
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2012-08-03 06:38:44 +00:00
|
|
|
static const WCHAR nullW[] = {'(','n','u','l','l',')',0};
|
|
|
|
const WCHAR *str = (const WCHAR *)arg;
|
|
|
|
|
|
|
|
if (!str) str = nullW;
|
2010-07-08 15:20:46 +00:00
|
|
|
*result = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) );
|
|
|
|
strcpyW( *result, str );
|
2009-12-03 18:57:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-08-03 06:38:44 +00:00
|
|
|
const char *str = (const char *)arg;
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD length = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
|
|
|
|
*result = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, str, -1, *result, length );
|
2009-12-03 18:57:57 +00:00
|
|
|
}
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
format++;
|
|
|
|
p = fmt;
|
|
|
|
*p++ = '%';
|
|
|
|
|
|
|
|
while (*format == '0' ||
|
|
|
|
*format == '+' ||
|
|
|
|
*format == '-' ||
|
|
|
|
*format == ' ' ||
|
|
|
|
*format == '*' ||
|
|
|
|
*format == '#')
|
|
|
|
{
|
|
|
|
if (*format == '*')
|
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
p += sprintfW( p, fmt_u, get_arg( insert, flags, args ));
|
2009-12-03 18:57:57 +00:00
|
|
|
insert = -1;
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
else *p++ = *format++;
|
|
|
|
}
|
|
|
|
while (isdigitW(*format)) *p++ = *format++;
|
|
|
|
|
|
|
|
if (*format == '.')
|
|
|
|
{
|
|
|
|
*p++ = *format++;
|
|
|
|
if (*format == '*')
|
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
p += sprintfW( p, fmt_u, get_arg( insert, flags, args ));
|
2009-12-03 18:57:57 +00:00
|
|
|
insert = -1;
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while (isdigitW(*format)) *p++ = *format++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* replicate MS bug: drop an argument when using va_list with width/precision */
|
|
|
|
if (insert == -1 && args->list) args->last--;
|
|
|
|
arg = get_arg( insert, flags, args );
|
|
|
|
|
|
|
|
/* check for ascii string format */
|
|
|
|
if ((format[0] == 'h' && format[1] == 's') ||
|
|
|
|
(format[0] == 'h' && format[1] == 'S') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(unicode_caller && format[0] == 'S') ||
|
|
|
|
(!unicode_caller && format[0] == 's'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD len = MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, NULL, 0 );
|
2009-12-03 18:57:57 +00:00
|
|
|
wstring = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, wstring, len );
|
|
|
|
arg = (ULONG_PTR)wstring;
|
|
|
|
*p++ = 's';
|
|
|
|
}
|
|
|
|
/* check for ascii character format */
|
|
|
|
else if ((format[0] == 'h' && format[1] == 'c') ||
|
|
|
|
(format[0] == 'h' && format[1] == 'C') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(unicode_caller && format[0] == 'C') ||
|
|
|
|
(!unicode_caller && format[0] == 'c'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
char ch = arg;
|
2009-12-03 18:57:57 +00:00
|
|
|
wstring = HeapAlloc( GetProcessHeap(), 0, 2 * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, &ch, 1, wstring, 1 );
|
|
|
|
wstring[1] = 0;
|
|
|
|
arg = (ULONG_PTR)wstring;
|
|
|
|
*p++ = 's';
|
|
|
|
}
|
|
|
|
/* check for wide string format */
|
|
|
|
else if ((format[0] == 'l' && format[1] == 's') ||
|
|
|
|
(format[0] == 'l' && format[1] == 'S') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(format[0] == 'w' && format[1] == 's') ||
|
|
|
|
(!unicode_caller && format[0] == 'S'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
|
|
|
*p++ = 's';
|
|
|
|
}
|
|
|
|
/* check for wide character format */
|
|
|
|
else if ((format[0] == 'l' && format[1] == 'c') ||
|
|
|
|
(format[0] == 'l' && format[1] == 'C') ||
|
2010-07-08 15:20:46 +00:00
|
|
|
(format[0] == 'w' && format[1] == 'c') ||
|
|
|
|
(!unicode_caller && format[0] == 'C'))
|
2009-12-03 18:57:57 +00:00
|
|
|
{
|
|
|
|
*p++ = 'c';
|
|
|
|
}
|
|
|
|
/* FIXME: handle I64 etc. */
|
|
|
|
else while (*format && *format != '!') *p++ = *format++;
|
|
|
|
|
|
|
|
*p = 0;
|
|
|
|
size = 256;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
WCHAR *ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) );
|
|
|
|
int needed = snprintfW( ret, size, fmt, arg );
|
|
|
|
if (needed == -1 || needed >= size)
|
|
|
|
{
|
|
|
|
HeapFree( GetProcessHeap(), 0, ret );
|
|
|
|
size = max( needed + 1, size * 2 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*result = ret;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*format && *format != '!') format++;
|
|
|
|
if (*format == '!') format++;
|
|
|
|
|
|
|
|
HeapFree( GetProcessHeap(), 0, wstring );
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
2014-02-17 21:51:48 +00:00
|
|
|
struct _format_message_data
|
|
|
|
{
|
|
|
|
LPWSTR formatted;
|
|
|
|
DWORD size;
|
|
|
|
LPWSTR t;
|
|
|
|
LPWSTR space;
|
|
|
|
BOOL inspace;
|
|
|
|
DWORD width, w;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void format_add_char(struct _format_message_data *fmd, WCHAR c)
|
|
|
|
{
|
|
|
|
*fmd->t++ = c;
|
|
|
|
if (fmd->width && fmd->width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
|
|
|
|
{
|
|
|
|
switch (c) {
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
fmd->space = NULL;
|
|
|
|
fmd->inspace = FALSE;
|
|
|
|
fmd->w = 0;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
if (!fmd->inspace)
|
|
|
|
fmd->space = fmd->t - 1;
|
|
|
|
fmd->inspace = TRUE;
|
|
|
|
fmd->w++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fmd->inspace = FALSE;
|
|
|
|
fmd->w++;
|
|
|
|
}
|
|
|
|
if (fmd->w == fmd->width) {
|
|
|
|
LPWSTR notspace;
|
|
|
|
if (fmd->space) {
|
|
|
|
notspace = fmd->space;
|
|
|
|
while (*notspace == ' ' && notspace != fmd->t)
|
|
|
|
notspace++;
|
|
|
|
} else
|
|
|
|
notspace = fmd->space = fmd->t;
|
|
|
|
fmd->w = fmd->t - notspace;
|
|
|
|
memmove(fmd->space+2, notspace, fmd->w * sizeof(*fmd->t));
|
|
|
|
*fmd->space++ = '\r';
|
|
|
|
*fmd->space++ = '\n';
|
|
|
|
fmd->t = fmd->space + fmd->w;
|
|
|
|
fmd->space = NULL;
|
|
|
|
fmd->inspace = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((DWORD)(fmd->t - fmd->formatted) == fmd->size) {
|
|
|
|
DWORD_PTR ispace = fmd->space - fmd->formatted;
|
|
|
|
/* Allocate two extra characters so we can insert a '\r\n' in
|
|
|
|
* the middle of a word.
|
|
|
|
*/
|
|
|
|
fmd->formatted = HeapReAlloc(GetProcessHeap(), 0, fmd->formatted, (fmd->size * 2 + 2) * sizeof(WCHAR));
|
|
|
|
fmd->t = fmd->formatted + fmd->size;
|
|
|
|
if (fmd->space)
|
|
|
|
fmd->space = fmd->formatted + ispace;
|
|
|
|
fmd->size *= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
/**********************************************************************
|
|
|
|
* format_message (internal)
|
|
|
|
*/
|
|
|
|
static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr,
|
|
|
|
struct format_args *format_args )
|
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
struct _format_message_data fmd;
|
2010-07-08 15:20:46 +00:00
|
|
|
LPCWSTR f;
|
|
|
|
BOOL eos = FALSE;
|
|
|
|
|
2014-02-17 21:51:48 +00:00
|
|
|
fmd.size = 100;
|
|
|
|
fmd.formatted = fmd.t = HeapAlloc( GetProcessHeap(), 0, (fmd.size + 2) * sizeof(WCHAR) );
|
2010-07-08 15:20:46 +00:00
|
|
|
|
2014-02-17 21:51:48 +00:00
|
|
|
fmd.width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
|
|
|
fmd.w = 0;
|
|
|
|
fmd.inspace = FALSE;
|
|
|
|
fmd.space = NULL;
|
2010-07-08 15:20:46 +00:00
|
|
|
f = fmtstr;
|
|
|
|
while (*f && !eos) {
|
|
|
|
if (*f=='%') {
|
|
|
|
int insertnr;
|
|
|
|
WCHAR *str,*x;
|
|
|
|
|
|
|
|
f++;
|
|
|
|
switch (*f) {
|
|
|
|
case '1':case '2':case '3':case '4':case '5':
|
|
|
|
case '6':case '7':case '8':case '9':
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)
|
|
|
|
goto ignore_inserts;
|
|
|
|
else if (((dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) && !format_args->args) ||
|
|
|
|
(!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) && !format_args->list))
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
2014-02-17 21:51:48 +00:00
|
|
|
HeapFree(GetProcessHeap(), 0, fmd.formatted);
|
2010-07-08 15:20:46 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
f = format_insert( unicode_caller, insertnr, f, dwFlags, format_args, &str );
|
2014-02-17 21:51:48 +00:00
|
|
|
for (x = str; *x; x++) format_add_char(&fmd, *x);
|
2010-07-08 15:20:46 +00:00
|
|
|
HeapFree( GetProcessHeap(), 0, str );
|
|
|
|
break;
|
|
|
|
case 'n':
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, '\r');
|
|
|
|
format_add_char(&fmd, '\n');
|
2010-07-08 15:20:46 +00:00
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case 'r':
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, '\r');
|
2010-07-08 15:20:46 +00:00
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case 't':
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, '\t');
|
2010-07-08 15:20:46 +00:00
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
eos = TRUE;
|
|
|
|
f++;
|
|
|
|
break;
|
|
|
|
case '\0':
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
2014-02-17 21:51:48 +00:00
|
|
|
HeapFree(GetProcessHeap(), 0, fmd.formatted);
|
2010-07-08 15:20:46 +00:00
|
|
|
return NULL;
|
|
|
|
ignore_inserts:
|
|
|
|
default:
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, '%');
|
|
|
|
format_add_char(&fmd, *f++);
|
2010-07-08 15:20:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2014-02-17 21:51:48 +00:00
|
|
|
WCHAR ch = *f;
|
2010-07-08 15:20:46 +00:00
|
|
|
f++;
|
|
|
|
if (ch == '\r') {
|
|
|
|
if (*f == '\n')
|
|
|
|
f++;
|
2014-02-17 21:51:48 +00:00
|
|
|
if(fmd.width)
|
|
|
|
format_add_char(&fmd, ' ');
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, '\r');
|
|
|
|
format_add_char(&fmd, '\n');
|
2010-07-08 15:20:46 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ch == '\n')
|
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
if(fmd.width)
|
|
|
|
format_add_char(&fmd, ' ');
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, '\r');
|
|
|
|
format_add_char(&fmd, '\n');
|
2010-07-08 15:20:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2014-02-17 21:51:48 +00:00
|
|
|
format_add_char(&fmd, ch);
|
2010-07-08 15:20:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-17 21:51:48 +00:00
|
|
|
*fmd.t = '\0';
|
2010-07-08 15:20:46 +00:00
|
|
|
|
2014-02-17 21:51:48 +00:00
|
|
|
return fmd.formatted;
|
2010-07-08 15:20:46 +00:00
|
|
|
}
|
2009-12-03 18:57:57 +00:00
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* FormatMessageA (KERNEL32.@)
|
|
|
|
*/
|
|
|
|
DWORD WINAPI FormatMessageA(
|
2005-11-14 21:53:21 +00:00
|
|
|
DWORD dwFlags,
|
|
|
|
LPCVOID lpSource,
|
|
|
|
DWORD dwMessageId,
|
|
|
|
DWORD dwLanguageId,
|
|
|
|
LPSTR lpBuffer,
|
|
|
|
DWORD nSize,
|
2009-12-03 18:57:57 +00:00
|
|
|
__ms_va_list* args )
|
2002-12-08 16:07:18 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
struct format_args format_args;
|
|
|
|
DWORD ret = 0;
|
2010-07-08 15:20:46 +00:00
|
|
|
LPWSTR target;
|
|
|
|
DWORD destlength;
|
|
|
|
LPWSTR from;
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
|
2005-11-14 21:53:21 +00:00
|
|
|
dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
2008-09-13 08:48:18 +00:00
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
if (!lpBuffer)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*(LPSTR *)lpBuffer = NULL;
|
2008-09-13 08:48:18 +00:00
|
|
|
}
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
|
|
|
|
{
|
|
|
|
format_args.args = (ULONG_PTR *)args;
|
|
|
|
format_args.list = NULL;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format_args.args = NULL;
|
|
|
|
format_args.list = args;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
from = NULL;
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
|
|
|
|
{
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD length = MultiByteToWideChar(CP_ACP, 0, lpSource, -1, NULL, 0);
|
|
|
|
from = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, lpSource, -1, from, length);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else if (dwFlags & (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM))
|
|
|
|
{
|
2002-12-08 16:07:18 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
|
2014-02-17 21:51:48 +00:00
|
|
|
from = load_message( (HMODULE)lpSource, dwMessageId, dwLanguageId );
|
2005-11-14 21:53:21 +00:00
|
|
|
if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
|
2014-02-17 21:51:48 +00:00
|
|
|
from = load_message( kernel32_handle, dwMessageId, dwLanguageId );
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!from) return 0;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
target = format_message( FALSE, dwFlags, from, &format_args );
|
|
|
|
if (!target)
|
|
|
|
goto failure;
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("-- %s\n", debugstr_w(target));
|
|
|
|
|
|
|
|
/* Only try writing to an output buffer if there are processed characters
|
|
|
|
* in the temporary output buffer. */
|
|
|
|
if (*target)
|
|
|
|
{
|
|
|
|
destlength = WideCharToMultiByte(CP_ACP, 0, target, -1, NULL, 0, NULL, NULL);
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
|
|
|
{
|
|
|
|
LPSTR buf = LocalAlloc(LMEM_ZEROINIT, max(nSize, destlength));
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, target, -1, buf, destlength, NULL, NULL);
|
|
|
|
*((LPSTR*)lpBuffer) = buf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (nSize < destlength)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
goto failure;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
WideCharToMultiByte(CP_ACP, 0, target, -1, lpBuffer, destlength, NULL, NULL);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
ret = destlength - 1; /* null terminator */
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
|
|
|
|
failure:
|
2005-11-14 21:53:21 +00:00
|
|
|
HeapFree(GetProcessHeap(),0,target);
|
|
|
|
HeapFree(GetProcessHeap(),0,from);
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("-- returning %u\n", ret);
|
2009-12-03 18:57:57 +00:00
|
|
|
return ret;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2009-12-03 18:57:57 +00:00
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* FormatMessageW (KERNEL32.@)
|
|
|
|
*/
|
|
|
|
DWORD WINAPI FormatMessageW(
|
2005-11-14 21:53:21 +00:00
|
|
|
DWORD dwFlags,
|
|
|
|
LPCVOID lpSource,
|
|
|
|
DWORD dwMessageId,
|
|
|
|
DWORD dwLanguageId,
|
|
|
|
LPWSTR lpBuffer,
|
|
|
|
DWORD nSize,
|
2009-12-03 18:57:57 +00:00
|
|
|
__ms_va_list* args )
|
2002-12-08 16:07:18 +00:00
|
|
|
{
|
2009-12-03 18:57:57 +00:00
|
|
|
struct format_args format_args;
|
2010-07-08 15:20:46 +00:00
|
|
|
DWORD ret = 0;
|
|
|
|
LPWSTR target;
|
2009-12-03 18:57:57 +00:00
|
|
|
DWORD talloced;
|
|
|
|
LPWSTR from;
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
|
2005-11-14 21:53:21 +00:00
|
|
|
dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2008-09-13 08:48:18 +00:00
|
|
|
if (!lpBuffer)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
|
|
|
*(LPWSTR *)lpBuffer = NULL;
|
|
|
|
|
2009-12-03 18:57:57 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
|
|
|
|
{
|
|
|
|
format_args.args = (ULONG_PTR *)args;
|
|
|
|
format_args.list = NULL;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format_args.args = NULL;
|
|
|
|
format_args.list = args;
|
|
|
|
format_args.last = 0;
|
|
|
|
}
|
|
|
|
|
2002-12-08 16:07:18 +00:00
|
|
|
from = NULL;
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
|
2009-12-03 18:57:57 +00:00
|
|
|
from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
|
2005-11-14 21:53:21 +00:00
|
|
|
sizeof(WCHAR) );
|
2009-12-03 18:57:57 +00:00
|
|
|
strcpyW( from, lpSource );
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else if (dwFlags & (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM))
|
|
|
|
{
|
2002-12-08 16:07:18 +00:00
|
|
|
if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
|
2014-02-17 21:51:48 +00:00
|
|
|
from = load_message( (HMODULE)lpSource, dwMessageId, dwLanguageId );
|
2005-11-14 21:53:21 +00:00
|
|
|
if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
|
2014-02-17 21:51:48 +00:00
|
|
|
from = load_message( kernel32_handle, dwMessageId, dwLanguageId );
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!from) return 0;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
target = format_message( TRUE, dwFlags, from, &format_args );
|
|
|
|
if (!target)
|
|
|
|
goto failure;
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
talloced = strlenW(target)+1;
|
|
|
|
TRACE("-- %s\n",debugstr_w(target));
|
2002-12-08 16:07:18 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
/* Only allocate a buffer if there are processed characters in the
|
|
|
|
* temporary output buffer. If a caller supplies the buffer, then
|
|
|
|
* a null terminator will be written to it. */
|
|
|
|
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
|
|
|
|
{
|
|
|
|
if (*target)
|
|
|
|
{
|
|
|
|
/* nSize is the MINIMUM size */
|
|
|
|
*((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT, max(nSize, talloced)*sizeof(WCHAR));
|
|
|
|
strcpyW(*(LPWSTR*)lpBuffer, target);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
|
|
|
}
|
2010-07-08 15:20:46 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (nSize < talloced)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
strcpyW(lpBuffer, target);
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|
2005-11-14 21:53:21 +00:00
|
|
|
|
2010-07-08 15:20:46 +00:00
|
|
|
ret = talloced - 1; /* null terminator */
|
|
|
|
failure:
|
2005-11-14 21:53:21 +00:00
|
|
|
HeapFree(GetProcessHeap(),0,target);
|
|
|
|
HeapFree(GetProcessHeap(),0,from);
|
2009-12-03 18:57:57 +00:00
|
|
|
if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
|
2010-07-08 15:20:46 +00:00
|
|
|
TRACE("-- returning %u\n", ret);
|
|
|
|
return ret;
|
2002-12-08 16:07:18 +00:00
|
|
|
}
|