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