Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright 2001-2004 Brandon Long
      3  * All Rights Reserved.
      4  *
      5  * ClearSilver Templating System
      6  *
      7  * This code is made available under the terms of the ClearSilver License.
      8  * http://www.clearsilver.net/license.hdf
      9  *
     10  */
     11 
     12 #include "cs_config.h"
     13 
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <stdarg.h>
     17 #include <errno.h>
     18 #include <string.h>
     19 
     20 #include "neo_misc.h"
     21 #include "neo_err.h"
     22 #include "ulist.h"
     23 #include "ulocks.h"
     24 
     25 int NERR_PASS = -1;
     26 int NERR_ASSERT = 0;
     27 int NERR_NOT_FOUND = 0;
     28 int NERR_DUPLICATE = 0;
     29 int NERR_NOMEM = 0;
     30 int NERR_PARSE = 0;
     31 int NERR_OUTOFRANGE = 0;
     32 int NERR_SYSTEM = 0;
     33 int NERR_IO = 0;
     34 int NERR_LOCK = 0;
     35 int NERR_DB = 0;
     36 int NERR_EXISTS = 0;
     37 
     38 static NEOERR *FreeList = NULL;
     39 static ULIST *Errors = NULL;
     40 static int Inited = 0;
     41 #ifdef HAVE_PTHREADS
     42 /* In multi-threaded environments, we have to init thread safely */
     43 static pthread_mutex_t InitLock = PTHREAD_MUTEX_INITIALIZER;
     44 #endif
     45 
     46 /* Set this to 1 to enable non-thread safe re-use of NEOERR data
     47  * structures.  This was a premature performance optimization that isn't
     48  * thread safe, if we want it thread safe we need to add mutex code...
     49  * which has its own performance penalties...
     50  */
     51 static int UseFreeList = 0;
     52 
     53 static NEOERR *_err_alloc(void)
     54 {
     55   NEOERR *err;
     56 
     57   if (!UseFreeList || FreeList == NULL)
     58   {
     59     err = (NEOERR *)calloc (1, sizeof (NEOERR));
     60     if (err == NULL)
     61     {
     62       ne_warn ("INTERNAL ERROR: Unable to allocate memory for NEOERR");
     63       return INTERNAL_ERR;
     64     }
     65     return err;
     66   }
     67   else
     68   {
     69     err = FreeList;
     70     FreeList = FreeList->next;
     71   }
     72   err->flags |= NE_IN_USE;
     73   err->next = NULL;
     74   return err;
     75 }
     76 
     77 static int _err_free (NEOERR *err)
     78 {
     79   if (err == NULL || err == INTERNAL_ERR)
     80     return 0;
     81   if (err->next != NULL)
     82     _err_free(err->next);
     83   if (UseFreeList)
     84   {
     85     err->next = FreeList;
     86     FreeList = err;
     87     err->flags = 0;
     88     err->desc[0] = '\0';
     89   }
     90   else
     91   {
     92     free(err);
     93   }
     94   return 0;
     95 }
     96 
     97 NEOERR *nerr_raisef (const char *func, const char *file, int lineno, int error,
     98                     const char *fmt, ...)
     99 {
    100   NEOERR *err;
    101   va_list ap;
    102 
    103   err = _err_alloc();
    104   if (err == INTERNAL_ERR)
    105     return err;
    106 
    107   va_start(ap, fmt);
    108   vsnprintf (err->desc, sizeof(err->desc), fmt, ap);
    109   va_end(ap);
    110 
    111   err->error = error;
    112   err->func = func;
    113   err->file = file;
    114   err->lineno = lineno;
    115 
    116   return err;
    117 }
    118 
    119 NEOERR *nerr_raise_errnof (const char *func, const char *file, int lineno,
    120     			   int error, const char *fmt, ...)
    121 {
    122   NEOERR *err;
    123   va_list ap;
    124   int l;
    125 
    126   err = _err_alloc();
    127   if (err == INTERNAL_ERR)
    128     return err;
    129 
    130   va_start(ap, fmt);
    131   vsnprintf (err->desc, sizeof(err->desc), fmt, ap);
    132   va_end(ap);
    133 
    134   l = strlen(err->desc);
    135   snprintf (err->desc + l, sizeof(err->desc)-l, ": [%d] %s", errno,
    136       strerror (errno));
    137 
    138   err->error = error;
    139   err->func = func;
    140   err->file = file;
    141   err->lineno = lineno;
    142 
    143   return err;
    144 }
    145 
    146 NEOERR *nerr_passf (const char *func, const char *file, int lineno, NEOERR *err)
    147 {
    148   NEOERR *nerr;
    149 
    150   if (err == STATUS_OK)
    151     return err;
    152 
    153   nerr = _err_alloc();
    154   if (nerr == INTERNAL_ERR)
    155     return err;
    156 
    157   nerr->error = NERR_PASS;
    158   nerr->func = func;
    159   nerr->file = file;
    160   nerr->lineno = lineno;
    161   nerr->next = err;
    162 
    163   return nerr;
    164 }
    165 
    166 NEOERR *nerr_pass_ctxf (const char *func, const char *file, int lineno,
    167 			NEOERR *err, const char *fmt, ...)
    168 {
    169   NEOERR *nerr;
    170   va_list ap;
    171 
    172   if (err == STATUS_OK)
    173     return err;
    174 
    175   nerr = _err_alloc();
    176   if (nerr == INTERNAL_ERR)
    177     return err;
    178 
    179   va_start(ap, fmt);
    180   vsnprintf (nerr->desc, sizeof(nerr->desc), fmt, ap);
    181   va_end(ap);
    182 
    183   nerr->error = NERR_PASS;
    184   nerr->func = func;
    185   nerr->file = file;
    186   nerr->lineno = lineno;
    187   nerr->next = err;
    188 
    189   return nerr;
    190 }
    191 
    192 /* In the future, we'll allow someone to register an error handler */
    193 void nerr_log_error (NEOERR *err)
    194 {
    195   NEOERR *more;
    196   char buf[1024];
    197   char *err_name;
    198 
    199   if (err == STATUS_OK)
    200     return;
    201 
    202   if (err == INTERNAL_ERR)
    203   {
    204     ne_warn ("Internal error");
    205     return;
    206   }
    207 
    208   more = err;
    209   fprintf (stderr, "Traceback (innermost last):\n");
    210   while (more && more != INTERNAL_ERR)
    211   {
    212     err = more;
    213     more = err->next;
    214     if (err->error != NERR_PASS)
    215     {
    216       NEOERR *r;
    217       if (err->error == 0)
    218       {
    219 	err_name = buf;
    220 	snprintf (buf, sizeof (buf), "Unknown Error");
    221       }
    222       else
    223       {
    224 	r = uListGet (Errors, err->error - 1, (void *)&err_name);
    225 	if (r != STATUS_OK)
    226 	{
    227 	  err_name = buf;
    228 	  snprintf (buf, sizeof (buf), "Error %d", err->error);
    229 	}
    230       }
    231 
    232       fprintf (stderr, "  File \"%s\", line %d, in %s()\n%s: %s\n", err->file,
    233 	  err->lineno, err->func, err_name, err->desc);
    234     }
    235     else
    236     {
    237       fprintf (stderr, "  File \"%s\", line %d, in %s()\n", err->file,
    238 	  err->lineno, err->func);
    239       if (err->desc[0])
    240       {
    241 	fprintf (stderr, "    %s\n", err->desc);
    242       }
    243     }
    244   }
    245 }
    246 
    247 void nerr_error_string (NEOERR *err, STRING *str)
    248 {
    249   NEOERR *more;
    250   char buf[1024];
    251   char *err_name;
    252 
    253   if (err == STATUS_OK)
    254     return;
    255 
    256   if (err == INTERNAL_ERR)
    257   {
    258     string_append (str, "Internal error");
    259     return;
    260   }
    261 
    262   more = err;
    263   while (more && more != INTERNAL_ERR)
    264   {
    265     err = more;
    266     more = err->next;
    267     if (err->error != NERR_PASS)
    268     {
    269       NEOERR *r;
    270       if (err->error == 0)
    271       {
    272 	err_name = buf;
    273 	snprintf (buf, sizeof (buf), "Unknown Error");
    274       }
    275       else
    276       {
    277 	r = uListGet (Errors, err->error - 1, (void *)&err_name);
    278 	if (r != STATUS_OK)
    279 	{
    280 	  err_name = buf;
    281 	  snprintf (buf, sizeof (buf), "Error %d", err->error);
    282 	}
    283       }
    284 
    285       string_appendf(str, "%s: %s", err_name, err->desc);
    286       return;
    287     }
    288   }
    289 }
    290 
    291 void nerr_error_traceback (NEOERR *err, STRING *str)
    292 {
    293   NEOERR *more;
    294   char buf[1024];
    295   char buf2[1024];
    296   char *err_name;
    297 
    298   if (err == STATUS_OK)
    299     return;
    300 
    301   if (err == INTERNAL_ERR)
    302   {
    303     string_append (str, "Internal error");
    304     return;
    305   }
    306 
    307   more = err;
    308   string_append (str, "Traceback (innermost last):\n");
    309   while (more && more != INTERNAL_ERR)
    310   {
    311     err = more;
    312     more = err->next;
    313     if (err->error != NERR_PASS)
    314     {
    315       NEOERR *r;
    316       if (err->error == 0)
    317       {
    318 	err_name = buf;
    319 	snprintf (buf, sizeof (buf), "Unknown Error");
    320       }
    321       else
    322       {
    323 	r = uListGet (Errors, err->error - 1, (void *)&err_name);
    324 	if (r != STATUS_OK)
    325 	{
    326 	  err_name = buf;
    327 	  snprintf (buf, sizeof (buf), "Error %d", err->error);
    328 	}
    329       }
    330 
    331       snprintf (buf2, sizeof(buf2),
    332 	  "  File \"%s\", line %d, in %s()\n%s: %s\n", err->file,
    333 	  err->lineno, err->func, err_name, err->desc);
    334       string_append(str, buf2);
    335     }
    336     else
    337     {
    338       snprintf (buf2, sizeof(buf2), "  File \"%s\", line %d, in %s()\n",
    339 	  err->file, err->lineno, err->func);
    340       string_append(str, buf2);
    341       if (err->desc[0])
    342       {
    343 	snprintf (buf2, sizeof(buf2), "    %s\n", err->desc);
    344 	string_append(str, buf2);
    345       }
    346     }
    347   }
    348 }
    349 
    350 void nerr_ignore (NEOERR **err)
    351 {
    352   _err_free (*err);
    353   *err = STATUS_OK;
    354 }
    355 
    356 int nerr_handle (NEOERR **err, int etype)
    357 {
    358   NEOERR *walk = *err;
    359 
    360   while (walk != STATUS_OK && walk != INTERNAL_ERR)
    361   {
    362 
    363     if (walk->error == etype)
    364     {
    365       _err_free(*err);
    366       *err = STATUS_OK;
    367       return 1;
    368     }
    369     walk = walk->next;
    370   }
    371 
    372   if (walk == STATUS_OK && etype == STATUS_OK_INT)
    373     return 1;
    374   if (walk == STATUS_OK)
    375     return 0;
    376 
    377   if (walk == INTERNAL_ERR && etype == INTERNAL_ERR_INT)
    378   {
    379     *err = STATUS_OK;
    380     return 1;
    381   }
    382   if (walk == INTERNAL_ERR)
    383     return 0;
    384 
    385   return 0;
    386 }
    387 
    388 int nerr_match (NEOERR *err, int etype)
    389 {
    390   while (err != STATUS_OK && err != INTERNAL_ERR)
    391   {
    392 
    393     if (err->error == etype)
    394       return 1;
    395     err = err->next;
    396   }
    397 
    398   if (err == STATUS_OK && etype == STATUS_OK_INT)
    399     return 1;
    400   if (err == STATUS_OK)
    401     return 0;
    402 
    403   if (err == INTERNAL_ERR && etype == INTERNAL_ERR_INT)
    404     return 1;
    405   if (err == INTERNAL_ERR)
    406     return 0;
    407 
    408   return 0;
    409 }
    410 
    411 NEOERR *nerr_register (int *val, const char *name)
    412 {
    413   NEOERR *err;
    414 
    415   err = uListAppend (Errors, (void *) name);
    416   if (err != STATUS_OK) return nerr_pass(err);
    417 
    418   *val = uListLength(Errors);
    419   return STATUS_OK;
    420 }
    421 
    422 NEOERR *nerr_init (void)
    423 {
    424   NEOERR *err;
    425 
    426   if (Inited == 0)
    427   {
    428 #ifdef HAVE_PTHREADS
    429     /* In threaded environments, we have to mutex lock to do this init, but
    430      * we don't want to use a mutex every time to check that it was Inited.
    431      * So, we only lock if our first test of Inited was false */
    432     err = mLock(&InitLock);
    433     if (err != STATUS_OK) return nerr_pass(err);
    434     if (Inited == 0) {
    435 #endif
    436     err = uListInit (&Errors, 10, 0);
    437     if (err != STATUS_OK) return nerr_pass(err);
    438 
    439     err = nerr_register (&NERR_PASS, "InternalPass");
    440     if (err != STATUS_OK) return nerr_pass(err);
    441     err = nerr_register (&NERR_ASSERT, "AssertError");
    442     if (err != STATUS_OK) return nerr_pass(err);
    443     err = nerr_register (&NERR_NOT_FOUND, "NotFoundError");
    444     if (err != STATUS_OK) return nerr_pass(err);
    445     err = nerr_register (&NERR_DUPLICATE, "DuplicateError");
    446     if (err != STATUS_OK) return nerr_pass(err);
    447     err = nerr_register (&NERR_NOMEM, "MemoryError");
    448     if (err != STATUS_OK) return nerr_pass(err);
    449     err = nerr_register (&NERR_PARSE, "ParseError");
    450     if (err != STATUS_OK) return nerr_pass(err);
    451     err = nerr_register (&NERR_OUTOFRANGE, "RangeError");
    452     if (err != STATUS_OK) return nerr_pass(err);
    453     err = nerr_register (&NERR_SYSTEM, "SystemError");
    454     if (err != STATUS_OK) return nerr_pass(err);
    455     err = nerr_register (&NERR_IO, "IOError");
    456     if (err != STATUS_OK) return nerr_pass(err);
    457     err = nerr_register (&NERR_LOCK, "LockError");
    458     if (err != STATUS_OK) return nerr_pass(err);
    459     err = nerr_register (&NERR_DB, "DBError");
    460     if (err != STATUS_OK) return nerr_pass(err);
    461     err = nerr_register (&NERR_EXISTS, "ExistsError");
    462     if (err != STATUS_OK) return nerr_pass(err);
    463 
    464     Inited = 1;
    465 #ifdef HAVE_PTHREADS
    466     }
    467     err = mUnlock(&InitLock);
    468     if (err != STATUS_OK) return nerr_pass(err);
    469 #endif
    470   }
    471   return STATUS_OK;
    472 }
    473