1212397c6Schristos /*
2212397c6Schristos * pufftest.c
3*ba340e45Schristos * Copyright (C) 2002-2013 Mark Adler
4212397c6Schristos * For conditions of distribution and use, see copyright notice in puff.h
5*ba340e45Schristos * version 2.3, 21 Jan 2013
6212397c6Schristos */
7212397c6Schristos
8212397c6Schristos /* Example of how to use puff().
9212397c6Schristos
10212397c6Schristos Usage: puff [-w] [-f] [-nnn] file
11212397c6Schristos ... | puff [-w] [-f] [-nnn]
12212397c6Schristos
13212397c6Schristos where file is the input file with deflate data, nnn is the number of bytes
14212397c6Schristos of input to skip before inflating (e.g. to skip a zlib or gzip header), and
15212397c6Schristos -w is used to write the decompressed data to stdout. -f is for coverage
16212397c6Schristos testing, and causes pufftest to fail with not enough output space (-f does
17212397c6Schristos a write like -w, so -w is not required). */
18212397c6Schristos
19212397c6Schristos #include <stdio.h>
20212397c6Schristos #include <stdlib.h>
21212397c6Schristos #include "puff.h"
22212397c6Schristos
23212397c6Schristos #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
24212397c6Schristos # include <fcntl.h>
25212397c6Schristos # include <io.h>
26212397c6Schristos # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
27212397c6Schristos #else
28212397c6Schristos # define SET_BINARY_MODE(file)
29212397c6Schristos #endif
30212397c6Schristos
31212397c6Schristos #define local static
32212397c6Schristos
33212397c6Schristos /* Return size times approximately the cube root of 2, keeping the result as 1,
34212397c6Schristos 3, or 5 times a power of 2 -- the result is always > size, until the result
35212397c6Schristos is the maximum value of an unsigned long, where it remains. This is useful
36212397c6Schristos to keep reallocations less than ~33% over the actual data. */
bythirds(size_t size)37212397c6Schristos local size_t bythirds(size_t size)
38212397c6Schristos {
39212397c6Schristos int n;
40212397c6Schristos size_t m;
41212397c6Schristos
42212397c6Schristos m = size;
43212397c6Schristos for (n = 0; m; n++)
44212397c6Schristos m >>= 1;
45212397c6Schristos if (n < 3)
46212397c6Schristos return size + 1;
47212397c6Schristos n -= 3;
48212397c6Schristos m = size >> n;
49212397c6Schristos m += m == 6 ? 2 : 1;
50212397c6Schristos m <<= n;
51212397c6Schristos return m > size ? m : (size_t)(-1);
52212397c6Schristos }
53212397c6Schristos
54212397c6Schristos /* Read the input file *name, or stdin if name is NULL, into allocated memory.
55212397c6Schristos Reallocate to larger buffers until the entire file is read in. Return a
56212397c6Schristos pointer to the allocated data, or NULL if there was a memory allocation
57212397c6Schristos failure. *len is the number of bytes of data read from the input file (even
58212397c6Schristos if load() returns NULL). If the input file was empty or could not be opened
59212397c6Schristos or read, *len is zero. */
load(const char * name,size_t * len)60212397c6Schristos local void *load(const char *name, size_t *len)
61212397c6Schristos {
62212397c6Schristos size_t size;
63212397c6Schristos void *buf, *swap;
64212397c6Schristos FILE *in;
65212397c6Schristos
66212397c6Schristos *len = 0;
67212397c6Schristos buf = malloc(size = 4096);
68212397c6Schristos if (buf == NULL)
69212397c6Schristos return NULL;
70212397c6Schristos in = name == NULL ? stdin : fopen(name, "rb");
71212397c6Schristos if (in != NULL) {
72212397c6Schristos for (;;) {
73212397c6Schristos *len += fread((char *)buf + *len, 1, size - *len, in);
74212397c6Schristos if (*len < size) break;
75212397c6Schristos size = bythirds(size);
76212397c6Schristos if (size == *len || (swap = realloc(buf, size)) == NULL) {
77212397c6Schristos free(buf);
78212397c6Schristos buf = NULL;
79212397c6Schristos break;
80212397c6Schristos }
81212397c6Schristos buf = swap;
82212397c6Schristos }
83212397c6Schristos fclose(in);
84212397c6Schristos }
85212397c6Schristos return buf;
86212397c6Schristos }
87212397c6Schristos
main(int argc,char ** argv)88212397c6Schristos int main(int argc, char **argv)
89212397c6Schristos {
90212397c6Schristos int ret, put = 0, fail = 0;
91212397c6Schristos unsigned skip = 0;
92212397c6Schristos char *arg, *name = NULL;
93212397c6Schristos unsigned char *source = NULL, *dest;
94212397c6Schristos size_t len = 0;
95212397c6Schristos unsigned long sourcelen, destlen;
96212397c6Schristos
97212397c6Schristos /* process arguments */
98212397c6Schristos while (arg = *++argv, --argc)
99212397c6Schristos if (arg[0] == '-') {
100212397c6Schristos if (arg[1] == 'w' && arg[2] == 0)
101212397c6Schristos put = 1;
102212397c6Schristos else if (arg[1] == 'f' && arg[2] == 0)
103212397c6Schristos fail = 1, put = 1;
104212397c6Schristos else if (arg[1] >= '0' && arg[1] <= '9')
105212397c6Schristos skip = (unsigned)atoi(arg + 1);
106212397c6Schristos else {
107212397c6Schristos fprintf(stderr, "invalid option %s\n", arg);
108212397c6Schristos return 3;
109212397c6Schristos }
110212397c6Schristos }
111212397c6Schristos else if (name != NULL) {
112212397c6Schristos fprintf(stderr, "only one file name allowed\n");
113212397c6Schristos return 3;
114212397c6Schristos }
115212397c6Schristos else
116212397c6Schristos name = arg;
117212397c6Schristos source = load(name, &len);
118212397c6Schristos if (source == NULL) {
119212397c6Schristos fprintf(stderr, "memory allocation failure\n");
120212397c6Schristos return 4;
121212397c6Schristos }
122212397c6Schristos if (len == 0) {
123212397c6Schristos fprintf(stderr, "could not read %s, or it was empty\n",
124212397c6Schristos name == NULL ? "<stdin>" : name);
125212397c6Schristos free(source);
126212397c6Schristos return 3;
127212397c6Schristos }
128212397c6Schristos if (skip >= len) {
129212397c6Schristos fprintf(stderr, "skip request of %d leaves no input\n", skip);
130212397c6Schristos free(source);
131212397c6Schristos return 3;
132212397c6Schristos }
133212397c6Schristos
134212397c6Schristos /* test inflate data with offset skip */
135212397c6Schristos len -= skip;
136212397c6Schristos sourcelen = (unsigned long)len;
137212397c6Schristos ret = puff(NIL, &destlen, source + skip, &sourcelen);
138212397c6Schristos if (ret)
139212397c6Schristos fprintf(stderr, "puff() failed with return code %d\n", ret);
140212397c6Schristos else {
141212397c6Schristos fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen);
142212397c6Schristos if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n",
143212397c6Schristos len - sourcelen);
144212397c6Schristos }
145212397c6Schristos
146212397c6Schristos /* if requested, inflate again and write decompressd data to stdout */
147212397c6Schristos if (put && ret == 0) {
148212397c6Schristos if (fail)
149212397c6Schristos destlen >>= 1;
150212397c6Schristos dest = malloc(destlen);
151212397c6Schristos if (dest == NULL) {
152212397c6Schristos fprintf(stderr, "memory allocation failure\n");
153212397c6Schristos free(source);
154212397c6Schristos return 4;
155212397c6Schristos }
156212397c6Schristos puff(dest, &destlen, source + skip, &sourcelen);
157212397c6Schristos SET_BINARY_MODE(stdout);
158212397c6Schristos fwrite(dest, 1, destlen, stdout);
159212397c6Schristos free(dest);
160212397c6Schristos }
161212397c6Schristos
162212397c6Schristos /* clean up */
163212397c6Schristos free(source);
164212397c6Schristos return ret;
165212397c6Schristos }
166