/*
    ####             #    #     # #
    #   #            #    #       #          The FreeWare C library for
    #   #  ##   ###  #  # #     # ###             RISC OS machines
    #   # #  # #     # #  #     # #  #   ___________________________________
    #   # ####  ###  ##   #     # #  #
    #   # #        # # #  #     # #  #    Please refer to the accompanying
    ####   ### ####  #  # ##### # ###    documentation for conditions of use
    ________________________________________________________________________

    File:    Error2.h
    Author:  Copyright  1995 Julian Smith
    Version: 1.02 (01 Dec 1995)
    Purpose: General error handling.
             These routines are intended to allow automatic error-safety.
             
    History: 1.00 (17 Jan 1995) JS
             1.01 (15 Nov 1995) JS Ensured #define for Desk_Error2_CheckOS
                                   isn't done when making the actual Error2 
                                   library.
             1.02 (01 Dec 1995) JS Added Desk_Error2_HandleAllSignals.
*/




/*

General info on the Error2 library:

The Error2 library is intended to provide a central error-handling
system.


The system is very simple, but hopefully sufficiently general to allow
whatever sort of error-handling is required. The core functionality is
provided by Desk_Error2_Handle(), Desk_Error2_SetHandler(), Desk_Error2_Exit() and the
structure Desk_error2_block:

Whenver a function discovers an error (eg out of memory), it should set
up an Desk_error2_block to contain details of the error and call
Desk_Error2_Handle, passing the address of the Desk_error_block. Many DeskLib
functions already do this.

Desk_Error2_Handle will call the function registered with Desk_Error2_SetHandler,
passing it the (Desk_error_block*). It is up to this function to deal with
the error if it can (in which case it should return Desk_error2_HANDLED).

If it can't deal with the error, it should return a (Desk_error_block*) -
probably the same one it received.

Desk_Error2_Handle will call Desk_Error2_Exit(), which terminates the program, if
the error wasn't handled, or return to its caller if the error was
handled.


Note that only one error handler is used - there is no Desk_Event_-type
system for sending different errors to different handlers, or passing
unhandled errors on to alternative error handlers. The Desk_Error3_ (not 2)
functions do this sort of thing (using the Error2 system as a base), but
they are completely un-tested at the moment.


The rest of the Error2 system consists of:

Desk_error2_globalblock is a global Desk_error2_block which is for anyone's use.

Desk_Error2_CheckOS - Simple wrapper which calls Desk_Error2_Handle if a function
which return an (Desk_os_error*), such as SWI veneers, returns non-NULL.


Desk_Error2_Verbose is an error handler which simply outputs diagnostic info
and then calls abort(). It is intended for use when debugging.

Desk_Error2_SignalHandler is an ANSI C signal handler for use with signal().
It sets up Desk_error2_globalblock to contain info about the signal, and then
calls Desk_Error2_Handle

Desk_Error2_HandleText is useful for generating an error which just consists
of some text. Desk_Error2_HandleTextf is similar, but uses printf-style
formatting.


See the individual declarations in this header file for more info.


NB, the function 'Desk_JumpAuto_Error2Handler', in DeskLib's 'Jump'
sublibrary, is a very useful error-handler. It uses the JumpAuto system
to do a longjmp back to the most recent setjmp place, allowing pseudo
C++ try...throw...catch constructs.




Future plans: A system is needed to allow apps to be given their own
Desk_error2_type values. eg. int Desk_Error2_GetNewErrorType(). Alternatively,
negative error types could be reserved for application use, and positive
for DeskLib libraries.

 */





#ifndef __Desk_Error2_h
#define __Desk_Error2_h

