/* Copyright 2010 Jan-Mark Wams. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY JAN-MARK WAMS ``AS IS'' AND ANY
 * EXPRESS 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 JAN-MARK WAMS OR
 * 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.
 * 
 * The views and conclusions contained in the software and
 * documentation are those of the authors and should not be
 * interpreted as representing official policies, either expressed or
 * implied, of Jan-Mark Wams.
 * 
 *****************************************************************************
 *                                                                  prefix: _X
 * CPP implementation of Java like try/catch/finally. 
 * - Keyword "ensure" replaces "finally" (like in Ruby).
 * - Using positive integer values for exceptions.
 * - Allow nesting and propagation.
 * - The try/catch construct is a single statement.
 * - Ensure part is not mandatory.
 *
 * The following "keywords", functions and variables defined are:
 * try, catch, ensure, retry, upagate(), raise(), excepno.
 *
 * * * * * * * * * * * * *  W A R N I N G S  * * * * * * * * * * * * *
 *
 * - Raise(), upagate() and retry necessitate volatile type
 * modification of autovariables (i.e., non static local variables and
 * arguments) in the scope of the try/catch statement if their content
 * is important. This is because variables might be kept in a register
 * and, thus, the longjmp() might restore it, so the content of any
 * auto, non-volatile variable is undefined if the compiler uses
 * register optimization. On gcc one might try the -traditional flag.
 *
 * - The following lines should behave the same (print ?!) however the
 * bottom one suffers the dangling else problem, and does not print !.
 * try raise(3); catch(3) { if(1) printf("?"); } ensure printf("!");
 * try raise(3); catch(3)   if(1) printf("?");   ensure printf("!");
 * Therefor, always use curly brackets!
 *
 * - Also, don't do the obvious bad things like using 'return' in a
 * try, catch or ensure block. Or even more obvious using 'retry' in a
 * ensure block. There are many such examples, but they are inherent
 * to the try/catch statement, not this implementation.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Example code:
 *
 * try      { if(!d) raise(3); e = 1/d; } 
 * catch(3) { printf("Seen exception three.\n"); d = 1; retry; } 
 * catch(*) { printf("Unknown exception %d raised!\n", excepno); upagate(5); } 
 * ensure   { if (e < 0) e *= -1; }
 *
 * The core technique used in this header file looks like a state
 * machine wrapped in a push-pop. Expanded by the pre-processor it resembles:
 *
 * PUSH() a new buffer on the stack.
 *   while(1)
 *     if (0 == (x = setjmp())) {  // Try. 
 *       Try part. 
 *       x = ENSURE;
 *     } 
 *     else if (SOME_EXCEPTION_NO == x) {  // Catch
 *       Handle SOME_EXCEPTION_NO;
 *       x = ENSURE
 *     } 
 *     :
 *     :
 *     else {  // Ensure
 *       Do the ensure part.
 *       break;
 *     }
 * POP() the buffer off the stack.
 * if(FINISH != x) raise(x) to PROPAGATE the exception.
 *
 * It is just that the PUSH() and POP(),PROPAGATE are put in a
 * prepending for-statment, and the break for the Ensure part is
 * replaced by changing the conditional in the "while(1)" because the
 * Ensure part is not mandatory. So the firt two likes look more like this:
 *
 * for(PUSH();;POP(),PROPAGATE())
 *   while(x-- > ENSURE)
 *
 * In the try part, or in a function that is called from the try part
 * (even in an other source file), an exception is raised by calling:
 * longjump(SOME_EXCEPTION_NO);
 * 
 * There is one particular CPP construct that might not be clear at
 * first reading. This construction allows for a macro to accept a
 * star as well as a integer expression as an argument. Both catch(2)
 * and catch(*) will expand to a correct C expression. The construct
 * has two main parts. First a statement is needed to distinguish
 * between the two types of arguments, and second an expression that
 * evaluates to the value of the given integer and any value for
 * *. The first uses the CPP make-string (#) operator to check the
 * first character of the argument: (#x[0] == '*'). Note that
 * expression x will be quoted, so it will not be exectued. The second
 * uses the fact that * can mean a pointer dereferance and & can mean
 * both pointer-of and bitwise-and: (x & all-ones). It expands either
 * in (* & all-ones) == all-ones == -1 or in (2 & all-ones) == 2. Note
 * that & all-ones does not have any side effects, nomatter what the
 * integer expression. See the definition of catch() and upagate().
 */
#ifndef __TRYCATCH_H__
#define __TRYCATCH_H__ 1

#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>

/*****************************************************************************
 * Prefixed private defines.
 */

/* The heart of the try/catch/ensure construct is a state machine,
 * where the exceptions are states too. Non-positive values are used as
 * internal state and are disallowed as arguments to raise(). Since
 * (postfix) decrement is used to change the state from ENSURE to
 * FINISH, _X_FINISH should be one less than _X_ENSURE and unique like
 * all states.
 */
