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 
fz_default_error_callback(void * user,const char * message)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 
fz_default_warning_callback(void * user,const char * message)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 
fz_set_warning_callback(fz_context * ctx,void (* print)(void * user,const char * message),void * user)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 
fz_var_imp(void * var)55 void fz_var_imp(void *var)
56 {
57 	/* Do nothing */
58 }
59 
fz_flush_warnings(fz_context * ctx)60 void fz_flush_warnings(fz_context *ctx)
61 {
62 	if (ctx->warn.count > 1)
63 	{
64 		char buf[50];
65 		fz_snprintf(buf, sizeof buf, "... repeated %d times...", ctx->warn.count);
66 		if (ctx->warn.print)
67 			ctx->warn.print(ctx->warn.print_user, buf);
68 	}
69 	ctx->warn.message[0] = 0;
70 	ctx->warn.count = 0;
71 }
72 
fz_vwarn(fz_context * ctx,const char * fmt,va_list ap)73 void fz_vwarn(fz_context *ctx, const char *fmt, va_list ap)
74 {
75 	char buf[sizeof ctx->warn.message];
76 
77 	fz_vsnprintf(buf, sizeof buf, fmt, ap);
78 	buf[sizeof(buf) - 1] = 0;
79 
80 	if (!strcmp(buf, ctx->warn.message))
81 	{
82 		ctx->warn.count++;
83 	}
84 	else
85 	{
86 		fz_flush_warnings(ctx);
87 		if (ctx->warn.print)
88 			ctx->warn.print(ctx->warn.print_user, buf);
89 		fz_strlcpy(ctx->warn.message, buf, sizeof ctx->warn.message);
90 		ctx->warn.count = 1;
91 	}
92 }
93 
fz_warn(fz_context * ctx,const char * fmt,...)94 void fz_warn(fz_context *ctx, const char *fmt, ...)
95 {
96 	va_list ap;
97 	va_start(ap, fmt);
98 	fz_vwarn(ctx, fmt, ap);
99 	va_end(ap);
100 }
101 
102 /* Error context */
103 
fz_set_error_callback(fz_context * ctx,void (* print)(void * user,const char * message),void * user)104 void fz_set_error_callback(fz_context *ctx, void (*print)(void *user, const char *message), void *user)
105 {
106 	ctx->error.print_user = user;
107 	ctx->error.print = print;
108 }
109 
110 /* When we first setjmp, state is set to 0. Whenever we throw, we add 2 to
111  * this state. Whenever we enter the always block, we add 1.
112  *
113  * fz_push_try sets state to 0.
114  * If (fz_throw called within fz_try)
115  *     fz_throw makes state = 2.
116  *     If (no always block present)
117  *         enter catch region with state = 2. OK.
118  *     else
119  *         fz_always entered as state < 3; Makes state = 3;
120  *         if (fz_throw called within fz_always)
121  *             fz_throw makes state = 5
122  *             fz_always is not reentered.
123  *             catch region entered with state = 5. OK.
124  *         else
125  *             catch region entered with state = 3. OK
126  * else
127  *     if (no always block present)
128  *         catch region not entered as state = 0. OK.
129  *     else
130  *         fz_always entered as state < 3. makes state = 1
131  *         if (fz_throw called within fz_always)
132  *             fz_throw makes state = 3;
133  *             fz_always NOT entered as state >= 3
134  *             catch region entered with state = 3. OK.
135  *         else
136  *             catch region entered with state = 1.
137  */
138 
throw(fz_context * ctx,int code)139 FZ_NORETURN static void throw(fz_context *ctx, int code)
140 {
141 	if (ctx->error.top > ctx->error.stack)
142 	{
143 		ctx->error.top->state += 2;
144 		if (ctx->error.top->code != FZ_ERROR_NONE)
145 			fz_warn(ctx, "clobbering previous error code and message (throw in always block?)");
146 		ctx->error.top->code = code;
147 		fz_longjmp(ctx->error.top->buffer, 1);
148 	}
149 	else
150 	{
151 		fz_flush_warnings(ctx);
152 		if (ctx->error.print)
153 			ctx->error.print(ctx->error.print_user, "aborting process from uncaught error!");
154 		exit(EXIT_FAILURE);
155 	}
156 }
157 
fz_push_try(fz_context * ctx)158 fz_jmp_buf *fz_push_try(fz_context *ctx)
159 {
160 	/* If we would overflow the exception stack, throw an exception instead
161 	 * of entering the try block. We assume that we always have room for
162 	 * 1 extra level on the stack here - i.e. we throw the error on us
163 	 * starting to use the last level. */
164 	if (ctx->error.top + 2 >= ctx->error.stack + nelem(ctx->error.stack))
165 	{
166 		fz_strlcpy(ctx->error.message, "exception stack overflow!", sizeof ctx->error.message);
167 
168 		fz_flush_warnings(ctx);
169 		if (ctx->error.print)
170 			ctx->error.print(ctx->error.print_user, ctx->error.message);
171 
172 		/* We need to arrive in the always/catch block as if throw had taken place. */
173 		ctx->error.top++;
174 		ctx->error.top->state = 2;
175 		ctx->error.top->code = FZ_ERROR_GENERIC;
176 	}
177 	else
178 	{
179 		ctx->error.top++;
180 		ctx->error.top->state = 0;
181 		ctx->error.top->code = FZ_ERROR_NONE;
182 	}
183 	return &ctx->error.top->buffer;
184 }
185 
fz_do_try(fz_context * ctx)186 int fz_do_try(fz_context *ctx)
187 {
188 #ifdef __COVERITY__
189 	return 1;
190 #else
191 	return ctx->error.top->state == 0;
192 #endif
193 }
194 
fz_do_always(fz_context * ctx)195 int fz_do_always(fz_context *ctx)
196 {
197 #ifdef __COVERITY__
198 	return 1;
199 #else
200 	if (ctx->error.top->state < 3)
201 	{
202 		ctx->error.top->state++;
203 		return 1;
204 	}
205 	return 0;
206 #endif
207 }
208 
fz_do_catch(fz_context * ctx)209 int fz_do_catch(fz_context *ctx)
210 {
211 	ctx->error.errcode = ctx->error.top->code;
212 	return (ctx->error.top--)->state > 1;
213 }
214 
fz_caught(fz_context * ctx)215 int fz_caught(fz_context *ctx)
216 {
217 	assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
218 	return ctx->error.errcode;
219 }
220 
fz_caught_message(fz_context * ctx)221 const char *fz_caught_message(fz_context *ctx)
222 {
223 	assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
224 	return ctx->error.message;
225 }
226 
227 /* coverity[+kill] */
fz_vthrow(fz_context * ctx,int code,const char * fmt,va_list ap)228 FZ_NORETURN void fz_vthrow(fz_context *ctx, int code, const char *fmt, va_list ap)
229 {
230 	fz_vsnprintf(ctx->error.message, sizeof ctx->error.message, fmt, ap);
231 	ctx->error.message[sizeof(ctx->error.message) - 1] = 0;
232 
233 	if (code != FZ_ERROR_ABORT && code != FZ_ERROR_TRYLATER)
234 	{
235 		fz_flush_warnings(ctx);
236 		if (ctx->error.print)
237 			ctx->error.print(ctx->error.print_user, ctx->error.message);
238 	}
239 
240 	throw(ctx, code);
241 }
242 
243 /* coverity[+kill] */
fz_throw(fz_context * ctx,int code,const char * fmt,...)244 FZ_NORETURN void fz_throw(fz_context *ctx, int code, const char *fmt, ...)
245 {
246 	va_list ap;
247 	va_start(ap, fmt);
248 	fz_vthrow(ctx, code, fmt, ap);
249 	va_end(ap);
250 }
251 
252 /* coverity[+kill] */
fz_rethrow(fz_context * ctx)253 FZ_NORETURN void fz_rethrow(fz_context *ctx)
254 {
255 	assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
256 	throw(ctx, ctx->error.errcode);
257 }
258 
fz_rethrow_if(fz_context * ctx,int err)259 void fz_rethrow_if(fz_context *ctx, int err)
260 {
261 	assert(ctx && ctx->error.errcode >= FZ_ERROR_NONE);
262 	if (ctx->error.errcode == err)
263 		fz_rethrow(ctx);
264 }
265