comparison 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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2024 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31
32 #ifdef _WIN32
33 #ifndef NDEBUG
34 #define USE_OUTPUT_DEBUG_STRING
35 #include <windows.h>
36 #endif
37 #endif
38
39 #ifdef __ANDROID__
40 #define USE_ANDROID_LOG
41 #include <android/log.h>
42 #endif
43
44 void fz_default_error_callback(void *user, const char *message)
45 {
46 /* TODO: send errcode and format it here instead of in fz_report_error */
47 fputs(message, stderr);
48 fputc('\n', stderr);
49 #ifdef USE_OUTPUT_DEBUG_STRING
50 OutputDebugStringA(message);
51 OutputDebugStringA("\n");
52 #endif
53 #ifdef USE_ANDROID_LOG
54 __android_log_print(ANDROID_LOG_ERROR, "libmupdf", "%s", message);
55 #endif
56 }
57
58 void fz_default_warning_callback(void *user, const char *message)
59 {
60 fprintf(stderr, "warning: %s\n", message);
61 #ifdef USE_OUTPUT_DEBUG_STRING
62 OutputDebugStringA("warning: ");
63 OutputDebugStringA(message);
64 OutputDebugStringA("\n");
65 #endif
66 #ifdef USE_ANDROID_LOG
67 __android_log_print(ANDROID_LOG_WARN, "libmupdf", "%s", message);
68 #endif
69 }
70
71 /* Warning context */
72
73 void fz_set_warning_callback(fz_context *ctx, fz_warning_cb *warning_cb, void *user)
74 {
75 ctx->warn.print_user = user;
76 ctx->warn.print = warning_cb;
77 }
78
79 fz_warning_cb *fz_warning_callback(fz_context *ctx, void **user)
80 {
81 if (user)
82 *user = ctx->warn.print_user;
83 return ctx->warn.print;
84 }
85
86 void fz_var_imp(void *var)
87 {
88 /* Do nothing */
89 }
90
91 void fz_flush_warnings(fz_context *ctx)
92 {
93 if (ctx->warn.count > 1)
94 {
95 char buf[50];
96 fz_snprintf(buf, sizeof buf, "... repeated %d times...", ctx->warn.count);
97 if (ctx->warn.print)
98 ctx->warn.print(ctx->warn.print_user, buf);
99 }
100 ctx->warn.message[0] = 0;
101 ctx->warn.count = 0;
102 }
103
104 void (fz_vwarn)(fz_context *ctx, const char *fmt, va_list ap)
105 {
106 char buf[sizeof ctx->warn.message];
107
108 fz_vsnprintf(buf, sizeof buf, fmt, ap);
109 buf[sizeof(buf) - 1] = 0;
110
111 if (!strcmp(buf, ctx->warn.message))
112 {
113 ctx->warn.count++;
114 }
115 else
116 {
117 fz_flush_warnings(ctx);
118 if (ctx->warn.print)
119 ctx->warn.print(ctx->warn.print_user, buf);
120 fz_strlcpy(ctx->warn.message, buf, sizeof ctx->warn.message);
121 ctx->warn.count = 1;
122 }
123 }
124
125 void (fz_warn)(fz_context *ctx, const char *fmt, ...)
126 {
127 va_list ap;
128 va_start(ap, fmt);
129 fz_vwarn(ctx, fmt, ap);
130 va_end(ap);
131 }
132
133 #if FZ_VERBOSE_EXCEPTIONS
134 void fz_vwarnFL(fz_context *ctx, const char *file, int line, const char *fmt, va_list ap)
135 {
136 char buf[sizeof ctx->warn.message];
137
138 fz_vsnprintf(buf, sizeof buf, fmt, ap);
139 buf[sizeof(buf) - 1] = 0;
140
141 if (!strcmp(buf, ctx->warn.message))
142 {
143 ctx->warn.count++;
144 }
145 else
146 {
147 fz_flush_warnings(ctx);
148 if (ctx->warn.print)
149 ctx->warn.print(ctx->warn.print_user, buf);
150 fz_strlcpy(ctx->warn.message, buf, sizeof ctx->warn.message);
151 ctx->warn.count = 1;
152 }
153 }
154
155 void fz_warnFL(fz_context *ctx, const char *file, int line, const char *fmt, ...)
156 {
157 va_list ap;
158 va_start(ap, fmt);
159 fz_vwarnFL(ctx, file, line, fmt, ap);
160 va_end(ap);
161 }
162 #endif
163
164 /* Error context */
165
166 void fz_set_error_callback(fz_context *ctx, fz_error_cb *error_cb, void *user)
167 {
168 ctx->error.print_user = user;
169 ctx->error.print = error_cb;
170 }
171
172 fz_error_cb *fz_error_callback(fz_context *ctx, void **user)
173 {
174 if (user)
175 *user = ctx->error.print_user;
176 return ctx->error.print;
177 }
178
179 /* When we first setjmp, state is set to 0. Whenever we throw, we add 2 to
180 * this state. Whenever we enter the always block, we add 1.
181 *
182 * fz_push_try sets state to 0.
183 * If (fz_throw called within fz_try)
184 * fz_throw makes state = 2.
185 * If (no always block present)
186 * enter catch region with state = 2. OK.
187 * else
188 * fz_always entered as state < 3; Makes state = 3;
189 * if (fz_throw called within fz_always)
190 * fz_throw makes state = 5
191 * fz_always is not reentered.
192 * catch region entered with state = 5. OK.
193 * else
194 * catch region entered with state = 3. OK
195 * else
196 * if (no always block present)
197 * catch region not entered as state = 0. OK.
198 * else
199 * fz_always entered as state < 3. makes state = 1
200 * if (fz_throw called within fz_always)
201 * fz_throw makes state = 3;
202 * fz_always NOT entered as state >= 3
203 * catch region entered with state = 3. OK.
204 * else
205 * catch region entered with state = 1.
206 */
207
208 FZ_NORETURN static void throw(fz_context *ctx, int code)
209 {
210 if (ctx->error.top > ctx->error.stack_base)
211 {
212 ctx->error.top->state += 2;
213 if (ctx->error.top->code != FZ_ERROR_NONE)
214 fz_warn(ctx, "clobbering previous error code and message (throw in always block?)");
215 ctx->error.top->code = code;
216 fz_longjmp(ctx->error.top->buffer, 1);
217 }
218 else
219 {
220 fz_flush_warnings(ctx);
221 if (ctx->error.print)
222 ctx->error.print(ctx->error.print_user, "aborting process from uncaught error!");
223 exit(EXIT_FAILURE);
224 }
225 }
226
227 fz_jmp_buf *fz_push_try(fz_context *ctx)
228 {
229 /* If we would overflow the exception stack, throw an exception instead
230 * of entering the try block. We assume that we always have room for
231 * 1 extra level on the stack here - i.e. we throw the error on us
232 * starting to use the last level. */
233 if (ctx->error.top + 2 >= ctx->error.stack_base + nelem(ctx->error.stack))
234 {
235 fz_strlcpy(ctx->error.message, "exception stack overflow!", sizeof ctx->error.message);
236
237 fz_flush_warnings(ctx);
238 if (ctx->error.print)
239 ctx->error.print(ctx->error.print_user, ctx->error.message);
240
241 /* We need to arrive in the always/catch block as if throw had taken place. */
242 ctx->error.top++;
243 ctx->error.top->state = 2;
244 ctx->error.top->code = FZ_ERROR_LIMIT;
245 }
246 else
247 {
248 ctx->error.top++;
249 ctx->error.top->state = 0;
250 ctx->error.top->code = FZ_ERROR_NONE;
251 }
252 return &ctx->error.top->buffer;
253 }
254
255 int fz_do_try(fz_context *ctx)
256 {
257 #ifdef __COVERITY__
258 return 1;
259 #else
260 return ctx->error.top->state == 0;
261 #endif
262 }
263
264 int fz_do_always(fz_context *ctx)
265 {
266 #ifdef __COVERITY__
267 return 1;
268 #else
269 if (ctx->error.top->state < 3)
270 {
271 ctx->error.top->state++;
272 return 1;
273 }
274 return 0;
275 #endif
276 }
277
278 int (fz_do_catch)(fz_context *ctx)
279 {
280 ctx->error.errcode = ctx->error.top->code;
281 return (ctx->error.top--)->state > 1;
282 }
283
284 int fz_caught(fz_context *ctx)
285 {
286 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
287 return ctx->error.errcode;
288 }
289
290 int fz_caught_errno(fz_context *ctx)
291 {
292 assert(ctx && ctx->error.errcode == FZ_ERROR_SYSTEM);
293 return ctx->error.errnum;
294 }
295
296 const char *fz_caught_message(fz_context *ctx)
297 {
298 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
299 return ctx->error.message;
300 }
301
302 void (fz_log_error_printf)(fz_context *ctx, const char *fmt, ...)
303 {
304 va_list ap;
305
306 va_start(ap, fmt);
307 (fz_vlog_error_printf)(ctx, fmt, ap);
308 va_end(ap);
309 }
310
311 void (fz_vlog_error_printf)(fz_context *ctx, const char *fmt, va_list ap)
312 {
313 char message[256];
314
315 fz_flush_warnings(ctx);
316 if (ctx->error.print)
317 {
318 fz_vsnprintf(message, sizeof message, fmt, ap);
319 message[sizeof(message) - 1] = 0;
320
321 ctx->error.print(ctx->error.print_user, message);
322 }
323 }
324
325 void (fz_log_error)(fz_context *ctx, const char *str)
326 {
327 fz_flush_warnings(ctx);
328 if (ctx->error.print)
329 ctx->error.print(ctx->error.print_user, str);
330 }
331
332 /* coverity[+kill] */
333 FZ_NORETURN void (fz_vthrow)(fz_context *ctx, int code, const char *fmt, va_list ap)
334 {
335 if (ctx->error.errcode)
336 {
337 fz_flush_warnings(ctx);
338 fz_warn(ctx, "UNHANDLED EXCEPTION!");
339 fz_report_error(ctx);
340 #ifdef CLUSTER
341 abort();
342 #endif
343 }
344
345 if (code == FZ_ERROR_SYSTEM)
346 ctx->error.errnum = errno;
347 else
348 ctx->error.errnum = 0;
349
350 fz_vsnprintf(ctx->error.message, sizeof ctx->error.message, fmt, ap);
351 ctx->error.message[sizeof(ctx->error.message) - 1] = 0;
352
353 throw(ctx, code);
354 }
355
356 /* coverity[+kill] */
357 FZ_NORETURN void (fz_throw)(fz_context *ctx, int code, const char *fmt, ...)
358 {
359 va_list ap;
360 va_start(ap, fmt);
361 fz_vthrow(ctx, code, fmt, ap);
362 va_end(ap);
363 }
364
365 /* coverity[+kill] */
366 FZ_NORETURN void (fz_rethrow)(fz_context *ctx)
367 {
368 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
369 throw(ctx, ctx->error.errcode);
370 }
371
372 void (fz_morph_error)(fz_context *ctx, int fromerr, int toerr)
373 {
374 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
375 if (ctx->error.errcode == fromerr)
376 ctx->error.errcode = toerr;
377 }
378
379 void (fz_rethrow_if)(fz_context *ctx, int err)
380 {
381 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
382 if (ctx->error.errcode == err)
383 fz_rethrow(ctx);
384 }
385
386 void (fz_rethrow_unless)(fz_context *ctx, int err)
387 {
388 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
389 if (ctx->error.errcode != err)
390 fz_rethrow(ctx);
391 }
392
393 static const char *
394 fz_error_type_name(enum fz_error_type exc)
395 {
396 switch (exc)
397 {
398 case FZ_ERROR_NONE: return "none";
399 case FZ_ERROR_GENERIC: return "generic";
400 case FZ_ERROR_SYSTEM: return "system";
401 case FZ_ERROR_LIBRARY: return "library";
402 case FZ_ERROR_UNSUPPORTED: return "unsupported";
403 case FZ_ERROR_ARGUMENT: return "argument";
404 case FZ_ERROR_LIMIT: return "limit";
405 case FZ_ERROR_FORMAT: return "format";
406 case FZ_ERROR_SYNTAX: return "syntax";
407 case FZ_ERROR_TRYLATER: return "trylater";
408 case FZ_ERROR_ABORT: return "abort";
409 case FZ_ERROR_REPAIRED: return "repaired";
410 }
411 return "invalid error type";
412 }
413
414 #if FZ_VERBOSE_EXCEPTIONS
415
416 int fz_do_catchFL(fz_context *ctx, const char *file, int line)
417 {
418 int rc = (fz_do_catch)(ctx);
419 if (rc)
420 (fz_log_error_printf)(ctx, "%s:%d: Catching", file, line);
421 return rc;
422 }
423
424
425 void fz_log_error_printfFL(fz_context *ctx, const char *file, int line, const char *fmt, ...)
426 {
427 va_list ap;
428
429 va_start(ap, fmt);
430 fz_vlog_error_printfFL(ctx, file, line, fmt, ap);
431 va_end(ap);
432 }
433
434 void fz_vlog_error_printfFL(fz_context *ctx, const char *file, int line, const char *fmt, va_list ap)
435 {
436 char message[256];
437
438 fz_flush_warnings(ctx);
439 if (ctx->error.print)
440 {
441 fz_vsnprintf(message, sizeof message, fmt, ap);
442 message[sizeof(message) - 1] = 0;
443
444 fz_log_errorFL(ctx, file, line, message);
445 }
446 }
447
448 void fz_log_errorFL(fz_context *ctx, const char *file, int line, const char *str)
449 {
450 char message[256];
451
452 fz_flush_warnings(ctx);
453 if (ctx->error.print)
454 {
455 fz_snprintf(message, sizeof message, "%s:%d '%s'", file, line, str);
456 message[sizeof(message) - 1] = 0;
457 ctx->error.print(ctx->error.print_user, message);
458 }
459 }
460
461 /* coverity[+kill] */
462 FZ_NORETURN void fz_vthrowFL(fz_context *ctx, const char *file, int line, int code, const char *fmt, va_list ap)
463 {
464 if (ctx->error.errcode)
465 {
466 fz_flush_warnings(ctx);
467 fz_warn(ctx, "UNHANDLED EXCEPTION!");
468 fz_report_error(ctx);
469 #ifdef CLUSTER
470 abort();
471 #endif
472 }
473
474 fz_vsnprintf(ctx->error.message, sizeof ctx->error.message, fmt, ap);
475 ctx->error.message[sizeof(ctx->error.message) - 1] = 0;
476
477 (fz_log_error_printf)(ctx, "%s:%d: Throwing %s '%s'", file, line, fz_error_type_name(code), ctx->error.message);
478
479 throw(ctx, code);
480 }
481
482 /* coverity[+kill] */
483 FZ_NORETURN void fz_throwFL(fz_context *ctx, const char *file, int line, int code, const char *fmt, ...)
484 {
485 va_list ap;
486 va_start(ap, fmt);
487 fz_vthrowFL(ctx, file, line, code, fmt, ap);
488 va_end(ap);
489 }
490
491 /* coverity[+kill] */
492 FZ_NORETURN void fz_rethrowFL(fz_context *ctx, const char *file, int line)
493 {
494 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
495 (fz_log_error_printf)(ctx, "%s:%d: Rethrowing", file, line);
496 throw(ctx, ctx->error.errcode);
497 }
498
499 void fz_morph_errorFL(fz_context *ctx, const char *file, int line, int fromerr, int toerr)
500 {
501 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
502 if (ctx->error.errcode == fromerr)
503 {
504 (fz_log_error_printf)(ctx, "%s:%d: Morphing %s->%s", file, line, fz_error_type_name(fromerr), fz_error_type_name(toerr));
505 ctx->error.errcode = toerr;
506 }
507 }
508
509 void fz_rethrow_unlessFL(fz_context *ctx, const char *file, int line, int err)
510 {
511 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
512 if (ctx->error.errcode != err)
513 {
514 (fz_log_error_printf)(ctx, "%s:%d: Rethrowing", file, line);
515 (fz_rethrow)(ctx);
516 }
517 }
518
519 void fz_rethrow_ifFL(fz_context *ctx, const char *file, int line, int err)
520 {
521 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
522 if (ctx->error.errcode == err)
523 {
524 (fz_log_error_printf)(ctx, "%s:%d: Rethrowing", file, line);
525 (fz_rethrow)(ctx);
526 }
527 }
528 #endif
529
530 void fz_start_throw_on_repair(fz_context *ctx)
531 {
532 fz_lock(ctx, FZ_LOCK_ALLOC);
533 ctx->throw_on_repair++;
534 fz_unlock(ctx, FZ_LOCK_ALLOC);
535 }
536
537 void fz_end_throw_on_repair(fz_context *ctx)
538 {
539 fz_lock(ctx, FZ_LOCK_ALLOC);
540 ctx->throw_on_repair--;
541 fz_unlock(ctx, FZ_LOCK_ALLOC);
542 }
543
544 void fz_report_error(fz_context *ctx)
545 {
546 #ifdef CLUSTER
547 if (ctx->error.errcode == FZ_ERROR_TRYLATER || ctx->error.errcode == FZ_ERROR_ABORT)
548 {
549 fprintf(stderr, "REPORTED ERROR THAT IS TRYLATER OR ABORT\n");
550 abort();
551 }
552 #endif
553 /* TODO: send errcode to fz_log_error instead of formatting it here */
554 fz_log_error_printf(ctx, "%s error: %s", fz_error_type_name(ctx->error.errcode), ctx->error.message);
555 ctx->error.errcode = FZ_ERROR_NONE;
556 }
557
558 void fz_ignore_error(fz_context *ctx)
559 {
560 #ifdef CLUSTER
561 if (ctx->error.errcode != FZ_ERROR_TRYLATER && ctx->error.errcode != FZ_ERROR_ABORT)
562 {
563 fprintf(stderr, "IGNORED ERROR THAT IS NOT TRYLATER OR ABORT\n");
564 abort();
565 }
566 #endif
567 ctx->error.errcode = FZ_ERROR_NONE;
568 }
569
570 /* Convert an error into another runtime exception. */
571 const char *fz_convert_error(fz_context *ctx, int *code)
572 {
573 if (code)
574 *code = ctx->error.errcode;
575 ctx->error.errcode = FZ_ERROR_NONE;
576 return ctx->error.message;
577 }