ntdll: Implement RtlFormatMessage().

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-05-13 14:38:36 +02:00
parent b3a0a39356
commit 5bb9f86dbe
6 changed files with 252 additions and 31 deletions

View File

@ -667,7 +667,8 @@
@ stub RtlFlushPropertySet
# @ stub RtlFlushSecureMemoryCache
@ stdcall RtlFormatCurrentUserKeyPath(ptr)
@ stdcall RtlFormatMessage(ptr long long long long ptr ptr long)
@ stdcall RtlFormatMessage(ptr long long long long ptr ptr long ptr)
@ stdcall RtlFormatMessageEx(ptr long long long long ptr ptr long ptr long)
@ stdcall RtlFreeAnsiString(ptr)
@ stdcall RtlFreeHandle(ptr ptr)
@ stdcall RtlFreeHeap(long long ptr)

View File

@ -325,6 +325,7 @@ LPWSTR __cdecl NTDLL_wcstok( LPWSTR str, LPCWSTR delim );
LONG __cdecl NTDLL_wcstol( LPCWSTR s, LPWSTR *end, INT base );
ULONG __cdecl NTDLL_wcstoul( LPCWSTR s, LPWSTR *end, INT base );
int WINAPIV NTDLL_swprintf( WCHAR *str, const WCHAR *format, ... );
int WINAPIV _snwprintf_s( WCHAR *str, SIZE_T size, SIZE_T len, const WCHAR *format, ... );
#define wcsicmp(s1,s2) NTDLL__wcsicmp(s1,s2)
#define wcsnicmp(s1,s2,n) NTDLL__wcsnicmp(s1,s2,n)

View File

@ -419,31 +419,3 @@ NTSTATUS WINAPI RtlFindMessage( HMODULE hmod, ULONG type, ULONG lang,
}
return STATUS_MESSAGE_NOT_FOUND;
}
/**********************************************************************
* RtlFormatMessage (NTDLL.@)
*
* Formats a message (similar to sprintf).
*
* PARAMS
* Message [I] Message to format.
* MaxWidth [I] Maximum width in characters of each output line.
* IgnoreInserts [I] Whether to copy the message without processing inserts.
* Ansi [I] Whether Arguments may have ANSI strings.
* ArgumentsIsArray [I] Whether Arguments is actually an array rather than a va_list *.
* Buffer [O] Buffer to store processed message in.
* BufferSize [I] Size of Buffer (in bytes?).
*
* RETURNS
* NTSTATUS code.
*/
NTSTATUS WINAPI RtlFormatMessage( LPWSTR Message, UCHAR MaxWidth,
BOOLEAN IgnoreInserts, BOOLEAN Ansi,
BOOLEAN ArgumentIsArray, __ms_va_list * Arguments,
LPWSTR Buffer, ULONG BufferSize )
{
FIXME("(%s, %u, %s, %s, %s, %p, %p, %d)\n", debugstr_w(Message),
MaxWidth, IgnoreInserts ? "TRUE" : "FALSE", Ansi ? "TRUE" : "FALSE",
ArgumentIsArray ? "TRUE" : "FALSE", Arguments, Buffer, BufferSize);
return STATUS_SUCCESS;
}

View File

