@@@ fltfmt mess
[mLib] / utils / exc.h
CommitLineData
0875b58f 1/* -*-c-*-
2 *
0875b58f 3 * Structured exception handling in C
4 *
5 * (c) 1998 Straylight/Edgeware
6 */
7
d4efbcd9 8/*----- Licensing notice --------------------------------------------------*
0875b58f 9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software; you can redistribute it and/or modify
c846879c 13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
d4efbcd9 16 *
0875b58f 17 * mLib is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
c846879c 20 * GNU Library General Public License for more details.
d4efbcd9 21 *
c846879c 22 * You should have received a copy of the GNU Library General Public
0bd98442 23 * License along with mLib; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
0875b58f 26 */
0875b58f 27
c6e0eaf0 28#ifndef MLIB_EXC_H
29#define MLIB_EXC_H
0875b58f 30
31#ifdef __cplusplus
32 extern "C" {
33#endif
34
35#include <setjmp.h>
36
b1a20bee
MW
37#ifndef MLIB_MACROS_H
38# include "macros.h"
39#endif
40
0875b58f 41/*----- Quick documentation -----------------------------------------------*
42 *
43 * This header file provides some exception handling facilities in C
44 * programs. It modifies the syntax of the language slightly, using the
45 * preprocessor.
46 *
47 * The `throw' expression returns no value. It has the syntax:
48 *
49 * THROW ( expr , expr )
50 *
51 * The first expression must have type compatible with unsigned integer; it
52 * identifies an `exception type'. The second must have type compatible
53 * with pointer to void; it contains the `exception data'. Control is
54 * passed to the current exception handler.
55 *
56 * The `RETHROW' expression, valid only within an exception handler, causes
57 * the current exception to be thrown again.
58 *
59 * A `try' statement has the syntax:
60 *
61 * TRY stat CATCH stat END_TRY;
62 *
63 * The first statement is called the `test'; the second is the `handler'.
64 * During execution of the test, the handler is added to a stack of
65 * active exception handlers; the topmost handler on this stack is called
66 * the `current' handler. When execution of the test completes, the
67 * corresponding handler is removed from the stack.
68 *
69 * The test statement may complete in one of these ways:
70 *
71 * * Normal completion -- control reaches the end of the statement
72 * normally.
73 *
74 * * Throwing an exception -- an exception is thrown when the handler is
75 * the current exception handler.
76 *
77 * * By executing a `break' statement.
78 *
79 * * By executing the expression `EXIT_TRY' and transferring control to
80 * a point outside the entire `try' statement (e.g., executing a `goto'
81 * or `return' statement).
82 *
83 * Any other attempt to leave the test causes undefined behaviour.
84 *
85 * If an exception is thrown while the handler is the current exception
86 * handler, it is given control. The variables `exc_type' and `exc_val'
87 * denote the exception type and value respectively -- they are passed
88 * unchanged from the `throw' expression which caused the exception.
89 * A handler is deactivated before it is invoked; if it causes an
90 * exception to be thrown (and does not contain a nested `try' statement)
91 * control will be passed to an earlier active handler.
92 *
93 * Control is passed to handlers using the `longjmp' function.
94 *
95 * Example:
96 *
97 * TRY {
98 * ... something dangerous ...
99 * } CATCH switch (exc_type) {
100 * case EXC_INTERESTING:
d4efbcd9
MW
101 * ... handle exception ...
102 * break;
0875b58f 103 * default:
d4efbcd9
MW
104 * ... do tidying up ...
105 * RETHROW;
0875b58f 106 * } END_TRY;
107 */
108
109/*----- Exception type allocation -----------------------------------------*
110 *
111 * Nobody allocates exception types, so we'll just have to try to get along
112 * without too many collisions. An exception type is an unsigned long,
113 * which gives us four bytes. The top two bytes identify the library which
114 * `owns' the exception, with special values zero meaning `defined as part
115 * of the system' and 0xFFFF providing a shared space of types which can
116 * be used by anyone as long as they don't get seen by anyone else.
117 *
118 * The lower byte pair encodes a type number, and a value which defines
119 * the type of the value field (see below).
120 */
121
7d40699f 122/* --- Type of an exception --- */
0875b58f 123
124typedef unsigned long exc_extype;
125
126/* --- Build a byte pair from two characters --- *
127 *
128 * Note the icky casting to handle signed chars.
129 */
130
131#define EXC_PAIR(x, y) (((unsigned long)(unsigned char)(x) << 8) | \
132 (unsigned long)(unsigned char)(y))
133
134/* --- Allocate an exception number --- */
135
136#define EXC_ALLOC(owner, type) (((unsigned long)(owner) << 16) | \
137 (unsigned long)(type))
138
139/* --- Special owner codes --- */
140
141#define EXC_GLOBAL 0u /* The global space defined here */
142#define EXC_SHARED 0xFFFFu /* The shared space for everyone */
e1ccd441 143#define EXC_MLIB EXC_PAIR('m', 'L') /* Space for mLib exceptions */
0875b58f 144
145/*----- Exception values --------------------------------------------------*
146 *
147 * Exception values can have several different types. This is a mess, and
148 * C doesn't handle it too well, but we can try. I'll encode the value type
149 * as part of the exception type, in the top bits of the bottom byte. Messy?
150 * You betcha.
151 */
152
153/* --- Encoding a value type in an extype --- */
154
155#define EXC_TYPECODE(t, w) (((w) & ~0xC0u) | ((t) & 0xC0u))
156
157/* --- The various value types --- */
158
159#define EXC_NOVAL 0x00u /* No interesting value */
160#define EXC_INTVAL 0x40u /* Integer value */
161#define EXC_PTRVAL 0x80u /* Arbitrary pointer value */
162#define EXC_STRVAL 0xC0u /* Pointer to character string */
163
164/* --- Allocating exceptions with appropriate types --- */
165
166#define EXC_ALLOCN(o, t) EXC_TYPECODE(EXC_NOVAL, EXC_ALLOC(o, t))
167#define EXC_ALLOCI(o, t) EXC_TYPECODE(EXC_INTVAL, EXC_ALLOC(o, t))
168#define EXC_ALLOCP(o, t) EXC_TYPECODE(EXC_PTRVAL, EXC_ALLOC(o, t))
169#define EXC_ALLOCS(o, t) EXC_TYPECODE(EXC_STRVAL, EXC_ALLOC(o, t))
170
171/* --- A union representing the type --- */
172
173typedef union exc_exval {
174 int i;
175 void *p;
176 char *s;
177} exc_exval;
178
179/*----- Predefined exceptions ---------------------------------------------*/
180
181/* --- @EXC_NOMEM@ --- *
182 *
183 * Value: ---
184 *
185 * Meaning: An attempt to allocate memory failed.
186 */
187
188#define EXC_NOMEM EXC_ALLOCN(EXC_GLOBAL, 0u)
189
190/* --- @EXC_ERRNO@ --- *
191 *
192 * Value: @int errno@ = the error raised
193 *
194 * Meaning: Some kind of OS error occurred.
195 */
196
197#define EXC_ERRNO EXC_ALLOCI(EXC_GLOBAL, 1u)
198
199/* --- @EXC_OSERROR@ --- *
200 *
201 * Value: @os_error *e@ = pointer to error block
202 *
203 * Meaning: For RISC OS programmers only: alternative way of propagating
204 * errors.
205 */
206
207#define EXC_OSERROR EXC_ALLOCP(EXC_GLOBAL, 1u)
208
209/* --- @EXC_SIGNAL@ --- *
210 *
211 * Value: @int sig@ = signal number
212 *
213 * Meaning: Report the raising of a signal.
214 */
215
216#define EXC_SIGNAL EXC_ALLOCI(EXC_GLOBAL, 2u)
217
218/* --- @EXC_FAIL@ --- *
219 *
220 * Value: @const char *p@ = pointer to expanatory string
221 *
222 * Meaning: Miscellaneous error.
223 */
224
225#define EXC_FAIL EXC_ALLOCS(EXC_GLOBAL, 0xFFu)
226
227/*----- An exception handler block ----------------------------------------*/
228
229/* --- Try to think of this as being opaque --- */
230
231typedef struct __exc_hnd {
232 struct __exc_hnd *next; /* Pointer to next record down */
233 exc_extype type; /* Type of this exception */
234 exc_exval val; /* Value of this exception */
235 jmp_buf buf; /* Jump buffer when exceptions hit */
236} __exc_hnd;
237
238/*----- Global variables --------------------------------------------------*/
239
240extern __exc_hnd *__exc_list; /* List of active handlers */
241
242/*----- Macros ------------------------------------------------------------*/
243
244/* --- References to current exception type and value --- */
245
246#define exc_type (__exc_ec.type)
247#define exc_val (__exc_ec.val)
248#define exc_i (__exc_ec.val.i)
249#define exc_p (__exc_ec.val.p)
250#define exc_s (__exc_ec.val.s)
251
252/* --- How it actually works --- *
253 *
254 * A `try' block is contained within a block which provides an exception
255 * handler buffer in automatic storage. This block is a loop, to allow
256 * `break' to escape from it. It adds the handler buffer to the top of a
257 * list, and does a `setjmp' to allow a return here following an exception.
258 * The `setjmp' returns zero for the `try' section, and nonzero if there's
259 * an exception to `catch'. It looks a little like this:
260 *
261 * do {
262 * __exc_hnd h;
263 * add_handler(&h);
264 * if (!setjmp(h.buf)) {
d4efbcd9
MW
265 * do <try code> while (0);
266 * remove_handler(&h);
0875b58f 267 * } else
d4efbcd9 268 * <catch code>
0875b58f 269 * } while (0)
270 *
271 * Everything else is ugly hacking to make things work.
272 */
273
274/* --- Trying things which may cause exceptions --- */
275
276#define TRY do { \
277 volatile __exc_hnd __exc_ec; \
278 __exc_ec.next = __exc_list; \
279 __exc_list = (__exc_hnd *)&__exc_ec; \
280 if (!setjmp(*(jmp_buf *)&__exc_ec.buf /* very nasty! */ )) { do
281
282#define EXIT_TRY do __exc_list = __exc_ec.next; while (0)
283#define CATCH while (0); EXIT_TRY; } else
284
285#define END_TRY } while (0)
286
287/* --- Raising exceptions --- */
288
289#define THROW __exc_throw
290#define RETHROW __exc_rethrow(__exc_ec.type, __exc_ec.val)
291
292/*----- Functions ---------------------------------------------------------*/
293
294/* --- @exc_uncaught@ --- *
295 *
296 * Arguments: @void (*proc)(exc_extype type, exc_exval val) = new handler
297 *
298 * Returns: Pointer to the old handler value.
299 *
300 * Use: Sets the handler for uncaught exceptions.
301 */
302
303typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/);
304extern exc__uncaught exc_uncaught(exc__uncaught /*proc*/);
305
306/* --- @__exc_throw@ --- *
307 *
308 * Arguments: @exc_extype type@ = type of exception to throw
309 *
310 * Returns: Doesn't
311 *
312 * Use: NOT FOR USER CONSUMPTION. Reads an appropriate exception
313 * value and throws an exception.
314 */
315
b1a20bee 316extern NORETURN void __exc_throw(exc_extype /*type*/, ...);
0875b58f 317
318/* --- @__exc_rethrow@ --- *
319 *
320 * Arguments: @exc_extype type@ = type of exception to throw
321 * @exc_exval val@ = value of exception to throw
322 *
323 * Returns: Doesn't
324 *
325 * Use: NOT FOR USER CONSUMPTION. Does the donkey-work of raising
326 * an exception.
327 */
328
b1a20bee 329extern NORETURN void __exc_rethrow(exc_extype /*type*/, exc_exval /*val*/);
0875b58f 330
331/*----- That's all, folks -------------------------------------------------*/
332
333#ifdef __cplusplus
334 }
335#endif
336
337#endif