xref: /plan9/sys/src/cmd/gs/zlib/gzio.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier /* gzio.c -- IO on .gz files
2*7dd7cddfSDavid du Colombier  * Copyright (C) 1995-1996 Jean-loup Gailly.
3*7dd7cddfSDavid du Colombier  * For conditions of distribution and use, see copyright notice in zlib.h
4*7dd7cddfSDavid du Colombier  */
5*7dd7cddfSDavid du Colombier 
6*7dd7cddfSDavid du Colombier /* $Id: gzio.c,v 1.14 1996/07/24 13:41:01 me Exp $ */
7*7dd7cddfSDavid du Colombier 
8*7dd7cddfSDavid du Colombier #include <stdio.h>
9*7dd7cddfSDavid du Colombier 
10*7dd7cddfSDavid du Colombier #include "zutil.h"
11*7dd7cddfSDavid du Colombier 
12*7dd7cddfSDavid du Colombier struct internal_state {int dummy;}; /* for buggy compilers */
13*7dd7cddfSDavid du Colombier 
14*7dd7cddfSDavid du Colombier #define Z_BUFSIZE 4096
15*7dd7cddfSDavid du Colombier 
16*7dd7cddfSDavid du Colombier #define ALLOC(size) malloc(size)
17*7dd7cddfSDavid du Colombier #define TRYFREE(p) {if (p) free(p);}
18*7dd7cddfSDavid du Colombier 
19*7dd7cddfSDavid du Colombier static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
20*7dd7cddfSDavid du Colombier 
21*7dd7cddfSDavid du Colombier /* gzip flag byte */
22*7dd7cddfSDavid du Colombier #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
23*7dd7cddfSDavid du Colombier #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
24*7dd7cddfSDavid du Colombier #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
25*7dd7cddfSDavid du Colombier #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
26*7dd7cddfSDavid du Colombier #define COMMENT      0x10 /* bit 4 set: file comment present */
27*7dd7cddfSDavid du Colombier #define RESERVED     0xE0 /* bits 5..7: reserved */
28*7dd7cddfSDavid du Colombier 
29*7dd7cddfSDavid du Colombier typedef struct gz_stream {
30*7dd7cddfSDavid du Colombier     z_stream stream;
31*7dd7cddfSDavid du Colombier     int      z_err;   /* error code for last stream operation */
32*7dd7cddfSDavid du Colombier     int      z_eof;   /* set if end of input file */
33*7dd7cddfSDavid du Colombier     FILE     *file;   /* .gz file */
34*7dd7cddfSDavid du Colombier     Byte     *inbuf;  /* input buffer */
35*7dd7cddfSDavid du Colombier     Byte     *outbuf; /* output buffer */
36*7dd7cddfSDavid du Colombier     uLong    crc;     /* crc32 of uncompressed data */
37*7dd7cddfSDavid du Colombier     char     *msg;    /* error message */
38*7dd7cddfSDavid du Colombier     char     *path;   /* path name for debugging only */
39*7dd7cddfSDavid du Colombier     int      transparent; /* 1 if input file is not a .gz file */
40*7dd7cddfSDavid du Colombier     char     mode;    /* 'w' or 'r' */
41*7dd7cddfSDavid du Colombier } gz_stream;
42*7dd7cddfSDavid du Colombier 
43*7dd7cddfSDavid du Colombier 
44*7dd7cddfSDavid du Colombier local gzFile gz_open      OF((const char *path, const char *mode, int  fd));
45*7dd7cddfSDavid du Colombier local int    get_byte     OF((gz_stream *s));
46*7dd7cddfSDavid du Colombier local void   check_header OF((gz_stream *s));
47*7dd7cddfSDavid du Colombier local int    destroy      OF((gz_stream *s));
48*7dd7cddfSDavid du Colombier local void   putLong      OF((FILE *file, uLong x));
49*7dd7cddfSDavid du Colombier local uLong  getLong      OF((gz_stream *s));
50*7dd7cddfSDavid du Colombier 
51*7dd7cddfSDavid du Colombier /* ===========================================================================
52*7dd7cddfSDavid du Colombier      Opens a gzip (.gz) file for reading or writing. The mode parameter
53*7dd7cddfSDavid du Colombier    is as in fopen ("rb" or "wb"). The file is given either by file descriptor
54*7dd7cddfSDavid du Colombier    or path name (if fd == -1).
55*7dd7cddfSDavid du Colombier      gz_open return NULL if the file could not be opened or if there was
56*7dd7cddfSDavid du Colombier    insufficient memory to allocate the (de)compression state; errno
57*7dd7cddfSDavid du Colombier    can be checked to distinguish the two cases (if errno is zero, the
58*7dd7cddfSDavid du Colombier    zlib error is Z_MEM_ERROR).
59*7dd7cddfSDavid du Colombier */
60*7dd7cddfSDavid du Colombier local gzFile gz_open (path, mode, fd)
61*7dd7cddfSDavid du Colombier     const char *path;
62*7dd7cddfSDavid du Colombier     const char *mode;
63*7dd7cddfSDavid du Colombier     int  fd;
64*7dd7cddfSDavid du Colombier {
65*7dd7cddfSDavid du Colombier     int err;
66*7dd7cddfSDavid du Colombier     int level = Z_DEFAULT_COMPRESSION; /* compression level */
67*7dd7cddfSDavid du Colombier     char *p = (char*)mode;
68*7dd7cddfSDavid du Colombier     gz_stream *s;
69*7dd7cddfSDavid du Colombier     char fmode[80]; /* copy of mode, without the compression level */
70*7dd7cddfSDavid du Colombier     char *m = fmode;
71*7dd7cddfSDavid du Colombier 
72*7dd7cddfSDavid du Colombier     if (!path || !mode) return Z_NULL;
73*7dd7cddfSDavid du Colombier 
74*7dd7cddfSDavid du Colombier     s = (gz_stream *)ALLOC(sizeof(gz_stream));
75*7dd7cddfSDavid du Colombier     if (!s) return Z_NULL;
76*7dd7cddfSDavid du Colombier 
77*7dd7cddfSDavid du Colombier     s->stream.zalloc = (alloc_func)0;
78*7dd7cddfSDavid du Colombier     s->stream.zfree = (free_func)0;
79*7dd7cddfSDavid du Colombier     s->stream.opaque = (voidpf)0;
80*7dd7cddfSDavid du Colombier     s->stream.next_in = s->inbuf = Z_NULL;
81*7dd7cddfSDavid du Colombier     s->stream.next_out = s->outbuf = Z_NULL;
82*7dd7cddfSDavid du Colombier     s->stream.avail_in = s->stream.avail_out = 0;
83*7dd7cddfSDavid du Colombier     s->file = NULL;
84*7dd7cddfSDavid du Colombier     s->z_err = Z_OK;
85*7dd7cddfSDavid du Colombier     s->z_eof = 0;
86*7dd7cddfSDavid du Colombier     s->crc = crc32(0L, Z_NULL, 0);
87*7dd7cddfSDavid du Colombier     s->msg = NULL;
88*7dd7cddfSDavid du Colombier     s->transparent = 0;
89*7dd7cddfSDavid du Colombier 
90*7dd7cddfSDavid du Colombier     s->path = (char*)ALLOC(strlen(path)+1);
91*7dd7cddfSDavid du Colombier     if (s->path == NULL) {
92*7dd7cddfSDavid du Colombier         return destroy(s), (gzFile)Z_NULL;
93*7dd7cddfSDavid du Colombier     }
94*7dd7cddfSDavid du Colombier     strcpy(s->path, path); /* do this early for debugging */
95*7dd7cddfSDavid du Colombier 
96*7dd7cddfSDavid du Colombier     s->mode = '\0';
97*7dd7cddfSDavid du Colombier     do {
98*7dd7cddfSDavid du Colombier         if (*p == 'r') s->mode = 'r';
99*7dd7cddfSDavid du Colombier         if (*p == 'w' || *p == 'a') s->mode = 'w';
100*7dd7cddfSDavid du Colombier         if (*p >= '0' && *p <= '9') {
101*7dd7cddfSDavid du Colombier 	    level = *p - '0';
102*7dd7cddfSDavid du Colombier 	} else {
103*7dd7cddfSDavid du Colombier 	    *m++ = *p; /* copy the mode */
104*7dd7cddfSDavid du Colombier 	}
105*7dd7cddfSDavid du Colombier     } while (*p++ && m != fmode + sizeof(fmode));
106*7dd7cddfSDavid du Colombier     if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
107*7dd7cddfSDavid du Colombier 
108*7dd7cddfSDavid du Colombier     if (s->mode == 'w') {
109*7dd7cddfSDavid du Colombier         err = deflateInit2(&(s->stream), level,
110*7dd7cddfSDavid du Colombier                            Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, 0);
111*7dd7cddfSDavid du Colombier         /* windowBits is passed < 0 to suppress zlib header */
112*7dd7cddfSDavid du Colombier 
113*7dd7cddfSDavid du Colombier         s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
114*7dd7cddfSDavid du Colombier 
115*7dd7cddfSDavid du Colombier         if (err != Z_OK || s->outbuf == Z_NULL) {
116*7dd7cddfSDavid du Colombier             return destroy(s), (gzFile)Z_NULL;
117*7dd7cddfSDavid du Colombier         }
118*7dd7cddfSDavid du Colombier     } else {
119*7dd7cddfSDavid du Colombier         err = inflateInit2(&(s->stream), -MAX_WBITS);
120*7dd7cddfSDavid du Colombier         s->stream.next_in  = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
121*7dd7cddfSDavid du Colombier 
122*7dd7cddfSDavid du Colombier         if (err != Z_OK || s->inbuf == Z_NULL) {
123*7dd7cddfSDavid du Colombier             return destroy(s), (gzFile)Z_NULL;
124*7dd7cddfSDavid du Colombier         }
125*7dd7cddfSDavid du Colombier     }
126*7dd7cddfSDavid du Colombier     s->stream.avail_out = Z_BUFSIZE;
127*7dd7cddfSDavid du Colombier 
128*7dd7cddfSDavid du Colombier     errno = 0;
129*7dd7cddfSDavid du Colombier     s->file = fd < 0 ? FOPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
130*7dd7cddfSDavid du Colombier 
131*7dd7cddfSDavid du Colombier     if (s->file == NULL) {
132*7dd7cddfSDavid du Colombier         return destroy(s), (gzFile)Z_NULL;
133*7dd7cddfSDavid du Colombier     }
134*7dd7cddfSDavid du Colombier     if (s->mode == 'w') {
135*7dd7cddfSDavid du Colombier         /* Write a very simple .gz header:
136*7dd7cddfSDavid du Colombier          */
137*7dd7cddfSDavid du Colombier         fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
138*7dd7cddfSDavid du Colombier              Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
139*7dd7cddfSDavid du Colombier     } else {
140*7dd7cddfSDavid du Colombier 	check_header(s); /* skip the .gz header */
141*7dd7cddfSDavid du Colombier     }
142*7dd7cddfSDavid du Colombier     return (gzFile)s;
143*7dd7cddfSDavid du Colombier }
144*7dd7cddfSDavid du Colombier 
145*7dd7cddfSDavid du Colombier /* ===========================================================================
146*7dd7cddfSDavid du Colombier      Opens a gzip (.gz) file for reading or writing.
147*7dd7cddfSDavid du Colombier */
148*7dd7cddfSDavid du Colombier gzFile gzopen (path, mode)
149*7dd7cddfSDavid du Colombier     const char *path;
150*7dd7cddfSDavid du Colombier     const char *mode;
151*7dd7cddfSDavid du Colombier {
152*7dd7cddfSDavid du Colombier     return gz_open (path, mode, -1);
153*7dd7cddfSDavid du Colombier }
154*7dd7cddfSDavid du Colombier 
155*7dd7cddfSDavid du Colombier /* ===========================================================================
156*7dd7cddfSDavid du Colombier      Associate a gzFile with the file descriptor fd. fd is not dup'ed here
157*7dd7cddfSDavid du Colombier    to mimic the behavio(u)r of fdopen.
158*7dd7cddfSDavid du Colombier */
159*7dd7cddfSDavid du Colombier gzFile gzdopen (fd, mode)
160*7dd7cddfSDavid du Colombier     int fd;
161*7dd7cddfSDavid du Colombier     const char *mode;
162*7dd7cddfSDavid du Colombier {
163*7dd7cddfSDavid du Colombier     char name[20];
164*7dd7cddfSDavid du Colombier 
165*7dd7cddfSDavid du Colombier     if (fd < 0) return (gzFile)Z_NULL;
166*7dd7cddfSDavid du Colombier     sprintf(name, "<fd:%d>", fd); /* for debugging */
167*7dd7cddfSDavid du Colombier 
168*7dd7cddfSDavid du Colombier     return gz_open (name, mode, fd);
169*7dd7cddfSDavid du Colombier }
170*7dd7cddfSDavid du Colombier 
171*7dd7cddfSDavid du Colombier /* ===========================================================================
172*7dd7cddfSDavid du Colombier      Read a byte from a gz_stream; update next_in and avail_in. Return EOF
173*7dd7cddfSDavid du Colombier    for end of file.
174*7dd7cddfSDavid du Colombier    IN assertion: the stream s has been sucessfully opened for reading.
175*7dd7cddfSDavid du Colombier */
176*7dd7cddfSDavid du Colombier local int get_byte(s)
177*7dd7cddfSDavid du Colombier     gz_stream *s;
178*7dd7cddfSDavid du Colombier {
179*7dd7cddfSDavid du Colombier     if (s->z_eof) return EOF;
180*7dd7cddfSDavid du Colombier     if (s->stream.avail_in == 0) {
181*7dd7cddfSDavid du Colombier 	errno = 0;
182*7dd7cddfSDavid du Colombier 	s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
183*7dd7cddfSDavid du Colombier 	if (s->stream.avail_in == 0) {
184*7dd7cddfSDavid du Colombier 	    s->z_eof = 1;
185*7dd7cddfSDavid du Colombier 	    if (ferror(s->file)) s->z_err = Z_ERRNO;
186*7dd7cddfSDavid du Colombier 	    return EOF;
187*7dd7cddfSDavid du Colombier 	}
188*7dd7cddfSDavid du Colombier 	s->stream.next_in = s->inbuf;
189*7dd7cddfSDavid du Colombier     }
190*7dd7cddfSDavid du Colombier     s->stream.avail_in--;
191*7dd7cddfSDavid du Colombier     return *(s->stream.next_in)++;
192*7dd7cddfSDavid du Colombier }
193*7dd7cddfSDavid du Colombier 
194*7dd7cddfSDavid du Colombier /* ===========================================================================
195*7dd7cddfSDavid du Colombier       Check the gzip header of a gz_stream opened for reading. Set the stream
196*7dd7cddfSDavid du Colombier     mode to transparent if the gzip magic header is not present; set s->err
197*7dd7cddfSDavid du Colombier     to Z_DATA_ERROR if the magic header is present but the rest of the header
198*7dd7cddfSDavid du Colombier     is incorrect.
199*7dd7cddfSDavid du Colombier     IN assertion: the stream s has already been created sucessfully;
200*7dd7cddfSDavid du Colombier        s->stream.avail_in is zero for the first time, but may be non-zero
201*7dd7cddfSDavid du Colombier        for concatenated .gz files.
202*7dd7cddfSDavid du Colombier */
203*7dd7cddfSDavid du Colombier local void check_header(s)
204*7dd7cddfSDavid du Colombier     gz_stream *s;
205*7dd7cddfSDavid du Colombier {
206*7dd7cddfSDavid du Colombier     int method; /* method byte */
207*7dd7cddfSDavid du Colombier     int flags;  /* flags byte */
208*7dd7cddfSDavid du Colombier     uInt len;
209*7dd7cddfSDavid du Colombier     int c;
210*7dd7cddfSDavid du Colombier 
211*7dd7cddfSDavid du Colombier     /* Check the gzip magic header */
212*7dd7cddfSDavid du Colombier     for (len = 0; len < 2; len++) {
213*7dd7cddfSDavid du Colombier 	c = get_byte(s);
214*7dd7cddfSDavid du Colombier 	if (c != gz_magic[len]) {
215*7dd7cddfSDavid du Colombier 	    s->transparent = 1;
216*7dd7cddfSDavid du Colombier 	    if (c != EOF) s->stream.avail_in++, s->stream.next_in--;
217*7dd7cddfSDavid du Colombier 	    s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
218*7dd7cddfSDavid du Colombier 	    return;
219*7dd7cddfSDavid du Colombier 	}
220*7dd7cddfSDavid du Colombier     }
221*7dd7cddfSDavid du Colombier     method = get_byte(s);
222*7dd7cddfSDavid du Colombier     flags = get_byte(s);
223*7dd7cddfSDavid du Colombier     if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
224*7dd7cddfSDavid du Colombier 	s->z_err = Z_DATA_ERROR;
225*7dd7cddfSDavid du Colombier 	return;
226*7dd7cddfSDavid du Colombier     }
227*7dd7cddfSDavid du Colombier 
228*7dd7cddfSDavid du Colombier     /* Discard time, xflags and OS code: */
229*7dd7cddfSDavid du Colombier     for (len = 0; len < 6; len++) (void)get_byte(s);
230*7dd7cddfSDavid du Colombier 
231*7dd7cddfSDavid du Colombier     if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
232*7dd7cddfSDavid du Colombier 	len  =  (uInt)get_byte(s);
233*7dd7cddfSDavid du Colombier 	len += ((uInt)get_byte(s))<<8;
234*7dd7cddfSDavid du Colombier 	/* len is garbage if EOF but the loop below will quit anyway */
235*7dd7cddfSDavid du Colombier 	while (len-- != 0 && get_byte(s) != EOF) ;
236*7dd7cddfSDavid du Colombier     }
237*7dd7cddfSDavid du Colombier     if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
238*7dd7cddfSDavid du Colombier 	while ((c = get_byte(s)) != 0 && c != EOF) ;
239*7dd7cddfSDavid du Colombier     }
240*7dd7cddfSDavid du Colombier     if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
241*7dd7cddfSDavid du Colombier 	while ((c = get_byte(s)) != 0 && c != EOF) ;
242*7dd7cddfSDavid du Colombier     }
243*7dd7cddfSDavid du Colombier     if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
244*7dd7cddfSDavid du Colombier 	for (len = 0; len < 2; len++) (void)get_byte(s);
245*7dd7cddfSDavid du Colombier     }
246*7dd7cddfSDavid du Colombier     s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
247*7dd7cddfSDavid du Colombier }
248*7dd7cddfSDavid du Colombier 
249*7dd7cddfSDavid du Colombier  /* ===========================================================================
250*7dd7cddfSDavid du Colombier  * Cleanup then free the given gz_stream. Return a zlib error code.
251*7dd7cddfSDavid du Colombier    Try freeing in the reverse order of allocations.
252*7dd7cddfSDavid du Colombier  */
253*7dd7cddfSDavid du Colombier local int destroy (s)
254*7dd7cddfSDavid du Colombier     gz_stream *s;
255*7dd7cddfSDavid du Colombier {
256*7dd7cddfSDavid du Colombier     int err = Z_OK;
257*7dd7cddfSDavid du Colombier 
258*7dd7cddfSDavid du Colombier     if (!s) return Z_STREAM_ERROR;
259*7dd7cddfSDavid du Colombier 
260*7dd7cddfSDavid du Colombier     TRYFREE(s->msg);
261*7dd7cddfSDavid du Colombier 
262*7dd7cddfSDavid du Colombier     if (s->stream.state != NULL) {
263*7dd7cddfSDavid du Colombier        if (s->mode == 'w') {
264*7dd7cddfSDavid du Colombier            err = deflateEnd(&(s->stream));
265*7dd7cddfSDavid du Colombier        } else if (s->mode == 'r') {
266*7dd7cddfSDavid du Colombier            err = inflateEnd(&(s->stream));
267*7dd7cddfSDavid du Colombier        }
268*7dd7cddfSDavid du Colombier     }
269*7dd7cddfSDavid du Colombier     if (s->file != NULL && fclose(s->file)) {
270*7dd7cddfSDavid du Colombier         err = Z_ERRNO;
271*7dd7cddfSDavid du Colombier     }
272*7dd7cddfSDavid du Colombier     if (s->z_err < 0) err = s->z_err;
273*7dd7cddfSDavid du Colombier 
274*7dd7cddfSDavid du Colombier     TRYFREE(s->inbuf);
275*7dd7cddfSDavid du Colombier     TRYFREE(s->outbuf);
276*7dd7cddfSDavid du Colombier     TRYFREE(s->path);
277*7dd7cddfSDavid du Colombier     TRYFREE(s);
278*7dd7cddfSDavid du Colombier     return err;
279*7dd7cddfSDavid du Colombier }
280*7dd7cddfSDavid du Colombier 
281*7dd7cddfSDavid du Colombier /* ===========================================================================
282*7dd7cddfSDavid du Colombier      Reads the given number of uncompressed bytes from the compressed file.
283*7dd7cddfSDavid du Colombier    gzread returns the number of bytes actually read (0 for end of file).
284*7dd7cddfSDavid du Colombier */
285*7dd7cddfSDavid du Colombier int gzread (file, buf, len)
286*7dd7cddfSDavid du Colombier     gzFile file;
287*7dd7cddfSDavid du Colombier     voidp buf;
288*7dd7cddfSDavid du Colombier     unsigned len;
289*7dd7cddfSDavid du Colombier {
290*7dd7cddfSDavid du Colombier     gz_stream *s = (gz_stream*)file;
291*7dd7cddfSDavid du Colombier     Bytef *start = buf; /* starting point for crc computation */
292*7dd7cddfSDavid du Colombier     Byte  *next_out; /* == stream.next_out but not forced far (for MSDOS) */
293*7dd7cddfSDavid du Colombier 
294*7dd7cddfSDavid du Colombier     if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
295*7dd7cddfSDavid du Colombier 
296*7dd7cddfSDavid du Colombier     if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
297*7dd7cddfSDavid du Colombier     if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
298*7dd7cddfSDavid du Colombier 
299*7dd7cddfSDavid du Colombier     s->stream.next_out = next_out = buf;
300*7dd7cddfSDavid du Colombier     s->stream.avail_out = len;
301*7dd7cddfSDavid du Colombier 
302*7dd7cddfSDavid du Colombier     while (s->stream.avail_out != 0) {
303*7dd7cddfSDavid du Colombier 
304*7dd7cddfSDavid du Colombier 	if (s->transparent) {
305*7dd7cddfSDavid du Colombier 	    /* Copy first the lookahead bytes: */
306*7dd7cddfSDavid du Colombier 	    uInt n = s->stream.avail_in;
307*7dd7cddfSDavid du Colombier 	    if (n > s->stream.avail_out) n = s->stream.avail_out;
308*7dd7cddfSDavid du Colombier 	    if (n > 0) {
309*7dd7cddfSDavid du Colombier 		zmemcpy(s->stream.next_out, s->stream.next_in, n);
310*7dd7cddfSDavid du Colombier 		next_out += n;
311*7dd7cddfSDavid du Colombier 		s->stream.next_out = next_out;
312*7dd7cddfSDavid du Colombier 		s->stream.next_in   += n;
313*7dd7cddfSDavid du Colombier 		s->stream.avail_out -= n;
314*7dd7cddfSDavid du Colombier 		s->stream.avail_in  -= n;
315*7dd7cddfSDavid du Colombier 	    }
316*7dd7cddfSDavid du Colombier 	    if (s->stream.avail_out > 0) {
317*7dd7cddfSDavid du Colombier 		s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out,
318*7dd7cddfSDavid du Colombier 					     s->file);
319*7dd7cddfSDavid du Colombier 	    }
320*7dd7cddfSDavid du Colombier 	    return (int)(len - s->stream.avail_out);
321*7dd7cddfSDavid du Colombier 	}
322*7dd7cddfSDavid du Colombier         if (s->stream.avail_in == 0 && !s->z_eof) {
323*7dd7cddfSDavid du Colombier 
324*7dd7cddfSDavid du Colombier             errno = 0;
325*7dd7cddfSDavid du Colombier             s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
326*7dd7cddfSDavid du Colombier             if (s->stream.avail_in == 0) {
327*7dd7cddfSDavid du Colombier                 s->z_eof = 1;
328*7dd7cddfSDavid du Colombier 		if (ferror(s->file)) {
329*7dd7cddfSDavid du Colombier 		    s->z_err = Z_ERRNO;
330*7dd7cddfSDavid du Colombier 		    break;
331*7dd7cddfSDavid du Colombier 		}
332*7dd7cddfSDavid du Colombier             }
333*7dd7cddfSDavid du Colombier             s->stream.next_in = s->inbuf;
334*7dd7cddfSDavid du Colombier         }
335*7dd7cddfSDavid du Colombier         s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
336*7dd7cddfSDavid du Colombier 
337*7dd7cddfSDavid du Colombier 	if (s->z_err == Z_STREAM_END) {
338*7dd7cddfSDavid du Colombier 	    /* Check CRC and original size */
339*7dd7cddfSDavid du Colombier 	    s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
340*7dd7cddfSDavid du Colombier 	    start = s->stream.next_out;
341*7dd7cddfSDavid du Colombier 
342*7dd7cddfSDavid du Colombier 	    if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
343*7dd7cddfSDavid du Colombier 		s->z_err = Z_DATA_ERROR;
344*7dd7cddfSDavid du Colombier 	    } else {
345*7dd7cddfSDavid du Colombier 		/* Check for concatenated .gz files: */
346*7dd7cddfSDavid du Colombier 		check_header(s);
347*7dd7cddfSDavid du Colombier 		if (s->z_err == Z_OK) {
348*7dd7cddfSDavid du Colombier 		    inflateReset(&(s->stream));
349*7dd7cddfSDavid du Colombier 		    s->crc = crc32(0L, Z_NULL, 0);
350*7dd7cddfSDavid du Colombier 		}
351*7dd7cddfSDavid du Colombier 	    }
352*7dd7cddfSDavid du Colombier 	}
353*7dd7cddfSDavid du Colombier 	if (s->z_err != Z_OK || s->z_eof) break;
354*7dd7cddfSDavid du Colombier     }
355*7dd7cddfSDavid du Colombier     s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
356*7dd7cddfSDavid du Colombier 
357*7dd7cddfSDavid du Colombier     return (int)(len - s->stream.avail_out);
358*7dd7cddfSDavid du Colombier }
359*7dd7cddfSDavid du Colombier 
360*7dd7cddfSDavid du Colombier /* ===========================================================================
361*7dd7cddfSDavid du Colombier      Writes the given number of uncompressed bytes into the compressed file.
362*7dd7cddfSDavid du Colombier    gzwrite returns the number of bytes actually written (0 in case of error).
363*7dd7cddfSDavid du Colombier */
364*7dd7cddfSDavid du Colombier int gzwrite (file, buf, len)
365*7dd7cddfSDavid du Colombier     gzFile file;
366*7dd7cddfSDavid du Colombier     const voidp buf;
367*7dd7cddfSDavid du Colombier     unsigned len;
368*7dd7cddfSDavid du Colombier {
369*7dd7cddfSDavid du Colombier     gz_stream *s = (gz_stream*)file;
370*7dd7cddfSDavid du Colombier 
371*7dd7cddfSDavid du Colombier     if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
372*7dd7cddfSDavid du Colombier 
373*7dd7cddfSDavid du Colombier     s->stream.next_in = buf;
374*7dd7cddfSDavid du Colombier     s->stream.avail_in = len;
375*7dd7cddfSDavid du Colombier 
376*7dd7cddfSDavid du Colombier     while (s->stream.avail_in != 0) {
377*7dd7cddfSDavid du Colombier 
378*7dd7cddfSDavid du Colombier         if (s->stream.avail_out == 0) {
379*7dd7cddfSDavid du Colombier 
380*7dd7cddfSDavid du Colombier             s->stream.next_out = s->outbuf;
381*7dd7cddfSDavid du Colombier             if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
382*7dd7cddfSDavid du Colombier                 s->z_err = Z_ERRNO;
383*7dd7cddfSDavid du Colombier                 break;
384*7dd7cddfSDavid du Colombier             }
385*7dd7cddfSDavid du Colombier             s->stream.avail_out = Z_BUFSIZE;
386*7dd7cddfSDavid du Colombier         }
387*7dd7cddfSDavid du Colombier         s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
388*7dd7cddfSDavid du Colombier         if (s->z_err != Z_OK) break;
389*7dd7cddfSDavid du Colombier     }
390*7dd7cddfSDavid du Colombier     s->crc = crc32(s->crc, buf, len);
391*7dd7cddfSDavid du Colombier 
392*7dd7cddfSDavid du Colombier     return (int)(len - s->stream.avail_in);
393*7dd7cddfSDavid du Colombier }
394*7dd7cddfSDavid du Colombier 
395*7dd7cddfSDavid du Colombier /* ===========================================================================
396*7dd7cddfSDavid du Colombier      Flushes all pending output into the compressed file. The parameter
397*7dd7cddfSDavid du Colombier    flush is as in the deflate() function.
398*7dd7cddfSDavid du Colombier      gzflush should be called only when strictly necessary because it can
399*7dd7cddfSDavid du Colombier    degrade compression.
400*7dd7cddfSDavid du Colombier */
401*7dd7cddfSDavid du Colombier int gzflush (file, flush)
402*7dd7cddfSDavid du Colombier     gzFile file;
403*7dd7cddfSDavid du Colombier     int flush;
404*7dd7cddfSDavid du Colombier {
405*7dd7cddfSDavid du Colombier     uInt len;
406*7dd7cddfSDavid du Colombier     int done = 0;
407*7dd7cddfSDavid du Colombier     gz_stream *s = (gz_stream*)file;
408*7dd7cddfSDavid du Colombier 
409*7dd7cddfSDavid du Colombier     if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
410*7dd7cddfSDavid du Colombier 
411*7dd7cddfSDavid du Colombier     s->stream.avail_in = 0; /* should be zero already anyway */
412*7dd7cddfSDavid du Colombier 
413*7dd7cddfSDavid du Colombier     for (;;) {
414*7dd7cddfSDavid du Colombier         len = Z_BUFSIZE - s->stream.avail_out;
415*7dd7cddfSDavid du Colombier 
416*7dd7cddfSDavid du Colombier         if (len != 0) {
417*7dd7cddfSDavid du Colombier             if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
418*7dd7cddfSDavid du Colombier                 s->z_err = Z_ERRNO;
419*7dd7cddfSDavid du Colombier                 return Z_ERRNO;
420*7dd7cddfSDavid du Colombier             }
421*7dd7cddfSDavid du Colombier             s->stream.next_out = s->outbuf;
422*7dd7cddfSDavid du Colombier             s->stream.avail_out = Z_BUFSIZE;
423*7dd7cddfSDavid du Colombier         }
424*7dd7cddfSDavid du Colombier         if (done) break;
425*7dd7cddfSDavid du Colombier         s->z_err = deflate(&(s->stream), flush);
426*7dd7cddfSDavid du Colombier 
427*7dd7cddfSDavid du Colombier         /* deflate has finished flushing only when it hasn't used up
428*7dd7cddfSDavid du Colombier          * all the available space in the output buffer:
429*7dd7cddfSDavid du Colombier          */
430*7dd7cddfSDavid du Colombier         done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
431*7dd7cddfSDavid du Colombier 
432*7dd7cddfSDavid du Colombier         if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
433*7dd7cddfSDavid du Colombier     }
434*7dd7cddfSDavid du Colombier     fflush(s->file);
435*7dd7cddfSDavid du Colombier     return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
436*7dd7cddfSDavid du Colombier }
437*7dd7cddfSDavid du Colombier 
438*7dd7cddfSDavid du Colombier /* ===========================================================================
439*7dd7cddfSDavid du Colombier    Outputs a long in LSB order to the given file
440*7dd7cddfSDavid du Colombier */
441*7dd7cddfSDavid du Colombier local void putLong (file, x)
442*7dd7cddfSDavid du Colombier     FILE *file;
443*7dd7cddfSDavid du Colombier     uLong x;
444*7dd7cddfSDavid du Colombier {
445*7dd7cddfSDavid du Colombier     int n;
446*7dd7cddfSDavid du Colombier     for (n = 0; n < 4; n++) {
447*7dd7cddfSDavid du Colombier         fputc((int)(x & 0xff), file);
448*7dd7cddfSDavid du Colombier         x >>= 8;
449*7dd7cddfSDavid du Colombier     }
450*7dd7cddfSDavid du Colombier }
451*7dd7cddfSDavid du Colombier 
452*7dd7cddfSDavid du Colombier /* ===========================================================================
453*7dd7cddfSDavid du Colombier    Reads a long in LSB order from the given gz_stream. Sets
454*7dd7cddfSDavid du Colombier */
455*7dd7cddfSDavid du Colombier local uLong getLong (s)
456*7dd7cddfSDavid du Colombier     gz_stream *s;
457*7dd7cddfSDavid du Colombier {
458*7dd7cddfSDavid du Colombier     uLong x = (uLong)get_byte(s);
459*7dd7cddfSDavid du Colombier     int c;
460*7dd7cddfSDavid du Colombier 
461*7dd7cddfSDavid du Colombier     x += ((uLong)get_byte(s))<<8;
462*7dd7cddfSDavid du Colombier     x += ((uLong)get_byte(s))<<16;
463*7dd7cddfSDavid du Colombier     c = get_byte(s);
464*7dd7cddfSDavid du Colombier     if (c == EOF) s->z_err = Z_DATA_ERROR;
465*7dd7cddfSDavid du Colombier     x += ((uLong)c)<<24;
466*7dd7cddfSDavid du Colombier     return x;
467*7dd7cddfSDavid du Colombier }
468*7dd7cddfSDavid du Colombier 
469*7dd7cddfSDavid du Colombier /* ===========================================================================
470*7dd7cddfSDavid du Colombier      Flushes all pending output if necessary, closes the compressed file
471*7dd7cddfSDavid du Colombier    and deallocates all the (de)compression state.
472*7dd7cddfSDavid du Colombier */
473*7dd7cddfSDavid du Colombier int gzclose (file)
474*7dd7cddfSDavid du Colombier     gzFile file;
475*7dd7cddfSDavid du Colombier {
476*7dd7cddfSDavid du Colombier     int err;
477*7dd7cddfSDavid du Colombier     gz_stream *s = (gz_stream*)file;
478*7dd7cddfSDavid du Colombier 
479*7dd7cddfSDavid du Colombier     if (s == NULL) return Z_STREAM_ERROR;
480*7dd7cddfSDavid du Colombier 
481*7dd7cddfSDavid du Colombier     if (s->mode == 'w') {
482*7dd7cddfSDavid du Colombier         err = gzflush (file, Z_FINISH);
483*7dd7cddfSDavid du Colombier         if (err != Z_OK) return destroy(file);
484*7dd7cddfSDavid du Colombier 
485*7dd7cddfSDavid du Colombier         putLong (s->file, s->crc);
486*7dd7cddfSDavid du Colombier         putLong (s->file, s->stream.total_in);
487*7dd7cddfSDavid du Colombier 
488*7dd7cddfSDavid du Colombier     }
489*7dd7cddfSDavid du Colombier     return destroy(file);
490*7dd7cddfSDavid du Colombier }
491*7dd7cddfSDavid du Colombier 
492*7dd7cddfSDavid du Colombier /* ===========================================================================
493*7dd7cddfSDavid du Colombier      Returns the error message for the last error which occured on the
494*7dd7cddfSDavid du Colombier    given compressed file. errnum is set to zlib error number. If an
495*7dd7cddfSDavid du Colombier    error occured in the file system and not in the compression library,
496*7dd7cddfSDavid du Colombier    errnum is set to Z_ERRNO and the application may consult errno
497*7dd7cddfSDavid du Colombier    to get the exact error code.
498*7dd7cddfSDavid du Colombier */
499*7dd7cddfSDavid du Colombier const char*  gzerror (file, errnum)
500*7dd7cddfSDavid du Colombier     gzFile file;
501*7dd7cddfSDavid du Colombier     int *errnum;
502*7dd7cddfSDavid du Colombier {
503*7dd7cddfSDavid du Colombier     char *m;
504*7dd7cddfSDavid du Colombier     gz_stream *s = (gz_stream*)file;
505*7dd7cddfSDavid du Colombier 
506*7dd7cddfSDavid du Colombier     if (s == NULL) {
507*7dd7cddfSDavid du Colombier         *errnum = Z_STREAM_ERROR;
508*7dd7cddfSDavid du Colombier         return (const char*)ERR_MSG(Z_STREAM_ERROR);
509*7dd7cddfSDavid du Colombier     }
510*7dd7cddfSDavid du Colombier     *errnum = s->z_err;
511*7dd7cddfSDavid du Colombier     if (*errnum == Z_OK) return (const char*)"";
512*7dd7cddfSDavid du Colombier 
513*7dd7cddfSDavid du Colombier     m =  (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
514*7dd7cddfSDavid du Colombier 
515*7dd7cddfSDavid du Colombier     if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
516*7dd7cddfSDavid du Colombier 
517*7dd7cddfSDavid du Colombier     TRYFREE(s->msg);
518*7dd7cddfSDavid du Colombier     s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
519*7dd7cddfSDavid du Colombier     strcpy(s->msg, s->path);
520*7dd7cddfSDavid du Colombier     strcat(s->msg, ": ");
521*7dd7cddfSDavid du Colombier     strcat(s->msg, m);
522*7dd7cddfSDavid du Colombier     return (const char*)s->msg;
523*7dd7cddfSDavid du Colombier }
524