]> git.imager.perl.org - imager.git/blob - error.c
try to generate all coverage into the one cover_db
[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   int 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(_MSC_VER)
264   _vsnprintf(buf, sizeof(buf), fmt, ap);
265 #else
266   /* is there a way to detect vsnprintf()? 
267      for this and other functions we need some mechanism to handle 
268      detection (like perl's Configure, or autoconf)
269    */
270   vsprintf(buf, fmt, ap);
271 #endif
272   i_push_error(code, buf);
273 }
274
275 /*
276 =item i_push_errorf(int code, char const *fmt, ...)
277 =synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
278 =category Error handling
279
280 A version of i_push_error() that does printf() like formatting.
281
282 Does not support perl specific format codes.
283
284 =cut
285 */
286 void i_push_errorf(int code, char const *fmt, ...) {
287   va_list ap;
288   va_start(ap, fmt);
289   i_push_errorvf(code, fmt, ap);
290   va_end(ap);
291 }
292
293 #ifdef IMAGER_I_FAILED
294 #error "This isn't used and is untested"
295
296 /*
297 =item i_failed(char const *msg)
298
299 Called by Imager code to indicate that a top-level has failed.
300
301 msg can be NULL, in which case no error is pushed.
302
303 Calls the current failed callback, if any.
304
305 Aborts the program with an error, if failures have been set to be fatal.
306
307 Returns zero if it does not abort.
308
309 =cut
310 */
311 int i_failed(int code, char const *msg) {
312   if (msg)
313     i_push_error(code, msg);
314   if (failed_cb)
315     failed_cb(error_stack + error_sp);
316   if (failures_fatal) {
317     int sp;
318     int total; /* total length of error messages */
319     char *full; /* full message for logging */
320     if (argv0)
321       fprintf(stderr, "%s: ", argv0);
322     fputs("error:\n", stderr);
323     sp = error_sp;
324     while (error_stack[sp].msg) {
325       fprintf(stderr, " %s\n", error_stack[sp].msg);
326       ++sp;
327     }
328     /* we want to log the error too, build an error message to hand to
329        i_fatal() */
330     total = 1; /* remember the NUL */
331     for (sp = error_sp; error_stack[sp].msg; ++sp) {
332       total += strlen(error_stack[sp].msg) + 2;
333     }
334     full = mymalloc(total);
335     if (!full) {
336       /* just quit, at least it's on stderr */
337       exit(EXIT_FAILURE);
338     }
339     *full = 0;
340     for (sp = error_sp; error_stack[sp].msg; ++sp) {
341       strcat(full, error_stack[sp].msg);
342       strcat(full, ": ");
343     }
344     /* lose the extra ": " */
345     full[strlen(full)-2] = '\0';
346     i_fatal(EXIT_FAILURE, "%s", full);
347   }
348
349   return 0;
350 }
351
352 #endif
353
354 /*
355 =item im_assert_fail(file, line, message)
356
357 Called when an im_assert() assertion fails.
358
359 =cut
360 */
361
362 void
363 im_assert_fail(char const *file, int line, char const *message) {
364   fprintf(stderr, "Assertion failed line %d file %s: %s\n", 
365           line, file, message);
366   exit(EXIT_FAILURE);
367 }
368
369 /*
370 =back
371
372 =head1 BUGS
373
374 This interface isn't thread safe.
375
376 =head1 AUTHOR
377
378 Tony Cook <tony@develop-help.com>
379
380 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
381
382 =cut
383 */