gun.c revision 1b33c96954667ba382fa595baf7b31290bfdd517
/* gun.c -- simple gunzip to give an example of the use of inflateBack()
* Copyright (C) 2003, 2005, 2008, 2010 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
Version 1.6 17 January 2010 Mark Adler */
/* Version history:
1.0 16 Feb 2003 First version for testing of inflateBack()
1.1 21 Feb 2005 Decompress concatenated gzip streams
Remove use of "this" variable (C++ keyword)
Fix return value for in()
Improve allocation failure checking
Add typecasting for void * structures
Add -h option for command version and usage
Add a bunch of comments
1.2 20 Mar 2005 Add Unix compress (LZW) decompression
Copy file attributes from input file to output file
1.3 12 Jun 2005 Add casts for error messages [Oberhumer]
1.4 8 Dec 2006 LZW decompression speed improvements
1.5 9 Feb 2008 Avoid warning in latest version of gcc
*/
/*
gun [ -t ] [ name ... ]
decompresses the data in the named gzip files. If no arguments are given,
gun will decompress from stdin to stdout. The names must end in .gz, -gz,
.z, -z, _z, or .Z. The uncompressed data will be written to a file name
with the suffix stripped. On success, the original file is deleted. On
failure, the output file is deleted. For most failures, the command will
continue to process the remaining names on the command line. A memory
allocation failure will abort the command. If -t is specified, then the
listed files or stdin will be tested as gzip files for integrity (without
checking for a proper suffix), no output will be written, and no files
will be deleted.
Like gzip, gun allows concatenated gzip streams and will decompress them,
writing all of the uncompressed data to the output. Unlike gzip, gun allows
an empty file on input, and will produce no error writing an empty output
file.
gun will also decompress files made by Unix compress, which uses LZW
compression. These files are automatically detected by virtue of their
magic header bytes. Since the end of Unix compress stream is marked by the
end-of-file, they cannot be concantenated. If a Unix compress stream is
encountered in an input file, it is the last stream in that file.
Like gunzip and uncompress, the file attributes of the orignal compressed
file are maintained in the final uncompressed file, to the extent that the
user permissions allow it.
On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version
1.2.4) is on the same file, when gun is linked with zlib 1.2.2. Also the
LZW decompression provided by gun is about twice as fast as the standard
Unix uncompress command.
*/
/* external functions and related types and constants */
#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strerror(), strcmp(), strlen(), memcpy() */
#include <errno.h> /* errno */
#include <fcntl.h> /* open() */
#include <unistd.h> /* read(), write(), close(), chown(), unlink() */
#include <utime.h> /* utime() */
#include "zlib.h" /* inflateBackInit(), inflateBack(), */
/* inflateBackEnd(), crc32() */
/* function declaration */
#define local static
/* buffer constants */
/* structure for infback() to pass to input function in() -- it maintains the
input file and a buffer of size SIZE */
struct ind {
int infile;
unsigned char *inbuf;
};
/* Load input buffer, assumed to be empty, and return bytes loaded and a
pointer to them. read() is called until the buffer is full, or until it
returns end-of-file or error. Return 0 on error. */
{
int ret;
unsigned len;
unsigned char *next;
len = 0;
do {
if (ret == -1) {
len = 0;
break;
}
return len;
}
/* structure for infback() to pass to output function out() -- it maintains the
output file, a running CRC-32 check on the output and the total number of
bytes output, both for checking against the gzip trailer. (The length in
the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
the output is greater than 4 GB.) */
struct outd {
int outfile;
int check; /* true if checking crc and total */
unsigned long crc;
unsigned long total;
};
/* Write output buffer and update the CRC-32 and total bytes written. write()
is called until all of the output is written or an error is encountered.
On success out() returns 0. For a write failure, out() returns 1. If the
output file descriptor is -1, then nothing is written.
*/
{
int ret;
}
do {
if (ret == -1)
return 1;
} while (len != 0);
return 0;
}
/* next input byte macro for use inside lunpipe() and gunpipe() */
/* memory for gunpipe() and lunpipe() --
the first 256 entries of prefix[] and suffix[] are never used, could
have offset the index, but it's faster to waste the memory */
32K sliding window */
/* throw out what's left in the current bits byte buffer (this is a vestigial
aspect of the compressed data format derived from an implementation that
made use of a special VAX machine instruction!) */
#define FLUSHCODE() \
do { \
left = 0; \
rem = 0; \
have = 0; \
if (NEXT() == -1) \
break; \
chunk--; \
break; \
} \
} \
chunk = 0; \
} while (0)
/* Decompress a compress (LZW) file from indp to outfile. The compress magic
header (two bytes) has already been read and verified. There are have bytes
of buffered input at next. strm is used for passing error information back
to gunpipe().
lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
file, read error, or write error (a write error indicated by strm->next_in
not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
*/
{
int last; /* last byte read by NEXT(), or -1 if EOF */
unsigned chunk; /* bytes left in current chunk */
int left; /* bits left in rem */
unsigned rem; /* unused bits from input */
int bits; /* current bits per code */
unsigned code; /* code, table traversal index */
unsigned mask; /* mask for current bits codes */
int max; /* maximum bits per code for this stream */
unsigned flags; /* compress flags, then block compress flag */
unsigned temp; /* current code */
unsigned prev; /* previous code */
unsigned final; /* last character written for previous code */
unsigned stack; /* next position for reversed string */
unsigned outcnt; /* bytes in output buffer */
unsigned char *p;
/* set up output */
/* process remainder of compress header -- a flags byte */
if (last == -1)
return Z_BUF_ERROR;
if (flags & 0x60) {
return Z_DATA_ERROR;
}
return Z_DATA_ERROR;
}
max = 10;
/* clear table */
bits = 9;
mask = 0x1ff;
/* set up: get first 9-bit code, which is the first decompressed byte, but
don't create a table entry until the next code */
return Z_OK;
return Z_BUF_ERROR;
return Z_DATA_ERROR;
}
left = 7;
outcnt = 1;
/* decode codes */
stack = 0;
for (;;) {
/* if the table will be full after this, increment the code size */
FLUSHCODE();
bits++;
mask <<= 1;
mask++;
}
/* get a code of length bits */
if (chunk == 0) /* decrement chunk modulo bits */
/* write remaining buffered output */
return Z_BUF_ERROR;
}
return Z_OK;
}
left += 8;
chunk--;
return Z_BUF_ERROR;
left += 8;
chunk--;
}
/* process clear code (256) */
FLUSHCODE();
mask = 0x1ff;
continue; /* get next code */
}
/* special code to reuse last match */
/* Be picky on the allowed code here, and make sure that the code
we drop through (prev) will be a valid index so that random
input does not cause an exception. The code != end + 1 check is
empirically derived, and not checked in the original uncompress
code. If this ever causes a problem, that check could be safely
removed. Leaving this check in greatly improves gun's ability
to detect random or corrupted input after a compress header.
In any case, the prev > end check must be retained. */
return Z_DATA_ERROR;
}
}
/* walk through linked list to generate output in reverse order */
while (code >= 256) {
}
/* link new table entry */
end++;
}
/* set previous code for next iteration */
/* write output in forward order */
return Z_BUF_ERROR;
}
outcnt = 0;
}
do {
} while (p > match);
stack = 0;
/* loop for next code with final and prev as the last match, rem and
left provide the first 0..7 bits of the next code, end is the last
valid table entry */
}
}
/* Decompress a gzip file from infile to outfile. strm is assumed to have been
successfully initialized with inflateBackInit(). The input file may consist
of a series of gzip streams, in which case all of them will be decompressed
to the output file. If outfile is -1, then the gzip stream(s) integrity is
checked and nothing is written.
The return value is a zlib error code: Z_MEM_ERROR if out of memory,
Z_DATA_ERROR if the header or the compressed data is invalid, or if the
trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
stream) follows a valid gzip stream.
*/
{
/* setup input buffer */
/* decompress concatenated gzip streams */
have = 0; /* no input data read in yet */
for (;;) {
/* look for the two magic header bytes for a gzip stream */
if (NEXT() == -1) {
break; /* empty gzip stream is ok */
}
break; /* not a gzip or compress header */
}
first = 0; /* next non-header is junk */
/* process a compress (LZW) file -- can't be concatenated after this */
if (last == 157) {
break;
}
/* process remainder of gzip header */
ret = Z_BUF_ERROR;
if (last == -1) break;
ret = Z_DATA_ERROR;
break;
}
NEXT(); /* discard mod time, xflgs, os */
NEXT();
NEXT();
NEXT();
NEXT();
NEXT();
if (last == -1) break;
if (flags & 0xe0) {
ret = Z_DATA_ERROR;
break;
}
if (last == -1) break;
have = 0;
if (NEXT() == -1) break;
len--;
}
if (last == -1) break;
}
;
;
NEXT();
NEXT();
}
if (last == -1) break;
/* set up output */
/* decompress data to output */
if (ret != Z_STREAM_END) break;
/* check trailer */
ret = Z_BUF_ERROR;
/* crc error */
if (last != -1) {
ret = Z_DATA_ERROR;
}
break;
}
/* length error */
if (last != -1) {
ret = Z_DATA_ERROR;
}
break;
}
/* go back and look for another gzip stream */
}
/* clean up and return */
return ret;
}
/* Copy file attributes, from -> to, as best we can. This is best effort, so
no errors are reported. The mode bits, including suid, sgid, and the sticky
bit are copied (if allowed), the owner's user id and group id are copied
(again if allowed), and the access and modify times are copied. */
{
/* get all of from's Unix meta data, return if not a regular file */
return;
/* set to's mode bits, ignore errors */
/* copy owner's user and group, ignore errors */
/* copy access and modify times, ignore errors */
}
/* Decompress the file inname to the file outnname, of if test is true, just
decompress without writing and check the gzip trailer for integrity. If
inname is NULL or an empty string, read from stdin. If outname is NULL or
an empty string, write to stdout. strm is a pre-initialized inflateBack
structure. When appropriate, copy the file attributes from inname to
outname.
gunzip() returns 1 if there is an out-of-memory error or an unexpected
return code from gunpipe(). Otherwise it returns 0.
*/
{
int ret;
/* open files */
inname = "-";
infile = 0; /* stdin */
}
else {
if (infile == -1) {
return 0;
}
}
if (test)
outfile = -1;
outname = "-";
}
else {
if (outfile == -1) {
return 0;
}
}
errno = 0;
/* decompress */
/* interpret result */
switch (ret) {
case Z_OK:
case Z_ERRNO:
}
inname);
break;
case Z_DATA_ERROR:
break;
case Z_MEM_ERROR:
return 1;
case Z_BUF_ERROR:
}
else if (errno) {
}
else {
inname);
}
break;
default:
return 1;
}
return 0;
}
/* Process the gun command line arguments. See the command syntax near the
beginning of this source file. */
{
char *outname;
unsigned char *window;
/* initialize inflateBack state for repeated use */
return 1;
}
/* decompress each file to the same name with the suffix removed */
argc--;
argv++;
test = 0;
return 0;
}
test = 1;
argc--;
argv++;
}
if (argc)
do {
if (test)
else {
len -= 3;
len -= 2;
else {
*argv);
continue;
}
ret = 1;
break;
}
}
if (ret) break;
else
/* clean up */
return ret;
}