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
_err_alloc(void)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
_err_free(NEOERR * err)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
nerr_raisef(const char * func,const char * file,int lineno,int error,const char * fmt,...)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
nerr_raise_errnof(const char * func,const char * file,int lineno,int error,const char * fmt,...)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
nerr_passf(const char * func,const char * file,int lineno,NEOERR * err)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
nerr_pass_ctxf(const char * func,const char * file,int lineno,NEOERR * err,const char * fmt,...)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 */
nerr_log_error(NEOERR * err)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
nerr_error_string(NEOERR * err,STRING * str)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
nerr_error_traceback(NEOERR * err,STRING * str)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
nerr_ignore(NEOERR ** err)350 void nerr_ignore (NEOERR **err)
351 {
352 _err_free (*err);
353 *err = STATUS_OK;
354 }
355
nerr_handle(NEOERR ** err,int etype)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
nerr_match(NEOERR * err,int etype)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
nerr_register(int * val,const char * name)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
nerr_init(void)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