@ -1707,3 +1707,248 @@ NTSTATUS WINAPI RtlStringFromGUID(const GUID* guid, UNICODE_STRING *str)
return STATUS_SUCCESS;
}
/***********************************************************************
* Message formatting
***********************************************************************/
struct format_message_args
{
int last; /* last used arg */
ULONG_PTR *array; /* args array */
__ms_va_list *list; /* args va_list */
UINT64 arglist[102]; /* arguments fetched from va_list */
};
static NTSTATUS add_chars( WCHAR **buffer, WCHAR *end, const WCHAR *str, ULONG len )
{
if (len > end - *buffer) return STATUS_BUFFER_OVERFLOW;
memcpy( *buffer, str, len * sizeof(WCHAR) );
*buffer += len;
return STATUS_SUCCESS;
}
static UINT64 get_arg( int nr, struct format_message_args *args_data, BOOL is64 )
{
if (nr == -1) nr = args_data->last + 1;
while (nr > args_data->last)
args_data->arglist[args_data->last++] = is64 ? va_arg( *args_data->list, UINT64 )
: va_arg( *args_data->list, ULONG_PTR );
return args_data->arglist[nr - 1];
}
static NTSTATUS add_format( WCHAR **buffer, WCHAR *end, const WCHAR **src, int insert, BOOLEAN ansi,
struct format_message_args *args_data )
{
static const WCHAR modifiers[] = {'0','1','2','3','4','5','6','7','8','9',' ','+','-','*','#','.',0};
const WCHAR *format = *src;
WCHAR *p, fmt[32];
ULONG_PTR args[5] = { 0 };
BOOL is_64 = FALSE;
UINT64 val;
int len, stars = 0, nb_args = 0;
p = fmt;
*p++ = '%';
if (*format++ == '!')
{
const WCHAR *end = wcschr( format, '!' );
if (!end || end - format > ARRAY_SIZE(fmt) - 2) return STATUS_INVALID_PARAMETER;
*src = end + 1;
while (wcschr( modifiers, *format ))
{
if (*format == '*') stars++;
*p++ = *format++;
}
if (stars > 2) return STATUS_INVALID_PARAMETER;
switch (*format)
{
case 'c': case 'C':
case 's': case 'S':
if (ansi) *p++ = *format++ ^ ('s' - 'S');
break;
case 'I':
if (sizeof(void *) == sizeof(int) && format[1] == '6' && format[2] == '4') is_64 = TRUE;
break;
}
while (format != end) *p++ = *format++;
}
else *p++ = ansi ? 'S' : 's'; /* simple string */
*p = 0;
if (args_data->list)
{
get_arg( insert - 1, args_data, is_64 ); /* make sure previous args have been fetched */
while (stars--)
{
args[nb_args++] = get_arg( insert, args_data, FALSE );
insert = -1;
}
/* replicate MS bug: drop an argument when using va_list with width/precision */
if (insert == -1) args_data->last--;
val = get_arg( insert, args_data, is_64 );
args[nb_args++] = val;
args[nb_args] = val >> 32;
}
else if (args_data->array)
{
args[nb_args++] = args_data->array[insert - 1];
if (args_data->last < insert) args_data->last = insert;
/* replicate MS bug: first arg is considered 64-bit, even if it's actually width or precision */
if (is_64) nb_args++;
while (stars--) args[nb_args++] = args_data->array[args_data->last++];
}
else return STATUS_INVALID_PARAMETER;
len = _snwprintf_s( *buffer, end - *buffer, end - *buffer - 1, fmt,
args[0], args[1], args[2], args[3], args[4] );
if (len == -1) return STATUS_BUFFER_OVERFLOW;
*buffer += len;
return STATUS_SUCCESS;
}
/**********************************************************************
* RtlFormatMessage (NTDLL.@)
*/
NTSTATUS WINAPI RtlFormatMessage( const WCHAR *src, ULONG width, BOOLEAN ignore_inserts,
BOOLEAN ansi, BOOLEAN is_array, __ms_va_list *args,
WCHAR *buffer, ULONG size, ULONG *retsize )
{
return RtlFormatMessageEx( src, width, ignore_inserts, ansi, is_array, args, buffer, size, retsize, 0 );
}
/**********************************************************************
* RtlFormatMessageEx (NTDLL.@)
*/
NTSTATUS WINAPI RtlFormatMessageEx( const WCHAR *src, ULONG width, BOOLEAN ignore_inserts,
BOOLEAN ansi, BOOLEAN is_array, __ms_va_list *args,
WCHAR *buffer, ULONG size, ULONG *retsize, ULONG flags )
{
static const WCHAR emptyW = 0;
static const WCHAR spaceW = ' ';
static const WCHAR crW = '\r';
static const WCHAR tabW = '\t';
static const WCHAR crlfW[] = {'\r','\n'};
struct format_message_args args_data;
NTSTATUS status = STATUS_SUCCESS;
WCHAR *start = buffer; /* start of buffer */
WCHAR *end = buffer + size / sizeof(WCHAR); /* end of buffer */
WCHAR *line = buffer; /* start of last line */
WCHAR *space = NULL; /* last space */
if (flags) FIXME( "%s unknown flags %x\n", debugstr_w(src), flags );
args_data.last = 0;
args_data.array = is_array ? (ULONG_PTR *)args : NULL;
args_data.list = is_array ? NULL : args;
for ( ; *src; src++)
{
switch (*src)
{
case '\r':
if (src[1] == '\n') src++;
/* fall through */
case '\n':
if (!width)
{
status = add_chars( &buffer, end, crlfW, 2 );
line = buffer;
space = NULL;
break;
}
/* fall through */
case ' ':
space = buffer;
status = add_chars( &buffer, end, &spaceW, 1 );
break;
case '\t':
if (space == buffer - 1) space = buffer;
status = add_chars( &buffer, end, &tabW, 1 );
break;
case '%':
src++;
switch (*src)
{
case 0:
return STATUS_INVALID_PARAMETER;
case 't':
if (!width)
{
status = add_chars( &buffer, end, &tabW, 1 );
break;
}
/* fall through */
case 'n':
status = add_chars( &buffer, end, crlfW, 2 );
line = buffer;
space = NULL;
break;
case 'r':
status = add_chars( &buffer, end, &crW, 1 );
line = buffer;
space = NULL;
break;
case '0':
while (src[1]) src++;
break;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
if (!ignore_inserts)
{
int nr = *src++ - '0';
if (*src >= '0' && *src <= '9') nr = nr * 10 + *src++ - '0';
status = add_format( &buffer, end, &src, nr, ansi, &args_data );
src--;
break;
}
/* fall through */
default:
if (ignore_inserts) status = add_chars( &buffer, end, src - 1, 2 );
else status = add_chars( &buffer, end, src, 1 );
break;
}
break;
default:
status = add_chars( &buffer, end, src, 1 );
break;
}
if (status) return status;
if (width && buffer - line >= width)
{
LONG_PTR diff = 2;
WCHAR *next;
if (space) /* split line at the last space */
{
next = space + 1;
while (space > line && (space[-1] == ' ' || space[-1] == '\t')) space--;
diff -= next - space;
}
else space = next = buffer; /* split at the end of the buffer */
if (diff > 0 && end - buffer < diff) return STATUS_BUFFER_OVERFLOW;
memmove( space + 2, next, (buffer - next) * sizeof(WCHAR) );
buffer += diff;
memcpy( space, crlfW, sizeof(crlfW) );
line = space + 2;
space = NULL;
}
}
if ((status = add_chars( &buffer, end, &emptyW, 1 ))) return status;
*retsize = (buffer - start) * sizeof(WCHAR);
return STATUS_SUCCESS;
}

