]> git.imager.perl.org - imager.git/blob - error.c
WIP, more coverage and fixes
[imager.git] / error.c
1 /*
2 =head1 NAME
3
4 error.c - error reporting code for Imager
5
6 =head1 SYNOPSIS
7
8   // user code:
9   int new_fatal; // non-zero if errors are fatal
10   int old_fatal = i_set_failure_fatal(new_fatal);
11   i_set_argv0("name of your program");
12   extern void error_cb(char const *);
13   i_error_cb old_ecb;
14   old_ecb = i_set_error_cb(error_cb);
15   i_failed_cb old_fcb;
16   extern void failed_cb(char **errors);
17   old_fcb = i_set_failed_cb(failed_cb);
18   if (!i_something(...)) {
19     char **errors = i_errors();
20   }
21
22   // imager code:
23   undef_int i_something(...) {
24     i_clear_error();
25     if (!some_lower_func(...)) {
26       return i_failed("could not something");
27     }
28     return 1;
29   }
30   undef_int some_lower_func(...) {
31     if (somethingelse_failed()) {
32       i_push_error("could not somethingelse");
33       return 0;
34     }
35     return 1;
36   }
37
38 =head1 DESCRIPTION
39
40 This module provides the C level error handling functionality for
41 Imager.
42
43 A few functions return or pass in an i_errmsg *, this is list of error
44 structures, terminated by an entry with a NULL msg value, each of
45 which contains a msg and an error code. Even though these aren't
46 passed as i_errmsg const * pointers, don't modify the strings
47 or the pointers.
48
49 The interface as currently defined isn't thread safe, unfortunately.
50
51 This code uses Imager's mymalloc() for memory allocation, so out of
52 memory errors are I<always> fatal.
53
54 =head1 INTERFACE
55
56 These functions form the interface that a user of Imager sees (from
57 C).  The Perl level won't use all of this.
58
59 =over
60
61 =cut
62 */
63
64 #include "imager.h"
65 #include <stdio.h>
66 #include <stdlib.h>
67
68 /* we never actually use the last item - it's the NULL terminator */
69 #define ERRSTK 20
70 static i_errmsg error_stack[ERRSTK];
71 static int error_sp = ERRSTK - 1;
72 /* we track the amount of space used each string, so we don't reallocate 
73    space unless we need to.
74    This also means that a memory tracking library may see the memory 
75    allocated for this as a leak. */
76 static int error_space[ERRSTK];
77
78 static i_error_cb error_cb;
79 static i_failed_cb failed_cb;
80 static int failures_fatal;
81 static char *argv0;
82
83 /*
84 =item i_set_argv0(char const *program)
85
86 Sets the name of the program to be displayed in fatal error messages.
87
88 The simplest way to use this is just:
89
90   i_set_argv0(argv[0]);
91
92 when your program starts.
93 */
94 void i_set_argv0(char const *name) {
95   char *dupl;
96   if (!name)
97     return;
98   /* if the user has an existing string of MAXINT length then
99      the system is broken anyway */
100   dupl = mymalloc(strlen(name)+1); /* check 17jul05 tonyc */
101   strcpy(dupl, name);
102   if (argv0)
103     myfree(argv0);
104   argv0 = dupl;
105 }
106
107 /*
108 =item i_set_failure_fatal(int failure_fatal)
109
110 If failure_fatal is non-zero then any future failures will result in
111 Imager exiting your program with a message describing the failure.
112
113 Returns the previous setting.
114
115 =cut
116 */
117 int i_set_failures_fatal(int fatal) {
118   int old = failures_fatal;
119   failures_fatal = fatal;
120
121   return old;
122 }
123
124 /*
125 =item i_set_error_cb(i_error_cb)
126
127 Sets a callback function that is called each time an error is pushed
128 onto the error stack.
129
130 Returns the previous callback.
131
132 i_set_failed_cb() is probably more useful.
133
134 =cut
135 */
136 i_error_cb i_set_error_cb(i_error_cb cb) {
137   i_error_cb old = error_cb;
138   error_cb = cb;
139
140   return old;
141 }
142
143 /*
144 =item i_set_failed_cb(i_failed_cb cb)
145
146 Sets a callback function that is called each time an Imager function
147 fails.
148
149 Returns the previous callback.
150
151 =cut
152 */
153 i_failed_cb i_set_failed_cb(i_failed_cb cb) {
154   i_failed_cb old = failed_cb;
155   failed_cb = cb;
156
157   return old;
158 }
159
160 /*
161 =item i_errors()
162
163 Returns a pointer to the first element of an array of error messages,
164 terminated by a NULL pointer.  The highest level message is first.
165
166 =cut
167 */
168 i_errmsg *i_errors() {
169   return error_stack + error_sp;
170 }
171
172 /*
173 =back
174
175 =head1 INTERNAL FUNCTIONS
176
177 These functions are called by Imager to report errors through the
178 above interface.
179
180 It may be desirable to have functions to mark the stack and reset to
181 the mark.
182
183 =over
184
185 =item i_clear_error()
186 =synopsis i_clear_error();
187 =category Error handling
188
189 Clears the error stack.
190
191 Called by any Imager function before doing any other processing.
192
193 =cut
194 */
195 void i_clear_error() {
196 #ifdef IMAGER_DEBUG_MALLOC
197   int i;
198
199   for (i = 0; i < ERRSTK; ++i) {
200     if (error_space[i]) {
201       myfree(error_stack[i].msg);
202       error_stack[i].msg = NULL;
203       error_space[i] = 0;
204     }
205   }
206 #endif
207   error_sp = ERRSTK-1;
208 }
209
210 /*
211 =item i_push_error(int code, char const *msg)
212 =synopsis i_push_error(0, "Yep, it's broken");
213 =synopsis i_push_error(errno, "Error writing");
214 =category Error handling
215
216 Called by an Imager function to push an error message onto the stack.
217
218 No message is pushed if the stack is full (since this means someone
219 forgot to call i_clear_error(), or that a function that doesn't do
220 error handling is calling function that does.).
221
222 =cut
223 */
224 void i_push_error(int code, char const *msg) {
225   size_t size = strlen(msg)+1;
226
227   if (error_sp <= 0)
228     /* bad, bad programmer */
229     return;
230
231   --error_sp;
232   if (error_space[error_sp] < size) {
233     if (error_stack[error_sp].msg)
234       myfree(error_stack[error_sp].msg);
235     /* memory allocated on the following line is only ever released when 
236        we need a bigger string */
237     /* size is size (len+1) of an existing string, overflow would mean
238        the system is broken anyway */
239     error_stack[error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
240     error_space[error_sp] = size;
241   }
242   strcpy(error_stack[error_sp].msg, msg);
243   error_stack[error_sp].code = code;
244
245   if (error_cb)
246     error_cb(code, msg);
247 }
248
249 /*
250 =item i_push_errorvf(int C<code>, char const *C<fmt>, va_list C<ap>)
251
252 =category Error handling
253
254 Intended for use by higher level functions, takes a varargs pointer
255 and a format to produce the finally pushed error message.
256
257 Does not support perl specific format codes.
258
259 =cut
260 */
261 void i_push_errorvf(int code, char const *fmt, va_list ap) {
262   char buf[1024];
263 #if defined(IMAGER_VSNPRINTF)
264   vsnprintf(buf, sizeof(buf), fmt, ap);
265 #elif defined(_MSC_VER)
266   _vsnprintf(buf, sizeof(buf), fmt, ap);
267 #else
268   /* is there a way to detect vsnprintf()? 
269      for this and other functions we need some mechanism to handle 
270      detection (like perl's Configure, or autoconf)
271    */
272   vsprintf(buf, fmt, ap);
273 #endif
274   i_push_error(code, buf);
275 }
276
277 /*
278 =item i_push_errorf(int code, char const *fmt, ...)
279 =synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
280 =category Error handling
281
282 A version of i_push_error() that does printf() like formatting.
283
284 Does not support perl specific format codes.
285
286 =cut
287 */
288 void i_push_errorf(int code, char const *fmt, ...) {
289   va_list ap;
290   va_start(ap, fmt);
291   i_push_errorvf(code, fmt, ap);
292   va_end(ap);
293 }
294
295 #ifdef IMAGER_I_FAILED
296 #error "This isn't used and is untested"
297
298 /*
299 =item i_failed(char const *msg)
300
301 Called by Imager code to indicate that a top-level has failed.
302
303 msg can be NULL, in which case no error is pushed.
304
305 Calls the current failed callback, if any.
306
307 Aborts the program with an error, if failures have been set to be fatal.
308
309 Returns zero if it does not abort.
310
311 =cut
312 */
313 int i_failed(int code, char const *msg) {
314   if (msg)
315     i_push_error(code, msg);
316   if (failed_cb)
317     failed_cb(error_stack + error_sp);
318   if (failures_fatal) {
319     int sp;
320     size_t total; /* total length of error messages */
321     char *full; /* full message for logging */
322     if (argv0)
323       fprintf(stderr, "%s: ", argv0);
324     fputs("error:\n", stderr);
325     sp = error_sp;
326     while (error_stack[sp].msg) {
327       fprintf(stderr, " %s\n", error_stack[sp].msg);
328       ++sp;
329     }
330     /* we want to log the error too, build an error message to hand to
331        i_fatal() */
332     total = 1; /* remember the NUL */
333     for (sp = error_sp; error_stack[sp].msg; ++sp) {
334       size_t new_total += strlen(error_stack[sp].msg) + 2;
335       if (new_total < total) {
336         /* overflow, somehow */
337         break;
338       }
339     }
340     full = mymalloc(total);
341     if (!full) {
342       /* just quit, at least it's on stderr */
343       exit(EXIT_FAILURE);
344     }
345     *full = 0;
346     for (sp = error_sp; error_stack[sp].msg; ++sp) {
347       strcat(full, error_stack[sp].msg);
348       strcat(full, ": ");
349     }
350     /* lose the extra ": " */
351     full[strlen(full)-2] = '\0';
352     i_fatal(EXIT_FAILURE, "%s", full);
353   }
354
355   return 0;
356 }
357
358 #endif
359
360 /*
361 =item im_assert_fail(file, line, message)
362
363 Called when an im_assert() assertion fails.
364
365 =cut
366 */
367
368 void
369 im_assert_fail(char const *file, int line, char const *message) {
370   fprintf(stderr, "Assertion failed line %d file %s: %s\n", 
371           line, file, message);
372   exit(EXIT_FAILURE);
373 }
374
375 /*
376 =back
377
378 =head1 BUGS
379
380 This interface isn't thread safe.
381
382 =head1 AUTHOR
383
384 Tony Cook <tony@develop-help.com>
385
386 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
387
388 =cut
389 */