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