2000-05-11 08:18:09 +00:00
|
|
|
/*
|
|
|
|
* Portable Exception Handling for ANSI C.
|
|
|
|
* Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
|
|
|
|
*
|
|
|
|
* Free Software License:
|
|
|
|
*
|
|
|
|
* All rights are reserved by the author, with the following exceptions:
|
|
|
|
* Permission is granted to freely reproduce and distribute this software,
|
|
|
|
* possibly in exchange for a fee, provided that this copyright notice appears
|
|
|
|
* intact. Permission is also granted to adapt this software to produce
|
|
|
|
* derivative works, as long as the modified versions carry this copyright
|
|
|
|
* notice and additional notices stating that the work has been modified.
|
|
|
|
* This source code may be translated into executable form and incorporated
|
|
|
|
* into proprietary software; there is no requirement for such software to
|
|
|
|
* contain a copyright notice related to this source.
|
2004-07-18 00:24:25 +00:00
|
|
|
* $Id$
|
2000-05-11 08:18:09 +00:00
|
|
|
* $Name: $
|
|
|
|
*/
|
|
|
|
|
2005-08-06 19:58:45 +00:00
|
|
|
/*
|
|
|
|
* Modified to support throwing an exception with a null message pointer,
|
|
|
|
* and to have the message not be const (as we generate messages with
|
|
|
|
* "g_strdup_sprintf()", which means they need to be freed; using
|
|
|
|
* a null message means that we don't have to use a special string
|
|
|
|
* for exceptions with no message, and don't have to worry about
|
|
|
|
* not freeing that).
|
|
|
|
*/
|
|
|
|
|
2000-05-11 08:18:09 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include "except.h"
|
|
|
|
|
|
|
|
#define XCEPT_BUFFER_SIZE 1024
|
|
|
|
|
|
|
|
#ifdef KAZLIB_RCSID
|
2004-07-18 00:24:25 +00:00
|
|
|
static const char rcsid[] = "$Id$";
|
2000-05-11 08:18:09 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef KAZLIB_POSIX_THREADS
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static int init_counter;
|
|
|
|
static pthread_key_t top_key;
|
|
|
|
static pthread_key_t uh_key;
|
|
|
|
static pthread_key_t alloc_key;
|
|
|
|
static pthread_key_t dealloc_key;
|
|
|
|
static void unhandled_catcher(except_t *);
|
|
|
|
|
|
|
|
#define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
|
|
|
|
#define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
|
|
|
|
#define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
|
|
|
|
#define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
|
|
|
|
#define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
|
|
|
|
|
|
|
|
static void (*get_catcher(void))(except_t *)
|
|
|
|
{
|
|
|
|
void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
|
|
|
|
return (catcher == 0) ? unhandled_catcher : catcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *(*get_alloc(void))(size_t)
|
|
|
|
{
|
|
|
|
void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
|
|
|
|
return (alloc == 0) ? malloc : alloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void (*get_dealloc(void))(void *)
|
|
|
|
{
|
|
|
|
void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
|
|
|
|
return (dealloc == 0) ? free : dealloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int except_init(void)
|
|
|
|
{
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&init_mtx);
|
|
|
|
|
|
|
|
assert (init_counter < INT_MAX);
|
|
|
|
|
|
|
|
if (init_counter++ == 0) {
|
|
|
|
int top_ok = (pthread_key_create(&top_key, 0) == 0);
|
|
|
|
int uh_ok = (pthread_key_create(&uh_key, 0) == 0);
|
|
|
|
int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0);
|
|
|
|
int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0);
|
2002-08-28 20:41:00 +00:00
|
|
|
|
2000-05-11 08:18:09 +00:00
|
|
|
if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
|
|
|
|
retval = 0;
|
|
|
|
init_counter = 0;
|
|
|
|
if (top_ok)
|
|
|
|
pthread_key_delete(top_key);
|
|
|
|
if (uh_ok)
|
|
|
|
pthread_key_delete(uh_key);
|
|
|
|
if (alloc_ok)
|
|
|
|
pthread_key_delete(alloc_key);
|
|
|
|
if (dealloc_ok)
|
|
|
|
pthread_key_delete(dealloc_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&init_mtx);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_deinit(void)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&init_mtx);
|
|
|
|
|
|
|
|
assert (init_counter > 0);
|
|
|
|
|
|
|
|
if (--init_counter == 0) {
|
|
|
|
pthread_key_delete(top_key);
|
|
|
|
pthread_key_delete(uh_key);
|
|
|
|
pthread_key_delete(alloc_key);
|
|
|
|
pthread_key_delete(dealloc_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&init_mtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* no thread support */
|
|
|
|
|
|
|
|
static int init_counter;
|
|
|
|
static void unhandled_catcher(except_t *);
|
|
|
|
static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
|
|
|
|
static void *(*allocator)(size_t) = malloc;
|
|
|
|
static void (*deallocator)(void *) = free;
|
|
|
|
static struct except_stacknode *stack_top;
|
|
|
|
|
|
|
|
#define get_top() (stack_top)
|
|
|
|
#define set_top(T) (stack_top = (T))
|
|
|
|
#define get_catcher() (uh_catcher_ptr)
|
|
|
|
#define set_catcher(C) (uh_catcher_ptr = (C))
|
|
|
|
#define get_alloc() (allocator)
|
|
|
|
#define set_alloc(A) (allocator = (A))
|
|
|
|
#define get_dealloc() (deallocator)
|
|
|
|
#define set_dealloc(D) (deallocator = (D))
|
|
|
|
|
|
|
|
int except_init(void)
|
|
|
|
{
|
|
|
|
assert (init_counter < INT_MAX);
|
|
|
|
init_counter++;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_deinit(void)
|
|
|
|
{
|
|
|
|
assert (init_counter > 0);
|
|
|
|
init_counter--;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2001-07-27 16:20:39 +00:00
|
|
|
static int match(const volatile except_id_t *thrown, const except_id_t *caught)
|
2000-05-11 08:18:09 +00:00
|
|
|
{
|
2004-12-29 16:25:06 +00:00
|
|
|
int group_match = (caught->except_group == XCEPT_GROUP_ANY ||
|
2004-12-28 19:59:50 +00:00
|
|
|
caught->except_group == thrown->except_group);
|
2004-12-29 16:25:06 +00:00
|
|
|
int code_match = (caught->except_code == XCEPT_CODE_ANY ||
|
2004-12-28 19:59:50 +00:00
|
|
|
caught->except_code == thrown->except_code);
|
2000-05-11 08:18:09 +00:00
|
|
|
|
|
|
|
return group_match && code_match;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_throw(except_t *except)
|
|
|
|
{
|
|
|
|
struct except_stacknode *top;
|
|
|
|
|
2004-12-29 16:25:06 +00:00
|
|
|
assert (except->except_id.except_group != 0 &&
|
2004-12-28 19:59:50 +00:00
|
|
|
except->except_id.except_code != 0);
|
2000-05-11 08:18:09 +00:00
|
|
|
|
2004-12-28 19:59:50 +00:00
|
|
|
for (top = get_top(); top != 0; top = top->except_down) {
|
|
|
|
if (top->except_type == XCEPT_CLEANUP) {
|
|
|
|
top->except_info.except_cleanup->except_func(top->except_info.except_cleanup->except_context);
|
2000-05-11 08:18:09 +00:00
|
|
|
} else {
|
2004-12-28 19:59:50 +00:00
|
|
|
struct except_catch *catcher = top->except_info.except_catcher;
|
|
|
|
const except_id_t *pi = catcher->except_id;
|
2000-05-11 08:18:09 +00:00
|
|
|
size_t i;
|
2002-08-28 20:41:00 +00:00
|
|
|
|
2004-12-28 19:59:50 +00:00
|
|
|
assert (top->except_type == XCEPT_CATCHER);
|
|
|
|
except_free(catcher->except_obj.except_dyndata);
|
2000-05-11 08:18:09 +00:00
|
|
|
|
2004-12-28 19:59:50 +00:00
|
|
|
for (i = 0; i < catcher->except_size; pi++, i++) {
|
|
|
|
if (match(&except->except_id, pi)) {
|
|
|
|
catcher->except_obj = *except;
|
2000-05-11 08:18:09 +00:00
|
|
|
set_top(top);
|
2004-12-28 19:59:50 +00:00
|
|
|
longjmp(catcher->except_jmp, 1);
|
2000-05-11 08:18:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
set_top(top);
|
|
|
|
get_catcher()(except); /* unhandled exception */
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unhandled_catcher(except_t *except)
|
|
|
|
{
|
2005-08-06 19:58:45 +00:00
|
|
|
if (except->except_message == NULL) {
|
|
|
|
fprintf(stderr, "Unhandled exception (group=%ld, code=%ld)\n",
|
|
|
|
except->except_id.except_group,
|
|
|
|
except->except_id.except_code);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n",
|
|
|
|
except->except_message, except->except_id.except_group,
|
|
|
|
except->except_id.except_code);
|
|
|
|
}
|
2000-05-11 08:18:09 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stack_push(struct except_stacknode *node)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
node->except_down = get_top();
|
2000-05-11 08:18:09 +00:00
|
|
|
set_top(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_setup_clean(struct except_stacknode *esn,
|
|
|
|
struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
esn->except_type = XCEPT_CLEANUP;
|
|
|
|
ecl->except_func = cleanf;
|
|
|
|
ecl->except_context = context;
|
|
|
|
esn->except_info.except_cleanup = ecl;
|
2000-05-11 08:18:09 +00:00
|
|
|
stack_push(esn);
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_setup_try(struct except_stacknode *esn,
|
|
|
|
struct except_catch *ech, const except_id_t id[], size_t size)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
ech->except_id = id;
|
|
|
|
ech->except_size = size;
|
|
|
|
ech->except_obj.except_dyndata = 0;
|
|
|
|
esn->except_type = XCEPT_CATCHER;
|
|
|
|
esn->except_info.except_catcher = ech;
|
2000-05-11 08:18:09 +00:00
|
|
|
stack_push(esn);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct except_stacknode *except_pop(void)
|
|
|
|
{
|
|
|
|
struct except_stacknode *top = get_top();
|
2004-12-28 19:59:50 +00:00
|
|
|
set_top(top->except_down);
|
2000-05-11 08:18:09 +00:00
|
|
|
return top;
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_rethrow(except_t *except)
|
|
|
|
{
|
|
|
|
struct except_stacknode *top = get_top();
|
|
|
|
assert (top != 0);
|
2004-12-28 19:59:50 +00:00
|
|
|
assert (top->except_type == XCEPT_CATCHER);
|
|
|
|
assert (&top->except_info.except_catcher->except_obj == except);
|
|
|
|
set_top(top->except_down);
|
2000-05-11 08:18:09 +00:00
|
|
|
do_throw(except);
|
|
|
|
}
|
|
|
|
|
2005-09-27 06:09:14 +00:00
|
|
|
void except_throw(long group, long code, const char *msg)
|
2000-05-11 08:18:09 +00:00
|
|
|
{
|
|
|
|
except_t except;
|
|
|
|
|
2004-12-28 19:59:50 +00:00
|
|
|
except.except_id.except_group = group;
|
|
|
|
except.except_id.except_code = code;
|
|
|
|
except.except_message = msg;
|
|
|
|
except.except_dyndata = 0;
|
2000-05-11 08:18:09 +00:00
|
|
|
|
|
|
|
do_throw(&except);
|
|
|
|
}
|
|
|
|
|
2005-09-27 06:09:14 +00:00
|
|
|
void except_throwd(long group, long code, const char *msg, void *data)
|
2000-05-11 08:18:09 +00:00
|
|
|
{
|
|
|
|
except_t except;
|
|
|
|
|
2004-12-28 19:59:50 +00:00
|
|
|
except.except_id.except_group = group;
|
|
|
|
except.except_id.except_code = code;
|
|
|
|
except.except_message = msg;
|
|
|
|
except.except_dyndata = data;
|
2000-05-11 08:18:09 +00:00
|
|
|
|
|
|
|
do_throw(&except);
|
|
|
|
}
|
|
|
|
|
2005-08-06 19:58:45 +00:00
|
|
|
/*
|
|
|
|
* XXX - should we use g_strdup_sprintf() here, so we're not limited by
|
|
|
|
* XCEPT_BUFFER_SIZE? We could then just use this to generate formatted
|
|
|
|
* messages.
|
|
|
|
*/
|
2000-05-11 08:18:09 +00:00
|
|
|
void except_throwf(long group, long code, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
char *buf = except_alloc(XCEPT_BUFFER_SIZE);
|
|
|
|
va_list vl;
|
|
|
|
|
|
|
|
va_start (vl, fmt);
|
|
|
|
vsprintf(buf, fmt, vl);
|
|
|
|
va_end (vl);
|
|
|
|
except_throwd(group, code, buf, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
|
|
|
|
{
|
|
|
|
void (*old_catcher)(except_t *) = get_catcher();
|
|
|
|
set_catcher(new_catcher);
|
|
|
|
return old_catcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef except_code
|
|
|
|
#undef except_group
|
|
|
|
#undef except_message
|
|
|
|
#undef except_data
|
|
|
|
|
|
|
|
unsigned long except_code(except_t *ex)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
return ex->except_id.except_code;
|
2000-05-11 08:18:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long except_group(except_t *ex)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
return ex->except_id.except_group;
|
2000-05-11 08:18:09 +00:00
|
|
|
}
|
|
|
|
|
2005-09-27 06:09:14 +00:00
|
|
|
const char *except_message(except_t *ex)
|
2000-05-11 08:18:09 +00:00
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
return ex->except_message;
|
2000-05-11 08:18:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *except_data(except_t *ex)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
return ex->except_dyndata;
|
2000-05-11 08:18:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *except_take_data(except_t *ex)
|
|
|
|
{
|
2004-12-28 19:59:50 +00:00
|
|
|
void *data = ex->except_dyndata;
|
|
|
|
ex->except_dyndata = 0;
|
2000-05-11 08:18:09 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
|
|
|
|
{
|
|
|
|
set_alloc(alloc);
|
|
|
|
set_dealloc(dealloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *except_alloc(size_t size)
|
|
|
|
{
|
|
|
|
void *ptr = get_alloc()(size);
|
|
|
|
|
|
|
|
if (ptr == 0)
|
|
|
|
except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void except_free(void *ptr)
|
|
|
|
{
|
|
|
|
get_dealloc()(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef KAZLIB_TEST_MAIN
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
static void cleanup(void *arg)
|
|
|
|
{
|
|
|
|
printf("cleanup(\"%s\") called\n", (char *) arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bottom_level(void)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
printf("throw exception? "); fflush(stdout);
|
|
|
|
fgets(buf, sizeof buf, stdin);
|
|
|
|
|
2001-07-27 16:20:39 +00:00
|
|
|
if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
|
2000-05-11 08:18:09 +00:00
|
|
|
except_throw(1, 1, "nasty exception");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void top_level(void)
|
|
|
|
{
|
|
|
|
except_cleanup_push(cleanup, "argument");
|
|
|
|
bottom_level();
|
|
|
|
except_cleanup_pop(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
|
|
|
|
except_t *ex;
|
2005-08-06 19:58:45 +00:00
|
|
|
char *msg;
|
2000-05-11 08:18:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Nested exception ``try blocks''
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* outer */
|
|
|
|
except_try_push(catch, 2, &ex);
|
|
|
|
if (!ex) {
|
|
|
|
/* inner */
|
|
|
|
except_try_push(catch, 2, &ex);
|
|
|
|
if (!ex) {
|
|
|
|
top_level();
|
|
|
|
} else {
|
|
|
|
/* inner catch */
|
2005-08-06 19:58:45 +00:00
|
|
|
msg = except_message(ex);
|
|
|
|
if (msg == NULL) {
|
|
|
|
printf("caught exception (inner): s=%ld, c=%ld\n",
|
|
|
|
except_group(ex), except_code(ex));
|
|
|
|
} else {
|
|
|
|
printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
|
|
|
|
msg, except_group(ex), except_code(ex));
|
|
|
|
}
|
2000-05-11 08:18:09 +00:00
|
|
|
except_rethrow(ex);
|
|
|
|
}
|
|
|
|
except_try_pop();
|
|
|
|
} else {
|
|
|
|
/* outer catch */
|
2005-08-06 19:58:45 +00:00
|
|
|
msg = except_message(ex);
|
|
|
|
if (msg == NULL) {
|
|
|
|
printf("caught exception (outer): s=%ld, c=%ld\n",
|
|
|
|
except_group(ex), except_code(ex));
|
|
|
|
} else {
|
|
|
|
printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
|
|
|
|
except_message(ex), except_group(ex), except_code(ex));
|
|
|
|
}
|
2000-05-11 08:18:09 +00:00
|
|
|
}
|
|
|
|
except_try_pop();
|
|
|
|
except_throw(99, 99, "exception in main");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|