From 26a99cd8957db86bdc75d9d1ebf00146cb20c87c Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Sun, 2 Oct 2011 13:24:43 -0700 Subject: [PATCH] Add a transparent write mode to gzopen() when 'T' is in the mode. --- gzguts.h | 2 +- gzlib.c | 13 +++++++++- gzread.c | 8 ++----- gzwrite.c | 72 ++++++++++++++++++++++++++++++++++++------------------- zlib.h | 26 ++++++++++++++------ 5 files changed, 82 insertions(+), 39 deletions(-) diff --git a/gzguts.h b/gzguts.h index 8193451..4d71db0 100644 --- a/gzguts.h +++ b/gzguts.h @@ -136,11 +136,11 @@ typedef struct { unsigned want; /* requested buffer size, default is GZBUFSIZE */ unsigned char *in; /* input buffer */ unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ int eof; /* true if end of input file reached */ z_off64_t start; /* where the gzip data started, for rewinding */ int how; /* 0: get header, 1: copy, 2: decompress */ - int direct; /* true if last read direct, false if gzip */ /* just for writing */ int level; /* compression level */ int strategy; /* compression strategy */ diff --git a/gzlib.c b/gzlib.c index 7b31d24..e53b6ab 100644 --- a/gzlib.c +++ b/gzlib.c @@ -79,7 +79,6 @@ local void gz_reset(state) if (state->mode == GZ_READ) { /* for reading ... */ state->eof = 0; /* not at end of file */ state->how = LOOK; /* look for gzip header */ - state->direct = 1; /* default for empty file */ } state->seek = 0; /* no seek request pending */ gz_error(state, Z_OK, NULL); /* clear error */ @@ -111,6 +110,7 @@ local gzFile gz_open(path, fd, mode) state->mode = GZ_NONE; state->level = Z_DEFAULT_COMPRESSION; state->strategy = Z_DEFAULT_STRATEGY; + state->direct = 0; while (*mode) { if (*mode >= '0' && *mode <= '9') state->level = *mode - '0'; @@ -143,6 +143,8 @@ local gzFile gz_open(path, fd, mode) break; case 'F': state->strategy = Z_FIXED; + case 'T': + state->direct = 1; default: /* could consider as an error, but just ignore */ ; } @@ -155,6 +157,15 @@ local gzFile gz_open(path, fd, mode) return NULL; } + /* can't force transparent read */ + if (state->mode == GZ_READ) { + if (state->direct) { + free(state); + return NULL; + } + state->direct = 1; /* for empty file */ + } + /* save the path name for error messages */ state->path = malloc(strlen(path) + 1); if (state->path == NULL) { diff --git a/gzread.c b/gzread.c index 4701f9e..521e26f 100644 --- a/gzread.c +++ b/gzread.c @@ -535,16 +535,12 @@ int ZEXPORT gzdirect(file) return 0; state = (gz_statep)file; - /* check that we're reading */ - if (state->mode != GZ_READ) - return 0; - /* if the state is not known, but we can find out, then do so (this is mainly for right after a gzopen() or gzdopen()) */ - if (state->how == LOOK && state->x.have == 0) + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) (void)gz_look(state); - /* return 1 if reading direct, 0 if decompressing a gzip stream */ + /* return 1 if transparent, 0 if processing a gzip stream */ return state->direct; } diff --git a/gzwrite.c b/gzwrite.c index 1d28807..6c991fe 100644 --- a/gzwrite.c +++ b/gzwrite.c @@ -18,44 +18,55 @@ local int gz_init(state) int ret; z_streamp strm = &(state->strm); - /* allocate input and output buffers */ + /* allocate input buffer */ state->in = malloc(state->want); - state->out = malloc(state->want); - if (state->in == NULL || state->out == NULL) { - if (state->out != NULL) - free(state->out); - if (state->in != NULL) - free(state->in); + if (state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } - /* allocate deflate memory, set up for gzip compression */ - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - ret = deflateInit2(strm, state->level, Z_DEFLATED, - 15 + 16, 8, state->strategy); - if (ret != Z_OK) { - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; + /* only need output buffer and deflate state if compressing */ + if (!state->direct) { + /* allocate output buffer */ + state->out = malloc(state->want); + if (state->out == NULL) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + 15 + 16, 8, state->strategy); + if (ret != Z_OK) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } } /* mark state as initialized */ state->size = state->want; - /* initialize write buffer */ - strm->avail_out = state->size; - strm->next_out = state->out; - state->x.next = strm->next_out; + /* initialize write buffer if compressing */ + if (!state->direct) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = strm->next_out; + } return 0; } /* Compress whatever is at avail_in and next_in and write to the output file. Return -1 if there is an error writing to the output file, otherwise 0. flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, - then the deflate() state is reset to start a new gzip stream. */ + then the deflate() state is reset to start a new gzip stream. If gz->direct + is true, then simply write to the output file without compressing, and + ignore flush. */ local int gz_comp(state, flush) gz_statep state; int flush; @@ -68,6 +79,17 @@ local int gz_comp(state, flush) if (state->size == 0 && gz_init(state) == -1) return -1; + /* write directly if requested */ + if (state->direct) { + got = write(state->fd, strm->next_in, strm->avail_in); + if (got < 0 || (unsigned)got != strm->avail_in) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in = 0; + return 0; + } + /* run deflate() on provided input until it produces no more output */ ret = Z_OK; do { @@ -526,8 +548,10 @@ int ZEXPORT gzclose_w(file) /* flush, free memory, and close file */ if (gz_comp(state, Z_FINISH) == -1) ret = state->err; - (void)deflateEnd(&(state->strm)); - free(state->out); + if (!state->direct) { + (void)deflateEnd(&(state->strm)); + free(state->out); + } free(state->in); gz_error(state, Z_OK, NULL); free(state->path); diff --git a/zlib.h b/zlib.h index 8050bdd..f77b596 100644 --- a/zlib.h +++ b/zlib.h @@ -696,7 +696,7 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. */ - + ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, unsigned *pending, int *bits)); @@ -706,7 +706,7 @@ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, provided would be due to the available output space having being consumed. The number of bits of output not provided are between 0 and 7, where they await more bits to join them in order to fill out a full byte. - + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ @@ -1196,10 +1196,13 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of - deflateInit2 for more information about the strategy parameter.) Also "a" - can be used instead of "w" to request that the gzip stream that will be - written be appended to the file. "+" will result in an error, since reading - and writing to the same gzip file is not supported. + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. These functions, as well as gzip, will read and decode a sequence of gzip streams in a file. The append function of gzopen() can be used to create @@ -1209,7 +1212,9 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was @@ -1440,6 +1445,13 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file));