#define _X_TRY     0             /* Has to be 0, because of setjmp().  */
#define _X_RETRY  -1
#define _X_ENSURE -2
#define _X_FINISH (_X_ENSURE - 1) 

/* To allow longjmp() and exit() in comma expressions, some compilers
 * need them to return an integer, since both do not reach the return,
 * it does not make sense, but this just causes less warnings.  Note
 * the static. This means every file has it's own version. This can be
 * optimized by the compiler, but it saves a separate global
 * definition.
 */
static int _X___longjmp(jmp_buf jb, int i) { longjmp(jb, i); return 0; }
#define longjmp _X___longjmp
static int _X___exit(int n) { exit(n); return 0; }
#define exit _X___exit

/* Since try/catch/ensure statments can nest, some jump buffer stack
 * is needed. The simplest possible form suffices. 
 */
typedef struct _X_s_node  *_X_node_t;
typedef struct _X_s_node {
  jmp_buf   jb;
  _X_node_t next;
} _X_s_node_t;

/* A lot of global variables are needed and despite the prefix there
 * is name space pollution. So all global variables are put in one
 * global struct.
 */
struct _X_s_globs {
  _X_node_t root;      /* Pointer to root node. */
  _X_node_t tmp;       /* Temp node pointer to enable push and pop. */
  int       no;        /* The current exception number. */
  int       upa;       /* The exception number to be upagated. */
  int       mo;        /* Constand set to -1 (~0 actually). */
};

/* Unfortunately, since a stack is needed, as well as some current
 * values and temporary place holders, there has to be one file that
 * declares and instanciates them. Normally a special .c file is
 * created to put all the declarations. However for this one struct
 * that is kind of overkill.  
 * Therefor, it is left to the programmer. To use the try/catch/ensure
 * construct one (and only one) source file needs to have the
 * declarations.  One (and only one) source file should contain
 * DECLARE_TRY_GLOBALS;, it is not very elegant, but the alternative
 * is making a static declaration. However, that would limit matching
 * raise() and catch() pairs to the same file scope.
 * In an attempt to make the undeclared variable warning more
 * readable, the _X variable is re-defined by a descriptive (long)
 * name. This will break some very old compilers that allow only 8
 * character variables, but not the ones that chop at 8 characters.
 */
#define _X Please_put_DECLARE_TRYCATCH_GLOBALS_in_one_source_file
#define DECLARE_TRYCATCH_GLOBALS struct _X_s_globs _X = {NULL, NULL, 0, 0, ~0}
extern struct _X_s_globs _X;

/* Some form of giving up is needed. This should be integrated with
 * the error handling that the application uses. But as a stand in, it
 * is ok. Note it is an expression with a value (thanks to redifining
 * exit) and, thus, it can be used in a comma list.
 */
#define _X_exit(s) (                                            \
  (_X.no ?                                                      \
    fprintf(stderr,"%s:%d: Exception %d %s. (quiting)\n",       \
            __FILE__, __LINE__, _X.no, (s)):                    \
    fprintf(stderr,"%s:%d: Error %s. (quiting)\n",              \
            __FILE__, __LINE__, (s))),                          \
  exit(_X.no ? _X.no : -1)                                        \
)

/* Clasic way to get a new node, by malloc(). 
 */
#define _X_new_node(p)                                                        \
  ((*(p)= malloc(sizeof(_X_s_node_t))) ? 1:(_X.no= 0,_X_exit("out of memory")))

/* Corresponging delete node by free().
 */
#define _X_del_node(p)                          \
  free(*(p))

/* Pushing is a matter of prepending to a linked list.
 */
#define _X_PUSH                                                 \
  (_X.tmp=_X.root,_X_new_node(&_X.root),_X.root->next=_X.tmp)

/* Poping is a matter of chopping off the head of the linked list.
 */
#define _X_POP                                                  \
  (_X.tmp=_X.root->next,_X_del_node(&_X.root),_X.root=_X.tmp)

/* Raising an exception is a call to longjmp(). An extra check is
 * needed to see if there is a setjmp() location to goto. If not,
 * there is no choice but to report the error and abort.  Note there
 * is no check for negative values, so it can be used to raise the
 * internal (negative) exceptions. Below the API raise() definition
 * does check for this.
 */
#define _X_RAISE(x)                                                     \
  (_X.root ? longjmp(_X.root->jb,(x)) : _X_exit("uncaught"))

/* For cpp hacking, the for-loop is a god's gift. It allows code to be
 * exectuted after a (compound) statement to be put before the
 * statement it self. Since it also repeats this can sometimes
 * necessitate some extra measures, but with a goto like sigjump()
 * this can be ignored. Defined this way, _X_GO_ENSURE can be put
 * between an if-statement (and others) and the (compound) statement
 * to set the state to ENSURE after the statement.
 */
#define _X_GO_ENSURE                                    \
  for(;_X.no > _X_ENSURE; _X.no = _X_ENSURE)

