view src/common/memgzio.c @ 331:6ec288064d49

improved do-save-corruption
author Robert McIntyre <rlm@mit.edu>
date Fri, 06 Apr 2012 12:18:58 -0500
parents f9f4f1b99eed
children
line wrap: on
line source
1 /* gzio.c -- IO on .gz files
2 * Copyright (C) 1995-2002 Jean-loup Gailly.
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 *
5 * Compile this file with -DNO_DEFLATE to avoid the compression code.
6 */
8 /* memgzio.c - IO on .gz files in memory
9 * Adapted from original gzio.c from zlib library by Forgotten
10 */
12 /* @(#) $Id: memgzio.c,v 1.5 2006/06/06 21:04:20 spacy51 Exp $ */
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
20 #include "memgzio.h"
22 #ifndef local
23 #define local static
24 #endif
26 #ifndef DEF_MEM_LEVEL
27 # define DEF_MEM_LEVEL 8
28 #endif
30 #ifndef OS_CODE
31 #define OS_CODE 3
32 #endif
34 #ifndef zmemcpy
35 #define zmemcpy memcpy
36 #endif
38 /*struct internal_state {int dummy;};*/ /* for buggy compilers */
40 #ifndef Z_BUFSIZE
41 # ifdef MAXSEG_64K
42 # define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
43 # else
44 # define Z_BUFSIZE 16384
45 # endif
46 #endif
47 #ifndef Z_PRINTF_BUFSIZE
48 # define Z_PRINTF_BUFSIZE 4096
49 #endif
51 #define ALLOC(size) malloc(size)
52 #define TRYFREE(p) \
53 {if (p) \
54 free(p);}
56 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
58 /* gzip flag byte */
59 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
60 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
61 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
62 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
63 #define COMMENT 0x10 /* bit 4 set: file comment present */
64 #define RESERVED 0xE0 /* bits 5..7: reserved */
66 typedef struct _MemFile
67 {
68 char *memory;
69 char *next;
70 int available;
71 int error;
72 char mode;
73 } MEMFILE;
75 typedef struct mem_stream
76 {
77 z_stream stream;
78 int z_err; /* error code for last stream operation */
79 int z_eof; /* set if end of input file */
80 MEMFILE *file; /* memoru file */
81 Byte * inbuf; /* input buffer */
82 Byte * outbuf; /* output buffer */
83 uLong crc; /* crc32 of uncompressed data */
84 char * msg; /* error message */
85 int transparent; /* 1 if input file is not a .gz file */
86 char mode; /* 'w' or 'r' */
87 long startpos; /* start of compressed data in file (header skipped) */
88 } mem_stream;
90 local gzFile gz_open OF((char *memory, const int available, const char *mode));
91 local int do_flush OF((gzFile file, int flush));
92 local int get_byte OF((mem_stream *s));
93 local void check_header OF((mem_stream *s));
94 local int destroy OF((mem_stream *s));
95 local void putLong OF((MEMFILE *file, uLong x));
96 local uLong getLong OF((mem_stream *s));
98 local MEMFILE *memOpen(char *memory, int available, char mode)
99 {
100 MEMFILE *f;
102 if (available <= 8)
103 return NULL;
105 if (mode != 'w' && mode != 'r')
106 return NULL;
108 f = (MEMFILE *)malloc(sizeof(MEMFILE));
110 f->memory = memory;
111 f->mode = mode;
112 f->error = 0;
114 if (mode == 'w')
115 {
116 f->available = available - 8;
117 f->next = memory + 8;
118 memory[0] = 'V';
119 memory[1] = 'B';
120 memory[2] = 'A';
121 memory[3] = ' ';
122 *((int *)(memory+4)) = 0;
123 }
124 else
125 {
126 if (memory[0] != 'V' || memory[1] != 'B' || memory[2] != 'A' ||
127 memory[3] != ' ')
128 {
129 free(f);
130 return NULL;
131 }
132 f->available = *((int *)(memory+4));
133 f->next = memory+8;
134 }
136 return f;
137 }
139 local size_t memWrite(const void *buffer, size_t size, size_t count,
140 MEMFILE *file)
141 {
142 size_t total = size*count;
144 if (file->mode != 'w')
145 {
146 file->error = 1;
147 return 0;
148 }
150 if (total > (size_t)file->available)
151 {
152 total = file->available;
153 }
154 memcpy(file->next, buffer, total);
155 file->available -= (int)total;
156 file->next += total;
157 return total;
158 }
160 local size_t memRead(void *buffer, size_t size, size_t count,
161 MEMFILE *file)
162 {
163 size_t total = size*count;
165 if (file->mode != 'r')
166 {
167 file->error = 1;
168 return 0;
169 }
171 if (file->available == 0)
172 return -1;
174 if (total > (size_t)file->available)
175 {
176 total = file->available;
177 }
178 memcpy(buffer, file->next, total);
179 file->available -= (int)total;
180 file->next += total;
181 return total;
182 }
184 local int memPutc(int c, MEMFILE *file)
185 {
186 if (file->mode != 'w')
187 {
188 file->error = 1;
189 return -1;
190 }
192 if (file->available >= 1)
193 {
194 *file->next++ = c;
195 file->available--;
196 }
197 else
198 return -1;
200 return c;
201 }
203 local long memTell(MEMFILE *f)
204 {
205 return (long)(f->next - f->memory) - 8;
206 }
208 local int memError(MEMFILE *f)
209 {
210 return f->error;
211 }
213 local int memClose(MEMFILE *f)
214 {
215 if (f->mode == 'w')
216 {
217 *((int *)(f->memory+4)) = memTell(f);
218 }
219 free(f);
220 return 0;
221 }
223 local int memPrintf(MEMFILE *f, const char *format, ...)
224 {
225 char buffer[80];
226 va_list list;
227 int len;
229 va_start(list, format);
230 len = vsprintf(buffer, format, list);
231 va_end(list);
233 return (int)memWrite(buffer, 1, len, f);
234 }
236 /* ===========================================================================
237 Opens a gzip (.gz) file for reading or writing. The mode parameter
238 is as in fopen ("rb" or "wb"). The file is given either by file descriptor
239 or path name (if fd == -1).
240 gz_open return NULL if the file could not be opened or if there was
241 insufficient memory to allocate the (de)compression state; errno
242 can be checked to distinguish the two cases (if errno is zero, the
243 zlib error is Z_MEM_ERROR).
244 */
245 local gzFile gz_open(memory, available, mode)
246 char *memory;
247 const int available;
248 const char *mode;
249 {
250 int err;
251 int level = Z_DEFAULT_COMPRESSION; /* compression level */
252 int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
253 char * p = (char *)mode;
254 mem_stream *s;
255 char fmode[80]; /* copy of mode, without the compression level */
256 char * m = fmode;
258 s = (mem_stream *)ALLOC(sizeof(mem_stream));
259 if (!s)
260 return Z_NULL;
262 s->stream.zalloc = (alloc_func)0;
263 s->stream.zfree = (free_func)0;
264 s->stream.opaque = (voidpf)0;
265 s->stream.next_in = s->inbuf = Z_NULL;
266 s->stream.next_out = s->outbuf = Z_NULL;
267 s->stream.avail_in = s->stream.avail_out = 0;
268 s->z_err = Z_OK;
269 s->z_eof = 0;
270 s->crc = crc32(0L, Z_NULL, 0);
271 s->msg = NULL;
272 s->transparent = 0;
273 s->file = NULL;
275 s->mode = '\0';
276 do
277 {
278 if (*p == 'r')
279 s->mode = 'r';
280 if (*p == 'w' || *p == 'a')
281 s->mode = 'w';
282 if (*p >= '0' && *p <= '9')
283 {
284 level = *p - '0';
285 }
286 else if (*p == 'f')
287 {
288 strategy = Z_FILTERED;
289 }
290 else if (*p == 'h')
291 {
292 strategy = Z_HUFFMAN_ONLY;
293 }
294 else
295 {
296 *m++ = *p; /* copy the mode */
297 }
298 }
299 while (*p++ && m != fmode + sizeof(fmode));
300 if (s->mode == '\0')
301 return destroy(s), (gzFile)Z_NULL;
303 if (s->mode == 'w')
304 {
305 #ifdef NO_DEFLATE
306 err = Z_STREAM_ERROR;
307 #else
308 err = deflateInit2(&(s->stream), level,
309 Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
310 /* windowBits is passed < 0 to suppress zlib header */
312 s->stream.next_out = s->outbuf = (Byte *)ALLOC(Z_BUFSIZE);
313 #endif
314 if (err != Z_OK || s->outbuf == Z_NULL)
315 {
316 return destroy(s), (gzFile)Z_NULL;
317 }
318 }
319 else
320 {
321 s->stream.next_in = s->inbuf = (Byte *)ALLOC(Z_BUFSIZE);
323 err = inflateInit2(&(s->stream), -MAX_WBITS);
324 /* windowBits is passed < 0 to tell that there is no zlib header.
325 * Note that in this case inflate *requires* an extra "dummy" byte
326 * after the compressed stream in order to complete decompression and
327 * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
328 * present after the compressed stream.
329 */
330 if (err != Z_OK || s->inbuf == Z_NULL)
331 {
332 return destroy(s), (gzFile)Z_NULL;
333 }
334 }
335 s->stream.avail_out = Z_BUFSIZE;
337 errno = 0;
338 s->file = memOpen(memory, available, s->mode);
340 if (s->file == NULL)
341 {
342 return destroy(s), (gzFile)Z_NULL;
343 }
345 if (s->mode == 'w')
346 {
347 /* Write a very simple .gz header:
348 */
349 memPrintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
350 Z_DEFLATED, 0 /*flags*/, 0, 0, 0, 0 /*time*/, 0 /*xflags*/, OS_CODE);
351 s->startpos = 10L;
352 /* We use 10L instead of ftell(s->file) to because ftell causes an
353 * fflush on some systems. This version of the library doesn't use
354 * startpos anyway in write mode, so this initialization is not
355 * necessary.
356 */
357 }
358 else
359 {
360 check_header(s); /* skip the .gz header */
361 s->startpos = (memTell(s->file) - s->stream.avail_in);
362 }
364 return (gzFile)s;
365 }
367 /* ===========================================================================
368 Opens a gzip (.gz) file for reading or writing.
369 */
370 gzFile ZEXPORT memgzopen(memory, available, mode)
371 char *memory;
372 int available;
373 const char *mode;
374 {
375 return gz_open(memory, available, mode);
376 }
378 /* ===========================================================================
379 Read a byte from a mem_stream; update next_in and avail_in. Return EOF
380 for end of file.
381 IN assertion: the stream s has been sucessfully opened for reading.
382 */
383 local int get_byte(s)
384 mem_stream *s;
385 {
386 if (s->z_eof)
387 return EOF;
388 if (s->stream.avail_in == 0)
389 {
390 errno = 0;
391 s->stream.avail_in = (uInt)memRead(s->inbuf, 1, Z_BUFSIZE, s->file);
392 if (s->stream.avail_in == 0)
393 {
394 s->z_eof = 1;
395 if (memError(s->file))
396 s->z_err = Z_ERRNO;
397 return EOF;
398 }
399 s->stream.next_in = s->inbuf;
400 }
401 s->stream.avail_in--;
402 return *(s->stream.next_in)++;
403 }
405 /* ===========================================================================
406 Check the gzip header of a mem_stream opened for reading. Set the stream
407 mode to transparent if the gzip magic header is not present; set s->err
408 to Z_DATA_ERROR if the magic header is present but the rest of the header
409 is incorrect.
410 IN assertion: the stream s has already been created sucessfully;
411 s->stream.avail_in is zero for the first time, but may be non-zero
412 for concatenated .gz files.
413 */
414 local void check_header(s)
415 mem_stream *s;
416 {
417 int method; /* method byte */
418 int flags; /* flags byte */
419 uInt len;
420 int c;
422 /* Check the gzip magic header */
423 for (len = 0; len < 2; len++)
424 {
425 c = get_byte(s);
426 if (c != gz_magic[len])
427 {
428 if (len != 0)
429 s->stream.avail_in++, s->stream.next_in--;
430 if (c != EOF)
431 {
432 s->stream.avail_in++, s->stream.next_in--;
433 s->transparent = 1;
434 }
435 s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
436 return;
437 }
438 }
439 method = get_byte(s);
440 flags = get_byte(s);
441 if (method != Z_DEFLATED || (flags & RESERVED) != 0)
442 {
443 s->z_err = Z_DATA_ERROR;
444 return;
445 }
447 /* Discard time, xflags and OS code: */
448 for (len = 0; len < 6; len++)
449 (void)get_byte(s);
451 if ((flags & EXTRA_FIELD) != 0) /* skip the extra field */
452 {
453 len = (uInt)get_byte(s);
454 len += ((uInt)get_byte(s))<<8;
455 /* len is garbage if EOF but the loop below will quit anyway */
456 while (len-- != 0 && get_byte(s) != EOF)
457 ;
458 }
459 if ((flags & ORIG_NAME) != 0) /* skip the original file name */
460 {
461 while ((c = get_byte(s)) != 0 && c != EOF)
462 ;
463 }
464 if ((flags & COMMENT) != 0) /* skip the .gz file comment */
465 {
466 while ((c = get_byte(s)) != 0 && c != EOF)
467 ;
468 }
469 if ((flags & HEAD_CRC) != 0) /* skip the header crc */
470 {
471 for (len = 0; len < 2; len++)
472 (void)get_byte(s);
473 }
474 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
475 }
477 /* ===========================================================================
478 * Cleanup then free the given mem_stream. Return a zlib error code.
479 Try freeing in the reverse order of allocations.
480 */
481 local int destroy(s)
482 mem_stream *s;
483 {
484 int err = Z_OK;
486 if (!s)
487 return Z_STREAM_ERROR;
489 TRYFREE(s->msg);
491 if (s->stream.state != NULL)
492 {
493 if (s->mode == 'w')
494 {
495 #ifdef NO_DEFLATE
496 err = Z_STREAM_ERROR;
497 #else
498 err = deflateEnd(&(s->stream));
499 #endif
500 }
501 else if (s->mode == 'r')
502 {
503 err = inflateEnd(&(s->stream));
504 }
505 }
506 if (s->file != NULL && memClose(s->file))
507 {
508 #ifdef ESPIPE
509 if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
510 #endif
511 err = Z_ERRNO;
512 }
513 if (s->z_err < 0)
514 err = s->z_err;
516 TRYFREE(s->inbuf);
517 TRYFREE(s->outbuf);
518 TRYFREE(s);
519 return err;
520 }
522 /* ===========================================================================
523 Reads the given number of uncompressed bytes from the compressed file.
524 gzread returns the number of bytes actually read (0 for end of file).
525 */
526 int ZEXPORT memgzread(file, buf, len)
527 gzFile file;
528 voidp buf;
529 unsigned len;
530 {
531 mem_stream *s = (mem_stream *)file;
532 Bytef * start = (Bytef *)buf; /* starting point for crc computation */
533 Byte * next_out; /* == stream.next_out but not forced far (for MSDOS) */
535 if (s == NULL || s->mode != 'r')
536 return Z_STREAM_ERROR;
538 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
539 return -1;
540 if (s->z_err == Z_STREAM_END)
541 return 0; /* EOF */
543 next_out = (Byte *)buf;
544 s->stream.next_out = (Bytef *)buf;
545 s->stream.avail_out = len;
547 while (s->stream.avail_out != 0)
548 {
549 if (s->transparent)
550 {
551 /* Copy first the lookahead bytes: */
552 uInt n = s->stream.avail_in;
553 if (n > s->stream.avail_out)
554 n = s->stream.avail_out;
555 if (n > 0)
556 {
557 zmemcpy(s->stream.next_out, s->stream.next_in, n);
558 next_out += n;
559 s->stream.next_out = next_out;
560 s->stream.next_in += n;
561 s->stream.avail_out -= n;
562 s->stream.avail_in -= n;
563 }
564 if (s->stream.avail_out > 0)
565 {
566 s->stream.avail_out -= (uInt)memRead(next_out, 1, s->stream.avail_out, s->file);
567 }
568 len -= s->stream.avail_out;
569 s->stream.total_in += (uLong)len;
570 s->stream.total_out += (uLong)len;
571 if (len == 0)
572 s->z_eof = 1;
573 return (int)len;
574 }
575 if (s->stream.avail_in == 0 && !s->z_eof)
576 {
577 errno = 0;
578 s->stream.avail_in = (uInt)memRead(s->inbuf, 1, Z_BUFSIZE, s->file);
579 if (s->stream.avail_in == 0)
580 {
581 s->z_eof = 1;
582 if (memError(s->file))
583 {
584 s->z_err = Z_ERRNO;
585 break;
586 }
587 }
588 s->stream.next_in = s->inbuf;
589 }
590 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
592 if (s->z_err == Z_STREAM_END)
593 {
594 /* Check CRC and original size */
595 s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
596 start = s->stream.next_out;
598 if (getLong(s) != s->crc)
599 {
600 s->z_err = Z_DATA_ERROR;
601 }
602 else
603 {
604 (void)getLong(s);
605 /* The uncompressed length returned by above getlong() may
606 * be different from s->stream.total_out) in case of
607 * concatenated .gz files. Check for such files:
608 */
609 check_header(s);
610 if (s->z_err == Z_OK)
611 {
612 uLong total_in = s->stream.total_in;
613 uLong total_out = s->stream.total_out;
615 inflateReset(&(s->stream));
616 s->stream.total_in = total_in;
617 s->stream.total_out = total_out;
618 s->crc = crc32(0L, Z_NULL, 0);
619 }
620 }
621 }
622 if (s->z_err != Z_OK || s->z_eof)
623 break;
624 }
625 s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
627 return (int)(len - s->stream.avail_out);
628 }
630 #ifndef NO_DEFLATE
631 /* ===========================================================================
632 Writes the given number of uncompressed bytes into the compressed file.
633 gzwrite returns the number of bytes actually written (0 in case of error).
634 */
635 int ZEXPORT memgzwrite(file, buf, len)
636 gzFile file;
637 const voidp buf;
638 unsigned len;
639 {
640 mem_stream *s = (mem_stream *)file;
642 if (s == NULL || s->mode != 'w')
643 return Z_STREAM_ERROR;
645 s->stream.next_in = (Bytef *)buf;
646 s->stream.avail_in = len;
648 while (s->stream.avail_in != 0)
649 {
650 if (s->stream.avail_out == 0)
651 {
652 s->stream.next_out = s->outbuf;
653 if (memWrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE)
654 {
655 s->z_err = Z_ERRNO;
656 break;
657 }
658 s->stream.avail_out = Z_BUFSIZE;
659 }
660 s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
661 if (s->z_err != Z_OK)
662 break;
663 }
664 s->crc = crc32(s->crc, (const Bytef *)buf, len);
666 return (int)(len - s->stream.avail_in);
667 }
668 #endif
669 /* ===========================================================================
670 Flushes all pending output into the compressed file. The parameter
671 flush is as in the deflate() function.
672 */
673 local int do_flush(file, flush)
674 gzFile file;
675 int flush;
676 {
677 uInt len;
678 int done = 0;
679 mem_stream *s = (mem_stream *)file;
681 if (s == NULL || s->mode != 'w')
682 return Z_STREAM_ERROR;
684 s->stream.avail_in = 0; /* should be zero already anyway */
686 for (;;)
687 {
688 len = Z_BUFSIZE - s->stream.avail_out;
690 if (len != 0)
691 {
692 if ((uInt)memWrite(s->outbuf, 1, len, s->file) != len)
693 {
694 s->z_err = Z_ERRNO;
695 return Z_ERRNO;
696 }
697 s->stream.next_out = s->outbuf;
698 s->stream.avail_out = Z_BUFSIZE;
699 }
700 if (done)
701 break;
702 s->z_err = deflate(&(s->stream), flush);
704 /* Ignore the second of two consecutive flushes: */
705 if (len == 0 && s->z_err == Z_BUF_ERROR)
706 s->z_err = Z_OK;
708 /* deflate has finished flushing only when it hasn't used up
709 * all the available space in the output buffer:
710 */
711 done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
713 if (s->z_err != Z_OK && s->z_err != Z_STREAM_END)
714 break;
715 }
716 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
717 }
719 /* ===========================================================================
720 Outputs a long in LSB order to the given file
721 */
722 local void putLong(file, x)
723 MEMFILE *file;
724 uLong x;
725 {
726 int n;
727 for (n = 0; n < 4; n++)
728 {
729 memPutc((int)(x & 0xff), file);
730 x >>= 8;
731 }
732 }
734 /* ===========================================================================
735 Reads a long in LSB order from the given mem_stream. Sets z_err in case
736 of error.
737 */
738 local uLong getLong(s)
739 mem_stream *s;
740 {
741 uLong x = (uLong)get_byte(s);
742 int c;
744 x += ((uLong)get_byte(s))<<8;
745 x += ((uLong)get_byte(s))<<16;
746 c = get_byte(s);
747 if (c == EOF)
748 s->z_err = Z_DATA_ERROR;
749 x += ((uLong)c)<<24;
750 return x;
751 }
753 /* ===========================================================================
754 Flushes all pending output if necessary, closes the compressed file
755 and deallocates all the (de)compression state.
756 */
757 int ZEXPORT memgzclose(file)
758 gzFile file;
759 {
760 int err;
761 mem_stream *s = (mem_stream *)file;
763 if (s == NULL)
764 return Z_STREAM_ERROR;
766 if (s->mode == 'w')
767 {
768 #ifdef NO_DEFLATE
769 return Z_STREAM_ERROR;
770 #else
771 err = do_flush(file, Z_FINISH);
772 if (err != Z_OK)
773 return destroy((mem_stream *)file);
775 putLong(s->file, s->crc);
776 putLong(s->file, s->stream.total_in);
777 #endif
778 }
779 return destroy((mem_stream *)file);
780 }
782 long ZEXPORT memtell(file)
783 gzFile file;
784 {
785 mem_stream *s = (mem_stream *)file;
787 if (s == NULL)
788 return Z_STREAM_ERROR;
790 return memTell(s->file);
791 }