| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- // 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;
- }
|