#include "mupdf/fitz.h" struct fz_output_context_s { int refs; fz_output *out; fz_output *err; }; static void std_write(fz_context *ctx, void *opaque, const void *buffer, int count); static fz_output fz_stdout_global = { &fz_stdout_global, std_write, NULL, NULL, NULL, }; static fz_output fz_stderr_global = { &fz_stderr_global, std_write, NULL, NULL, NULL, }; void fz_new_output_context(fz_context *ctx) { ctx->output = fz_malloc_struct(ctx, fz_output_context); ctx->output->refs = 1; ctx->output->out = &fz_stdout_global; ctx->output->err = &fz_stderr_global; } fz_output_context * fz_keep_output_context(fz_context *ctx) { if (!ctx) return NULL; return fz_keep_imp(ctx, ctx->output, &ctx->output->refs); } void fz_drop_output_context(fz_context *ctx) { if (!ctx || !ctx->output) return; if (fz_drop_imp(ctx, ctx->output, &ctx->output->refs)) { /* FIXME: should we flush here? closing the streams seems wrong */ fz_free(ctx, ctx->output); ctx->output = NULL; } } void fz_set_stdout(fz_context *ctx, fz_output *out) { fz_drop_output(ctx, ctx->output->out); ctx->output->out = out ? out : &fz_stdout_global; } void fz_set_stderr(fz_context *ctx, fz_output *err) { fz_drop_output(ctx, ctx->output->err); ctx->output->err = err ? err : &fz_stderr_global; } fz_output * fz_stdout(fz_context *ctx) { return ctx->output->out; } fz_output * fz_stderr(fz_context *ctx) { return ctx->output->err; } static void file_write(fz_context *ctx, void *opaque, const void *buffer, int count) { FILE *file = opaque; size_t n; if (count < 0) return; if (count == 1) { int x = putc(((unsigned char*)buffer)[0], file); if (x == EOF && ferror(file)) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno)); return; } n = fwrite(buffer, 1, count, file); if (n < (size_t)count && ferror(file)) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno)); } static void std_write(fz_context *ctx, void *opaque, const void *buffer, int count) { FILE *f = opaque == &fz_stdout_global ? stdout : opaque == &fz_stderr_global ? stderr : NULL; file_write(ctx, f, buffer, count); } static void file_seek(fz_context *ctx, void *opaque, fz_off_t off, int whence) { FILE *file = opaque; int n = fz_fseek(file, off, whence); if (n < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fseek: %s", strerror(errno)); } static fz_off_t file_tell(fz_context *ctx, void *opaque) { FILE *file = opaque; fz_off_t off = fz_ftell(file); if (off == -1) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot ftell: %s", strerror(errno)); return off; } static void file_close(fz_context *ctx, void *opaque) { FILE *file = opaque; int n = fclose(file); if (n < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fclose: %s", strerror(errno)); } fz_output * fz_new_output_with_file_ptr(fz_context *ctx, FILE *file, int close) { fz_output *out = fz_malloc_struct(ctx, fz_output); out->opaque = file; out->write = file_write; out->seek = file_seek; out->tell = file_tell; out->close = close ? file_close : NULL; return out; } fz_output * fz_new_output_with_path(fz_context *ctx, const char *filename, int append) { fz_output *out = NULL; FILE *file; if (!strcmp(filename, "/dev/null") || !fz_strcasecmp(filename, "nul:")) return NULL; file = fz_fopen(filename, append ? "ab" : "wb"); if (!file) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); fz_try(ctx) { out = fz_new_output_with_file_ptr(ctx, file, 1); } fz_catch(ctx) { fclose(file); fz_rethrow(ctx); } return out; } static void buffer_write(fz_context *ctx, void *opaque, const void *data, int len) { fz_buffer *buffer = opaque; fz_write_buffer(ctx, buffer, data, len); } static void buffer_seek(fz_context *ctx, void *opaque, fz_off_t off, int whence) { fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek in buffer: %s", strerror(errno)); } static fz_off_t buffer_tell(fz_context *ctx, void *opaque) { fz_buffer *buffer = opaque; return buffer->len; } static void buffer_close(fz_context *ctx, void *opaque) { fz_buffer *buffer = opaque; fz_drop_buffer(ctx, buffer); } fz_output * fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf) { fz_output *out = fz_malloc_struct(ctx, fz_output); out->opaque = fz_keep_buffer(ctx, buf); out->write = buffer_write; out->seek = buffer_seek; out->tell = buffer_tell; out->close = buffer_close; return out; } void fz_drop_output(fz_context *ctx, fz_output *out) { if (!out) return; if (out->close) out->close(ctx, out->opaque); if (out->opaque != &fz_stdout_global && out->opaque != &fz_stderr_global) fz_free(ctx, out); } void fz_seek_output(fz_context *ctx, fz_output *out, fz_off_t off, int whence) { if (!out) return; out->seek(ctx, out->opaque, off, whence); } fz_off_t fz_tell_output(fz_context *ctx, fz_output *out) { if (!out) return 0; return out->tell(ctx, out->opaque); } void fz_vprintf(fz_context *ctx, fz_output *out, const char *fmt, va_list old_args) { char buffer[256], *p = buffer; int len; va_list args; if (!out) return; /* First try using our fixed size buffer */ va_copy(args, old_args); len = fz_vsnprintf(buffer, sizeof buffer, fmt, args); va_copy_end(args); /* If that failed, allocate a big enough buffer */ if (len > sizeof buffer) { p = fz_malloc(ctx, len); va_copy(args, old_args); fz_vsnprintf(p, len, fmt, args); va_copy_end(args); } fz_try(ctx) out->write(ctx, out->opaque, p, len); fz_always(ctx) if (p != buffer) fz_free(ctx, p); fz_catch(ctx) fz_rethrow(ctx); } void fz_printf(fz_context *ctx, fz_output *out, const char *fmt, ...) { va_list args; if (!out) return; va_start(args, fmt); fz_vprintf(ctx, out, fmt, args); va_end(args); } void fz_save_buffer(fz_context *ctx, fz_buffer *buf, const char *filename) { fz_output *out = fz_new_output_with_path(ctx, filename, 0); fz_try(ctx) fz_write(ctx, out, buf->data, buf->len); fz_always(ctx) fz_drop_output(ctx, out); fz_catch(ctx) fz_rethrow(ctx); }