Skip to content
Newer
Older
100644 333 lines (256 sloc) 7.11 KB
606237f C level error handling code
Tony Cook authored
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
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
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.
606237f C level error handling code
Tony Cook authored
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 "image.h"
65 #include <stdio.h>
66 #include <stdlib.h>
67
68 /* we never actually use the last item - it's the NULL terminator */
69 #define ERRSTK 20
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
70 i_errmsg error_stack[ERRSTK];
606237f C level error handling code
Tony Cook authored
71 int error_sp = ERRSTK - 1;
72 /* we track the amount of space used each string, so we don't reallocate
73 space unless we need to.
74 This also means that a memory tracking library may see the memory
75 allocated for this as a leak. */
76 int error_space[ERRSTK];
77
78 static i_error_cb error_cb;
79 static i_failed_cb failed_cb;
80 static int failures_fatal;
81 static char *argv0;
82
83 /*
84 =item i_set_argv0(char const *program)
85
86 Sets the name of the program to be displayed in fatal error messages.
87
88 The simplest way to use this is just:
89
90 i_set_argv0(argv[0]);
91
92 when your program starts.
93 */
94 void i_set_argv0(char const *name) {
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
95 char *dupl;
606237f C level error handling code
Tony Cook authored
96 if (!name)
97 return;
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
98 dupl = mymalloc(strlen(name)+1);
606237f C level error handling code
Tony Cook authored
99 strcpy(dupl, name);
100 if (argv0)
101 myfree(argv0);
102 argv0 = dupl;
103 }
104
105 /*
106 =item i_set_failure_fatal(int failure_fatal)
107
108 If failure_fatal is non-zero then any future failures will result in
109 Imager exiting your program with a message describing the failure.
110
111 Returns the previous setting.
112
113 =cut
114 */
115 int i_set_failures_fatal(int fatal) {
116 int old = failures_fatal;
117 failures_fatal = fatal;
118
119 return old;
120 }
121
122 /*
123 =item i_set_error_cb(i_error_cb)
124
125 Sets a callback function that is called each time an error is pushed
126 onto the error stack.
127
128 Returns the previous callback.
129
130 i_set_failed_cb() is probably more useful.
131
132 =cut
133 */
134 i_error_cb i_set_error_cb(i_error_cb cb) {
135 i_error_cb old = error_cb;
136 error_cb = cb;
137
138 return old;
139 }
140
141 /*
142 =item i_set_failed_cb(i_failed_cb cb)
143
144 Sets a callback function that is called each time an Imager function
145 fails.
146
147 Returns the previous callback.
148
149 =cut
150 */
151 i_failed_cb i_set_failed_cb(i_failed_cb cb) {
152 i_failed_cb old = failed_cb;
153 failed_cb = cb;
154
155 return old;
156 }
157
158 /*
159 =item i_errors()
160
161 Returns a pointer to the first element of an array of error messages,
162 terminated by a NULL pointer. The highest level message is first.
163
164 =cut
165 */
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
166 i_errmsg *i_errors() {
606237f C level error handling code
Tony Cook authored
167 return error_stack + error_sp;
168 }
169
170 /*
171 =back
172
173 =head1 INTERNAL FUNCTIONS
174
175 These functions are called by Imager to report errors through the
176 above interface.
177
178 It may be desirable to have functions to mark the stack and reset to
179 the mark.
180
181 =over
182
183 =item i_clear_error()
184
185 Called by any imager function before doing any other processing.
186
187 =cut */
188 void i_clear_error() {
189 error_sp = ERRSTK-1;
190 }
191
192 /*
193 =item i_push_error(char const *msg)
194
195 Called by an imager function to push an error message onto the stack.
196
197 No message is pushed if the stack is full (since this means someone
198 forgot to call i_clear_error(), or that a function that doesn't do
199 error handling is calling function that does.).
200
201 =cut
202 */
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
203 void i_push_error(int code, char const *msg) {
606237f C level error handling code
Tony Cook authored
204 int size = strlen(msg)+1;
205
206 if (error_sp <= 0)
207 /* bad, bad programmer */
208 return;
209
210 --error_sp;
211 if (error_space[error_sp] < size) {
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
212 if (error_stack[error_sp].msg)
213 myfree(error_stack[error_sp].msg);
606237f C level error handling code
Tony Cook authored
214 /* memory allocated on the following line is only ever release when
215 we need a bigger string */
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
216 error_stack[error_sp].msg = mymalloc(size);
606237f C level error handling code
Tony Cook authored
217 error_space[error_sp] = size;
218 }
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
219 strcpy(error_stack[error_sp].msg, msg);
220 error_stack[error_sp].code = code;
606237f C level error handling code
Tony Cook authored
221
222 if (error_cb)
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
223 error_cb(code, msg);
224 }
225
226 /*
227 =item i_push_errorvf(int code, char const *fmt, va_list ap)
228
229 Intended for use by higher level functions, takes a varargs pointer
230 and a format to produce the finally pushed error message.
231
232 =cut
233 */
234 void i_push_errorvf(int code, char const *fmt, va_list ap) {
235 char buf[1024];
236 #if defined(_MSC_VER)
237 _vsnprintf(buf, sizeof(buf), fmt, ap);
238 #else
239 /* is there a way to detect vsnprintf()?
240 for this and other functions we need some mechanism to handle
241 detection (like perl's Configure, or autoconf)
242 */
243 vsprintf(buf, fmt, ap);
244 #endif
245 i_push_error(code, buf);
246 }
247
248 /*
249 =item i_push_errorf(int code, char const *fmt, ...)
250
251 A version of i_push_error() that does printf() like formating.
252
253 =cut
254 */
255 void i_push_errorf(int code, char const *fmt, ...) {
256 va_list ap;
257 va_start(ap, fmt);
258 i_push_errorvf(code, fmt, ap);
259 va_end(ap);
606237f C level error handling code
Tony Cook authored
260 }
261
262 /*
263 =item i_failed(char const *msg)
264
265 Called by Imager code to indicate that a top-level has failed.
266
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
267 msg can be NULL, in which case no error is pushed.
606237f C level error handling code
Tony Cook authored
268
269 Calls the current failed callback, if any.
270
271 Aborts the program with an error, if failures have been set to be fatal.
272
273 Returns zero if it does not abort.
274
275 =cut
276 */
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
277 int i_failed(int code, char const *msg) {
606237f C level error handling code
Tony Cook authored
278 if (msg)
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
279 i_push_error(code, msg);
606237f C level error handling code
Tony Cook authored
280 if (failed_cb)
281 failed_cb(error_stack + error_sp);
282 if (failures_fatal) {
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
283 int sp;
284 int total; /* total length of error messages */
285 char *full; /* full message for logging */
606237f C level error handling code
Tony Cook authored
286 if (argv0)
287 fprintf(stderr, "%s: ", argv0);
288 fputs("error:\n", stderr);
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
289 sp = error_sp;
290 while (error_stack[sp].msg) {
291 fprintf(stderr, " %s\n", error_stack[sp]);
292 ++sp;
293 }
294 /* we want to log the error too, build an error message to hand to
295 m_fatal() */
296 total = 1; /* remember the NUL */
297 for (sp = error_sp; error_stack[sp].msg; ++sp) {
298 total += strlen(error_stack[sp].msg) + 2;
299 }
300 full = malloc(total);
301 if (!full) {
302 /* just quit, at least it's on stderr */
303 exit(EXIT_FAILURE);
304 }
305 *full = 0;
306 for (sp = error_sp; error_stack[sp].msg; ++sp) {
307 strcat(full, error_stack[sp].msg);
308 strcat(full, ": ");
606237f C level error handling code
Tony Cook authored
309 }
c5cf761 added error codes and calls to m_fatal() on fatal errors
Tony Cook authored
310 /* lose the extra ": " */
311 full[strlen(full)-2] = '\0';
312 m_fatal(EXIT_FAILURE, "%s", full);
606237f C level error handling code
Tony Cook authored
313 }
314
315 return 0;
316 }
317
318 /*
319 =back
320
321 =head1 BUGS
322
323 This interface isn't thread safe.
324
325 =head1 AUTHOR
326
327 Tony Cook <tony@develop-help.com>
328
329 Stack concept by Arnar Mar Hrafnkelsson <addi@umich.edu>
330
331 =cut
332 */
Something went wrong with that request. Please try again.