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 | |
21 | void 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 | |
34 | void 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 | |
49 | void 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 | |
55 | void 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 | */ |
69 | void 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 | |
82 | void 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 | |
103 | void 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 | |
113 | void 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 | |
148 | FZ_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 | |
167 | fz_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 | |
195 | int 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 | |
204 | int 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 | |
218 | int fz_do_catch(fz_context *ctx) |
219 | { |
220 | ctx->error.errcode = ctx->error.top->code; |
221 | return (ctx->error.top--)->state > 1; |
222 | } |
223 | |
224 | int fz_caught(fz_context *ctx) |
225 | { |
226 | assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE); |
227 | return ctx->error.errcode; |
228 | } |
229 | |
230 | const 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] */ |
237 | FZ_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] */ |
253 | FZ_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] */ |
262 | FZ_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 | |
268 | void 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 | |