]> git.imager.perl.org - imager.git/blob - error.c
- clean up Imager::Font documentation:
[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   /* 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
187 Called by any imager function before doing any other processing.
188
189 =cut */
190 void i_clear_error() {
191 #ifdef IMAGER_DEBUG_MALLOC
192   int i;
193
194   for (i = 0; i < ERRSTK; ++i) {
195     if (error_space[i]) {
196       myfree(error_stack[i].msg);
197       error_stack[i].msg = NULL;
198       error_space[i] = 0;
199     }
200   }
201 #endif
202   error_sp = ERRSTK-1;
203 }
204
205 /*
206 =item i_push_error(int code, char const *msg)
207
208 Called by an imager function to push an error message onto the stack.
209
210 No message is pushed if the stack is full (since this means someone
211 forgot to call i_clear_error(), or that a function that doesn't do
212 error handling is calling function that does.).
213
214 =cut
215 */
216 void i_push_error(int code, char const *msg) {
217   int size = strlen(msg)+1;
218
219   if (error_sp <= 0)
220     /* bad, bad programmer */
221     return;
222
223   --error_sp;
224   if (error_space[error_sp] < size) {
225     if (error_stack[error_sp].msg)
226       myfree(error_stack[error_sp].msg);
227     /* memory allocated on the following line is only ever released when 
228        we need a bigger string */
229     /* size is size (len+1) of an existing string, overflow would mean
230        the system is broken anyway */
231     error_stack[error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
232     error_space[error_sp] = size;
233   }
234   strcpy(error_stack[error_sp].msg, msg);
235   error_stack[error_sp].code = code;
236
237   if (error_cb)
238     error_cb(code, msg);
239 }
240
241 /*
242 =item i_push_errorvf(int code, char const *fmt, va_list ap)
243
244 Intended for use by higher level functions, takes a varargs pointer
245 and a format to produce the finally pushed error message.
246
247 =cut
248 */
249 void i_push_errorvf(int code, char const *fmt, va_list ap) {
250   char buf[1024];
251 #if defined(_MSC_VER)
252   _vsnprintf(buf, sizeof(buf), fmt, ap);
253 #else
254   /* is there a way to detect vsnprintf()? 
255      for this and other functions we need some mechanism to handle 
256      detection (like perl's Configure, or autoconf)
257    */
258   vsprintf(buf, fmt, ap);
259 #endif
260   i_push_error(code, buf);
261 }
262
263 /*
264 =item i_push_errorf(int code, char const *fmt, ...)
265
266 A version of i_push_error() that does printf() like formating.
267
268 =cut
269 */
270 void i_push_errorf(int code, char const *fmt, ...) {
271   va_list ap;
272   va_start(ap, fmt);
273   i_push_errorvf(code, fmt, ap);
274   va_end(ap);
275 }
276
277 #ifdef IMAGER_I_FAILED
278 #error "This isn't used and is untested"
279
280 /*
281 =item i_failed(char const *msg)
282
283 Called by Imager code to indicate that a top-level has failed.
284
285 msg can be NULL, in which case no error is pushed.
286
287 Calls the current failed callback, if any.
288
289 Aborts the program with an error, if failures have been set to be fatal.
290
291 Returns zero if it does not abort.
292
293 =cut
294 */
295 int i_failed(int code, char const *msg) {
296   if (msg)
297     i_push_error(code, msg);
298   if (failed_cb)
299     failed_cb(error_stack + error_sp);
300   if (failures_fatal) {
301     int sp;
302     int total; /* total length of error messages */
303     char *full; /* full message for logging */
304     if (argv0)
305       fprintf(stderr, "%s: ", argv0);
306     fputs("error:\n", stderr);
307     sp = error_sp;
308     while (error_stack[sp].msg) {
309       fprintf(stderr, " %s\n", error_stack[sp].msg);
310       ++sp;
311     }
312     /* we want to log the error too, build an error message to hand to
313        m_fatal() */
314     total = 1; /* remember the NUL */
315     for (sp = error_sp; error_stack[sp].msg; ++sp) {
316       total += strlen(error_stack[sp].msg) + 2;
317     }
318     full = mymalloc(total);
319     if (!full) {
320       /* just quit, at least it's on stderr */
321       exit(EXIT_FAILURE);
322     }
323     *full = 0;
324     for (sp = error_sp; error_stack[sp].msg; ++sp) {
325       strcat(full, error_stack[sp].msg);
326       strcat(full, ": ");
327     }
328     /* lose the extra ": " */
329     full[strlen(full)-2] = '\0';
330     m_fatal(EXIT_FAILURE, "%s", full);
331   }
332
333   return 0;
334 }
335
336 #endif
337
338 /*
339 =back
340
341 =head1 BUGS
342
343 This interface isn't thread safe.
344
345 =head1 AUTHOR
346
347 Tony Cook <tony@develop-help.com>
348
349 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
350
351 =cut
352 */