2 * Portable Exception Handling for ANSI C.
3 * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
5 * Free Software License:
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.
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
37 /* IsDebuggerPresent() needs this #define! */
38 #define _WIN32_WINNT 0x0400
40 #include "exceptions.h"
43 #define XCEPT_BUFFER_SIZE 1024
46 static const char rcsid[] = "$Id$";
49 #ifdef KAZLIB_POSIX_THREADS
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 *);
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))
67 static void (*get_catcher(void))(except_t *)
69 void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
70 return (catcher == 0) ? unhandled_catcher : catcher;
73 static void *(*get_alloc(void))(size_t)
75 void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
76 return (alloc == 0) ? malloc : alloc;
79 static void (*get_dealloc(void))(void *)
81 void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
82 return (dealloc == 0) ? free : dealloc;
89 pthread_mutex_lock(&init_mtx);
91 assert (init_counter < INT_MAX);
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);
99 if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
103 pthread_key_delete(top_key);
105 pthread_key_delete(uh_key);
107 pthread_key_delete(alloc_key);
109 pthread_key_delete(dealloc_key);
113 pthread_mutex_unlock(&init_mtx);
118 void except_deinit(void)
120 pthread_mutex_lock(&init_mtx);
122 assert (init_counter > 0);
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);
131 pthread_mutex_unlock(&init_mtx);
134 #else /* no thread support */
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;
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))
152 int except_init(void)
154 assert (init_counter < INT_MAX);
159 void except_deinit(void)
161 assert (init_counter > 0);
168 static int match(const volatile except_id_t *thrown, const except_id_t *caught)
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);
175 return group_match && code_match;
178 static void do_throw(except_t *except)
180 struct except_stacknode *top;
182 assert (except->except_id.except_group != 0 &&
183 except->except_id.except_code != 0);
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);
189 struct except_catch *catcher = top->except_info.except_catcher;
190 const except_id_t *pi = catcher->except_id;
193 assert (top->except_type == XCEPT_CATCHER);
194 except_free(catcher->except_obj.except_dyndata);
196 for (i = 0; i < catcher->except_size; pi++, i++) {
197 if (match(&except->except_id, pi)) {
198 catcher->except_obj = *except;
200 longjmp(catcher->except_jmp, 1);
207 get_catcher()(except); /* unhandled exception */
211 static void unhandled_catcher(except_t *except)
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);
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);
225 static void stack_push(struct except_stacknode *node)
227 node->except_down = get_top();
231 void except_setup_clean(struct except_stacknode *esn,
232 struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
234 esn->except_type = XCEPT_CLEANUP;
235 ecl->except_func = cleanf;
236 ecl->except_context = context;
237 esn->except_info.except_cleanup = ecl;
241 void except_setup_try(struct except_stacknode *esn,
242 struct except_catch *ech, const except_id_t id[], size_t size)
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;
252 struct except_stacknode *except_pop(void)
254 struct except_stacknode *top = get_top();
255 set_top(top->except_down);
259 void except_rethrow(except_t *except)
261 struct except_stacknode *top = get_top();
263 assert (top->except_type == XCEPT_CATCHER);
264 assert (&top->except_info.except_catcher->except_obj == except);
265 set_top(top->except_down);
269 void except_throw(long group, long code, const char *msg)
273 except.except_id.except_group = group;
274 except.except_id.except_code = code;
275 except.except_message = msg;
276 except.except_dyndata = 0;
279 if (code == DissectorError && IsDebuggerPresent()) {
287 void except_throwd(long group, long code, const char *msg, void *data)
291 except.except_id.except_group = group;
292 except.except_id.except_code = code;
293 except.except_message = msg;
294 except.except_dyndata = data;
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
304 void except_throwf(long group, long code, const char *fmt, ...)
306 char *buf = except_alloc(XCEPT_BUFFER_SIZE);
310 vsprintf(buf, fmt, vl);
312 except_throwd(group, code, buf, buf);
315 void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
317 void (*old_catcher)(except_t *) = get_catcher();
318 set_catcher(new_catcher);
324 #undef except_message
327 unsigned long except_code(except_t *ex)
329 return ex->except_id.except_code;
332 unsigned long except_group(except_t *ex)
334 return ex->except_id.except_group;
337 const char *except_message(except_t *ex)
339 return ex->except_message;
342 void *except_data(except_t *ex)
344 return ex->except_dyndata;
347 void *except_take_data(except_t *ex)
349 void *data = ex->except_dyndata;
350 ex->except_dyndata = 0;
354 void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
357 set_dealloc(dealloc);
360 void *except_alloc(size_t size)
362 void *ptr = get_alloc()(size);
365 except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
369 void except_free(void *ptr)
374 #ifdef KAZLIB_TEST_MAIN
379 static void cleanup(void *arg)
381 printf("cleanup(\"%s\") called\n", (char *) arg);
384 static void bottom_level(void)
387 printf("throw exception? "); fflush(stdout);
388 fgets(buf, sizeof buf, stdin);
390 if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
391 except_throw(1, 1, "nasty exception");
394 static void top_level(void)
396 except_cleanup_push(cleanup, "argument");
398 except_cleanup_pop(0);
401 int main(int argc, char **argv)
403 static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
408 * Nested exception ``try blocks''
412 except_try_push(catch, 2, &ex);
415 except_try_push(catch, 2, &ex);
420 msg = except_message(ex);
422 printf("caught exception (inner): s=%ld, c=%ld\n",
423 except_group(ex), except_code(ex));
425 printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
426 msg, except_group(ex), except_code(ex));
433 msg = except_message(ex);
435 printf("caught exception (outer): s=%ld, c=%ld\n",
436 except_group(ex), except_code(ex));
438 printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
439 except_message(ex), except_group(ex), except_code(ex));
443 except_throw(99, 99, "exception in main");