diff options
Diffstat (limited to 'gzio.c')
-rw-r--r-- | gzio.c | 459 |
1 files changed, 459 insertions, 0 deletions
@@ -0,0 +1,459 @@ | |||
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 | ||
4 | */ | ||
5 | |||
6 | /* $Id: gzio.c,v 1.4 1995/04/14 14:50:52 jloup Exp $ */ | ||
7 | |||
8 | #include <stdio.h> | ||
9 | |||
10 | #include "zutil.h" | ||
11 | |||
12 | struct internal_state {int dummy;}; /* for buggy compilers */ | ||
13 | |||
14 | #define Z_BUFSIZE 4096 | ||
15 | |||
16 | #define ALLOC(size) zcalloc((voidp)0, 1, size) | ||
17 | #define TRYFREE(p) {if (p) zcfree((voidp)0, p);} | ||
18 | |||
19 | #define GZ_MAGIC_1 0x1f | ||
20 | #define GZ_MAGIC_2 0x8b | ||
21 | |||
22 | /* gzip flag byte */ | ||
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 */ | ||
29 | |||
30 | #ifndef SEEK_CUR | ||
31 | # define SEEK_CUR 1 | ||
32 | #endif | ||
33 | |||
34 | typedef struct gz_stream { | ||
35 | z_stream 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' */ | ||
46 | } gz_stream; | ||
47 | |||
48 | |||
49 | /* =========================================================================== | ||
50 | * Cleanup then free the given gz_stream. Return a zlib error code. | ||
51 | */ | ||
52 | local int destroy (s) | ||
53 | gz_stream *s; | ||
54 | { | ||
55 | int err = Z_OK; | ||
56 | |||
57 | if (!s) return Z_STREAM_ERROR; | ||
58 | |||
59 | TRYFREE(s->inbuf); | ||
60 | TRYFREE(s->outbuf); | ||
61 | TRYFREE(s->path); | ||
62 | TRYFREE(s->msg); | ||
63 | |||
64 | if (s->stream.state != NULL) { | ||
65 | if (s->mode == 'w') { | ||
66 | err = deflateEnd(&(s->stream)); | ||
67 | } else if (s->mode == 'r') { | ||
68 | err = inflateEnd(&(s->stream)); | ||
69 | } | ||
70 | } | ||
71 | if (s->file != NULL && fclose(s->file)) { | ||
72 | err = Z_ERRNO; | ||
73 | } | ||
74 | zcfree((voidp)0, s); | ||
75 | return s->z_err < 0 ? s->z_err : err; | ||
76 | } | ||
77 | |||
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). | ||
86 | */ | ||
87 | local gzFile gz_open (path, mode, fd) | ||
88 | char *path; | ||
89 | char *mode; | ||
90 | int fd; | ||
91 | { | ||
92 | int err; | ||
93 | char *p = mode; | ||
94 | gz_stream *s = (gz_stream *)ALLOC(sizeof(gz_stream)); | ||
95 | |||
96 | if (!s) return Z_NULL; | ||
97 | |||
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; | ||
103 | s->file = NULL; | ||
104 | s->z_err = Z_OK; | ||
105 | s->z_eof = 0; | ||
106 | s->crc = crc32(0L, Z_NULL, 0); | ||
107 | s->msg = NULL; | ||
108 | s->transparent = 0; | ||
109 | |||
110 | s->path = (char*)ALLOC(strlen(path)+1); | ||
111 | if (s->path == NULL) { | ||
112 | return destroy(s), (gzFile)Z_NULL; | ||
113 | } | ||
114 | strcpy(s->path, path); /* do this early for debugging */ | ||
115 | |||
116 | s->mode = '\0'; | ||
117 | do { | ||
118 | if (*p == 'r') s->mode = 'r'; | ||
119 | if (*p == 'w') s->mode = 'w'; | ||
120 | } while (*p++); | ||
121 | if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; | ||
122 | |||
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 */ | ||
127 | |||
128 | s->stream.next_out = s->outbuf = ALLOC(Z_BUFSIZE); | ||
129 | |||
130 | if (err != Z_OK || s->outbuf == Z_NULL) { | ||
131 | return destroy(s), (gzFile)Z_NULL; | ||
132 | } | ||
133 | } else { | ||
134 | err = inflateInit2(&(s->stream), -WBITS); | ||
135 | s->stream.next_in = s->inbuf = ALLOC(Z_BUFSIZE); | ||
136 | |||
137 | if (err != Z_OK || s->inbuf == Z_NULL) { | ||
138 | return destroy(s), (gzFile)Z_NULL; | ||
139 | } | ||
140 | } | ||
141 | s->stream.avail_out = Z_BUFSIZE; | ||
142 | |||
143 | errno = 0; | ||
144 | s->file = fd < 0 ? FOPEN(path, mode) : fdopen(fd, mode); | ||
145 | |||
146 | if (s->file == NULL) { | ||
147 | return destroy(s), (gzFile)Z_NULL; | ||
148 | } | ||
149 | if (s->mode == 'w') { | ||
150 | /* Write a very simple .gz header: | ||
151 | */ | ||
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); | ||
154 | } else { | ||
155 | /* Check and skip the header: | ||
156 | */ | ||
157 | Byte c1 = 0, c2 = 0; | ||
158 | Byte method = 0; | ||
159 | Byte flags = 0; | ||
160 | Byte xflags = 0; | ||
161 | Byte time[4]; | ||
162 | Byte osCode; | ||
163 | int c; | ||
164 | |||
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) { | ||
168 | s->transparent = 1; | ||
169 | return (gzFile)s; | ||
170 | } | ||
171 | s->stream.avail_in = 0; | ||
172 | fscanf(s->file,"%c%c%4c%c%c", &method, &flags, time, &xflags, &osCode); | ||
173 | |||
174 | if (method != DEFLATED || feof(s->file) || (flags & RESERVED) != 0) { | ||
175 | s->z_err = Z_DATA_ERROR; | ||
176 | return (gzFile)s; | ||
177 | } | ||
178 | if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ | ||
179 | long len; | ||
180 | fscanf(s->file, "%c%c", &c1, &c2); | ||
181 | len = c1 + ((long)c2<<8); | ||
182 | fseek(s->file, len, SEEK_CUR); | ||
183 | } | ||
184 | if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ | ||
185 | while ((c = getc(s->file)) != 0 && c != EOF) ; | ||
186 | } | ||
187 | if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ | ||
188 | while ((c = getc(s->file)) != 0 && c != EOF) ; | ||
189 | } | ||
190 | if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ | ||
191 | fscanf(s->file, "%c%c", &c1, &c2); | ||
192 | } | ||
193 | if (feof(s->file)) { | ||
194 | s->z_err = Z_DATA_ERROR; | ||
195 | } | ||
196 | } | ||
197 | return (gzFile)s; | ||
198 | } | ||
199 | |||
200 | /* =========================================================================== | ||
201 | Opens a gzip (.gz) file for reading or writing. | ||
202 | */ | ||
203 | gzFile gzopen (path, mode) | ||
204 | char *path; | ||
205 | char *mode; | ||
206 | { | ||
207 | return gz_open (path, mode, -1); | ||
208 | } | ||
209 | |||
210 | /* =========================================================================== | ||
211 | Associate a gzFile with the file descriptor fd. | ||
212 | */ | ||
213 | gzFile gzdopen (fd, mode) | ||
214 | int fd; | ||
215 | char *mode; | ||
216 | { | ||
217 | char name[20]; | ||
218 | sprintf(name, "_fd:%d_", fd); /* for debugging */ | ||
219 | |||
220 | return gz_open (name, mode, fd); | ||
221 | } | ||
222 | |||
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). | ||
226 | */ | ||
227 | int gzread (file, buf, len) | ||
228 | gzFile file; | ||
229 | voidp buf; | ||
230 | unsigned len; | ||
231 | { | ||
232 | gz_stream *s = (gz_stream*)file; | ||
233 | |||
234 | if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; | ||
235 | |||
236 | if (s->transparent) { | ||
237 | unsigned n = 0; | ||
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--; | ||
242 | len--; n++; | ||
243 | } | ||
244 | if (len == 0) return n; | ||
245 | return n + fread(buf, 1, len, s->file); | ||
246 | } | ||
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 */ | ||
249 | |||
250 | s->stream.next_out = buf; | ||
251 | s->stream.avail_out = len; | ||
252 | |||
253 | while (s->stream.avail_out != 0) { | ||
254 | |||
255 | if (s->stream.avail_in == 0 && !s->z_eof) { | ||
256 | |||
257 | errno = 0; | ||
258 | s->stream.avail_in = | ||
259 | fread(s->inbuf, 1, Z_BUFSIZE, s->file); | ||
260 | if (s->stream.avail_in == 0) { | ||
261 | s->z_eof = 1; | ||
262 | } else if (s->stream.avail_in == (uInt)EOF) { | ||
263 | s->stream.avail_in = 0; | ||
264 | s->z_eof = 1; | ||
265 | s->z_err = Z_ERRNO; | ||
266 | break; | ||
267 | } | ||
268 | s->stream.next_in = s->inbuf; | ||
269 | } | ||
270 | s->z_err = inflate(&(s->stream), Z_NO_FLUSH); | ||
271 | |||
272 | if (s->z_err == Z_STREAM_END || | ||
273 | s->z_err != Z_OK || s->z_eof) break; | ||
274 | } | ||
275 | len -= s->stream.avail_out; | ||
276 | s->crc = crc32(s->crc, buf, len); | ||
277 | return len; | ||
278 | } | ||
279 | |||
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). | ||
283 | */ | ||
284 | int gzwrite (file, buf, len) | ||
285 | gzFile file; | ||
286 | voidp buf; | ||
287 | unsigned len; | ||
288 | { | ||
289 | gz_stream *s = (gz_stream*)file; | ||
290 | |||
291 | if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; | ||
292 | |||
293 | s->stream.next_in = buf; | ||
294 | s->stream.avail_in = len; | ||
295 | |||
296 | while (s->stream.avail_in != 0) { | ||
297 | |||
298 | if (s->stream.avail_out == 0) { | ||
299 | |||
300 | s->stream.next_out = s->outbuf; | ||
301 | if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { | ||
302 | s->z_err = Z_ERRNO; | ||
303 | break; | ||
304 | } | ||
305 | s->stream.avail_out = Z_BUFSIZE; | ||
306 | } | ||
307 | s->z_err = deflate(&(s->stream), Z_NO_FLUSH); | ||
308 | |||
309 | if (s->z_err != Z_OK) break; | ||
310 | } | ||
311 | s->crc = crc32(s->crc, buf, len); | ||
312 | |||
313 | return len - s->stream.avail_in; | ||
314 | } | ||
315 | |||
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 | ||
320 | degrade compression. | ||
321 | */ | ||
322 | int gzflush (file, flush) | ||
323 | gzFile file; | ||
324 | int flush; | ||
325 | { | ||
326 | uInt len; | ||
327 | int done = 0; | ||
328 | gz_stream *s = (gz_stream*)file; | ||
329 | |||
330 | if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; | ||
331 | |||
332 | s->stream.avail_in = 0; /* should be zero already anyway */ | ||
333 | |||
334 | for (;;) { | ||
335 | len = Z_BUFSIZE - s->stream.avail_out; | ||
336 | |||
337 | if (len != 0) { | ||
338 | if (fwrite(s->outbuf, 1, len, s->file) != len) { | ||
339 | s->z_err = Z_ERRNO; | ||
340 | break; | ||
341 | } | ||
342 | s->stream.next_out = s->outbuf; | ||
343 | s->stream.avail_out = Z_BUFSIZE; | ||
344 | } | ||
345 | if (done) break; | ||
346 | s->z_err = deflate(&(s->stream), flush); | ||
347 | |||
348 | if (s->z_err != Z_OK) break; | ||
349 | |||
350 | /* deflate has finished flushing only when it hasn't used up | ||
351 | * all the available space in the output buffer: | ||
352 | */ | ||
353 | done = (s->stream.avail_out != 0); | ||
354 | } | ||
355 | return s->z_err; | ||
356 | } | ||
357 | |||
358 | /* =========================================================================== | ||
359 | Outputs a long in LSB order to the given file | ||
360 | */ | ||
361 | local void putLong (file, x) | ||
362 | FILE *file; | ||
363 | uLong x; | ||
364 | { | ||
365 | int n; | ||
366 | for (n = 0; n < 4; n++) { | ||
367 | fputc(x & 0xff, file); | ||
368 | x >>= 8; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | /* =========================================================================== | ||
373 | Reads a long in LSB order from the given buffer | ||
374 | */ | ||
375 | local uLong getLong (buf) | ||
376 | Byte *buf; | ||
377 | { | ||
378 | uLong x = 0; | ||
379 | Byte *p = buf+4; | ||
380 | |||
381 | do { | ||
382 | x <<= 8; | ||
383 | x |= *--p; | ||
384 | } while (p != buf); | ||
385 | return x; | ||
386 | } | ||
387 | |||
388 | /* =========================================================================== | ||
389 | Flushes all pending output if necessary, closes the compressed file | ||
390 | and deallocates all the (de)compression state. | ||
391 | */ | ||
392 | int gzclose (file) | ||
393 | gzFile file; | ||
394 | { | ||
395 | uInt n; | ||
396 | gz_stream *s = (gz_stream*)file; | ||
397 | |||
398 | if (s == NULL) return Z_STREAM_ERROR; | ||
399 | |||
400 | if (s->mode == 'w') { | ||
401 | gzflush (file, Z_FINISH); | ||
402 | putLong (s->file, s->crc); | ||
403 | putLong (s->file, s->stream.total_in); | ||
404 | |||
405 | } else if (s->mode == 'r' && s->z_err == Z_STREAM_END) { | ||
406 | |||
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) { | ||
409 | Byte *p = s->inbuf; | ||
410 | Byte *q = s->stream.next_in; | ||
411 | while (n--) { *p++ = *q++; }; | ||
412 | |||
413 | n = s->stream.avail_in; | ||
414 | n += fread(p, 1, 8, s->file); | ||
415 | s->stream.next_in = s->inbuf; | ||
416 | } | ||
417 | /* check CRC and original size */ | ||
418 | if (n < 8 || | ||
419 | getLong(s->stream.next_in) != s->crc || | ||
420 | getLong(s->stream.next_in + 4) != s->stream.total_out) { | ||
421 | |||
422 | s->z_err = Z_DATA_ERROR; | ||
423 | } | ||
424 | } | ||
425 | return destroy(file); | ||
426 | } | ||
427 | |||
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. | ||
434 | */ | ||
435 | char* gzerror (file, errnum) | ||
436 | gzFile file; | ||
437 | int *errnum; | ||
438 | { | ||
439 | char *m; | ||
440 | gz_stream *s = (gz_stream*)file; | ||
441 | |||
442 | if (s == NULL) { | ||
443 | *errnum = Z_STREAM_ERROR; | ||
444 | return z_errmsg[1-Z_STREAM_ERROR]; | ||
445 | } | ||
446 | *errnum = s->z_err; | ||
447 | if (*errnum == Z_OK) return ""; | ||
448 | |||
449 | m = *errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg; | ||
450 | |||
451 | if (m == NULL || *m == '\0') m = z_errmsg[1-s->z_err]; | ||
452 | |||
453 | TRYFREE(s->msg); | ||
454 | s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); | ||
455 | strcpy(s->msg, s->path); | ||
456 | strcat(s->msg, ": "); | ||
457 | strcat(s->msg, m); | ||
458 | return s->msg; | ||
459 | } | ||