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