#ifdef __cplusplus
	extern "C" {
#endif

#include <stdio.h>
#include <stdarg.h>

#ifndef __Desk_Error_h
	#include "Error.h"
#endif

#ifndef __Desk_Jump_h
	#include "JumpRaw.h"
#endif



typedef enum	{
	Desk_error2_type_NONE	= 0,
	Desk_error2_type_MISC	= 1, 	/* Miscellaneous error		*/
	Desk_error2_type_OSERROR	= 2, 	/* A SWI returned an error. 	*/
	Desk_error2_type_DESKMEM	= 3, 	/* A Desk_DeskMem_ error.		*/
	Desk_error2_type_SIGNAL	= 4, 	/* A signal was raised.		*/
	Desk_error2_type_MEM		= 5, 	/* A Desk_Mem_ error	(NIY)		*/
	Desk_error2_type_TEXT	= 6	/* An error with a text message	*/
	}
	Desk_error2_type;
/*
Categories of error. Used in Desk_error2_block.

Note that the values of these enums will never change (after the first
official release of DeskLib > 2.30), so that DeskLib DLLs will still
work.
 */

	
typedef struct	Desk_error2_block	{
	Desk_error2_type	type;
	
	union	{
		void*				misc;
		const Desk_os_error*			oserror;
		struct Desk_deskmem_errorblock*	deskmem;
		int				signal;
		/* struct Desk_mem_errorblock* ???	mem;	*/
		const char*			text;
		}
		data;
	}
	Desk_error2_block;
/*
Contains information about an error. 

'type' tells you roughly what sort of error has occurred. For many
purposes, this will be all you need.

Depending on the value of 'type', use
data.misc/oserror/deskmem/signal/text.

See DeskLib:DeskMem.h for definition of struct Desk_deskmem_errorblock - it
isn't included here so that you can use Error2 without necessarily
pulling in DeskMem.

In general, any sublibrary-specific error type will have its own
structure defined in the sublibrary, so that this header file doesn't
have to know about all errors.
 */






void	Desk_Error2_Init_JumpSig( void);
/*
This sets up the standard Desk error-handling system. It simply calls
Desk_Error2_SetHandler( Desk_JumpAuto_Error2Handler) and
Desk_Error2_HandleAllSignals().
 */




void	Desk_Error2_Handle( Desk_error2_block* error);
/*
This function should be called if an error condition arises which can't
be coped with locally.

It calls the function registered with Desk_Error2_SetHandler. If this
function returns, Desk_Error2_globalblock will contain info relating to
how the problem was solved.. Normally, it will simply do a longjmp.

If there us nowhere to longjmp, it will call Desk_Error2_Exit.
 */



#define	Desk_Error2_Try		Desk_JumpAuto_Try

#define	Desk_Error2_Catch	Desk_JumpAuto_Catch

#define	Desk_Error2_EndCatch	Desk_JumpAuto_EndCatch

#define	Desk_Error2_TryCatch	Desk_JumpAuto_TryCatch



Desk_error2_block	*Desk_Error2_Exit( Desk_error2_block *error);
/*
Terminates program by calling Desk_Error_ReportFatal with a default error
message.

Useful to do a 'Desk_Error2_SetHandler( Desk_Error_Exit) to garrantee any error
will terminate the program with a message.
 */

Desk_error2_block	*Desk_Error2_VerboseExit( Desk_error2_block *error);
/*
Useful for debugging / command-line programs. Uses Desk_Error_ReportFatal to
give a description of the error that occurred.

Useful to do a 'Desk_Error2_SetHandler( Desk_Error_VerboseExit) to garrantee any
error will terminate the program with a message.

Desk_Error2_VerboseExit only knows about the error types in the Desk_error2_type
enum. Hence if you have your own error type, you should write your own
verbose exit handler.
 */




typedef int	(*Desk_Error2_vprintf_fn)( void* reference, const char* format, va_list va);
/*
A function-type similar to vfprintf, except that the first parameter is
a (void*).
 */

typedef int	(*Desk_Error2_printf_fn)( void* reference, const char* format, ...);
/*
A function-type similar to fprintf, except that the first parameter is a
(void*).
 */

void	Desk_Error2_VDescribe( Desk_Error2_vprintf_fn fn, void* reference, const Desk_error2_block* error);
/*
Sends text about 'error' to 'fn', passing 'reference'.
 */

void	Desk_Error2_FDescribe( Desk_Error2_printf_fn fn, void* reference, const Desk_error2_block* error);
/*
Sends text about 'error' to 'fn', passing 'reference'.
 */

void	Desk_Error2_Describe( FILE* stream, const Desk_error2_block* error);
/*
Uses Desk_Error2_VDescribe to send info about 'error' to 'stream'.
 */



typedef Desk_error2_block*	(*Desk_error2_handler)( Desk_error2_block *error);
/*
The type of a function which tries to handles errors - the function
returns Desk_error2_HANDLED if succesfull. Use Desk_Error2_SetHandler to register
such a handler function with Error2.
 */

#define	Desk_error2_HANDLED (NULL)
/*
An Desk_error2_handler should return this if it has dealt with an error.
 */



#define	Desk_Error2_SetHandler( handlerfn)	Desk_error2__handlerfn = (handlerfn)
/*
Sets the handler to be called by Desk_Error2_Handle.
*/

#define	Desk_Error2_GetHandler()	Desk_error2__handlerfn
/*
Returns the current handler.
 */



int	Desk_Error2_GetNewErrorType( void);
/*
Returns a unique error number. Useful if an application (or part of an
application) wants to have its own error type.
 */



void	Desk_Error2_CheckOS( const Desk_os_error *error);
/*
A simple error-handler for use with functions which return an
(Desk_os_error*). If error!=NULL, it sets up Desk_error2_globalblock
appropriately for 'error', and calls Desk_Error2_Handle(
Desk_error2_globalblock).

Use like:

Desk_Error2_CheckOS( _swix( ...);

When Desk_DEBUG is defined, Desk_Error2_CheckOS is macro-ed to
Desk_Error2_CheckOS_Debug, which uses Desk_error_PLACE to output the
place where the error occurred.
 */




void			Desk_Error2_CheckOS_Debug( const Desk_os_error *error, const char *place);
/*
Similar to Desk_Error2_CheckOS, except that it expects an extra string
argument, for use with the Desk_DeskLib_DEBUG-version of Desk_Error2_CheckOS.
 */



#if defined( Desk_DeskLib_DEBUG) && !defined( Desk__making_Error2)
	
	#define	Desk_Error2_CheckOS( oserror)	Desk_Error2_CheckOS_Debug( oserror, Desk_error_PLACE)
/*
This macro is defined for debugging purposes. It behaves in the same
way, except that the 'text' field of Desk_error2_globalblock is set to
Desk_error_PALCE, allowing an error-handler to display where the error came
from.
 */
#endif



void	Desk_Error2_CheckBOOL( Desk_bool error);
/*
Calls Desk_Error2_Handle if error!=NOERROR.
*/



void	Desk_Error2_HandleText( const char* text);
/*
Sets up Desk_error2_globalblock to contain the text message and than calls
Desk_Error2_Handle
 */


void	Desk_Error2_HandleTextf( const char* text, ...);
/*
Sets up Desk_error2_globalblock to contain the text message and than calls
Desk_Error2_Handle. The text is specified as with printf.

Note that the text is sprintf-ed into an internal static 252-byte block,
and no checking is done that it isn't too long.
 */




void	Desk_Error2_SignalHandler( int signalnumber);
/*
This function sets up Desk_error2_globalblock to contain information about
the signal number 'signalnumber', and then calls Desk_Error2_Handle.

To use, register Desk_Error2_SignalHandler with the ANSI 'signal()' function
for all the signals that you want to be treated with the Error2-library
mechanism, eg:

int	main( void)
{
signal( SIGFPE,  &Desk_Error2_SignalHandler);
...
}

Note that when a signal occurs, ANSI C resets signal handling for the
signal. To get around this, when called, Desk_Error2_SignalHandler calls
signal() to re-register itself for the specified signal.

You shouldn't call Desk_Error2_SignalHandler directly.
 */



void	Desk_Error2_HandleAllSignals( void);
/*
This registers Desk_Error2_SignalHandler with signal() for all signals
(SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGSTAK, SIGUSR1,
SIGUSR2, SIGOSERROR).

This could be useful when debugging.
 */



void	Desk_Error2_ClearErrorBlock( Desk_error2_block *error);
/*
Clears the various fields in 'error'. Useful when setting up an error
block.
 */

#ifdef Desk__using_SDLS
	extern	Desk_error2_block*	Desk_Error2__Ref_globalblock( void);
	extern	Desk_error2_handler*	Desk_Error2__Ref__handlerfn( void);
#endif


#if defined( Desk__using_SDLS) && !defined( Desk__making_Error2)

	#define	Desk_error2_globalblock	(*Desk_Error2__Ref_globalblock())
	#define	Desk_error2__handlerfn	(*Desk_Error2__Ref__handlerfn())

#else

	extern	Desk_error2_block	Desk_Error2_globalblock;
/*
A global Desk_error2_block, for use by anyone. It is used by Desk_Error2_HandleOS
etc.
 */

	extern	Desk_error2_handler	Desk_error2__handlerfn;
/*
This is a global pointer to the function which is called by
Desk_Error2_Handle. Use the macros Desk_Error2_SetHandler and
Desk_Error2_GetHandler to access it.

Set to NULL initally.
 */

#endif



#define	Desk_Error2_ReThrow()	Desk_Error2_Handle( &Desk_Error2_globalblock)



extern const char *Desk_Error2_signalnames[];
/*
Array of textual descriptions of signals.
 */

void	Desk_Error2_ConvertToOS( Desk_os_error* oserror, Desk_error2_block* error2);
/*
Makes the os_error pointed to by 'oserror' contain info from 'error2'.
 */


#define	Desk_Error2_ConvertToOS2()	(Desk_Error2_ConvertToOS( &Desk_error_global, &Desk_Error2_globalblock), &Desk_error_global)
/*
Treat like:
Desk_os_error*	Desk_Error2_ConvertToOS2( void);

Returns the current Desk_Error2 error converted to a (Desk_os_error*).
Useful in functions which need to return a (os_error*) - eg:


Desk_os_error*	Foo( ...)
	{
	Desk_Error2_Try	{
		// Call various functions which might raise a Desk_Error2 error
		}
	Desk_Error2_Catch	{
		// Clean up.
		return Desk_Error2_ConvertToOS2();
		}
	Desk_Error2_EndCatch
	return NULL;
	}

 */


void	Desk_Error2_MsgTransInit( const char* filename);
/*
Loads the specified file using Desk_MessageTrans_OpenFileRMA, using an
internal Desk_msgtrans_rmafiledesc*.

This messages file will be used for all subsequent calls to
Desk_Error2_MsgTransHandle().
 */

void	Desk_Error2_MsgTransFinal( void);
/*
Calls Desk_MessageTrans_CloseFileRMA() for the messages file previously
passed to Desk_Error2_MsgTransInit().
 */

void	Desk_Error2_MsgTransHandle( const char* token, const char* p0, const char* p1, const char* p2, const char* p3);
/*
Raises a text Desk_Error2 exception using the specified token and p0-p3,
using the messages file opened with Desk_Error2_MsgTransInit().
 */

void	Desk_Error2_MsgTransHandle0( const char* token);
/*
As Desk_Error2_MsgTransHandle, except no substitution is performed. This
requires the message text to be terminated with '\0'.
 */


void	Desk_Error2_MsgTransHandlef( const char* token, ...);
/*
As Desk_Error2_MsgTransHandle, except uses Desk_MessageTrans_Lookupf()
to do a printf-style expansion of the token's value.
 */








/*
Error3
------

Error3 is an Desk_Event_-type library for registering handlers for different
sorts of error. 

To use Error3, simply call Desk_Error3_UseError3() somwhere. This simply
registers Desk_Error3__Dispatch as Error2's single error-handling function.

Desk_Error3__Dispatch looks for a previously-registered handler which matches
the error, and calls it.

Hence any calls to Desk_Error2_Handle and Desk_Error2_XHandle (even those from
within a previously-compiled library, such as DeskLib) will end up being
dealt with by Error3's registered handler functions.

You shouldn't normally have to call Desk_Error3__Dispatch directly yourself -
use Desk_Error2_Handle and Desk_Error2_XHandle.

Note that the Error3 calls are completely untested...
 */


#define	Desk_Error3_UseError3()	Desk_Error2_SetHandler( Desk_Error3__Dispatch);
/*
Makes all subsequent error handling by Desk_Error2_Handle and Desk_Error2_XHandle
use the the Error3 system.
 */


typedef Desk_error2_block*	(*Desk_error3_handler)( Desk_error2_block *error, void *reference);
/*
The type of functions which can be registered with Desk_Error2_Claim* to
handle errors. These functions should return Desk_error2_HANDLED if succesful.
 */

/*void	Desk_Error3_ClaimFunction( Desk_error2_fnptr function, Desk_error3_handler handler, void *reference);
*//*
Register a handler to be called when an error is raised by 'function'. 

Function-specific handlers are checked before type-specific handlers
(registered with Desk_Error3_ClaimType) and general handlers (registered with
Desk_Error3_ClaimMisc).

New handlers are added to front of the list of handlers, so they are
called in preference to any existing handlers.
 */

void	Desk_Error3_ClaimType( Desk_error2_type type, Desk_error3_handler handler, void *reference);
/*
Register a handler to be called when an error of a particular type
'type' occurs. 

Type-specific handlers are checked after function-specific ones
(registered with Desk_Error3_ClaimFunction), and before general handlers
(registered with Desk_Error3_ClaimMisc).

New handlers are added to front of the list of handlers, so they are
called in preference to any existing handlers.
 */

void	Desk_Error3_ClaimMisc( Desk_error3_handler handler, void *reference);
/*
Register a misc handler to be called when any error occurs. 

Misc handlers are only checked after function-specific handlers
(registered with Desk_Error3_ClaimFunction) and type-specific
handlers (registered with Desk_Error3_ClaimType).

New handlers are added to front of the list of handlers, so they are
called in preference to any existing handlers.
 */


/*void	Desk_Error3_ReleaseFunction( Desk_error2_fnptr function, Desk_error3_handler handler, void *reference);
*//*
Removes 'handler' from Error3's list of handlers. 'function' and
'reference' must be the same as in the call to Desk_Error3_ClaimFunction
which registered 'handler'.
 */

void	Desk_Error3_ReleaseType( Desk_error2_type type, Desk_error3_handler handler, void *reference);
/*
Removes 'handler' from Error3's list of handlers. 'type' and 'reference'
must be the same as in the call to Desk_Error3_ClaimType which registered
'handler'.
 */

void	Desk_Error3_ReleaseMisc( Desk_error3_handler handler, void *reference);
/*
Removes 'handler' from Error3's list of handlers. 'reference' must be
the same as in the call to Desk_Error3_ClaimFunction which registered
'handler'.
 */

Desk_error2_block*	Desk_Error3__Dispatch( Desk_error2_block* error, void* reference);
/*
Searches for and calls a suitable error-handler for error.
 */




#ifdef __cplusplus
}
#endif


#endif
