long delayed renaming of m_fatal() to i_fatal() to match Imager's
[imager.git] / error.c
CommitLineData
606237f9
TC
1/*
2=head1 NAME
3
4error.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
40This module provides the C level error handling functionality for
41Imager.
42
c5cf7614
TC
43A few functions return or pass in an i_errmsg *, this is list of error
44structures, terminated by an entry with a NULL msg value, each of
45which contains a msg and an error code. Even though these aren't
46passed as i_errmsg const * pointers, don't modify the strings
47or the pointers.
606237f9
TC
48
49The interface as currently defined isn't thread safe, unfortunately.
50
51This code uses Imager's mymalloc() for memory allocation, so out of
52memory errors are I<always> fatal.
53
54=head1 INTERFACE
55
56These functions form the interface that a user of Imager sees (from
57C). The Perl level won't use all of this.
58
59=over
60
61=cut
62*/
63
92bda632 64#include "imager.h"
606237f9
TC
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
b33c08f8
TC
70static i_errmsg error_stack[ERRSTK];
71static int error_sp = ERRSTK - 1;
606237f9
TC
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. */
b33c08f8 76static int error_space[ERRSTK];
606237f9
TC
77
78static i_error_cb error_cb;
79static i_failed_cb failed_cb;
80static int failures_fatal;
81static char *argv0;
82
83/*
84=item i_set_argv0(char const *program)
85
86Sets the name of the program to be displayed in fatal error messages.
87
88The simplest way to use this is just:
89
90 i_set_argv0(argv[0]);
91
92when your program starts.
93*/
94void i_set_argv0(char const *name) {
c5cf7614 95 char *dupl;
606237f9
TC
96 if (!name)
97 return;
f0960b14
TC
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 */
606237f9
TC
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
110If failure_fatal is non-zero then any future failures will result in
111Imager exiting your program with a message describing the failure.
112
113Returns the previous setting.
114
115=cut
116*/
117int 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
127Sets a callback function that is called each time an error is pushed
128onto the error stack.
129
130Returns the previous callback.
131
132i_set_failed_cb() is probably more useful.
133
134=cut
135*/
136i_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
146Sets a callback function that is called each time an Imager function
147fails.
148
149Returns the previous callback.
150
151=cut
152*/
153i_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
163Returns a pointer to the first element of an array of error messages,
164terminated by a NULL pointer. The highest level message is first.
165
166=cut
167*/
c5cf7614 168i_errmsg *i_errors() {
606237f9
TC
169 return error_stack + error_sp;
170}
171
172/*
173=back
174
175=head1 INTERNAL FUNCTIONS
176
177These functions are called by Imager to report errors through the
178above interface.
179
180It may be desirable to have functions to mark the stack and reset to
181the mark.
182
183=over
184
185=item i_clear_error()
186
92bda632
TC
187=category Error handling
188
189Clears the error stack.
190
606237f9
TC
191Called by any imager function before doing any other processing.
192
92bda632
TC
193=cut
194*/
606237f9 195void i_clear_error() {
cd4b0b20
TC
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
606237f9
TC
207 error_sp = ERRSTK-1;
208}
209
210/*
faa9b3e7 211=item i_push_error(int code, char const *msg)
606237f9 212
92bda632
TC
213=category Error handling
214
606237f9
TC
215Called by an imager function to push an error message onto the stack.
216
217No message is pushed if the stack is full (since this means someone
218forgot to call i_clear_error(), or that a function that doesn't do
219error handling is calling function that does.).
220
221=cut
222*/
c5cf7614 223void i_push_error(int code, char const *msg) {
606237f9
TC
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) {
c5cf7614
TC
232 if (error_stack[error_sp].msg)
233 myfree(error_stack[error_sp].msg);
f0960b14 234 /* memory allocated on the following line is only ever released when
606237f9 235 we need a bigger string */
f0960b14
TC
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 */
606237f9
TC
239 error_space[error_sp] = size;
240 }
c5cf7614
TC
241 strcpy(error_stack[error_sp].msg, msg);
242 error_stack[error_sp].code = code;
606237f9
TC
243
244 if (error_cb)
c5cf7614
TC
245 error_cb(code, msg);
246}
247
248/*
249=item i_push_errorvf(int code, char const *fmt, va_list ap)
250
92bda632
TC
251=category Error handling
252
c5cf7614
TC
253Intended for use by higher level functions, takes a varargs pointer
254and a format to produce the finally pushed error message.
255
256=cut
257*/
258void 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
92bda632
TC
275=category Error handling
276
c5cf7614
TC
277A version of i_push_error() that does printf() like formating.
278
279=cut
280*/
281void 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);
606237f9
TC
286}
287
f0960b14
TC
288#ifdef IMAGER_I_FAILED
289#error "This isn't used and is untested"
290
606237f9
TC
291/*
292=item i_failed(char const *msg)
293
294Called by Imager code to indicate that a top-level has failed.
295
c5cf7614 296msg can be NULL, in which case no error is pushed.
606237f9
TC
297
298Calls the current failed callback, if any.
299
300Aborts the program with an error, if failures have been set to be fatal.
301
302Returns zero if it does not abort.
303
304=cut
305*/
c5cf7614 306int i_failed(int code, char const *msg) {
606237f9 307 if (msg)
c5cf7614 308 i_push_error(code, msg);
606237f9
TC
309 if (failed_cb)
310 failed_cb(error_stack + error_sp);
311 if (failures_fatal) {
c5cf7614
TC
312 int sp;
313 int total; /* total length of error messages */
314 char *full; /* full message for logging */
606237f9
TC
315 if (argv0)
316 fprintf(stderr, "%s: ", argv0);
317 fputs("error:\n", stderr);
c5cf7614
TC
318 sp = error_sp;
319 while (error_stack[sp].msg) {
a743c0a6 320 fprintf(stderr, " %s\n", error_stack[sp].msg);
c5cf7614
TC
321 ++sp;
322 }
323 /* we want to log the error too, build an error message to hand to
b1e96952 324 i_fatal() */
c5cf7614
TC
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 }
a6c47345 329 full = mymalloc(total);
c5cf7614
TC
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, ": ");
606237f9 338 }
c5cf7614
TC
339 /* lose the extra ": " */
340 full[strlen(full)-2] = '\0';
b1e96952 341 i_fatal(EXIT_FAILURE, "%s", full);
606237f9
TC
342 }
343
344 return 0;
345}
346
f0960b14
TC
347#endif
348
606237f9
TC
349/*
350=back
351
352=head1 BUGS
353
354This interface isn't thread safe.
355
356=head1 AUTHOR
357
358Tony Cook <tony@develop-help.com>
359
360Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
361
362=cut
363*/