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