diff --git a/Makefile b/Makefile index 4e9bbf4..58ce247 100644 --- a/Makefile +++ b/Makefile @@ -2,18 +2,19 @@ CC=gcc LD=gcc EXECOBJ= LIBOBJECTS=src/exclib.o -DEMOS=demo/single.exe demo/twolevel.exe demo/trypair.exe demo/catchgroup.exe demo/finally.exe demo/default.exe demo/helpers.exe demo/deepuncaught.exe +DEMOS=demo/single.exe demo/twolevel.exe demo/trypair.exe demo/catchgroup.exe demo/finally.exe demo/default.exe demo/helpers.exe demo/deepuncaught.exe demo/cleanup.exe demo/skeleton.exe LIBTARGET=lib/libexc.a LIBS= -CFLAGS= +#CFLAGS=-Wall -Wextra -std=c89 -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition +CFLAGS=-std=c89 all: lib demo demo/%.exe: demo/%.o lib - $(LD) -o $@ $(CFLAGS) -L./lib $< -lexc -ggdb -gstabs + $(LD) -o $@ $(CFLAGS) -L./lib $< -lexc -ggdb %.o: %.c - $(CC) -c -o $@ $(CFLAGS) -ggdb -gstabs -I./include $< + $(CC) -c -o $@ $(CFLAGS) -ggdb -I./include $< .PHONY: demo demo: $(DEMOS) diff --git a/demo/catchgroup.c b/demo/catchgroup.c index 81f8f68..dec8e01 100644 --- a/demo/catchgroup.c +++ b/demo/catchgroup.c @@ -4,10 +4,12 @@ int main(void) { TRY { THROW(2, NULL); + } EXCEPT { } CATCH_GROUP(1) { } CATCH_GROUP(2) { } CATCH_GROUP(3) { EXCLIB_TRACE("Inside of CATCH_GROUP"); + } FINALLY { } ETRY; return 0; } diff --git a/demo/cleanup.c b/demo/cleanup.c new file mode 100644 index 0000000..4da09a5 --- /dev/null +++ b/demo/cleanup.c @@ -0,0 +1,36 @@ +#include "exclib.h" + +int main(void) +{ + int val = 512; + int *ptr = &val; + + TRY { + THROW(3, "Checking to ensure cleanup behavior executes before EXCEPT block"); + } CLEANUP { + ptr = NULL; + } EXCEPT { + } CATCH(3) { + if ( ptr != NULL ) { + EXCLIB_TRACE("pointer was not reset to NULL in first CLEANUP block"); + return 1; + } + } FINALLY { + } ETRY; + + ptr = &val; + TRY { + /* No exception thrown here, CLEANUP should still execute */ + } CLEANUP { + ptr = NULL; + } EXCEPT { + } FINALLY { + } ETRY; + + if ( ptr != NULL ) { + EXCLIB_TRACE("pointer was not reset to NULL in first TRY block"); + return 1; + } + + return 0; +} diff --git a/demo/deepuncaught.c b/demo/deepuncaught.c index 6caf46e..9c77152 100644 --- a/demo/deepuncaught.c +++ b/demo/deepuncaught.c @@ -6,8 +6,17 @@ int main(void) TRY { TRY { THROW(3, NULL); + } CLEANUP { + } EXCEPT { + } FINALLY { } ETRY; + } CLEANUP { + } EXCEPT { + } FINALLY { } ETRY; + } CLEANUP { + } EXCEPT { + } FINALLY { } ETRY; return 0; diff --git a/demo/default.c b/demo/default.c index e209bb3..0734117 100644 --- a/demo/default.c +++ b/demo/default.c @@ -5,8 +5,12 @@ int main(void) { TRY { THROW(2, NULL); + } CLEANUP { + } EXCEPT { } DEFAULT { EXCLIB_TRACE("Inside of DEFAULT - I catch everything!"); + return 0; + } FINALLY { } ETRY; - return 0; + return 1; } diff --git a/demo/finally.c b/demo/finally.c index 889dbdd..a94ed7d 100644 --- a/demo/finally.c +++ b/demo/finally.c @@ -5,8 +5,9 @@ int main(void) { TRY { THROW(2, NULL); + } EXCEPT { } FINALLY { - printf("I am in the finally clause, and I am about to issue an unhandled exception error.\n"); + printf("I am in the finally clause, and I am about to issue an unhandled exception error.\n"); } ETRY; return 0; } diff --git a/demo/helpers.c b/demo/helpers.c index 5c8758a..9807dce 100644 --- a/demo/helpers.c +++ b/demo/helpers.c @@ -4,10 +4,13 @@ int main(void) { TRY { THROW_ZERO(NULL, EXC_NULLPOINTER, "I just threw with THROW_ZERO"); + } CLEANUP { + } EXCEPT { } CATCH ( EXC_NULLPOINTER ) { THROW_NONZERO(strcmp("not", "equal"), 3, "strcmp was nonzero!"); + } FINALLY { } ETRY; return 0; diff --git a/demo/single.c b/demo/single.c index c49d0ac..cb6c3b7 100644 --- a/demo/single.c +++ b/demo/single.c @@ -12,8 +12,11 @@ int main(void) TRY { THROW(3, NULL); + } CLEANUP { + } EXCEPT { } CATCH(3) { - EXCLIB_TRACE("Caught 3"); + EXCLIB_TRACE("Caught 3"); + } FINALLY { } ETRY; EXCLIB_TRACE("Exiting program"); diff --git a/demo/skeleton.c b/demo/skeleton.c new file mode 100644 index 0000000..5fb82d8 --- /dev/null +++ b/demo/skeleton.c @@ -0,0 +1,10 @@ +#include "exclib.h" + +int main(void) +{ + TRY { + } CLEANUP { + } EXCEPT { + } FINALLY { + } ETRY; +} diff --git a/demo/throwoutside.c b/demo/throwoutside.c index 0310710..af94148 100644 --- a/demo/throwoutside.c +++ b/demo/throwoutside.c @@ -5,11 +5,15 @@ int main(void) TRY { TRY { THROW(3, NULL); + } EXCEPT { } CATCH(5) { EXCLIB_TRACE("Caught 5"); + } FINALLY { } ETRY; + } EXCEPT { } CATCH(3) { EXCLIB_TRACE("Caught 3"); + } FINALLY { } ETRY; return 0; diff --git a/demo/trypair.c b/demo/trypair.c index c6cc59c..af12be4 100644 --- a/demo/trypair.c +++ b/demo/trypair.c @@ -4,15 +4,24 @@ int main(void) { TRY { THROW(3, NULL); + } CLEANUP { + } EXCEPT { } CATCH(3) { + } FINALLY { } ETRY; TRY { THROW(3, NULL); + } CLEANUP { + } EXCEPT { } CATCH(3) { - TRY { - THROW(3, NULL); - } ETRY; + TRY { + THROW(3, NULL); + } CLEANUP { + } EXCEPT { + } FINALLY { + } ETRY; + } FINALLY { } ETRY; EXCLIB_TRACE("Should never get here"); diff --git a/demo/twolevel.c b/demo/twolevel.c index e67fe1e..ad5bad2 100644 --- a/demo/twolevel.c +++ b/demo/twolevel.c @@ -10,8 +10,11 @@ int main(void) { TRY { THROW(3, NULL); + } CLEANUP { + } EXCEPT { } CATCH(3) { - THROW(3, NULL); + THROW(3, NULL); + } FINALLY { } ETRY; EXCLIB_TRACE("Should never get here"); diff --git a/include/exclib.h b/include/exclib.h index 8b2db89..fd9a742 100644 --- a/include/exclib.h +++ b/include/exclib.h @@ -34,12 +34,16 @@ * TRY { * ... do something here ... * THROW(int, "descriptive message") - * } CATCH(int) { - * ... do error handling here ... - * } DEFAULT { - * ... Do something with an exception here without caring about its value ... - * } FINALLY { - * ... Do something here ... + * } CLEANUP { + * ... do any cleanup from your TRY {} block here ... + * } EXCEPT { + * } CATCH(int) { + * ... do error handling here ... + * } DEFAULT { + * ... Do something with an exception here without caring about its value ... + * } FINALLY { + * ... Do something here regardless of which exception occurred, but ONLY if an exception occurred ... + * } * } ETRY; * * The syntax is fairly obvious. Code inside the TRY block is executed, and any exceptions are propagated out to the CATCH blocks. @@ -47,10 +51,18 @@ * integer exception. If DEFAULT is not present and there is no CATCH block, then the exception is propagated back up the stack to the * first TRY() block encountered in the program. (Unhandled exceptions behave as in #5,6 in the bullet list above.) * + * DO NOT PLACE ANY CODE BETWEEN THE EXCEPT { AND THE FIRST CATCH {. IT WILL NEVER BE EXECUTED. + * + * The CLEANUP block is optional and is executed before any exceptions are processed. Use this to clean up any resources which must be + * handled before the EXCEPT{} block potentially sends control out of the currrent scope, orphaning resources like file handles and + * memory. + * * The FINALLY clause is executed after the try block has executed, and after any applicable CATCH/DEFAULT blocks. If present, it will * be executed REGARDLESS of which exception was actually caught. This is useful to examine an exception as it passes through * without actually handling/modifying it, or to execute some generic action on exception, regardless of what type, but only after - * specific exceptions have been handled + * specific exceptions have been handled. Beware, however, that FINALLY is executed AFTER CATCH blocks, therefore one of the CATCH blocks + * may have transferred control outside of the TRY {} statement, meaning that the FINALLY {} clause never actually gets executed. + * Cleaning up resources should be done in CLEANUP, not FINALLY. * * THROW_ZERO is a convenience function to replace blocks like this: * @@ -79,7 +91,7 @@ * ... * } CATCH (EXC_NULLPOINTER) { * ... - * } + * } ETRY; * * This example also highlights one of the problems of this library : You can't check for multiple exceptions on the same block, * because it's all actually a hidden 'switch' statement. But you can use a mechanism like shown above; when you have multiple @@ -87,6 +99,14 @@ * does not provide a break in the flow between the previous case and the current one, so fallthrough is achieved. Just make * sure to use a CATCH on any proceeding exceptions that don't need to fall through (like the EXC_NULLPOINTER above). * + * You MUST use TRY / EXCEPT / FINALLY / ETRY. This is the minimal form: + * + * TRY { + * } EXCEPT { + * } FINALLY { + * } ETRY; + * + * Without this, the generated code will be syntactically incorrect for the preprocessor. */ #ifndef EXC_STRBUF_SIZE @@ -95,62 +115,69 @@ #ifndef EXC_MAX_FRAMES #define EXC_MAX_FRAMES 50 -#endif // EXC_MAX_FRAMES +#endif /* EXC_MAX_FRAMES */ #ifndef EXC_MAX_EXCEPTIONS #define EXC_MAX_EXCEPTIONS 4096 -#endif // EXC_MAX_EXCEPTIONS +#endif /* EXC_MAX_EXCEPTIONS */ #define TRY \ if (__exclib_curidx >= EXC_MAX_FRAMES) \ exclib_print_exception_stack("No available exception stack context", __FILE__, (char *)__func__, __LINE__); \ if ( exclib_new_exc_frame(&__exclib_statuses[__exclib_curidx++], __FILE__, (char *)__func__, __LINE__) != 0) \ exclib_print_exception_stack("Tried to TRY but couldn't create new exception frame", __FILE__, (char *)__func__, __LINE__); \ -EXCLIB_EXCEPTION->value = setjmp(EXCLIB_EXCEPTION->buf); \ -EXCLIB_EXCEPTION->tried = 1;\ -switch( EXCLIB_EXCEPTION->value ) { \ - case 0: + EXCLIB_EXCEPTION->setjmpstatus = setjmp(EXCLIB_EXCEPTION->buf); \ +EXCLIB_EXCEPTION->tried = 1; \ + +#define CLEANUP + +#define EXCEPT \ + if ( EXCLIB_EXCEPTION && EXCLIB_EXCEPTION->setjmpstatus != 0 ) { \ + switch( EXCLIB_EXCEPTION->value ) { \ + case 0: #define CATCH(x) \ - break; \ - case x: \ - EXCLIB_EXCEPTION->caught = 1; \ - EXCLIB_EXCEPTION->catching = 1; + break; \ + case x: \ + EXCLIB_EXCEPTION->caught = 1; \ + EXCLIB_EXCEPTION->catching = 1; #define CATCH_GROUP(x) \ - case x: \ - EXCLIB_EXCEPTION->caught = 1; \ - EXCLIB_EXCEPTION->catching = 1; + case x: \ + EXCLIB_EXCEPTION->caught = 1; \ + EXCLIB_EXCEPTION->catching = 1; #define DEFAULT \ - break; \ - default: \ - EXCLIB_EXCEPTION->caught = 1; \ - EXCLIB_EXCEPTION->catching = 1; + break; \ + default: \ + EXCLIB_EXCEPTION->caught = 1; \ + EXCLIB_EXCEPTION->catching = 1; + +#define FINALLY \ + }; \ #define ETRY \ }; \ exclib_clear_exc_frame(); \ __exclib_curidx--; -#define FINALLY \ -}; \ -if ( EXCLIB_EXCEPTION && EXCLIB_EXCEPTION->value ) { -#define THROW_NONZERO(x, y, z) __exclib_rc = (x); if ( __exclib_rc != 0 ) { __exclib_rc += y; THROW(__exclib_rc, z); } -#define THROW_ZERO(x, y, z) if ( (x) == 0 ) THROW(y, z); +#define THROW_NONZERO(x, y, z) if ( (x) != 0 ) { THROW(y, z); } +#define THROW_ZERO(x, y, z) if ( (x) == 0 ) { THROW(y, z); } #define THROW(x, y) \ - THROW_EXPLICIT(x, y, __FILE__, (char *)__func__, __LINE__, 1); + THROW_EXPLICIT(x, y, __FILE__, (char *)__func__, __LINE__, 1) #define THROW_EXPLICIT(x, y, file, func, line, setflag) \ - exclib_prep_throw(x, y, file, func, line, setflag); \ - if ( EXCLIB_EXCEPTION->thrown > 0 ) { \ - longjmp(EXCLIB_EXCEPTION->buf, x); \ - } else { \ - sprintf((char *)&__exclib_strbuf, "Uncaught exception %d", x); \ - exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); \ - exit(x); \ + if ( EXCLIB_EXCEPTION && EXCLIB_EXCEPTION->setjmpstatus == 0 ) { \ + exclib_prep_throw(x, y, file, func, line, setflag); \ + if ( EXCLIB_EXCEPTION->thrown > 0 ) { \ + longjmp(EXCLIB_EXCEPTION->buf, x); \ + } else { \ + sprintf((char *)&__exclib_strbuf, "Uncaught exception %d", x); \ + exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); \ + exit(x); \ + } \ } #define EXCLIB_TRACE(x) exclib_print_exception_stack(x, __FILE__, (char *)__func__, __LINE__) @@ -170,6 +197,7 @@ struct exclib_status { struct exclib_status *next; struct exclib_status *prev; jmp_buf buf; + int setjmpstatus; int value; int caught; int tried; @@ -198,4 +226,4 @@ extern void exclib_print_exception_stack(char *mbuf, char *file, char *func, int extern int exclib_new_exc_frame(struct exclib_status *es, char *file, char *function, int line); extern int exclib_clear_exc_frame(); -#endif // __EXCLIB_H__ +#endif /* __EXCLIB_H__ */ diff --git a/src/exclib.c b/src/exclib.c index 6fb3e98..66bbfd2 100644 --- a/src/exclib.c +++ b/src/exclib.c @@ -21,7 +21,7 @@ struct exclib_name_data __exclib_exc_names[EXC_PREDEFINED_EXCEPTIONS] = { void exclib_print_exception_stack(char *mbuf, char *file, char *func, int line) { - char buf[256]; + char buf[512]; char flagbuf[256]; char *excname = NULL; struct exclib_status *cur = EXCLIB_EXCEPTION; @@ -72,9 +72,9 @@ void exclib_print_exception_stack(char *mbuf, char *file, char *func, int line) else excname = "NULL"; sprintf((char *)&buf, - "EXCLIB: #%d[0x%x] %s:%d:%s:%s:%d:%s: %s\n", + "EXCLIB: #%d[0x%lx] %s:%d:%s:%s:%d:%s: %s\n", idx, - cur, + (unsigned long int)cur, cur->file, cur->line, cur->function, @@ -93,7 +93,7 @@ void exclib_bulk_name_exceptions(struct exclib_name_data *exclib_exc_names, int { struct exclib_name_data *ptr; int i = 0; - //THROW_ZERO(exclib_exc_names, EXC_NULLPOINTER, "Null Pointer"); + /*THROW_ZERO(exclib_exc_names, EXC_NULLPOINTER, "Null Pointer");*/ ptr = exclib_exc_names; for ( i = 0; i < size ; i++) { exclib_name_exception(ptr->exc, ptr->name); @@ -113,7 +113,7 @@ void exclib_init() return; __exclib_inited = 1; memset(&__exclib_statuses, 0x00, sizeof(struct exclib_status) * EXC_MAX_FRAMES); - // for whatever reason memset isn't safe here, so ... + /* for whatever reason memset isn't safe here, so ... */ for ( i = 0 ; i < EXC_MAX_EXCEPTIONS; i++ ) { __exclib_names[i] = NULL; } @@ -123,29 +123,29 @@ void exclib_init() void exclib_prep_throw(int value, char *msg, char *file, char *func, int line, int setflag) { if ( EXCLIB_EXCEPTION && EXCLIB_EXCEPTION->tried == 1) { - sprintf((char *)&__exclib_strbuf, "Tried to THROW %d but couldn't create new exception frame", value); - if ( EXCLIB_EXCEPTION->catching == 1 && EXCLIB_EXCEPTION->prev ) { - if ( exclib_new_exc_frame(EXCLIB_EXCEPTION, file, func, line) ) { - exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); - exit(value); - } - memcpy(EXCLIB_EXCEPTION->buf, EXCLIB_EXCEPTION->prev->buf, sizeof(jmp_buf)); - if ( setflag ) - EXCLIB_EXCEPTION->thrown = 1; - } else if ( EXCLIB_EXCEPTION->catching == 1 && (EXCLIB_EXCEPTION->prev == NULL) ) { - exclib_clear_exc_frame(); - } else { - if ( exclib_new_exc_frame(EXCLIB_EXCEPTION, file, func, line) ) - exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); - if ( setflag ) - EXCLIB_EXCEPTION->thrown = 1; - } - if ( setflag ) { - EXCLIB_EXCEPTION->value = value; - EXCLIB_EXCEPTION->name = __exclib_names[value]; - EXCLIB_EXCEPTION->description = msg; - } - return; + sprintf((char *)&__exclib_strbuf, "Tried to THROW %d but couldn't create new exception frame", value); + if ( EXCLIB_EXCEPTION->catching == 1 && EXCLIB_EXCEPTION->prev ) { + if ( exclib_new_exc_frame(EXCLIB_EXCEPTION, file, func, line) ) { + exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); + exit(value); + } + memcpy(EXCLIB_EXCEPTION->buf, EXCLIB_EXCEPTION->prev->buf, sizeof(jmp_buf)); + if ( setflag ) + EXCLIB_EXCEPTION->thrown = 1; + } else if ( EXCLIB_EXCEPTION->catching == 1 && (EXCLIB_EXCEPTION->prev == NULL) ) { + exclib_clear_exc_frame(); + } else { + if ( exclib_new_exc_frame(EXCLIB_EXCEPTION, file, func, line) ) + exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); + if ( setflag ) + EXCLIB_EXCEPTION->thrown = 1; + } + if ( setflag ) { + EXCLIB_EXCEPTION->value = value; + EXCLIB_EXCEPTION->name = __exclib_names[value]; + EXCLIB_EXCEPTION->description = msg; + } + return; } sprintf((char *)&__exclib_strbuf, "Tried to THROW Exception %d but had no exception context. (Called outside of TRY block, or thrown while TRY was setting up?)", value); exclib_print_exception_stack((char *)&__exclib_strbuf, file, func, line); @@ -190,14 +190,13 @@ int exclib_clear_exc_frame() if ( es->prev ) es->prev->next = NULL; if ( es->thrown && !es->caught ) { - // thrown exception was unhandled - do we have anywhere else to go? + /* thrown exception was unhandled - do we have anywhere else to go? */ if ( !es->prev ) { - // No frame above us to propagate this into, stacktrace and kill ourselves + /* No frame above us to propagate this into, stacktrace and kill ourselves */ exclib_print_exception_stack("Uncaught exception", es->file, es->function, es->line); exit(es->value); - //kill(getpid(), SIGKILL); } else if ( es->tried && es->prev && (es->catching == 0)) { - // copy this exception up into the upper frame and siglongjmp back to that + /* copy this exception up into the upper frame and siglongjmp back to that */ EXCLIB_EXCEPTION = es->prev; EXCLIB_EXCEPTION->caught = 0; EXCLIB_EXCEPTION->name = es->name; @@ -216,5 +215,6 @@ int exclib_clear_exc_frame() } memset((void *)es, 0x00, sizeof(struct exclib_status)); } + return 0; }