/*
* Copyright (c) 2002-2007 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Routines related to error handling.
*/
#include
#include
#include
#include
#ifdef AG_THREADS
AG_ThreadKey agErrorKey; /* Error message (thread-specific) */
AG_ThreadKey agErrorCode; /* Error code (thread-specific) */
#else
char *agErrorKey; /* Error message */
AG_ErrorCode agErrorCode; /* Error code */
#endif
int agDebugLvl = 1; /* Default debug level */
/* Error callback for AG_FatalError() */
static void (*agErrorCallback)(const char *) = NULL;
/* Initialize the error facility. */
void
AG_InitError(void)
{
#ifdef AG_THREADS
AG_ThreadKeyCreate(&agErrorKey);
AG_ThreadKeyCreate(&agErrorCode);
#else
agErrorKey = NULL;
agErrorCode = AG_EUNDEFINED;
#endif
}
/* Destroy the error facility. */
void
AG_DestroyError(void)
{
#ifdef AG_THREADS
#if 0
/* XXX uninitialized warnings */
AG_ThreadKeyDelete(agErrorKey);
#endif
#else
Free(agErrorKey);
#endif
}
/* Set the error message string. */
void
AG_SetError(const char *fmt, ...)
{
va_list args;
char *buf;
va_start(args, fmt);
Vasprintf(&buf, fmt, args);
va_end(args);
#ifdef AG_THREADS
{
char *ekey;
if ((ekey = (char *)AG_ThreadKeyGet(agErrorKey)) != NULL) {
Free(ekey);
}
AG_ThreadKeySet(agErrorKey, buf);
}
#else
Free(agErrorKey);
agErrorKey = buf;
#endif
}
/* Retrieve the error message string. */
const char *
AG_GetError(void)
{
#ifdef AG_THREADS
return ((const char *)AG_ThreadKeyGet(agErrorKey));
#else
return ((const char *)agErrorKey);
#endif
}
/* Set the symbolic error code. */
void
AG_SetErrorCode(AG_ErrorCode code)
{
#ifdef AG_THREADS
AG_ThreadKeySet(agErrorCode, (void *)code);
#else
agErrorCode = code;
#endif
}
/* Retrieve the symbolic error code. */
AG_ErrorCode
AG_GetErrorCode(void)
{
#ifdef AG_THREADS
return ((AG_ErrorCode)AG_ThreadKeyGet(agErrorCode));
#else
return agErrorCode;
#endif
}
/* Issue a debug message. */
void
AG_Debug(void *p, const char *fmt, ...)
{
#ifdef AG_DEBUG
AG_Object *obj = p;
va_list args;
if (obj != NULL && obj->debugFn != NULL) {
char *buf, *p;
va_start(args, fmt);
Vasprintf(&buf, fmt, args);
p = &buf[strlen(buf)-1];
if (*p == '\n') { *p = '\0'; }
obj->debugFn(obj, obj->debugPtr, buf);
Free(buf);
va_end(args);
} else {
if (agDebugLvl >= 1 || (obj != NULL && OBJECT_DEBUG(obj))) {
va_start(args, fmt);
if (obj != NULL) {
if (OBJECT(obj)->name[0] != '\0') {
printf("%s: ", OBJECT(obj)->name);
} else {
printf("<%p>: ", obj);
}
}
vprintf(fmt, args);
va_end(args);
}
}
#endif /* AG_DEBUG */
}
/* Issue a verbose message. */
void
AG_Verbose(const char *fmt, ...)
{
va_list args;
if (!agVerbose)
return;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
/* Raise a fatal error condition. */
void
AG_FatalError(const char *fmt, ...)
{
va_list args;
char *buf;
/* Use callback if defined. The callback must gracefully exit. */
if (agErrorCallback != NULL) {
if (fmt != NULL) {
va_start(args, fmt);
Vasprintf(&buf, fmt, args);
va_end(args);
agErrorCallback(buf);
abort(); /* not reached */
} else {
agErrorCallback(AG_GetError());
abort(); /* not reached */
}
} else {
fprintf(stderr, "Fatal error: ");
if (fmt != NULL) {
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
} else {
fprintf(stderr, "%s", AG_GetError());
}
fprintf(stderr, "\n");
abort();
}
}
void
AG_SetFatalCallback(void (*callback)(const char *))
{
agErrorCallback = callback;
}
/*
* Raise fatal error condition due to a type access mismatch in an event
* handler routine.
*/
void *
AG_PtrMismatch(void)
{
AG_FatalError("AG_PTR mismatch");
return (NULL);
}
int
AG_IntMismatch(void)
{
AG_FatalError("AG_INT mismatch");
return (0);
}
float
AG_FloatMismatch(void)
{
AG_FatalError("AG_FLOAT mismatch");
return (0.0);
}
void *
AG_ObjectMismatch(const char *t1, const char *t2)
{
AG_FatalError("Object type mismatch (%s != %s)", t1, t2);
return (NULL);
}