1 /*
2 gzmemio.c, heavily based on gzio.c from zlib-1.1.4. Some cleanup
3 and changes done by the Coin team to be able to read gzipped files
4 from memory instead of from a FILE *. The zlib copyright notice is
5 included below.
6
7 Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
8
9 This software is provided 'as-is', without any express or implied
10 warranty. In no event will the authors be held liable for any damages
11 arising from the use of this software.
12
13 Permission is granted to anyone to use this software for any purpose,
14 including commercial applications, and to alter it and redistribute it
15 freely, subject to the following restrictions:
16
17 1. The origin of this software must not be misrepresented; you must not
18 claim that you wrote the original software. If you use this software
19 in a product, an acknowledgment in the product documentation would be
20 appreciated but is not required.
21 2. Altered source versions must be plainly marked as such, and must not be
22 misrepresented as being the original software.
23 3. This notice may not be removed or altered from any source distribution.
24
25 Jean-loup Gailly Mark Adler
26 jloup@gzip.org madler@alumni.caltech.edu
27 */
28
29 #include "io/gzmemio.h"
30 #include "coindefs.h"
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif /* HAVE_CONFIG_H */
35
36 #include <cstdio>
37 #include <cstdlib>
38 #include <cstring>
39 #include <cassert>
40
41 #include "glue/zlib.h"
42
43 /* stuff copied from the zlib.h header file (we want to avoid
44 including it here so that zlib is not required to compile Coin) */
45 struct internal_state;
46 typedef void * (*alloc_func)(void * opaque, unsigned int items, unsigned int size);
47 typedef void (*free_func)(void * opaque, void * address);
48
49 #define Z_DEFAULT_COMPRESSION (-1)
50 #define Z_DEFAULT_STRATEGY 0
51 #define Z_OK 0
52 #define Z_DATA_ERROR (-3)
53 #define Z_STREAM_ERROR (-2)
54 #define Z_ERRNO (-1)
55 #define Z_STREAM_END 1
56 #define Z_NO_FLUSH 0
57 #define Z_DEFLATED 8
58 #define MAX_WBITS 15 /* 32K LZ77 window */
59
60 /* This zlib struct should never change */
61 typedef struct {
62 unsigned char * next_in; /* next input byte */
63 unsigned int avail_in; /* number of bytes available at next_in */
64 unsigned long total_in; /* total nb of input bytes read so far */
65
66 unsigned char * next_out; /* next output byte should be put there */
67 unsigned int avail_out; /* remaining free space at next_out */
68 unsigned long total_out; /* total nb of bytes output so far */
69
70 char * msg; /* last error message, NULL if no error */
71 struct internal_state * state; /* not visible by applications */
72
73 alloc_func zalloc; /* used to allocate the internal state */
74 free_func zfree; /* used to free the internal state */
75 void * opaque; /* private data object passed to zalloc and zfree */
76
77 int data_type; /* best guess about the data type: ascii or binary */
78 unsigned long adler; /* adler32 value of the uncompressed data */
79 unsigned long reserved; /* reserved for future use */
80 } z_stream;
81
82 /*
83 Needed for inflateInit2().
84 */
85 int
cc_gzm_sizeof_z_stream(void)86 cc_gzm_sizeof_z_stream(void)
87 {
88 return sizeof(z_stream);
89 }
90
91 #define Z_BUFSIZE 16384
92 #define Z_NO_DEFLATE 1
93
94 #define Z_ALLOC(size) malloc(size)
95 #define Z_TRYFREE(p) {if (p) free(p);}
96
97 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
98
99 /* gzip flag byte */
100 #define Z_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
101 #define Z_HEAD_CRC 0x02 /* bit 1 set: header CRC present */
102 #define Z_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
103 #define Z_ORIG_NAME 0x08 /* bit 3 set: original file name present */
104 #define Z_COMMENT 0x10 /* bit 4 set: file comment present */
105 #define Z_RESERVED 0xE0 /* bits 5..7: reserved */
106
107 typedef struct {
108 unsigned char * buf;
109 unsigned int buflen;
110 unsigned int currpos;
111 } cc_gzm_file;
112
113 typedef struct {
114 z_stream stream;
115 int z_err; /* error code for last stream operation */
116 int z_eof; /* set if end of input file */
117 uint8_t * inbuf; /* input buffer */
118 uint8_t * outbuf; /* output buffer */
119 uint32_t crc; /* crc32 of uncompressed data */
120 char * msg; /* error message */
121 char * path; /* path name for debugging only */
122 int transparent; /* 1 if input file is not a .gz file */
123 char mode; /* 'w' or 'r' */
124 int32_t startpos; /* start of compressed data in file (header skipped) */
125
126 /* memory file */
127 cc_gzm_file * memfile;
128 } cc_gzm_stream;
129
130 static int get_byte(cc_gzm_stream * s);
131 static void check_header(cc_gzm_stream * s);
132 static int destroy(cc_gzm_stream * s);
133 static uint32_t getInt32(cc_gzm_stream * s);
134
135 static int cc_gzm_fseek(cc_gzm_file * file, long offset, int whence);
136 static int cc_gzm_ftell(cc_gzm_file * file);
137 static size_t cc_gzm_fread(void * ptr, size_t size, size_t nmemb, cc_gzm_file * file);
138 static int cc_gzm_ferror(cc_gzm_file * file);
139
140 /* ===========================================================================
141 Opens a gzip (.gz) mem buffer for reading or writing.
142 */
143
cc_gzm_open(const uint8_t * buffer,uint32_t len)144 void * cc_gzm_open(const uint8_t * buffer, uint32_t len)
145 {
146 int err;
147 //int level = Z_DEFAULT_COMPRESSION; /* compression level */
148 //int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
149 cc_gzm_stream * s;
150
151
152 s = (cc_gzm_stream *) Z_ALLOC(sizeof(cc_gzm_stream));
153 if (!s) return NULL;
154
155 s->stream.zalloc = (alloc_func)0;
156 s->stream.zfree = (free_func)0;
157 s->stream.opaque = (void *)0;
158 s->stream.next_in = s->inbuf = NULL;
159 s->stream.next_out = s->outbuf = NULL;
160 s->stream.avail_in = s->stream.avail_out = 0;
161 s->z_err = Z_OK;
162 s->z_eof = 0;
163 s->crc = cc_zlibglue_crc32(0L, NULL, 0);
164 s->msg = NULL;
165 s->transparent = 0;
166 s->path = NULL;
167 s->memfile = NULL;
168
169 s->mode = 'r'; /* read */
170 if (s->mode == 'w') { /* not supported yet */
171 #ifdef Z_NO_DEFLATE
172 err = Z_STREAM_ERROR;
173 #else
174 err = cc_zlibglue_deflateInit2(&(s->stream), level,
175 Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
176 /* windowBits is passed < 0 to suppress zlib header */
177
178 s->stream.next_out = s->outbuf = (uint8_t*)Z_ALLOC(Z_BUFSIZE);
179 #endif
180 if (err != Z_OK || s->outbuf == NULL) {
181 destroy(s);
182 return NULL;
183 }
184 }
185 else {
186 s->memfile = (cc_gzm_file*) Z_ALLOC(sizeof(cc_gzm_file));
187 s->memfile->buf = (uint8_t*) buffer;
188 s->memfile->buflen = len;
189 s->memfile->currpos = 0;
190
191 s->stream.next_in = s->inbuf = (uint8_t*)Z_ALLOC(Z_BUFSIZE);
192
193 err = cc_zlibglue_inflateInit2(&(s->stream), -MAX_WBITS);
194 /* windowBits is passed < 0 to tell that there is no zlib header.
195 * Note that in this case inflate *requires* an extra "dummy" byte
196 * after the compressed stream in order to complete decompression and
197 * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
198 * present after the compressed stream.
199 */
200 if (err != Z_OK || s->inbuf == NULL) {
201 destroy(s);
202 return NULL;
203 }
204 }
205 s->stream.avail_out = Z_BUFSIZE;
206
207 if (s->mode == 'w') {
208 #ifndef Z_NO_DEFLATE
209 /* Write a very simple .gz header:
210 */
211 fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
212 Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
213 s->startpos = 10L;
214 /* We use 10L instead of ftell(s->file) to because ftell causes an
215 * fflush on some systems. This version of the library doesn't use
216 * startpos anyway in write mode, so this initialization is not
217 * necessary.
218 */
219 #endif /* Z_NO_DEFLATE */
220 }
221 else {
222 check_header(s); /* skip the .gz header */
223 s->startpos = (cc_gzm_ftell(s->memfile) - s->stream.avail_in);
224 }
225 return (void*) s;
226 }
227
228 /* ===========================================================================
229 Read a byte from a cc_gzm_stream; update next_in and avail_in. Return EOF
230 for end of file.
231 IN assertion: the stream s has been sucessfully opened for reading.
232 */
233 static int
get_byte(cc_gzm_stream * s)234 get_byte(cc_gzm_stream * s)
235 {
236 if (s->z_eof) return EOF;
237 if (s->stream.avail_in == 0) {
238 /* errno = 0; */
239 s->stream.avail_in = cc_gzm_fread(s->inbuf, 1, Z_BUFSIZE, s->memfile);
240 if (s->stream.avail_in == 0) {
241 s->z_eof = 1;
242 if (cc_gzm_ferror(s->memfile)) s->z_err = Z_ERRNO;
243 return EOF;
244 }
245 s->stream.next_in = s->inbuf;
246 }
247 s->stream.avail_in--;
248 return *(s->stream.next_in)++;
249 }
250
251 /* ===========================================================================
252 Check the gzip header of a cc_gzm_stream opened for reading. Set the stream
253 mode to transparent if the gzip magic header is not present; set s->err
254 to Z_DATA_ERROR if the magic header is present but the rest of the header
255 is incorrect.
256 IN assertion: the stream s has already been created sucessfully;
257 s->stream.avail_in is zero for the first time, but may be non-zero
258 for concatenated .gz files.
259 */
260 static void
check_header(cc_gzm_stream * s)261 check_header(cc_gzm_stream * s)
262 {
263 int method; /* method byte */
264 int flags; /* flags byte */
265 uint32_t len;
266 int c;
267
268 /* Check the gzip magic header */
269 for (len = 0; len < 2; len++) {
270 c = get_byte(s);
271 if (c != gz_magic[len]) {
272 if (len != 0) s->stream.avail_in++, s->stream.next_in--;
273 if (c != EOF) {
274 s->stream.avail_in++, s->stream.next_in--;
275 s->transparent = 1;
276 }
277 s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
278 return;
279 }
280 }
281 method = get_byte(s);
282 flags = get_byte(s);
283 if (method != Z_DEFLATED || (flags & Z_RESERVED) != 0) {
284 s->z_err = Z_DATA_ERROR;
285 return;
286 }
287
288 /* Discard time, xflags and OS code: */
289 for (len = 0; len < 6; len++) (void)get_byte(s);
290
291 if ((flags & Z_EXTRA_FIELD) != 0) { /* skip the extra field */
292 len = (uint32_t)get_byte(s);
293 len += ((uint32_t)get_byte(s))<<8;
294 /* len is garbage if EOF but the loop below will quit anyway */
295 while (len-- != 0 && get_byte(s) != EOF) ;
296 }
297 if ((flags & Z_ORIG_NAME) != 0) { /* skip the original file name */
298 while ((c = get_byte(s)) != 0 && c != EOF) ;
299 }
300 if ((flags & Z_COMMENT) != 0) { /* skip the .gz file comment */
301 while ((c = get_byte(s)) != 0 && c != EOF) ;
302 }
303 if ((flags & Z_HEAD_CRC) != 0) { /* skip the header crc */
304 for (len = 0; len < 2; len++) (void)get_byte(s);
305 }
306 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
307 }
308
309 /* ===========================================================================
310 * Cleanup then free the given cc_gzm_stream. Return a zlib error code.
311 Try freeing in the reverse order of allocations.
312 */
destroy(cc_gzm_stream * s)313 static int destroy (cc_gzm_stream * s)
314 {
315 int err = Z_OK;
316
317 if (!s) return Z_STREAM_ERROR;
318
319 Z_TRYFREE(s->msg);
320
321 if (s->stream.state != NULL) {
322 if (s->mode == 'w') {
323 #ifdef Z_NO_DEFLATE
324 err = Z_STREAM_ERROR;
325 #else
326 err = deflateEnd(&(s->stream));
327 #endif
328 }
329 else if (s->mode == 'r') {
330 err = cc_zlibglue_inflateEnd(&(s->stream));
331 }
332 }
333
334 if (s->z_err < 0) err = s->z_err;
335
336 Z_TRYFREE(s->memfile);
337 Z_TRYFREE(s->inbuf);
338 Z_TRYFREE(s->outbuf);
339 Z_TRYFREE(s->path);
340 Z_TRYFREE(s);
341 return err;
342 }
343
344 /* ===========================================================================
345 Reads the given number of uncompressed bytes from the compressed file.
346 gzread returns the number of bytes actually read (0 for end of file).
347 */
348 int
cc_gzm_read(void * file,void * buf,uint32_t len)349 cc_gzm_read (void * file, void * buf, uint32_t len)
350 {
351 cc_gzm_stream *s = (cc_gzm_stream*)file;
352 uint8_t *start = (uint8_t*)buf; /* starting point for crc computation */
353 uint8_t * next_out; /* == stream.next_out but not forced far (for MSDOS) */
354
355 if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
356
357 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
358 if (s->z_err == Z_STREAM_END) return 0; /* EOF */
359
360 next_out = (uint8_t*)buf;
361 s->stream.next_out = (uint8_t*)buf;
362 s->stream.avail_out = len;
363
364 while (s->stream.avail_out != 0) {
365 if (s->transparent) {
366 /* Copy first the lookahead bytes: */
367 uint32_t n = s->stream.avail_in;
368 if (n > s->stream.avail_out) n = s->stream.avail_out;
369 if (n > 0) {
370 memcpy(s->stream.next_out, s->stream.next_in, n);
371 next_out += n;
372 s->stream.next_out = next_out;
373 s->stream.next_in += n;
374 s->stream.avail_out -= n;
375 s->stream.avail_in -= n;
376 }
377 if (s->stream.avail_out > 0) {
378 s->stream.avail_out -= cc_gzm_fread(next_out, 1, s->stream.avail_out,
379 s->memfile);
380 }
381 len -= s->stream.avail_out;
382 s->stream.total_in += (uint32_t)len;
383 s->stream.total_out += (uint32_t)len;
384 if (len == 0) s->z_eof = 1;
385 return (int)len;
386 }
387 if (s->stream.avail_in == 0 && !s->z_eof) {
388
389 /* errno = 0; */
390 s->stream.avail_in = cc_gzm_fread(s->inbuf, 1, Z_BUFSIZE, s->memfile);
391 if (s->stream.avail_in == 0) {
392 s->z_eof = 1;
393 if (cc_gzm_ferror(s->memfile)) {
394 s->z_err = Z_ERRNO;
395 break;
396 }
397 }
398 s->stream.next_in = s->inbuf;
399 }
400 s->z_err = cc_zlibglue_inflate(&(s->stream), Z_NO_FLUSH);
401
402 if (s->z_err == Z_STREAM_END) {
403 /* Check CRC and original size */
404 s->crc = cc_zlibglue_crc32(s->crc, (const char*) start, (uint32_t)(s->stream.next_out - start));
405 start = s->stream.next_out;
406
407 if (getInt32(s) != s->crc) {
408 s->z_err = Z_DATA_ERROR;
409 } else {
410 (void)getInt32(s);
411 /* The uncompressed length returned by above getint32_t() may
412 * be different from s->stream.total_out) in case of
413 * concatenated .gz files. Check for such files:
414 */
415 check_header(s);
416 if (s->z_err == Z_OK) {
417 uint32_t total_in = s->stream.total_in;
418 uint32_t total_out = s->stream.total_out;
419
420 cc_zlibglue_inflateReset(&(s->stream));
421 s->stream.total_in = total_in;
422 s->stream.total_out = total_out;
423 s->crc = cc_zlibglue_crc32(0L, NULL, 0);
424 }
425 }
426 }
427 if (s->z_err != Z_OK || s->z_eof) break;
428 }
429 s->crc = cc_zlibglue_crc32(s->crc, (const char*) start, (uint32_t)(s->stream.next_out - start));
430
431 return (int)(len - s->stream.avail_out);
432 }
433
434 /* ===========================================================================
435 Reads one byte from the compressed file. gzgetc returns this byte
436 or -1 in case of end of file or error.
437 */
438 int
cc_gzm_getc(void * file)439 cc_gzm_getc(void * file)
440 {
441 unsigned char c;
442
443 return cc_gzm_read(file, &c, 1) == 1 ? c : -1;
444 }
445
446 /* ===========================================================================
447 Reads bytes from the compressed file until len-1 characters are
448 read, or a newline character is read and transferred to buf, or an
449 end-of-file condition is encountered. The string is then terminated
450 with a null character.
451 gzgets returns buf, or NULL in case of error.
452
453 The current implementation is not optimized at all.
454 */
455 char *
cc_gzm_gets(void * file,char * buf,int len)456 cc_gzm_gets(void * file, char * buf, int len)
457 {
458 char * b = buf;
459 if (buf == NULL || len <= 0) return NULL;
460
461 while (--len > 0 && cc_gzm_read(file, buf, 1) == 1 && *buf++ != '\n') ;
462 *buf = '\0';
463 return b == buf && len > 0 ? NULL : b;
464 }
465
466 #ifndef Z_NO_DEFLATE
467
468
469 /* ===========================================================================
470 * Update the compression level and strategy
471 */
472 int
cc_gzm_setparams(void * file,int level,int strategy)473 cc_gzm_setparams(void * file, int level, int strategy)
474 {
475 cc_gzm_stream *s = (cc_gzm_stream*)file;
476
477 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
478
479 /* Make room to allow flushing */
480 if (s->stream.avail_out == 0) {
481 s->stream.next_out = s->outbuf;
482 if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
483 s->z_err = Z_ERRNO;
484 }
485 s->stream.avail_out = Z_BUFSIZE;
486 }
487 return deflateParams (&(s->stream), level, strategy);
488 }
489
490 /* ===========================================================================
491 Writes the given number of uncompressed bytes into the compressed file.
492 gzwrite returns the number of bytes actually written (0 in case of error).
493 */
494 int
cc_gzm_write(void * file,void * buf,unsigned int len)495 cc_gzm_write(void * file, void * buf, unsigned int len)
496 {
497 cc_gzm_stream *s = (cc_gzm_stream*)file;
498
499 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
500
501 s->stream.next_in = (uint8_t*)buf;
502 s->stream.avail_in = len;
503
504 while (s->stream.avail_in != 0) {
505
506 if (s->stream.avail_out == 0) {
507
508 s->stream.next_out = s->outbuf;
509 if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
510 s->z_err = Z_ERRNO;
511 break;
512 }
513 s->stream.avail_out = Z_BUFSIZE;
514 }
515 s->z_err = cc_zlibglue_deflate(&(s->stream), Z_NO_FLUSH);
516 if (s->z_err != Z_OK) break;
517 }
518 s->crc = cc_zlibglue_crc32(s->crc, (const uint8_t *)buf, len);
519
520 return (int)(len - s->stream.avail_in);
521 }
522
523 /* ===========================================================================
524 Writes c, converted to an unsigned char, into the compressed file.
525 gzputc returns the value that was written, or -1 in case of error.
526 */
527 int
cc_gzm_putc(void * file,int c)528 cc_gzm_putc(void * file, int c)
529 {
530 unsigned char cc = (unsigned char) c; /* required for big endian systems */
531 return cc_gzm_write(file, &cc, 1) == 1 ? (int)cc : -1;
532 }
533
534 /* ===========================================================================
535 Writes the given null-terminated string to the compressed file, excluding
536 the terminating null character.
537 gzputs returns the number of characters written, or -1 in case of error.
538 */
539 int
cc_gzm_puts(void * file,const char * s)540 cc_gzm_puts(void * file, const char * s)
541 {
542 return gzwrite(file, (char*)s, (unsigned)strlen(s));
543 }
544
545
546 /* ===========================================================================
547 Flushes all pending output into the compressed file. The parameter
548 flush is as in the deflate() function.
549 */
do_flush(void * file,int flush)550 static int do_flush (void * file, int flush)
551 {
552 uint32_t len;
553 int done = 0;
554 cc_gzm_stream *s = (cc_gzm_stream*)file;
555
556 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
557
558 s->stream.avail_in = 0; /* should be zero already anyway */
559
560 for (;;) {
561 len = Z_BUFSIZE - s->stream.avail_out;
562
563 if (len != 0) {
564 if ((uint32_t)fwrite(s->outbuf, 1, len, s->file) != len) {
565 s->z_err = Z_ERRNO;
566 return Z_ERRNO;
567 }
568 s->stream.next_out = s->outbuf;
569 s->stream.avail_out = Z_BUFSIZE;
570 }
571 if (done) break;
572 s->z_err = cc_zlibglue_deflate(&(s->stream), flush);
573
574 /* Ignore the second of two consecutive flushes: */
575 if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
576
577 /* deflate has finished flushing only when it hasn't used up
578 * all the available space in the output buffer:
579 */
580 done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
581
582 if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
583 }
584 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
585 }
586
cc_gzm_flush(void * file,int flush)587 int cc_gzm_flush (void * file, int flush)
588 {
589 cc_gzm_stream *s = (cc_gzm_stream*)file;
590 int err = do_flush (file, flush);
591
592 if (err) return err;
593 fflush(s->file);
594 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
595 }
596 #endif /* Z_NO_DEFLATE */
597
598 /* ===========================================================================
599 Sets the starting position for the next gzread or gzwrite on the
600 given compressed file. The offset represents a number of bytes in
601 the gzseek returns the resulting offset location as measured in
602 bytes from the beginning of the uncompressed stream, or -1 in case
603 of error. SEEK_END is not implemented, returns error. In this
604 version of the library, gzseek can be extremely slow.
605 */
606 off_t
cc_gzm_seek(void * file,off_t offset,int whence)607 cc_gzm_seek(void * file, off_t offset, int whence)
608 {
609 cc_gzm_stream *s = (cc_gzm_stream*)file;
610
611 if (s == NULL || whence == SEEK_END ||
612 s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
613 return -1L;
614 }
615
616 if (s->mode == 'w') {
617 #ifdef Z_NO_DEFLATE
618 return -1L;
619 #else
620 if (whence == SEEK_SET) {
621 offset -= s->stream.total_in;
622 }
623 if (offset < 0) return -1L;
624
625 /* At this point, offset is the number of zero bytes to write. */
626 if (s->inbuf == NULL) {
627 s->inbuf = (uint8_t*)Z_ALLOC(Z_BUFSIZE); /* for seeking */
628 zmemzero(s->inbuf, Z_BUFSIZE);
629 }
630 while (offset > 0) {
631 uint32_t size = Z_BUFSIZE;
632 if (offset < Z_BUFSIZE) size = (uint32_t)offset;
633
634 size = gzwrite(file, s->inbuf, size);
635 if (size == 0) return -1L;
636
637 offset -= size;
638 }
639 return (off_t)s->stream.total_in;
640 #endif
641 }
642 /* Rest of function is for reading only */
643
644 /* compute absolute position */
645 if (whence == SEEK_CUR) {
646 offset += s->stream.total_out;
647 }
648 if (offset < 0) return -1L;
649
650 if (s->transparent) {
651 /* map to fseek */
652 s->stream.avail_in = 0;
653 s->stream.next_in = s->inbuf;
654 if (cc_gzm_fseek(s->memfile, offset, SEEK_SET) < 0) return -1L;
655
656 s->stream.total_in = s->stream.total_out = (uint32_t)offset;
657 return offset;
658 }
659
660 /* For a negative seek, rewind and use positive seek */
661 if ((uint32_t)offset >= s->stream.total_out) {
662 offset -= s->stream.total_out;
663 } else if (cc_zlibglue_gzrewind(file) < 0) {
664 return -1L;
665 }
666 /* offset is now the number of bytes to skip. */
667
668 if (offset != 0 && s->outbuf == NULL) {
669 s->outbuf = (uint8_t*)Z_ALLOC(Z_BUFSIZE);
670 }
671 while (offset > 0) {
672 int size = Z_BUFSIZE;
673 if (offset < Z_BUFSIZE) size = (int)offset;
674
675 size = cc_zlibglue_gzread(file, s->outbuf, (uint32_t)size);
676 if (size <= 0) return -1L;
677 offset -= size;
678 }
679 return (off_t) s->stream.total_out;
680 }
681
682 /* ===========================================================================
683 Rewinds input file.
684 */
cc_gzm_rewind(void * file)685 int cc_gzm_rewind(void * file)
686 {
687 cc_gzm_stream *s = (cc_gzm_stream*)file;
688
689 if (s == NULL || s->mode != 'r') return -1;
690
691 s->z_err = Z_OK;
692 s->z_eof = 0;
693 s->stream.avail_in = 0;
694 s->stream.next_in = s->inbuf;
695 s->crc = cc_zlibglue_crc32(0L, NULL, 0);
696
697 if (s->startpos == 0) { /* not a compressed file */
698 s->memfile->currpos = 0;
699 return 0;
700 }
701
702 (void) cc_zlibglue_inflateReset(&s->stream);
703 return cc_gzm_fseek(s->memfile, s->startpos, SEEK_SET);
704 }
705
706 /* ===========================================================================
707 Returns the starting position for the next gzread or gzwrite on the
708 given compressed file. This position represents a number of bytes in the
709 uncompressed data stream.
710 */
cc_gzm_tell(void * file)711 off_t cc_gzm_tell(void * file)
712 {
713 return cc_gzm_seek(file, 0L, SEEK_CUR);
714 }
715
716 /* ===========================================================================
717 Returns 1 when EOF has previously been detected reading the given
718 input stream, otherwise zero.
719 */
cc_gzm_eof(void * file)720 int cc_gzm_eof(void * file)
721 {
722 cc_gzm_stream *s = (cc_gzm_stream*)file;
723
724 return (s == NULL || s->mode != 'r') ? 0 : s->z_eof;
725 }
726
727 #ifndef Z_NO_DEFLATE
728 /* ===========================================================================
729 Outputs a int32_t in LSB order to the given file
730 */
731 static void
putInt32(cc_gzm_file * file,uint32_t x)732 putInt32(cc_gzm_file * file, uint32_t x)
733 {
734 int n;
735 for (n = 0; n < 4; n++) {
736 cc_gzm_fputc((int)(x & 0xff), file);
737 x >>= 8;
738 }
739 }
740 #endif /* Z_NO_DEFLATE */
741
742 /* ===========================================================================
743 Reads a int32_t in LSB order from the given cc_gzm_stream. Sets z_err in case
744 of error.
745 */
746 static uint32_t
getInt32(cc_gzm_stream * s)747 getInt32(cc_gzm_stream * s)
748 {
749 uint32_t x = (uint32_t)get_byte(s);
750 int c;
751
752 x += ((uint32_t)get_byte(s))<<8;
753 x += ((uint32_t)get_byte(s))<<16;
754 c = get_byte(s);
755 if (c == EOF) s->z_err = Z_DATA_ERROR;
756 x += ((uint32_t)c)<<24;
757 return x;
758 }
759
760 /* ===========================================================================
761 Flushes all pending output if necessary, closes the compressed file
762 and deallocates all the (de)compression state.
763 */
cc_gzm_close(void * file)764 int cc_gzm_close(void * file)
765 {
766 int err;
767 cc_gzm_stream *s = (cc_gzm_stream*)file;
768
769 if (s == NULL) return Z_STREAM_ERROR;
770
771 if (s->mode == 'w') {
772 #ifdef Z_NO_DEFLATE
773 err = Z_STREAM_ERROR;
774 return err;
775 #else
776 err = do_flush (file, Z_FINISH);
777 if (err != Z_OK) return destroy((cc_gzm_stream*)file);
778
779 putInt32(s->file, s->crc);
780 putInt32(s->file, s->stream.total_in);
781 #endif
782 }
783 return destroy((cc_gzm_stream*)file);
784 }
785
786 /* stdio layer */
787
788 static int
cc_gzm_fseek(cc_gzm_file * file,long offset,int whence)789 cc_gzm_fseek(cc_gzm_file * file, long offset, int whence)
790 {
791 switch (whence) {
792 case SEEK_SET:
793 if (offset > 0 && offset <= (long) file->buflen) {
794 file->currpos = offset;
795 return 0;
796 }
797 assert(0 && "illegal seek");
798 return -1;
799 break;
800 case SEEK_CUR:
801 if ((file->currpos + offset > 0) &&
802 (file->currpos + offset <= file->buflen)) {
803 file->currpos += offset;
804 return 0;
805 }
806 assert(0 && "illegal seek");
807 return -1;
808 break;
809 case SEEK_END:
810 if ((file->buflen + offset > 0) &&
811 (file->buflen + offset <= file->buflen)) {
812 file->currpos = file->buflen + offset;
813 return 0;
814 }
815 assert(0 && "illegal seek");
816 return -1;
817 break;
818 default:
819 assert(0 && "illegal whence");
820 return -1;
821 }
822 return 0;
823 }
824
825 static int
cc_gzm_ftell(cc_gzm_file * file)826 cc_gzm_ftell(cc_gzm_file * file)
827 {
828 return file->currpos;
829 }
830
831 static size_t
cc_gzm_fread(void * ptr,size_t size,size_t nmemb,cc_gzm_file * file)832 cc_gzm_fread(void * ptr, size_t size, size_t nmemb, cc_gzm_file * file)
833 {
834 uint32_t remain;
835
836 assert(size == 1); /* to simplify implementation */
837
838 remain = file->buflen - file->currpos;
839 if (remain > nmemb) remain = nmemb;
840 if (remain == 0) return 0;
841
842 (void) memcpy(ptr, file->buf + file->currpos, remain);
843 file->currpos += remain;
844 return remain;
845 }
846
847 static int
cc_gzm_ferror(cc_gzm_file * COIN_UNUSED_ARG (file))848 cc_gzm_ferror(cc_gzm_file * COIN_UNUSED_ARG(file))
849 {
850 return 0;
851 }
852
853 #undef Z_BUFSIZE
854 #undef Z_NO_DEFLATE
855 #undef Z_ALLOC
856 #undef Z_TRYFREE
857 #undef Z_ASCII_FLAG
858 #undef Z_HEAD_CRC
859 #undef Z_EXTRA_FIELD
860 #undef Z_ORIG_NAME
861 #undef Z_COMMENT
862 #undef Z_RESERVED
863