Better implementation of GetCalendarInfo{A,W}, not perfect.
[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 "thread.h"
31 #include "winbase.h"
32 #include "winnt.h"
33 #include "ntddk.h"
34 #include "msvcrt/excpt.h"
35
36 WINE_DECLARE_DEBUG_CHANNEL(tid);
37
38 /* ---------------------------------------------------------------------- */
39
40 struct debug_info
41 {
42     char *str_pos;       /* current position in strings buffer */
43     char *out_pos;       /* current position in output buffer */
44     char  strings[1024]; /* buffer for temporary strings */
45     char  output[1024];  /* current output line */
46 };
47
48 static struct debug_info initial_thread_info;  /* debug info for initial thread */
49
50 /* filter for page-fault exceptions */
51 static WINE_EXCEPTION_FILTER(page_fault)
52 {
53     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
54         return EXCEPTION_EXECUTE_HANDLER;
55     return EXCEPTION_CONTINUE_SEARCH;
56 }
57
58 /* get the debug info pointer for the current thread */
59 static inline struct debug_info *get_info(void)
60 {
61     struct debug_info *info = NtCurrentTeb()->debug_info;
62
63     if (!info) NtCurrentTeb()->debug_info = info = &initial_thread_info;
64     if (!info->str_pos)
65     {
66         info->str_pos = info->strings;
67         info->out_pos = info->output;
68     }
69     return info;
70 }
71
72 /* allocate some tmp space for a string */
73 static void *gimme1(int n)
74 {
75     struct debug_info *info = get_info();
76     char *res = info->str_pos;
77
78     if (res + n >= &info->strings[sizeof(info->strings)]) res = info->strings;
79     info->str_pos = res + n;
80     return res;
81 }
82
83 /* release extra space that we requested in gimme1() */
84 static inline void release( void *ptr )
85 {
86     struct debug_info *info = NtCurrentTeb()->debug_info;
87     info->str_pos = ptr;
88 }
89
90 /* put an ASCII string into the debug buffer */
91 inline static char *put_string_a( const char *src, int n )
92 {
93     char *dst, *res;
94
95     if (n < 0) n = 0;
96     else if (n > 200) n = 200;
97     dst = res = gimme1 (n * 4 + 6);
98     *dst++ = '"';
99     while (n-- > 0 && *src)
100     {
101         unsigned char c = *src++;
102         switch (c)
103         {
104         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
105         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
106         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
107         case '"': *dst++ = '\\'; *dst++ = '"'; break;
108         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
109         default:
110             if (c >= ' ' && c <= 126)
111                 *dst++ = c;
112             else
113             {
114                 *dst++ = '\\';
115                 *dst++ = '0' + ((c >> 6) & 7);
116                 *dst++ = '0' + ((c >> 3) & 7);
117                 *dst++ = '0' + ((c >> 0) & 7);
118             }
119         }
120     }
121     *dst++ = '"';
122     if (*src)
123     {
124         *dst++ = '.';
125         *dst++ = '.';
126         *dst++ = '.';
127     }
128     *dst++ = '\0';
129     release( dst );
130     return res;
131 }
132
133 /* put a Unicode string into the debug buffer */
134 inline static char *put_string_w( const WCHAR *src, int n )
135 {
136     char *dst, *res;
137
138     if (n < 0) n = 0;
139     else if (n > 200) n = 200;
140     dst = res = gimme1 (n * 5 + 7);
141     *dst++ = 'L';
142     *dst++ = '"';
143     while (n-- > 0 && *src)
144     {
145         WCHAR c = *src++;
146         switch (c)
147         {
148         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
149         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
150         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
151         case '"': *dst++ = '\\'; *dst++ = '"'; break;
152         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
153         default:
154             if (c >= ' ' && c <= 126)
155                 *dst++ = c;
156             else
157             {
158                 *dst++ = '\\';
159                 sprintf(dst,"%04x",c);
160                 dst+=4;
161             }
162         }
163     }
164     *dst++ = '"';
165     if (*src)
166     {
167         *dst++ = '.';
168         *dst++ = '.';
169         *dst++ = '.';
170     }
171     *dst++ = '\0';
172     release( dst );
173     return res;
174 }
175
176 /***********************************************************************
177  *              wine_dbgstr_an (NTDLL.@)
178  */
179 const char *wine_dbgstr_an( const char *src, int n )
180 {
181     char *res, *old_pos;
182     struct debug_info *info = get_info();
183
184     if (!HIWORD(src))
185     {
186         if (!src) return "(null)";
187         res = gimme1(6);
188         sprintf(res, "#%04x", LOWORD(src) );
189         return res;
190     }
191     /* save current position to restore it on exception */
192     old_pos = info->str_pos;
193     __TRY
194     {
195         res = put_string_a( src, n );
196     }
197     __EXCEPT(page_fault)
198     {
199         release( old_pos );
200         return "(invalid)";
201     }
202     __ENDTRY
203     return res;
204 }
205
206 /***********************************************************************
207  *              wine_dbgstr_wn (NTDLL.@)
208  */
209 const char *wine_dbgstr_wn( const WCHAR *src, int n )
210 {
211     char *res, *old_pos;
212     struct debug_info *info = get_info();
213
214     if (!HIWORD(src))
215     {
216         if (!src) return "(null)";
217         res = gimme1(6);
218         sprintf(res, "#%04x", LOWORD(src) );
219         return res;
220     }
221
222     /* save current position to restore it on exception */
223     old_pos = info->str_pos;
224     __TRY
225     {
226         res = put_string_w( src, n );
227     }
228     __EXCEPT(page_fault)
229     {
230         release( old_pos );
231         return "(invalid)";
232     }
233     __ENDTRY
234      return res;
235 }
236
237 /***********************************************************************
238  *              wine_dbgstr_guid (NTDLL.@)
239  */
240 const char *wine_dbgstr_guid( const GUID *id )
241 {
242     char *str;
243
244     if (!id) return "(null)";
245     if (!HIWORD(id))
246     {
247         str = gimme1(12);
248         sprintf( str, "<guid-0x%04x>", LOWORD(id) );
249     }
250     else
251     {
252         str = gimme1(40);
253         sprintf( str, "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
254                  id->Data1, id->Data2, id->Data3,
255                  id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
256                  id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
257     }
258     return str;
259 }
260
261 /***********************************************************************
262  *              wine_dbg_vprintf (NTDLL.@)
263  */
264 int wine_dbg_vprintf( const char *format, va_list args )
265 {
266     struct debug_info *info = get_info();
267     char *p;
268
269     int ret = vsnprintf( info->out_pos, sizeof(info->output) - (info->out_pos - info->output),
270                          format, args );
271
272     /* make sure we didn't exceed the buffer length
273      * the two checks are due to glibc changes in vsnprintfs return value
274      * the buffer size can be exceeded in case of a missing \n in
275      * debug output */
276     if ((ret == -1) || (ret >= sizeof(info->output) - (info->out_pos - info->output)))
277     {
278        fprintf( stderr, "wine_dbg_vprintf: debugstr buffer overflow (contents: '%s')\n",
279                 info->output);
280        info->out_pos = info->output;
281        abort();
282     }
283
284     p = strrchr( info->out_pos, '\n' );
285     if (!p) info->out_pos += ret;
286     else
287     {
288         char *pos = info->output;
289         p++;
290         write( 2, pos, p - pos );
291         /* move beginning of next line to start of buffer */
292         while ((*pos = *p++)) pos++;
293         info->out_pos = pos;
294     }
295     return ret;
296 }
297
298 /***********************************************************************
299  *              wine_dbg_printf (NTDLL.@)
300  */
301 int wine_dbg_printf(const char *format, ...)
302 {
303     int ret;
304     va_list valist;
305
306     va_start(valist, format);
307     ret = wine_dbg_vprintf( format, valist );
308     va_end(valist);
309     return ret;
310 }
311
312 /***********************************************************************
313  *              wine_dbg_log (NTDLL.@)
314  */
315 int wine_dbg_log(enum __WINE_DEBUG_CLASS cls, const char *channel,
316                  const char *function, const char *format, ... )
317 {
318     static const char *classes[__WINE_DBCL_COUNT] = { "fixme", "err", "warn", "trace" };
319     va_list valist;
320     int ret = 0;
321
322     va_start(valist, format);
323     if (TRACE_ON(tid))
324         ret = wine_dbg_printf( "%08lx:", (DWORD)NtCurrentTeb()->tid );
325     if (cls < __WINE_DBCL_COUNT)
326         ret += wine_dbg_printf( "%s:%s:%s ", classes[cls], channel + 1, function );
327     if (format)
328         ret += wine_dbg_vprintf( format, valist );
329     va_end(valist);
330     return ret;
331 }