From: "Rémi Bernon" Subject: [PATCH 4/6] ntdll: Implement debugstr_a/w(n) format extensions. Message-Id: <20201121201913.1177092-4-rbernon@codeweavers.com> Date: Sat, 21 Nov 2020 21:19:11 +0100 In-Reply-To: <20201121201913.1177092-1-rbernon@codeweavers.com> References: <20201121201913.1177092-1-rbernon@codeweavers.com> This makes it possible to write TRACE("%p(astr)", "string") and get the equivalent of TRACE("%s", debugstr_a("string")), respectively with "%p(wstr)" and debugstr_w(...). The width format specifier (including '*') can be used to control the length (GCC format checker doesn't like precision specifier on %p). Note that the format extension is just here to illustrate, we could think of any other syntax. The main issue is to pass GCC format checker, and as done in the Linux kernel %p suffixes seems to be the best fit. Signed-off-by: Rémi Bernon --- dlls/ntdll/unix/debug.c | 96 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index 2b2d63b7adf..6c4b86196c8 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -289,6 +289,90 @@ int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_ return append_output( info, buffer, strlen( buffer )); } +static size_t sprintf_dbgstr_an( char *buffer, size_t length, const char *str, int n ) +{ + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + char *dst = buffer; + + if (!str) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + if (!((ULONG_PTR)str >> 16)) return snprintf( buffer, length, "#%04x", LOWORD(str) ); + if (n == -1) for (n = 0; str[n]; n++) ; + *dst++ = '"'; + while (n-- > 0 && dst <= buffer + length - 9) + { + unsigned char c = *str++; + switch (c) + { + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '"': *dst++ = '\\'; *dst++ = '"'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + default: + if (c < ' ' || c >= 127) + { + *dst++ = '\\'; + *dst++ = 'x'; + *dst++ = hex[(c >> 4) & 0x0f]; + *dst++ = hex[c & 0x0f]; + } + else *dst++ = c; + } + } + *dst++ = '"'; + if (n > 0) + { + *dst++ = '.'; + *dst++ = '.'; + *dst++ = '.'; + } + *dst = 0; + return dst - buffer; +} + +static size_t sprintf_dbgstr_wn( char *buffer, size_t length, const WCHAR *str, int n ) +{ + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + char *dst = buffer; + + if (!str) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + if (!((ULONG_PTR)str >> 16)) return snprintf( buffer, length, "#%04x", LOWORD(str) ); + if (n == -1) for (n = 0; str[n]; n++) ; + *dst++ = 'L'; + *dst++ = '"'; + while (n-- > 0 && dst <= buffer + length - 10) + { + WCHAR c = *str++; + switch (c) + { + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '"': *dst++ = '\\'; *dst++ = '"'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + default: + if (c < ' ' || c >= 127) + { + *dst++ = '\\'; + *dst++ = hex[(c >> 12) & 0x0f]; + *dst++ = hex[(c >> 8) & 0x0f]; + *dst++ = hex[(c >> 4) & 0x0f]; + *dst++ = hex[c & 0x0f]; + } + else *dst++ = (char)c; + } + } + *dst++ = '"'; + if (n > 0) + { + *dst++ = '.'; + *dst++ = '.'; + *dst++ = '.'; + } + *dst = 0; + return dst - buffer; +} + static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char *format, __ms_va_list args ) { char fmtbuf[1024]; @@ -439,7 +523,17 @@ static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char * } break; case 'p': - snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); + if (!strncmp( spec, "p(astr)", 7 )) /* debugstr_a / debugstr_an */ + { + append_checked( buf, end - buf, sprintf_dbgstr_an( buf, end - buf, va_arg( args, const char * ), w ) ); + snprintf_checked( buf, end - buf, spec + 7 ); + } + else if (!strncmp( spec, "p(wstr)", 7 )) /* debugstr_w / debugstr_wn */ + { + append_checked( buf, end - buf, sprintf_dbgstr_wn( buf, end - buf, va_arg( args, const WCHAR * ), w ) ); + snprintf_checked( buf, end - buf, spec + 7 ); + } + else snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); break; case 'A': case 'a': -- 2.29.2