]> git.imager.perl.org - imager.git/blob - error.c
b1e87d38d77a10eff2d562b8990dbb114d057720
[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 "image.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   dupl = mymalloc(strlen(name)+1);
99   strcpy(dupl, name);
100   if (argv0)
101     myfree(argv0);
102   argv0 = dupl;
103 }
104
105 /*
106 =item i_set_failure_fatal(int failure_fatal)
107
108 If failure_fatal is non-zero then any future failures will result in
109 Imager exiting your program with a message describing the failure.
110
111 Returns the previous setting.
112
113 =cut
114 */
115 int i_set_failures_fatal(int fatal) {
116   int old = failures_fatal;
117   failures_fatal = fatal;
118
119   return old;
120 }
121
122 /*
123 =item i_set_error_cb(i_error_cb)
124
125 Sets a callback function that is called each time an error is pushed
126 onto the error stack.
127
128 Returns the previous callback.
129
130 i_set_failed_cb() is probably more useful.
131
132 =cut
133 */
134 i_error_cb i_set_error_cb(i_error_cb cb) {
135   i_error_cb old = error_cb;
136   error_cb = cb;
137
138   return old;
139 }
140
141 /*
142 =item i_set_failed_cb(i_failed_cb cb)
143
144 Sets a callback function that is called each time an Imager function
145 fails.
146
147 Returns the previous callback.
148
149 =cut
150 */
151 i_failed_cb i_set_failed_cb(i_failed_cb cb) {
152   i_failed_cb old = failed_cb;
153   failed_cb = cb;
154
155   return old;
156 }
157
158 /*
159 =item i_errors()
160
161 Returns a pointer to the first element of an array of error messages,
162 terminated by a NULL pointer.  The highest level message is first.
163
164 =cut
165 */
166 i_errmsg *i_errors() {
167   return error_stack + error_sp;
168 }
169
170 /*
171 =back
172
173 =head1 INTERNAL FUNCTIONS
174
175 These functions are called by Imager to report errors through the
176 above interface.
177
178 It may be desirable to have functions to mark the stack and reset to
179 the mark.
180
181 =over
182
183 =item i_clear_error()
184
185 Called by any imager function before doing any other processing.
186
187 =cut */
188 void i_clear_error() {
189 #ifdef IMAGER_DEBUG_MALLOC
190   int i;
191
192   for (i = 0; i < ERRSTK; ++i) {
193     if (error_space[i]) {
194       myfree(error_stack[i].msg);
195       error_stack[i].msg = NULL;
196       error_space[i] = 0;
197     }
198   }
199 #endif
200   error_sp = ERRSTK-1;
201 }
202
203 /*
204 =item i_push_error(int code, char const *msg)
205
206 Called by an imager function to push an error message onto the stack.
207
208 No message is pushed if the stack is full (since this means someone
209 forgot to call i_clear_error(), or that a function that doesn't do
210 error handling is calling function that does.).
211
212 =cut
213 */
214 void i_push_error(int code, char const *msg) {
215   int size = strlen(msg)+1;
216
217   if (error_sp <= 0)
218     /* bad, bad programmer */
219     return;
220
221   --error_sp;
222   if (error_space[error_sp] < size) {
223     if (error_stack[error_sp].msg)
224       myfree(error_stack[error_sp].msg);
225     /* memory allocated on the following line is only ever release when 
226        we need a bigger string */
227     error_stack[error_sp].msg = mymalloc(size);
228     error_space[error_sp] = size;
229   }
230   strcpy(error_stack[error_sp].msg, msg);
231   error_stack[error_sp].code = code;
232
233   if (error_cb)
234     error_cb(code, msg);
235 }
236
237 /*
238 =item i_push_errorvf(int code, char const *fmt, va_list ap)
239
240 Intended for use by higher level functions, takes a varargs pointer
241 and a format to produce the finally pushed error message.
242
243 =cut
244 */
245 void i_push_errorvf(int code, char const *fmt, va_list ap) {
246   char buf[1024];
247 #if defined(_MSC_VER)
248   _vsnprintf(buf, sizeof(buf), fmt, ap);
249 #else
250   /* is there a way to detect vsnprintf()? 
251      for this and other functions we need some mechanism to handle 
252      detection (like perl's Configure, or autoconf)
253    */
254   vsprintf(buf, fmt, ap);
255 #endif
256   i_push_error(code, buf);
257 }
258
259 /*
260 =item i_push_errorf(int code, char const *fmt, ...)
261
262 A version of i_push_error() that does printf() like formating.
263
264 =cut
265 */
266 void i_push_errorf(int code, char const *fmt, ...) {
267   va_list ap;
268   va_start(ap, fmt);
269   i_push_errorvf(code, fmt, ap);
270   va_end(ap);
271 }
272
273 /*
274 =item i_failed(char const *msg)
275
276 Called by Imager code to indicate that a top-level has failed.
277
278 msg can be NULL, in which case no error is pushed.
279
280 Calls the current failed callback, if any.
281
282 Aborts the program with an error, if failures have been set to be fatal.
283
284 Returns zero if it does not abort.
285
286 =cut
287 */
288 int i_failed(int code, char const *msg) {
289   if (msg)
290     i_push_error(code, msg);
291   if (failed_cb)
292     failed_cb(error_stack + error_sp);
293   if (failures_fatal) {
294     int sp;
295     int total; /* total length of error messages */
296     char *full; /* full message for logging */
297     if (argv0)
298       fprintf(stderr, "%s: ", argv0);
299     fputs("error:\n", stderr);
300     sp = error_sp;
301     while (error_stack[sp].msg) {
302       fprintf(stderr, " %s\n", error_stack[sp].msg);
303       ++sp;
304     }
305     /* we want to log the error too, build an error message to hand to
306        m_fatal() */
307     total = 1; /* remember the NUL */
308     for (sp = error_sp; error_stack[sp].msg; ++sp) {
309       total += strlen(error_stack[sp].msg) + 2;
310     }
311     full = mymalloc(total);
312     if (!full) {
313       /* just quit, at least it's on stderr */
314       exit(EXIT_FAILURE);
315     }
316     *full = 0;
317     for (sp = error_sp; error_stack[sp].msg; ++sp) {
318       strcat(full, error_stack[sp].msg);
319       strcat(full, ": ");
320     }
321     /* lose the extra ": " */
322     full[strlen(full)-2] = '\0';
323     m_fatal(EXIT_FAILURE, "%s", full);
324   }
325
326   return 0;
327 }
328
329 /*
330 =back
331
332 =head1 BUGS
333
334 This interface isn't thread safe.
335
336 =head1 AUTHOR
337
338 Tony Cook <tony@develop-help.com>
339
340 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
341
342 =cut
343 */