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: }