error.c 5.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
#include "mupdf/fitz.h"

#ifdef USE_OUTPUT_DEBUG_STRING
#include <windows.h>
#endif

/* Warning context */

void fz_var_imp(void *var)
{
	UNUSED(var); /* Do nothing */
}

void fz_flush_warnings(fz_context *ctx)
{
	if (ctx->warn->count > 1)
	{
		fprintf(stderr, "warning: ... repeated %d times ...\n", ctx->warn->count);
		LOGE("warning: ... repeated %d times ...\n", ctx->warn->count);
	}
	ctx->warn->message[0] = 0;
	ctx->warn->count = 0;
}

/* SumatraPDF: add filename and line number to errors and warnings */
static inline char *get_base_name(char *path)
{
#ifdef _WIN32
	char *last_sep = strrchr(path, '\\');
#else
	char *last_sep = strrchr(path, '/');
#endif
	return last_sep ? last_sep + 1 : path;
}

void fz_warn_imp(fz_context *ctx, char *file, int line, const char *fmt, ...)
{
	va_list ap;
	char buf[sizeof ctx->warn->message];

	va_start(ap, fmt);
	vsnprintf(buf, sizeof buf, fmt, ap);
	va_end(ap);
#ifdef USE_OUTPUT_DEBUG_STRING
	OutputDebugStringA(buf);
	OutputDebugStringA("\n");
#endif

	if (!strcmp(buf, ctx->warn->message))
	{
		ctx->warn->count++;
	}
	else
	{
		fz_flush_warnings(ctx);
		fprintf(stderr, "- %s:%d: %s\n", get_base_name(file), line, buf);
		LOGE("warning: %s\n", buf);
		fz_strlcpy(ctx->warn->message, buf, sizeof ctx->warn->message);
		ctx->warn->count = 1;
	}
}

/* Error context */

/* When we first setjmp, code is set to 0. Whenever we throw, we add 2 to
 * this code. Whenever we enter the always block, we add 1.
 *
 * fz_push_try sets code to 0.
 * If (fz_throw called within fz_try)
 *     fz_throw makes code = 2.
 *     If (no always block present)
 *         enter catch region with code = 2. OK.
 *     else
 *         fz_always entered as code < 3; Makes code = 3;
 *         if (fz_throw called within fz_always)
 *             fz_throw makes code = 5
 *             fz_always is not reentered.
 *             catch region entered with code = 5. OK.
 *         else
 *             catch region entered with code = 3. OK
 * else
 *     if (no always block present)
 *         catch region not entered as code = 0. OK.
 *     else
 *         fz_always entered as code < 3. makes code = 1
 *         if (fz_throw called within fz_always)
 *             fz_throw makes code = 3;
 *             fz_always NOT entered as code >= 3
 *             catch region entered with code = 3. OK.
 *         else
 *             catch region entered with code = 1.
 */

/* SumatraPDF: force crash so that we get crash report */
inline void fz_crash_abort()
{
	char *p = NULL;
	*p = 0;
}

FZ_NORETURN static void throw(fz_error_context *ex)
{
	if (ex->top >= 0)
	{
		fz_longjmp(ex->stack[ex->top].buffer, ex->stack[ex->top].code + 2);
	}
	else
	{
		fprintf(stderr, "uncaught exception: %s\n", ex->message);
		LOGE("uncaught exception: %s\n", ex->message);
#ifdef USE_OUTPUT_DEBUG_STRING
		OutputDebugStringA("uncaught exception: ");
		OutputDebugStringA(ex->message);
		OutputDebugStringA("\n");
#endif
		fz_crash_abort();
	}
}

int fz_push_try(fz_error_context *ex)
{
	assert(ex);
	ex->top++;
	/* Normal case, get out of here quick */
	if (ex->top < nelem(ex->stack)-1)
		return 1; /* We exit here, and the setjmp sets the code to 0 */
	/* We reserve the top slot on the exception stack purely to cope with
	 * the case when we overflow. If we DO hit this, then we 'throw'
	 * immediately - returning 0 stops the setjmp happening and takes us
	 * direct to the always/catch clauses. */
	assert(ex->top == nelem(ex->stack)-1);
	strcpy(ex->message, "exception stack overflow!");
	ex->stack[ex->top].code = 2;
	/* SumatraPDF: add filename and line number to errors and warnings */
	fprintf(stderr, "! %s:%d: %s\n", get_base_name(__FILE__), __LINE__, ex->message);
	LOGE("error: %s\n", ex->message);
	/* cf. http://bugs.ghostscript.com/show_bug.cgi?id=695948 */
	/* cf. http://git.ghostscript.com/?p=user/robin/mupdf.git;a=commitdiff;h=3decd1bbaeba261943fc2ff5a731deeb0c18e0b9 */
	ex->errcode = FZ_ERROR_GENERIC;
	ex->top--;
	throw(ex);
}

int fz_caught(fz_context *ctx)
{
	assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE);
	return ctx->error->errcode;
}

const char *fz_caught_message(fz_context *ctx)
{
	assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE);
	return ctx->error->message;
}

/* SumatraPDF: add filename and line number to errors and warnings */
void fz_throw_imp(fz_context *ctx, char *file, int line, int code, const char *fmt, ...)
{
	va_list args;
	ctx->error->errcode = code;
	va_start(args, fmt);
	vsnprintf(ctx->error->message, sizeof ctx->error->message, fmt, args);
	va_end(args);

	if (code != FZ_ERROR_ABORT)
	{
		fz_flush_warnings(ctx);
		fprintf(stderr, "! %s:%d: %s\n", get_base_name(file), line, ctx->error->message);
		LOGE("error: %s\n", ctx->error->message);
#ifdef USE_OUTPUT_DEBUG_STRING
		OutputDebugStringA("error: ");
		OutputDebugStringA(ctx->error->message);
		OutputDebugStringA("\n");
#endif
	}

	throw(ctx->error);
}

void fz_rethrow(fz_context *ctx)
{
	assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE);
	throw(ctx->error);
}

/* SumatraPDF: add filename and line number to errors and warnings */
void fz_rethrow_message_imp(fz_context *ctx, char *file, int line, const char *fmt, ...)
{
	va_list args;

	assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE);

	va_start(args, fmt);
	vsnprintf(ctx->error->message, sizeof ctx->error->message, fmt, args);
	va_end(args);

	if (ctx->error->errcode != FZ_ERROR_ABORT)
	{
		fz_flush_warnings(ctx);
		fprintf(stderr, "! %s:%d: %s\n", get_base_name(file), line, ctx->error->message);
		LOGE("error: %s\n", ctx->error->message);
#ifdef USE_OUTPUT_DEBUG_STRING
		OutputDebugStringA("error: ");
		OutputDebugStringA(ctx->error->message);
		OutputDebugStringA("\n");
#endif
	}

	throw(ctx->error);
}

void fz_rethrow_if(fz_context *ctx, int err)
{
	assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE);
	if (ctx->error->errcode == err)
		fz_rethrow(ctx);
}