--- /dev/null
+/* -*-c-*-
+ *
+ * Structured exception handling in C
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef MLIB_EXC_H
+#define MLIB_EXC_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include <setjmp.h>
+
+/*----- Quick documentation -----------------------------------------------*
+ *
+ * This header file provides some exception handling facilities in C
+ * programs. It modifies the syntax of the language slightly, using the
+ * preprocessor.
+ *
+ * The `throw' expression returns no value. It has the syntax:
+ *
+ * THROW ( expr , expr )
+ *
+ * The first expression must have type compatible with unsigned integer; it
+ * identifies an `exception type'. The second must have type compatible
+ * with pointer to void; it contains the `exception data'. Control is
+ * passed to the current exception handler.
+ *
+ * The `RETHROW' expression, valid only within an exception handler, causes
+ * the current exception to be thrown again.
+ *
+ * A `try' statement has the syntax:
+ *
+ * TRY stat CATCH stat END_TRY;
+ *
+ * The first statement is called the `test'; the second is the `handler'.
+ * During execution of the test, the handler is added to a stack of
+ * active exception handlers; the topmost handler on this stack is called
+ * the `current' handler. When execution of the test completes, the
+ * corresponding handler is removed from the stack.
+ *
+ * The test statement may complete in one of these ways:
+ *
+ * * Normal completion -- control reaches the end of the statement
+ * normally.
+ *
+ * * Throwing an exception -- an exception is thrown when the handler is
+ * the current exception handler.
+ *
+ * * By executing a `break' statement.
+ *
+ * * By executing the expression `EXIT_TRY' and transferring control to
+ * a point outside the entire `try' statement (e.g., executing a `goto'
+ * or `return' statement).
+ *
+ * Any other attempt to leave the test causes undefined behaviour.
+ *
+ * If an exception is thrown while the handler is the current exception
+ * handler, it is given control. The variables `exc_type' and `exc_val'
+ * denote the exception type and value respectively -- they are passed
+ * unchanged from the `throw' expression which caused the exception.
+ * A handler is deactivated before it is invoked; if it causes an
+ * exception to be thrown (and does not contain a nested `try' statement)
+ * control will be passed to an earlier active handler.
+ *
+ * Control is passed to handlers using the `longjmp' function.
+ *
+ * Example:
+ *
+ * TRY {
+ * ... something dangerous ...
+ * } CATCH switch (exc_type) {
+ * case EXC_INTERESTING:
+ * ... handle exception ...
+ * break;
+ * default:
+ * ... do tidying up ...
+ * RETHROW;
+ * } END_TRY;
+ */
+
+/*----- Exception type allocation -----------------------------------------*
+ *
+ * Nobody allocates exception types, so we'll just have to try to get along
+ * without too many collisions. An exception type is an unsigned long,
+ * which gives us four bytes. The top two bytes identify the library which
+ * `owns' the exception, with special values zero meaning `defined as part
+ * of the system' and 0xFFFF providing a shared space of types which can
+ * be used by anyone as long as they don't get seen by anyone else.
+ *
+ * The lower byte pair encodes a type number, and a value which defines
+ * the type of the value field (see below).
+ */
+
+/* --- Type of an exception --- */
+
+typedef unsigned long exc_extype;
+
+/* --- Build a byte pair from two characters --- *
+ *
+ * Note the icky casting to handle signed chars.
+ */
+
+#define EXC_PAIR(x, y) (((unsigned long)(unsigned char)(x) << 8) | \
+ (unsigned long)(unsigned char)(y))
+
+/* --- Allocate an exception number --- */
+
+#define EXC_ALLOC(owner, type) (((unsigned long)(owner) << 16) | \
+ (unsigned long)(type))
+
+/* --- Special owner codes --- */
+
+#define EXC_GLOBAL 0u /* The global space defined here */
+#define EXC_SHARED 0xFFFFu /* The shared space for everyone */
+#define EXC_MLIB EXC_PAIR('m', 'L') /* Space for mLib exceptions */
+
+/*----- Exception values --------------------------------------------------*
+ *
+ * Exception values can have several different types. This is a mess, and
+ * C doesn't handle it too well, but we can try. I'll encode the value type
+ * as part of the exception type, in the top bits of the bottom byte. Messy?
+ * You betcha.
+ */
+
+/* --- Encoding a value type in an extype --- */
+
+#define EXC_TYPECODE(t, w) (((w) & ~0xC0u) | ((t) & 0xC0u))
+
+/* --- The various value types --- */
+
+#define EXC_NOVAL 0x00u /* No interesting value */
+#define EXC_INTVAL 0x40u /* Integer value */
+#define EXC_PTRVAL 0x80u /* Arbitrary pointer value */
+#define EXC_STRVAL 0xC0u /* Pointer to character string */
+
+/* --- Allocating exceptions with appropriate types --- */
+
+#define EXC_ALLOCN(o, t) EXC_TYPECODE(EXC_NOVAL, EXC_ALLOC(o, t))
+#define EXC_ALLOCI(o, t) EXC_TYPECODE(EXC_INTVAL, EXC_ALLOC(o, t))
+#define EXC_ALLOCP(o, t) EXC_TYPECODE(EXC_PTRVAL, EXC_ALLOC(o, t))
+#define EXC_ALLOCS(o, t) EXC_TYPECODE(EXC_STRVAL, EXC_ALLOC(o, t))
+
+/* --- A union representing the type --- */
+
+typedef union exc_exval {
+ int i;
+ void *p;
+ char *s;
+} exc_exval;
+
+/*----- Predefined exceptions ---------------------------------------------*/
+
+/* --- @EXC_NOMEM@ --- *
+ *
+ * Value: ---
+ *
+ * Meaning: An attempt to allocate memory failed.
+ */
+
+#define EXC_NOMEM EXC_ALLOCN(EXC_GLOBAL, 0u)
+
+/* --- @EXC_ERRNO@ --- *
+ *
+ * Value: @int errno@ = the error raised
+ *
+ * Meaning: Some kind of OS error occurred.
+ */
+
+#define EXC_ERRNO EXC_ALLOCI(EXC_GLOBAL, 1u)
+
+/* --- @EXC_OSERROR@ --- *
+ *
+ * Value: @os_error *e@ = pointer to error block
+ *
+ * Meaning: For RISC OS programmers only: alternative way of propagating
+ * errors.
+ */
+
+#define EXC_OSERROR EXC_ALLOCP(EXC_GLOBAL, 1u)
+
+/* --- @EXC_SIGNAL@ --- *
+ *
+ * Value: @int sig@ = signal number
+ *
+ * Meaning: Report the raising of a signal.
+ */
+
+#define EXC_SIGNAL EXC_ALLOCI(EXC_GLOBAL, 2u)
+
+/* --- @EXC_FAIL@ --- *
+ *
+ * Value: @const char *p@ = pointer to expanatory string
+ *
+ * Meaning: Miscellaneous error.
+ */
+
+#define EXC_FAIL EXC_ALLOCS(EXC_GLOBAL, 0xFFu)
+
+/*----- An exception handler block ----------------------------------------*/
+
+/* --- Try to think of this as being opaque --- */
+
+typedef struct __exc_hnd {
+ struct __exc_hnd *next; /* Pointer to next record down */
+ exc_extype type; /* Type of this exception */
+ exc_exval val; /* Value of this exception */
+ jmp_buf buf; /* Jump buffer when exceptions hit */
+} __exc_hnd;
+
+/*----- Global variables --------------------------------------------------*/
+
+extern __exc_hnd *__exc_list; /* List of active handlers */
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- References to current exception type and value --- */
+
+#define exc_type (__exc_ec.type)
+#define exc_val (__exc_ec.val)
+#define exc_i (__exc_ec.val.i)
+#define exc_p (__exc_ec.val.p)
+#define exc_s (__exc_ec.val.s)
+
+/* --- How it actually works --- *
+ *
+ * A `try' block is contained within a block which provides an exception
+ * handler buffer in automatic storage. This block is a loop, to allow
+ * `break' to escape from it. It adds the handler buffer to the top of a
+ * list, and does a `setjmp' to allow a return here following an exception.
+ * The `setjmp' returns zero for the `try' section, and nonzero if there's
+ * an exception to `catch'. It looks a little like this:
+ *
+ * do {
+ * __exc_hnd h;
+ * add_handler(&h);
+ * if (!setjmp(h.buf)) {
+ * do <try code> while (0);
+ * remove_handler(&h);
+ * } else
+ * <catch code>
+ * } while (0)
+ *
+ * Everything else is ugly hacking to make things work.
+ */
+
+/* --- Trying things which may cause exceptions --- */
+
+#define TRY do { \
+ volatile __exc_hnd __exc_ec; \
+ __exc_ec.next = __exc_list; \
+ __exc_list = (__exc_hnd *)&__exc_ec; \
+ if (!setjmp(*(jmp_buf *)&__exc_ec.buf /* very nasty! */ )) { do
+
+#define EXIT_TRY do __exc_list = __exc_ec.next; while (0)
+#define CATCH while (0); EXIT_TRY; } else
+
+#define END_TRY } while (0)
+
+/* --- Raising exceptions --- */
+
+#define THROW __exc_throw
+#define RETHROW __exc_rethrow(__exc_ec.type, __exc_ec.val)
+
+/*----- Functions ---------------------------------------------------------*/
+
+/* --- @exc_uncaught@ --- *
+ *
+ * Arguments: @void (*proc)(exc_extype type, exc_exval val) = new handler
+ *
+ * Returns: Pointer to the old handler value.
+ *
+ * Use: Sets the handler for uncaught exceptions.
+ */
+
+typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/);
+extern exc__uncaught exc_uncaught(exc__uncaught /*proc*/);
+
+/* --- @__exc_throw@ --- *
+ *
+ * Arguments: @exc_extype type@ = type of exception to throw
+ *
+ * Returns: Doesn't
+ *
+ * Use: NOT FOR USER CONSUMPTION. Reads an appropriate exception
+ * value and throws an exception.
+ */
+
+extern void __exc_throw(exc_extype /*type*/, ...);
+
+/* --- @__exc_rethrow@ --- *
+ *
+ * Arguments: @exc_extype type@ = type of exception to throw
+ * @exc_exval val@ = value of exception to throw
+ *
+ * Returns: Doesn't
+ *
+ * Use: NOT FOR USER CONSUMPTION. Does the donkey-work of raising
+ * an exception.
+ */
+
+extern void __exc_rethrow(exc_extype /*type*/, exc_exval /*val*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif