diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 46597317f55..86739fa862c 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -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) diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 4c8acab15db..a0ef13c3869 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -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) diff --git a/dlls/ntdll/resource.c b/dlls/ntdll/resource.c index 9ebf374b444..13b68128081 100644 --- a/dlls/ntdll/resource.c +++ b/dlls/ntdll/resource.c @@ -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; -} diff --git a/dlls/ntdll/rtlstr.c b/dlls/ntdll/rtlstr.c index fc11aa46547..534e42e8e32 100644 --- a/dlls/ntdll/rtlstr.c +++ b/dlls/ntdll/rtlstr.c @@ -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; +} diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 76c4afb2818..88d1e402aac 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -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) diff --git a/include/winternl.h b/include/winternl.h index fec07412049..9df1f0bb800 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -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);