X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/c5cf76149d2325d53cc92434f8478a8db2509b39..e1c0692925:/error.c diff --git a/error.c b/error.c index 0edce898..03a271ca 100644 --- a/error.c +++ b/error.c @@ -61,110 +61,24 @@ C). The Perl level won't use all of this. =cut */ -#include "image.h" +#include "imageri.h" #include #include -/* we never actually use the last item - it's the NULL terminator */ -#define ERRSTK 20 -i_errmsg 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) { - char *dupl; - if (!name) - return; - 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() +=item im_errors(ctx) +=synopsis i_errmsg *errors = im_errors(aIMCTX); +=synopsis i_errmsg *errors = 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. +Also callable as C. + =cut */ -i_errmsg *i_errors() { - return error_stack + error_sp; +i_errmsg *im_errors(im_context_t ctx) { + return ctx->error_stack + ctx->error_sp; } /* @@ -180,19 +94,46 @@ the mark. =over -=item i_clear_error() +=item im_clear_error(ctx) +XX +=synopsis im_clear_error(aIMCTX); +=synopsis i_clear_error(); +=category Error handling -Called by any imager function before doing any other processing. +Clears the error stack. -=cut */ -void i_clear_error() { - error_sp = ERRSTK-1; +Called by any Imager function before doing any other processing. + +Also callable as C. + +=cut +*/ + +void +im_clear_error(im_context_t ctx) { +#ifdef IMAGER_DEBUG_MALLOC + int i; + + for (i = 0; i < IM_ERROR_COUNT; ++i) { + if (ctx->error_space[i]) { + myfree(ctx->error_stack[i].msg); + ctx->error_stack[i].msg = NULL; + ctx->error_space[i] = 0; + } + } +#endif + ctx->error_sp = IM_ERROR_COUNT-1; } /* -=item i_push_error(char const *msg) +=item im_push_error(ctx, code, message) +XX +=synopsis i_push_error(0, "Yep, it's broken"); +=synopsis i_push_error(errno, "Error writing"); +=synopsis im_push_error(aIMCTX, 0, "Something is wrong"); +=category Error handling -Called by an imager function to push an error message onto the stack. +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 @@ -200,40 +141,52 @@ error handling is calling function that does.). =cut */ -void i_push_error(int code, char const *msg) { - int size = strlen(msg)+1; +void +im_push_error(im_context_t ctx, int code, char const *msg) { + size_t size = strlen(msg)+1; - if (error_sp <= 0) + if (ctx->error_sp <= 0) /* bad, bad programmer */ return; - --error_sp; - if (error_space[error_sp] < size) { - if (error_stack[error_sp].msg) - myfree(error_stack[error_sp].msg); - /* memory allocated on the following line is only ever release when + --ctx->error_sp; + if (ctx->error_alloc[ctx->error_sp] < size) { + if (ctx->error_stack[ctx->error_sp].msg) + myfree(ctx->error_stack[ctx->error_sp].msg); + /* memory allocated on the following line is only ever released when we need a bigger string */ - error_stack[error_sp].msg = mymalloc(size); - error_space[error_sp] = size; + /* size is size (len+1) of an existing string, overflow would mean + the system is broken anyway */ + ctx->error_stack[ctx->error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */ + ctx->error_alloc[ctx->error_sp] = size; } - strcpy(error_stack[error_sp].msg, msg); - error_stack[error_sp].code = code; - - if (error_cb) - error_cb(code, msg); + strcpy(ctx->error_stack[ctx->error_sp].msg, msg); + ctx->error_stack[ctx->error_sp].code = code; } /* -=item i_push_errorvf(int code, char const *fmt, va_list ap) +=item im_push_errorvf(ctx, code, format, args) +XX +=synopsis va_args args; +=synopsis va_start(args, lastarg); +=synopsis im_push_errorvf(ctx, code, format, args); +=category Error handling Intended for use by higher level functions, takes a varargs pointer and a format to produce the finally pushed error message. +Does not support perl specific format codes. + +Also callable as C + =cut */ -void i_push_errorvf(int code, char const *fmt, va_list ap) { +void +im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list ap) { char buf[1024]; -#if defined(_MSC_VER) +#if defined(IMAGER_VSNPRINTF) + vsnprintf(buf, sizeof(buf), fmt, ap); +#elif defined(_MSC_VER) _vsnprintf(buf, sizeof(buf), fmt, ap); #else /* is there a way to detect vsnprintf()? @@ -242,23 +195,50 @@ void i_push_errorvf(int code, char const *fmt, va_list ap) { */ vsprintf(buf, fmt, ap); #endif - i_push_error(code, buf); + im_push_error(ctx, code, buf); } /* =item i_push_errorf(int code, char const *fmt, ...) +=synopsis i_push_errorf(errno, "Cannot open file %s: %d", filename, errno); +=category Error handling + +A version of i_push_error() that does printf() like formatting. -A version of i_push_error() that does printf() like formating. +Does not support perl specific format codes. =cut */ -void i_push_errorf(int code, char const *fmt, ...) { +void +i_push_errorf(int code, char const *fmt, ...) { va_list ap; va_start(ap, fmt); i_push_errorvf(code, fmt, ap); va_end(ap); } +/* +=item im_push_errorf(ctx, code, char const *fmt, ...) +=synopsis im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno); +=category Error handling + +A version of im_push_error() that does printf() like formatting. + +Does not support perl specific format codes. + +=cut +*/ +void +im_push_errorf(im_context_t ctx, int code, char const *fmt, ...) { + va_list ap; + va_start(ap, fmt); + im_push_errorvf(ctx, code, fmt, ap); + va_end(ap); +} + +#ifdef IMAGER_I_FAILED +#error "This isn't used and is untested" + /* =item i_failed(char const *msg) @@ -281,23 +261,27 @@ int i_failed(int code, char const *msg) { failed_cb(error_stack + error_sp); if (failures_fatal) { int sp; - int total; /* total length of error messages */ + size_t total; /* total length of error messages */ char *full; /* full message for logging */ if (argv0) fprintf(stderr, "%s: ", argv0); fputs("error:\n", stderr); sp = error_sp; while (error_stack[sp].msg) { - fprintf(stderr, " %s\n", error_stack[sp]); + fprintf(stderr, " %s\n", error_stack[sp].msg); ++sp; } /* we want to log the error too, build an error message to hand to - m_fatal() */ + i_fatal() */ total = 1; /* remember the NUL */ for (sp = error_sp; error_stack[sp].msg; ++sp) { - total += strlen(error_stack[sp].msg) + 2; + size_t new_total += strlen(error_stack[sp].msg) + 2; + if (new_total < total) { + /* overflow, somehow */ + break; + } } - full = malloc(total); + full = mymalloc(total); if (!full) { /* just quit, at least it's on stderr */ exit(EXIT_FAILURE); @@ -309,12 +293,35 @@ int i_failed(int code, char const *msg) { } /* lose the extra ": " */ full[strlen(full)-2] = '\0'; - m_fatal(EXIT_FAILURE, "%s", full); + i_fatal(EXIT_FAILURE, "%s", full); } return 0; } +#endif + +#ifdef IM_ASSERT + +/* +=item im_assert_fail(file, line, message) + +Called when an im_assert() assertion fails. + +Only available when Imager is built with assertions. + +=cut +*/ + +void +im_assert_fail(char const *file, int line, char const *message) { + fprintf(stderr, "Assertion failed line %d file %s: %s\n", + line, file, message); + abort(); +} + +#endif + /* =back