1f89bb4229bbee351a2fd6b721aac0b866d0fe58
[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 i_clear_error()
98 =synopsis i_clear_error();
99 =category Error handling
100
101 Clears the error stack.
102
103 Called by any Imager function before doing any other processing.
104
105 =cut
106 */
107
108 void
109 im_clear_error(im_context_t ctx) {
110 #ifdef IMAGER_DEBUG_MALLOC
111   int i;
112
113   for (i = 0; i < IM_ERROR_COUNT; ++i) {
114     if (ctx->error_space[i]) {
115       myfree(ctx->error_stack[i].msg);
116       ctx->error_stack[i].msg = NULL;
117       ctx->error_space[i] = 0;
118     }
119   }
120 #endif
121   ctx->error_sp = IM_ERROR_COUNT-1;
122 }
123
124 /*
125 =item i_push_error(int code, char const *msg)
126 =synopsis i_push_error(0, "Yep, it's broken");
127 =synopsis i_push_error(errno, "Error writing");
128 =category Error handling
129
130 Called by an Imager function to push an error message onto the stack.
131
132 No message is pushed if the stack is full (since this means someone
133 forgot to call i_clear_error(), or that a function that doesn't do
134 error handling is calling function that does.).
135
136 =cut
137 */
138 void
139 im_push_error(im_context_t ctx, int code, char const *msg) {
140   size_t size = strlen(msg)+1;
141
142   if (ctx->error_sp <= 0)
143     /* bad, bad programmer */
144     return;
145
146   --ctx->error_sp;
147   if (ctx->error_alloc[ctx->error_sp] < size) {
148     if (ctx->error_stack[ctx->error_sp].msg)
149       myfree(ctx->error_stack[ctx->error_sp].msg);
150     /* memory allocated on the following line is only ever released when 
151        we need a bigger string */
152     /* size is size (len+1) of an existing string, overflow would mean
153        the system is broken anyway */
154     ctx->error_stack[ctx->error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
155     ctx->error_alloc[ctx->error_sp] = size;
156   }
157   strcpy(ctx->error_stack[ctx->error_sp].msg, msg);
158   ctx->error_stack[ctx->error_sp].code = code;
159 }
160
161 /*
162 =item i_push_errorvf(int C<code>, char const *C<fmt>, va_list C<ap>)
163
164 =category Error handling
165
166 Intended for use by higher level functions, takes a varargs pointer
167 and a format to produce the finally pushed error message.
168
169 Does not support perl specific format codes.
170
171 =cut
172 */
173 void 
174 im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list ap) {
175   char buf[1024];
176 #if defined(IMAGER_VSNPRINTF)
177   vsnprintf(buf, sizeof(buf), fmt, ap);
178 #elif defined(_MSC_VER)
179   _vsnprintf(buf, sizeof(buf), fmt, ap);
180 #else
181   /* is there a way to detect vsnprintf()? 
182      for this and other functions we need some mechanism to handle 
183      detection (like perl's Configure, or autoconf)
184    */
185   vsprintf(buf, fmt, ap);
186 #endif
187   im_push_error(ctx, code, buf);
188 }
189
190 void
191 (i_push_errorvf)(int code, char const *fmt, va_list ap) {
192   im_push_errorvf(im_get_context(), code, fmt, ap);
193 }
194
195 /*
196 =item i_push_errorf(int code, char const *fmt, ...)
197 =synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
198 =category Error handling
199
200 A version of i_push_error() that does printf() like formatting.
201
202 Does not support perl specific format codes.
203
204 =cut
205 */
206 void
207 i_push_errorf(int code, char const *fmt, ...) {
208   va_list ap;
209   va_start(ap, fmt);
210   i_push_errorvf(code, fmt, ap);
211   va_end(ap);
212 }
213
214 void
215 im_push_errorf(im_context_t ctx, int code, char const *fmt, ...) {
216   va_list ap;
217   va_start(ap, fmt);
218   im_push_errorvf(ctx, code, fmt, ap);
219   va_end(ap);
220 }
221
222 #ifdef IMAGER_I_FAILED
223 #error "This isn't used and is untested"
224
225 /*
226 =item i_failed(char const *msg)
227
228 Called by Imager code to indicate that a top-level has failed.
229
230 msg can be NULL, in which case no error is pushed.
231
232 Calls the current failed callback, if any.
233
234 Aborts the program with an error, if failures have been set to be fatal.
235
236 Returns zero if it does not abort.
237
238 =cut
239 */
240 int i_failed(int code, char const *msg) {
241   if (msg)
242     i_push_error(code, msg);
243   if (failed_cb)
244     failed_cb(error_stack + error_sp);
245   if (failures_fatal) {
246     int sp;
247     size_t total; /* total length of error messages */
248     char *full; /* full message for logging */
249     if (argv0)
250       fprintf(stderr, "%s: ", argv0);
251     fputs("error:\n", stderr);
252     sp = error_sp;
253     while (error_stack[sp].msg) {
254       fprintf(stderr, " %s\n", error_stack[sp].msg);
255       ++sp;
256     }
257     /* we want to log the error too, build an error message to hand to
258        i_fatal() */
259     total = 1; /* remember the NUL */
260     for (sp = error_sp; error_stack[sp].msg; ++sp) {
261       size_t new_total += strlen(error_stack[sp].msg) + 2;
262       if (new_total < total) {
263         /* overflow, somehow */
264         break;
265       }
266     }
267     full = mymalloc(total);
268     if (!full) {
269       /* just quit, at least it's on stderr */
270       exit(EXIT_FAILURE);
271     }
272     *full = 0;
273     for (sp = error_sp; error_stack[sp].msg; ++sp) {
274       strcat(full, error_stack[sp].msg);
275       strcat(full, ": ");
276     }
277     /* lose the extra ": " */
278     full[strlen(full)-2] = '\0';
279     i_fatal(EXIT_FAILURE, "%s", full);
280   }
281
282   return 0;
283 }
284
285 #endif
286
287 /*
288 =item im_assert_fail(file, line, message)
289
290 Called when an im_assert() assertion fails.
291
292 =cut
293 */
294
295 void
296 im_assert_fail(char const *file, int line, char const *message) {
297   fprintf(stderr, "Assertion failed line %d file %s: %s\n", 
298           line, file, message);
299   abort();
300 }
301
302 /*
303 =back
304
305 =head1 BUGS
306
307 This interface isn't thread safe.
308
309 =head1 AUTHOR
310
311 Tony Cook <tony@develop-help.com>
312
313 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
314
315 =cut
316 */