ntdll: Implement RtlFormatMessage().
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
b3a0a39356
commit
5bb9f86dbe
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue