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