From: Tony Cook Date: Sun, 8 Apr 2001 13:29:36 +0000 (+0000) Subject: C level error handling code X-Git-Tag: Imager-0.48^2~756 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/606237f9e23c29da8a2f38d0ef31dd0fc1540c64 C level error handling code --- diff --git a/error.c b/error.c new file mode 100644 index 00000000..809911cb --- /dev/null +++ b/error.c @@ -0,0 +1,271 @@ +/* +=head1 NAME + +error.c - error reporting code for Imager + +=head1 SYNOPSIS + + // user code: + int new_fatal; // non-zero if errors are fatal + int old_fatal = i_set_failure_fatal(new_fatal); + i_set_argv0("name of your program"); + extern void error_cb(char const *); + i_error_cb old_ecb; + old_ecb = i_set_error_cb(error_cb); + i_failed_cb old_fcb; + extern void failed_cb(char **errors); + old_fcb = i_set_failed_cb(failed_cb); + if (!i_something(...)) { + char **errors = i_errors(); + } + + // imager code: + undef_int i_something(...) { + i_clear_error(); + if (!some_lower_func(...)) { + return i_failed("could not something"); + } + return 1; + } + undef_int some_lower_func(...) { + if (somethingelse_failed()) { + i_push_error("could not somethingelse"); + return 0; + } + return 1; + } + +=head1 DESCRIPTION + +This module provides the C level error handling functionality for +Imager. + +A few functions return or pass in a char **, this is a NULL terminated +list of pointers to error messages (which are NUL terminated strings, +just as it normal in C :). Even though these aren't passed as char +const * const * pointers, don't modify the strings or the pointers. + +The interface as currently defined isn't thread safe, unfortunately. + +This code uses Imager's mymalloc() for memory allocation, so out of +memory errors are I fatal. + +=head1 INTERFACE + +These functions form the interface that a user of Imager sees (from +C). The Perl level won't use all of this. + +=over + +=cut +*/ + +#include "image.h" +#include +#include + +/* we never actually use the last item - it's the NULL terminator */ +#define ERRSTK 20 +char *error_stack[ERRSTK]; +int error_sp = ERRSTK - 1; +/* we track the amount of space used each string, so we don't reallocate + space unless we need to. + This also means that a memory tracking library may see the memory + allocated for this as a leak. */ +int error_space[ERRSTK]; + +static i_error_cb error_cb; +static i_failed_cb failed_cb; +static int failures_fatal; +static char *argv0; + +/* +=item i_set_argv0(char const *program) + +Sets the name of the program to be displayed in fatal error messages. + +The simplest way to use this is just: + + i_set_argv0(argv[0]); + +when your program starts. +*/ +void i_set_argv0(char const *name) { + if (!name) + return; + char *dupl = mymalloc(strlen(name)+1); + strcpy(dupl, name); + if (argv0) + myfree(argv0); + argv0 = dupl; +} + +/* +=item i_set_failure_fatal(int failure_fatal) + +If failure_fatal is non-zero then any future failures will result in +Imager exiting your program with a message describing the failure. + +Returns the previous setting. + +=cut +*/ +int i_set_failures_fatal(int fatal) { + int old = failures_fatal; + failures_fatal = fatal; + + return old; +} + +/* +=item i_set_error_cb(i_error_cb) + +Sets a callback function that is called each time an error is pushed +onto the error stack. + +Returns the previous callback. + +i_set_failed_cb() is probably more useful. + +=cut +*/ +i_error_cb i_set_error_cb(i_error_cb cb) { + i_error_cb old = error_cb; + error_cb = cb; + + return old; +} + +/* +=item i_set_failed_cb(i_failed_cb cb) + +Sets a callback function that is called each time an Imager function +fails. + +Returns the previous callback. + +=cut +*/ +i_failed_cb i_set_failed_cb(i_failed_cb cb) { + i_failed_cb old = failed_cb; + failed_cb = cb; + + return old; +} + +/* +=item i_errors() + +Returns a pointer to the first element of an array of error messages, +terminated by a NULL pointer. The highest level message is first. + +=cut +*/ +char **i_errors() { + return error_stack + error_sp; +} + +/* +=back + +=head1 INTERNAL FUNCTIONS + +These functions are called by Imager to report errors through the +above interface. + +It may be desirable to have functions to mark the stack and reset to +the mark. + +=over + +=item i_clear_error() + +Called by any imager function before doing any other processing. + +=cut */ +void i_clear_error() { + error_sp = ERRSTK-1; +} + +/* +=item i_push_error(char const *msg) + +Called by an imager function to push an error message onto the stack. + +No message is pushed if the stack is full (since this means someone +forgot to call i_clear_error(), or that a function that doesn't do +error handling is calling function that does.). + +=cut +*/ +void i_push_error(char const *msg) { + int size = strlen(msg)+1; + + if (error_sp <= 0) + /* bad, bad programmer */ + return; + + --error_sp; + if (error_space[error_sp] < size) { + if (error_stack[error_sp]) + myfree(error_stack[error_sp]); + /* memory allocated on the following line is only ever release when + we need a bigger string */ + error_stack[error_sp] = mymalloc(size); + error_space[error_sp] = size; + } + strcpy(error_stack[error_sp], msg); + + if (error_cb) + error_cb(msg); +} + +/* +=item i_failed(char const *msg) + +Called by Imager code to indicate that a top-level has failed. + +msg can be NULL. + +Calls the current failed callback, if any. + +Aborts the program with an error, if failures have been set to be fatal. + +Returns zero if it does not abort. + +=cut +*/ +int i_failed(char const *msg) { + if (msg) + i_push_error(msg); + if (failed_cb) + failed_cb(error_stack + error_sp); + if (failures_fatal) { + if (argv0) + fprintf(stderr, "%s: ", argv0); + fputs("error:\n", stderr); + while (error_stack[error_sp]) { + fprintf(stderr, " %s\n", error_stack[error_sp]); + ++error_sp; + } + exit(EXIT_FAILURE); + } + + return 0; +} + +/* +=back + +=head1 BUGS + +This interface isn't thread safe. + +=head1 AUTHOR + +Tony Cook + +Stack concept by Arnar Mar Hrafnkelsson + +=cut +*/