| /* |
| * Copyright 2001-2004 Brandon Long |
| * All Rights Reserved. |
| * |
| * ClearSilver Templating System |
| * |
| * This code is made available under the terms of the ClearSilver License. |
| * http://www.clearsilver.net/license.hdf |
| * |
| */ |
| |
| #include "cs_config.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #include "neo_misc.h" |
| #include "neo_err.h" |
| #include "ulist.h" |
| #include "ulocks.h" |
| |
| int NERR_PASS = -1; |
| int NERR_ASSERT = 0; |
| int NERR_NOT_FOUND = 0; |
| int NERR_DUPLICATE = 0; |
| int NERR_NOMEM = 0; |
| int NERR_PARSE = 0; |
| int NERR_OUTOFRANGE = 0; |
| int NERR_SYSTEM = 0; |
| int NERR_IO = 0; |
| int NERR_LOCK = 0; |
| int NERR_DB = 0; |
| int NERR_EXISTS = 0; |
| |
| static NEOERR *FreeList = NULL; |
| static ULIST *Errors = NULL; |
| static int Inited = 0; |
| #ifdef HAVE_PTHREADS |
| /* In multi-threaded environments, we have to init thread safely */ |
| static pthread_mutex_t InitLock = PTHREAD_MUTEX_INITIALIZER; |
| #endif |
| |
| /* Set this to 1 to enable non-thread safe re-use of NEOERR data |
| * structures. This was a premature performance optimization that isn't |
| * thread safe, if we want it thread safe we need to add mutex code... |
| * which has its own performance penalties... |
| */ |
| static int UseFreeList = 0; |
| |
| static NEOERR *_err_alloc(void) |
| { |
| NEOERR *err; |
| |
| if (!UseFreeList || FreeList == NULL) |
| { |
| err = (NEOERR *)calloc (1, sizeof (NEOERR)); |
| if (err == NULL) |
| { |
| ne_warn ("INTERNAL ERROR: Unable to allocate memory for NEOERR"); |
| return INTERNAL_ERR; |
| } |
| return err; |
| } |
| else |
| { |
| err = FreeList; |
| FreeList = FreeList->next; |
| } |
| err->flags |= NE_IN_USE; |
| err->next = NULL; |
| return err; |
| } |
| |
| static int _err_free (NEOERR *err) |
| { |
| if (err == NULL || err == INTERNAL_ERR) |
| return 0; |
| if (err->next != NULL) |
| _err_free(err->next); |
| if (UseFreeList) |
| { |
| err->next = FreeList; |
| FreeList = err; |
| err->flags = 0; |
| err->desc[0] = '\0'; |
| } |
| else |
| { |
| free(err); |
| } |
| return 0; |
| } |
| |
| NEOERR *nerr_raisef (const char *func, const char *file, int lineno, int error, |
| const char *fmt, ...) |
| { |
| NEOERR *err; |
| va_list ap; |
| |
| err = _err_alloc(); |
| if (err == INTERNAL_ERR) |
| return err; |
| |
| va_start(ap, fmt); |
| vsnprintf (err->desc, sizeof(err->desc), fmt, ap); |
| va_end(ap); |
| |
| err->error = error; |
| err->func = func; |
| err->file = file; |
| err->lineno = lineno; |
| |
| return err; |
| } |
| |
| NEOERR *nerr_raise_errnof (const char *func, const char *file, int lineno, |
| int error, const char *fmt, ...) |
| { |
| NEOERR *err; |
| va_list ap; |
| int l; |
| |
| err = _err_alloc(); |
| if (err == INTERNAL_ERR) |
| return err; |
| |
| va_start(ap, fmt); |
| vsnprintf (err->desc, sizeof(err->desc), fmt, ap); |
| va_end(ap); |
| |
| l = strlen(err->desc); |
| snprintf (err->desc + l, sizeof(err->desc)-l, ": [%d] %s", errno, |
| strerror (errno)); |
| |
| err->error = error; |
| err->func = func; |
| err->file = file; |
| err->lineno = lineno; |
| |
| return err; |
| } |
| |
| NEOERR *nerr_passf (const char *func, const char *file, int lineno, NEOERR *err) |
| { |
| NEOERR *nerr; |
| |
| if (err == STATUS_OK) |
| return err; |
| |
| nerr = _err_alloc(); |
| if (nerr == INTERNAL_ERR) |
| return err; |
| |
| nerr->error = NERR_PASS; |
| nerr->func = func; |
| nerr->file = file; |
| nerr->lineno = lineno; |
| nerr->next = err; |
| |
| return nerr; |
| } |
| |
| NEOERR *nerr_pass_ctxf (const char *func, const char *file, int lineno, |
| NEOERR *err, const char *fmt, ...) |
| { |
| NEOERR *nerr; |
| va_list ap; |
| |
| if (err == STATUS_OK) |
| return err; |
| |
| nerr = _err_alloc(); |
| if (nerr == INTERNAL_ERR) |
| return err; |
| |
| va_start(ap, fmt); |
| vsnprintf (nerr->desc, sizeof(nerr->desc), fmt, ap); |
| va_end(ap); |
| |
| nerr->error = NERR_PASS; |
| nerr->func = func; |
| nerr->file = file; |
| nerr->lineno = lineno; |
| nerr->next = err; |
| |
| return nerr; |
| } |
| |
| /* In the future, we'll allow someone to register an error handler */ |
| void nerr_log_error (NEOERR *err) |
| { |
| NEOERR *more; |
| char buf[1024]; |
| char *err_name; |
| |
| if (err == STATUS_OK) |
| return; |
| |
| if (err == INTERNAL_ERR) |
| { |
| ne_warn ("Internal error"); |
| return; |
| } |
| |
| more = err; |
| fprintf (stderr, "Traceback (innermost last):\n"); |
| while (more && more != INTERNAL_ERR) |
| { |
| err = more; |
| more = err->next; |
| if (err->error != NERR_PASS) |
| { |
| NEOERR *r; |
| if (err->error == 0) |
| { |
| err_name = buf; |
| snprintf (buf, sizeof (buf), "Unknown Error"); |
| } |
| else |
| { |
| r = uListGet (Errors, err->error - 1, (void *)&err_name); |
| if (r != STATUS_OK) |
| { |
| err_name = buf; |
| snprintf (buf, sizeof (buf), "Error %d", err->error); |
| } |
| } |
| |
| fprintf (stderr, " File \"%s\", line %d, in %s()\n%s: %s\n", err->file, |
| err->lineno, err->func, err_name, err->desc); |
| } |
| else |
| { |
| fprintf (stderr, " File \"%s\", line %d, in %s()\n", err->file, |
| err->lineno, err->func); |
| if (err->desc[0]) |
| { |
| fprintf (stderr, " %s\n", err->desc); |
| } |
| } |
| } |
| } |
| |
| void nerr_error_string (NEOERR *err, STRING *str) |
| { |
| NEOERR *more; |
| char buf[1024]; |
| char *err_name; |
| |
| if (err == STATUS_OK) |
| return; |
| |
| if (err == INTERNAL_ERR) |
| { |
| string_append (str, "Internal error"); |
| return; |
| } |
| |
| more = err; |
| while (more && more != INTERNAL_ERR) |
| { |
| err = more; |
| more = err->next; |
| if (err->error != NERR_PASS) |
| { |
| NEOERR *r; |
| if (err->error == 0) |
| { |
| err_name = buf; |
| snprintf (buf, sizeof (buf), "Unknown Error"); |
| } |
| else |
| { |
| r = uListGet (Errors, err->error - 1, (void *)&err_name); |
| if (r != STATUS_OK) |
| { |
| err_name = buf; |
| snprintf (buf, sizeof (buf), "Error %d", err->error); |
| } |
| } |
| |
| string_appendf(str, "%s: %s", err_name, err->desc); |
| return; |
| } |
| } |
| } |
| |
| void nerr_error_traceback (NEOERR *err, STRING *str) |
| { |
| NEOERR *more; |
| char buf[1024]; |
| char buf2[1024]; |
| char *err_name; |
| |
| if (err == STATUS_OK) |
| return; |
| |
| if (err == INTERNAL_ERR) |
| { |
| string_append (str, "Internal error"); |
| return; |
| } |
| |
| more = err; |
| string_append (str, "Traceback (innermost last):\n"); |
| while (more && more != INTERNAL_ERR) |
| { |
| err = more; |
| more = err->next; |
| if (err->error != NERR_PASS) |
| { |
| NEOERR *r; |
| if (err->error == 0) |
| { |
| err_name = buf; |
| snprintf (buf, sizeof (buf), "Unknown Error"); |
| } |
| else |
| { |
| r = uListGet (Errors, err->error - 1, (void *)&err_name); |
| if (r != STATUS_OK) |
| { |
| err_name = buf; |
| snprintf (buf, sizeof (buf), "Error %d", err->error); |
| } |
| } |
| |
| snprintf (buf2, sizeof(buf2), |
| " File \"%s\", line %d, in %s()\n%s: %s\n", err->file, |
| err->lineno, err->func, err_name, err->desc); |
| string_append(str, buf2); |
| } |
| else |
| { |
| snprintf (buf2, sizeof(buf2), " File \"%s\", line %d, in %s()\n", |
| err->file, err->lineno, err->func); |
| string_append(str, buf2); |
| if (err->desc[0]) |
| { |
| snprintf (buf2, sizeof(buf2), " %s\n", err->desc); |
| string_append(str, buf2); |
| } |
| } |
| } |
| } |
| |
| void nerr_ignore (NEOERR **err) |
| { |
| _err_free (*err); |
| *err = STATUS_OK; |
| } |
| |
| int nerr_handle (NEOERR **err, int etype) |
| { |
| NEOERR *walk = *err; |
| |
| while (walk != STATUS_OK && walk != INTERNAL_ERR) |
| { |
| |
| if (walk->error == etype) |
| { |
| _err_free(*err); |
| *err = STATUS_OK; |
| return 1; |
| } |
| walk = walk->next; |
| } |
| |
| if (walk == STATUS_OK && etype == STATUS_OK_INT) |
| return 1; |
| if (walk == STATUS_OK) |
| return 0; |
| |
| if (walk == INTERNAL_ERR && etype == INTERNAL_ERR_INT) |
| { |
| *err = STATUS_OK; |
| return 1; |
| } |
| if (walk == INTERNAL_ERR) |
| return 0; |
| |
| return 0; |
| } |
| |
| int nerr_match (NEOERR *err, int etype) |
| { |
| while (err != STATUS_OK && err != INTERNAL_ERR) |
| { |
| |
| if (err->error == etype) |
| return 1; |
| err = err->next; |
| } |
| |
| if (err == STATUS_OK && etype == STATUS_OK_INT) |
| return 1; |
| if (err == STATUS_OK) |
| return 0; |
| |
| if (err == INTERNAL_ERR && etype == INTERNAL_ERR_INT) |
| return 1; |
| if (err == INTERNAL_ERR) |
| return 0; |
| |
| return 0; |
| } |
| |
| NEOERR *nerr_register (int *val, const char *name) |
| { |
| NEOERR *err; |
| |
| err = uListAppend (Errors, (void *) name); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| *val = uListLength(Errors); |
| return STATUS_OK; |
| } |
| |
| NEOERR *nerr_init (void) |
| { |
| NEOERR *err; |
| |
| if (Inited == 0) |
| { |
| #ifdef HAVE_PTHREADS |
| /* In threaded environments, we have to mutex lock to do this init, but |
| * we don't want to use a mutex every time to check that it was Inited. |
| * So, we only lock if our first test of Inited was false */ |
| err = mLock(&InitLock); |
| if (err != STATUS_OK) return nerr_pass(err); |
| if (Inited == 0) { |
| #endif |
| err = uListInit (&Errors, 10, 0); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| err = nerr_register (&NERR_PASS, "InternalPass"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_ASSERT, "AssertError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_NOT_FOUND, "NotFoundError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_DUPLICATE, "DuplicateError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_NOMEM, "MemoryError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_PARSE, "ParseError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_OUTOFRANGE, "RangeError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_SYSTEM, "SystemError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_IO, "IOError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_LOCK, "LockError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_DB, "DBError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| err = nerr_register (&NERR_EXISTS, "ExistsError"); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| Inited = 1; |
| #ifdef HAVE_PTHREADS |
| } |
| err = mUnlock(&InitLock); |
| if (err != STATUS_OK) return nerr_pass(err); |
| #endif |
| } |
| return STATUS_OK; |
| } |