/* This macro is best read bottom line first. It is meant to be placed
 * just after the _X_POP statement. Translated the second line states:
 * If the current exception number (_X.no) is not _X_ENSURE, the
 * exception has not been handled by the current construct, so it is
 * raised again for the enclosing construct. Since it has to be placed
 * just after the _X_POP statement, simply raising the exception
 * number suffies. The first line sets the current exception number to
 * _X.upa if it is non zero (and zero's it out). If this happens, the
 * next line will propagate the exception to the enclosing try/catch
 * construct. This is used to implement upagate() (see below) that
 * propagates the current (or given) exception to the enclosing
 * construct, but not after a possible eventually part is exectued.
 */
#define _X_PROPAGATE                            \
  ((_X.upa ? _X.no = _X.upa, _X.upa = 0 :0),    \
   (_X.no > _X_ENSURE ? _X_RAISE(_X.no) : 0))

/* This macro is just to declutter the definition of try. It call's
 * setjmp() with the top-of-stack jump buffer and returns true is it
 * is the first time (setjmp() returns 0) or if it was called with a
 * retry statement (setjmp() returns state _X_RETRY).
 */
#define _X_SETJMP_SAYS_TRY()                                    \
  (_X_TRY == (_X.no = setjmp(_X.root->jb)) || _X_RETRY == _X.no)

/*****************************************************************************
 * Clear named defines.
 */

/* Try: 

 * Use a for-loop to put a _X_PUSH and _X_POP (and _X_PROPAGATE)
 * around the whole construct and make sure (by using !_X.no) it does
 * not loop. Followed by a while() loop to go from state to state,
 * until FINISHEed. Note that in the while loop, ENSURE is decremented
 * into FINISH whether or not there is a ensure part. This guarantees
 * that the loop is exectuted with the state ENSURE exactly once.
 * Followed by an if-construct that does a setjmp() and checks if the
 * try (re-try) compound statement has to be executed. Followed by a
 * _X_GO_ENSURE.
 */
#define try                                                             \
  for(_X_PUSH,_X.no = _X_TRY; !_X.no; _X_POP,_X_PROPAGATE)              \
    while((_X_ENSURE == _X.no ? _X.no-- : _X.no) != _X_FINISH)          \
      if(_X.no != _X_FINISH && _X_SETJMP_SAYS_TRY())                    \
        _X_GO_ENSURE

/* Catch: 
 * An else-if block that checks to see if the ensuing compound has to
 * be executed. First (line) negative exception numbers never match,
 * they are special for "internal" usage. Second (line) a '*' or a
 * matching exception argument causes the ensuing compound statement
 * to be executed, followed by a _X_GO_ENSURE.
 */
#define catch(x)                                       \
  else if (_X.no > 0 &&                                \
           ((#x[0] == '*') || ((x & _X.mo) == _X.no))) \
    _X_GO_ENSURE

/* Ensure:
 * The ensure is just a simple else, it can be put at the end of the
 * if(){} [ else if() {} ] ... construction to execute some code just
 * before the POP. Since it has no if-part, it will always be executed
 * when provided after the exception has been dealt with or ignored
 * (in which case it will be propagated by _X_PROPAGATE).
 */
#define ensure                                  \
  else

/* Excepno:
 * Analogous to the global variable errno, the excepno "variable" is
 * defined to be the current exception number. However a negative
 * values are bumped to 0 and it can not be used as a lval.
 */
#define excepno                                 \
  (_X.no > 0 ? _X.no : 0)

/* Longjmp macro's: raise(), upagate() and retry might necessitate 
 * volatile autovars. See warning on top of file.
 */

/* Raise:
 * Raise is like the internal _X_RAISE() with a additional check for
 * negative arguments. The _X_RAISE() basically is a longjump using
 * the current (top of stack) buffer.  Note that x is assigned to
 * _X.no to prevent side effect from happening twice.
 */     
#define raise(x)                                                        \
  ((_X.no = (x)) > 0 ? _X_RAISE(_X.no) :_X_exit("illegal non-positive value"))

/* Upagate:
 * Propagating up to the enclosing try/catch construct basically means
 * setting a special variable to either the current exception number
 * or the number provided to the macro and signalling that there are
 * no exceptions left to process by jumping to the first if-statment
 * with a _X_ENSURE. The _X_PROPAGATE macro will check (after the _X_POP)
 * if this upagate variable _X.upa is set, and if so will call
 * _X_RAISE() on the enclosing try/catch construction.
 */
#define upagate(x)                                                      \
  ((_X.upa = #x[0] != '*' ? (x & _X.mo) : _X.no), _X_RAISE(_X_FINISH))

/* Retry:

 * Retry should be as simple as longjmp(buf, 0), but not all systems
 * will propagate the '0', so a standin is used and a double check is
 * done in the try macro.  A simple _X_RAISE(_X_RETRY) would do the
 * trick nicely, however to enhance the error messages the long jump
 * is used with a check for the top of the stack.
 */
#define retry                                                           \
  (_X.root ? longjmp(_X.root->jb,(_X_RETRY)) : _X_exit("uncaught retry"))



/* That's all fokes!
 */
#endif /* __TRYCATCH_H__ */