API documentation (mostly)
[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()
6cfee9d1 186=synopsis i_clear_error();
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)
6cfee9d1
TC
212=synopsis i_push_error(0, "Yep, it's broken");
213=synopsis i_push_error(errno, "Error writing");
92bda632
TC
214=category Error handling
215
6cfee9d1 216Called by an Imager function to push an error message onto the stack.
606237f9
TC
217
218No message is pushed if the stack is full (since this means someone
219forgot to call i_clear_error(), or that a function that doesn't do
220error handling is calling function that does.).
221
222=cut
223*/
c5cf7614 224void i_push_error(int code, char const *msg) {
606237f9
TC
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) {
c5cf7614
TC
233 if (error_stack[error_sp].msg)
234 myfree(error_stack[error_sp].msg);
f0960b14 235 /* memory allocated on the following line is only ever released when
606237f9 236 we need a bigger string */
f0960b14
TC
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 */
606237f9
TC
240 error_space[error_sp] = size;
241 }
c5cf7614
TC
242 strcpy(error_stack[error_sp].msg, msg);
243 error_stack[error_sp].code = code;
606237f9
TC
244
245 if (error_cb)
c5cf7614
TC
246 error_cb(code, msg);
247}
248
249/*
250=item i_push_errorvf(int code, char const *fmt, va_list ap)
251
92bda632
TC
252=category Error handling
253
c5cf7614
TC
254Intended for use by higher level functions, takes a varargs pointer
255and a format to produce the finally pushed error message.
256
6cfee9d1
TC
257Does not support perl specific format codes.
258
c5cf7614
TC
259=cut
260*/
261void 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, ...)
6cfee9d1 277=synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
92bda632
TC
278=category Error handling
279
c5cf7614
TC
280A version of i_push_error() that does printf() like formating.
281
6cfee9d1
TC
282Does not support perl specific format codes.
283
c5cf7614
TC
284=cut
285*/
286void 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);
606237f9
TC
291}
292
f0960b14
TC
293#ifdef IMAGER_I_FAILED
294#error "This isn't used and is untested"
295
606237f9
TC
296/*
297=item i_failed(char const *msg)
298
299Called by Imager code to indicate that a top-level has failed.
300
c5cf7614 301msg can be NULL, in which case no error is pushed.
606237f9
TC
302
303Calls the current failed callback, if any.
304
305Aborts the program with an error, if failures have been set to be fatal.
306
307Returns zero if it does not abort.
308
309=cut
310*/
c5cf7614 311int i_failed(int code, char const *msg) {
606237f9 312 if (msg)
c5cf7614 313 i_push_error(code, msg);
606237f9
TC
314 if (failed_cb)
315 failed_cb(error_stack + error_sp);
316 if (failures_fatal) {
c5cf7614
TC
317 int sp;
318 int total; /* total length of error messages */
319 char *full; /* full message for logging */
606237f9
TC
320 if (argv0)
321 fprintf(stderr, "%s: ", argv0);
322 fputs("error:\n", stderr);
c5cf7614
TC
323 sp = error_sp;
324 while (error_stack[sp].msg) {
a743c0a6 325 fprintf(stderr, " %s\n", error_stack[sp].msg);
c5cf7614
TC
326 ++sp;
327 }
328 /* we want to log the error too, build an error message to hand to
b1e96952 329 i_fatal() */
c5cf7614
TC
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 }
a6c47345 334 full = mymalloc(total);
c5cf7614
TC
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, ": ");
606237f9 343 }
c5cf7614
TC
344 /* lose the extra ": " */
345 full[strlen(full)-2] = '\0';
b1e96952 346 i_fatal(EXIT_FAILURE, "%s", full);
606237f9
TC
347 }
348
349 return 0;
350}
351
f0960b14
TC
352#endif
353
606237f9
TC
354/*
355=back
356
357=head1 BUGS
358
359This interface isn't thread safe.
360
361=head1 AUTHOR
362
363Tony Cook <tony@develop-help.com>
364
365Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
366
367=cut
368*/