mc/slang/slerr.c
2009-01-10 20:53:19 +01:00

767 lines
18 KiB
C

/* error handling common to all routines. */
/*
Copyright (C) 2004, 2005, 2006 John E. Davis
This file is part of the S-Lang Library.
The S-Lang Library 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.
The S-Lang Library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include "slinclud.h"
#include "slang.h"
#include "_slang.h"
void (*_pSLinterpreter_Error_Hook) (int);
void (*SLang_VMessage_Hook) (char *, va_list);
void (*SLang_Error_Hook)(char *);
void (*SLang_Exit_Error_Hook)(char *, va_list);
void (*SLang_Dump_Routine)(char *);
volatile int _pSLang_Error = 0;
volatile int SLKeyBoard_Quit = 0;
typedef struct _Exception_Type Exception_Type;
struct _Exception_Type
{
int error_code;
char *name;
char *description;
Exception_Type *subclasses;
Exception_Type *next;
Exception_Type *parent;
};
static Exception_Type *Exception_Root;
static Exception_Type Exception_Root_Buf =
{
-1, "AnyError", "All Errors", NULL, NULL, NULL
};
/* Built-in error codes */
/* These values should correspond to the values produced by _pSLerr_init.
* Some apps may not use the interpreter, and as such _pSLerr_init will not
* get called.
*/
int SL_Any_Error = -1;
int SL_Unknown_Error = 6;
int SL_Internal_Error = 5;
int SL_OS_Error = 1;
int SL_Malloc_Error = 2;
int SL_Import_Error = 7;
int SL_RunTime_Error = 3;
int SL_InvalidParm_Error = 4;
int SL_TypeMismatch_Error = 8;
int SL_UserBreak_Error = 9;
int SL_Stack_Error = 10;
int SL_StackOverflow_Error = 12;
int SL_StackUnderflow_Error = 11;
int SL_ReadOnly_Error = 13;
int SL_VariableUninitialized_Error = 14;
int SL_NumArgs_Error = 15;
int SL_Index_Error = 16;
int SL_Usage_Error = 17;
int SL_Application_Error = 18;
int SL_NotImplemented_Error = 19;
int SL_LimitExceeded_Error = 20;
int SL_Forbidden_Error = 21;
int SL_Math_Error = 22;
int SL_DivideByZero_Error = 23;
int SL_ArithOverflow_Error = 24;
int SL_ArithUnderflow_Error = 25;
int SL_Domain_Error = 26;
int SL_IO_Error = 27;
int SL_Write_Error = 28;
int SL_Read_Error = 29;
int SL_Open_Error = 30;
int SL_Data_Error = 31;
int SL_Unicode_Error = 32;
int SL_InvalidUTF8_Error = 33;
int SL_Namespace_Error = 34;
int SL_Parse_Error = 35;
int SL_Syntax_Error = 36;
int SL_DuplicateDefinition_Error = 37;
int SL_UndefinedName_Error = 38;
typedef struct
{
int *errcode_ptr;
char *name;
char *description;
int *base_class_ptr;
}
BuiltIn_Exception_Table_Type;
static SLCONST BuiltIn_Exception_Table_Type BuiltIn_Exception_Table[] =
{
/* Define MallocError and InvalidParmError ASAP */
{&SL_OS_Error, "OSError", "OS Error", &SL_Any_Error},
{&SL_Malloc_Error, "MallocError", "Not enough memory", &SL_OS_Error},
{&SL_RunTime_Error, "RunTimeError", "Run-Time Error", &SL_Any_Error},
{&SL_InvalidParm_Error, "InvalidParmError", "Invalid Parameter", &SL_RunTime_Error},
{&SL_Internal_Error, "InternalError", "Internal Error", &SL_Any_Error},
{&SL_Unknown_Error, "UnknownError", "Unknown Error", &SL_Any_Error},
/* Rest of OSErrors */
{&SL_Import_Error, "ImportError", "Import Error", &SL_OS_Error},
/* Rest of RunTimeErrors */
{&SL_TypeMismatch_Error, "TypeMismatchError", "Type Mismatch", &SL_RunTime_Error},
{&SL_UserBreak_Error, "UserBreakError", "User Break", &SL_RunTime_Error},
{&SL_Stack_Error, "StackError", "Stack Error", &SL_RunTime_Error},
{&SL_StackUnderflow_Error, "StackUnderflowError", "Stack Underflow Error", &SL_Stack_Error},
{&SL_StackOverflow_Error, "StackOverflowError", "Stack Overflow Error", &SL_Stack_Error},
{&SL_ReadOnly_Error, "ReadOnlyError", "Read-Only Error", &SL_RunTime_Error},
{&SL_VariableUninitialized_Error, "VariableUninitializedError", "Variable Uninitialized Error", &SL_RunTime_Error},
{&SL_NumArgs_Error, "NumArgsError", "Invalid Number of Arguments", &SL_RunTime_Error},
{&SL_Index_Error, "IndexError", "Invalid Index", &SL_RunTime_Error},
{&SL_Usage_Error, "UsageError", "Illegal Usage", &SL_RunTime_Error},
{&SL_Application_Error, "ApplicationError", "Application Error", &SL_RunTime_Error},
{&SL_NotImplemented_Error, "NotImplementedError", "Not Implemented", &SL_RunTime_Error},
{&SL_LimitExceeded_Error, "LimitExceededError", "Limit Exceeded", &SL_RunTime_Error},
{&SL_Forbidden_Error, "ForbiddenError", "Operation Forbidden", &SL_RunTime_Error},
{&SL_Math_Error, "MathError", "Math Error", &SL_RunTime_Error},
{&SL_DivideByZero_Error, "DivideByZeroError", "Divide by Zero", &SL_Math_Error},
{&SL_ArithOverflow_Error, "ArithOverflowError", "Arithmetic Overflow", &SL_Math_Error},
{&SL_ArithUnderflow_Error, "ArithUnderflowError", "Arithmetic Underflow", &SL_Math_Error},
{&SL_Domain_Error, "DomainError", "Domain Error", &SL_Math_Error},
{&SL_IO_Error, "IOError", "I/O Error", &SL_RunTime_Error},
{&SL_Write_Error, "WriteError", "Write failed", &SL_IO_Error},
{&SL_Read_Error, "ReadError", "Read failed", &SL_IO_Error},
{&SL_Open_Error, "OpenError", "Open failed", &SL_IO_Error},
{&SL_Data_Error, "DataError", "Data Error", &SL_RunTime_Error},
{&SL_Unicode_Error, "UnicodeError", "Unicode Error", &SL_RunTime_Error},
{&SL_InvalidUTF8_Error, "UTF8Error", "Invalid UTF8", &SL_Unicode_Error},
{&SL_Namespace_Error, "NamespaceError", "Namespace Error", &SL_RunTime_Error},
/* Parse Errors */
{&SL_Parse_Error, "ParseError", "Parse Error", &SL_Any_Error},
{&SL_Syntax_Error, "SyntaxError", "Syntax Error", &SL_Parse_Error},
{&SL_DuplicateDefinition_Error, "DuplicateDefinitionError", "Duplicate Definition", &SL_Parse_Error},
{&SL_UndefinedName_Error, "UndefinedNameError", "Undefined Name", &SL_Parse_Error},
{NULL, NULL, NULL, NULL}
};
static Exception_Type *find_exception (Exception_Type *root, int error_code)
{
Exception_Type *e;
while (root != NULL)
{
if (error_code == root->error_code)
return root;
if (root->subclasses != NULL)
{
e = find_exception (root->subclasses, error_code);
if (e != NULL)
return e;
}
root = root->next;
}
return root;
}
static int is_exception_ancestor (int a, int b)
{
Exception_Type *e;
if (a == b)
return 1;
if (NULL == (e = find_exception (Exception_Root, a)))
return 0;
while (e->parent != NULL)
{
e = e->parent;
if (e->error_code == b)
return 1;
}
return 0;
}
int SLerr_exception_eqs (int a, int b)
{
if (is_exception_ancestor (a, b))
return 1;
return 0;
}
static void free_this_exception (Exception_Type *e)
{
if (e == NULL)
return;
if (e->name != NULL)
SLang_free_slstring (e->name);
if (e->description != NULL)
SLang_free_slstring (e->description);
SLfree ((char *)e);
}
static int Next_Exception_Code;
/* The whole point of this nonsense involving the _pSLerr_New_Exception_Hook
* is to provide a mechanism to avoid linking in the interpreter for apps
* that just want the other facilities.
*/
int (*_pSLerr_New_Exception_Hook)(char *name, char *desc, int error_code);
int _pSLerr_init_interp_exceptions (void)
{
SLCONST BuiltIn_Exception_Table_Type *b;
Exception_Type *e;
if (_pSLerr_New_Exception_Hook == NULL)
return 0;
e = &Exception_Root_Buf;
if (-1 == (*_pSLerr_New_Exception_Hook)(e->name, e->description, e->error_code))
return -1;
b = BuiltIn_Exception_Table;
while (b->errcode_ptr != NULL)
{
if (-1 == (*_pSLerr_New_Exception_Hook)(b->name, b->description, *b->errcode_ptr))
return -1;
b++;
}
return 0;
}
int SLerr_new_exception (int baseclass, char *name, char *descript)
{
Exception_Type *base;
Exception_Type *e;
if (-1 == _pSLerr_init ())
return -1;
base = find_exception (Exception_Root, baseclass);
if (base == NULL)
{
SLang_verror (SL_InvalidParm_Error,
"Base class for new exception not found");
return -1;
}
e = (Exception_Type *) SLcalloc (1, sizeof (Exception_Type));
if (e == NULL)
return -1;
if ((NULL == (e->name = SLang_create_slstring (name)))
|| (NULL == (e->description = SLang_create_slstring (descript))))
{
free_this_exception (e);
return -1;
}
e->error_code = Next_Exception_Code;
if ((_pSLerr_New_Exception_Hook != NULL)
&& (-1 == (*_pSLerr_New_Exception_Hook) (e->name, e->description, e->error_code)))
{
free_this_exception (e);
return -1;
}
e->parent = base;
e->next = base->subclasses;
base->subclasses = e;
Next_Exception_Code++;
return e->error_code;
}
static int init_exceptions (void)
{
SLCONST BuiltIn_Exception_Table_Type *b;
if (Exception_Root != NULL)
return 0;
Exception_Root = &Exception_Root_Buf;
Next_Exception_Code = 1;
b = BuiltIn_Exception_Table;
while (b->errcode_ptr != NULL)
{
int err_code;
err_code = SLerr_new_exception (*b->base_class_ptr, b->name, b->description);
if (err_code == -1)
return -1;
*b->errcode_ptr = err_code;
b++;
}
return 0;
}
static void free_exceptions (Exception_Type *root)
{
while (root != NULL)
{
Exception_Type *next;
if (root->subclasses != NULL)
free_exceptions (root->subclasses);
next = root->next;
free_this_exception (root);
root = next;
}
}
static void deinit_exceptions (void)
{
Exception_Type *root = Exception_Root;
if (root != NULL)
free_exceptions (root->subclasses);
Exception_Root = NULL;
Next_Exception_Code = 0;
}
char *SLerr_strerror (int err_code)
{
Exception_Type *e;
if (err_code == 0)
err_code = _pSLang_Error;
if (-1 == _pSLerr_init ())
return "Unable to initialize SLerr module";
if (NULL == (e = find_exception (Exception_Root, err_code)))
return "Invalid/Unknown Error Code";
return e->description;
}
/* Error Queue Functions
* SLang_verror (int errcode, fmt, args)
* Add an error message to the queue.
* SLerr_delete_queue ()
* Removes messages from the error queue
* SLerr_print_queue ()
* Prints all messages from the queue, deletes the queue
*/
typedef struct _Error_Message_Type
{
char *msg; /* SLstring, may be NULL */
int msg_type;
#define _SLERR_MSG_ERROR 1
#define _SLERR_MSG_WARNING 2
#define _SLERR_MSG_TRACEBACK 4
struct _Error_Message_Type *next;
}
Error_Message_Type;
typedef struct
{
Error_Message_Type *head;
Error_Message_Type *tail;
}
Error_Queue_Type;
static Error_Queue_Type *Default_Error_Queue;
static void free_error_msg (Error_Message_Type *m)
{
if (m == NULL)
return;
if (m->msg != NULL)
SLang_free_slstring (m->msg);
SLfree ((char *)m);
}
static Error_Message_Type *allocate_error_msg (char *msg, int msg_type)
{
Error_Message_Type *m;
if (NULL == (m = (Error_Message_Type*) SLcalloc (1, sizeof (Error_Message_Type))))
return NULL;
if ((NULL != msg) && (NULL == (m->msg = SLang_create_slstring (msg))))
{
free_error_msg (m);
return NULL;
}
m->msg_type = msg_type;
return m;
}
static void free_queued_messages (Error_Queue_Type *q)
{
Error_Message_Type *m;
if (q == NULL)
return;
m = q->head;
while (m != NULL)
{
Error_Message_Type *m1 = m->next;
free_error_msg (m);
m = m1;
}
q->head = NULL;
q->tail = NULL;
}
static void delete_msg_queue (Error_Queue_Type *q)
{
if (q == NULL)
return;
free_queued_messages (q);
SLfree ((char *)q);
}
static Error_Queue_Type *create_msg_queue (void)
{
Error_Queue_Type *q;
if (NULL == (q = (Error_Queue_Type *)SLcalloc (1, sizeof(Error_Queue_Type))))
return NULL;
return q;
}
static int queue_message (Error_Queue_Type *q, char *msg, int msg_type)
{
Error_Message_Type *m;
if (NULL == (m = allocate_error_msg (msg, msg_type)))
return -1;
if (q->tail != NULL)
q->tail->next = m;
if (q->head == NULL)
q->head = m;
q->tail = m;
return 0;
}
static void print_error (int msg_type, char *err)
{
unsigned int len;
switch (msg_type)
{
case _SLERR_MSG_ERROR:
if (SLang_Error_Hook != NULL)
{
(*SLang_Error_Hook)(err);
return;
}
break;
case _SLERR_MSG_TRACEBACK:
case _SLERR_MSG_WARNING:
if (SLang_Dump_Routine != NULL)
{
(*SLang_Dump_Routine)(err);
return;
}
break;
}
len = strlen (err);
if (len == 0)
return;
fputs (err, stderr);
if ((err[len-1] != '\n')
&& (msg_type != _SLERR_MSG_TRACEBACK))
fputs("\n", stderr);
fflush (stderr);
}
static void print_queue (void)
{
if (-1 == _pSLerr_init ())
print_error (_SLERR_MSG_ERROR, "Unable to initialize SLerr module");
if (_pSLang_Error == 0)
return;
if (Default_Error_Queue != NULL)
{
Error_Queue_Type *q = Default_Error_Queue;
Error_Message_Type *m = q->head;
while (m != NULL)
{
Error_Message_Type *m_next = m->next;
if (m->msg != NULL)
print_error (m->msg_type, m->msg);
m = m_next;
}
free_queued_messages (q);
}
#if 0
if (_pSLang_Error != SL_Usage_Error)
{
print_error (_SLERR_MSG_ERROR, SLerr_strerror (_pSLang_Error));
}
#endif
}
/* This function returns a pointer to the first error message in the queue.
* Make no attempts to free the returned pointer.
*/
char *_pSLerr_get_error_from_queue (void)
{
Error_Queue_Type *q;
Error_Message_Type *m;
unsigned int len;
char *err, *err1, *err_max;
if (NULL == (q = Default_Error_Queue))
return NULL;
len = 0;
m = q->head;
while (m != NULL)
{
if (m->msg_type == _SLERR_MSG_ERROR)
len += 1 + strlen (m->msg);
m = m->next;
}
if (len)
len--; /* last \n not needed */
if (NULL == (err = _pSLallocate_slstring (len)))
return NULL;
err_max = err + len;
err1 = err;
m = q->head;
while (m != NULL)
{
if (m->msg_type == _SLERR_MSG_ERROR)
{
unsigned int dlen = strlen (m->msg);
strcpy (err1, m->msg);
err1 += dlen;
if (err1 != err_max)
*err1++ = '\n';
}
m = m->next;
}
*err1 = 0;
return _pSLcreate_via_alloced_slstring (err, len);
}
void _pSLerr_print_message_queue (void)
{
print_queue ();
}
static volatile int Suspend_Error_Messages = 0;
int _pSLerr_resume_messages (void)
{
if (Suspend_Error_Messages == 0)
return 0;
Suspend_Error_Messages--;
if (Suspend_Error_Messages == 0)
print_queue ();
return 0;
}
int _pSLerr_suspend_messages (void)
{
Suspend_Error_Messages++;
return 0;
}
void _pSLerr_free_queued_messages (void)
{
free_queued_messages (Default_Error_Queue);
}
void SLang_verror (int err_code, char *fmt, ...)
{
va_list ap;
char err [4096];
if (-1 == _pSLerr_init ())
{
print_queue ();
return;
}
if (err_code == 0)
err_code = SL_INTRINSIC_ERROR;
if (_pSLang_Error == 0)
SLang_set_error (err_code);
if (fmt == NULL)
return;
va_start(ap, fmt);
(void) SLvsnprintf (err, sizeof (err), fmt, ap);
va_end(ap);
if (Suspend_Error_Messages)
(void) queue_message (Default_Error_Queue, err, _SLERR_MSG_ERROR);
else
print_error (_SLERR_MSG_ERROR, err);
}
int _pSLerr_traceback_msg (char *fmt, ...)
{
va_list ap;
char msg [4096];
va_start(ap, fmt);
(void) SLvsnprintf (msg, sizeof (msg), fmt, ap);
va_end(ap);
return queue_message (Default_Error_Queue, msg, _SLERR_MSG_TRACEBACK);
}
void SLang_exit_error (char *fmt, ...)
{
va_list ap;
print_queue ();
va_start (ap, fmt);
if (SLang_Exit_Error_Hook != NULL)
{
(*SLang_Exit_Error_Hook) (fmt, ap);
exit (1);
}
if (fmt != NULL)
{
vfprintf (stderr, fmt, ap);
fputs ("\n", stderr);
fflush (stderr);
}
va_end (ap);
exit (1);
}
int SLang_set_error (int error)
{
/* Only allow an error to be cleared (error==0), but not changed
* if there already is an error.
*/
if ((error == 0)
|| (_pSLang_Error == 0))
_pSLang_Error = error;
if (_pSLinterpreter_Error_Hook != NULL)
(*_pSLinterpreter_Error_Hook) (_pSLang_Error);
return 0;
}
int SLang_get_error (void)
{
return _pSLang_Error;
}
void SLang_vmessage (char *fmt, ...)
{
va_list ap;
if (fmt == NULL)
return;
va_start (ap, fmt);
if (SLang_VMessage_Hook != NULL)
(*SLang_VMessage_Hook) (fmt, ap);
else
{
vfprintf (stdout, fmt, ap);
fputs ("\n", stdout);
}
va_end (ap);
}
/* This routine does not queue messages. It is used for tracing, etc. */
void _pSLerr_dump_msg (char *fmt, ...)
{
char buf[1024];
va_list ap;
va_start (ap, fmt);
if (SLang_Dump_Routine != NULL)
{
(void) SLvsnprintf (buf, sizeof (buf), fmt, ap);
(*SLang_Dump_Routine) (buf);
}
else
{
vfprintf (stderr, fmt, ap);
fflush (stderr);
}
va_end (ap);
}
int _pSLerr_init (void)
{
if (Default_Error_Queue == NULL)
{
Suspend_Error_Messages = 0;
if (NULL == (Default_Error_Queue = create_msg_queue ()))
return -1;
}
if (-1 == init_exceptions ())
return -1;
return 0;
}
void _pSLerr_deinit (void)
{
deinit_exceptions ();
delete_msg_queue (Default_Error_Queue);
Suspend_Error_Messages = 0;
Default_Error_Queue = NULL;
}