Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / ntdll / debugtools.c
1 /*
2  * Debugging functions
3  *
4  * Copyright 2000 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <ctype.h>
27
28 #include "wine/debug.h"
29 #include "wine/exception.h"
30 #include "wine/library.h"
31 #include "wine/unicode.h"
32 #include "thread.h"
33 #include "winbase.h"
34 #include "winnt.h"
35 #include "ntddk.h"
36 #include "msvcrt/excpt.h"
37
38 WINE_DECLARE_DEBUG_CHANNEL(tid);
39
40 /* ---------------------------------------------------------------------- */
41
42 struct debug_info
43 {
44     char *str_pos;       /* current position in strings buffer */
45     char *out_pos;       /* current position in output buffer */
46     char  strings[1024]; /* buffer for temporary strings */
47     char  output[1024];  /* current output line */
48 };
49
50 static struct debug_info initial_thread_info;  /* debug info for initial thread */
51
52 /* filter for page-fault exceptions */
53 static WINE_EXCEPTION_FILTER(page_fault)
54 {
55     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
56         return EXCEPTION_EXECUTE_HANDLER;
57     return EXCEPTION_CONTINUE_SEARCH;
58 }
59
60 /* get the debug info pointer for the current thread */
61 static inline struct debug_info *get_info(void)
62 {
63     struct debug_info *info = NtCurrentTeb()->debug_info;
64
65     if (!info) NtCurrentTeb()->debug_info = info = &initial_thread_info;
66     if (!info->str_pos)
67     {
68         info->str_pos = info->strings;
69         info->out_pos = info->output;
70     }
71     return info;
72 }
73
74 /* allocate some tmp space for a string */
75 static void *gimme1(int n)
76 {
77     struct debug_info *info = get_info();
78     char *res = info->str_pos;
79
80     if (res + n >= &info->strings[sizeof(info->strings)]) res = info->strings;
81     info->str_pos = res + n;
82     return res;
83 }
84
85 /* release extra space that we requested in gimme1() */
86 static inline void release( void *ptr )
87 {
88     struct debug_info *info = NtCurrentTeb()->debug_info;
89     info->str_pos = ptr;
90 }
91
92 /* put an ASCII string into the debug buffer */
93 inline static char *put_string_a( const char *src, int n )
94 {
95     char *dst, *res;
96
97     if (n == -1) n = strlen(src);
98     if (n < 0) n = 0;
99     else if (n > 200) n = 200;
100     dst = res = gimme1 (n * 4 + 6);
101     *dst++ = '"';
102     while (n-- > 0)
103     {
104         unsigned char c = *src++;
105         switch (c)
106         {
107         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
108         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
109         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
110         case '"': *dst++ = '\\'; *dst++ = '"'; break;
111         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
112         default:
113             if (c >= ' ' && c <= 126)
114                 *dst++ = c;
115             else
116             {
117                 *dst++ = '\\';
118                 *dst++ = '0' + ((c >> 6) & 7);
119                 *dst++ = '0' + ((c >> 3) & 7);
120                 *dst++ = '0' + ((c >> 0) & 7);
121             }
122         }
123     }
124     *dst++ = '"';
125     if (*src)
126     {
127         *dst++ = '.';
128         *dst++ = '.';
129         *dst++ = '.';
130     }
131     *dst++ = '\0';
132     release( dst );
133     return res;
134 }
135
136 /* put a Unicode string into the debug buffer */
137 inline static char *put_string_w( const WCHAR *src, int n )
138 {
139     char *dst, *res;
140
141     if (n == -1) n = strlenW(src);
142     if (n < 0) n = 0;
143     else if (n > 200) n = 200;
144     dst = res = gimme1 (n * 5 + 7);
145     *dst++ = 'L';
146     *dst++ = '"';
147     while (n-- > 0)
148     {
149         WCHAR c = *src++;
150         switch (c)
151         {
152         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
153         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
154         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
155         case '"': *dst++ = '\\'; *dst++ = '"'; break;
156         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
157         default:
158             if (c >= ' ' && c <= 126)
159                 *dst++ = c;
160             else
161             {
162                 *dst++ = '\\';
163                 sprintf(dst,"%04x",c);
164                 dst+=4;
165             }
166         }
167     }
168     *dst++ = '"';
169     if (*src)
170     {
171         *dst++ = '.';
172         *dst++ = '.';
173         *dst++ = '.';
174     }
175     *dst++ = '\0';
176     release( dst );
177     return res;
178 }
179
180 /***********************************************************************
181  *              NTDLL_dbgstr_an
182  */
183 static const char *NTDLL_dbgstr_an( const char *src, int n )
184 {
185     char *res, *old_pos;
186     struct debug_info *info = get_info();
187
188     if (!HIWORD(src))
189     {
190         if (!src) return "(null)";
191         res = gimme1(6);
192         sprintf(res, "#%04x", LOWORD(src) );
193         return res;
194     }
195     /* save current position to restore it on exception */
196     old_pos = info->str_pos;
197     __TRY
198     {
199         res = put_string_a( src, n );
200     }
201     __EXCEPT(page_fault)
202     {
203         release( old_pos );
204         return "(invalid)";
205     }
206     __ENDTRY
207     return res;
208 }
209
210 /***********************************************************************
211  *              NTDLL_dbgstr_wn
212  */
213 static const char *NTDLL_dbgstr_wn( const WCHAR *src, int n )
214 {
215     char *res, *old_pos;
216     struct debug_info *info = get_info();
217
218     if (!HIWORD(src))
219     {
220         if (!src) return "(null)";
221         res = gimme1(6);
222         sprintf(res, "#%04x", LOWORD(src) );
223         return res;
224     }
225
226     /* save current position to restore it on exception */
227     old_pos = info->str_pos;
228     __TRY
229     {
230         res = put_string_w( src, n );
231     }
232     __EXCEPT(page_fault)
233     {
234         release( old_pos );
235         return "(invalid)";
236     }
237     __ENDTRY
238      return res;
239 }
240
241 /***********************************************************************
242  *              NTDLL_dbgstr_guid
243  */
244 static const char *NTDLL_dbgstr_guid( const GUID *id )
245 {
246     char *str;
247
248     if (!id) return "(null)";
249     if (!HIWORD(id))
250     {
251         str = gimme1(12);
252         sprintf( str, "<guid-0x%04x>", LOWORD(id) );
253     }
254     else
255     {
256         str = gimme1(40);
257         sprintf( str, "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
258                  id->Data1, id->Data2, id->Data3,
259                  id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
260                  id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
261     }
262     return str;
263 }
264
265 /***********************************************************************
266  *              NTDLL_dbg_vprintf
267  */
268 static int NTDLL_dbg_vprintf( const char *format, va_list args )
269 {
270     struct debug_info *info = get_info();
271     char *p;
272
273     int ret = vsnprintf( info->out_pos, sizeof(info->output) - (info->out_pos - info->output),
274                          format, args );
275
276     /* make sure we didn't exceed the buffer length
277      * the two checks are due to glibc changes in vsnprintfs return value
278      * the buffer size can be exceeded in case of a missing \n in
279      * debug output */
280     if ((ret == -1) || (ret >= sizeof(info->output) - (info->out_pos - info->output)))
281     {
282        fprintf( stderr, "wine_dbg_vprintf: debugstr buffer overflow (contents: '%s')\n",
283                 info->output);
284        info->out_pos = info->output;
285        abort();
286     }
287
288     p = strrchr( info->out_pos, '\n' );
289     if (!p) info->out_pos += ret;
290     else
291     {
292         char *pos = info->output;
293         p++;
294         write( 2, pos, p - pos );
295         /* move beginning of next line to start of buffer */
296         while ((*pos = *p++)) pos++;
297         info->out_pos = pos;
298     }
299     return ret;
300 }
301
302 /***********************************************************************
303  *              NTDLL_dbg_vlog
304  */
305 static int NTDLL_dbg_vlog( int cls, const char *channel,
306                            const char *function, const char *format, va_list args )
307 {
308     static const char *classes[] = { "fixme", "err", "warn", "trace" };
309     int ret = 0;
310
311     if (TRACE_ON(tid))
312         ret = wine_dbg_printf( "%08lx:", (DWORD)NtCurrentTeb()->tid );
313     if (cls < sizeof(classes)/sizeof(classes[0]))
314         ret += wine_dbg_printf( "%s:%s:%s ", classes[cls], channel + 1, function );
315     if (format)
316         ret += NTDLL_dbg_vprintf( format, args );
317     return ret;
318 }
319
320 /***********************************************************************
321  *              debug_init
322  */
323 DECL_GLOBAL_CONSTRUCTOR(debug_init)
324 {
325     __wine_dbgstr_an   = NTDLL_dbgstr_an;
326     __wine_dbgstr_wn   = NTDLL_dbgstr_wn;
327     __wine_dbgstr_guid = NTDLL_dbgstr_guid;
328     __wine_dbg_vprintf = NTDLL_dbg_vprintf;
329     __wine_dbg_vlog    = NTDLL_dbg_vlog;
330 }