Add Q.932 to extra dist.
[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  * 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  * $Id$
17  * $Name:  $
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 #include <assert.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <limits.h>
34 #include "except.h"
35
36 #ifdef _WIN32
37 /* IsDebuggerPresent() needs this #define! */
38 #define _WIN32_WINNT 0x0400
39 #include <windows.h>
40 #include "exceptions.h"
41 #endif
42
43 #define XCEPT_BUFFER_SIZE       1024
44
45 #ifdef KAZLIB_RCSID
46 static const char rcsid[] = "$Id$";
47 #endif
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 static void *(*allocator)(size_t) = malloc;
140 static void (*deallocator)(void *) = free;
141 static struct except_stacknode *stack_top;
142
143 #define get_top() (stack_top)
144 #define set_top(T) (stack_top = (T))
145 #define get_catcher() (uh_catcher_ptr)
146 #define set_catcher(C) (uh_catcher_ptr = (C))
147 #define get_alloc() (allocator)
148 #define set_alloc(A) (allocator = (A))
149 #define get_dealloc() (deallocator)
150 #define set_dealloc(D) (deallocator = (D))
151
152 int except_init(void)
153 {
154     assert (init_counter < INT_MAX);
155     init_counter++;
156     return 1;
157 }
158
159 void except_deinit(void)
160 {
161     assert (init_counter > 0);
162     init_counter--;
163 }
164
165 #endif
166
167
168 static int match(const volatile except_id_t *thrown, const except_id_t *caught)
169 {
170     int group_match = (caught->except_group == XCEPT_GROUP_ANY || 
171         caught->except_group == thrown->except_group);
172     int code_match = (caught->except_code == XCEPT_CODE_ANY || 
173         caught->except_code == thrown->except_code);
174
175     return group_match && code_match;
176 }
177
178 static void do_throw(except_t *except)
179 {
180     struct except_stacknode *top;
181
182     assert (except->except_id.except_group != 0 && 
183         except->except_id.except_code != 0);
184
185     for (top = get_top(); top != 0; top = top->except_down) {
186         if (top->except_type == XCEPT_CLEANUP) {
187             top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context);
188         } else {
189             struct except_catch *catcher = top->except_info.except_catcher;
190             const except_id_t *pi = catcher->except_id;
191             size_t i;
192
193             assert (top->except_type == XCEPT_CATCHER);
194             except_free(catcher->except_obj.except_dyndata);
195
196             for (i = 0; i < catcher->except_size; pi++, i++) {
197                 if (match(&except->except_id, pi)) {
198                     catcher->except_obj = *except;
199                     set_top(top);
200                     longjmp(catcher->except_jmp, 1);
201                 }
202             }
203         }
204     }
205
206     set_top(top);
207     get_catcher()(except);      /* unhandled exception */
208     abort();
209 }
210
211 static void unhandled_catcher(except_t *except)
212 {
213     if (except->except_message == NULL) {
214         fprintf(stderr, "Unhandled exception (group=%ld, code=%ld)\n",
215                 except->except_id.except_group,
216                 except->except_id.except_code);
217     } else {
218         fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n",
219                 except->except_message, except->except_id.except_group,
220                 except->except_id.except_code);
221     }
222     abort();
223 }
224
225 static void stack_push(struct except_stacknode *node)
226 {
227     node->except_down = get_top();
228     set_top(node);
229 }
230
231 void except_setup_clean(struct except_stacknode *esn,
232         struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
233 {
234     esn->except_type = XCEPT_CLEANUP;
235     ecl->except_func = cleanf;
236     ecl->except_context = context;
237     esn->except_info.except_cleanup = ecl;
238     stack_push(esn);
239 }
240
241 void except_setup_try(struct except_stacknode *esn,
242         struct except_catch *ech, const except_id_t id[], size_t size)
243 {
244    ech->except_id = id;
245    ech->except_size = size;
246    ech->except_obj.except_dyndata = 0;
247    esn->except_type = XCEPT_CATCHER;
248    esn->except_info.except_catcher = ech;
249    stack_push(esn);
250 }
251
252 struct except_stacknode *except_pop(void)
253 {
254     struct except_stacknode *top = get_top();
255     set_top(top->except_down);
256     return top;
257 }
258
259 void except_rethrow(except_t *except)
260 {
261     struct except_stacknode *top = get_top();
262     assert (top != 0);
263     assert (top->except_type == XCEPT_CATCHER);
264     assert (&top->except_info.except_catcher->except_obj == except);
265     set_top(top->except_down);
266     do_throw(except);
267 }
268
269 void except_throw(long group, long code, const char *msg)
270 {
271     except_t except;
272
273     except.except_id.except_group = group;
274     except.except_id.except_code = code;
275     except.except_message = msg;
276     except.except_dyndata = 0;
277
278 #ifdef _WIN32
279     if (code == DissectorError && IsDebuggerPresent()) {
280         DebugBreak();
281     }
282 #endif
283
284     do_throw(&except);
285 }
286
287 void except_throwd(long group, long code, const char *msg, void *data)
288 {
289     except_t except;
290
291     except.except_id.except_group = group;
292     except.except_id.except_code = code;
293     except.except_message = msg;
294     except.except_dyndata = data;
295
296     do_throw(&except);
297 }
298
299 /*
300  * XXX - should we use g_strdup_sprintf() here, so we're not limited by
301  * XCEPT_BUFFER_SIZE?  We could then just use this to generate formatted
302  * messages.
303  */
304 void except_throwf(long group, long code, const char *fmt, ...)
305 {
306     char *buf = except_alloc(XCEPT_BUFFER_SIZE);
307     va_list vl;
308
309     va_start (vl, fmt);
310     vsprintf(buf, fmt, vl);
311     va_end (vl);
312     except_throwd(group, code, buf, buf);
313 }
314
315 void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
316 {
317     void (*old_catcher)(except_t *) = get_catcher();
318     set_catcher(new_catcher);
319     return old_catcher;
320 }
321
322 #undef except_code
323 #undef except_group
324 #undef except_message
325 #undef except_data
326
327 unsigned long except_code(except_t *ex)
328 {
329     return ex->except_id.except_code;
330 }
331
332 unsigned long except_group(except_t *ex)
333 {
334     return ex->except_id.except_group;
335 }
336
337 const char *except_message(except_t *ex)
338 {
339     return ex->except_message;
340 }
341
342 void *except_data(except_t *ex)
343 {
344     return ex->except_dyndata;
345 }
346
347 void *except_take_data(except_t *ex)
348 {
349     void *data = ex->except_dyndata;
350     ex->except_dyndata = 0;
351     return data;
352 }
353
354 void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
355 {
356     set_alloc(alloc);
357     set_dealloc(dealloc);
358 }
359
360 void *except_alloc(size_t size)
361 {
362     void *ptr = get_alloc()(size);
363
364     if (ptr == 0)
365         except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
366     return ptr;
367 }
368
369 void except_free(void *ptr)
370 {
371     get_dealloc()(ptr);
372 }
373
374 #ifdef KAZLIB_TEST_MAIN
375
376 #include <stdio.h>
377 #include <ctype.h>
378
379 static void cleanup(void *arg)
380 {
381     printf("cleanup(\"%s\") called\n", (char *) arg);
382 }
383
384 static void bottom_level(void)
385 {
386     char buf[256];
387     printf("throw exception? "); fflush(stdout);
388     fgets(buf, sizeof buf, stdin);
389
390     if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
391         except_throw(1, 1, "nasty exception");
392 }
393
394 static void top_level(void)
395 {
396     except_cleanup_push(cleanup, "argument");
397     bottom_level();
398     except_cleanup_pop(0);
399 }
400
401 int main(int argc, char **argv)
402 {
403     static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
404     except_t *ex;
405     char *msg;
406
407     /*
408      * Nested exception ``try blocks''
409      */
410
411     /* outer */
412     except_try_push(catch, 2, &ex);
413     if (!ex) {
414         /* inner */
415         except_try_push(catch, 2, &ex);
416         if (!ex) {
417             top_level();
418         } else {
419             /* inner catch */
420             msg = except_message(ex);
421             if (msg == NULL) {
422                 printf("caught exception (inner): s=%ld, c=%ld\n",
423                        except_group(ex), except_code(ex));
424             } else {
425                 printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
426                        msg, except_group(ex), except_code(ex));
427             }
428             except_rethrow(ex);
429         }
430         except_try_pop();
431     } else {
432         /* outer catch */
433         msg = except_message(ex);
434         if (msg == NULL) {
435             printf("caught exception (outer): s=%ld, c=%ld\n",
436                    except_group(ex), except_code(ex));
437         } else {
438             printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
439                    except_message(ex), except_group(ex), except_code(ex));
440         }
441     }
442     except_try_pop();
443     except_throw(99, 99, "exception in main");
444     return 0;
445 }
446
447
448 #endif