1#include "mupdf/fitz.h"
2
3#include <assert.h>
4#include <stdarg.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#ifdef _MSC_VER
10#ifndef NDEBUG
11#define USE_OUTPUT_DEBUG_STRING
12#include <windows.h>
13#endif
14#endif
15
16#ifdef __ANDROID__
17#define USE_ANDROID_LOG
18#include <android/log.h>
19#endif
20
21void fz_default_error_callback(void *user, const char *message)
22{
23 fprintf(stderr, "error: %s\n", message);
24#ifdef USE_OUTPUT_DEBUG_STRING
25 OutputDebugStringA("error: ");
26 OutputDebugStringA(message);
27 OutputDebugStringA("\n");
28#endif
29#ifdef USE_ANDROID_LOG
30 __android_log_print(ANDROID_LOG_ERROR, "libmupdf", "%s", message);
31#endif
32}
33
34void fz_default_warning_callback(void *user, const char *message)
35{
36 fprintf(stderr, "warning: %s\n", message);
37#ifdef USE_OUTPUT_DEBUG_STRING
38 OutputDebugStringA("warning: ");
39 OutputDebugStringA(message);
40 OutputDebugStringA("\n");
41#endif
42#ifdef USE_ANDROID_LOG
43 __android_log_print(ANDROID_LOG_WARN, "libmupdf", "%s", message);
44#endif
45}
46
47/* Warning context */
48
49void fz_set_warning_callback(fz_context *ctx, void (*print)(void *user, const char *message), void *user)
50{
51 ctx->warn.print_user = user;
52 ctx->warn.print = print;
53}
54
55void fz_var_imp(void *var)
56{
57 /* Do nothing */
58}
59
60/*
61 Flush any repeated warnings.
62
63 Repeated warnings are buffered, counted and eventually printed
64 along with the number of repetitions. Call fz_flush_warnings
65 to force printing of the latest buffered warning and the
66 number of repetitions, for example to make sure that all
67 warnings are printed before exiting an application.
68*/
69void fz_flush_warnings(fz_context *ctx)
70{
71 if (ctx->warn.count > 1)
72 {
73 char buf[50];
74 fz_snprintf(buf, sizeof buf, "... repeated %d times...", ctx->warn.count);
75 if (ctx->warn.print)
76 ctx->warn.print(ctx->warn.print_user, buf);
77 }
78 ctx->warn.message[0] = 0;
79 ctx->warn.count = 0;
80}
81
82void fz_vwarn(fz_context *ctx, const char *fmt, va_list ap)
83{
84 char buf[sizeof ctx->warn.message];
85
86 fz_vsnprintf(buf, sizeof buf, fmt, ap);
87 buf[sizeof(buf) - 1] = 0;
88
89 if (!strcmp(buf, ctx->warn.message))
90 {
91 ctx->warn.count++;
92 }
93 else
94 {
95 fz_flush_warnings(ctx);
96 if (ctx->warn.print)
97 ctx->warn.print(ctx->warn.print_user, buf);
98 fz_strlcpy(ctx->warn.message, buf, sizeof ctx->warn.message);
99 ctx->warn.count = 1;
100 }
101}
102
103void fz_warn(fz_context *ctx, const char *fmt, ...)
104{
105 va_list ap;
106 va_start(ap, fmt);
107 fz_vwarn(ctx, fmt, ap);
108 va_end(ap);
109}
110
111/* Error context */
112
113void fz_set_error_callback(fz_context *ctx, void (*print)(void *user, const char *message), void *user)
114{
115 ctx->error.print_user = user;
116 ctx->error.print = print;
117}
118
119/* When we first setjmp, state is set to 0. Whenever we throw, we add 2 to
120 * this state. Whenever we enter the always block, we add 1.
121 *
122 * fz_push_try sets state to 0.
123 * If (fz_throw called within fz_try)
124 * fz_throw makes state = 2.
125 * If (no always block present)
126 * enter catch region with state = 2. OK.
127 * else
128 * fz_always entered as state < 3; Makes state = 3;
129 * if (fz_throw called within fz_always)
130 * fz_throw makes state = 5
131 * fz_always is not reentered.
132 * catch region entered with state = 5. OK.
133 * else
134 * catch region entered with state = 3. OK
135 * else
136 * if (no always block present)
137 * catch region not entered as state = 0. OK.
138 * else
139 * fz_always entered as state < 3. makes state = 1
140 * if (fz_throw called within fz_always)
141 * fz_throw makes state = 3;
142 * fz_always NOT entered as state >= 3
143 * catch region entered with state = 3. OK.
144 * else
145 * catch region entered with state = 1.
146 */
147
148FZ_NORETURN static void throw(fz_context *ctx, int code)
149{
150 if (ctx->error.top > ctx->error.stack)
151 {
152 ctx->error.top->state += 2;
153 if (ctx->error.top->code != FZ_ERROR_NONE)
154 fz_warn(ctx, "clobbering previous error code and message (throw in always block?)");
155 ctx->error.top->code = code;
156 fz_longjmp(ctx->error.top->buffer, 1);
157 }
158 else
159 {
160 fz_flush_warnings(ctx);
161 if (ctx->error.print)
162 ctx->error.print(ctx->error.print_user, "aborting process from uncaught error!");
163 exit(EXIT_FAILURE);
164 }
165}
166
167fz_jmp_buf *fz_push_try(fz_context *ctx)
168{
169 /* If we would overflow the exception stack, throw an exception instead
170 * of entering the try block. We assume that we always have room for
171 * 1 extra level on the stack here - i.e. we throw the error on us
172 * starting to use the last level. */
173 if (ctx->error.top + 2 >= ctx->error.stack + nelem(ctx->error.stack))
174 {
175 fz_strlcpy(ctx->error.message, "exception stack overflow!", sizeof ctx->error.message);
176
177 fz_flush_warnings(ctx);
178 if (ctx->error.print)
179 ctx->error.print(ctx->error.print_user, ctx->error.message);
180
181 /* We need to arrive in the always/catch block as if throw had taken place. */
182 ctx->error.top++;
183 ctx->error.top->state = 2;
184 ctx->error.top->code = FZ_ERROR_GENERIC;
185 }
186 else
187 {
188 ctx->error.top++;
189 ctx->error.top->state = 0;
190 ctx->error.top->code = FZ_ERROR_NONE;
191 }
192 return &ctx->error.top->buffer;
193}
194
195int fz_do_try(fz_context *ctx)
196{
197#ifdef __COVERITY__
198 return 1;
199#else
200 return ctx->error.top->state == 0;
201#endif
202}
203
204int fz_do_always(fz_context *ctx)
205{
206#ifdef __COVERITY__
207 return 1;
208#else
209 if (ctx->error.top->state < 3)
210 {
211 ctx->error.top->state++;
212 return 1;
213 }
214 return 0;
215#endif
216}
217
218int fz_do_catch(fz_context *ctx)
219{
220 ctx->error.errcode = ctx->error.top->code;
221 return (ctx->error.top--)->state > 1;
222}
223
224int fz_caught(fz_context *ctx)
225{
226 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
227 return ctx->error.errcode;
228}
229
230const char *fz_caught_message(fz_context *ctx)
231{
232 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
233 return ctx->error.message;
234}
235
236/* coverity[+kill] */
237FZ_NORETURN void fz_vthrow(fz_context *ctx, int code, const char *fmt, va_list ap)
238{
239 fz_vsnprintf(ctx->error.message, sizeof ctx->error.message, fmt, ap);
240 ctx->error.message[sizeof(ctx->error.message) - 1] = 0;
241
242 if (code != FZ_ERROR_ABORT && code != FZ_ERROR_TRYLATER)
243 {
244 fz_flush_warnings(ctx);
245 if (ctx->error.print)
246 ctx->error.print(ctx->error.print_user, ctx->error.message);
247 }
248
249 throw(ctx, code);
250}
251
252/* coverity[+kill] */
253FZ_NORETURN void fz_throw(fz_context *ctx, int code, const char *fmt, ...)
254{
255 va_list ap;
256 va_start(ap, fmt);
257 fz_vthrow(ctx, code, fmt, ap);
258 va_end(ap);
259}
260
261/* coverity[+kill] */
262FZ_NORETURN void fz_rethrow(fz_context *ctx)
263{
264 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
265 throw(ctx, ctx->error.errcode);
266}
267
268void fz_rethrow_if(fz_context *ctx, int err)
269{
270 assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
271 if (ctx->error.errcode == err)
272 fz_rethrow(ctx);
273}
274