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