View File

@ -1069,7 +1069,8 @@
@ stub RtlFindUnicodePrefix
@ stdcall RtlFirstFreeAce(ptr ptr)
@ stdcall RtlFormatCurrentUserKeyPath(ptr)
@ stdcall RtlFormatMessage(ptr long long long long ptr ptr long)
@ stdcall RtlFormatMessage(ptr long long long long ptr ptr long ptr)
@ stdcall RtlFormatMessageEx(ptr long long long long ptr ptr long ptr long)
@ stdcall RtlFreeAnsiString(ptr)
@ stdcall RtlFreeHeap(long long ptr)
@ stdcall RtlFreeOemString(ptr)

View File

@ -2841,7 +2841,8 @@ NTSYSAPI ULONG WINAPI RtlFindSetBitsAndClear(PRTL_BITMAP,ULONG,ULONG);
NTSYSAPI ULONG WINAPI RtlFindSetRuns(PCRTL_BITMAP,PRTL_BITMAP_RUN,ULONG,BOOLEAN);
NTSYSAPI BOOLEAN WINAPI RtlFirstFreeAce(PACL,PACE_HEADER *);
NTSYSAPI NTSTATUS WINAPI RtlFormatCurrentUserKeyPath(PUNICODE_STRING);
NTSYSAPI NTSTATUS WINAPI RtlFormatMessage(LPWSTR,UCHAR,BOOLEAN,BOOLEAN,BOOLEAN,__ms_va_list *,LPWSTR,ULONG);
NTSYSAPI NTSTATUS WINAPI RtlFormatMessage(LPCWSTR,ULONG,BOOLEAN,BOOLEAN,BOOLEAN,__ms_va_list *,LPWSTR,ULONG,ULONG*);
NTSYSAPI NTSTATUS WINAPI RtlFormatMessageEx(LPCWSTR,ULONG,BOOLEAN,BOOLEAN,BOOLEAN,__ms_va_list *,LPWSTR,ULONG,ULONG*,ULONG);
NTSYSAPI void WINAPI RtlFreeAnsiString(PANSI_STRING);
NTSYSAPI BOOLEAN WINAPI RtlFreeHandle(RTL_HANDLE_TABLE *,RTL_HANDLE *);
NTSYSAPI BOOLEAN WINAPI RtlFreeHeap(HANDLE,ULONG,PVOID);