WIP, more context changes
[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
9c852bc4 64#include "imageri.h"
606237f9
TC
65#include <stdio.h>
66#include <stdlib.h>
67
9c852bc4 68#if 0
606237f9
TC
69static i_error_cb error_cb;
70static i_failed_cb failed_cb;
71static int failures_fatal;
72static char *argv0;
606237f9
TC
73/*
74=item i_set_argv0(char const *program)
75
76Sets the name of the program to be displayed in fatal error messages.
77
78The simplest way to use this is just:
79
80 i_set_argv0(argv[0]);
81
82when your program starts.
83*/
84void i_set_argv0(char const *name) {
c5cf7614 85 char *dupl;
606237f9
TC
86 if (!name)
87 return;
f0960b14
TC
88 /* if the user has an existing string of MAXINT length then
89 the system is broken anyway */
90 dupl = mymalloc(strlen(name)+1); /* check 17jul05 tonyc */
606237f9
TC
91 strcpy(dupl, name);
92 if (argv0)
93 myfree(argv0);
94 argv0 = dupl;
95}
96
97/*
98=item i_set_failure_fatal(int failure_fatal)
99
100If failure_fatal is non-zero then any future failures will result in
101Imager exiting your program with a message describing the failure.
102
103Returns the previous setting.
104
105=cut
106*/
107int i_set_failures_fatal(int fatal) {
108 int old = failures_fatal;
109 failures_fatal = fatal;
110
111 return old;
112}
113
114/*
115=item i_set_error_cb(i_error_cb)
116
117Sets a callback function that is called each time an error is pushed
118onto the error stack.
119
120Returns the previous callback.
121
122i_set_failed_cb() is probably more useful.
123
124=cut
125*/
126i_error_cb i_set_error_cb(i_error_cb cb) {
127 i_error_cb old = error_cb;
128 error_cb = cb;
129
130 return old;
131}
132
133/*
134=item i_set_failed_cb(i_failed_cb cb)
135
136Sets a callback function that is called each time an Imager function
137fails.
138
139Returns the previous callback.
140
141=cut
142*/
143i_failed_cb i_set_failed_cb(i_failed_cb cb) {
144 i_failed_cb old = failed_cb;
145 failed_cb = cb;
146
147 return old;
148}
149
9c852bc4
TC
150#endif
151
606237f9
TC
152/*
153=item i_errors()
154
155Returns a pointer to the first element of an array of error messages,
156terminated by a NULL pointer. The highest level message is first.
157
158=cut
159*/
9c852bc4
TC
160i_errmsg *im_errors(im_context_t ctx) {
161 return ctx->error_stack + ctx->error_sp;
162}
163
164i_errmsg *i_errors(void) {
165 return im_errors(im_get_context());
606237f9
TC
166}
167
168/*
169=back
170
171=head1 INTERNAL FUNCTIONS
172
173These functions are called by Imager to report errors through the
174above interface.
175
176It may be desirable to have functions to mark the stack and reset to
177the mark.
178
179=over
180
181=item i_clear_error()
6cfee9d1 182=synopsis i_clear_error();
92bda632
TC
183=category Error handling
184
185Clears the error stack.
186
5715f7c3 187Called by any Imager function before doing any other processing.
606237f9 188
92bda632
TC
189=cut
190*/
9c852bc4
TC
191
192void
193im_clear_error(im_context_t ctx) {
cd4b0b20
TC
194#ifdef IMAGER_DEBUG_MALLOC
195 int i;
196
9c852bc4
TC
197 for (i = 0; i < IM_ERROR_COUNT; ++i) {
198 if (ctx->error_space[i]) {
199 myfree(ctx->error_stack[i].msg);
200 ctx->error_stack[i].msg = NULL;
201 ctx->error_space[i] = 0;
cd4b0b20
TC
202 }
203 }
204#endif
9c852bc4
TC
205 ctx->error_sp = IM_ERROR_COUNT-1;
206}
207
606237f9 208/*
faa9b3e7 209=item i_push_error(int code, char const *msg)
6cfee9d1
TC
210=synopsis i_push_error(0, "Yep, it's broken");
211=synopsis i_push_error(errno, "Error writing");
92bda632
TC
212=category Error handling
213
6cfee9d1 214Called by an Imager function to push an error message onto the stack.
606237f9
TC
215
216No message is pushed if the stack is full (since this means someone
217forgot to call i_clear_error(), or that a function that doesn't do
218error handling is calling function that does.).
219
220=cut
221*/
9c852bc4
TC
222void
223im_push_error(im_context_t ctx, int code, char const *msg) {
8d14daab 224 size_t size = strlen(msg)+1;
606237f9 225
9c852bc4 226 if (ctx->error_sp <= 0)
606237f9
TC
227 /* bad, bad programmer */
228 return;
229
9c852bc4
TC
230 --ctx->error_sp;
231 if (ctx->error_alloc[ctx->error_sp] < size) {
232 if (ctx->error_stack[ctx->error_sp].msg)
233 myfree(ctx->error_stack[ctx->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 */
9c852bc4
TC
238 ctx->error_stack[ctx->error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
239 ctx->error_alloc[ctx->error_sp] = size;
606237f9 240 }
9c852bc4
TC
241 strcpy(ctx->error_stack[ctx->error_sp].msg, msg);
242 ctx->error_stack[ctx->error_sp].code = code;
243}
606237f9 244
156699af
TC
245#if 0
246
9c852bc4
TC
247void
248i_push_error(int code, char const *msg) {
249 im_push_error(im_get_context(), code, msg);
c5cf7614
TC
250}
251
156699af
TC
252#endif
253
c5cf7614 254/*
5715f7c3 255=item i_push_errorvf(int C<code>, char const *C<fmt>, va_list C<ap>)
c5cf7614 256
92bda632
TC
257=category Error handling
258
c5cf7614
TC
259Intended for use by higher level functions, takes a varargs pointer
260and a format to produce the finally pushed error message.
261
6cfee9d1
TC
262Does not support perl specific format codes.
263
c5cf7614
TC
264=cut
265*/
9c852bc4
TC
266void
267im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list ap) {
c5cf7614 268 char buf[1024];
86c8d19a
TC
269#if defined(IMAGER_VSNPRINTF)
270 vsnprintf(buf, sizeof(buf), fmt, ap);
271#elif defined(_MSC_VER)
c5cf7614
TC
272 _vsnprintf(buf, sizeof(buf), fmt, ap);
273#else
274 /* is there a way to detect vsnprintf()?
275 for this and other functions we need some mechanism to handle
276 detection (like perl's Configure, or autoconf)
277 */
278 vsprintf(buf, fmt, ap);
279#endif
9c852bc4
TC
280 im_push_error(ctx, code, buf);
281}
282
283void
284i_push_errorvf(int code, char const *fmt, va_list ap) {
285 im_push_errorvf(im_get_context(), code, fmt, ap);
c5cf7614
TC
286}
287
288/*
289=item i_push_errorf(int code, char const *fmt, ...)
6cfee9d1 290=synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
92bda632
TC
291=category Error handling
292
5715f7c3 293A version of i_push_error() that does printf() like formatting.
c5cf7614 294
6cfee9d1
TC
295Does not support perl specific format codes.
296
c5cf7614
TC
297=cut
298*/
9c852bc4
TC
299void
300i_push_errorf(int code, char const *fmt, ...) {
c5cf7614
TC
301 va_list ap;
302 va_start(ap, fmt);
303 i_push_errorvf(code, fmt, ap);
304 va_end(ap);
606237f9
TC
305}
306
9c852bc4
TC
307void
308im_push_errorf(im_context_t ctx, int code, char const *fmt, ...) {
309 va_list ap;
310 va_start(ap, fmt);
311 im_push_errorvf(ctx, code, fmt, ap);
312 va_end(ap);
313}
314
f0960b14
TC
315#ifdef IMAGER_I_FAILED
316#error "This isn't used and is untested"
317
606237f9
TC
318/*
319=item i_failed(char const *msg)
320
321Called by Imager code to indicate that a top-level has failed.
322
c5cf7614 323msg can be NULL, in which case no error is pushed.
606237f9
TC
324
325Calls the current failed callback, if any.
326
327Aborts the program with an error, if failures have been set to be fatal.
328
329Returns zero if it does not abort.
330
331=cut
332*/
c5cf7614 333int i_failed(int code, char const *msg) {
606237f9 334 if (msg)
c5cf7614 335 i_push_error(code, msg);
606237f9
TC
336 if (failed_cb)
337 failed_cb(error_stack + error_sp);
338 if (failures_fatal) {
c5cf7614 339 int sp;
8d14daab 340 size_t total; /* total length of error messages */
c5cf7614 341 char *full; /* full message for logging */
606237f9
TC
342 if (argv0)
343 fprintf(stderr, "%s: ", argv0);
344 fputs("error:\n", stderr);
c5cf7614
TC
345 sp = error_sp;
346 while (error_stack[sp].msg) {
a743c0a6 347 fprintf(stderr, " %s\n", error_stack[sp].msg);
c5cf7614
TC
348 ++sp;
349 }
350 /* we want to log the error too, build an error message to hand to
b1e96952 351 i_fatal() */
c5cf7614
TC
352 total = 1; /* remember the NUL */
353 for (sp = error_sp; error_stack[sp].msg; ++sp) {
8d14daab
TC
354 size_t new_total += strlen(error_stack[sp].msg) + 2;
355 if (new_total < total) {
356 /* overflow, somehow */
357 break;
358 }
c5cf7614 359 }
a6c47345 360 full = mymalloc(total);
c5cf7614
TC
361 if (!full) {
362 /* just quit, at least it's on stderr */
363 exit(EXIT_FAILURE);
364 }
365 *full = 0;
366 for (sp = error_sp; error_stack[sp].msg; ++sp) {
367 strcat(full, error_stack[sp].msg);
368 strcat(full, ": ");
606237f9 369 }
c5cf7614
TC
370 /* lose the extra ": " */
371 full[strlen(full)-2] = '\0';
b1e96952 372 i_fatal(EXIT_FAILURE, "%s", full);
606237f9
TC
373 }
374
375 return 0;
376}
377
f0960b14
TC
378#endif
379
b3afeed5
TC
380/*
381=item im_assert_fail(file, line, message)
382
383Called when an im_assert() assertion fails.
384
385=cut
386*/
387
388void
389im_assert_fail(char const *file, int line, char const *message) {
390 fprintf(stderr, "Assertion failed line %d file %s: %s\n",
391 line, file, message);
9c852bc4 392 abort();
b3afeed5
TC
393}
394
606237f9
TC
395/*
396=back
397
398=head1 BUGS
399
400This interface isn't thread safe.
401
402=head1 AUTHOR
403
404Tony Cook <tony@develop-help.com>
405
406Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
407
408=cut
409*/