--- /dev/null
+/*
+** OSSP ex - Exception Handling
+** Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+** Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+** Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+**
+** This file is part of OSSP ex, an exception handling library
+** which can be found at http://www.ossp.org/pkg/lib/ex/.
+**
+** Permission to use, copy, modify, and distribute this software for
+** any purpose with or without fee is hereby granted, provided that
+** the above copyright notice and this permission notice appear in all
+** copies.
+**
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+**
+** ex.h: exception handling (pre-processor part)
+*/
+
+#ifndef __EX_H__
+#define __EX_H__
+
+/* required ISO-C standard facilities */
+#include <stdio.h>
+
+/* convenience define */
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+/* determine how the current function name can be fetched */
+#if ( defined(__STDC__) \
+ && defined(__STDC_VERSION__) \
+ && __STDC_VERSION__ >= 199901L)
+#define __EX_FUNC__ __func__ /* ISO-C99 compliant */
+#elif ( defined(__GNUC__) \
+ && defined(__GNUC_MINOR__) \
+ && ( __GNUC__ > 2 \
+ || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)))
+#define __EX_FUNC__ __FUNCTION__ /* gcc >= 2.8 */
+#else
+#define __EX_FUNC__ "#NA#" /* not available */
+#endif
+
+/* the machine context */
+#if defined(__EX_MCTX_MCSC__)
+#include <ucontext.h> /* POSIX.1 ucontext(3) */
+#define __ex_mctx_struct ucontext_t uc;
+#define __ex_mctx_save(mctx) (getcontext(&(mctx)->uc) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx) (void)setcontext(&(mctx)->uc)
+
+#elif defined(__EX_MCTX_SSJLJ__)
+#include <setjmp.h> /* POSIX.1 sigjmp_buf(3) */
+#define __ex_mctx_struct sigjmp_buf jb;
+#define __ex_mctx_save(mctx) (sigsetjmp((mctx)->jb, 1) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx) (void)siglongjmp((mctx)->jb, 1)
+
+#elif defined(__EX_MCTX_SJLJ__) || !defined(__EX_MCTX_CUSTOM__)
+#include <setjmp.h> /* ISO-C jmp_buf(3) */
+#define __ex_mctx_struct jmp_buf jb;
+#define __ex_mctx_save(mctx) (setjmp((mctx)->jb) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx) (void)longjmp((mctx)->jb, 1)
+#endif
+
+/* declare the machine context type */
+typedef struct { __ex_mctx_struct } __ex_mctx_t;
+
+/* declare the exception type (public) */
+typedef struct {
+ /* throw value */
+ void *ex_class;
+ void *ex_object;
+ void *ex_value;
+ /* throw point */
+ const char *ex_file;
+ int ex_line;
+ const char *ex_func;
+} ex_t;
+
+/* declare the context type (private) */
+typedef struct {
+ __ex_mctx_t *ctx_mctx; /* permanent machine context of enclosing try/catch */
+ int ctx_deferred; /* permanent flag whether exception is deferred */
+ int ctx_deferring;/* permanent counter of exception deferring level */
+ int ctx_defer; /* temporary flag for exception deferring macro */
+ int ctx_shielding;/* permanent counter of exception shielding level */
+ int ctx_shield; /* temporary flag for exception shielding macro */
+ int ctx_caught; /* temporary flag whether exception was caught */
+ volatile ex_t ctx_ex; /* temporary exception storage */
+} ex_ctx_t;
+
+/* the static and dynamic initializers for a context structure */
+#define EX_CTX_INITIALIZER \
+ { NULL, 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, 0, NULL } }
+#define EX_CTX_INITIALIZE(ctx) \
+ do { \
+ (ctx)->ctx_mctx = NULL; \
+ (ctx)->ctx_deferred = 0; \
+ (ctx)->ctx_deferring = 0; \
+ (ctx)->ctx_defer = 0; \
+ (ctx)->ctx_shielding = 0; \
+ (ctx)->ctx_shield = 0; \
+ (ctx)->ctx_caught = 0; \
+ (ctx)->ctx_ex.ex_class = NULL; \
+ (ctx)->ctx_ex.ex_object = NULL; \
+ (ctx)->ctx_ex.ex_value = NULL; \
+ (ctx)->ctx_ex.ex_file = NULL; \
+ (ctx)->ctx_ex.ex_line = 0; \
+ (ctx)->ctx_ex.ex_func = NULL; \
+ } while (0)
+
+/* the exception context */
+typedef ex_ctx_t *(*ex_ctx_cb_t)(void);
+extern ex_ctx_cb_t __ex_ctx;
+extern ex_ctx_t *__ex_ctx_default(void);
+
+/* the termination handler */
+typedef void (*ex_term_cb_t)(ex_t *);
+extern ex_term_cb_t __ex_terminate;
+extern void __ex_terminate_default(ex_t *e);
+
+/* the block for trying execution */
+#define ex_try \
+ { \
+ ex_ctx_t *__ex_ctx_ptr = __ex_ctx(); \
+ int __ex_cleanup = 0; \
+ __ex_mctx_t *__ex_mctx_en; \
+ __ex_mctx_t __ex_mctx_me; \
+ __ex_mctx_en = __ex_ctx_ptr->ctx_mctx; \
+ __ex_ctx_ptr->ctx_mctx = &__ex_mctx_me; \
+ if (__ex_mctx_save(&__ex_mctx_me)) { \
+ if (1)
+
+/* the optional(!) block for cleanup */
+#define ex_cleanup \
+ else { \
+ } \
+ __ex_ctx_ptr->ctx_caught = 0; \
+ } \
+ else { \
+ __ex_mctx_restored(&__ex_mctx_me); \
+ __ex_ctx_ptr->ctx_caught = 1; \
+ } \
+ __ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
+ __ex_cleanup = 1; \
+ if (1) { \
+ if (1)
+
+/* the block for catching an exception */
+#define ex_catch(e) \
+ else { \
+ } \
+ if (!(__ex_cleanup)) \
+ __ex_ctx_ptr->ctx_caught = 0; \
+ } \
+ else { \
+ if (!(__ex_cleanup)) { \
+ __ex_mctx_restored(&__ex_mctx_me); \
+ __ex_ctx_ptr->ctx_caught = 1; \
+ } \
+ } \
+ __ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
+ } \
+ if ( !(__ex_ctx()->ctx_caught) \
+ || ((e) = __ex_ctx()->ctx_ex, 0)) { \
+ } \
+ else
+
+/* the throwing of a new exception */
+#define ex_throw(c,o,v) \
+ (( __ex_ctx()->ctx_shielding > 0 \
+ || (__ex_ctx()->ctx_deferring > 0 && __ex_ctx()->ctx_deferred == 1)) ? 0 : \
+ (__ex_ctx()->ctx_ex.ex_class = (void *)(c), \
+ __ex_ctx()->ctx_ex.ex_object = (void *)(o), \
+ __ex_ctx()->ctx_ex.ex_value = (void *)(v), \
+ __ex_ctx()->ctx_ex.ex_file = __FILE__, \
+ __ex_ctx()->ctx_ex.ex_line = __LINE__, \
+ __ex_ctx()->ctx_ex.ex_func = __EX_FUNC__, \
+ __ex_ctx()->ctx_deferred = 1, \
+ (__ex_ctx()->ctx_deferring > 0 ? 0 : \
+ (__ex_ctx()->ctx_mctx == NULL \
+ ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
+ : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) ))))
+
+/* the re-throwing of an already caught exception */
+#define ex_rethrow \
+ (( __ex_ctx()->ctx_shielding > 0 \
+ || __ex_ctx()->ctx_deferring > 0) ? 0 : \
+ ( __ex_ctx()->ctx_mctx == NULL \
+ ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
+ : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) ))
+
+/* shield an operation from exception handling */
+#define ex_shield \
+ for (__ex_ctx()->ctx_shielding++, \
+ __ex_ctx()->ctx_shield = 1; \
+ __ex_ctx()->ctx_shield == 1; \
+ __ex_ctx()->ctx_shield = 0, \
+ __ex_ctx()->ctx_shielding--)
+
+/* defer immediate exception handling */
+#define ex_defer \
+ for (((__ex_ctx()->ctx_deferring)++ == 0 ? __ex_ctx()->ctx_deferred = 0 : 0), \
+ __ex_ctx()->ctx_defer = 1; \
+ __ex_ctx()->ctx_defer == 1; \
+ __ex_ctx()->ctx_defer = 0, \
+ ((--(__ex_ctx()->ctx_deferring) == 0 && __ex_ctx()->ctx_deferred == 1) ? ex_rethrow : 0))
+
+/* exception handling tests */
+#define ex_catching \
+ (__ex_ctx()->ctx_mctx != NULL)
+#define ex_shielding \
+ (__ex_ctx()->ctx_shielding > 0)
+#define ex_deferring \
+ (__ex_ctx()->ctx_deferring > 0)
+
+/* optional namespace mapping */
+#if defined(__EX_NS_UCCXX__)
+#define Try ex_try
+#define Cleanup ex_cleanup
+#define Catch ex_catch
+#define Throw ex_throw
+#define Rethrow ex_rethrow
+#define Shield ex_shield
+#define Defer ex_defer
+#elif defined(__EX_NS_CXX__) || (!defined(__cplusplus) && !defined(__EX_NS_CUSTOM__))
+#define try ex_try
+#define cleanup ex_cleanup
+#define catch ex_catch
+#define throw ex_throw
+#define rethrow ex_rethrow
+#define shield ex_shield
+#define defer ex_defer
+#endif
+
+#endif /* __EX_H__ */
+
--- /dev/null
+/*
+** OSSP ex - Exception Handling
+** Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+** Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+** Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+**
+** This file is part of OSSP ex, an exception handling library
+** which can be found at http://www.ossp.org/pkg/lib/ex/.
+**
+** Permission to use, copy, modify, and distribute this software for
+** any purpose with or without fee is hereby granted, provided that
+** the above copyright notice and this permission notice appear in all
+** copies.
+**
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+**
+** ex.c: exception handling (compiler part)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "xbt/ex.h"
+
+/* default __ex_ctx callback function */
+ex_ctx_t *__ex_ctx_default(void)
+{
+ static ex_ctx_t ctx = EX_CTX_INITIALIZER;
+
+ return &ctx;
+}
+
+/* default __ex_terminate callback function */
+void __ex_terminate_default(ex_t *e)
+{
+ fprintf(stderr,
+ "**EX: UNCAUGHT EXCEPTION: "
+ "class=0x%lx object=0x%lx value=0x%lx [%s:%d@%s]\n",
+ (long)((e)->ex_class), (long)((e)->ex_object), (long)((e)->ex_value),
+ (e)->ex_file, (e)->ex_line, (e)->ex_func);
+ abort();
+}
+
+/* the externally visible API */
+ex_ctx_cb_t __ex_ctx = &__ex_ctx_default;
+ex_term_cb_t __ex_terminate = &__ex_terminate_default;
+
--- /dev/null
+/*
+** OSSP ex - Exception Handling
+** Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+** Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+** Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+**
+** This file is part of OSSP ex, an exception handling library
+** which can be found at http://www.ossp.org/pkg/lib/ex/.
+**
+** Permission to use, copy, modify, and distribute this software for
+** any purpose with or without fee is hereby granted, provided that
+** the above copyright notice and this permission notice appear in all
+** copies.
+**
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+**
+** ex_test.c: exception handling test suite
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "ex_test_ts.h"
+#include "xbt/ex.h"
+
+TS_TEST(test_controlflow)
+{
+ ex_t ex;
+ volatile int n;
+
+ ts_test_check(TS_CTX, "basic nested control flow");
+ n = 1;
+ ex_try {
+ if (n != 1)
+ ts_test_fail(TS_CTX, "M1: n=%d (!= 1)", n);
+ n++;
+ ex_try {
+ if (n != 2)
+ ts_test_fail(TS_CTX, "M2: n=%d (!= 2)", n);
+ n++;
+ ex_throw(0, 0, 0);
+ }
+ ex_catch (ex) {
+ if (n != 3)
+ ts_test_fail(TS_CTX, "M3: n=%d (!= 1)", n);
+ n++;
+ ex_rethrow;
+ }
+ ts_test_fail(TS_CTX, "MX: n=%d (expected: not reached)", n);
+ }
+ ex_catch (ex) {
+ if (n != 4)
+ ts_test_fail(TS_CTX, "M4: n=%d (!= 4)", n);
+ n++;
+ }
+ if (n != 5)
+ ts_test_fail(TS_CTX, "M5: n=%d (!= 5)", n);
+}
+
+TS_TEST(test_value)
+{
+ ex_t ex;
+
+ ex_try {
+ ex_throw(1, 2, 3);
+ }
+ ex_catch (ex) {
+ ts_test_check(TS_CTX, "exception value passing");
+ if (ex.ex_class != (void *)1)
+ ts_test_fail(TS_CTX, "ex_class=0x%lx (!= 1)", (long)ex.ex_class);
+ if (ex.ex_object != (void *)2)
+ ts_test_fail(TS_CTX, "ex_object=0x%lx (!= 2)", (long)ex.ex_object);
+ if (ex.ex_value != (void *)3)
+ ts_test_fail(TS_CTX, "ex_value=0x%lx (!= 3)", (long)ex.ex_value);
+ }
+}
+
+TS_TEST(test_variables)
+{
+ ex_t ex;
+ int r1, r2;
+ volatile int v1, v2;
+
+ r1 = r2 = v1 = v2 = 1234;
+ ex_try {
+ r2 = 5678;
+ v2 = 5678;
+ ex_throw(0, 0, 0);
+ }
+ ex_catch (ex) {
+ ts_test_check(TS_CTX, "variable preservation");
+ if (r1 != 1234)
+ ts_test_fail(TS_CTX, "r1=%d (!= 1234)", r1);
+ if (v1 != 1234)
+ ts_test_fail(TS_CTX, "v1=%d (!= 1234)", v1);
+ /* r2 is allowed to be destroyed because not volatile */
+ if (v2 != 5678)
+ ts_test_fail(TS_CTX, "v2=%d (!= 5678)", v2);
+ }
+}
+
+TS_TEST(test_defer)
+{
+ ex_t ex;
+ volatile int i1 = 0;
+ volatile int i2 = 0;
+ volatile int i3 = 0;
+
+ ts_test_check(TS_CTX, "exception deferring");
+ if (ex_deferring)
+ ts_test_fail(TS_CTX, "unexpected deferring scope");
+ ex_try {
+ ex_defer {
+ if (!ex_deferring)
+ ts_test_fail(TS_CTX, "unexpected non-deferring scope");
+ ex_defer {
+ i1 = 1;
+ ex_throw(0, 0, 4711);
+ i2 = 2;
+ ex_throw(0, 0, 0);
+ i3 = 3;
+ ex_throw(0, 0, 0);
+ }
+ ex_throw(0, 0, 0);
+ }
+ ts_test_fail(TS_CTX, "unexpected not occurred deferred throwing");
+ }
+ ex_catch (ex) {
+ if ((long)ex.ex_value != (long)4711)
+ ts_test_fail(TS_CTX, "caught exception with value %d, expected 4711", (long)ex.ex_value);
+ }
+ if (i1 != 1)
+ ts_test_fail(TS_CTX, "v.i1 not set (expected 1, got %d)", i1);
+ if (i2 != 2)
+ ts_test_fail(TS_CTX, "v.i2 not set (expected 2, got %d)", i2);
+ if (i3 != 3)
+ ts_test_fail(TS_CTX, "v.i3 not set (expected 3, got %d)", i3);
+}
+
+TS_TEST(test_shield)
+{
+ ex_t ex;
+
+ ts_test_check(TS_CTX, "exception shielding");
+ if (ex_shielding)
+ ts_test_fail(TS_CTX, "unexpected shielding scope");
+ if (ex_catching)
+ ts_test_fail(TS_CTX, "unexpected catching scope");
+ ex_try {
+ ex_shield {
+ if (!ex_shielding)
+ ts_test_fail(TS_CTX, "unexpected non-shielding scope");
+ ex_throw(0, 0, 0);
+ }
+ if (ex_shielding)
+ ts_test_fail(TS_CTX, "unexpected shielding scope");
+ if (!ex_catching)
+ ts_test_fail(TS_CTX, "unexpected non-catching scope");
+ }
+ ex_catch (ex) {
+ ts_test_fail(TS_CTX, "unexpected exception catched");
+ if (ex_catching)
+ ts_test_fail(TS_CTX, "unexpected catching scope");
+ }
+ if (ex_catching)
+ ts_test_fail(TS_CTX, "unexpected catching scope");
+}
+
+TS_TEST(test_cleanup)
+{
+ ex_t ex;
+ volatile int v1;
+ int c;
+
+ ts_test_check(TS_CTX, "cleanup handling");
+
+ v1 = 1234;
+ c = 0;
+ ex_try {
+ v1 = 5678;
+ ex_throw(1, 2, 3);
+ }
+ ex_cleanup {
+ if (v1 != 5678)
+ ts_test_fail(TS_CTX, "v1 = %d (!= 5678)", v1);
+ c = 1;
+ }
+ ex_catch (ex) {
+ if (v1 != 5678)
+ ts_test_fail(TS_CTX, "v1 = %d (!= 5678)", v1);
+ if (!(ex.ex_class == (void *)1 && ex.ex_object == (void *)2 && ex.ex_value == (void *)3))
+ ts_test_fail(TS_CTX, "unexpected exception contents");
+ }
+ if (!c)
+ ts_test_fail(TS_CTX, "ex_cleanup not executed");
+}
+
+int main(int argc, char *argv[])
+{
+ ts_suite_t *ts;
+ int n;
+
+ ts = ts_suite_new("OSSP ex (Exception Handling)");
+ ts_suite_test(ts, test_controlflow, "basic nested control flow");
+ ts_suite_test(ts, test_value, "exception value passing");
+ ts_suite_test(ts, test_variables, "variable value preservation");
+ ts_suite_test(ts, test_defer, "exception deferring");
+ ts_suite_test(ts, test_shield, "exception shielding");
+ ts_suite_test(ts, test_cleanup, "cleanup handling");
+ n = ts_suite_run(ts);
+ ts_suite_free(ts);
+ return n;
+}
+
--- /dev/null
+/*
+** OSSP ts - Test Suite Library
+** Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
+** Copyright (c) 2001-2004 The OSSP Project <http://www.ossp.org/>
+** Copyright (c) 2001-2004 Cable & Wireless <http://www.cw.com/>
+**
+** This file is part of OSSP ts, a small test suite library which
+** can be found at http://www.ossp.org/pkg/lib/ts/.
+**
+** Permission to use, copy, modify, and distribute this software for
+** any purpose with or without fee is hereby granted, provided that
+** the above copyright notice and this permission notice appear in all
+** copies.
+**
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+**
+** ts.c: test suite library
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "ex_test_ts.h"
+
+/* embedded ring data structure library */
+#define RING_ENTRY(elem) \
+ struct { elem *next; elem *prev; }
+#define RING_HEAD(elem) \
+ struct { elem *next; elem *prev; }
+#define RING_SENTINEL(hp, elem, link) \
+ (elem *)((char *)(hp) - ((size_t)(&((elem *)0)->link)))
+#define RING_FIRST(hp) \
+ (hp)->next
+#define RING_LAST(hp) \
+ (hp)->prev
+#define RING_NEXT(ep, link) \
+ (ep)->link.next
+#define RING_PREV(ep, link) \
+ (ep)->link.prev
+#define RING_INIT(hp, elem, link) \
+ do { RING_FIRST((hp)) = RING_SENTINEL((hp), elem, link); \
+ RING_LAST((hp)) = RING_SENTINEL((hp), elem, link); } while (0)
+#define RING_EMPTY(hp, elem, link) \
+ (RING_FIRST((hp)) == RING_SENTINEL((hp), elem, link))
+#define RING_ELEM_INIT(ep, link) \
+ do { RING_NEXT((ep), link) = (ep); \
+ RING_PREV((ep), link) = (ep); } while (0)
+#define RING_SPLICE_BEFORE(lep, ep1, epN, link) \
+ do { RING_NEXT((epN), link) = (lep); \
+ RING_PREV((ep1), link) = RING_PREV((lep), link); \
+ RING_NEXT(RING_PREV((lep), link), link) = (ep1); \
+ RING_PREV((lep), link) = (epN); } while (0)
+#define RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \
+ RING_SPLICE_BEFORE(RING_SENTINEL((hp), elem, link), (ep1), (epN), link)
+#define RING_INSERT_TAIL(hp, nep, elem, link) \
+ RING_SPLICE_TAIL((hp), (nep), (nep), elem, link)
+#define RING_FOREACH(ep, hp, elem, link) \
+ for ((ep) = RING_FIRST((hp)); \
+ (ep) != RING_SENTINEL((hp), elem, link); \
+ (ep) = RING_NEXT((ep), link))
+#define RING_FOREACH_LA(ep, epT, hp, elem, link) \
+ for ((ep) = RING_FIRST((hp)), (epT) = RING_NEXT((ep), link); \
+ (ep) != RING_SENTINEL((hp), elem, link); \
+ (ep) = (epT), (epT) = RING_NEXT((epT), link))
+
+/* test suite test log */
+struct tstl_st;
+typedef struct tstl_st tstl_t;
+struct tstl_st {
+ RING_ENTRY(tstl_t) next;
+ char *text;
+ const char *file;
+ int line;
+};
+
+/* test suite test check */
+struct tstc_st;
+typedef struct tstc_st tstc_t;
+struct tstc_st {
+ RING_ENTRY(tstc_t) next;
+ char *title;
+ int failed;
+ const char *file;
+ int line;
+ RING_HEAD(tstl_t) logs;
+};
+
+/* test suite test */
+struct ts_test_st {
+ RING_ENTRY(ts_test_t) next;
+ char *title;
+ ts_test_cb_t func;
+ const char *file;
+ int line;
+ RING_HEAD(tstc_t) checks;
+};
+
+/* test suite */
+struct ts_suite_st {
+ char *title;
+ RING_HEAD(ts_test_t) tests;
+};
+
+/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
+static int ts_suite_mvxprintf(char *buffer, size_t bufsize, const char *format, va_list ap)
+{
+ /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
+ char ibuf[((sizeof(int)*8)/3)+10];
+ char *cp;
+ char c;
+ int d;
+ int n;
+ int bytes;
+
+ if (format == NULL)
+ return -1;
+ bytes = 0;
+ while (*format != '\0') {
+ if (*format == '%') {
+ c = *(format+1);
+ if (c == '%') {
+ /* expand "%%" */
+ cp = &c;
+ n = sizeof(char);
+ }
+ else if (c == 'c') {
+ /* expand "%c" */
+ c = (char)va_arg(ap, int);
+ cp = &c;
+ n = sizeof(char);
+ }
+ else if (c == 's') {
+ /* expand "%s" */
+ if ((cp = (char *)va_arg(ap, char *)) == NULL)
+ cp = (char*)"(null)";
+ n = strlen(cp);
+ }
+ else if (c == 'd') {
+ /* expand "%d" */
+ d = (int)va_arg(ap, int);
+#ifdef HAVE_SNPRINTF
+ snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
+#else
+ sprintf(ibuf, "%d", d); /* implicitly secure */
+#endif
+ cp = ibuf;
+ n = strlen(cp);
+ }
+ else {
+ /* any other "%X" */
+ cp = (char *)format;
+ n = 2;
+ }
+ format += 2;
+ }
+ else {
+ /* plain text */
+ cp = (char *)format;
+ if ((format = strchr(cp, '%')) == NULL)
+ format = strchr(cp, '\0');
+ n = format - cp;
+ }
+ /* perform output operation */
+ if (buffer != NULL) {
+ if (n > bufsize)
+ return -1;
+ memcpy(buffer, cp, n);
+ buffer += n;
+ bufsize -= n;
+ }
+ bytes += n;
+ }
+ /* nul-terminate output */
+ if (buffer != NULL) {
+ if (bufsize == 0)
+ return -1;
+ *buffer = '\0';
+ }
+ return bytes;
+}
+
+/* minimal vasprintf(3) variant which supports %{c,s,d} only */
+static char *ts_suite_mvasprintf(const char *format, va_list ap)
+{
+ char *buffer;
+ int n;
+ va_list ap2;
+
+ if (format == NULL)
+ return NULL;
+ va_copy(ap2, ap);
+ if ((n = ts_suite_mvxprintf(NULL, 0, format, ap)) == -1)
+ return NULL;
+ if ((buffer = (char *)malloc(n+1)) == NULL)
+ return NULL;
+ ts_suite_mvxprintf(buffer, n+1, format, ap2);
+ return buffer;
+}
+
+/* minimal asprintf(3) variant which supports %{c,s,d} only */
+static char *ts_suite_masprintf(const char *format, ...)
+{
+ va_list ap;
+ char *cp;
+
+ va_start(ap, format);
+ cp = ts_suite_mvasprintf(format, ap);
+ va_end(ap);
+ return cp;
+}
+
+/* create test suite */
+ts_suite_t *ts_suite_new(const char *fmt, ...)
+{
+ ts_suite_t *ts;
+ va_list ap;
+
+ if ((ts = (ts_suite_t *)malloc(sizeof(ts_suite_t))) == NULL)
+ return NULL;
+ va_start(ap, fmt);
+ ts->title = ts_suite_mvasprintf(fmt, ap);
+ RING_INIT(&ts->tests, ts_test_t, next);
+ va_end(ap);
+ return ts;
+}
+
+/* add test case to test suite */
+void ts_suite_test(ts_suite_t *ts, ts_test_cb_t func, const char *fmt, ...)
+{
+ ts_test_t *tst;
+ va_list ap;
+
+ if (ts == NULL || func == NULL || fmt == NULL)
+ return;
+ if ((tst = (ts_test_t *)malloc(sizeof(ts_test_t))) == NULL)
+ return;
+ RING_ELEM_INIT(tst, next);
+ va_start(ap, fmt);
+ tst->title = ts_suite_mvasprintf(fmt, ap);
+ va_end(ap);
+ tst->func = func;
+ tst->file = NULL;
+ tst->line = 0;
+ RING_INIT(&tst->checks, tstc_t, next);
+ RING_INSERT_TAIL(&ts->tests, tst, ts_test_t, next);
+ return;
+}
+
+/* run test suite */
+int ts_suite_run(ts_suite_t *ts)
+{
+ ts_test_t *tst;
+ tstc_t *tstc;
+ tstl_t *tstl;
+ int total_tests, total_tests_suite_failed;
+ int total_checks, total_checks_failed;
+ int test_checks, test_checks_failed;
+ const char *file;
+ int line;
+ char *cp;
+
+ if (ts == NULL)
+ return 0;
+
+ /* init total counters */
+ total_tests = 0;
+ total_tests_suite_failed = 0;
+ total_checks = 0;
+ total_checks_failed = 0;
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, " Test Suite: %s\n", ts->title);
+ fprintf(stdout, " __________________________________________________________________\n");
+ fprintf(stdout, "\n");
+ fflush(stdout);
+
+ /* iterate through all test cases */
+ RING_FOREACH(tst, &ts->tests, ts_test_t, next) {
+ cp = ts_suite_masprintf(" Test: %s ........................................"
+ "........................................", tst->title);
+ cp[60] = '\0';
+ fprintf(stdout, "%s", cp);
+ free(cp);
+ fflush(stdout);
+
+ /* init test case counters */
+ test_checks = 0;
+ test_checks_failed = 0;
+
+ /* run the test case function */
+ tst->func(tst);
+
+ /* iterate through all performed checks to determine status */
+ RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
+ test_checks++;
+ if (tstc->failed)
+ test_checks_failed++;
+ }
+
+ if (test_checks_failed > 0) {
+ /* some checks failed, so do detailed reporting of test case */
+ fprintf(stdout, " FAILED\n");
+ fprintf(stdout, " Ops, %d/%d checks failed! Detailed report follows:\n",
+ test_checks_failed, test_checks);
+ RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
+ file = (tstc->file != NULL ? tstc->file : tst->file);
+ line = (tstc->line != 0 ? tstc->line : tst->line);
+ if (file != NULL)
+ fprintf(stdout, " Check: %s [%s:%d]\n", tstc->title, file, line);
+ else
+ fprintf(stdout, " Check: %s\n", tstc->title);
+ RING_FOREACH(tstl, &tstc->logs, tstl_t, next) {
+ file = (tstl->file != NULL ? tstl->file : file);
+ line = (tstl->line != 0 ? tstl->line : line);
+ if (file != NULL)
+ fprintf(stdout, " Log: %s [%s:%d]\n", tstl->text, file, line);
+ else
+ fprintf(stdout, " Log: %s\n", tstl->text);
+ }
+ }
+ }
+ else {
+ /* test case ran successfully */
+ fprintf(stdout, ".... OK\n");
+ }
+ fflush(stdout);
+
+ /* accumulate counters */
+ total_checks += test_checks;
+ total_tests++;
+ if (test_checks_failed > 0) {
+ total_checks_failed += test_checks_failed;
+ total_tests_suite_failed++;
+ }
+ }
+
+ /* print test suite summary */
+ fprintf(stdout, " __________________________________________________________________\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, " Test Summary: %d tests (%d ok, %d failed), %d checks (%d ok, %d failed)\n",
+ total_tests, (total_tests - total_tests_suite_failed), total_tests_suite_failed,
+ total_checks, (total_checks - total_checks_failed), total_checks_failed);
+ if (total_tests_suite_failed > 0)
+ fprintf(stdout, " Test Suite: FAILED\n");
+ else
+ fprintf(stdout, " Test Suite: OK\n");
+ fprintf(stdout, "\n");
+ fflush(stdout);
+
+ return total_checks_failed;
+}
+
+/* destroy test suite */
+void ts_suite_free(ts_suite_t *ts)
+{
+ ts_test_t *tst, *tstT;
+ tstc_t *tstc, *tstcT;
+ tstl_t *tstl, *tstlT;
+
+ if (ts == NULL)
+ return;
+ RING_FOREACH_LA(tst, tstT, &ts->tests, ts_test_t, next) {
+ RING_FOREACH_LA(tstc, tstcT, &tst->checks, tstc_t, next) {
+ RING_FOREACH_LA(tstl, tstlT, &tstc->logs, tstl_t, next) {
+ free(tstl->text);
+ }
+ free(tstc->title);
+ free(tstc);
+ }
+ free(tst->title);
+ free(tst);
+ }
+ free(ts->title);
+ free(ts);
+ return;
+}
+
+/* annotate test case with file name and line number */
+ts_test_t *ts_test_ctx(ts_test_t *tst, const char *file, int line)
+{
+ if (tst != NULL && file != NULL) {
+ tst->file = file;
+ tst->line = line;
+ }
+ return tst;
+}
+
+/* annotate test case with check */
+void ts_test_check(ts_test_t *tst, const char *fmt, ...)
+{
+ tstc_t *tstc;
+ va_list ap;
+
+ if (tst == NULL || fmt == NULL)
+ return;
+ if ((tstc = (tstc_t *)malloc(sizeof(tstc_t))) == NULL)
+ return;
+ va_start(ap, fmt);
+ RING_ELEM_INIT(tstc, next);
+ tstc->title = ts_suite_mvasprintf(fmt, ap);
+ tstc->failed = 0;
+ tstc->file = tst->file;
+ tstc->line = tst->line;
+ RING_INIT(&tstc->logs, tstl_t, next);
+ RING_INSERT_TAIL(&tst->checks, tstc, tstc_t, next);
+ va_end(ap);
+ return;
+}
+
+/* annotate test case with log message and failure */
+void ts_test_fail(ts_test_t *tst, const char *fmt, ...)
+{
+ tstc_t *tstc;
+ tstl_t *tstl;
+ va_list ap;
+
+ if (tst == NULL || fmt == NULL)
+ return;
+ if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
+ return;
+ va_start(ap, fmt);
+ tstl->text = ts_suite_mvasprintf(fmt, ap);
+ tstl->file = tst->file;
+ tstl->line = tst->line;
+ RING_ELEM_INIT(tstl, next);
+ tstc = RING_LAST(&tst->checks);
+ RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next);
+ tstc->failed = 1;
+ va_end(ap);
+ return;
+}
+
+/* annotate test case with log message only */
+void ts_test_log(ts_test_t *tst, const char *fmt, ...)
+{
+ tstc_t *tstc;
+ tstl_t *tstl;
+ va_list ap;
+
+ if (tst == NULL || fmt == NULL)
+ return;
+ if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
+ return;
+ va_start(ap, fmt);
+ tstl->text = ts_suite_mvasprintf(fmt, ap);
+ tstl->file = tst->file;
+ tstl->line = tst->line;
+ RING_ELEM_INIT(tstl, next);
+ tstc = RING_LAST(&tst->checks);
+ RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next);
+ va_end(ap);
+ return;
+}
+
--- /dev/null
+/*
+** OSSP ts - Test Suite Library
+** Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
+** Copyright (c) 2001-2004 The OSSP Project <http://www.ossp.org/>
+** Copyright (c) 2001-2004 Cable & Wireless <http://www.cw.com/>
+**
+** This file is part of OSSP ts, a small test suite library which
+** can be found at http://www.ossp.org/pkg/lib/ts/.
+**
+** Permission to use, copy, modify, and distribute this software for
+** any purpose with or without fee is hereby granted, provided that
+** the above copyright notice and this permission notice appear in all
+** copies.
+**
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+**
+** ts.h: test suite library API
+*/
+
+#ifndef _TS_H_
+#define _TS_H_
+
+/* test suite object type */
+struct ts_suite_st;
+typedef struct ts_suite_st ts_suite_t;
+
+/* test object type */
+struct ts_test_st;
+typedef struct ts_test_st ts_test_t;
+
+/* test callback function type */
+typedef void (*ts_test_cb_t)(ts_test_t *);
+
+/* test suite operations */
+ts_suite_t *ts_suite_new (const char *fmt, ...);
+void ts_suite_test (ts_suite_t *s, ts_test_cb_t func, const char *fmt, ...);
+int ts_suite_run (ts_suite_t *s);
+void ts_suite_free (ts_suite_t *s);
+
+/* test operations */
+ts_test_t *ts_test_ctx (ts_test_t *t, const char *file, int line);
+void ts_test_check (ts_test_t *t, const char *fmt, ...);
+void ts_test_fail (ts_test_t *t, const char *fmt, ...);
+void ts_test_log (ts_test_t *t, const char *fmt, ...);
+
+/* test suite short-cut macros */
+#define TS_TEST(name) \
+ static void name(ts_test_t *_t)
+#define TS_CTX \
+ ts_test_ctx(_t, __FILE__, __LINE__)
+
+#endif /* _TS_H_ */
+