- extra concept index entries
[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;
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
187Called by any imager function before doing any other processing.
188
189=cut */
190void i_clear_error() {
cd4b0b20
TC
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
606237f9
TC
202 error_sp = ERRSTK-1;
203}
204
205/*
faa9b3e7 206=item i_push_error(int code, char const *msg)
606237f9
TC
207
208Called by an imager function to push an error message onto the stack.
209
210No message is pushed if the stack is full (since this means someone
211forgot to call i_clear_error(), or that a function that doesn't do
212error handling is calling function that does.).
213
214=cut
215*/
c5cf7614 216void i_push_error(int code, char const *msg) {
606237f9
TC
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) {
c5cf7614
TC
225 if (error_stack[error_sp].msg)
226 myfree(error_stack[error_sp].msg);
f0960b14 227 /* memory allocated on the following line is only ever released when
606237f9 228 we need a bigger string */
f0960b14
TC
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 */
606237f9
TC
232 error_space[error_sp] = size;
233 }
c5cf7614
TC
234 strcpy(error_stack[error_sp].msg, msg);
235 error_stack[error_sp].code = code;
606237f9
TC
236
237 if (error_cb)
c5cf7614
TC
238 error_cb(code, msg);
239}
240
241/*
242=item i_push_errorvf(int code, char const *fmt, va_list ap)
243
244Intended for use by higher level functions, takes a varargs pointer
245and a format to produce the finally pushed error message.
246
247=cut
248*/
249void 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
266A version of i_push_error() that does printf() like formating.
267
268=cut
269*/
270void 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);
606237f9
TC
275}
276
f0960b14
TC
277#ifdef IMAGER_I_FAILED
278#error "This isn't used and is untested"
279
606237f9
TC
280/*
281=item i_failed(char const *msg)
282
283Called by Imager code to indicate that a top-level has failed.
284
c5cf7614 285msg can be NULL, in which case no error is pushed.
606237f9
TC
286
287Calls the current failed callback, if any.
288
289Aborts the program with an error, if failures have been set to be fatal.
290
291Returns zero if it does not abort.
292
293=cut
294*/
c5cf7614 295int i_failed(int code, char const *msg) {
606237f9 296 if (msg)
c5cf7614 297 i_push_error(code, msg);
606237f9
TC
298 if (failed_cb)
299 failed_cb(error_stack + error_sp);
300 if (failures_fatal) {
c5cf7614
TC
301 int sp;
302 int total; /* total length of error messages */
303 char *full; /* full message for logging */
606237f9
TC
304 if (argv0)
305 fprintf(stderr, "%s: ", argv0);
306 fputs("error:\n", stderr);
c5cf7614
TC
307 sp = error_sp;
308 while (error_stack[sp].msg) {
a743c0a6 309 fprintf(stderr, " %s\n", error_stack[sp].msg);
c5cf7614
TC
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 }
a6c47345 318 full = mymalloc(total);
c5cf7614
TC
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, ": ");
606237f9 327 }
c5cf7614
TC
328 /* lose the extra ": " */
329 full[strlen(full)-2] = '\0';
330 m_fatal(EXIT_FAILURE, "%s", full);
606237f9
TC
331 }
332
333 return 0;
334}
335
f0960b14
TC
336#endif
337
606237f9
TC
338/*
339=back
340
341=head1 BUGS
342
343This interface isn't thread safe.
344
345=head1 AUTHOR
346
347Tony Cook <tony@develop-help.com>
348
349Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
350
351=cut
352*/