1 #define _LARGEFILE_SOURCE
2 #ifndef _FILE_OFFSET_BITS
3 #define _FILE_OFFSET_BITS 64
4 #endif
5 
6 #include "mupdf/fitz.h"
7 
8 #include <errno.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12 #ifdef _WIN32
13 #include <io.h>
14 #else
15 #include <unistd.h>
16 #endif
17 
18 static void
file_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)19 file_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
20 {
21 	FILE *file = opaque;
22 	size_t n;
23 
24 	if (count == 0)
25 		return;
26 
27 	if (count == 1)
28 	{
29 		int x = putc(((unsigned char*)buffer)[0], file);
30 		if (x == EOF && ferror(file))
31 			fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno));
32 		return;
33 	}
34 
35 	n = fwrite(buffer, 1, count, file);
36 	if (n < count && ferror(file))
37 		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fwrite: %s", strerror(errno));
38 }
39 
40 static void
stdout_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)41 stdout_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
42 {
43 	file_write(ctx, stdout, buffer, count);
44 }
45 
46 static fz_output fz_stdout_global = {
47 	NULL,
48 	stdout_write,
49 	NULL,
50 	NULL,
51 	NULL,
52 };
53 
fz_stdout(fz_context * ctx)54 fz_output *fz_stdout(fz_context *ctx)
55 {
56 	return &fz_stdout_global;
57 }
58 
59 static void
stderr_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)60 stderr_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
61 {
62 	file_write(ctx, stderr, buffer, count);
63 }
64 
65 static fz_output fz_stderr_global = {
66 	NULL,
67 	stderr_write,
68 	NULL,
69 	NULL,
70 	NULL,
71 };
72 
fz_stderr(fz_context * ctx)73 fz_output *fz_stderr(fz_context *ctx)
74 {
75 	return &fz_stderr_global;
76 }
77 
78 static void
file_seek(fz_context * ctx,void * opaque,int64_t off,int whence)79 file_seek(fz_context *ctx, void *opaque, int64_t off, int whence)
80 {
81 	FILE *file = opaque;
82 #ifdef _WIN32
83 	int n = _fseeki64(file, off, whence);
84 #else
85 	int n = fseeko(file, off, whence);
86 #endif
87 	if (n < 0)
88 		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot fseek: %s", strerror(errno));
89 }
90 
91 static int64_t
file_tell(fz_context * ctx,void * opaque)92 file_tell(fz_context *ctx, void *opaque)
93 {
94 	FILE *file = opaque;
95 #ifdef _WIN32
96 	int64_t off = _ftelli64(file);
97 #else
98 	int64_t off = ftello(file);
99 #endif
100 	if (off == -1)
101 		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot ftell: %s", strerror(errno));
102 	return off;
103 }
104 
105 static void
file_drop(fz_context * ctx,void * opaque)106 file_drop(fz_context *ctx, void *opaque)
107 {
108 	FILE *file = opaque;
109 	int n = fclose(file);
110 	if (n < 0)
111 		fz_warn(ctx, "cannot fclose: %s", strerror(errno));
112 }
113 
114 static fz_stream *
file_as_stream(fz_context * ctx,void * opaque)115 file_as_stream(fz_context *ctx, void *opaque)
116 {
117 	FILE *file = opaque;
118 	fflush(file);
119 	return fz_open_file_ptr_no_close(ctx, file);
120 }
121 
file_truncate(fz_context * ctx,void * opaque)122 static void file_truncate(fz_context *ctx, void *opaque)
123 {
124 	FILE *file = opaque;
125 	fflush(file);
126 
127 #ifdef _WIN32
128 	_chsize_s(fileno(file), ftell(file));
129 #else
130 	ftruncate(fileno(file), ftell(file));
131 #endif
132 }
133 
134 fz_output *
fz_new_output(fz_context * ctx,int bufsiz,void * state,fz_output_write_fn * write,fz_output_close_fn * close,fz_output_drop_fn * drop)135 fz_new_output(fz_context *ctx, int bufsiz, void *state, fz_output_write_fn *write, fz_output_close_fn *close, fz_output_drop_fn *drop)
136 {
137 	fz_output *out = NULL;
138 
139 	fz_var(out);
140 
141 	fz_try(ctx)
142 	{
143 		out = fz_malloc_struct(ctx, fz_output);
144 		out->state = state;
145 		out->write = write;
146 		out->close = close;
147 		out->drop = drop;
148 		if (bufsiz > 0)
149 		{
150 			out->bp = Memento_label(fz_malloc(ctx, bufsiz), "output_buf");
151 			out->wp = out->bp;
152 			out->ep = out->bp + bufsiz;
153 		}
154 	}
155 	fz_catch(ctx)
156 	{
157 		if (drop)
158 			drop(ctx, state);
159 		fz_free(ctx, out);
160 		fz_rethrow(ctx);
161 	}
162 	return out;
163 }
164 
null_write(fz_context * ctx,void * opaque,const void * buffer,size_t count)165 static void null_write(fz_context *ctx, void *opaque, const void *buffer, size_t count)
166 {
167 }
168 
169 fz_output *
fz_new_output_with_path(fz_context * ctx,const char * filename,int append)170 fz_new_output_with_path(fz_context *ctx, const char *filename, int append)
171 {
172 	FILE *file;
173 	fz_output *out;
174 
175 	if (filename == NULL)
176 		fz_throw(ctx, FZ_ERROR_GENERIC, "no output to write to");
177 
178 	if (!strcmp(filename, "/dev/null") || !fz_strcasecmp(filename, "nul:"))
179 		return fz_new_output(ctx, 0, NULL, null_write, NULL, NULL);
180 
181 #ifdef _WIN32
182 	/* Ensure we create a brand new file. We don't want to clobber our old file. */
183 	if (!append)
184 	{
185 		if (fz_remove_utf8(filename) < 0)
186 			if (errno != ENOENT)
187 				fz_throw(ctx, FZ_ERROR_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
188 	}
189 	file = fz_fopen_utf8(filename, append ? "rb+" : "wb+");
190 	if (append)
191 	{
192 		if (file == NULL)
193 			file = fz_fopen_utf8(filename, "wb+");
194 		else
195 			fseek(file, 0, SEEK_END);
196 	}
197 #else
198 	/* Ensure we create a brand new file. We don't want to clobber our old file. */
199 	if (!append)
200 	{
201 		if (remove(filename) < 0)
202 			if (errno != ENOENT)
203 				fz_throw(ctx, FZ_ERROR_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
204 	}
205 	file = fopen(filename, append ? "rb+" : "wb+");
206 	if (file == NULL && append)
207 		file = fopen(filename, "wb+");
208 #endif
209 	if (!file)
210 		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno));
211 
212 	setvbuf(file, NULL, _IONBF, 0); /* we do our own buffering */
213 	out = fz_new_output(ctx, 8192, file, file_write, NULL, file_drop);
214 	out->seek = file_seek;
215 	out->tell = file_tell;
216 	out->as_stream = file_as_stream;
217 	out->truncate = file_truncate;
218 
219 	return out;
220 }
221 
222 static void
buffer_write(fz_context * ctx,void * opaque,const void * data,size_t len)223 buffer_write(fz_context *ctx, void *opaque, const void *data, size_t len)
224 {
225 	fz_buffer *buffer = opaque;
226 	fz_append_data(ctx, buffer, data, len);
227 }
228 
229 static void
buffer_seek(fz_context * ctx,void * opaque,int64_t off,int whence)230 buffer_seek(fz_context *ctx, void *opaque, int64_t off, int whence)
231 {
232 	fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek in buffer: %s", strerror(errno));
233 }
234 
235 static int64_t
buffer_tell(fz_context * ctx,void * opaque)236 buffer_tell(fz_context *ctx, void *opaque)
237 {
238 	fz_buffer *buffer = opaque;
239 	return (int64_t)buffer->len;
240 }
241 
242 static void
buffer_drop(fz_context * ctx,void * opaque)243 buffer_drop(fz_context *ctx, void *opaque)
244 {
245 	fz_buffer *buffer = opaque;
246 	fz_drop_buffer(ctx, buffer);
247 }
248 
249 fz_output *
fz_new_output_with_buffer(fz_context * ctx,fz_buffer * buf)250 fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf)
251 {
252 	fz_output *out = fz_new_output(ctx, 0, fz_keep_buffer(ctx, buf), buffer_write, NULL, buffer_drop);
253 	out->seek = buffer_seek;
254 	out->tell = buffer_tell;
255 	return out;
256 }
257 
258 void
fz_close_output(fz_context * ctx,fz_output * out)259 fz_close_output(fz_context *ctx, fz_output *out)
260 {
261 	if (out == NULL)
262 		return;
263 	fz_flush_output(ctx, out);
264 	if (out->close)
265 		out->close(ctx, out->state);
266 	out->close = NULL;
267 }
268 
269 void
fz_drop_output(fz_context * ctx,fz_output * out)270 fz_drop_output(fz_context *ctx, fz_output *out)
271 {
272 	if (out)
273 	{
274 		if (out->close)
275 			fz_warn(ctx, "dropping unclosed output");
276 		if (out->drop)
277 			out->drop(ctx, out->state);
278 		fz_free(ctx, out->bp);
279 		if (out != &fz_stdout_global && out != &fz_stderr_global)
280 			fz_free(ctx, out);
281 	}
282 }
283 
284 void
fz_seek_output(fz_context * ctx,fz_output * out,int64_t off,int whence)285 fz_seek_output(fz_context *ctx, fz_output *out, int64_t off, int whence)
286 {
287 	if (out->seek == NULL)
288 		fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot seek in unseekable output stream\n");
289 	fz_flush_output(ctx, out);
290 	out->seek(ctx, out->state, off, whence);
291 }
292 
293 int64_t
fz_tell_output(fz_context * ctx,fz_output * out)294 fz_tell_output(fz_context *ctx, fz_output *out)
295 {
296 	if (out->tell == NULL)
297 		fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot tell in untellable output stream\n");
298 	if (out->bp)
299 		return out->tell(ctx, out->state) + (out->wp - out->bp);
300 	return out->tell(ctx, out->state);
301 }
302 
303 fz_stream *
fz_stream_from_output(fz_context * ctx,fz_output * out)304 fz_stream_from_output(fz_context *ctx, fz_output *out)
305 {
306 	if (out->as_stream == NULL)
307 		fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot derive input stream from output stream");
308 	fz_flush_output(ctx, out);
309 	return out->as_stream(ctx, out->state);
310 }
311 
312 void
fz_truncate_output(fz_context * ctx,fz_output * out)313 fz_truncate_output(fz_context *ctx, fz_output *out)
314 {
315 	if (out->truncate == NULL)
316 		fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot truncate this output stream");
317 	fz_flush_output(ctx, out);
318 	out->truncate(ctx, out->state);
319 }
320 
321 static void
fz_write_emit(fz_context * ctx,void * out,int c)322 fz_write_emit(fz_context *ctx, void *out, int c)
323 {
324 	fz_write_byte(ctx, out, c);
325 }
326 
327 void
fz_write_vprintf(fz_context * ctx,fz_output * out,const char * fmt,va_list args)328 fz_write_vprintf(fz_context *ctx, fz_output *out, const char *fmt, va_list args)
329 {
330 	fz_format_string(ctx, out, fz_write_emit, fmt, args);
331 }
332 
333 void
fz_write_printf(fz_context * ctx,fz_output * out,const char * fmt,...)334 fz_write_printf(fz_context *ctx, fz_output *out, const char *fmt, ...)
335 {
336 	va_list args;
337 	va_start(args, fmt);
338 	fz_format_string(ctx, out, fz_write_emit, fmt, args);
339 	va_end(args);
340 }
341 
342 void
fz_flush_output(fz_context * ctx,fz_output * out)343 fz_flush_output(fz_context *ctx, fz_output *out)
344 {
345 	if (out->wp > out->bp)
346 	{
347 		out->write(ctx, out->state, out->bp, out->wp - out->bp);
348 		out->wp = out->bp;
349 	}
350 }
351 
352 void
fz_write_byte(fz_context * ctx,fz_output * out,unsigned char x)353 fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x)
354 {
355 	if (out->bp)
356 	{
357 		if (out->wp == out->ep)
358 		{
359 			out->write(ctx, out->state, out->bp, out->wp - out->bp);
360 			out->wp = out->bp;
361 		}
362 		*out->wp++ = x;
363 	}
364 	else
365 	{
366 		out->write(ctx, out->state, &x, 1);
367 	}
368 }
369 
370 void
fz_write_char(fz_context * ctx,fz_output * out,char x)371 fz_write_char(fz_context *ctx, fz_output *out, char x)
372 {
373 	fz_write_byte(ctx, out, (unsigned char)x);
374 }
375 
376 void
fz_write_data(fz_context * ctx,fz_output * out,const void * data_,size_t size)377 fz_write_data(fz_context *ctx, fz_output *out, const void *data_, size_t size)
378 {
379 	const char *data = data_;
380 
381 	if (out->bp)
382 	{
383 		if (size >= (size_t) (out->ep - out->bp)) /* too large for buffer */
384 		{
385 			if (out->wp > out->bp)
386 			{
387 				out->write(ctx, out->state, out->bp, out->wp - out->bp);
388 				out->wp = out->bp;
389 			}
390 			out->write(ctx, out->state, data, size);
391 		}
392 		else if (out->wp + size <= out->ep) /* fits in current buffer */
393 		{
394 			memcpy(out->wp, data, size);
395 			out->wp += size;
396 		}
397 		else /* fits if we flush first */
398 		{
399 			size_t n = out->ep - out->wp;
400 			memcpy(out->wp, data, n);
401 			out->write(ctx, out->state, out->bp, out->ep - out->bp);
402 			memcpy(out->bp, data + n, size - n);
403 			out->wp = out->bp + size - n;
404 		}
405 	}
406 	else
407 	{
408 		out->write(ctx, out->state, data, size);
409 	}
410 }
411 
412 void
fz_write_string(fz_context * ctx,fz_output * out,const char * s)413 fz_write_string(fz_context *ctx, fz_output *out, const char *s)
414 {
415 	fz_write_data(ctx, out, s, strlen(s));
416 }
417 
418 void
fz_write_int32_be(fz_context * ctx,fz_output * out,int x)419 fz_write_int32_be(fz_context *ctx, fz_output *out, int x)
420 {
421 	char data[4];
422 
423 	data[0] = x>>24;
424 	data[1] = x>>16;
425 	data[2] = x>>8;
426 	data[3] = x;
427 
428 	fz_write_data(ctx, out, data, 4);
429 }
430 
431 void
fz_write_uint32_be(fz_context * ctx,fz_output * out,unsigned int x)432 fz_write_uint32_be(fz_context *ctx, fz_output *out, unsigned int x)
433 {
434 	fz_write_int32_be(ctx, out, (unsigned int)x);
435 }
436 
437 void
fz_write_int32_le(fz_context * ctx,fz_output * out,int x)438 fz_write_int32_le(fz_context *ctx, fz_output *out, int x)
439 {
440 	char data[4];
441 
442 	data[0] = x;
443 	data[1] = x>>8;
444 	data[2] = x>>16;
445 	data[3] = x>>24;
446 
447 	fz_write_data(ctx, out, data, 4);
448 }
449 
450 void
fz_write_uint32_le(fz_context * ctx,fz_output * out,unsigned int x)451 fz_write_uint32_le(fz_context *ctx, fz_output *out, unsigned int x)
452 {
453 	fz_write_int32_le(ctx, out, (int)x);
454 }
455 
456 void
fz_write_int16_be(fz_context * ctx,fz_output * out,int x)457 fz_write_int16_be(fz_context *ctx, fz_output *out, int x)
458 {
459 	char data[2];
460 
461 	data[0] = x>>8;
462 	data[1] = x;
463 
464 	fz_write_data(ctx, out, data, 2);
465 }
466 
467 void
fz_write_uint16_be(fz_context * ctx,fz_output * out,unsigned int x)468 fz_write_uint16_be(fz_context *ctx, fz_output *out, unsigned int x)
469 {
470 	fz_write_int16_be(ctx, out, (int)x);
471 }
472 
473 void
fz_write_int16_le(fz_context * ctx,fz_output * out,int x)474 fz_write_int16_le(fz_context *ctx, fz_output *out, int x)
475 {
476 	char data[2];
477 
478 	data[0] = x;
479 	data[1] = x>>8;
480 
481 	fz_write_data(ctx, out, data, 2);
482 }
483 
484 void
fz_write_uint16_le(fz_context * ctx,fz_output * out,unsigned int x)485 fz_write_uint16_le(fz_context *ctx, fz_output *out, unsigned int x)
486 {
487 	fz_write_int16_le(ctx, out, (int)x);
488 }
489 
490 void
fz_write_float_le(fz_context * ctx,fz_output * out,float f)491 fz_write_float_le(fz_context *ctx, fz_output *out, float f)
492 {
493 	union {float f; int32_t i;} u;
494 	u.f = f;
495 	fz_write_int32_le(ctx, out, u.i);
496 }
497 
498 void
fz_write_float_be(fz_context * ctx,fz_output * out,float f)499 fz_write_float_be(fz_context *ctx, fz_output *out, float f)
500 {
501 	union {float f; int32_t i;} u;
502 	u.f = f;
503 	fz_write_int32_be(ctx, out, u.i);
504 }
505 
506 void
fz_write_rune(fz_context * ctx,fz_output * out,int rune)507 fz_write_rune(fz_context *ctx, fz_output *out, int rune)
508 {
509 	char data[10];
510 	fz_write_data(ctx, out, data, fz_runetochar(data, rune));
511 }
512 
513 void
fz_write_base64(fz_context * ctx,fz_output * out,const unsigned char * data,size_t size,int newline)514 fz_write_base64(fz_context *ctx, fz_output *out, const unsigned char *data, size_t size, int newline)
515 {
516 	static const char set[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
517 	size_t i;
518 	for (i = 0; i + 3 <= size; i += 3)
519 	{
520 		int c = data[i];
521 		int d = data[i+1];
522 		int e = data[i+2];
523 		if (newline && (i & 15) == 0)
524 			fz_write_byte(ctx, out, '\n');
525 		fz_write_byte(ctx, out, set[c>>2]);
526 		fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
527 		fz_write_byte(ctx, out, set[((d&15)<<2)|(e>>6)]);
528 		fz_write_byte(ctx, out, set[e&63]);
529 	}
530 	if (size - i == 2)
531 	{
532 		int c = data[i];
533 		int d = data[i+1];
534 		fz_write_byte(ctx, out, set[c>>2]);
535 		fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]);
536 		fz_write_byte(ctx, out, set[((d&15)<<2)]);
537 		fz_write_byte(ctx, out, '=');
538 	}
539 	else if (size - i == 1)
540 	{
541 		int c = data[i];
542 		fz_write_byte(ctx, out, set[c>>2]);
543 		fz_write_byte(ctx, out, set[((c&3)<<4)]);
544 		fz_write_byte(ctx, out, '=');
545 		fz_write_byte(ctx, out, '=');
546 	}
547 }
548 
549 void
fz_write_base64_buffer(fz_context * ctx,fz_output * out,fz_buffer * buf,int newline)550 fz_write_base64_buffer(fz_context *ctx, fz_output *out, fz_buffer *buf, int newline)
551 {
552 	unsigned char *data;
553 	size_t size = fz_buffer_storage(ctx, buf, &data);
554 	fz_write_base64(ctx, out, data, size, newline);
555 }
556 
557 void
fz_save_buffer(fz_context * ctx,fz_buffer * buf,const char * filename)558 fz_save_buffer(fz_context *ctx, fz_buffer *buf, const char *filename)
559 {
560 	fz_output *out = fz_new_output_with_path(ctx, filename, 0);
561 	fz_try(ctx)
562 	{
563 		fz_write_data(ctx, out, buf->data, buf->len);
564 		fz_close_output(ctx, out);
565 	}
566 	fz_always(ctx)
567 		fz_drop_output(ctx, out);
568 	fz_catch(ctx)
569 		fz_rethrow(ctx);
570 }
571 
fz_new_band_writer_of_size(fz_context * ctx,size_t size,fz_output * out)572 fz_band_writer *fz_new_band_writer_of_size(fz_context *ctx, size_t size, fz_output *out)
573 {
574 	fz_band_writer *writer = fz_calloc(ctx, size, 1);
575 	writer->out = out;
576 	return writer;
577 }
578 
fz_write_header(fz_context * ctx,fz_band_writer * writer,int w,int h,int n,int alpha,int xres,int yres,int pagenum,fz_colorspace * cs,fz_separations * seps)579 void fz_write_header(fz_context *ctx, fz_band_writer *writer, int w, int h, int n, int alpha, int xres, int yres, int pagenum, fz_colorspace *cs, fz_separations *seps)
580 {
581 	if (writer == NULL || writer->band == NULL)
582 		return;
583 
584 	writer->w = w;
585 	writer->h = h;
586 	writer->s = fz_count_active_separations(ctx, seps);
587 	writer->n = n;
588 	writer->alpha = alpha;
589 	writer->xres = xres;
590 	writer->yres = yres;
591 	writer->pagenum = pagenum;
592 	writer->line = 0;
593 	writer->seps = fz_keep_separations(ctx, seps);
594 	writer->header(ctx, writer, cs);
595 }
596 
fz_write_band(fz_context * ctx,fz_band_writer * writer,int stride,int band_height,const unsigned char * samples)597 void fz_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_height, const unsigned char *samples)
598 {
599 	if (writer == NULL || writer->band == NULL)
600 		return;
601 	if (writer->line + band_height > writer->h)
602 		band_height = writer->h - writer->line;
603 	if (band_height < 0) {
604 		fz_throw(ctx, FZ_ERROR_GENERIC, "Too much band data!");
605 	}
606 	if (band_height > 0) {
607 		writer->band(ctx, writer, stride, writer->line, band_height, samples);
608 		writer->line += band_height;
609 	}
610 	if (writer->line == writer->h && writer->trailer) {
611 		writer->trailer(ctx, writer);
612 		/* Protect against more band_height == 0 calls */
613 		writer->line++;
614 	}
615 }
616 
fz_drop_band_writer(fz_context * ctx,fz_band_writer * writer)617 void fz_drop_band_writer(fz_context *ctx, fz_band_writer *writer)
618 {
619 	if (writer == NULL)
620 		return;
621 	if (writer->drop != NULL)
622 		writer->drop(ctx, writer);
623 	fz_drop_separations(ctx, writer->seps);
624 	fz_free(ctx, writer);
625 }
626 
fz_output_supports_stream(fz_context * ctx,fz_output * out)627 int fz_output_supports_stream(fz_context *ctx, fz_output *out)
628 {
629 	return out != NULL && out->as_stream != NULL;
630 }
631