#ifndef COMMON_EXCEPT_H
#define COMMON_EXCEPT_H

/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * 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.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
 */

/* Support for quasi-exceptions in C. */

typedef const char *Exception;

typedef struct excpt_unwind excpt_unwind;
struct excpt_unwind {
  OC_bool isArmed;
  const char *path;
  excpt_unwind *next;
};

typedef struct catch catch_t;
struct catch {
  jmp_buf  jbuf;		/* saved catch environment */
  Exception excpt;		/* thrown exception value */
  const char *fname;		/* file where thrown (string) */
  uint32_t line;		/* line number where thrown */
  const char *str;		/* explanation string */
  catch_t  *up;			/* next (upward) catch block */
  OC_bool caught;                  /* whether this exception was handled */
  excpt_unwind *unwind;		/* things to unwind on throw/exit */
};

#define EXCEPTION(name) \
  extern const Exception name;

#define DEFEXCEPTION(name,str) \
  const Exception name = #name

#define EXCEPTION_NAME(ex) ex

EXCEPTION(ExAssert);		/* Assertion failure */
EXCEPTION(ExOutOfMemory);
EXCEPTION(ExIntegrityFail);	/* object integrity has failed */
EXCEPTION(ExMalformed);		/* object violates its schema */
EXCEPTION(ExBadValue);		/* inappropriate value as procedure arg */
EXCEPTION(ExNullArg);		/* null argument where valid ptr required */
EXCEPTION(ExNoAccess);
EXCEPTION(ExNoObject);
EXCEPTION(ExObjectExists);
EXCEPTION(ExTruncated);
EXCEPTION(ExOverrun);		/* I/O proceeded past end of object */
EXCEPTION(ExSubprocess);	/* FIX: This is too general */
EXCEPTION(ExLockFail);		/* Could not acquire lock */
EXCEPTION(ExLostLock);		/* Lock race failed */
EXCEPTION(ExUniverseDied);	/* Heat death of universe has transpired! */
EXCEPTION(ExNoConnect);		/* Network connect/session setup failed */
EXCEPTION(ExNoAuth);		/* Unable to authenticate */
EXCEPTION(ExEnviron);		/* Missing something from environment */
EXCEPTION(ExVersionError);	/* Unknown object version */
EXCEPTION(ExConnLost);		/* Network connection lost */
EXCEPTION(ExIoError);		/* Error during I/O */
EXCEPTION(ExBadOpcode);		/* Unknown protocol operation */
EXCEPTION(ExPrngError);         /* Some problem with the PRNG */

/* It is *always* a mistake to use this: */
EXCEPTION(ExUnspecified);	/* use when you can't decide */

extern catch_t *_curCatch;

/* There is no real need for a function here, but it won't be called
   much in a properly functioning program, and it is very convenient
   to have a place to breakpoint in order to catch such things in a
   debugger. */
extern void _throw(catch_t *, 
		   const char *fname, 
		   uint32_t line, 
		   Exception,
		   const char *s) __attribute__ ((__noreturn__));

extern void _disarm_throw(catch_t *);

#define TRY { catch_t _catch; \
    _catch.excpt = NULL; \
    _catch.str = 0; \
    _catch.fname = "<unknown>"; \
    _catch.line = 0; \
    _catch.caught = FALSE; \
    _catch.up = _curCatch; \
    _catch.unwind = 0; \
    _curCatch = &_catch; \
    if (!setjmp(_catch.jbuf)) { /* block ends up here */

#define _EXCEPT_EQUAL(e1, e2) \
   (e1 == e2 || strcmp(e1,e2) == 0)

/* Use this one if you don't care about the object pointer */
#define CATCH(exception) } \
  else if (_EXCEPT_EQUAL(_catch.excpt,exception)) { \
    _catch.caught = TRUE;
    /* block ends up here */

/* Use this one to access just the errcode */
#define DEFAULT(exception) } else { \
    Exception exception __attribute__ ((unused)) = _catch.excpt; \
    _catch.caught = TRUE;
    /* block ends up here */

#define THROW(ex,s) _throw(_curCatch, __FILE__, __LINE__, (ex), (s))
#define RETHROW(ex) _throw(_catch.up, _catch.fname, _catch.line, (ex), _catch.str)

#define END_CATCH }  \
   assert(_curCatch == &_catch); \
   _catch.unwind = 0; \
   if (_catch.excpt && !_catch.caught) \
     RETHROW(_catch.excpt); \
   _curCatch = _catch.up; /* defuse */ }

void onthrow_remove(const char *pathName);
void do_exit(int status) __attribute__ ((__noreturn__));

#endif /* COMMON_EXCEPT_H */
