1 /* gzio.c -- IO on .gz files
2 * Copyright (C) 1995 Jean-loup Gailly.
3 * For conditions of distribution and use, see copyright notice in zlib.h
6 /* $Id: gzio.c,v 1.4 1995/04/14 14:50:52 jloup Exp $ */
12 struct internal_state {int dummy;}; /* for buggy compilers */
14 #define Z_BUFSIZE 4096
16 #define ALLOC(size) zcalloc((voidp)0, 1, size)
17 #define TRYFREE(p) {if (p) zcfree((voidp)0, p);}
19 #define GZ_MAGIC_1 0x1f
20 #define GZ_MAGIC_2 0x8b
23 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
24 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
25 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
26 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
27 #define COMMENT 0x10 /* bit 4 set: file comment present */
28 #define RESERVED 0xE0 /* bits 5..7: reserved */
34 typedef struct gz_stream {
36 int z_err; /* error code for last stream operation */
37 int z_eof; /* set if end of input file */
38 FILE *file; /* .gz file */
39 Byte *inbuf; /* input buffer */
40 Byte *outbuf; /* output buffer */
41 uLong crc; /* crc32 of uncompressed data */
42 char *msg; /* error message */
43 char *path; /* path name for debugging only */
44 int transparent; /* 1 if input file is not a .gz file */
45 char mode; /* 'w' or 'r' */
49 /* ===========================================================================
50 * Cleanup then free the given gz_stream. Return a zlib error code.
57 if (!s) return Z_STREAM_ERROR;
64 if (s->stream.state != NULL) {
66 err = deflateEnd(&(s->stream));
67 } else if (s->mode == 'r') {
68 err = inflateEnd(&(s->stream));
71 if (s->file != NULL && fclose(s->file)) {
75 return s->z_err < 0 ? s->z_err : err;
78 /* ===========================================================================
79 Opens a gzip (.gz) file for reading or writing. The mode parameter
80 is as in fopen ("rb" or "wb"). The file is given either by file descritor
81 or path name (if fd == -1).
82 gz_open return NULL if the file could not be opened or if there was
83 insufficient memory to allocate the (de)compression state; errno
84 can be checked to distinguish the two cases (if errno is zero, the
85 zlib error is Z_MEM_ERROR).
87 local gzFile gz_open (path, mode, fd)
94 gz_stream *s = (gz_stream *)ALLOC(sizeof(gz_stream));
96 if (!s) return Z_NULL;
98 s->stream.zalloc = (alloc_func)0;
99 s->stream.zfree = (free_func)0;
100 s->stream.next_in = s->inbuf = Z_NULL;
101 s->stream.next_out = s->outbuf = Z_NULL;
102 s->stream.avail_in = s->stream.avail_out = 0;
106 s->crc = crc32(0L, Z_NULL, 0);
110 s->path = (char*)ALLOC(strlen(path)+1);
111 if (s->path == NULL) {
112 return destroy(s), (gzFile)Z_NULL;
114 strcpy(s->path, path); /* do this early for debugging */
118 if (*p == 'r') s->mode = 'r';
119 if (*p == 'w') s->mode = 'w';
121 if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
123 if (s->mode == 'w') {
124 err = deflateInit2(&(s->stream), Z_DEFAULT_COMPRESSION,
125 DEFLATED, -WBITS, MEM_LEVEL, 0);
126 /* windowBits is passed < 0 to suppress zlib header */
128 s->stream.next_out = s->outbuf = ALLOC(Z_BUFSIZE);
130 if (err != Z_OK || s->outbuf == Z_NULL) {
131 return destroy(s), (gzFile)Z_NULL;
134 err = inflateInit2(&(s->stream), -WBITS);
135 s->stream.next_in = s->inbuf = ALLOC(Z_BUFSIZE);
137 if (err != Z_OK || s->inbuf == Z_NULL) {
138 return destroy(s), (gzFile)Z_NULL;
141 s->stream.avail_out = Z_BUFSIZE;
144 s->file = fd < 0 ? FOPEN(path, mode) : fdopen(fd, mode);
146 if (s->file == NULL) {
147 return destroy(s), (gzFile)Z_NULL;
149 if (s->mode == 'w') {
150 /* Write a very simple .gz header:
152 fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", GZ_MAGIC_1, GZ_MAGIC_2,
153 DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
155 /* Check and skip the header:
165 s->stream.avail_in = fread(s->inbuf, 1, 2, s->file);
166 if (s->stream.avail_in != 2 || s->inbuf[0] != GZ_MAGIC_1
167 || s->inbuf[1] != GZ_MAGIC_2) {
171 s->stream.avail_in = 0;
172 fscanf(s->file,"%c%c%4c%c%c", &method, &flags, time, &xflags, &osCode);
174 if (method != DEFLATED || feof(s->file) || (flags & RESERVED) != 0) {
175 s->z_err = Z_DATA_ERROR;
178 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
180 fscanf(s->file, "%c%c", &c1, &c2);
181 len = c1 + ((long)c2<<8);
182 fseek(s->file, len, SEEK_CUR);
184 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
185 while ((c = getc(s->file)) != 0 && c != EOF) ;
187 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
188 while ((c = getc(s->file)) != 0 && c != EOF) ;
190 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
191 fscanf(s->file, "%c%c", &c1, &c2);
194 s->z_err = Z_DATA_ERROR;
200 /* ===========================================================================
201 Opens a gzip (.gz) file for reading or writing.
203 gzFile gzopen (path, mode)
207 return gz_open (path, mode, -1);
210 /* ===========================================================================
211 Associate a gzFile with the file descriptor fd.
213 gzFile gzdopen (fd, mode)
218 sprintf(name, "_fd:%d_", fd); /* for debugging */
220 return gz_open (name, mode, fd);
223 /* ===========================================================================
224 Reads the given number of uncompressed bytes from the compressed file.
225 gzread returns the number of bytes actually read (0 for end of file).
227 int gzread (file, buf, len)
232 gz_stream *s = (gz_stream*)file;
234 if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
236 if (s->transparent) {
238 /* Copy the first two (non-magic) bytes if not done already */
239 while (s->stream.avail_in > 0 && len > 0) {
240 *((Byte*)buf)++ = *s->stream.next_in++;
241 s->stream.avail_in--;
244 if (len == 0) return n;
245 return n + fread(buf, 1, len, s->file);
247 if (s->z_err == Z_DATA_ERROR) return -1; /* bad .gz file */
248 if (s->z_err == Z_STREAM_END) return 0; /* don't read crc as data */
250 s->stream.next_out = buf;
251 s->stream.avail_out = len;
253 while (s->stream.avail_out != 0) {
255 if (s->stream.avail_in == 0 && !s->z_eof) {
259 fread(s->inbuf, 1, Z_BUFSIZE, s->file);
260 if (s->stream.avail_in == 0) {
262 } else if (s->stream.avail_in == (uInt)EOF) {
263 s->stream.avail_in = 0;
268 s->stream.next_in = s->inbuf;
270 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
272 if (s->z_err == Z_STREAM_END ||
273 s->z_err != Z_OK || s->z_eof) break;
275 len -= s->stream.avail_out;
276 s->crc = crc32(s->crc, buf, len);
280 /* ===========================================================================
281 Writes the given number of uncompressed bytes into the compressed file.
282 gzwrite returns the number of bytes actually written (0 in case of error).
284 int gzwrite (file, buf, len)
289 gz_stream *s = (gz_stream*)file;
291 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
293 s->stream.next_in = buf;
294 s->stream.avail_in = len;
296 while (s->stream.avail_in != 0) {
298 if (s->stream.avail_out == 0) {
300 s->stream.next_out = s->outbuf;
301 if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
305 s->stream.avail_out = Z_BUFSIZE;
307 s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
309 if (s->z_err != Z_OK) break;
311 s->crc = crc32(s->crc, buf, len);
313 return len - s->stream.avail_in;
316 /* ===========================================================================
317 Flushes all pending output into the compressed file. The parameter
318 flush is as in the deflate() function.
319 gzflush should be called only when strictly necessary because it can
322 int gzflush (file, flush)
328 gz_stream *s = (gz_stream*)file;
330 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
332 s->stream.avail_in = 0; /* should be zero already anyway */
335 len = Z_BUFSIZE - s->stream.avail_out;
338 if (fwrite(s->outbuf, 1, len, s->file) != len) {
342 s->stream.next_out = s->outbuf;
343 s->stream.avail_out = Z_BUFSIZE;
346 s->z_err = deflate(&(s->stream), flush);
348 if (s->z_err != Z_OK) break;
350 /* deflate has finished flushing only when it hasn't used up
351 * all the available space in the output buffer:
353 done = (s->stream.avail_out != 0);
358 /* ===========================================================================
359 Outputs a long in LSB order to the given file
361 local void putLong (file, x)
366 for (n = 0; n < 4; n++) {
367 fputc(x & 0xff, file);
372 /* ===========================================================================
373 Reads a long in LSB order from the given buffer
375 local uLong getLong (buf)
388 /* ===========================================================================
389 Flushes all pending output if necessary, closes the compressed file
390 and deallocates all the (de)compression state.
396 gz_stream *s = (gz_stream*)file;
398 if (s == NULL) return Z_STREAM_ERROR;
400 if (s->mode == 'w') {
401 gzflush (file, Z_FINISH);
402 putLong (s->file, s->crc);
403 putLong (s->file, s->stream.total_in);
405 } else if (s->mode == 'r' && s->z_err == Z_STREAM_END) {
407 /* slide CRC and original size if they are at the end of inbuf */
408 if ((n = s->stream.avail_in) < 8 && !s->z_eof) {
410 Byte *q = s->stream.next_in;
411 while (n--) { *p++ = *q++; };
413 n = s->stream.avail_in;
414 n += fread(p, 1, 8, s->file);
415 s->stream.next_in = s->inbuf;
417 /* check CRC and original size */
419 getLong(s->stream.next_in) != s->crc ||
420 getLong(s->stream.next_in + 4) != s->stream.total_out) {
422 s->z_err = Z_DATA_ERROR;
425 return destroy(file);
428 /* ===========================================================================
429 Returns the error message for the last error which occured on the
430 given compressed file. errnum is set to zlib error number. If an
431 error occured in the file system and not in the compression library,
432 errnum is set to Z_ERRNO and the application may consult errno
433 to get the exact error code.
435 char* gzerror (file, errnum)
440 gz_stream *s = (gz_stream*)file;
443 *errnum = Z_STREAM_ERROR;
444 return z_errmsg[1-Z_STREAM_ERROR];
447 if (*errnum == Z_OK) return "";
449 m = *errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg;
451 if (m == NULL || *m == '\0') m = z_errmsg[1-s->z_err];
454 s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
455 strcpy(s->msg, s->path);
456 strcat(s->msg, ": ");