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