1ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes/*
2ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes * pufftest.c
304351a92ecc8429c999acbfc5dfe5aa8bee1d19dElliott Hughes * Copyright (C) 2002-2013 Mark Adler
4ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes * For conditions of distribution and use, see copyright notice in puff.h
504351a92ecc8429c999acbfc5dfe5aa8bee1d19dElliott Hughes * version 2.3, 21 Jan 2013
6ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes */
7ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
8ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes/* Example of how to use puff().
9ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
10ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   Usage: puff [-w] [-f] [-nnn] file
11ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes          ... | puff [-w] [-f] [-nnn]
12ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
13ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   where file is the input file with deflate data, nnn is the number of bytes
14ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   of input to skip before inflating (e.g. to skip a zlib or gzip header), and
15ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   -w is used to write the decompressed data to stdout.  -f is for coverage
16ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   testing, and causes pufftest to fail with not enough output space (-f does
17ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   a write like -w, so -w is not required). */
18ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
19ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#include <stdio.h>
20ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#include <stdlib.h>
21ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#include "puff.h"
22ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
23ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
24ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#  include <fcntl.h>
25ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#  include <io.h>
26ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
27ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#else
28ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#  define SET_BINARY_MODE(file)
29ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#endif
30ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
31ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes#define local static
32ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
33ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes/* Return size times approximately the cube root of 2, keeping the result as 1,
34ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   3, or 5 times a power of 2 -- the result is always > size, until the result
35ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   is the maximum value of an unsigned long, where it remains.  This is useful
36ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   to keep reallocations less than ~33% over the actual data. */
37ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hugheslocal size_t bythirds(size_t size)
38ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes{
39ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    int n;
40ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    size_t m;
41ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
42ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    m = size;
43ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    for (n = 0; m; n++)
44ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        m >>= 1;
45ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (n < 3)
46ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        return size + 1;
47ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    n -= 3;
48ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    m = size >> n;
49ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    m += m == 6 ? 2 : 1;
50ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    m <<= n;
51ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    return m > size ? m : (size_t)(-1);
52ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes}
53ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
54ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes/* Read the input file *name, or stdin if name is NULL, into allocated memory.
55ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   Reallocate to larger buffers until the entire file is read in.  Return a
56ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   pointer to the allocated data, or NULL if there was a memory allocation
57ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   failure.  *len is the number of bytes of data read from the input file (even
58ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   if load() returns NULL).  If the input file was empty or could not be opened
59ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes   or read, *len is zero. */
60ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hugheslocal void *load(const char *name, size_t *len)
61ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes{
62ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    size_t size;
63ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    void *buf, *swap;
64ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    FILE *in;
65ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
66ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    *len = 0;
67ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    buf = malloc(size = 4096);
68ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (buf == NULL)
69ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        return NULL;
70ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    in = name == NULL ? stdin : fopen(name, "rb");
71ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (in != NULL) {
72ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        for (;;) {
73ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            *len += fread((char *)buf + *len, 1, size - *len, in);
74ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            if (*len < size) break;
75ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            size = bythirds(size);
76ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            if (size == *len || (swap = realloc(buf, size)) == NULL) {
77ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                free(buf);
78ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                buf = NULL;
79ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                break;
80ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            }
81ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            buf = swap;
82ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        }
83ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fclose(in);
84ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    }
85ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    return buf;
86ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes}
87ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
88ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughesint main(int argc, char **argv)
89ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes{
90ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    int ret, put = 0, fail = 0;
91ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    unsigned skip = 0;
92ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    char *arg, *name = NULL;
93ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    unsigned char *source = NULL, *dest;
94ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    size_t len = 0;
95ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    unsigned long sourcelen, destlen;
96ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
97ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    /* process arguments */
98ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    while (arg = *++argv, --argc)
99ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        if (arg[0] == '-') {
100ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            if (arg[1] == 'w' && arg[2] == 0)
101ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                put = 1;
102ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            else if (arg[1] == 'f' && arg[2] == 0)
103ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                fail = 1, put = 1;
104ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            else if (arg[1] >= '0' && arg[1] <= '9')
105ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                skip = (unsigned)atoi(arg + 1);
106ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            else {
107ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                fprintf(stderr, "invalid option %s\n", arg);
108ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                return 3;
109ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            }
110ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        }
111ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        else if (name != NULL) {
112ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            fprintf(stderr, "only one file name allowed\n");
113ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            return 3;
114ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        }
115ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        else
116ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            name = arg;
117ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    source = load(name, &len);
118ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (source == NULL) {
119ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fprintf(stderr, "memory allocation failure\n");
120ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        return 4;
121ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    }
122ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (len == 0) {
123ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fprintf(stderr, "could not read %s, or it was empty\n",
124ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                name == NULL ? "<stdin>" : name);
125ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        free(source);
126ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        return 3;
127ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    }
128ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (skip >= len) {
129ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fprintf(stderr, "skip request of %d leaves no input\n", skip);
130ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        free(source);
131ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        return 3;
132ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    }
133ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
134ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    /* test inflate data with offset skip */
135ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    len -= skip;
136ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    sourcelen = (unsigned long)len;
137ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    ret = puff(NIL, &destlen, source + skip, &sourcelen);
138ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (ret)
139ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fprintf(stderr, "puff() failed with return code %d\n", ret);
140ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    else {
141ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen);
142ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n",
143ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes                                     len - sourcelen);
144ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    }
145ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
146ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    /* if requested, inflate again and write decompressd data to stdout */
147ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    if (put && ret == 0) {
148ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        if (fail)
149ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            destlen >>= 1;
150ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        dest = malloc(destlen);
151ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        if (dest == NULL) {
152ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            fprintf(stderr, "memory allocation failure\n");
153ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            free(source);
154ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes            return 4;
155ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        }
156ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        puff(dest, &destlen, source + skip, &sourcelen);
157ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        SET_BINARY_MODE(stdout);
158ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        fwrite(dest, 1, destlen, stdout);
159ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes        free(dest);
160ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    }
161ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes
162ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    /* clean up */
163ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    free(source);
164ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes    return ret;
165ee9e11d0d4e3361533860bf04896abb86a291bfbElliott Hughes}
166