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