PA-PK-AS-REP-Win2k ::= PaPkAsRep
[metze/wireshark/wip.git] / epan / except.c
1 /*
2  * Portable Exception Handling for ANSI C.
3  * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
4  *
5  * Free Software License:
6  *
7  * All rights are reserved by the author, with the following exceptions:
8  * Permission is granted to freely reproduce and distribute this software,
9  * possibly in exchange for a fee, provided that this copyright notice appears
10  * intact. Permission is also granted to adapt this software to produce
11  * derivative works, as long as the modified versions carry this copyright
12  * notice and additional notices stating that the work has been modified.
13  * This source code may be translated into executable form and incorporated
14  * into proprietary software; there is no requirement for such software to
15  * contain a copyright notice related to this source.
16  */
17
18 /*
19  * Modified to support throwing an exception with a null message pointer,
20  * and to have the message not be const (as we generate messages with
21  * "g_strdup_sprintf()", which means they need to be freed; using
22  * a null message means that we don't have to use a special string
23  * for exceptions with no message, and don't have to worry about
24  * not freeing that).
25  */
26
27 #include "config.h"
28
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <limits.h>
34
35 #include <glib.h>
36
37 #include "except.h"
38 #ifdef KAZLIB_TEST_MAIN
39 #include <wsutil/ws_printf.h> /* ws_debug_printf */
40 #endif
41
42 #ifdef _WIN32
43 #include <windows.h>
44 #include "exceptions.h"
45 #endif
46
47 #define XCEPT_BUFFER_SIZE 1024
48
49 #ifdef KAZLIB_POSIX_THREADS
50
51 #include <pthread.h>
52
53 static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER;
54 static int init_counter;
55 static pthread_key_t top_key;
56 static pthread_key_t uh_key;
57 static pthread_key_t alloc_key;
58 static pthread_key_t dealloc_key;
59 static void unhandled_catcher(except_t *);
60
61 #define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
62 #define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
63 #define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
64 #define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
65 #define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
66
67 static void (*get_catcher(void))(except_t *)
68 {
69     void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
70     return (catcher == 0) ? unhandled_catcher : catcher;
71 }
72
73 static void *(*get_alloc(void))(size_t)
74 {
75     void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
76     return (alloc == 0) ? malloc : alloc;
77 }
78
79 static void (*get_dealloc(void))(void *)
80 {
81     void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
82     return (dealloc == 0) ? free : dealloc;
83 }
84
85 int except_init(void)
86 {
87     int retval = 1;
88
89     pthread_mutex_lock(&init_mtx);
90
91     assert (init_counter < INT_MAX);
92
93     if (init_counter++ == 0) {
94         int top_ok = (pthread_key_create(&top_key, 0) == 0);
95         int uh_ok = (pthread_key_create(&uh_key, 0) == 0);
96         int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0);
97         int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0);
98
99         if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
100             retval = 0;
101             init_counter = 0;
102             if (top_ok)
103                 pthread_key_delete(top_key);
104             if (uh_ok)
105                 pthread_key_delete(uh_key);
106             if (alloc_ok)
107                 pthread_key_delete(alloc_key);
108             if (dealloc_ok)
109                 pthread_key_delete(dealloc_key);
110         }
111     }
112
113     pthread_mutex_unlock(&init_mtx);
114
115     return retval;
116 }
117
118 void except_deinit(void)
119 {
120     pthread_mutex_lock(&init_mtx);
121
122     assert (init_counter > 0);
123
124     if (--init_counter == 0) {
125         pthread_key_delete(top_key);
126         pthread_key_delete(uh_key);
127         pthread_key_delete(alloc_key);
128         pthread_key_delete(dealloc_key);
129     }
130
131     pthread_mutex_unlock(&init_mtx);
132 }
133
134 #else /* no thread support */
135
136 static int init_counter;
137 static void unhandled_catcher(except_t *);
138 static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
139 /* We need this 'size_t' cast due to a glitch in GLib where g_malloc was prototyped
140  * as 'gpointer g_malloc (gulong n_bytes)'. This was later fixed to the correct prototype
141  * 'gpointer g_malloc (gsize n_bytes)'. In Wireshark we use the latter prototype
142  * throughout the code. We can get away with this even with older versions of GLib by
143  * adding a '(void *(*)(size_t))' cast whenever we refer to g_malloc. The only platform
144  * supported by Wireshark where this isn't safe (sizeof size_t != sizeof gulong) is Win64.
145  * However, we _always_ bundle the newest version of GLib on this platform so
146  * the size_t issue doesn't exists here. Pheew.. */
147 static void *(*allocator)(size_t) = (void *(*)(size_t)) g_malloc;
148 static void (*deallocator)(void *) = g_free;
149 static struct except_stacknode *stack_top;
150
151 #define get_top() (stack_top)
152 #define set_top(T) (stack_top = (T))
153 #define get_catcher() (uh_catcher_ptr)
154 #define set_catcher(C) (uh_catcher_ptr = (C))
155 #define get_alloc() (allocator)
156 #define set_alloc(A) (allocator = (A))
157 #define get_dealloc() (deallocator)
158 #define set_dealloc(D) (deallocator = (D))
159
160 int except_init(void)
161 {
162     assert (init_counter < INT_MAX);
163     init_counter++;
164     return 1;
165 }
166
167 void except_deinit(void)
168 {
169     assert (init_counter > 0);
170     init_counter--;
171 }
172
173 #endif
174
175
176 static int match(const volatile except_id_t *thrown, const except_id_t *caught)
177 {
178     int group_match = (caught->except_group == XCEPT_GROUP_ANY ||
179         caught->except_group == thrown->except_group);
180     int code_match = (caught->except_code == XCEPT_CODE_ANY ||
181         caught->except_code == thrown->except_code);
182
183     return group_match && code_match;
184 }
185
186 WS_NORETURN static void do_throw(except_t *except)
187 {
188     struct except_stacknode *top;
189
190     assert (except->except_id.except_group != 0 &&
191         except->except_id.except_code != 0);
192
193     for (top = get_top(); top != 0; top = top->except_down) {
194         if (top->except_type == XCEPT_CLEANUP) {
195             top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context);
196         } else {
197             struct except_catch *catcher = top->except_info.except_catcher;
198             const except_id_t *pi = catcher->except_id;
199             size_t i;
200
201             assert (top->except_type == XCEPT_CATCHER);
202             except_free(catcher->except_obj.except_dyndata);
203
204             for (i = 0; i < catcher->except_size; pi++, i++) {
205                 if (match(&except->except_id, pi)) {
206                     catcher->except_obj = *except;
207                     set_top(top);
208                     longjmp(catcher->except_jmp, 1);
209                 }
210             }
211         }
212     }
213
214     set_top(top);
215     get_catcher()(except); /* unhandled exception */
216     abort();
217 }
218
219 static void unhandled_catcher(except_t *except)
220 {
221     if (except->except_message == NULL) {
222         fprintf(stderr, "Unhandled exception (group=%lu, code=%lu)\n",
223                 except->except_id.except_group,
224                 except->except_id.except_code);
225     } else {
226         fprintf(stderr, "Unhandled exception (\"%s\", group=%lu, code=%lu)\n",
227                 except->except_message, except->except_id.except_group,
228                 except->except_id.except_code);
229     }
230     abort();
231 }
232
233 static void stack_push(struct except_stacknode *node)
234 {
235     node->except_down = get_top();
236     set_top(node);
237 }
238
239 void except_setup_clean(struct except_stacknode *esn,
240         struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
241 {
242     esn->except_type = XCEPT_CLEANUP;
243     ecl->except_func = cleanf;
244     ecl->except_context = context;
245     esn->except_info.except_cleanup = ecl;
246     stack_push(esn);
247 }
248
249 void except_setup_try(struct except_stacknode *esn,
250         struct except_catch *ech, const except_id_t id[], size_t size)
251 {
252    ech->except_id = id;
253    ech->except_size = size;
254    ech->except_obj.except_dyndata = 0;
255    esn->except_type = XCEPT_CATCHER;
256    esn->except_info.except_catcher = ech;
257    stack_push(esn);
258 }
259
260 struct except_stacknode *except_pop(void)
261 {
262     struct except_stacknode *top = get_top();
263     assert (top->except_type == XCEPT_CLEANUP || top->except_type == XCEPT_CATCHER);
264     set_top(top->except_down);
265     return top;
266 }
267
268 WS_NORETURN void except_rethrow(except_t *except)
269 {
270     struct except_stacknode *top = get_top();
271     assert (top != 0);
272     assert (top->except_type == XCEPT_CATCHER);
273     assert (&top->except_info.except_catcher->except_obj == except);
274     set_top(top->except_down);
275     do_throw(except);
276 }
277
278 WS_NORETURN void except_throw(long group, long code, const char *msg)
279 {
280     except_t except;
281
282     except.except_id.except_group = group;
283     except.except_id.except_code = code;
284     except.except_message = msg;
285     except.except_dyndata = 0;
286
287 #ifdef _WIN32
288     if (code == DissectorError && IsDebuggerPresent()) {
289         DebugBreak();
290     }
291 #endif
292
293     do_throw(&except);
294 }
295
296 WS_NORETURN void except_throwd(long group, long code, const char *msg, void *data)
297 {
298     except_t except;
299
300     except.except_id.except_group = group;
301     except.except_id.except_code = code;
302     except.except_message = msg;
303     except.except_dyndata = data;
304
305     do_throw(&except);
306 }
307
308 /*
309  * XXX - should we use g_strdup_sprintf() here, so we're not limited by
310  * XCEPT_BUFFER_SIZE?  We could then just use this to generate formatted
311  * messages.
312  */
313 WS_NORETURN void except_vthrowf(long group, long code, const char *fmt,
314                                 va_list vl)
315 {
316     char *buf = (char *)except_alloc(XCEPT_BUFFER_SIZE);
317
318     g_vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl);
319     except_throwd(group, code, buf, buf);
320 }
321
322 WS_NORETURN void except_throwf(long group, long code, const char *fmt, ...)
323 {
324     va_list vl;
325
326     va_start (vl, fmt);
327     except_vthrowf(group, code, fmt, vl);
328     va_end (vl);
329 }
330
331 void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
332 {
333     void (*old_catcher)(except_t *) = get_catcher();
334     set_catcher(new_catcher);
335     return old_catcher;
336 }
337
338 #undef except_code
339 #undef except_group
340 #undef except_message
341 #undef except_data
342
343 unsigned long except_code(except_t *ex)
344 {
345     return ex->except_id.except_code;
346 }
347
348 unsigned long except_group(except_t *ex)
349 {
350     return ex->except_id.except_group;
351 }
352
353 const char *except_message(except_t *ex)
354 {
355     return ex->except_message;
356 }
357
358 void *except_data(except_t *ex)
359 {
360     return ex->except_dyndata;
361 }
362
363 void *except_take_data(except_t *ex)
364 {
365     void *data = ex->except_dyndata;
366     ex->except_dyndata = 0;
367     return data;
368 }
369
370 void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
371 {
372     set_alloc(alloc);
373     set_dealloc(dealloc);
374 }
375
376 void *except_alloc(size_t size)
377 {
378     void *ptr = get_alloc()(size);
379
380     if (ptr == 0)
381         except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
382     return ptr;
383 }
384
385 void except_free(void *ptr)
386 {
387     get_dealloc()(ptr);
388 }
389
390 #ifdef KAZLIB_TEST_MAIN
391
392
393 static void cleanup(void *arg)
394 {
395     ws_debug_printf("cleanup(\"%s\") called\n", (char *) arg);
396 }
397
398 static void bottom_level(void)
399 {
400     char buf[256];
401     ws_debug_printf("throw exception? "); fflush(stdout);
402     fgets(buf, sizeof buf, stdin);
403
404     if (buf[0] >= 0 && (buf[0] == 'Y' || buf[0] == 'y'))
405         except_throw(1, 1, "nasty exception");
406 }
407
408 static void top_level(void)
409 {
410     except_cleanup_push(cleanup, "argument");
411     bottom_level();
412     except_cleanup_pop(0);
413 }
414
415 int main(int argc, char **argv)
416 {
417     static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
418     except_t *ex;
419     char *msg;
420
421     /*
422      * Nested exception ``try blocks''
423      */
424
425     /* outer */
426     except_try_push(catch, 2, &ex);
427     if (!ex) {
428         /* inner */
429         except_try_push(catch, 2, &ex);
430         if (!ex) {
431             top_level();
432         } else {
433             /* inner catch */
434             msg = except_message(ex);
435             if (msg == NULL) {
436                 ws_debug_printf("caught exception (inner): s=%lu, c=%lu\n",
437                        except_group(ex), except_code(ex));
438             } else {
439                 ws_debug_printf("caught exception (inner): \"%s\", s=%lu, c=%lu\n",
440                        msg, except_group(ex), except_code(ex));
441             }
442             except_rethrow(ex);
443         }
444         except_try_pop();
445     } else {
446         /* outer catch */
447         msg = except_message(ex);
448         if (msg == NULL) {
449             ws_debug_printf("caught exception (outer): s=%lu, c=%lu\n",
450                    except_group(ex), except_code(ex));
451         } else {
452             ws_debug_printf("caught exception (outer): \"%s\", s=%lu, c=%lu\n",
453                    except_message(ex), except_group(ex), except_code(ex));
454         }
455     }
456     except_try_pop();
457     except_throw(99, 99, "exception in main");
458     return 0;
459 }
460
461
462 #endif
463
464 /*
465  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
466  *
467  * Local variables:
468  * c-basic-offset: 4
469  * tab-width: 8
470  * indent-tabs-mode: nil
471  * End:
472  *
473  * vi: set shiftwidth=4 tabstop=8 expandtab:
474  * :indentSize=4:tabSize=8:noTabs=true:
475  */