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