Initial commit
This commit is contained in:
342
engines/glk/tads/tads2/error_handling.h
Normal file
342
engines/glk/tads/tads2/error_handling.h
Normal file
@@ -0,0 +1,342 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Library error handling definitions
|
||||
* All of the functions and macros in here are named ERRxxx because
|
||||
* this file was based on the TADS err.h, which used the ERRxxx naming
|
||||
* convention, and it would be a lot of trouble to change.
|
||||
*
|
||||
* This package defines a set of macros that allows code to raise and
|
||||
* handle exceptions. A macro is provided which signals an error, which
|
||||
* does a non-local goto to the innermost enclosing exception handler.
|
||||
* A set of macros sets up exception handling code.
|
||||
*
|
||||
* To catch exceptions that occur inside a block of code (i.e., in the
|
||||
* code or in any subroutines called by the code), begin the block with
|
||||
* ERRBEGIN. At the end of the protected code, place the exception
|
||||
* handler, which starts with ERRCATCH. At the end of the exception
|
||||
* handler, place ERREND. If no exception occurs, execution goes
|
||||
* through the protected code, then resumes at the code following
|
||||
* the ERREND.
|
||||
*
|
||||
* The exception handler can signal another error, which will cause
|
||||
* the next enclosing frame to catch the error. Alternatively, if
|
||||
* the exception handler doesn't signal an error or return, execution
|
||||
* continues at the code following the ERREND. Exceptions that are
|
||||
* signalled during exception handling will be caught by the next
|
||||
* enclosing frame, unless the exception handler code is itself
|
||||
* protected by another ERRBEGIN-ERREND block.
|
||||
*
|
||||
* To signal an error, use errsig().
|
||||
*
|
||||
* To use a string argument in a signalled error, cover the string
|
||||
* with errstr(ctx, str, len); for example:
|
||||
*
|
||||
* errsig1(ctx, ERR_XYZ, ERRTSTR, errstr(ctx, buf, strlen(buf)));
|
||||
*
|
||||
* This copies the string into a buffer that is unaffected by
|
||||
* stack resetting during error signalling.
|
||||
*/
|
||||
|
||||
#ifndef GLK_TADS_TADS2_ERROR_HANDLING
|
||||
#define GLK_TADS_TADS2_ERROR_HANDLING
|
||||
|
||||
#include "glk/tads/tads2/lib.h"
|
||||
#include "glk/tads/os_frob_tads.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace TADS {
|
||||
namespace TADS2 {
|
||||
|
||||
/**
|
||||
* Maximum length of a facility identifier
|
||||
*/
|
||||
#define ERRFACMAX 6
|
||||
|
||||
union erradef {
|
||||
int erraint; /* integer argument */
|
||||
const char *errastr; /* text string argument */
|
||||
};
|
||||
|
||||
struct errdef {
|
||||
struct errdef *errprv; /* previous error frame */
|
||||
int errcode; /* error code of exception being handled */
|
||||
char errfac[ERRFACMAX+1]; /* facility of current error */
|
||||
erradef erraav[10]; /* parameters for error */
|
||||
int erraac; /* count of parameters in argc */
|
||||
};
|
||||
|
||||
#define ERRBUFSIZ 512
|
||||
|
||||
/**
|
||||
* Seek location record for an error message by number
|
||||
*/
|
||||
struct errmfdef {
|
||||
uint errmfnum; /* error number */
|
||||
ulong errmfseek; /* seek location of this message */
|
||||
};
|
||||
typedef struct errmfdef errmfdef;
|
||||
|
||||
struct errcxdef {
|
||||
errdef *errcxptr; /* current error frame */
|
||||
void (*errcxlog)(void *, const char *fac, int err, int argc, erradef *);
|
||||
/* error logging callback function */
|
||||
void *errcxlgc; /* context for error logging callback */
|
||||
int errcxofs; /* offset in argument buffer */
|
||||
char errcxbuf[ERRBUFSIZ]; /* space for argument strings */
|
||||
osfildef *errcxfp; /* message file, if one is being used */
|
||||
errmfdef *errcxseek; /* seek locations of messages in file */
|
||||
uint errcxsksz; /* size of errcxseek array */
|
||||
ulong errcxbase; /* offset in physical file of logical error file */
|
||||
struct appctxdef *errcxappctx; /* host application context */
|
||||
};
|
||||
|
||||
/**
|
||||
* Begin protected code
|
||||
*/
|
||||
#define ERRBEGIN(ctx) \
|
||||
{ \
|
||||
errdef fr_; \
|
||||
if (1 /*(fr_.errcode = setjmp(fr_.errbuf)) == 0 */) \
|
||||
{ \
|
||||
fr_.errprv = (ctx)->errcxptr; \
|
||||
(ctx)->errcxptr = &fr_;
|
||||
|
||||
/**
|
||||
* End protected code, begin error handler
|
||||
*/
|
||||
#define ERRCATCH(ctx, e) \
|
||||
assert(1==1 && (ctx)->errcxptr != fr_.errprv); \
|
||||
(ctx)->errcxptr = fr_.errprv; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
assert(2==2 && (ctx)->errcxptr != fr_.errprv); \
|
||||
(e) = fr_.errcode; \
|
||||
(ctx)->errcxptr = fr_.errprv;
|
||||
|
||||
#define ERRCATCH_ERRCODE_UNUSED(ctx) \
|
||||
assert(1==1 && (ctx)->errcxptr != fr_.errprv); \
|
||||
(ctx)->errcxptr = fr_.errprv; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
assert(2==2 && (ctx)->errcxptr != fr_.errprv); \
|
||||
(ctx)->errcxptr = fr_.errprv;
|
||||
|
||||
/* retrieve argument (int, string) in current error frame */
|
||||
#define errargint(argnum) (fr_.erraav[argnum].erraint)
|
||||
#define errargstr(argnum) (fr_.erraav[argnum].errastr)
|
||||
|
||||
|
||||
#define ERREND(ctx) \
|
||||
} \
|
||||
}
|
||||
|
||||
/* end protected code, begin cleanup (no handling; just cleaning up) */
|
||||
#define ERRCLEAN(ctx) \
|
||||
assert((ctx)->errcxptr != fr_.errprv); \
|
||||
(ctx)->errcxptr = fr_.errprv; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
assert((ctx)->errcxptr != fr_.errprv); \
|
||||
(ctx)->errcxptr = fr_.errprv;
|
||||
|
||||
#define ERRENDCLN(ctx) \
|
||||
errrse(ctx); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* argument types for errors with arguments */
|
||||
#define ERRTINT erraint
|
||||
#define ERRTSTR errastr
|
||||
|
||||
/* set argument count in error frame */
|
||||
#define errargc(ctx,cnt) ((ctx)->errcxptr->erraac=(cnt))
|
||||
|
||||
/* enter string argument; returns pointer to argument used in errargv */
|
||||
#ifdef ERR_NO_MACRO
|
||||
char *errstr(errcxdef *ctx, const char *str, int len);
|
||||
#else /* ERR_NO_MACRO */
|
||||
|
||||
#define errstr(ctx,str,len) \
|
||||
((memcpy(&(ctx)->errcxbuf[(ctx)->errcxofs],str,(size_t)len), \
|
||||
(ctx)->errcxofs += (len), \
|
||||
(ctx)->errcxbuf[(ctx)->errcxofs++] = '\0'), \
|
||||
&(ctx)->errcxbuf[(ctx)->errcxofs-(len)-1])
|
||||
|
||||
#endif /* ERR_NO_MACRO */
|
||||
|
||||
/* set argument in error frame argument vector */
|
||||
#define errargv(ctx,index,typ,arg) \
|
||||
((ctx)->errcxptr->erraav[index].typ=(arg))
|
||||
|
||||
/* signal an error with argument count already set */
|
||||
#ifdef ERR_NO_MACRO
|
||||
void errsign(errcxdef *ctx, int e, const char *facility);
|
||||
#else /* ERR_NO_MACRO */
|
||||
# ifdef DEBUG
|
||||
void errjmp(jump_buf buf, int e);
|
||||
# define errsign(ctx, e, fac) \
|
||||
(strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\
|
||||
(ctx)->errcxptr->errfac[ERRFACMAX]='\0',\
|
||||
(ctx)->errcxofs=0, errjmp((ctx)->errcxptr->errbuf, e))
|
||||
# else /* DEBUG */
|
||||
# define errsign(ctx, e, fac) \
|
||||
(strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\
|
||||
(ctx)->errcxptr->errfac[ERRFACMAX]='\0',\
|
||||
(ctx)->errcxofs=0, longjmp((ctx)->errcxptr->errbuf, e))
|
||||
# endif /* DEBUG */
|
||||
#endif /* ERR_NO_MACRO */
|
||||
|
||||
|
||||
/* signal an error with no arguments */
|
||||
#ifdef ERR_NO_MACRO
|
||||
void errsigf(errcxdef *ctx, const char *facility, int err);
|
||||
#else /* ERR_NO_MACRO */
|
||||
#define errsigf(ctx, fac, e) (errargc(ctx,0),errsign(ctx,e,fac))
|
||||
#endif /* ERR_NO_MACRO */
|
||||
|
||||
/* signal an error with one argument */
|
||||
#define errsigf1(ctx, fac, e, typ1, arg1) \
|
||||
(errargv(ctx,0,typ1,arg1),errargc(ctx,1),errsign(ctx,e,fac))
|
||||
|
||||
/* signal an error with two arguments */
|
||||
#define errsigf2(ctx, fac, e, typ1, arg1, typ2, arg2) \
|
||||
(errargv(ctx,0,typ1,arg1), errargv(ctx,1,typ2,arg2), \
|
||||
errargc(ctx,2), errsign(ctx,e,fac))
|
||||
|
||||
/* resignal the current error - only usable within exception handlers */
|
||||
#ifdef ERR_NO_MACRO
|
||||
void errrse1(errcxdef *ctx, errdef *fr);
|
||||
# define errrse(ctx) errrse1(ctx, &fr_)
|
||||
#else /* ERR_NO_MACRO */
|
||||
|
||||
/* void errrse(errcxdef *ctx); */
|
||||
# define errrse(ctx) \
|
||||
(errargc(ctx, fr_.erraac),\
|
||||
memcpy((ctx)->errcxptr->erraav, fr_.erraav, \
|
||||
(size_t)(fr_.erraac*sizeof(erradef))),\
|
||||
errsign(ctx, fr_.errcode, fr_.errfac))
|
||||
|
||||
#endif /* ERR_NO_MACRO */
|
||||
|
||||
/*
|
||||
* For use in an error handler (ERRCATCH..ERREND) only: Copy the
|
||||
* parameters from the error currently being handled to the enclosing
|
||||
* frame. This is useful when "keeping" an error being handled - i.e.,
|
||||
* the arguments will continue to be used outside of the
|
||||
* ERRCATCH..ERREND code.
|
||||
*/
|
||||
/* void errkeepargs(errcxdef *ctx); */
|
||||
#define errkeepargs(ctx) errcopyargs(ctx, &fr_)
|
||||
|
||||
/*
|
||||
* copy the parameters for an error from another frame into the current
|
||||
* frame - this can be used when we want to be able to display an error
|
||||
* that occurred in an inner frame within code that is protected by a
|
||||
* new enclosing error frame
|
||||
*/
|
||||
/* void errcopyargs(errcxdef *ctx, errdef *fr); */
|
||||
#define errcopyargs(ctx, fr) \
|
||||
(errargc((ctx), (fr)->erraac), \
|
||||
memcpy((ctx)->errcxptr->erraav, (fr)->erraav, \
|
||||
(size_t)((fr)->erraac*sizeof(erradef))))
|
||||
|
||||
/* log error that's been caught, using arguments already caught */
|
||||
#define errclog(ctx) \
|
||||
((*(ctx)->errcxlog)((ctx)->errcxlgc,fr_.errfac,fr_.errcode,\
|
||||
fr_.erraac,fr_.erraav))
|
||||
|
||||
/* log an error that's been set up but not signalled yet */
|
||||
#define errprelog(ctx, err) \
|
||||
((*(ctx)->errcxlog)((ctx)->errcxlgc,(ctx)->errcxptr->errfac,\
|
||||
err,(ctx)->errcxptr->erraac,\
|
||||
(ctx)->errcxptr->erraav))
|
||||
|
||||
/* log an error (no signalling, just reporting) */
|
||||
#ifdef ERR_NO_MACRO
|
||||
void errlogn(errcxdef *ctx, int err, const char *facility);
|
||||
#else /* ERR_NO_MACRO */
|
||||
|
||||
#define errlogn(ctx,err,fac) \
|
||||
((ctx)->errcxofs=0,\
|
||||
(*(ctx)->errcxlog)((ctx)->errcxlgc,fac,err,(ctx)->errcxptr->erraac,\
|
||||
(ctx)->errcxptr->erraav))
|
||||
|
||||
#endif /* ERR_NO_MACRO */
|
||||
|
||||
/* log an error with no arguments */
|
||||
#ifdef ERR_NO_MACRO
|
||||
void errlogf(errcxdef *ctx, const char *facility, int err);
|
||||
#else /* ERR_NO_MACRO */
|
||||
|
||||
/* void errlogf(errcxdef *ctx, char *facility, int err); */
|
||||
#define errlogf(ctx,fac,err) (errargc(ctx,0),errlogn(ctx,err,fac))
|
||||
|
||||
#endif /* ERR_NO_MACRO */
|
||||
|
||||
/* log an error with one argument */
|
||||
#define errlogf1(ctx, fac, e, typ1, arg1) \
|
||||
(errargv(ctx,0,typ1,arg1),errargc(ctx,1),errlogn(ctx,e,fac))
|
||||
|
||||
/* log an error with two arguments */
|
||||
#define errlogf2(ctx, fac, e, typ1, arg1, typ2, arg2) \
|
||||
(errargv(ctx,0,typ1,arg1),errargv(ctx,1,typ2,arg2),\
|
||||
errargc(ctx,2),errlogn(ctx,e,fac))
|
||||
|
||||
|
||||
/*
|
||||
* Format an error message, sprintf-style, using arguments in an
|
||||
* erradef array (which is passed to the error-logging callback).
|
||||
* Returns the length of the output string, even if the actual
|
||||
* output string was truncated because the outbuf was too short.
|
||||
* (If called with outbufl == 0, nothing will be written out, but
|
||||
* the size of the buffer needed, minus the terminating null byte,
|
||||
* will be computed and returned.)
|
||||
*/
|
||||
int errfmt(char *outbuf, int outbufl, char *fmt, int argc,
|
||||
erradef *argv);
|
||||
|
||||
/* get the text of an error */
|
||||
void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err);
|
||||
|
||||
/* initialize error subsystem, opening error message file if necessary */
|
||||
void errini(errcxdef *ctx, osfildef *fp);
|
||||
|
||||
/* allocate and initialize error context, free error context */
|
||||
errcxdef *lerini();
|
||||
void lerfre(errcxdef *ctx);
|
||||
|
||||
/* error message structure - number + text */
|
||||
struct errmdef {
|
||||
uint errmerr; /* error number */
|
||||
char *errmtxt; /* text of error message */
|
||||
};
|
||||
|
||||
} // End of namespace TADS2
|
||||
} // End of namespace TADS
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user