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