/*
 * Exception Handling
 *
 * Copyright (c) 2000
 *    Issei Suzuki <issei@issei.org>   All rights reserved.
 *
 * $Id: except.h,v 1.7 2000/08/20 03:50:01 issei Exp $
 */

#ifndef _EXCEPTION_H_20000819_
#define _EXCEPTION_H_20000819_

#include <setjmp.h>
#include <assert.h>

#ifndef EXCEPT_NESTMAX
# define EXCEPT_NESTMAX	32
#endif

struct except;
typedef void (*except_handler_t)(void);

extern const int except_inflag;

except_handler_t  except_handler_regist	  (except_handler_t);
int		  except_nestlevel	  (void);
int	 	  except_type		  (void);
const char 	 *except_message	  (void);

/* DO NOT CALL THESE FUNCTIONS DIRECTLY */
void		  except_stack_grow_xxx	  (void);
void		  except_stack_shrink_xxx (void);
jmp_buf		 *except_try_xxx	  (void);
void		  except_throw_xxx	  (const char *, int);
void		  except_throwup_xxx	  (void);
void		  except_catch_xxx	  (void);
int		  except_ishold_xxx	  (void);
void		  except_handler_exec_xxx (const char *, int);

/*
 * 1. TRY, CATCH を用いてコードを保護する
 *
 * -------------------------------------------------------------------
 *   TRY {
 *      protected_area
 *   }
 *   CATCH (EX_IOERR) {
 *      protected_area で EX_IOERR 例外が発生したときに実行される
 *   }
 *   CATCH (EX_FOOERR) {
 *      ...
 *   }
 *   CATCH_ALL {
 *      protected_area で発生した例外に対応する CATCH が存在しない
 *      ときに実行される
 *   }
 *   END_TRY
 * -------------------------------------------------------------------
 *
 *  a) CATCH セクションは必要に応じて 0 個以上記述できる。各々の
 *     CATCH セクションでは、ただ一種類の例外を捕獲できる。
 *  b) CATCH_ALL は CATCH セクションで捕獲できなかった例外を全て
 *     捕獲する。
 *  c) CATCH, CATCH_ALL セクションは、いずれも省略可能。
 *  d) 捕獲されなかった例外は、自動的に上位のハンドラに投げられる。
 *  e) main までたどり着いても例外が捕獲されなかった場合には、あら
 *     かじめ except_handler_regist() で登録しておいた例外ハンドラ
 *     が呼ばれる。デフォルトでは例外発生個所を標準エラー出力に書き
 *     出して終了する。
 *
 * 2. 例外を投げる
 *
 *   THROW(except_type, except_message);
 *     except_type 型の例外を発生させる。例外に関する付加情報を
 *     except_message に記述する。
 *
 *   THROW_PASSUP()
 *     例外ハンドラで捕らえた例外を、そのまま上位のハンドラに投
 *     げる。
 */
#define TRY								\
	{								\
	switch (setjmp(*except_try_xxx())) {				\
	case 0:								\
	{

#define CATCH_XXX(case_label)						\
	break;								\
	}								\
	case_label:							\
	{								\
		const int except_inflag = 1;				\
		except_catch_xxx();

#define CATCH(type)	CATCH_XXX(case type)
#define CATCH_ALL	CATCH_XXX(default)

#define END_TRY								\
	}}								\
	assert(except_nestlevel() > 0);					\
	if (except_ishold_xxx()) {					\
		THROW_PASSUP();						\
		/* NOTREACHED */					\
	}								\
	except_stack_shrink_xxx();					\
	}

#define THROW(type,msg)							\
	do {								\
		if (except_inflag) {					\
			assert(except_nestlevel() > 1);			\
			except_stack_shrink_xxx();			\
		}							\
		if (except_nestlevel() <= 0)				\
			except_handler_exec_xxx(__FILE__, __LINE__);	\
		except_throw_xxx(msg, type);				\
	} while (0)
		
#define	THROW_PASSUP()							\
	do {								\
 		assert(except_inflag || except_ishold_xxx());		\
		if (except_nestlevel() <= 1)				\
			except_handler_exec_xxx(__FILE__, __LINE__);	\
		except_throwup_xxx();					\
	} while (0)

#endif /* _EXCEPTION_H_20000819_ */
