]> git.imager.perl.org - imager.git/blob - error.c
allow imcover to use a non-default perl
[imager.git] / error.c
1 /*
2 =head1 NAME
3
4 error.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
40 This module provides the C level error handling functionality for
41 Imager.
42
43 A few functions return or pass in an i_errmsg *, this is list of error
44 structures, terminated by an entry with a NULL msg value, each of
45 which contains a msg and an error code. Even though these aren't
46 passed as i_errmsg const * pointers, don't modify the strings
47 or the pointers.
48
49 The interface as currently defined isn't thread safe, unfortunately.
50
51 This code uses Imager's mymalloc() for memory allocation, so out of
52 memory errors are I<always> fatal.
53
54 =head1 INTERFACE
55
56 These functions form the interface that a user of Imager sees (from
57 C).  The Perl level won't use all of this.
58
59 =over
60
61 =cut
62 */
63
64 #include "imageri.h"
65 #include <stdio.h>
66 #include <stdlib.h>
67
68 /*
69 =item im_errors(ctx)
70 =synopsis i_errmsg *errors = im_errors(aIMCTX);
71 =synopsis i_errmsg *errors = i_errors();
72
73 Returns a pointer to the first element of an array of error messages,
74 terminated by a NULL pointer.  The highest level message is first.
75
76 Also callable as C<i_errors()>.
77
78 =cut
79 */
80 i_errmsg *im_errors(im_context_t ctx) {
81   return ctx->error_stack + ctx->error_sp;
82 }
83
84 /*
85 =back
86
87 =head1 INTERNAL FUNCTIONS
88
89 These functions are called by Imager to report errors through the
90 above interface.
91
92 It may be desirable to have functions to mark the stack and reset to
93 the mark.
94
95 =over
96
97 =item im_clear_error(ctx)
98 X<im_clear_error API>X<i_clear_error API>
99 =synopsis im_clear_error(aIMCTX);
100 =synopsis i_clear_error();
101 =category Error handling
102
103 Clears the error stack.
104
105 Called by any Imager function before doing any other processing.
106
107 Also callable as C<i_clear_error()>.
108
109 =cut
110 */
111
112 void
113 im_clear_error(im_context_t ctx) {
114 #ifdef IMAGER_DEBUG_MALLOC
115   int i;
116
117   for (i = 0; i < IM_ERROR_COUNT; ++i) {
118     if (ctx->error_space[i]) {
119       myfree(ctx->error_stack[i].msg);
120       ctx->error_stack[i].msg = NULL;
121       ctx->error_space[i] = 0;
122     }
123   }
124 #endif
125   ctx->error_sp = IM_ERROR_COUNT-1;
126 }
127
128 /*
129 =item im_push_error(ctx, code, message)
130 X<im_push_error API>X<i_push_error API>
131 =synopsis i_push_error(0, "Yep, it's broken");
132 =synopsis i_push_error(errno, "Error writing");
133 =synopsis im_push_error(aIMCTX, 0, "Something is wrong");
134 =category Error handling
135
136 Called by an Imager function to push an error message onto the stack.
137
138 No message is pushed if the stack is full (since this means someone
139 forgot to call i_clear_error(), or that a function that doesn't do
140 error handling is calling function that does.).
141
142 =cut
143 */
144 void
145 im_push_error(im_context_t ctx, int code, char const *msg) {
146   size_t size = strlen(msg)+1;
147
148   if (ctx->error_sp <= 0)
149     /* bad, bad programmer */
150     return;
151
152   --ctx->error_sp;
153   if (ctx->error_alloc[ctx->error_sp] < size) {
154     if (ctx->error_stack[ctx->error_sp].msg)
155       myfree(ctx->error_stack[ctx->error_sp].msg);
156     /* memory allocated on the following line is only ever released when 
157        we need a bigger string */
158     /* size is size (len+1) of an existing string, overflow would mean
159        the system is broken anyway */
160     ctx->error_stack[ctx->error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
161     ctx->error_alloc[ctx->error_sp] = size;
162   }
163   strcpy(ctx->error_stack[ctx->error_sp].msg, msg);
164   ctx->error_stack[ctx->error_sp].code = code;
165 }
166
167 /*
168 =item im_push_errorvf(ctx, code, format, args)
169 X<im_push_error_vf API>X<i_push_errorvf API>
170 =synopsis va_args args;
171 =synopsis va_start(args, lastarg);
172 =synopsis im_push_errorvf(ctx, code, format, args);
173 =category Error handling
174
175 Intended for use by higher level functions, takes a varargs pointer
176 and a format to produce the finally pushed error message.
177
178 Does not support perl specific format codes.
179
180 Also callable as C<i_push_errorvf(code, format, args)>
181
182 =cut
183 */
184 void 
185 im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list ap) {
186   char buf[1024];
187 #if defined(IMAGER_VSNPRINTF)
188   vsnprintf(buf, sizeof(buf), fmt, ap);
189 #elif defined(_MSC_VER)
190   _vsnprintf(buf, sizeof(buf), fmt, ap);
191 #else
192   /* is there a way to detect vsnprintf()? 
193      for this and other functions we need some mechanism to handle 
194      detection (like perl's Configure, or autoconf)
195    */
196   vsprintf(buf, fmt, ap);
197 #endif
198   im_push_error(ctx, code, buf);
199 }
200
201 /*
202 =item i_push_errorf(int code, char const *fmt, ...)
203 =synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
204 =category Error handling
205
206 A version of i_push_error() that does printf() like formatting.
207
208 Does not support perl specific format codes.
209
210 =cut
211 */
212 void
213 i_push_errorf(int code, char const *fmt, ...) {
214   va_list ap;
215   va_start(ap, fmt);
216   i_push_errorvf(code, fmt, ap);
217   va_end(ap);
218 }
219
220 /*
221 =item im_push_errorf(ctx, code, char const *fmt, ...)
222 =synopsis im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno);
223 =category Error handling
224
225 A version of im_push_error() that does printf() like formatting.
226
227 Does not support perl specific format codes.
228
229 =cut
230 */
231 void
232 im_push_errorf(im_context_t ctx, int code, char const *fmt, ...) {
233   va_list ap;
234   va_start(ap, fmt);
235   im_push_errorvf(ctx, code, fmt, ap);
236   va_end(ap);
237 }
238
239 #ifdef IMAGER_I_FAILED
240 #error "This isn't used and is untested"
241
242 /*
243 =item i_failed(char const *msg)
244
245 Called by Imager code to indicate that a top-level has failed.
246
247 msg can be NULL, in which case no error is pushed.
248
249 Calls the current failed callback, if any.
250
251 Aborts the program with an error, if failures have been set to be fatal.
252
253 Returns zero if it does not abort.
254
255 =cut
256 */
257 int i_failed(int code, char const *msg) {
258   if (msg)
259     i_push_error(code, msg);
260   if (failed_cb)
261     failed_cb(error_stack + error_sp);
262   if (failures_fatal) {
263     int sp;
264     size_t total; /* total length of error messages */
265     char *full; /* full message for logging */
266     if (argv0)
267       fprintf(stderr, "%s: ", argv0);
268     fputs("error:\n", stderr);
269     sp = error_sp;
270     while (error_stack[sp].msg) {
271       fprintf(stderr, " %s\n", error_stack[sp].msg);
272       ++sp;
273     }
274     /* we want to log the error too, build an error message to hand to
275        i_fatal() */
276     total = 1; /* remember the NUL */
277     for (sp = error_sp; error_stack[sp].msg; ++sp) {
278       size_t new_total += strlen(error_stack[sp].msg) + 2;
279       if (new_total < total) {
280         /* overflow, somehow */
281         break;
282       }
283     }
284     full = mymalloc(total);
285     if (!full) {
286       /* just quit, at least it's on stderr */
287       exit(EXIT_FAILURE);
288     }
289     *full = 0;
290     for (sp = error_sp; error_stack[sp].msg; ++sp) {
291       strcat(full, error_stack[sp].msg);
292       strcat(full, ": ");
293     }
294     /* lose the extra ": " */
295     full[strlen(full)-2] = '\0';
296     i_fatal(EXIT_FAILURE, "%s", full);
297   }
298
299   return 0;
300 }
301
302 #endif
303
304 #ifdef IM_ASSERT
305
306 /*
307 =item im_assert_fail(file, line, message)
308
309 Called when an im_assert() assertion fails.
310
311 Only available when Imager is built with assertions.
312
313 =cut
314 */
315
316 void
317 im_assert_fail(char const *file, int line, char const *message) {
318   fprintf(stderr, "Assertion failed line %d file %s: %s\n", 
319           line, file, message);
320   abort();
321 }
322
323 #endif
324
325 /*
326 =back
327
328 =head1 BUGS
329
330 This interface isn't thread safe.
331
332 =head1 AUTHOR
333
334 Tony Cook <tony@develop-help.com>
335
336 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
337
338 =cut
339 */