cannam@89: /* cannam@89: * pufftest.c cannam@89: * Copyright (C) 2002-2010 Mark Adler cannam@89: * For conditions of distribution and use, see copyright notice in puff.h cannam@89: * version 2.2, 25 Apr 2010 cannam@89: */ cannam@89: cannam@89: /* Example of how to use puff(). cannam@89: cannam@89: Usage: puff [-w] [-f] [-nnn] file cannam@89: ... | puff [-w] [-f] [-nnn] cannam@89: cannam@89: where file is the input file with deflate data, nnn is the number of bytes cannam@89: of input to skip before inflating (e.g. to skip a zlib or gzip header), and cannam@89: -w is used to write the decompressed data to stdout. -f is for coverage cannam@89: testing, and causes pufftest to fail with not enough output space (-f does cannam@89: a write like -w, so -w is not required). */ cannam@89: cannam@89: #include cannam@89: #include cannam@89: #include "puff.h" cannam@89: cannam@89: #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) cannam@89: # include cannam@89: # include cannam@89: # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) cannam@89: #else cannam@89: # define SET_BINARY_MODE(file) cannam@89: #endif cannam@89: cannam@89: #define local static cannam@89: cannam@89: /* Return size times approximately the cube root of 2, keeping the result as 1, cannam@89: 3, or 5 times a power of 2 -- the result is always > size, until the result cannam@89: is the maximum value of an unsigned long, where it remains. This is useful cannam@89: to keep reallocations less than ~33% over the actual data. */ cannam@89: local size_t bythirds(size_t size) cannam@89: { cannam@89: int n; cannam@89: size_t m; cannam@89: cannam@89: m = size; cannam@89: for (n = 0; m; n++) cannam@89: m >>= 1; cannam@89: if (n < 3) cannam@89: return size + 1; cannam@89: n -= 3; cannam@89: m = size >> n; cannam@89: m += m == 6 ? 2 : 1; cannam@89: m <<= n; cannam@89: return m > size ? m : (size_t)(-1); cannam@89: } cannam@89: cannam@89: /* Read the input file *name, or stdin if name is NULL, into allocated memory. cannam@89: Reallocate to larger buffers until the entire file is read in. Return a cannam@89: pointer to the allocated data, or NULL if there was a memory allocation cannam@89: failure. *len is the number of bytes of data read from the input file (even cannam@89: if load() returns NULL). If the input file was empty or could not be opened cannam@89: or read, *len is zero. */ cannam@89: local void *load(const char *name, size_t *len) cannam@89: { cannam@89: size_t size; cannam@89: void *buf, *swap; cannam@89: FILE *in; cannam@89: cannam@89: *len = 0; cannam@89: buf = malloc(size = 4096); cannam@89: if (buf == NULL) cannam@89: return NULL; cannam@89: in = name == NULL ? stdin : fopen(name, "rb"); cannam@89: if (in != NULL) { cannam@89: for (;;) { cannam@89: *len += fread((char *)buf + *len, 1, size - *len, in); cannam@89: if (*len < size) break; cannam@89: size = bythirds(size); cannam@89: if (size == *len || (swap = realloc(buf, size)) == NULL) { cannam@89: free(buf); cannam@89: buf = NULL; cannam@89: break; cannam@89: } cannam@89: buf = swap; cannam@89: } cannam@89: fclose(in); cannam@89: } cannam@89: return buf; cannam@89: } cannam@89: cannam@89: int main(int argc, char **argv) cannam@89: { cannam@89: int ret, put = 0, fail = 0; cannam@89: unsigned skip = 0; cannam@89: char *arg, *name = NULL; cannam@89: unsigned char *source = NULL, *dest; cannam@89: size_t len = 0; cannam@89: unsigned long sourcelen, destlen; cannam@89: cannam@89: /* process arguments */ cannam@89: while (arg = *++argv, --argc) cannam@89: if (arg[0] == '-') { cannam@89: if (arg[1] == 'w' && arg[2] == 0) cannam@89: put = 1; cannam@89: else if (arg[1] == 'f' && arg[2] == 0) cannam@89: fail = 1, put = 1; cannam@89: else if (arg[1] >= '0' && arg[1] <= '9') cannam@89: skip = (unsigned)atoi(arg + 1); cannam@89: else { cannam@89: fprintf(stderr, "invalid option %s\n", arg); cannam@89: return 3; cannam@89: } cannam@89: } cannam@89: else if (name != NULL) { cannam@89: fprintf(stderr, "only one file name allowed\n"); cannam@89: return 3; cannam@89: } cannam@89: else cannam@89: name = arg; cannam@89: source = load(name, &len); cannam@89: if (source == NULL) { cannam@89: fprintf(stderr, "memory allocation failure\n"); cannam@89: return 4; cannam@89: } cannam@89: if (len == 0) { cannam@89: fprintf(stderr, "could not read %s, or it was empty\n", cannam@89: name == NULL ? "" : name); cannam@89: free(source); cannam@89: return 3; cannam@89: } cannam@89: if (skip >= len) { cannam@89: fprintf(stderr, "skip request of %d leaves no input\n", skip); cannam@89: free(source); cannam@89: return 3; cannam@89: } cannam@89: cannam@89: /* test inflate data with offset skip */ cannam@89: len -= skip; cannam@89: sourcelen = (unsigned long)len; cannam@89: ret = puff(NIL, &destlen, source + skip, &sourcelen); cannam@89: if (ret) cannam@89: fprintf(stderr, "puff() failed with return code %d\n", ret); cannam@89: else { cannam@89: fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen); cannam@89: if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n", cannam@89: len - sourcelen); cannam@89: } cannam@89: cannam@89: /* if requested, inflate again and write decompressd data to stdout */ cannam@89: if (put && ret == 0) { cannam@89: if (fail) cannam@89: destlen >>= 1; cannam@89: dest = malloc(destlen); cannam@89: if (dest == NULL) { cannam@89: fprintf(stderr, "memory allocation failure\n"); cannam@89: free(source); cannam@89: return 4; cannam@89: } cannam@89: puff(dest, &destlen, source + skip, &sourcelen); cannam@89: SET_BINARY_MODE(stdout); cannam@89: fwrite(dest, 1, destlen, stdout); cannam@89: free(dest); cannam@89: } cannam@89: cannam@89: /* clean up */ cannam@89: free(source); cannam@89: return ret; cannam@89: }