1193323Sed/* 2193323Sed * pufftest.c 3193323Sed * Copyright (C) 2002-2013 Mark Adler 4193323Sed * For conditions of distribution and use, see copyright notice in puff.h 5193323Sed * version 2.3, 21 Jan 2013 6193323Sed */ 7193323Sed 8193323Sed/* Example of how to use puff(). 9193323Sed 10193323Sed Usage: puff [-w] [-f] [-nnn] file 11193323Sed ... | puff [-w] [-f] [-nnn] 12193323Sed 13193323Sed where file is the input file with deflate data, nnn is the number of bytes 14193323Sed of input to skip before inflating (e.g. to skip a zlib or gzip header), and 15249423Sdim -w is used to write the decompressed data to stdout. -f is for coverage 16249423Sdim testing, and causes pufftest to fail with not enough output space (-f does 17193323Sed a write like -w, so -w is not required). */ 18251662Sdim 19193323Sed#include <stdio.h> 20203954Srdivacky#include <stdlib.h> 21198892Srdivacky#include "puff.h" 22249423Sdim 23234353Sdim#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) 24249423Sdim# include <fcntl.h> 25198090Srdivacky# include <io.h> 26193323Sed# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) 27234353Sdim#else 28234353Sdim# define SET_BINARY_MODE(file) 29234353Sdim#endif 30249423Sdim 31193323Sed#define local static 32193323Sed 33193323Sed/* Return size times approximately the cube root of 2, keeping the result as 1, 34193323Sed 3, or 5 times a power of 2 -- the result is always > size, until the result 35193323Sed is the maximum value of an unsigned long, where it remains. This is useful 36263508Sdim to keep reallocations less than ~33% over the actual data. */ 37198090Srdivackylocal size_t bythirds(size_t size) 38193323Sed{ 39193323Sed int n; 40193323Sed size_t m; 41195098Sed 42195098Sed m = size; 43195098Sed for (n = 0; m; n++) 44193323Sed m >>= 1; 45195098Sed if (n < 3) 46251662Sdim return size + 1; 47263508Sdim n -= 3; 48234353Sdim m = size >> n; 49193323Sed m += m == 6 ? 2 : 1; 50193323Sed m <<= n; 51218893Sdim return m > size ? m : (size_t)(-1); 52218893Sdim} 53193323Sed 54198090Srdivacky/* Read the input file *name, or stdin if name is NULL, into allocated memory. 55198892Srdivacky Reallocate to larger buffers until the entire file is read in. Return a 56198892Srdivacky pointer to the allocated data, or NULL if there was a memory allocation 57198892Srdivacky failure. *len is the number of bytes of data read from the input file (even 58198892Srdivacky if load() returns NULL). If the input file was empty or could not be opened 59198892Srdivacky or read, *len is zero. */ 60198892Srdivackylocal void *load(const char *name, size_t *len) 61198892Srdivacky{ 62198090Srdivacky size_t size; 63198892Srdivacky void *buf, *swap; 64198892Srdivacky FILE *in; 65198090Srdivacky 66193323Sed *len = 0; 67198090Srdivacky buf = malloc(size = 4096); 68198090Srdivacky if (buf == NULL) 69193323Sed return NULL; 70193323Sed in = name == NULL ? stdin : fopen(name, "rb"); 71198892Srdivacky if (in != NULL) { 72193323Sed for (;;) { 73193323Sed *len += fread((char *)buf + *len, 1, size - *len, in); 74193323Sed if (*len < size) break; 75193323Sed size = bythirds(size); 76193323Sed if (size == *len || (swap = realloc(buf, size)) == NULL) { 77198090Srdivacky free(buf); 78193323Sed buf = NULL; 79193323Sed break; 80198892Srdivacky } 81198090Srdivacky buf = swap; 82218893Sdim } 83193323Sed fclose(in); 84193323Sed } 85193323Sed return buf; 86198090Srdivacky} 87193323Sed 88193323Sedint main(int argc, char **argv) 89193323Sed{ 90198090Srdivacky int ret, put = 0, fail = 0; 91218893Sdim unsigned skip = 0; 92218893Sdim char *arg, *name = NULL; 93243830Sdim unsigned char *source = NULL, *dest; 94198090Srdivacky size_t len = 0; 95193323Sed unsigned long sourcelen, destlen; 96193323Sed 97218893Sdim /* process arguments */ 98218893Sdim while (arg = *++argv, --argc) 99218893Sdim if (arg[0] == '-') { 100218893Sdim if (arg[1] == 'w' && arg[2] == 0) 101218893Sdim put = 1; 102218893Sdim else if (arg[1] == 'f' && arg[2] == 0) 103218893Sdim fail = 1, put = 1; 104218893Sdim else if (arg[1] >= '0' && arg[1] <= '9') 105218893Sdim skip = (unsigned)atoi(arg + 1); 106218893Sdim else { 107193323Sed fprintf(stderr, "invalid option %s\n", arg); 108218893Sdim return 3; 109243830Sdim } 110218893Sdim } 111218893Sdim else if (name != NULL) { 112198892Srdivacky fprintf(stderr, "only one file name allowed\n"); 113218893Sdim return 3; 114218893Sdim } 115193323Sed else 116218893Sdim name = arg; 117218893Sdim source = load(name, &len); 118218893Sdim if (source == NULL) { 119193323Sed fprintf(stderr, "memory allocation failure\n"); 120193323Sed return 4; 121198090Srdivacky } 122198090Srdivacky if (len == 0) { 123193323Sed fprintf(stderr, "could not read %s, or it was empty\n", 124218893Sdim name == NULL ? "<stdin>" : name); 125218893Sdim free(source); 126203954Srdivacky return 3; 127221345Sdim } 128243830Sdim if (skip >= len) { 129221345Sdim fprintf(stderr, "skip request of %d leaves no input\n", skip); 130193323Sed free(source); 131218893Sdim return 3; 132193323Sed } 133193323Sed 134218893Sdim /* test inflate data with offset skip */ 135218893Sdim len -= skip; 136203954Srdivacky sourcelen = (unsigned long)len; 137203954Srdivacky ret = puff(NIL, &destlen, source + skip, &sourcelen); 138203954Srdivacky if (ret) 139203954Srdivacky fprintf(stderr, "puff() failed with return code %d\n", ret); 140203954Srdivacky else { 141223017Sdim fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen); 142218893Sdim if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n", 143218893Sdim len - sourcelen); 144218893Sdim } 145263508Sdim 146218893Sdim /* if requested, inflate again and write decompressd data to stdout */ 147223017Sdim if (put && ret == 0) { 148226633Sdim if (fail) 149193323Sed destlen >>= 1; 150193323Sed dest = malloc(destlen); 151218893Sdim if (dest == NULL) { 152218893Sdim fprintf(stderr, "memory allocation failure\n"); 153218893Sdim free(source); 154221345Sdim return 4; 155193323Sed } 156263508Sdim puff(dest, &destlen, source + skip, &sourcelen); 157193323Sed SET_BINARY_MODE(stdout); 158193323Sed fwrite(dest, 1, destlen, stdout); 159218893Sdim free(dest); 160193323Sed } 161193323Sed 162193323Sed /* clean up */ 163193323Sed free(source); 164193323Sed return ret; 165193323Sed} 166193323Sed