C level error handling code
[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 a char **, this is a NULL terminated
44 list of pointers to error messages (which are NUL terminated strings,
45 just as it normal in C :).  Even though these aren't passed as char
46 const * const * pointers, don't modify the strings or the pointers.
47
48 The interface as currently defined isn't thread safe, unfortunately.
49
50 This code uses Imager's mymalloc() for memory allocation, so out of
51 memory errors are I<always> fatal.
52
53 =head1 INTERFACE
54
55 These functions form the interface that a user of Imager sees (from
56 C).  The Perl level won't use all of this.
57
58 =over
59
60 =cut
61 */
62
63 #include "image.h"
64 #include <stdio.h>
65 #include <stdlib.h>
66
67 /* we never actually use the last item - it's the NULL terminator */
68 #define ERRSTK 20
69 char *error_stack[ERRSTK];
70 int error_sp = ERRSTK - 1;
71 /* we track the amount of space used each string, so we don't reallocate 
72    space unless we need to.
73    This also means that a memory tracking library may see the memory 
74    allocated for this as a leak. */
75 int error_space[ERRSTK];
76
77 static i_error_cb error_cb;
78 static i_failed_cb failed_cb;
79 static int failures_fatal;
80 static char *argv0;
81
82 /*
83 =item i_set_argv0(char const *program)
84
85 Sets the name of the program to be displayed in fatal error messages.
86
87 The simplest way to use this is just:
88
89   i_set_argv0(argv[0]);
90
91 when your program starts.
92 */
93 void i_set_argv0(char const *name) {
94   if (!name)
95     return;
96   char *dupl = mymalloc(strlen(name)+1);
97   strcpy(dupl, name);
98   if (argv0)
99     myfree(argv0);
100   argv0 = dupl;
101 }
102
103 /*
104 =item i_set_failure_fatal(int failure_fatal)
105
106 If failure_fatal is non-zero then any future failures will result in
107 Imager exiting your program with a message describing the failure.
108
109 Returns the previous setting.
110
111 =cut
112 */
113 int i_set_failures_fatal(int fatal) {
114   int old = failures_fatal;
115   failures_fatal = fatal;
116
117   return old;
118 }
119
120 /*
121 =item i_set_error_cb(i_error_cb)
122
123 Sets a callback function that is called each time an error is pushed
124 onto the error stack.
125
126 Returns the previous callback.
127
128 i_set_failed_cb() is probably more useful.
129
130 =cut
131 */
132 i_error_cb i_set_error_cb(i_error_cb cb) {
133   i_error_cb old = error_cb;
134   error_cb = cb;
135
136   return old;
137 }
138
139 /*
140 =item i_set_failed_cb(i_failed_cb cb)
141
142 Sets a callback function that is called each time an Imager function
143 fails.
144
145 Returns the previous callback.
146
147 =cut
148 */
149 i_failed_cb i_set_failed_cb(i_failed_cb cb) {
150   i_failed_cb old = failed_cb;
151   failed_cb = cb;
152
153   return old;
154 }
155
156 /*
157 =item i_errors()
158
159 Returns a pointer to the first element of an array of error messages,
160 terminated by a NULL pointer.  The highest level message is first.
161
162 =cut
163 */
164 char **i_errors() {
165   return error_stack + error_sp;
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
183 Called by any imager function before doing any other processing.
184
185 =cut */
186 void i_clear_error() {
187   error_sp = ERRSTK-1;
188 }
189
190 /*
191 =item i_push_error(char const *msg)
192
193 Called by an imager function to push an error message onto the stack.
194
195 No message is pushed if the stack is full (since this means someone
196 forgot to call i_clear_error(), or that a function that doesn't do
197 error handling is calling function that does.).
198
199 =cut
200 */
201 void i_push_error(char const *msg) {
202   int size = strlen(msg)+1;
203
204   if (error_sp <= 0)
205     /* bad, bad programmer */
206     return;
207
208   --error_sp;
209   if (error_space[error_sp] < size) {
210     if (error_stack[error_sp])
211       myfree(error_stack[error_sp]);
212     /* memory allocated on the following line is only ever release when 
213        we need a bigger string */
214     error_stack[error_sp] = mymalloc(size);
215     error_space[error_sp] = size;
216   }
217   strcpy(error_stack[error_sp], msg);
218
219   if (error_cb)
220     error_cb(msg);
221 }
222
223 /*
224 =item i_failed(char const *msg)
225
226 Called by Imager code to indicate that a top-level has failed.
227
228 msg can be NULL.
229
230 Calls the current failed callback, if any.
231
232 Aborts the program with an error, if failures have been set to be fatal.
233
234 Returns zero if it does not abort.
235
236 =cut
237 */
238 int i_failed(char const *msg) {
239   if (msg)
240     i_push_error(msg);
241   if (failed_cb)
242     failed_cb(error_stack + error_sp);
243   if (failures_fatal) {
244     if (argv0)
245       fprintf(stderr, "%s: ", argv0);
246     fputs("error:\n", stderr);
247     while (error_stack[error_sp]) {
248       fprintf(stderr, " %s\n", error_stack[error_sp]);
249       ++error_sp;
250     }
251     exit(EXIT_FAILURE);
252   }
253
254   return 0;
255 }
256
257 /*
258 =back
259
260 =head1 BUGS
261
262 This interface isn't thread safe.
263
264 =head1 AUTHOR
265
266 Tony Cook <tony@develop-help.com>
267
268 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
269
270 =cut
271 */