Chris@43: Chris@43: Chris@43:
Chris@43: Chris@43:Chris@43: Without further adieu, here is the program zpipe.c: Chris@43:
Chris@43: /* zpipe.c: example of proper use of zlib's inflate() and deflate() Chris@43: Not copyrighted -- provided to the public domain Chris@43: Version 1.4 11 December 2005 Mark Adler */ Chris@43: Chris@43: /* Version history: Chris@43: 1.0 30 Oct 2004 First version Chris@43: 1.1 8 Nov 2004 Add void casting for unused return values Chris@43: Use switch statement for inflate() return values Chris@43: 1.2 9 Nov 2004 Add assertions to document zlib guarantees Chris@43: 1.3 6 Apr 2005 Remove incorrect assertion in inf() Chris@43: 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions Chris@43: Avoid some compiler warnings for input and output buffers Chris@43: */ Chris@43:Chris@43: We now include the header files for the required definitions. From Chris@43: stdio.h we use fopen(), fread(), fwrite(), Chris@43: feof(), ferror(), and fclose() for file i/o, and Chris@43: fputs() for error messages. From string.h we use Chris@43: strcmp() for command line argument processing. Chris@43: From assert.h we use the assert() macro. Chris@43: From zlib.h Chris@43: we use the basic compression functions deflateInit(), Chris@43: deflate(), and deflateEnd(), and the basic decompression Chris@43: functions inflateInit(), inflate(), and Chris@43: inflateEnd(). Chris@43:
Chris@43: #include <stdio.h> Chris@43: #include <string.h> Chris@43: #include <assert.h> Chris@43: #include "zlib.h" Chris@43:Chris@43: This is an ugly hack required to avoid corruption of the input and output data on Chris@43: Windows/MS-DOS systems. Without this, those systems would assume that the input and output Chris@43: files are text, and try to convert the end-of-line characters from one standard to Chris@43: another. That would corrupt binary data, and in particular would render the compressed data unusable. Chris@43: This sets the input and output to binary which suppresses the end-of-line conversions. Chris@43: SET_BINARY_MODE() will be used later on stdin and stdout, at the beginning of main(). Chris@43:
Chris@43: #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) Chris@43: # include <fcntl.h> Chris@43: # include <io.h> Chris@43: # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) Chris@43: #else Chris@43: # define SET_BINARY_MODE(file) Chris@43: #endif Chris@43:Chris@43: CHUNK is simply the buffer size for feeding data to and pulling data Chris@43: from the zlib routines. Larger buffer sizes would be more efficient, Chris@43: especially for inflate(). If the memory is available, buffers sizes Chris@43: on the order of 128K or 256K bytes should be used. Chris@43:
Chris@43: #define CHUNK 16384 Chris@43:Chris@43: The def() routine compresses data from an input file to an output file. The output data Chris@43: will be in the zlib format, which is different from the gzip or zip Chris@43: formats. The zlib format has a very small header of only two bytes to identify it as Chris@43: a zlib stream and to provide decoding information, and a four-byte trailer with a fast Chris@43: check value to verify the integrity of the uncompressed data after decoding. Chris@43:
Chris@43: /* Compress from file source to file dest until EOF on source. Chris@43: def() returns Z_OK on success, Z_MEM_ERROR if memory could not be Chris@43: allocated for processing, Z_STREAM_ERROR if an invalid compression Chris@43: level is supplied, Z_VERSION_ERROR if the version of zlib.h and the Chris@43: version of the library linked do not match, or Z_ERRNO if there is Chris@43: an error reading or writing the files. */ Chris@43: int def(FILE *source, FILE *dest, int level) Chris@43: { Chris@43:Chris@43: Here are the local variables for def(). ret will be used for zlib Chris@43: return codes. flush will keep track of the current flushing state for deflate(), Chris@43: which is either no flushing, or flush to completion after the end of the input file is reached. Chris@43: have is the amount of data returned from deflate(). The strm structure Chris@43: is used to pass information to and from the zlib routines, and to maintain the Chris@43: deflate() state. in and out are the input and output buffers for Chris@43: deflate(). Chris@43:
Chris@43: int ret, flush; Chris@43: unsigned have; Chris@43: z_stream strm; Chris@43: unsigned char in[CHUNK]; Chris@43: unsigned char out[CHUNK]; Chris@43:Chris@43: The first thing we do is to initialize the zlib state for compression using Chris@43: deflateInit(). This must be done before the first use of deflate(). Chris@43: The zalloc, zfree, and opaque fields in the strm Chris@43: structure must be initialized before calling deflateInit(). Here they are Chris@43: set to the zlib constant Z_NULL to request that zlib use Chris@43: the default memory allocation routines. An application may also choose to provide Chris@43: custom memory allocation routines here. deflateInit() will allocate on the Chris@43: order of 256K bytes for the internal state. Chris@43: (See zlib Technical Details.) Chris@43:
Chris@43: deflateInit() is called with a pointer to the structure to be initialized and Chris@43: the compression level, which is an integer in the range of -1 to 9. Lower compression Chris@43: levels result in faster execution, but less compression. Higher levels result in Chris@43: greater compression, but slower execution. The zlib constant Z_DEFAULT_COMPRESSION, Chris@43: equal to -1, Chris@43: provides a good compromise between compression and speed and is equivalent to level 6. Chris@43: Level 0 actually does no compression at all, and in fact expands the data slightly to produce Chris@43: the zlib format (it is not a byte-for-byte copy of the input). Chris@43: More advanced applications of zlib Chris@43: may use deflateInit2() here instead. Such an application may want to reduce how Chris@43: much memory will be used, at some price in compression. Or it may need to request a Chris@43: gzip header and trailer instead of a zlib header and trailer, or raw Chris@43: encoding with no header or trailer at all. Chris@43:
Chris@43: We must check the return value of deflateInit() against the zlib constant Chris@43: Z_OK to make sure that it was able to Chris@43: allocate memory for the internal state, and that the provided arguments were valid. Chris@43: deflateInit() will also check that the version of zlib that the zlib.h Chris@43: file came from matches the version of zlib actually linked with the program. This Chris@43: is especially important for environments in which zlib is a shared library. Chris@43:
Chris@43: Note that an application can initialize multiple, independent zlib streams, which can Chris@43: operate in parallel. The state information maintained in the structure allows the zlib Chris@43: routines to be reentrant. Chris@43:
Chris@43: /* allocate deflate state */ Chris@43: strm.zalloc = Z_NULL; Chris@43: strm.zfree = Z_NULL; Chris@43: strm.opaque = Z_NULL; Chris@43: ret = deflateInit(&strm, level); Chris@43: if (ret != Z_OK) Chris@43: return ret; Chris@43:Chris@43: With the pleasantries out of the way, now we can get down to business. The outer do-loop Chris@43: reads all of the input file and exits at the bottom of the loop once end-of-file is reached. Chris@43: This loop contains the only call of deflate(). So we must make sure that all of the Chris@43: input data has been processed and that all of the output data has been generated and consumed Chris@43: before we fall out of the loop at the bottom. Chris@43:
Chris@43: /* compress until end of file */ Chris@43: do { Chris@43:Chris@43: We start off by reading data from the input file. The number of bytes read is put directly Chris@43: into avail_in, and a pointer to those bytes is put into next_in. We also Chris@43: check to see if end-of-file on the input has been reached. If we are at the end of file, then flush is set to the Chris@43: zlib constant Z_FINISH, which is later passed to deflate() to Chris@43: indicate that this is the last chunk of input data to compress. We need to use feof() Chris@43: to check for end-of-file as opposed to seeing if fewer than CHUNK bytes have been read. The Chris@43: reason is that if the input file length is an exact multiple of CHUNK, we will miss Chris@43: the fact that we got to the end-of-file, and not know to tell deflate() to finish Chris@43: up the compressed stream. If we are not yet at the end of the input, then the zlib Chris@43: constant Z_NO_FLUSH will be passed to deflate to indicate that we are still Chris@43: in the middle of the uncompressed data. Chris@43:
Chris@43: If there is an error in reading from the input file, the process is aborted with Chris@43: deflateEnd() being called to free the allocated zlib state before returning Chris@43: the error. We wouldn't want a memory leak, now would we? deflateEnd() can be called Chris@43: at any time after the state has been initialized. Once that's done, deflateInit() (or Chris@43: deflateInit2()) would have to be called to start a new compression process. There is Chris@43: no point here in checking the deflateEnd() return code. The deallocation can't fail. Chris@43:
Chris@43: strm.avail_in = fread(in, 1, CHUNK, source); Chris@43: if (ferror(source)) { Chris@43: (void)deflateEnd(&strm); Chris@43: return Z_ERRNO; Chris@43: } Chris@43: flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; Chris@43: strm.next_in = in; Chris@43:Chris@43: The inner do-loop passes our chunk of input data to deflate(), and then Chris@43: keeps calling deflate() until it is done producing output. Once there is no more Chris@43: new output, deflate() is guaranteed to have consumed all of the input, i.e., Chris@43: avail_in will be zero. Chris@43:
Chris@43: /* run deflate() on input until output buffer not full, finish Chris@43: compression if all of source has been read in */ Chris@43: do { Chris@43:Chris@43: Output space is provided to deflate() by setting avail_out to the number Chris@43: of available output bytes and next_out to a pointer to that space. Chris@43:
Chris@43: strm.avail_out = CHUNK; Chris@43: strm.next_out = out; Chris@43:Chris@43: Now we call the compression engine itself, deflate(). It takes as many of the Chris@43: avail_in bytes at next_in as it can process, and writes as many as Chris@43: avail_out bytes to next_out. Those counters and pointers are then Chris@43: updated past the input data consumed and the output data written. It is the amount of Chris@43: output space available that may limit how much input is consumed. Chris@43: Hence the inner loop to make sure that Chris@43: all of the input is consumed by providing more output space each time. Since avail_in Chris@43: and next_in are updated by deflate(), we don't have to mess with those Chris@43: between deflate() calls until it's all used up. Chris@43:
Chris@43: The parameters to deflate() are a pointer to the strm structure containing Chris@43: the input and output information and the internal compression engine state, and a parameter Chris@43: indicating whether and how to flush data to the output. Normally deflate will consume Chris@43: several K bytes of input data before producing any output (except for the header), in order Chris@43: to accumulate statistics on the data for optimum compression. It will then put out a burst of Chris@43: compressed data, and proceed to consume more input before the next burst. Eventually, Chris@43: deflate() Chris@43: must be told to terminate the stream, complete the compression with provided input data, and Chris@43: write out the trailer check value. deflate() will continue to compress normally as long Chris@43: as the flush parameter is Z_NO_FLUSH. Once the Z_FINISH parameter is provided, Chris@43: deflate() will begin to complete the compressed output stream. However depending on how Chris@43: much output space is provided, deflate() may have to be called several times until it Chris@43: has provided the complete compressed stream, even after it has consumed all of the input. The flush Chris@43: parameter must continue to be Z_FINISH for those subsequent calls. Chris@43:
Chris@43: There are other values of the flush parameter that are used in more advanced applications. You can Chris@43: force deflate() to produce a burst of output that encodes all of the input data provided Chris@43: so far, even if it wouldn't have otherwise, for example to control data latency on a link with Chris@43: compressed data. You can also ask that deflate() do that as well as erase any history up to Chris@43: that point so that what follows can be decompressed independently, for example for random access Chris@43: applications. Both requests will degrade compression by an amount depending on how often such Chris@43: requests are made. Chris@43:
Chris@43: deflate() has a return value that can indicate errors, yet we do not check it here. Why Chris@43: not? Well, it turns out that deflate() can do no wrong here. Let's go through Chris@43: deflate()'s return values and dispense with them one by one. The possible values are Chris@43: Z_OK, Z_STREAM_END, Z_STREAM_ERROR, or Z_BUF_ERROR. Z_OK Chris@43: is, well, ok. Z_STREAM_END is also ok and will be returned for the last call of Chris@43: deflate(). This is already guaranteed by calling deflate() with Z_FINISH Chris@43: until it has no more output. Z_STREAM_ERROR is only possible if the stream is not Chris@43: initialized properly, but we did initialize it properly. There is no harm in checking for Chris@43: Z_STREAM_ERROR here, for example to check for the possibility that some Chris@43: other part of the application inadvertently clobbered the memory containing the zlib state. Chris@43: Z_BUF_ERROR will be explained further below, but Chris@43: suffice it to say that this is simply an indication that deflate() could not consume Chris@43: more input or produce more output. deflate() can be called again with more output space Chris@43: or more available input, which it will be in this code. Chris@43:
Chris@43: ret = deflate(&strm, flush); /* no bad return value */ Chris@43: assert(ret != Z_STREAM_ERROR); /* state not clobbered */ Chris@43:Chris@43: Now we compute how much output deflate() provided on the last call, which is the Chris@43: difference between how much space was provided before the call, and how much output space Chris@43: is still available after the call. Then that data, if any, is written to the output file. Chris@43: We can then reuse the output buffer for the next call of deflate(). Again if there Chris@43: is a file i/o error, we call deflateEnd() before returning to avoid a memory leak. Chris@43:
Chris@43: have = CHUNK - strm.avail_out; Chris@43: if (fwrite(out, 1, have, dest) != have || ferror(dest)) { Chris@43: (void)deflateEnd(&strm); Chris@43: return Z_ERRNO; Chris@43: } Chris@43:Chris@43: The inner do-loop is repeated until the last deflate() call fails to fill the Chris@43: provided output buffer. Then we know that deflate() has done as much as it can with Chris@43: the provided input, and that all of that input has been consumed. We can then fall out of this Chris@43: loop and reuse the input buffer. Chris@43:
Chris@43: The way we tell that deflate() has no more output is by seeing that it did not fill Chris@43: the output buffer, leaving avail_out greater than zero. However suppose that Chris@43: deflate() has no more output, but just so happened to exactly fill the output buffer! Chris@43: avail_out is zero, and we can't tell that deflate() has done all it can. Chris@43: As far as we know, deflate() Chris@43: has more output for us. So we call it again. But now deflate() produces no output Chris@43: at all, and avail_out remains unchanged as CHUNK. That deflate() call Chris@43: wasn't able to do anything, either consume input or produce output, and so it returns Chris@43: Z_BUF_ERROR. (See, I told you I'd cover this later.) However this is not a problem at Chris@43: all. Now we finally have the desired indication that deflate() is really done, Chris@43: and so we drop out of the inner loop to provide more input to deflate(). Chris@43:
Chris@43: With flush set to Z_FINISH, this final set of deflate() calls will Chris@43: complete the output stream. Once that is done, subsequent calls of deflate() would return Chris@43: Z_STREAM_ERROR if the flush parameter is not Z_FINISH, and do no more processing Chris@43: until the state is reinitialized. Chris@43:
Chris@43: Some applications of zlib have two loops that call deflate() Chris@43: instead of the single inner loop we have here. The first loop would call Chris@43: without flushing and feed all of the data to deflate(). The second loop would call Chris@43: deflate() with no more Chris@43: data and the Z_FINISH parameter to complete the process. As you can see from this Chris@43: example, that can be avoided by simply keeping track of the current flush state. Chris@43:
Chris@43: } while (strm.avail_out == 0); Chris@43: assert(strm.avail_in == 0); /* all input will be used */ Chris@43:Chris@43: Now we check to see if we have already processed all of the input file. That information was Chris@43: saved in the flush variable, so we see if that was set to Z_FINISH. If so, Chris@43: then we're done and we fall out of the outer loop. We're guaranteed to get Z_STREAM_END Chris@43: from the last deflate() call, since we ran it until the last chunk of input was Chris@43: consumed and all of the output was generated. Chris@43:
Chris@43: /* done when last data in file processed */ Chris@43: } while (flush != Z_FINISH); Chris@43: assert(ret == Z_STREAM_END); /* stream will be complete */ Chris@43:Chris@43: The process is complete, but we still need to deallocate the state to avoid a memory leak Chris@43: (or rather more like a memory hemorrhage if you didn't do this). Then Chris@43: finally we can return with a happy return value. Chris@43:
Chris@43: /* clean up and return */ Chris@43: (void)deflateEnd(&strm); Chris@43: return Z_OK; Chris@43: } Chris@43:Chris@43: Now we do the same thing for decompression in the inf() routine. inf() Chris@43: decompresses what is hopefully a valid zlib stream from the input file and writes the Chris@43: uncompressed data to the output file. Much of the discussion above for def() Chris@43: applies to inf() as well, so the discussion here will focus on the differences between Chris@43: the two. Chris@43:
Chris@43: /* Decompress from file source to file dest until stream ends or EOF. Chris@43: inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be Chris@43: allocated for processing, Z_DATA_ERROR if the deflate data is Chris@43: invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and Chris@43: the version of the library linked do not match, or Z_ERRNO if there Chris@43: is an error reading or writing the files. */ Chris@43: int inf(FILE *source, FILE *dest) Chris@43: { Chris@43:Chris@43: The local variables have the same functionality as they do for def(). The Chris@43: only difference is that there is no flush variable, since inflate() Chris@43: can tell from the zlib stream itself when the stream is complete. Chris@43:
Chris@43: int ret; Chris@43: unsigned have; Chris@43: z_stream strm; Chris@43: unsigned char in[CHUNK]; Chris@43: unsigned char out[CHUNK]; Chris@43:Chris@43: The initialization of the state is the same, except that there is no compression level, Chris@43: of course, and two more elements of the structure are initialized. avail_in Chris@43: and next_in must be initialized before calling inflateInit(). This Chris@43: is because the application has the option to provide the start of the zlib stream in Chris@43: order for inflateInit() to have access to information about the compression Chris@43: method to aid in memory allocation. In the current implementation of zlib Chris@43: (up through versions 1.2.x), the method-dependent memory allocations are deferred to the first call of Chris@43: inflate() anyway. However those fields must be initialized since later versions Chris@43: of zlib that provide more compression methods may take advantage of this interface. Chris@43: In any case, no decompression is performed by inflateInit(), so the Chris@43: avail_out and next_out fields do not need to be initialized before calling. Chris@43:
Chris@43: Here avail_in is set to zero and next_in is set to Z_NULL to Chris@43: indicate that no input data is being provided. Chris@43:
Chris@43: /* allocate inflate state */ Chris@43: strm.zalloc = Z_NULL; Chris@43: strm.zfree = Z_NULL; Chris@43: strm.opaque = Z_NULL; Chris@43: strm.avail_in = 0; Chris@43: strm.next_in = Z_NULL; Chris@43: ret = inflateInit(&strm); Chris@43: if (ret != Z_OK) Chris@43: return ret; Chris@43:Chris@43: The outer do-loop decompresses input until inflate() indicates Chris@43: that it has reached the end of the compressed data and has produced all of the uncompressed Chris@43: output. This is in contrast to def() which processes all of the input file. Chris@43: If end-of-file is reached before the compressed data self-terminates, then the compressed Chris@43: data is incomplete and an error is returned. Chris@43:
Chris@43: /* decompress until deflate stream ends or end of file */ Chris@43: do { Chris@43:Chris@43: We read input data and set the strm structure accordingly. If we've reached the Chris@43: end of the input file, then we leave the outer loop and report an error, since the Chris@43: compressed data is incomplete. Note that we may read more data than is eventually consumed Chris@43: by inflate(), if the input file continues past the zlib stream. Chris@43: For applications where zlib streams are embedded in other data, this routine would Chris@43: need to be modified to return the unused data, or at least indicate how much of the input Chris@43: data was not used, so the application would know where to pick up after the zlib stream. Chris@43:
Chris@43: strm.avail_in = fread(in, 1, CHUNK, source); Chris@43: if (ferror(source)) { Chris@43: (void)inflateEnd(&strm); Chris@43: return Z_ERRNO; Chris@43: } Chris@43: if (strm.avail_in == 0) Chris@43: break; Chris@43: strm.next_in = in; Chris@43:Chris@43: The inner do-loop has the same function it did in def(), which is to Chris@43: keep calling inflate() until has generated all of the output it can with the Chris@43: provided input. Chris@43:
Chris@43: /* run inflate() on input until output buffer not full */ Chris@43: do { Chris@43:Chris@43: Just like in def(), the same output space is provided for each call of inflate(). Chris@43:
Chris@43: strm.avail_out = CHUNK; Chris@43: strm.next_out = out; Chris@43:Chris@43: Now we run the decompression engine itself. There is no need to adjust the flush parameter, since Chris@43: the zlib format is self-terminating. The main difference here is that there are Chris@43: return values that we need to pay attention to. Z_DATA_ERROR Chris@43: indicates that inflate() detected an error in the zlib compressed data format, Chris@43: which means that either the data is not a zlib stream to begin with, or that the data was Chris@43: corrupted somewhere along the way since it was compressed. The other error to be processed is Chris@43: Z_MEM_ERROR, which can occur since memory allocation is deferred until inflate() Chris@43: needs it, unlike deflate(), whose memory is allocated at the start by deflateInit(). Chris@43:
Chris@43: Advanced applications may use Chris@43: deflateSetDictionary() to prime deflate() with a set of likely data to improve the Chris@43: first 32K or so of compression. This is noted in the zlib header, so inflate() Chris@43: requests that that dictionary be provided before it can start to decompress. Without the dictionary, Chris@43: correct decompression is not possible. For this routine, we have no idea what the dictionary is, Chris@43: so the Z_NEED_DICT indication is converted to a Z_DATA_ERROR. Chris@43:
Chris@43: inflate() can also return Z_STREAM_ERROR, which should not be possible here, Chris@43: but could be checked for as noted above for def(). Z_BUF_ERROR does not need to be Chris@43: checked for here, for the same reasons noted for def(). Z_STREAM_END will be Chris@43: checked for later. Chris@43:
Chris@43: ret = inflate(&strm, Z_NO_FLUSH); Chris@43: assert(ret != Z_STREAM_ERROR); /* state not clobbered */ Chris@43: switch (ret) { Chris@43: case Z_NEED_DICT: Chris@43: ret = Z_DATA_ERROR; /* and fall through */ Chris@43: case Z_DATA_ERROR: Chris@43: case Z_MEM_ERROR: Chris@43: (void)inflateEnd(&strm); Chris@43: return ret; Chris@43: } Chris@43:Chris@43: The output of inflate() is handled identically to that of deflate(). Chris@43:
Chris@43: have = CHUNK - strm.avail_out; Chris@43: if (fwrite(out, 1, have, dest) != have || ferror(dest)) { Chris@43: (void)inflateEnd(&strm); Chris@43: return Z_ERRNO; Chris@43: } Chris@43:Chris@43: The inner do-loop ends when inflate() has no more output as indicated Chris@43: by not filling the output buffer, just as for deflate(). In this case, we cannot Chris@43: assert that strm.avail_in will be zero, since the deflate stream may end before the file Chris@43: does. Chris@43:
Chris@43: } while (strm.avail_out == 0); Chris@43:Chris@43: The outer do-loop ends when inflate() reports that it has reached the Chris@43: end of the input zlib stream, has completed the decompression and integrity Chris@43: check, and has provided all of the output. This is indicated by the inflate() Chris@43: return value Z_STREAM_END. The inner loop is guaranteed to leave ret Chris@43: equal to Z_STREAM_END if the last chunk of the input file read contained the end Chris@43: of the zlib stream. So if the return value is not Z_STREAM_END, the Chris@43: loop continues to read more input. Chris@43:
Chris@43: /* done when inflate() says it's done */ Chris@43: } while (ret != Z_STREAM_END); Chris@43:Chris@43: At this point, decompression successfully completed, or we broke out of the loop due to no Chris@43: more data being available from the input file. If the last inflate() return value Chris@43: is not Z_STREAM_END, then the zlib stream was incomplete and a data error Chris@43: is returned. Otherwise, we return with a happy return value. Of course, inflateEnd() Chris@43: is called first to avoid a memory leak. Chris@43:
Chris@43: /* clean up and return */ Chris@43: (void)inflateEnd(&strm); Chris@43: return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; Chris@43: } Chris@43:Chris@43: That ends the routines that directly use zlib. The following routines make this Chris@43: a command-line program by running data through the above routines from stdin to Chris@43: stdout, and handling any errors reported by def() or inf(). Chris@43:
Chris@43: zerr() is used to interpret the possible error codes from def() Chris@43: and inf(), as detailed in their comments above, and print out an error message. Chris@43: Note that these are only a subset of the possible return values from deflate() Chris@43: and inflate(). Chris@43:
Chris@43: /* report a zlib or i/o error */ Chris@43: void zerr(int ret) Chris@43: { Chris@43: fputs("zpipe: ", stderr); Chris@43: switch (ret) { Chris@43: case Z_ERRNO: Chris@43: if (ferror(stdin)) Chris@43: fputs("error reading stdin\n", stderr); Chris@43: if (ferror(stdout)) Chris@43: fputs("error writing stdout\n", stderr); Chris@43: break; Chris@43: case Z_STREAM_ERROR: Chris@43: fputs("invalid compression level\n", stderr); Chris@43: break; Chris@43: case Z_DATA_ERROR: Chris@43: fputs("invalid or incomplete deflate data\n", stderr); Chris@43: break; Chris@43: case Z_MEM_ERROR: Chris@43: fputs("out of memory\n", stderr); Chris@43: break; Chris@43: case Z_VERSION_ERROR: Chris@43: fputs("zlib version mismatch!\n", stderr); Chris@43: } Chris@43: } Chris@43:Chris@43: Here is the main() routine used to test def() and inf(). The Chris@43: zpipe command is simply a compression pipe from stdin to stdout, if Chris@43: no arguments are given, or it is a decompression pipe if zpipe -d is used. If any other Chris@43: arguments are provided, no compression or decompression is performed. Instead a usage Chris@43: message is displayed. Examples are zpipe < foo.txt > foo.txt.z to compress, and Chris@43: zpipe -d < foo.txt.z > foo.txt to decompress. Chris@43:
Chris@43: /* compress or decompress from stdin to stdout */ Chris@43: int main(int argc, char **argv) Chris@43: { Chris@43: int ret; Chris@43: Chris@43: /* avoid end-of-line conversions */ Chris@43: SET_BINARY_MODE(stdin); Chris@43: SET_BINARY_MODE(stdout); Chris@43: Chris@43: /* do compression if no arguments */ Chris@43: if (argc == 1) { Chris@43: ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION); Chris@43: if (ret != Z_OK) Chris@43: zerr(ret); Chris@43: return ret; Chris@43: } Chris@43: Chris@43: /* do decompression if -d specified */ Chris@43: else if (argc == 2 && strcmp(argv[1], "-d") == 0) { Chris@43: ret = inf(stdin, stdout); Chris@43: if (ret != Z_OK) Chris@43: zerr(ret); Chris@43: return ret; Chris@43: } Chris@43: Chris@43: /* otherwise, report usage */ Chris@43: else { Chris@43: fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr); Chris@43: return 1; Chris@43: } Chris@43: } Chris@43:Chris@43: