Removed silencing of gtk warning logs from gtk3.22-client.
[freeciv.git] / utility / ioz.c
blobe742e13ffe588c48c0b68d201e9190ca6e0c9a6d
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 /***********************************************************************
15 An IO layer to support transparent compression/uncompression.
16 (Currently only "required" functionality is supported.)
18 There are various reasons for making this a full-blown module
19 instead of just defining a few macros:
21 - Ability to switch between compressed and uncompressed at run-time
22 (zlib with compression level 0 saves uncompressed, but still with
23 gzip header, so non-zlib server cannot read the savefile).
25 - Flexibility to add other methods if desired (eg, bzip2, arbitrary
26 external filter program, etc).
28 FIXME: when zlib support _not_ included, should sanity check whether
29 the first few bytes are gzip marker and complain if so.
30 ***********************************************************************/
32 #ifdef HAVE_CONFIG_H
33 #include <fc_config.h>
34 #endif
36 #include "fc_prehdrs.h"
38 #include <errno.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <string.h>
43 #ifdef FREECIV_HAVE_LIBZ
44 #include <zlib.h>
45 #endif
47 #ifdef HAVE_BZLIB_H
48 #include <bzlib.h>
49 #endif
51 #ifdef HAVE_LZMA_H
52 #include <lzma.h>
53 #endif
55 /* utility */
56 #include "log.h"
57 #include "mem.h"
58 #include "shared.h"
59 #include "support.h"
61 #include "ioz.h"
63 #ifdef FREECIV_HAVE_LIBBZ2
64 struct bzip2_struct {
65 BZFILE *file;
66 FILE *plain;
67 int error;
68 int firstbyte;
69 bool eof;
71 #endif /* FREECIV_HAVE_LIBBZ2 */
73 #ifdef FREECIV_HAVE_LIBLZMA
75 #define PLAIN_FILE_BUF_SIZE (1024*1024) /* 1024kb */
76 #define XZ_DECODER_TEST_SIZE (4*1024) /* 4kb */
78 /* In my tests 7Mb proved to be not enough and with 10Mb decompression
79 succeeded in typical case. */
80 #define XZ_DECODER_MEMLIMIT (65*1024*1024) /* 65Mb */
81 #define XZ_DECODER_MEMLIMIT_STEP (25*1024*1024) /* Increase 25Mb at a time */
82 #define XZ_DECODER_MEMLIMIT_FINAL (100*1024*1024) /* 100Mb */
84 struct xz_struct {
85 lzma_stream stream;
86 int out_index;
87 uint64_t memlimit;
89 /* liblzma bug workaround. This is what stream.avail_out should be,
90 calculated correctly. Used only when reading file. */
91 int out_avail;
92 int total_read;
94 FILE *plain;
95 uint8_t *in_buf; /* uint8_t is what xz headers use */
96 uint8_t *out_buf;
97 lzma_ret error;
99 /* There seems to be bug in liblzma decompression that if all the
100 processing happened when lzma_code() last time was called with
101 LZMA_RUN action, it does not set next_out or avail_out variables
102 to sane values when lzma_code() is called with LZMA_FINISH.
103 We never call lzma_code() with LZMA_RUN action with all the input
104 we have left. This hack_byte is kept in storage in case there is no
105 more input when we try to get it. This byte can then be provided to
106 lzma_code(LZMA_FINISH) */
107 char hack_byte;
108 bool hack_byte_used;
111 static bool xz_outbuffer_to_file(fz_FILE *fp, lzma_action action);
112 static void xz_action(fz_FILE *fp, lzma_action action);
114 #endif /* FREECIV_HAVE_LIBLZMA */
116 struct mem_fzFILE {
117 bool control;
118 char *buffer;
119 int pos;
120 int size;
123 struct fz_FILE_s {
124 enum fz_method method;
125 char mode;
126 bool memory;
127 union {
128 struct mem_fzFILE mem;
129 FILE *plain; /* FZ_PLAIN */
130 #ifdef FREECIV_HAVE_LIBZ
131 gzFile zlib; /* FZ_ZLIB */
132 #endif
133 #ifdef FREECIV_HAVE_LIBBZ2
134 struct bzip2_struct bz2;
135 #endif
136 #ifdef FREECIV_HAVE_LIBLZMA
137 struct xz_struct xz;
138 #endif
139 } u;
142 /****************************************************************************
143 Validate the compression method.
144 ****************************************************************************/
145 static inline bool fz_method_is_valid(enum fz_method method)
147 switch (method) {
148 case FZ_PLAIN:
149 #ifdef FREECIV_HAVE_LIBZ
150 case FZ_ZLIB:
151 #endif
152 #ifdef FREECIV_HAVE_LIBBZ2
153 case FZ_BZIP2:
154 #endif
155 #ifdef FREECIV_HAVE_LIBLZMA
156 case FZ_XZ:
157 #endif
158 return TRUE;
160 return FALSE;
163 #define fz_method_validate(method) \
164 (fz_method_is_valid(method) ? method \
165 : (fc_assert_msg(TRUE == fz_method_is_valid(method), \
166 "Unsupported compress method %d, reverting to plain.",\
167 method), FZ_PLAIN))
170 /***************************************************************
171 Open memory buffer for reading as fz_FILE.
172 If control is TRUE, caller gives up control of the buffer
173 so ioz will free it when fz_FILE closed.
174 ***************************************************************/
175 fz_FILE *fz_from_memory(char *buffer, int size, bool control)
177 fz_FILE *fp;
179 fp = (fz_FILE *)fc_malloc(sizeof(*fp));
180 fp->memory = TRUE;
181 fp->u.mem.control = control;
182 fp->u.mem.buffer = buffer;
183 fp->u.mem.pos = 0;
184 fp->u.mem.size = size;
186 return fp;
189 /***************************************************************
190 Open file for reading/writing, like fopen.
191 Parameters compress_method and compress_level only apply
192 for writing: for reading try to use the most appropriate
193 available method.
194 Returns NULL if there was a problem; check errno for details.
195 (If errno is 0, and using FZ_ZLIB, probably had zlib error
196 Z_MEM_ERROR. Wishlist: better interface for errors?)
197 ***************************************************************/
198 fz_FILE *fz_from_file(const char *filename, const char *in_mode,
199 enum fz_method method, int compress_level)
201 fz_FILE *fp;
202 char mode[64];
204 if (!is_reg_file_for_access(filename, in_mode[0] == 'w')) {
205 return NULL;
208 fp = (fz_FILE *)fc_malloc(sizeof(*fp));
209 fp->memory = FALSE;
210 sz_strlcpy(mode, in_mode);
212 if (mode[0] == 'w') {
213 /* Writing: */
214 fp->mode = 'w';
215 } else {
216 #if defined(FREECIV_HAVE_LIBBZ2) || defined(FREECIV_HAVE_LIBLZMA)
217 char test_mode[4];
218 sz_strlcpy(test_mode, mode);
219 sz_strlcat(test_mode, "b");
220 #endif /* FREECIV_HAVE_LIBBZ2 || FREECIV_HAVE_LIBLZMA */
222 /* Reading: ignore specified method and try each: */
223 fp->mode = 'r';
225 #ifdef FREECIV_HAVE_LIBBZ2
226 /* Try to open as bzip2 file
227 This is simplest test, so do it first. */
228 method = FZ_BZIP2;
229 fp->u.bz2.plain = fc_fopen(filename, test_mode);
230 if (fp->u.bz2.plain) {
231 fp->u.bz2.file = BZ2_bzReadOpen(&fp->u.bz2.error, fp->u.bz2.plain, 1, 0,
232 NULL, 0);
233 } else {
234 /* This may currently have garbage value assigned via other union member. */
235 fp->u.bz2.file = NULL;
237 if (!fp->u.bz2.file) {
238 if (fp->u.bz2.plain) {
239 fclose(fp->u.bz2.plain);
241 free(fp);
242 return NULL;
243 } else {
244 /* Try to read first byte out of stream so we can figure out if this
245 really is bzip2 file or not. Store byte for later use */
246 char tmp;
247 int read_len;
249 /* We put error to tmp variable when we don't want to overwrite
250 * error already in fp->u.bz2.error. So calls to fz_ferror() or
251 * fz_strerror() will later return what originally went wrong,
252 * and not what happened in error recovery. */
253 int tmp_err;
255 read_len = BZ2_bzRead(&fp->u.bz2.error, fp->u.bz2.file, &tmp, 1);
256 if (fp->u.bz2.error != BZ_DATA_ERROR_MAGIC) {
257 /* bzip2 file */
258 if (fp->u.bz2.error == BZ_STREAM_END) {
259 /* We already reached end of file with our read of one byte */
260 if (read_len == 0) {
261 /* 0 byte file */
262 fp->u.bz2.firstbyte = -1;
263 } else {
264 fp->u.bz2.firstbyte = tmp;
266 fp->u.bz2.eof = TRUE;
267 } else if (fp->u.bz2.error != BZ_OK) {
268 /* Read failed */
269 BZ2_bzReadClose(&tmp_err, fp->u.bz2.file);
270 fclose(fp->u.bz2.plain);
271 free(fp);
272 return NULL;
273 } else {
274 /* Read success and we can continue reading */
275 fp->u.bz2.firstbyte = tmp;
276 fp->u.bz2.eof = FALSE;
278 fp->method = FZ_BZIP2;
279 return fp;
282 /* Not bzip2 file */
283 BZ2_bzReadClose(&tmp_err, fp->u.bz2.file);
284 fclose(fp->u.bz2.plain);
286 #endif /* FREECIV_HAVE_LIBBZ2 */
288 #ifdef FREECIV_HAVE_LIBLZMA
289 /* Try to open as xz file */
290 fp->u.xz.memlimit = XZ_DECODER_MEMLIMIT;
291 memset(&fp->u.xz.stream, 0, sizeof(lzma_stream));
292 fp->u.xz.error = lzma_stream_decoder(&fp->u.xz.stream,
293 fp->u.xz.memlimit,
294 LZMA_CONCATENATED);
295 if (fp->u.xz.error != LZMA_OK) {
296 free(fp);
297 return NULL;
299 fp->u.xz.plain = fc_fopen(filename, test_mode);
300 if (fp->u.xz.plain) {
301 size_t len = 0;
303 fp->u.xz.in_buf = fc_malloc(PLAIN_FILE_BUF_SIZE);
305 len = fread(fp->u.xz.in_buf, 1, XZ_DECODER_TEST_SIZE,
306 fp->u.xz.plain);
307 if (len > 0) {
308 lzma_action action;
310 fp->u.xz.stream.next_in = fp->u.xz.in_buf;
311 fp->u.xz.stream.avail_in = len;
312 fp->u.xz.out_buf = fc_malloc(PLAIN_FILE_BUF_SIZE);
313 fp->u.xz.stream.next_out = fp->u.xz.out_buf;
314 fp->u.xz.stream.avail_out = PLAIN_FILE_BUF_SIZE;
315 len = fread(&fp->u.xz.hack_byte, 1, 1, fp->u.xz.plain);
316 if (len > 0) {
317 fp->u.xz.hack_byte_used = TRUE;
318 action = LZMA_RUN;
319 } else {
320 fp->u.xz.hack_byte_used = FALSE;
321 action = LZMA_FINISH;
323 xz_action(fp, action);
324 if (fp->u.xz.error == LZMA_OK || fp->u.xz.error == LZMA_STREAM_END) {
325 fp->method = FZ_XZ;
326 fp->u.xz.out_index = 0;
327 fp->u.xz.total_read = 0;
328 fp->u.xz.out_avail = fp->u.xz.stream.total_out;
329 return fp;
332 free(fp->u.xz.out_buf);
334 fclose(fp->u.xz.plain);
335 lzma_end(&fp->u.xz.stream);
336 free(fp->u.xz.in_buf);
337 } else {
338 free(fp);
339 return NULL;
341 #endif /* FREECIV_HAVE_LIBLZMA */
343 #ifdef FREECIV_HAVE_LIBZ
344 method = FZ_ZLIB;
345 #else
346 method = FZ_PLAIN;
347 #endif
350 fp->method = fz_method_validate(method);
352 switch (fp->method) {
353 #ifdef FREECIV_HAVE_LIBLZMA
354 case FZ_XZ:
356 lzma_ret ret;
358 /* xz files are binary files, so we should add "b" to mode! */
359 sz_strlcat(mode,"b");
360 memset(&fp->u.xz.stream, 0, sizeof(lzma_stream));
361 ret = lzma_easy_encoder(&fp->u.xz.stream, compress_level, LZMA_CHECK_CRC32);
362 fp->u.xz.error = ret;
363 if (ret != LZMA_OK) {
364 free(fp);
365 return NULL;
367 fp->u.xz.in_buf = fc_malloc(PLAIN_FILE_BUF_SIZE);
368 fp->u.xz.stream.next_in = fp->u.xz.in_buf;
369 fp->u.xz.out_buf = fc_malloc(PLAIN_FILE_BUF_SIZE);
370 fp->u.xz.stream.next_out = fp->u.xz.out_buf;
371 fp->u.xz.stream.avail_out = PLAIN_FILE_BUF_SIZE;
372 fp->u.xz.out_index = 0;
373 fp->u.xz.total_read = 0;
374 fp->u.xz.plain = fc_fopen(filename, mode);
376 return fp;
377 #endif /* FREECIV_HAVE_LIBLZMA */
378 #ifdef FREECIV_HAVE_LIBBZ2
379 case FZ_BZIP2:
380 /* bz2 files are binary files, so we should add "b" to mode! */
381 sz_strlcat(mode,"b");
382 fp->u.bz2.plain = fc_fopen(filename, mode);
383 if (fp->u.bz2.plain) {
384 /* Open for read handled earlier */
385 fc_assert_ret_val('w' == mode[0], NULL);
386 fp->u.bz2.file = BZ2_bzWriteOpen(&fp->u.bz2.error, fp->u.bz2.plain,
387 compress_level, 1, 15);
388 if (fp->u.bz2.error != BZ_OK) {
389 int tmp_err; /* See comments for similar variable
390 * near BZ2_bzReadOpen() */
391 BZ2_bzWriteClose(&tmp_err, fp->u.bz2.file, 0, NULL, NULL);
392 fp->u.bz2.file = NULL;
395 if (!fp->u.bz2.file) {
396 if (fp->u.bz2.plain) {
397 fclose(fp->u.bz2.plain);
399 free(fp);
400 fp = NULL;
402 return fp;
403 #endif /* FREECIV_HAVE_LIBBZ2 */
404 #ifdef FREECIV_HAVE_LIBZ
405 case FZ_ZLIB:
406 /* gz files are binary files, so we should add "b" to mode! */
407 sz_strlcat(mode,"b");
408 if (mode[0] == 'w') {
409 cat_snprintf(mode, sizeof(mode), "%d", compress_level);
411 fp->u.zlib = fc_gzopen(filename, mode);
412 if (!fp->u.zlib) {
413 free(fp);
414 fp = NULL;
416 return fp;
417 #endif /* FREECIV_HAVE_LIBZ */
418 case FZ_PLAIN:
419 fp->u.plain = fc_fopen(filename, mode);
420 if (!fp->u.plain) {
421 free(fp);
422 fp = NULL;
424 return fp;
427 /* Should never happen */
428 fc_assert_msg(FALSE, "Internal error in %s() (method = %d)",
429 __FUNCTION__, fp->method);
430 free(fp);
431 return NULL;
434 /***************************************************************
435 Open uncompressed stream for reading/writing.
436 ***************************************************************/
437 fz_FILE *fz_from_stream(FILE *stream)
439 fz_FILE *fp;
441 if (!stream) {
442 return NULL;
445 fp = fc_malloc(sizeof(*fp));
446 fp->method = FZ_PLAIN;
447 fp->memory = FALSE;
448 fp->u.plain = stream;
449 return fp;
452 /***************************************************************
453 Close file, like fclose.
454 Returns 0 on success, or non-zero for problems (but don't call
455 fz_ferror in that case because the struct has already been
456 free'd; wishlist: better interface for errors?)
457 (For FZ_PLAIN returns EOF and could check errno;
458 for FZ_ZLIB: returns zlib error number; see zlib.h.)
459 ***************************************************************/
460 int fz_fclose(fz_FILE *fp)
462 int error = 0;
464 fc_assert_ret_val(NULL != fp, 1);
466 if (fp->memory) {
467 if (fp->u.mem.control) {
468 FC_FREE(fp->u.mem.buffer);
470 FC_FREE(fp);
472 return 0;
475 switch (fz_method_validate(fp->method)) {
476 #ifdef FREECIV_HAVE_LIBLZMA
477 case FZ_XZ:
478 if (fp->mode == 'w' && !xz_outbuffer_to_file(fp, LZMA_FINISH)) {
479 error = 1;
481 lzma_end(&fp->u.xz.stream);
482 free(fp->u.xz.in_buf);
483 free(fp->u.xz.out_buf);
484 fclose(fp->u.xz.plain);
485 free(fp);
486 return error;
487 #endif /* FREECIV_HAVE_LIBLZMA */
488 #ifdef FREECIV_HAVE_LIBBZ2
489 case FZ_BZIP2:
490 if ('w' == fp->mode) {
491 BZ2_bzWriteClose(&fp->u.bz2.error, fp->u.bz2.file, 0, NULL, NULL);
492 } else {
493 BZ2_bzReadClose(&fp->u.bz2.error, fp->u.bz2.file);
495 error = fp->u.bz2.error;
496 fclose(fp->u.bz2.plain);
497 free(fp);
498 return BZ_OK == error ? 0 : 1;
499 #endif /* FREECIV_HAVE_LIBBZ2 */
500 #ifdef FREECIV_HAVE_LIBZ
501 case FZ_ZLIB:
502 error = gzclose(fp->u.zlib);
503 free(fp);
504 return 0 > error ? error : 0; /* Only negative Z values are errors. */
505 #endif /* FREECIV_HAVE_LIBZ */
506 case FZ_PLAIN:
507 error = fclose(fp->u.plain);
508 free(fp);
509 return error;
512 /* Should never happen */
513 fc_assert_msg(FALSE, "Internal error in %s() (method = %d)",
514 __FUNCTION__, fp->method);
515 free(fp);
516 return 1;
519 /***************************************************************
520 Get a line, like fgets.
521 Returns NULL in case of error, or when end-of-file reached
522 and no characters have been read.
523 ***************************************************************/
524 char *fz_fgets(char *buffer, int size, fz_FILE *fp)
526 fc_assert_ret_val(NULL != fp, NULL);
528 if (fp->memory) {
529 int i, j;
531 for (i = fp->u.mem.pos, j = 0;
532 i < fp->u.mem.size && j < size - 1 /* Space for '\0' */
533 && fp->u.mem.buffer[i] != '\n'
534 && (fp->u.mem.buffer[i] != '\r'
535 || fp->u.mem.size == i + 1
536 || fp->u.mem.buffer[i + 1] != '\n'); i++) {
537 buffer[j++] = fp->u.mem.buffer[i];
540 if (j < size - 2) {
541 /* Space for both newline and terminating '\0' */
542 if (i + 1 < fp->u.mem.size
543 && fp->u.mem.buffer[i] == '\r'
544 && fp->u.mem.buffer[i + 1] == '\n') {
545 i += 2;
546 buffer[j++] = '\n';
547 } else if (i < fp->u.mem.size
548 && fp->u.mem.buffer[i] == '\n') {
549 i++;
550 buffer[j++] = '\n';
554 if (j == 0) {
555 return NULL;
558 fp->u.mem.pos = i;
559 buffer[j] = '\0';
561 return buffer;
564 switch (fz_method_validate(fp->method)) {
565 #ifdef FREECIV_HAVE_LIBLZMA
566 case FZ_XZ:
568 int i, j;
570 for (i = 0; i < size - 1; i += j) {
571 size_t len = 0;
572 bool line_end;
574 for (j = 0, line_end = FALSE; fp->u.xz.out_avail > 0
575 && !line_end
576 && j < size - i - 1;
577 j++, fp->u.xz.out_avail--) {
578 buffer[i + j] = fp->u.xz.out_buf[fp->u.xz.out_index++];
579 fp->u.xz.total_read++;
580 if (buffer[i + j] == '\n') {
581 line_end = TRUE;
585 if (line_end || size <= j + i + 1) {
586 buffer[i + j] = '\0';
587 return buffer;
590 if (fp->u.xz.hack_byte_used) {
591 size_t hblen = 0;
593 fp->u.xz.in_buf[0] = fp->u.xz.hack_byte;
594 len = fread(fp->u.xz.in_buf + 1, 1, PLAIN_FILE_BUF_SIZE - 1,
595 fp->u.xz.plain);
596 len++;
598 if (len <= 1) {
599 hblen = fread(&fp->u.xz.hack_byte, 1, 1, fp->u.xz.plain);
601 if (hblen == 0) {
602 fp->u.xz.hack_byte_used = FALSE;
605 if (len == 0) {
606 if (fp->u.xz.error == LZMA_STREAM_END) {
607 if (i + j == 0) {
608 /* Plain file read complete, and there was nothing in xz buffers
609 -> end-of-file. */
610 return NULL;
612 buffer[i + j] = '\0';
613 return buffer;
614 } else {
615 fp->u.xz.stream.next_out = fp->u.xz.out_buf;
616 fp->u.xz.stream.avail_out = PLAIN_FILE_BUF_SIZE;
617 xz_action(fp, LZMA_FINISH);
618 fp->u.xz.out_index = 0;
619 fp->u.xz.out_avail =
620 fp->u.xz.stream.total_out - fp->u.xz.total_read;
621 if (fp->u.xz.error != LZMA_OK
622 && fp->u.xz.error != LZMA_STREAM_END) {
623 return NULL;
626 } else {
627 lzma_action action;
629 fp->u.xz.stream.next_in = fp->u.xz.in_buf;
630 fp->u.xz.stream.avail_in = len;
631 fp->u.xz.stream.next_out = fp->u.xz.out_buf;
632 fp->u.xz.stream.avail_out = PLAIN_FILE_BUF_SIZE;
633 if (fp->u.xz.hack_byte_used) {
634 action = LZMA_RUN;
635 } else {
636 action = LZMA_FINISH;
638 xz_action(fp, action);
639 fp->u.xz.out_avail =
640 fp->u.xz.stream.total_out - fp->u.xz.total_read;
641 fp->u.xz.out_index = 0;
642 if (fp->u.xz.error != LZMA_OK && fp->u.xz.error != LZMA_STREAM_END) {
643 return NULL;
648 buffer[i] = '\0';
649 return buffer;
651 break;
652 #endif /* FREECIV_HAVE_LIBLZMA */
653 #ifdef FREECIV_HAVE_LIBBZ2
654 case FZ_BZIP2:
656 char *retval = NULL;
657 int i = 0;
658 int last_read;
660 /* See if first byte is already read and stored */
661 if (fp->u.bz2.firstbyte >= 0) {
662 buffer[0] = fp->u.bz2.firstbyte;
663 fp->u.bz2.firstbyte = -1;
664 i++;
665 } else {
666 if (!fp->u.bz2.eof) {
667 last_read = BZ2_bzRead(&fp->u.bz2.error, fp->u.bz2.file, buffer + i, 1);
668 i += last_read; /* 0 or 1 */
671 if (!fp->u.bz2.eof) {
672 /* Leave space for trailing zero */
673 for (; i < size - 1
674 && fp->u.bz2.error == BZ_OK && buffer[i - 1] != '\n' ;
675 i += last_read) {
676 last_read = BZ2_bzRead(&fp->u.bz2.error, fp->u.bz2.file,
677 buffer + i, 1);
679 if (fp->u.bz2.error != BZ_OK &&
680 (fp->u.bz2.error != BZ_STREAM_END ||
681 i == 0)) {
682 retval = NULL;
683 } else {
684 retval = buffer;
686 if (fp->u.bz2.error == BZ_STREAM_END) {
687 /* EOF reached. Do not BZ2_bzRead() any more. */
688 fp->u.bz2.eof = TRUE;
691 buffer[i] = '\0';
692 return retval;
694 #endif /* FREECIV_HAVE_LIBBZ2 */
695 #ifdef FREECIV_HAVE_LIBZ
696 case FZ_ZLIB:
697 return gzgets(fp->u.zlib, buffer, size);
698 #endif /* FREECIV_HAVE_LIBZ */
699 case FZ_PLAIN:
700 return fgets(buffer, size, fp->u.plain);
703 /* Should never happen */
704 fc_assert_msg(FALSE, "Internal error in %s() (method = %d)",
705 __FUNCTION__, fp->method);
706 return NULL;
709 #ifdef FREECIV_HAVE_LIBLZMA
711 /***************************************************************
712 Helper function to do given compression action and writing
713 results from output buffer to file.
714 ***************************************************************/
715 static bool xz_outbuffer_to_file(fz_FILE *fp, lzma_action action)
717 do {
718 size_t len;
719 size_t total = 0;
721 fp->u.xz.error = lzma_code(&fp->u.xz.stream, action);
723 if (fp->u.xz.error != LZMA_OK && fp->u.xz.error != LZMA_STREAM_END) {
724 return FALSE;
727 while (total < PLAIN_FILE_BUF_SIZE - fp->u.xz.stream.avail_out) {
728 len = fwrite(fp->u.xz.out_buf, 1,
729 PLAIN_FILE_BUF_SIZE - fp->u.xz.stream.avail_out - total,
730 fp->u.xz.plain);
731 total += len;
732 if (len == 0) {
733 return FALSE;
736 fp->u.xz.stream.avail_out = PLAIN_FILE_BUF_SIZE;
737 fp->u.xz.stream.next_out = fp->u.xz.out_buf;
738 } while (fp->u.xz.stream.avail_in > 0);
740 return TRUE;
743 /***************************************************************
744 Helper function to do given decompression action.
745 ***************************************************************/
746 static void xz_action(fz_FILE *fp, lzma_action action)
748 fp->u.xz.error = lzma_code(&fp->u.xz.stream, action);
749 if (fp->u.xz.error != LZMA_MEMLIMIT_ERROR) {
750 return;
753 while (fp->u.xz.error == LZMA_MEMLIMIT_ERROR
754 && fp->u.xz.memlimit < XZ_DECODER_MEMLIMIT_FINAL) {
755 fp->u.xz.memlimit += XZ_DECODER_MEMLIMIT_STEP;
756 if (fp->u.xz.memlimit > XZ_DECODER_MEMLIMIT_FINAL) {
757 fp->u.xz.memlimit = XZ_DECODER_MEMLIMIT_FINAL;
759 fp->u.xz.error = lzma_memlimit_set(&fp->u.xz.stream, fp->u.xz.memlimit);
762 fp->u.xz.error = lzma_code(&fp->u.xz.stream, action);
764 #endif /* FREECIV_HAVE_LIBLZMA */
766 /***************************************************************
767 Print formated, like fprintf.
769 Note: zlib doesn't have gzvfprintf, but thats ok because its
770 fprintf only does similar to what we do here (print to fixed
771 buffer), and in addition this way we get to use our safe
772 snprintf.
774 Returns number of (uncompressed) bytes actually written, or
775 0 on error.
776 ***************************************************************/
777 int fz_fprintf(fz_FILE *fp, const char *format, ...)
779 int num;
780 va_list ap;
782 fc_assert_ret_val(NULL != fp, 0);
783 fc_assert_ret_val(!fp->memory, 0);
785 switch (fz_method_validate(fp->method)) {
786 #ifdef FREECIV_HAVE_LIBLZMA
787 case FZ_XZ:
789 va_start(ap, format);
790 num = fc_vsnprintf((char *)fp->u.xz.in_buf, PLAIN_FILE_BUF_SIZE, format, ap);
791 va_end(ap);
793 if (num == -1) {
794 log_error("Too much data: truncated in fz_fprintf (%u)",
795 PLAIN_FILE_BUF_SIZE);
796 num = PLAIN_FILE_BUF_SIZE;
798 fp->u.xz.stream.next_in = fp->u.xz.in_buf;
799 fp->u.xz.stream.avail_in = num;
801 if (!xz_outbuffer_to_file(fp, LZMA_RUN)) {
802 return 0;
803 } else {
804 return strlen((char *)fp->u.xz.in_buf);
807 break;
808 #endif /* FREECIV_HAVE_LIBLZMA */
809 #ifdef FREECIV_HAVE_LIBBZ2
810 case FZ_BZIP2:
812 char buffer[65536];
814 va_start(ap, format);
815 num = fc_vsnprintf(buffer, sizeof(buffer), format, ap);
816 va_end(ap);
817 if (num == -1) {
818 log_error("Too much data: truncated in fz_fprintf (%lu)",
819 (unsigned long) sizeof(buffer));
821 BZ2_bzWrite(&fp->u.bz2.error, fp->u.bz2.file, buffer, strlen(buffer));
822 if (fp->u.bz2.error != BZ_OK) {
823 return 0;
824 } else {
825 return strlen(buffer);
828 #endif /* FREECIV_HAVE_LIBBZ2 */
829 #ifdef FREECIV_HAVE_LIBZ
830 case FZ_ZLIB:
832 char buffer[65536];
834 va_start(ap, format);
835 num = fc_vsnprintf(buffer, sizeof(buffer), format, ap);
836 va_end(ap);
837 if (num == -1) {
838 log_error("Too much data: truncated in fz_fprintf (%lu)",
839 (unsigned long) sizeof(buffer));
841 return gzwrite(fp->u.zlib, buffer, (unsigned int)strlen(buffer));
843 #endif /* FREECIV_HAVE_LIBZ */
844 case FZ_PLAIN:
845 va_start(ap, format);
846 num = vfprintf(fp->u.plain, format, ap);
847 va_end(ap);
848 return num;
851 /* Should never happen */
852 fc_assert_msg(FALSE, "Internal error in %s() (method = %d)",
853 __FUNCTION__, fp->method);
854 return 0;
857 /***************************************************************
858 Return non-zero if there is an error status associated with
859 this stream. Check fz_strerror for details.
860 ***************************************************************/
861 int fz_ferror(fz_FILE *fp)
863 fc_assert_ret_val(NULL != fp, 0);
865 if (fp->memory) {
866 return 0;
869 switch (fz_method_validate(fp->method)) {
870 #ifdef FREECIV_HAVE_LIBLZMA
871 case FZ_XZ:
872 if (fp->u.xz.error != LZMA_OK
873 && fp->u.xz.error != LZMA_STREAM_END) {
874 return 1;
875 } else {
876 return 0;
878 break;
879 #endif /* FREECIV_HAVE_LZMA */
880 #ifdef FREECIV_HAVE_LIBBZ2
881 case FZ_BZIP2:
882 return (BZ_OK != fp->u.bz2.error
883 && BZ_STREAM_END != fp->u.bz2.error);
884 #endif /* FREECIV_HAVE_LIBBZ2 */
885 #ifdef FREECIV_HAVE_LIBZ
886 case FZ_ZLIB:
888 int error;
890 (void) gzerror(fp->u.zlib, &error); /* Ignore string result here. */
891 return 0 > error ? error : 0; /* Only negative Z values are errors. */
893 #endif /* FREECIV_HAVE_LIBZ */
894 case FZ_PLAIN:
895 return ferror(fp->u.plain);
896 break;
899 /* Should never happen */
900 fc_assert_msg(FALSE, "Internal error in %s() (method = %d)",
901 __FUNCTION__, fp->method);
902 return 0;
905 /***************************************************************
906 Return string (pointer to static memory) containing an error
907 description associated with the file. Should only call
908 this is you know there is an error (eg, from fz_ferror()).
909 Note the error string may be based on errno, so should call
910 this immediately after problem, or possibly something else
911 might overwrite errno.
912 ***************************************************************/
913 const char *fz_strerror(fz_FILE *fp)
915 fc_assert_ret_val(NULL != fp, NULL);
916 fc_assert_ret_val(!fp->memory, NULL);
918 switch (fz_method_validate(fp->method)) {
919 #ifdef FREECIV_HAVE_LIBLZMA
920 case FZ_XZ:
922 static char xzerror[50];
923 char *cleartext = NULL;
925 switch(fp->u.xz.error) {
926 case LZMA_OK:
927 cleartext = "OK";
928 break;
929 case LZMA_STREAM_END:
930 cleartext = "Stream end";
931 break;
932 case LZMA_NO_CHECK:
933 cleartext = "No integrity check";
934 break;
935 case LZMA_UNSUPPORTED_CHECK:
936 cleartext = "Cannot calculate the integrity check";
937 break;
938 case LZMA_MEM_ERROR:
939 cleartext = "Mem error";
940 break;
941 case LZMA_MEMLIMIT_ERROR:
942 cleartext = "Memory limit reached";
943 break;
944 case LZMA_FORMAT_ERROR:
945 cleartext = "Unrecognized file format";
946 break;
947 case LZMA_OPTIONS_ERROR:
948 cleartext = "Unsupported options";
949 break;
950 case LZMA_DATA_ERROR:
951 cleartext = "Data error";
952 break;
953 case LZMA_BUF_ERROR:
954 cleartext = "Progress not possible";
955 break;
956 default:
957 break;
960 if (NULL != cleartext) {
961 fc_snprintf(xzerror, sizeof(xzerror), "XZ: \"%s\" (%d)",
962 cleartext, fp->u.xz.error);
963 } else {
964 fc_snprintf(xzerror, sizeof(xzerror), "XZ error %d",
965 fp->u.xz.error);
967 return xzerror;
969 break;
970 #endif /* FREECIV_HAVE_LIBLZMA */
971 #ifdef FREECIV_HAVE_LIBBZ2
972 case FZ_BZIP2:
974 static char bzip2error[50];
975 const char *cleartext = NULL;
977 /* Rationale for translating these:
978 * - Some of them provide usable information to user
979 * - Messages still contain numerical error code for developers
981 switch(fp->u.bz2.error) {
982 case BZ_OK:
983 cleartext = "OK";
984 break;
985 case BZ_RUN_OK:
986 cleartext = "Run ok";
987 break;
988 case BZ_FLUSH_OK:
989 cleartext = "Flush ok";
990 break;
991 case BZ_FINISH_OK:
992 cleartext = "Finish ok";
993 break;
994 case BZ_STREAM_END:
995 cleartext = "Stream end";
996 break;
997 case BZ_CONFIG_ERROR:
998 cleartext = "Config error";
999 break;
1000 case BZ_SEQUENCE_ERROR:
1001 cleartext = "Sequence error";
1002 break;
1003 case BZ_PARAM_ERROR:
1004 cleartext = "Parameter error";
1005 break;
1006 case BZ_MEM_ERROR:
1007 cleartext = "Mem error";
1008 break;
1009 case BZ_DATA_ERROR:
1010 cleartext = "Data error";
1011 break;
1012 case BZ_DATA_ERROR_MAGIC:
1013 cleartext = "Not bzip2 file";
1014 break;
1015 case BZ_IO_ERROR:
1016 cleartext = "IO error";
1017 break;
1018 case BZ_UNEXPECTED_EOF:
1019 cleartext = "Unexpected EOF";
1020 break;
1021 case BZ_OUTBUFF_FULL:
1022 cleartext = "Output buffer full";
1023 break;
1024 default:
1025 break;
1028 if (cleartext != NULL) {
1029 fc_snprintf(bzip2error, sizeof(bzip2error), "Bz2: \"%s\" (%d)",
1030 cleartext, fp->u.bz2.error);
1031 } else {
1032 fc_snprintf(bzip2error, sizeof(bzip2error), "Bz2 error %d",
1033 fp->u.bz2.error);
1035 return bzip2error;
1037 #endif /* FREECIV_HAVE_LIBBZ2 */
1038 #ifdef FREECIV_HAVE_LIBZ
1039 case FZ_ZLIB:
1041 int errnum;
1042 const char *estr = gzerror(fp->u.zlib, &errnum);
1044 return Z_ERRNO == errnum ? fc_strerror(fc_get_errno()) : estr;
1046 #endif /* FREECIV_HAVE_LIBZ */
1047 case FZ_PLAIN:
1048 return fc_strerror(fc_get_errno());
1051 /* Should never happen */
1052 fc_assert_msg(FALSE, "Internal error in %s() (method = %d)",
1053 __FUNCTION__, fp->method);
1054 return NULL;