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