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