Re-apply r40728 and fix Coverity CID 1371 UNINIT again.
[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 #if _MSC_VER < 1500
45 /* IsDebuggerPresent() needs this #define! */
46 #define _WIN32_WINNT 0x0400
47 #endif
48 #include <windows.h>
49 #include "exceptions.h"
50 #endif
51
52 #define XCEPT_BUFFER_SIZE       1024
53
54 #ifdef KAZLIB_POSIX_THREADS
55
56 #include <pthread.h>
57
58 static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER;
59 static int init_counter;
60 static pthread_key_t top_key;
61 static pthread_key_t uh_key;
62 static pthread_key_t alloc_key;
63 static pthread_key_t dealloc_key;
64 static void unhandled_catcher(except_t *);
65
66 #define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
67 #define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
68 #define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
69 #define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
70 #define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
71
72 static void (*get_catcher(void))(except_t *)
73 {
74     void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
75     return (catcher == 0) ? unhandled_catcher : catcher;
76 }
77
78 static void *(*get_alloc(void))(size_t)
79 {
80     void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
81     return (alloc == 0) ? malloc : alloc;
82 }
83
84 static void (*get_dealloc(void))(void *)
85 {
86     void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
87     return (dealloc == 0) ? free : dealloc;
88 }
89
90 int except_init(void)
91 {
92     int retval = 1;
93
94     pthread_mutex_lock(&init_mtx);
95
96     assert (init_counter < INT_MAX);
97
98     if (init_counter++ == 0) {
99         int top_ok = (pthread_key_create(&top_key, 0) == 0);
100         int uh_ok = (pthread_key_create(&uh_key, 0) == 0);
101         int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0);
102         int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0);
103
104         if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
105             retval = 0;
106             init_counter = 0;
107             if (top_ok)
108                 pthread_key_delete(top_key);
109             if (uh_ok)
110                 pthread_key_delete(uh_key);
111             if (alloc_ok)
112                 pthread_key_delete(alloc_key);
113             if (dealloc_ok)
114                 pthread_key_delete(dealloc_key);
115         }
116     }
117
118     pthread_mutex_unlock(&init_mtx);
119
120     return retval;
121 }
122
123 void except_deinit(void)
124 {
125     pthread_mutex_lock(&init_mtx);
126
127     assert (init_counter > 0);
128
129     if (--init_counter == 0) {
130         pthread_key_delete(top_key);
131         pthread_key_delete(uh_key);
132         pthread_key_delete(alloc_key);
133         pthread_key_delete(dealloc_key);
134     }
135
136     pthread_mutex_unlock(&init_mtx);
137 }
138
139 #else   /* no thread support */
140
141 static int init_counter;
142 static void unhandled_catcher(except_t *);
143 static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
144 /* We need this 'size_t' cast due to a glitch in GLib where g_malloc was prototyped
145  * as 'gpointer g_malloc (gulong n_bytes)'. This was later fixed to the correct prototype
146  * 'gpointer g_malloc (gsize n_bytes)'. In Wireshark we use the latter prototype
147  * throughout the code. We can get away with this even with older versions of GLib by
148  * adding a '(void *(*)(size_t))' cast whenever we refer to g_malloc. The only platform
149  * supported by Wireshark where this isn't safe (sizeof size_t != sizeof gulong) is Win64.
150  * However, we _always_ bundle the newest version of GLib on this platform so
151  * the size_t issue doesn't exists here. Pheew.. */
152 static void *(*allocator)(size_t) = (void *(*)(size_t)) g_malloc;
153 static void (*deallocator)(void *) = g_free;
154 static struct except_stacknode *stack_top;
155
156 #define get_top() (stack_top)
157 #define set_top(T) (stack_top = (T))
158 #define get_catcher() (uh_catcher_ptr)
159 #define set_catcher(C) (uh_catcher_ptr = (C))
160 #define get_alloc() (allocator)
161 #define set_alloc(A) (allocator = (A))
162 #define get_dealloc() (deallocator)
163 #define set_dealloc(D) (deallocator = (D))
164
165 int except_init(void)
166 {
167     assert (init_counter < INT_MAX);
168     init_counter++;
169     return 1;
170 }
171
172 void except_deinit(void)
173 {
174     assert (init_counter > 0);
175     init_counter--;
176 }
177
178 #endif
179
180
181 static int match(const volatile except_id_t *thrown, const except_id_t *caught)
182 {
183     int group_match = (caught->except_group == XCEPT_GROUP_ANY ||
184         caught->except_group == thrown->except_group);
185     int code_match = (caught->except_code == XCEPT_CODE_ANY ||
186         caught->except_code == thrown->except_code);
187
188     return group_match && code_match;
189 }
190
191 G_GNUC_NORETURN WS_MSVC_NORETURN static void do_throw(except_t *except)
192 {
193     struct except_stacknode *top;
194
195     assert (except->except_id.except_group != 0 &&
196         except->except_id.except_code != 0);
197
198     for (top = get_top(); top != 0; top = top->except_down) {
199         if (top->except_type == XCEPT_CLEANUP) {
200             top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context);
201         } else {
202             struct except_catch *catcher = top->except_info.except_catcher;
203             const except_id_t *pi = catcher->except_id;
204             size_t i;
205
206             assert (top->except_type == XCEPT_CATCHER);
207             except_free(catcher->except_obj.except_dyndata);
208
209             for (i = 0; i < catcher->except_size; pi++, i++) {
210                 if (match(&except->except_id, pi)) {
211                     catcher->except_obj = *except;
212                     set_top(top);
213                     longjmp(catcher->except_jmp, 1);
214                 }
215             }
216         }
217     }
218
219     set_top(top);
220     get_catcher()(except);      /* unhandled exception */
221     abort();
222 }
223
224 static void unhandled_catcher(except_t *except)
225 {
226     if (except->except_message == NULL) {
227         fprintf(stderr, "Unhandled exception (group=%ld, code=%ld)\n",
228                 except->except_id.except_group,
229                 except->except_id.except_code);
230     } else {
231         fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n",
232                 except->except_message, except->except_id.except_group,
233                 except->except_id.except_code);
234     }
235     abort();
236 }
237
238 static void stack_push(struct except_stacknode *node)
239 {
240     node->except_down = get_top();
241     set_top(node);
242 }
243
244 void except_setup_clean(struct except_stacknode *esn,
245         struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
246 {
247     esn->except_type = XCEPT_CLEANUP;
248     ecl->except_func = cleanf;
249     ecl->except_context = context;
250     esn->except_info.except_cleanup = ecl;
251     stack_push(esn);
252 }
253
254 void except_setup_try(struct except_stacknode *esn,
255         struct except_catch *ech, const except_id_t id[], size_t size)
256 {
257    ech->except_id = id;
258    ech->except_size = size;
259    ech->except_obj.except_dyndata = 0;
260    esn->except_type = XCEPT_CATCHER;
261    esn->except_info.except_catcher = ech;
262    stack_push(esn);
263 }
264
265 struct except_stacknode *except_pop(void)
266 {
267     struct except_stacknode *top = get_top();
268     set_top(top->except_down);
269     return top;
270 }
271
272 G_GNUC_NORETURN WS_MSVC_NORETURN void except_rethrow(except_t *except)
273 {
274     struct except_stacknode *top = get_top();
275     assert (top != 0);
276     assert (top->except_type == XCEPT_CATCHER);
277     assert (&top->except_info.except_catcher->except_obj == except);
278     set_top(top->except_down);
279     do_throw(except);
280 }
281
282 G_GNUC_NORETURN WS_MSVC_NORETURN void except_throw(long group, long code, const char *msg)
283 {
284     except_t except;
285
286     except.except_id.except_group = group;
287     except.except_id.except_code = code;
288     except.except_message = msg;
289     except.except_dyndata = 0;
290
291 #ifdef _WIN32
292     if (code == DissectorError && IsDebuggerPresent()) {
293         DebugBreak();
294     }
295 #endif
296
297     do_throw(&except);
298 }
299
300 G_GNUC_NORETURN WS_MSVC_NORETURN void except_throwd(long group, long code, const char *msg, void *data)
301 {
302     except_t except;
303
304     except.except_id.except_group = group;
305     except.except_id.except_code = code;
306     except.except_message = msg;
307     except.except_dyndata = data;
308
309     do_throw(&except);
310 }
311
312 /*
313  * XXX - should we use g_strdup_sprintf() here, so we're not limited by
314  * XCEPT_BUFFER_SIZE?  We could then just use this to generate formatted
315  * messages.
316  */
317 G_GNUC_NORETURN WS_MSVC_NORETURN void except_throwf(long group, long code, const char *fmt, ...)
318 {
319     char *buf = except_alloc(XCEPT_BUFFER_SIZE);
320     va_list vl;
321
322     va_start (vl, fmt);
323     g_vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl);
324     va_end (vl);
325     except_throwd(group, code, buf, buf);
326 }
327
328 void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
329 {
330     void (*old_catcher)(except_t *) = get_catcher();
331     set_catcher(new_catcher);
332     return old_catcher;
333 }
334
335 #undef except_code
336 #undef except_group
337 #undef except_message
338 #undef except_data
339
340 unsigned long except_code(except_t *ex)
341 {
342     return ex->except_id.except_code;
343 }
344
345 unsigned long except_group(except_t *ex)
346 {
347     return ex->except_id.except_group;
348 }
349
350 const char *except_message(except_t *ex)
351 {
352     return ex->except_message;
353 }
354
355 void *except_data(except_t *ex)
356 {
357     return ex->except_dyndata;
358 }
359
360 void *except_take_data(except_t *ex)
361 {
362     void *data = ex->except_dyndata;
363     ex->except_dyndata = 0;
364     return data;
365 }
366
367 void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
368 {
369     set_alloc(alloc);
370     set_dealloc(dealloc);
371 }
372
373 void *except_alloc(size_t size)
374 {
375     void *ptr = get_alloc()(size);
376
377     if (ptr == 0)
378         except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
379     return ptr;
380 }
381
382 void except_free(void *ptr)
383 {
384     get_dealloc()(ptr);
385 }
386
387 #ifdef KAZLIB_TEST_MAIN
388
389 #include <stdio.h>
390 #include <ctype.h>
391
392 static void cleanup(void *arg)
393 {
394     printf("cleanup(\"%s\") called\n", (char *) arg);
395 }
396
397 static void bottom_level(void)
398 {
399     char buf[256];
400     printf("throw exception? "); fflush(stdout);
401     fgets(buf, sizeof buf, stdin);
402
403     if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
404         except_throw(1, 1, "nasty exception");
405 }
406
407 static void top_level(void)
408 {
409     except_cleanup_push(cleanup, "argument");
410     bottom_level();
411     except_cleanup_pop(0);
412 }
413
414 int main(int argc, char **argv)
415 {
416     static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
417     except_t *ex;
418     char *msg;
419
420     /*
421      * Nested exception ``try blocks''
422      */
423
424     /* outer */
425     except_try_push(catch, 2, &ex);
426     if (!ex) {
427         /* inner */
428         except_try_push(catch, 2, &ex);
429         if (!ex) {
430             top_level();
431         } else {
432             /* inner catch */
433             msg = except_message(ex);
434             if (msg == NULL) {
435                 printf("caught exception (inner): s=%ld, c=%ld\n",
436                        except_group(ex), except_code(ex));
437             } else {
438                 printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
439                        msg, except_group(ex), except_code(ex));
440             }
441             except_rethrow(ex);
442         }
443         except_try_pop();
444     } else {
445         /* outer catch */
446         msg = except_message(ex);
447         if (msg == NULL) {
448             printf("caught exception (outer): s=%ld, c=%ld\n",
449                    except_group(ex), except_code(ex));
450         } else {
451             printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
452                    except_message(ex), except_group(ex), except_code(ex));
453         }
454     }
455     except_try_pop();
456     except_throw(99, 99, "exception in main");
457     return 0;
458 }
459
460
461 #endif