1 /* $Header: d:/cvsroot/tads/tads3/vmerr.h,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */
2 
3 /*
4  *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5  *
6  *   Please see the accompanying license file, LICENSE.TXT, for information
7  *   on using and copying this software.
8  */
9 /*
10 Name
11   vmerr.h - VM exception handling
12 Function
13   Defines error macros for try/throw exception handling.
14 
15   To throw an exception, use err_throw(exception_object), where the
16   exception_object is an object describing the exception.  This object
17   must be allocated with 'new'.  Control will immediately transfer to
18   the nearest enclosing err_catch() block.
19 
20   Protected code is defined as shown below.  The blocks must occur
21   in the order shown, but the err_catch and err_finally blocks are
22   optional (although it would be pointless to have an err_try that
23   omits both).
24 
25     err_try
26     {
27         // protected code
28     }
29     err_catch(exception_variable) // or just err_catch_disc
30     {
31         // Exception handler - this code is executed only if an
32         // exception occurs in the protected block.
33         //
34         // If an exception is thrown here (with no nested exception
35         // handler to catch it), the err_finally block will be executed
36         // and then the exception will be thrown to the enclosing block.
37     }
38     err_finally
39     {
40         // Code that is executed regardless whether exception occurs
41         // or not.  If no exception occurs, this code is executed as
42         // soon as the protected block finishes.  If an exception
43         // occurs, this code is executed, then the exception is
44         // re-thrown.
45         //
46         // If an exception is thrown here, the finally block will
47         // be aborted and the enclosing error handler will be activated.
48         // Care should be taken to ensure that code within this block
49         // is properly protected against exceptions if necessary.
50     }
51     err_end;
52 
53   err_catch automatically defines the given exception_variable as
54   a local variable of type (CVmException *) in the scope of the
55   err_catch block.  The referenced CVmException object is valid
56   *only* within the err_catch block; after the err_catch block exits,
57   the CVmException object will no longer be present.
58 
59   If you don't need to access the CVmException information, use
60   err_catch_disc instead of err_catch - this has the same effect as
61   err_catch, but does not declare a local variable to point to the
62   exception object.
63 
64   To re-throw the exception being handled from an err_catch() block,
65   use err_rethrow().
66 
67   IMPORTANT: control must NOT be transferred out of an err_try, err_catch,
68   or err_finally block via break, goto, or return.  Actual execution of
69   the err_end is required in order to properly unwind the error stack.
70   The easiest way to leave one of these blocks prematurely, if necessary,
71   is with a goto:
72 
73      err_try
74      {
75         // some code
76 
77         if (done_with_this_block)
78             goto done;
79 
80         // more code
81 
82      done: ;
83      }
84      err_catch_disc
85      {
86          // etc...
87      }
88      err_end;
89 
90 Notes
91 
92 Modified
93   10/20/98 MJRoberts  - Creation
94 */
95 
96 #ifndef VMERR_H
97 #define VMERR_H
98 
99 #include <setjmp.h>
100 #include <stdarg.h>
101 
102 #include "t3std.h"
103 #include "vmerrnum.h"
104 
105 
106 /*
107  *   Error Message Definition structure
108  */
109 struct err_msg_t
110 {
111     /* message number */
112     int msgnum;
113 
114     /* concise message text */
115     const char *short_msgtxt;
116 
117     /* verbose message text */
118     const char *long_msgtxt;
119 };
120 
121 /* VM error message array */
122 extern const err_msg_t *vm_messages;
123 extern size_t vm_message_count;
124 
125 /*
126  *   VM error message array - English version.  This version of the
127  *   messages is linked directly into the VM; at run time, we can attempt
128  *   to replace this version with another language version obtained from
129  *   an external file.  We link in the English version so that we will
130  *   always have a valid set of messages even if the user doesn't have a
131  *   message file installed.
132  */
133 extern const err_msg_t vm_messages_english[];
134 extern size_t vm_message_count_english;
135 
136 /* external message file signature */
137 #define VM_MESSAGE_FILE_SIGNATURE "TADS3.Message.0001\n\r\032"
138 
139 
140 /*
141  *   load an external message file - returns zero on success, non-zero on
142  *   failure
143  */
144 int err_load_message_file(osfildef *fp,
145                           const err_msg_t **arr, size_t *arr_size,
146                           const err_msg_t *default_arr,
147                           size_t default_arr_size);
148 
149 /* load default message file */
150 #define err_load_vm_message_file(fp) \
151     err_load_message_file((fp), &vm_messages, &vm_message_count, \
152                           vm_messages_english, vm_message_count_english)
153 
154 /*
155  *   check to see if an external message file has been loaded for the
156  *   default VM message set
157  */
158 int err_is_message_file_loaded();
159 
160 /*
161  *   delete messages previously loaded with err_load_message_file (this is
162  *   called automatically by err_terminate, so clients generally will not
163  *   need to call this directly)
164  */
165 void err_delete_message_array(const err_msg_t **arr, size_t *arr_size,
166                               const err_msg_t *default_arr,
167                               size_t default_arr_size);
168 
169 
170 /*
171  *   Search an array of messages for a given message number.  The array
172  *   must be sorted by message ID.
173  */
174 const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count,
175                         int msgnum, int verbose);
176 
177 /*
178  *   Format a message with the parameters contained in an exception
179  *   object.  Suports the following format codes:
180  *
181  *   %s - String.  Formats an ERR_TYPE_CHAR, ERR_TYPE_TEXTCHAR, or
182  *   ERR_TYPE_TEXTCHAR_LEN value.
183  *
184  *   %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer.
185  *   Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value.
186  *   Automatically uses the correct size for the argument.
187  *
188  *   %% - Formats as a single percent sign.
189  */
190 void err_format_msg(char *outbuf, size_t outbuflen,
191                     const char *msg, const struct CVmException *exc);
192 
193 /*
194  *   exception ID - this identifies an error
195  */
196 typedef uint err_id_t;
197 
198 /*
199  *   Error parameter type codes
200  */
201 enum err_param_type
202 {
203     /* parameter is a native 'int' value */
204     ERR_TYPE_INT,
205 
206     /* parameter is a native 'unsigned long' value */
207     ERR_TYPE_ULONG,
208 
209     /* parameter is a 'textchar_t *' value (null terminated) */
210     ERR_TYPE_TEXTCHAR,
211 
212     /* parameter is a 'char *' value (null terminated) */
213     ERR_TYPE_CHAR,
214 
215     /*
216      *   parameter is a 'textchar_t *' value followed by a 'size_t' value
217      *   giving the number of bytes in the string
218      */
219     ERR_TYPE_TEXTCHAR_LEN,
220 
221     /* parameter is a 'char *' value with a separate length */
222     ERR_TYPE_CHAR_LEN
223 };
224 
225 /*
226  *   Exception parameter
227  */
228 struct CVmExcParam
229 {
230     /* type of this parameter */
231     err_param_type type_;
232 
233     /* value of the parameter */
234     union
235     {
236         /* as an integer */
237         int intval_;
238 
239         /* as an unsigned long */
240         unsigned long ulong_;
241 
242         /* as a text string */
243         const textchar_t *strval_;
244 
245         /* as a char string */
246         const char *charval_;
247 
248         /* as char string with separate length counter */
249         struct
250         {
251             const char *str_;
252             size_t len_;
253         } charlenval_;
254     } val_;
255 };
256 
257 /*
258  *   Exception object
259  */
260 struct CVmException
261 {
262     /* get the error code */
get_error_codeCVmException263     int get_error_code() const { return error_code_; }
264 
265     /* get the number of parameters */
get_param_countCVmException266     int get_param_count() const { return param_count_; }
267 
268     /* get the type of the nth parameter */
get_param_typeCVmException269     err_param_type get_param_type(int n) const { return params_[n].type_; }
270 
271     /* get the nth parameter as an integer */
get_param_intCVmException272     int get_param_int(int n) const { return params_[n].val_.intval_; }
273 
274     /* get the nth parameter as an unsigned long */
get_param_ulongCVmException275     unsigned long get_param_ulong(int n) const
276         { return params_[n].val_.ulong_; }
277 
278     /* get the nth parameter as a string */
get_param_textCVmException279     const textchar_t *get_param_text(int n) const
280         { return params_[n].val_.strval_; }
281 
282     /* get the nth parameter as a char string */
get_param_charCVmException283     const char *get_param_char(int n) const
284         { return params_[n].val_.charval_; }
285 
286     /* get the nth parameter as a counted-length string */
get_param_char_lenCVmException287     const char *get_param_char_len(int n, size_t *len) const
288     {
289         /* set the length return */
290         *len = params_[n].val_.charlenval_.len_;
291 
292         /* return the string pointer */
293         return params_[n].val_.charlenval_.str_;
294     }
295 
296 
297     /* set a parameter - null-terminated string value */
set_param_strCVmException298     void set_param_str(int n, const char *str)
299     {
300         params_[n].type_ = ERR_TYPE_CHAR;
301         params_[n].val_.charval_ = str;
302     }
303 
304     /* set a parameter - counted-length string value */
set_param_strCVmException305     void set_param_str(int n, const char *str, size_t len)
306     {
307         params_[n].type_ = ERR_TYPE_CHAR_LEN;
308         params_[n].val_.charlenval_.str_ = str;
309         params_[n].val_.charlenval_.len_ = len;
310     }
311 
312     /* set a parameter - integer value */
set_param_intCVmException313     void set_param_int(int n, int val)
314     {
315         params_[n].type_ = ERR_TYPE_INT;
316         params_[n].val_.intval_ = val;
317     }
318 
319     /* previous exception in the exception stack */
320     CVmException *prv_;
321 
322     /* the error code */
323     err_id_t error_code_;
324 
325     /* number of parameters stored in the exception */
326     int param_count_;
327 
328     /* parameters (actual array size is given by param_cnt_) */
329     CVmExcParam params_[1];
330 };
331 
332 
333 /* error states */
334 enum err_state_t
335 {
336     /* no exception */
337     ERR_STATE_OKAY = 0,
338 
339     /* trying */
340     ERR_STATE_TRYING = 1,
341 
342     /* exception in progress, and has not been caught */
343     ERR_STATE_EXCEPTION = 2,
344 
345     /* exception has been caught */
346     ERR_STATE_CAUGHT = 3,
347 
348     /* error thrown while exception handler in progress */
349     ERR_STATE_RETHROWN = 4
350 };
351 
352 /*
353  *   Error frame item - allocated by err_try.  This object is not
354  *   manipulated directly by the client; this is handled automatically by
355  *   the error macros.
356  */
357 struct err_frame_t
358 {
359     /* enclosing error frame */
360     err_frame_t *prv_;
361 
362     /* jmpbuf for this handler */
363     jmp_buf      jmpbuf_;
364 
365     /* current state */
366     err_state_t  state_;
367 
368     /*
369      *   Flag: processing the 'finally' clause.  If an exception is thrown
370      *   while we're processing this, we will not process the 'finally'
371      *   clause again, nor will we execute the 'catch' clause.
372      */
373     int          in_finally_;
374 };
375 
376 /*
377  *   Global error context structure
378  */
379 struct err_context_t
380 {
381     /* current active error frame */
382     err_frame_t *cur_frame_;
383 
384     /* current exception in the exception stack */
385     CVmException *cur_exc_;
386 
387     /* next free byte of parameter stack */
388     char *param_free_;
389 
390     /* size of parameter stack */
391     size_t param_stack_size_;
392 
393     /* parameter stack - actual size given by param_stack_size */
394     char param_stack_[1];
395 };
396 
397 /*
398  *   global static error context
399  */
400 extern err_context_t *G_err;
401 
402 /* reference count for error context */
403 extern int G_err_refs;
404 
405 /*
406  *   Initialize the global error context.  Should be called at program
407  *   initialization.  'param_stack_size' is the size in bytes of the error
408  *   parameter stack; this space is used to store all error parameters in
409  *   err_throw_a() calls.  Generally, only one or two errors are active at
410  *   a given time, so it should be safe to make this three or four times
411  *   sizeof(CVmException) plus sizeof(CVmExcParam) plus the total
412  *   parameter sizes of the largest parameterized exception codes; if
413  *   still in doubt, one or two kbytes should be adequate in any case.
414  */
415 void err_init(size_t param_stack_size);
416 
417 /*
418  *   Delete the global error context.  Should be called at program
419  *   termination.
420  */
421 void err_terminate();
422 
423 /*
424  *   Throw an exception.  The first form takes no parameters except the
425  *   error code; the second form takes a parameter count followed by that
426  *   number of parameters.  Each parameter requires two arguments: the
427  *   first is a type code of type err_param_type, and the second the
428  *   value, whose interpretation depends on the type code.
429  */
430 void err_throw(err_id_t error_code);
431 void err_throw_a(err_id_t error_code, int param_count, ...);
432 
433 /*
434  *   Rethrow the current exception.  This is valid only in 'catch' blocks.
435  */
436 void err_rethrow();
437 
438 /* pop the top exception from the exception stack */
439 void err_pop_exc();
440 
441 /*
442  *   Serious error - abort program
443  */
444 void err_abort(const char *message);
445 
446 /*
447  *   determine the error stack depth
448  */
449 int err_stack_depth();
450 
451 #define err_try \
452     { \
453         err_frame_t err_cur__; \
454         err_cur__.prv_ = G_err->cur_frame_; \
455         err_cur__.in_finally_ = FALSE; \
456         G_err->cur_frame_ = &err_cur__; \
457         if ((err_cur__.state_ = \
458             (err_state_t)setjmp(err_cur__.jmpbuf_)) == 0) \
459         { \
460             err_cur__.state_ = ERR_STATE_TRYING;
461 
462 #define err_catch(exc) \
463         } \
464         if (!err_cur__.in_finally_ && \
465             err_cur__.state_ == ERR_STATE_EXCEPTION) \
466         { \
467             CVmException *exc = G_err->cur_exc_; \
468             err_cur__.state_ = ERR_STATE_CAUGHT;
469 
470 #define err_catch_disc \
471         } \
472         if (!err_cur__.in_finally_ && \
473             err_cur__.state_ == ERR_STATE_EXCEPTION) \
474         { \
475            err_cur__.state_ = ERR_STATE_CAUGHT;
476 
477 
478 #define err_finally \
479         } \
480         if (!err_cur__.in_finally_) \
481         { \
482             err_cur__.in_finally_ = TRUE;
483 
484 #define err_end \
485         } \
486         G_err->cur_frame_ = err_cur__.prv_; \
487         if (err_cur__.state_ == ERR_STATE_EXCEPTION \
488             || err_cur__.state_ == ERR_STATE_RETHROWN) \
489             err_rethrow(); \
490         if (err_cur__.state_ == ERR_STATE_CAUGHT) \
491             err_pop_exc(); \
492     }
493 
494 #endif /* VMERR_H */
495 
496