- add smoke test for nearest_color filter
[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
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
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;
c5cf7614 98 dupl = mymalloc(strlen(name)+1);
606237f9
TC
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
108If failure_fatal is non-zero then any future failures will result in
109Imager exiting your program with a message describing the failure.
110
111Returns the previous setting.
112
113=cut
114*/
115int 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
125Sets a callback function that is called each time an error is pushed
126onto the error stack.
127
128Returns the previous callback.
129
130i_set_failed_cb() is probably more useful.
131
132=cut
133*/
134i_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
144Sets a callback function that is called each time an Imager function
145fails.
146
147Returns the previous callback.
148
149=cut
150*/
151i_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
161Returns a pointer to the first element of an array of error messages,
162terminated by a NULL pointer. The highest level message is first.
163
164=cut
165*/
c5cf7614 166i_errmsg *i_errors() {
606237f9
TC
167 return error_stack + error_sp;
168}
169
170/*
171=back
172
173=head1 INTERNAL FUNCTIONS
174
175These functions are called by Imager to report errors through the
176above interface.
177
178It may be desirable to have functions to mark the stack and reset to
179the mark.
180
181=over
182
183=item i_clear_error()
184
185Called by any imager function before doing any other processing.
186
187=cut */
188void i_clear_error() {
cd4b0b20
TC
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
606237f9
TC
200 error_sp = ERRSTK-1;
201}
202
203/*
faa9b3e7 204=item i_push_error(int code, char const *msg)
606237f9
TC
205
206Called by an imager function to push an error message onto the stack.
207
208No message is pushed if the stack is full (since this means someone
209forgot to call i_clear_error(), or that a function that doesn't do
210error handling is calling function that does.).
211
212=cut
213*/
c5cf7614 214void i_push_error(int code, char const *msg) {
606237f9
TC
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) {
c5cf7614
TC
223 if (error_stack[error_sp].msg)
224 myfree(error_stack[error_sp].msg);
606237f9
TC
225 /* memory allocated on the following line is only ever release when
226 we need a bigger string */
c5cf7614 227 error_stack[error_sp].msg = mymalloc(size);
606237f9
TC
228 error_space[error_sp] = size;
229 }
c5cf7614
TC
230 strcpy(error_stack[error_sp].msg, msg);
231 error_stack[error_sp].code = code;
606237f9
TC
232
233 if (error_cb)
c5cf7614
TC
234 error_cb(code, msg);
235}
236
237/*
238=item i_push_errorvf(int code, char const *fmt, va_list ap)
239
240Intended for use by higher level functions, takes a varargs pointer
241and a format to produce the finally pushed error message.
242
243=cut
244*/
245void 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
262A version of i_push_error() that does printf() like formating.
263
264=cut
265*/
266void 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);
606237f9
TC
271}
272
273/*
274=item i_failed(char const *msg)
275
276Called by Imager code to indicate that a top-level has failed.
277
c5cf7614 278msg can be NULL, in which case no error is pushed.
606237f9
TC
279
280Calls the current failed callback, if any.
281
282Aborts the program with an error, if failures have been set to be fatal.
283
284Returns zero if it does not abort.
285
286=cut
287*/
c5cf7614 288int i_failed(int code, char const *msg) {
606237f9 289 if (msg)
c5cf7614 290 i_push_error(code, msg);
606237f9
TC
291 if (failed_cb)
292 failed_cb(error_stack + error_sp);
293 if (failures_fatal) {
c5cf7614
TC
294 int sp;
295 int total; /* total length of error messages */
296 char *full; /* full message for logging */
606237f9
TC
297 if (argv0)
298 fprintf(stderr, "%s: ", argv0);
299 fputs("error:\n", stderr);
c5cf7614
TC
300 sp = error_sp;
301 while (error_stack[sp].msg) {
a743c0a6 302 fprintf(stderr, " %s\n", error_stack[sp].msg);
c5cf7614
TC
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 }
a6c47345 311 full = mymalloc(total);
c5cf7614
TC
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, ": ");
606237f9 320 }
c5cf7614
TC
321 /* lose the extra ": " */
322 full[strlen(full)-2] = '\0';
323 m_fatal(EXIT_FAILURE, "%s", full);
606237f9
TC
324 }
325
326 return 0;
327}
328
329/*
330=back
331
332=head1 BUGS
333
334This interface isn't thread safe.
335
336=head1 AUTHOR
337
338Tony Cook <tony@develop-help.com>
339
340Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
341
342=cut
343*/