1: /* exception.c --- source implementing lightweight, but featured 2: exception handling. 4: Time-stamp: "2005-04-10 17:06:35 admp" 6: Copyright (C) 2003-5 Adomas P. <adomas.paltanavicius@gmail.com> 8: This program is free software; you can redistribute it and/or modify 9: it under the terms of the GNU General Public License as published by 10: the Free Software Foundation; either version 2 of the License, or 11: (at your option) any later version. 13: This program is distributed in the hope that it will be useful, 14: but WITHOUT ANY WARRANTY; without even the implied warranty of 15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16: GNU General Public License for more details. 18: You should have received a copy of the GNU General Public License 19: along with this program; if not, write to the Free Software 20: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ 22: /* This code relies on few customization macros, and thus 23: it must be recompiled once those are changed. 24: */ 26: #include "exception-local.h" 29: /* Variable declarations at first. */ 30: 31: /* This counter is used by __EXC_BLOCK. It works well even if nested. */ 32: volatile int __exc_block_pass; 34: /* Flag to be set by ON? */ 35: volatile int __exc_handled; 37: /* For indexing every call to TRY. */ 38: volatile unsigned __exc_tries; 40: /* These identify the thrown exception. File, function, line and 41: the exception itself. */ 42: char *__exc_file; 43: char *__exc_function; 44: unsigned __exc_line; 45: volatile __EXC_TYPE __exc_code; 47: /* This is the global stack of catchers. */ 48: struct __exc_stack *__exc_global; 52: #ifdef __EXC_DEBUG 54: /* Prints error message. */ 55: void 56: __exc_debug (char *fmt, ...) 57: { 58: va_list ap; 60: fprintf (__EXC_STREAM, "__EXC: "); 61: va_start (ap, fmt); 62: vfprintf (__EXC_STREAM, fmt, ap); 63: va_end (ap); 64: fprintf (__EXC_STREAM, "\n"); 65: } 69: /* For printing __exc_global. */ 70: void 71: __exc_print_global () 72: { 73: struct __exc_stack *level = __exc_global; 74: unsigned items; 76: if (level == NULL) 77: { 78: fprintf (__EXC_STREAM, "Stack empty\n"); 79: return; 80: } 82: fprintf (__EXC_STREAM, "Current stack (from bottom to top):\n"); 83: for (items = 0; level; level = level->prev) 84: { 85: fprintf (__EXC_STREAM, "%c ", items == 0 ? '[' : ' '); 87: /* FIXME: does printing jmp_buf literally (as we now address and 88: its size) good? No, unless human understands it. One 89: can't unless he chooses one popular C library and study the 90: implementation carefully;) */ 91: fprintf (__EXC_STREAM, "%u", level->num); 92: /* TODO: newline every four or so items. Use tab stops. */ 93: fprintf (__EXC_STREAM, " %c\n", level->prev ? ' ' : ']'); 94: items++; 95: } 97: fprintf (__EXC_STREAM, "Totally %u items.\n", items); 98: } 100: #endif 104: /* Prints information about exception. Called in debug mode, or when 105: no handler is found. */ 107: void 108: __exc_print (FILE *stream, char *file, char *function, unsigned line, 109: __EXC_TYPE code) 110: { 111: fprintf (stream, "Exception in file \"%s\", at line %u", 112: file, line); 113: if (function) 114: { 115: fprintf (stream, ", in function \"%s\"", function); 116: } 117: fprintf (stream, "."); 119: #ifdef __EXC_PRINT 120: fprintf (stream, " Exception: "); 121: __EXC_PRINT (code, stream); 122: #endif 123: fprintf (stream, "\n"); 124: } 128: /* Pop exception from stack, putting into J (if nonzero). If stack is 129: empty, print error message and exit. Used in EXCEPT. */ 130: void 131: __exc_pop (jmp_buf *j) 132: { 133: struct __exc_stack *stored = __exc_global; 135: __exc_debug ("POP () to %p", j); 137: if (stored == NULL) 138: { 139: __exc_debug ("Unhandled exception."); 141: fprintf (stderr, "Unhandled exception:\n"); 142: __exc_print (stderr, __exc_file, __exc_function, 143: __exc_line, __exc_code); 145: exit (3); 146: } 148: __exc_global = stored->prev; 150: if (j) 151: { 152: /* This assumes that JMP_BUF is a structure etc. and can be 153: copied rawely. This is true in GLIBC, as I know. */ 154: memcpy (j, &stored->j, sizeof (jmp_buf)); 155: } 157: __exc_debug ("Popped"); 158: __exc_print_global (); 160: /* While with MALLOC, free. When using obstacks it is better not to 161: free and hold up. */ 162: free (stored); 163: } 167: /* Push J onto the stack, with RETURNED as value from SETJMP. Return 168: nonzero, if RETURNED is 0. If RETURNED is nonzero, returns 0. 169: Used in TRY. */ 171: int 172: __exc_push (jmp_buf *j, int returned) 173: { 174: struct __exc_stack *new; 176: __exc_debug ("PUSH (), %p, %d", j, returned); 178: /* SETJMP returns 0 first time, nonzero from __EXC_THROW. 179: Returning false-like value here (0) will enter the 180: else branch (that is, EXCEPT.) */ 181: if (returned != 0) 182: { 183: __exc_debug ("Returning from THROW"); 184: return 0; 185: } 187: /* Since this didn't come from THROW, fine to increase counter. */ 188: ++__exc_tries; 189: __exc_debug ("This is PUSH () number %u", __exc_tries); 191: /* Using memcpy here is the best alternative. */ 192: new = malloc (sizeof (struct __exc_stack)); 193: memcpy (&new->j, j, sizeof (jmp_buf)); 194: new->num = __exc_tries; 195: new->prev = __exc_global; 196: __exc_global = new; 198: __exc_print_global (); 200: return 1; 201: } 205: /* Throw an exception in FILE at LINE, with code CODE. Used in THROW. */ 207: void 208: __exc_throw (char *file, char *function, unsigned line, __EXC_TYPE code) 209: { 210: jmp_buf j; 212: __exc_debug ("THROW ()"); 213: #if defined __EXC_DEBUG 214: __exc_print (__EXC_STREAM, file, function, line, code); 215: #endif 217: __exc_file = file; 218: __exc_function = function; 219: __exc_line = line; 220: __exc_code = code; 222: /* Pop for jumping. */ 223: __exc_pop (&j); 224: 225: __exc_debug ("Jumping to the handler"); 227: /* LONGJUMP to J with nonzero value. */ 228: longjmp (j, 1); 229: } 231: /* Throw it in upper level of catcher blocks. */ 233: void 234: __exc_rethrow () 235: { 236: jmp_buf j; 238: __exc_debug ("RETHROW ()"); 239: #ifdef __EXC_DEBUG 240: __exc_print (__EXC_STREAM, __exc_file, __exc_function, 241: __exc_line, __exc_code); 242: #endif 244: __exc_pop (&j); 245: longjmp (j, 1); 246: } 250: #ifdef __EXC_DEBUG 251: # define __EXC_NDEBUG_UN(decl) decl 252: #else 253: # define __EXC_NDEBUG_UN(decl) decl __attribute__((unused)) 254: #endif 256: int 257: __exc_on (__EXC_NDEBUG_UN(char *file), 258: __EXC_NDEBUG_UN(char *function), 259: __EXC_NDEBUG_UN(unsigned line), 260: __EXC_TYPE code) 261: { 262: /* It is impossible to jump to the end of EXCEPT block, nor it is 263: possible to mix ELSE with IF, so use before-defined __EXC_HANDLED 264: flag. 266: Actually, use of 267: on (...) ...; 268: else on (...) ...; 270: Is possible, but don't force the *user* to care about it. */ 272: __exc_debug ("ON ()"); 273: __exc_debug ("Trying to handle in file \"%s\", at line %u", file, line); 274: #ifdef __EXC_DEBUG 275: if (function) 276: { 277: __exc_debug ("In function \"%s\".", function); 278: } 279: #endif 281: if (__exc_handled == 1) 282: { 283: __exc_debug ("Exception already handled in this level, skip"); 284: return 0; 285: } 286: 287: if (__EXC_EQ (code, __exc_code)) 288: { 289: __exc_debug ("This handler FITS"); 291: __exc_handled = 1; 292: return 1; 293: } 295: __exc_debug ("This handler DOESN'T FIT"); 297: /* Not matched. */ 298: return 0; 299: }