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