aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/error_handling.h
blob: d1275944fde62896cd9dc70295645cd1d2fe6044 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

/* 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;
        
/* 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