Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/error.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/error.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,577 @@ +// Copyright (C) 2004-2024 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef _WIN32 +#ifndef NDEBUG +#define USE_OUTPUT_DEBUG_STRING +#include <windows.h> +#endif +#endif + +#ifdef __ANDROID__ +#define USE_ANDROID_LOG +#include <android/log.h> +#endif + +void fz_default_error_callback(void *user, const char *message) +{ + /* TODO: send errcode and format it here instead of in fz_report_error */ + fputs(message, stderr); + fputc('\n', stderr); +#ifdef USE_OUTPUT_DEBUG_STRING + OutputDebugStringA(message); + OutputDebugStringA("\n"); +#endif +#ifdef USE_ANDROID_LOG + __android_log_print(ANDROID_LOG_ERROR, "libmupdf", "%s", message); +#endif +} + +void fz_default_warning_callback(void *user, const char *message) +{ + fprintf(stderr, "warning: %s\n", message); +#ifdef USE_OUTPUT_DEBUG_STRING + OutputDebugStringA("warning: "); + OutputDebugStringA(message); + OutputDebugStringA("\n"); +#endif +#ifdef USE_ANDROID_LOG + __android_log_print(ANDROID_LOG_WARN, "libmupdf", "%s", message); +#endif +} + +/* Warning context */ + +void fz_set_warning_callback(fz_context *ctx, fz_warning_cb *warning_cb, void *user) +{ + ctx->warn.print_user = user; + ctx->warn.print = warning_cb; +} + +fz_warning_cb *fz_warning_callback(fz_context *ctx, void **user) +{ + if (user) + *user = ctx->warn.print_user; + return ctx->warn.print; +} + +void fz_var_imp(void *var) +{ + /* Do nothing */ +} + +void fz_flush_warnings(fz_context *ctx) +{ + if (ctx->warn.count > 1) + { + char buf[50]; + fz_snprintf(buf, sizeof buf, "... repeated %d times...", ctx->warn.count); + if (ctx->warn.print) + ctx->warn.print(ctx->warn.print_user, buf); + } + ctx->warn.message[0] = 0; + ctx->warn.count = 0; +} + +void (fz_vwarn)(fz_context *ctx, const char *fmt, va_list ap) +{ + char buf[sizeof ctx->warn.message]; + + fz_vsnprintf(buf, sizeof buf, fmt, ap); + buf[sizeof(buf) - 1] = 0; + + if (!strcmp(buf, ctx->warn.message)) + { + ctx->warn.count++; + } + else + { + fz_flush_warnings(ctx); + if (ctx->warn.print) + ctx->warn.print(ctx->warn.print_user, buf); + fz_strlcpy(ctx->warn.message, buf, sizeof ctx->warn.message); + ctx->warn.count = 1; + } +} + +void (fz_warn)(fz_context *ctx, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fz_vwarn(ctx, fmt, ap); + va_end(ap); +} + +#if FZ_VERBOSE_EXCEPTIONS +void fz_vwarnFL(fz_context *ctx, const char *file, int line, const char *fmt, va_list ap) +{ + char buf[sizeof ctx->warn.message]; + + fz_vsnprintf(buf, sizeof buf, fmt, ap); + buf[sizeof(buf) - 1] = 0; + + if (!strcmp(buf, ctx->warn.message)) + { + ctx->warn.count++; + } + else + { + fz_flush_warnings(ctx); + if (ctx->warn.print) + ctx->warn.print(ctx->warn.print_user, buf); + fz_strlcpy(ctx->warn.message, buf, sizeof ctx->warn.message); + ctx->warn.count = 1; + } +} + +void fz_warnFL(fz_context *ctx, const char *file, int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fz_vwarnFL(ctx, file, line, fmt, ap); + va_end(ap); +} +#endif + +/* Error context */ + +void fz_set_error_callback(fz_context *ctx, fz_error_cb *error_cb, void *user) +{ + ctx->error.print_user = user; + ctx->error.print = error_cb; +} + +fz_error_cb *fz_error_callback(fz_context *ctx, void **user) +{ + if (user) + *user = ctx->error.print_user; + return ctx->error.print; +} + +/* When we first setjmp, state is set to 0. Whenever we throw, we add 2 to + * this state. Whenever we enter the always block, we add 1. + * + * fz_push_try sets state to 0. + * If (fz_throw called within fz_try) + * fz_throw makes state = 2. + * If (no always block present) + * enter catch region with state = 2. OK. + * else + * fz_always entered as state < 3; Makes state = 3; + * if (fz_throw called within fz_always) + * fz_throw makes state = 5 + * fz_always is not reentered. + * catch region entered with state = 5. OK. + * else + * catch region entered with state = 3. OK + * else + * if (no always block present) + * catch region not entered as state = 0. OK. + * else + * fz_always entered as state < 3. makes state = 1 + * if (fz_throw called within fz_always) + * fz_throw makes state = 3; + * fz_always NOT entered as state >= 3 + * catch region entered with state = 3. OK. + * else + * catch region entered with state = 1. + */ + +FZ_NORETURN static void throw(fz_context *ctx, int code) +{ + if (ctx->error.top > ctx->error.stack_base) + { + ctx->error.top->state += 2; + if (ctx->error.top->code != FZ_ERROR_NONE) + fz_warn(ctx, "clobbering previous error code and message (throw in always block?)"); + ctx->error.top->code = code; + fz_longjmp(ctx->error.top->buffer, 1); + } + else + { + fz_flush_warnings(ctx); + if (ctx->error.print) + ctx->error.print(ctx->error.print_user, "aborting process from uncaught error!"); + exit(EXIT_FAILURE); + } +} + +fz_jmp_buf *fz_push_try(fz_context *ctx) +{ + /* If we would overflow the exception stack, throw an exception instead + * of entering the try block. We assume that we always have room for + * 1 extra level on the stack here - i.e. we throw the error on us + * starting to use the last level. */ + if (ctx->error.top + 2 >= ctx->error.stack_base + nelem(ctx->error.stack)) + { + fz_strlcpy(ctx->error.message, "exception stack overflow!", sizeof ctx->error.message); + + fz_flush_warnings(ctx); + if (ctx->error.print) + ctx->error.print(ctx->error.print_user, ctx->error.message); + + /* We need to arrive in the always/catch block as if throw had taken place. */ + ctx->error.top++; + ctx->error.top->state = 2; + ctx->error.top->code = FZ_ERROR_LIMIT; + } + else + { + ctx->error.top++; + ctx->error.top->state = 0; + ctx->error.top->code = FZ_ERROR_NONE; + } + return &ctx->error.top->buffer; +} + +int fz_do_try(fz_context *ctx) +{ +#ifdef __COVERITY__ + return 1; +#else + return ctx->error.top->state == 0; +#endif +} + +int fz_do_always(fz_context *ctx) +{ +#ifdef __COVERITY__ + return 1; +#else + if (ctx->error.top->state < 3) + { + ctx->error.top->state++; + return 1; + } + return 0; +#endif +} + +int (fz_do_catch)(fz_context *ctx) +{ + ctx->error.errcode = ctx->error.top->code; + return (ctx->error.top--)->state > 1; +} + +int fz_caught(fz_context *ctx) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + return ctx->error.errcode; +} + +int fz_caught_errno(fz_context *ctx) +{ + assert(ctx && ctx->error.errcode == FZ_ERROR_SYSTEM); + return ctx->error.errnum; +} + +const char *fz_caught_message(fz_context *ctx) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + return ctx->error.message; +} + +void (fz_log_error_printf)(fz_context *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (fz_vlog_error_printf)(ctx, fmt, ap); + va_end(ap); +} + +void (fz_vlog_error_printf)(fz_context *ctx, const char *fmt, va_list ap) +{ + char message[256]; + + fz_flush_warnings(ctx); + if (ctx->error.print) + { + fz_vsnprintf(message, sizeof message, fmt, ap); + message[sizeof(message) - 1] = 0; + + ctx->error.print(ctx->error.print_user, message); + } +} + +void (fz_log_error)(fz_context *ctx, const char *str) +{ + fz_flush_warnings(ctx); + if (ctx->error.print) + ctx->error.print(ctx->error.print_user, str); +} + +/* coverity[+kill] */ +FZ_NORETURN void (fz_vthrow)(fz_context *ctx, int code, const char *fmt, va_list ap) +{ + if (ctx->error.errcode) + { + fz_flush_warnings(ctx); + fz_warn(ctx, "UNHANDLED EXCEPTION!"); + fz_report_error(ctx); +#ifdef CLUSTER + abort(); +#endif + } + + if (code == FZ_ERROR_SYSTEM) + ctx->error.errnum = errno; + else + ctx->error.errnum = 0; + + fz_vsnprintf(ctx->error.message, sizeof ctx->error.message, fmt, ap); + ctx->error.message[sizeof(ctx->error.message) - 1] = 0; + + throw(ctx, code); +} + +/* coverity[+kill] */ +FZ_NORETURN void (fz_throw)(fz_context *ctx, int code, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fz_vthrow(ctx, code, fmt, ap); + va_end(ap); +} + +/* coverity[+kill] */ +FZ_NORETURN void (fz_rethrow)(fz_context *ctx) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + throw(ctx, ctx->error.errcode); +} + +void (fz_morph_error)(fz_context *ctx, int fromerr, int toerr) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + if (ctx->error.errcode == fromerr) + ctx->error.errcode = toerr; +} + +void (fz_rethrow_if)(fz_context *ctx, int err) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + if (ctx->error.errcode == err) + fz_rethrow(ctx); +} + +void (fz_rethrow_unless)(fz_context *ctx, int err) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + if (ctx->error.errcode != err) + fz_rethrow(ctx); +} + +static const char * +fz_error_type_name(enum fz_error_type exc) +{ + switch (exc) + { + case FZ_ERROR_NONE: return "none"; + case FZ_ERROR_GENERIC: return "generic"; + case FZ_ERROR_SYSTEM: return "system"; + case FZ_ERROR_LIBRARY: return "library"; + case FZ_ERROR_UNSUPPORTED: return "unsupported"; + case FZ_ERROR_ARGUMENT: return "argument"; + case FZ_ERROR_LIMIT: return "limit"; + case FZ_ERROR_FORMAT: return "format"; + case FZ_ERROR_SYNTAX: return "syntax"; + case FZ_ERROR_TRYLATER: return "trylater"; + case FZ_ERROR_ABORT: return "abort"; + case FZ_ERROR_REPAIRED: return "repaired"; + } + return "invalid error type"; +} + +#if FZ_VERBOSE_EXCEPTIONS + +int fz_do_catchFL(fz_context *ctx, const char *file, int line) +{ + int rc = (fz_do_catch)(ctx); + if (rc) + (fz_log_error_printf)(ctx, "%s:%d: Catching", file, line); + return rc; +} + + +void fz_log_error_printfFL(fz_context *ctx, const char *file, int line, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fz_vlog_error_printfFL(ctx, file, line, fmt, ap); + va_end(ap); +} + +void fz_vlog_error_printfFL(fz_context *ctx, const char *file, int line, const char *fmt, va_list ap) +{ + char message[256]; + + fz_flush_warnings(ctx); + if (ctx->error.print) + { + fz_vsnprintf(message, sizeof message, fmt, ap); + message[sizeof(message) - 1] = 0; + + fz_log_errorFL(ctx, file, line, message); + } +} + +void fz_log_errorFL(fz_context *ctx, const char *file, int line, const char *str) +{ + char message[256]; + + fz_flush_warnings(ctx); + if (ctx->error.print) + { + fz_snprintf(message, sizeof message, "%s:%d '%s'", file, line, str); + message[sizeof(message) - 1] = 0; + ctx->error.print(ctx->error.print_user, message); + } +} + +/* coverity[+kill] */ +FZ_NORETURN void fz_vthrowFL(fz_context *ctx, const char *file, int line, int code, const char *fmt, va_list ap) +{ + if (ctx->error.errcode) + { + fz_flush_warnings(ctx); + fz_warn(ctx, "UNHANDLED EXCEPTION!"); + fz_report_error(ctx); +#ifdef CLUSTER + abort(); +#endif + } + + fz_vsnprintf(ctx->error.message, sizeof ctx->error.message, fmt, ap); + ctx->error.message[sizeof(ctx->error.message) - 1] = 0; + + (fz_log_error_printf)(ctx, "%s:%d: Throwing %s '%s'", file, line, fz_error_type_name(code), ctx->error.message); + + throw(ctx, code); +} + +/* coverity[+kill] */ +FZ_NORETURN void fz_throwFL(fz_context *ctx, const char *file, int line, int code, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fz_vthrowFL(ctx, file, line, code, fmt, ap); + va_end(ap); +} + +/* coverity[+kill] */ +FZ_NORETURN void fz_rethrowFL(fz_context *ctx, const char *file, int line) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + (fz_log_error_printf)(ctx, "%s:%d: Rethrowing", file, line); + throw(ctx, ctx->error.errcode); +} + +void fz_morph_errorFL(fz_context *ctx, const char *file, int line, int fromerr, int toerr) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + if (ctx->error.errcode == fromerr) + { + (fz_log_error_printf)(ctx, "%s:%d: Morphing %s->%s", file, line, fz_error_type_name(fromerr), fz_error_type_name(toerr)); + ctx->error.errcode = toerr; + } +} + +void fz_rethrow_unlessFL(fz_context *ctx, const char *file, int line, int err) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + if (ctx->error.errcode != err) + { + (fz_log_error_printf)(ctx, "%s:%d: Rethrowing", file, line); + (fz_rethrow)(ctx); + } +} + +void fz_rethrow_ifFL(fz_context *ctx, const char *file, int line, int err) +{ + assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); + if (ctx->error.errcode == err) + { + (fz_log_error_printf)(ctx, "%s:%d: Rethrowing", file, line); + (fz_rethrow)(ctx); + } +} +#endif + +void fz_start_throw_on_repair(fz_context *ctx) +{ + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->throw_on_repair++; + fz_unlock(ctx, FZ_LOCK_ALLOC); +} + +void fz_end_throw_on_repair(fz_context *ctx) +{ + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->throw_on_repair--; + fz_unlock(ctx, FZ_LOCK_ALLOC); +} + +void fz_report_error(fz_context *ctx) +{ +#ifdef CLUSTER + if (ctx->error.errcode == FZ_ERROR_TRYLATER || ctx->error.errcode == FZ_ERROR_ABORT) + { + fprintf(stderr, "REPORTED ERROR THAT IS TRYLATER OR ABORT\n"); + abort(); + } +#endif + /* TODO: send errcode to fz_log_error instead of formatting it here */ + fz_log_error_printf(ctx, "%s error: %s", fz_error_type_name(ctx->error.errcode), ctx->error.message); + ctx->error.errcode = FZ_ERROR_NONE; +} + +void fz_ignore_error(fz_context *ctx) +{ +#ifdef CLUSTER + if (ctx->error.errcode != FZ_ERROR_TRYLATER && ctx->error.errcode != FZ_ERROR_ABORT) + { + fprintf(stderr, "IGNORED ERROR THAT IS NOT TRYLATER OR ABORT\n"); + abort(); + } +#endif + ctx->error.errcode = FZ_ERROR_NONE; +} + +/* Convert an error into another runtime exception. */ +const char *fz_convert_error(fz_context *ctx, int *code) +{ + if (code) + *code = ctx->error.errcode; + ctx->error.errcode = FZ_ERROR_NONE; + return ctx->error.message; +}
