Home | History | Annotate | Line # | Download | only in examples
zran.c revision 1.1.1.3
      1      1.1  christos /* zran.c -- example of zlib/gzip stream indexing and random access
      2  1.1.1.3  christos  * Copyright (C) 2005, 2012, 2018 Mark Adler
      3      1.1  christos  * For conditions of distribution and use, see copyright notice in zlib.h
      4  1.1.1.3  christos  * Version 1.2  14 Oct 2018  Mark Adler */
      5  1.1.1.2  christos 
      6  1.1.1.2  christos /* Version History:
      7  1.1.1.2  christos  1.0  29 May 2005  First version
      8  1.1.1.2  christos  1.1  29 Sep 2012  Fix memory reallocation error
      9  1.1.1.3  christos  1.2  14 Oct 2018  Handle gzip streams with multiple members
     10  1.1.1.3  christos                    Add a header file to facilitate usage in applications
     11  1.1.1.2  christos  */
     12      1.1  christos 
     13      1.1  christos /* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
     14      1.1  christos    for random access of a compressed file.  A file containing a zlib or gzip
     15      1.1  christos    stream is provided on the command line.  The compressed stream is decoded in
     16      1.1  christos    its entirety, and an index built with access points about every SPAN bytes
     17      1.1  christos    in the uncompressed output.  The compressed file is left open, and can then
     18      1.1  christos    be read randomly, having to decompress on the average SPAN/2 uncompressed
     19      1.1  christos    bytes before getting to the desired block of data.
     20      1.1  christos 
     21      1.1  christos    An access point can be created at the start of any deflate block, by saving
     22      1.1  christos    the starting file offset and bit of that block, and the 32K bytes of
     23      1.1  christos    uncompressed data that precede that block.  Also the uncompressed offset of
     24  1.1.1.3  christos    that block is saved to provide a reference for locating a desired starting
     25  1.1.1.3  christos    point in the uncompressed stream.  deflate_index_build() works by
     26  1.1.1.3  christos    decompressing the input zlib or gzip stream a block at a time, and at the
     27  1.1.1.3  christos    end of each block deciding if enough uncompressed data has gone by to
     28  1.1.1.3  christos    justify the creation of a new access point.  If so, that point is saved in a
     29  1.1.1.3  christos    data structure that grows as needed to accommodate the points.
     30      1.1  christos 
     31      1.1  christos    To use the index, an offset in the uncompressed data is provided, for which
     32  1.1.1.2  christos    the latest access point at or preceding that offset is located in the index.
     33      1.1  christos    The input file is positioned to the specified location in the index, and if
     34      1.1  christos    necessary the first few bits of the compressed data is read from the file.
     35      1.1  christos    inflate is initialized with those bits and the 32K of uncompressed data, and
     36      1.1  christos    the decompression then proceeds until the desired offset in the file is
     37      1.1  christos    reached.  Then the decompression continues to read the desired uncompressed
     38      1.1  christos    data from the file.
     39      1.1  christos 
     40      1.1  christos    Another approach would be to generate the index on demand.  In that case,
     41      1.1  christos    requests for random access reads from the compressed data would try to use
     42      1.1  christos    the index, but if a read far enough past the end of the index is required,
     43      1.1  christos    then further index entries would be generated and added.
     44      1.1  christos 
     45      1.1  christos    There is some fair bit of overhead to starting inflation for the random
     46      1.1  christos    access, mainly copying the 32K byte dictionary.  So if small pieces of the
     47      1.1  christos    file are being accessed, it would make sense to implement a cache to hold
     48  1.1.1.3  christos    some lookahead and avoid many calls to deflate_index_extract() for small
     49  1.1.1.3  christos    lengths.
     50      1.1  christos 
     51      1.1  christos    Another way to build an index would be to use inflateCopy().  That would
     52      1.1  christos    not be constrained to have access points at block boundaries, but requires
     53      1.1  christos    more memory per access point, and also cannot be saved to file due to the
     54      1.1  christos    use of pointers in the state.  The approach here allows for storage of the
     55      1.1  christos    index in a file.
     56      1.1  christos  */
     57      1.1  christos 
     58      1.1  christos #include <stdio.h>
     59      1.1  christos #include <stdlib.h>
     60      1.1  christos #include <string.h>
     61      1.1  christos #include "zlib.h"
     62  1.1.1.3  christos #include "zran.h"
     63      1.1  christos 
     64      1.1  christos #define WINSIZE 32768U      /* sliding window size */
     65      1.1  christos #define CHUNK 16384         /* file input buffer size */
     66      1.1  christos 
     67  1.1.1.3  christos /* Access point entry. */
     68      1.1  christos struct point {
     69      1.1  christos     off_t out;          /* corresponding offset in uncompressed data */
     70      1.1  christos     off_t in;           /* offset in input file of first full byte */
     71  1.1.1.3  christos     int bits;           /* number of bits (1-7) from byte at in-1, or 0 */
     72      1.1  christos     unsigned char window[WINSIZE];  /* preceding 32K of uncompressed data */
     73      1.1  christos };
     74      1.1  christos 
     75  1.1.1.3  christos /* See comments in zran.h. */
     76  1.1.1.3  christos void deflate_index_free(struct deflate_index *index)
     77      1.1  christos {
     78      1.1  christos     if (index != NULL) {
     79      1.1  christos         free(index->list);
     80      1.1  christos         free(index);
     81      1.1  christos     }
     82      1.1  christos }
     83      1.1  christos 
     84  1.1.1.3  christos /* Add an entry to the access point list. If out of memory, deallocate the
     85  1.1.1.3  christos    existing list and return NULL. index->gzip is the allocated size of the
     86  1.1.1.3  christos    index in point entries, until it is time for deflate_index_build() to
     87  1.1.1.3  christos    return, at which point gzip is set to indicate a gzip file or not.
     88  1.1.1.3  christos  */
     89  1.1.1.3  christos static struct deflate_index *addpoint(struct deflate_index *index, int bits,
     90  1.1.1.3  christos                                       off_t in, off_t out, unsigned left,
     91  1.1.1.3  christos                                       unsigned char *window)
     92      1.1  christos {
     93      1.1  christos     struct point *next;
     94      1.1  christos 
     95      1.1  christos     /* if list is empty, create it (start with eight points) */
     96      1.1  christos     if (index == NULL) {
     97  1.1.1.3  christos         index = malloc(sizeof(struct deflate_index));
     98      1.1  christos         if (index == NULL) return NULL;
     99      1.1  christos         index->list = malloc(sizeof(struct point) << 3);
    100      1.1  christos         if (index->list == NULL) {
    101      1.1  christos             free(index);
    102      1.1  christos             return NULL;
    103      1.1  christos         }
    104  1.1.1.3  christos         index->gzip = 8;
    105      1.1  christos         index->have = 0;
    106      1.1  christos     }
    107      1.1  christos 
    108      1.1  christos     /* if list is full, make it bigger */
    109  1.1.1.3  christos     else if (index->have == index->gzip) {
    110  1.1.1.3  christos         index->gzip <<= 1;
    111  1.1.1.3  christos         next = realloc(index->list, sizeof(struct point) * index->gzip);
    112      1.1  christos         if (next == NULL) {
    113  1.1.1.3  christos             deflate_index_free(index);
    114      1.1  christos             return NULL;
    115      1.1  christos         }
    116      1.1  christos         index->list = next;
    117      1.1  christos     }
    118      1.1  christos 
    119      1.1  christos     /* fill in entry and increment how many we have */
    120  1.1.1.3  christos     next = (struct point *)(index->list) + index->have;
    121      1.1  christos     next->bits = bits;
    122      1.1  christos     next->in = in;
    123      1.1  christos     next->out = out;
    124      1.1  christos     if (left)
    125      1.1  christos         memcpy(next->window, window + WINSIZE - left, left);
    126      1.1  christos     if (left < WINSIZE)
    127      1.1  christos         memcpy(next->window + left, window, WINSIZE - left);
    128      1.1  christos     index->have++;
    129      1.1  christos 
    130      1.1  christos     /* return list, possibly reallocated */
    131      1.1  christos     return index;
    132      1.1  christos }
    133      1.1  christos 
    134  1.1.1.3  christos /* See comments in zran.h. */
    135  1.1.1.3  christos int deflate_index_build(FILE *in, off_t span, struct deflate_index **built)
    136      1.1  christos {
    137      1.1  christos     int ret;
    138  1.1.1.3  christos     int gzip = 0;               /* true if reading a gzip file */
    139      1.1  christos     off_t totin, totout;        /* our own total counters to avoid 4GB limit */
    140      1.1  christos     off_t last;                 /* totout value of last access point */
    141  1.1.1.3  christos     struct deflate_index *index;    /* access points being generated */
    142      1.1  christos     z_stream strm;
    143      1.1  christos     unsigned char input[CHUNK];
    144      1.1  christos     unsigned char window[WINSIZE];
    145      1.1  christos 
    146      1.1  christos     /* initialize inflate */
    147      1.1  christos     strm.zalloc = Z_NULL;
    148      1.1  christos     strm.zfree = Z_NULL;
    149      1.1  christos     strm.opaque = Z_NULL;
    150      1.1  christos     strm.avail_in = 0;
    151      1.1  christos     strm.next_in = Z_NULL;
    152      1.1  christos     ret = inflateInit2(&strm, 47);      /* automatic zlib or gzip decoding */
    153      1.1  christos     if (ret != Z_OK)
    154      1.1  christos         return ret;
    155      1.1  christos 
    156      1.1  christos     /* inflate the input, maintain a sliding window, and build an index -- this
    157      1.1  christos        also validates the integrity of the compressed data using the check
    158  1.1.1.3  christos        information in the gzip or zlib stream */
    159      1.1  christos     totin = totout = last = 0;
    160      1.1  christos     index = NULL;               /* will be allocated by first addpoint() */
    161      1.1  christos     strm.avail_out = 0;
    162      1.1  christos     do {
    163      1.1  christos         /* get some compressed data from input file */
    164      1.1  christos         strm.avail_in = fread(input, 1, CHUNK, in);
    165      1.1  christos         if (ferror(in)) {
    166      1.1  christos             ret = Z_ERRNO;
    167  1.1.1.3  christos             goto deflate_index_build_error;
    168      1.1  christos         }
    169      1.1  christos         if (strm.avail_in == 0) {
    170      1.1  christos             ret = Z_DATA_ERROR;
    171  1.1.1.3  christos             goto deflate_index_build_error;
    172      1.1  christos         }
    173      1.1  christos         strm.next_in = input;
    174      1.1  christos 
    175  1.1.1.3  christos         /* check for a gzip stream */
    176  1.1.1.3  christos         if (totin == 0 && strm.avail_in >= 3 &&
    177  1.1.1.3  christos             input[0] == 31 && input[1] == 139 && input[2] == 8)
    178  1.1.1.3  christos             gzip = 1;
    179  1.1.1.3  christos 
    180      1.1  christos         /* process all of that, or until end of stream */
    181      1.1  christos         do {
    182      1.1  christos             /* reset sliding window if necessary */
    183      1.1  christos             if (strm.avail_out == 0) {
    184      1.1  christos                 strm.avail_out = WINSIZE;
    185      1.1  christos                 strm.next_out = window;
    186      1.1  christos             }
    187      1.1  christos 
    188      1.1  christos             /* inflate until out of input, output, or at end of block --
    189      1.1  christos                update the total input and output counters */
    190      1.1  christos             totin += strm.avail_in;
    191      1.1  christos             totout += strm.avail_out;
    192      1.1  christos             ret = inflate(&strm, Z_BLOCK);      /* return at end of block */
    193      1.1  christos             totin -= strm.avail_in;
    194      1.1  christos             totout -= strm.avail_out;
    195      1.1  christos             if (ret == Z_NEED_DICT)
    196      1.1  christos                 ret = Z_DATA_ERROR;
    197      1.1  christos             if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
    198  1.1.1.3  christos                 goto deflate_index_build_error;
    199  1.1.1.3  christos             if (ret == Z_STREAM_END) {
    200  1.1.1.3  christos                 if (gzip &&
    201  1.1.1.3  christos                     (strm.avail_in || ungetc(getc(in), in) != EOF)) {
    202  1.1.1.3  christos                     ret = inflateReset(&strm);
    203  1.1.1.3  christos                     if (ret != Z_OK)
    204  1.1.1.3  christos                         goto deflate_index_build_error;
    205  1.1.1.3  christos                     continue;
    206  1.1.1.3  christos                 }
    207      1.1  christos                 break;
    208  1.1.1.3  christos             }
    209      1.1  christos 
    210      1.1  christos             /* if at end of block, consider adding an index entry (note that if
    211      1.1  christos                data_type indicates an end-of-block, then all of the
    212      1.1  christos                uncompressed data from that block has been delivered, and none
    213      1.1  christos                of the compressed data after that block has been consumed,
    214      1.1  christos                except for up to seven bits) -- the totout == 0 provides an
    215      1.1  christos                entry point after the zlib or gzip header, and assures that the
    216      1.1  christos                index always has at least one access point; we avoid creating an
    217      1.1  christos                access point after the last block by checking bit 6 of data_type
    218      1.1  christos              */
    219      1.1  christos             if ((strm.data_type & 128) && !(strm.data_type & 64) &&
    220      1.1  christos                 (totout == 0 || totout - last > span)) {
    221      1.1  christos                 index = addpoint(index, strm.data_type & 7, totin,
    222      1.1  christos                                  totout, strm.avail_out, window);
    223      1.1  christos                 if (index == NULL) {
    224      1.1  christos                     ret = Z_MEM_ERROR;
    225  1.1.1.3  christos                     goto deflate_index_build_error;
    226      1.1  christos                 }
    227      1.1  christos                 last = totout;
    228      1.1  christos             }
    229      1.1  christos         } while (strm.avail_in != 0);
    230      1.1  christos     } while (ret != Z_STREAM_END);
    231      1.1  christos 
    232      1.1  christos     /* clean up and return index (release unused entries in list) */
    233      1.1  christos     (void)inflateEnd(&strm);
    234  1.1.1.2  christos     index->list = realloc(index->list, sizeof(struct point) * index->have);
    235  1.1.1.3  christos     index->gzip = gzip;
    236  1.1.1.3  christos     index->length = totout;
    237      1.1  christos     *built = index;
    238  1.1.1.3  christos     return index->have;
    239      1.1  christos 
    240      1.1  christos     /* return error */
    241  1.1.1.3  christos   deflate_index_build_error:
    242      1.1  christos     (void)inflateEnd(&strm);
    243  1.1.1.3  christos     deflate_index_free(index);
    244      1.1  christos     return ret;
    245      1.1  christos }
    246      1.1  christos 
    247  1.1.1.3  christos /* See comments in zran.h. */
    248  1.1.1.3  christos int deflate_index_extract(FILE *in, struct deflate_index *index, off_t offset,
    249  1.1.1.3  christos                           unsigned char *buf, int len)
    250      1.1  christos {
    251      1.1  christos     int ret, skip;
    252      1.1  christos     z_stream strm;
    253      1.1  christos     struct point *here;
    254      1.1  christos     unsigned char input[CHUNK];
    255      1.1  christos     unsigned char discard[WINSIZE];
    256      1.1  christos 
    257      1.1  christos     /* proceed only if something reasonable to do */
    258      1.1  christos     if (len < 0)
    259      1.1  christos         return 0;
    260      1.1  christos 
    261      1.1  christos     /* find where in stream to start */
    262      1.1  christos     here = index->list;
    263      1.1  christos     ret = index->have;
    264      1.1  christos     while (--ret && here[1].out <= offset)
    265      1.1  christos         here++;
    266      1.1  christos 
    267      1.1  christos     /* initialize file and inflate state to start there */
    268      1.1  christos     strm.zalloc = Z_NULL;
    269      1.1  christos     strm.zfree = Z_NULL;
    270      1.1  christos     strm.opaque = Z_NULL;
    271      1.1  christos     strm.avail_in = 0;
    272      1.1  christos     strm.next_in = Z_NULL;
    273      1.1  christos     ret = inflateInit2(&strm, -15);         /* raw inflate */
    274      1.1  christos     if (ret != Z_OK)
    275      1.1  christos         return ret;
    276      1.1  christos     ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
    277      1.1  christos     if (ret == -1)
    278  1.1.1.3  christos         goto deflate_index_extract_ret;
    279      1.1  christos     if (here->bits) {
    280      1.1  christos         ret = getc(in);
    281      1.1  christos         if (ret == -1) {
    282      1.1  christos             ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
    283  1.1.1.3  christos             goto deflate_index_extract_ret;
    284      1.1  christos         }
    285      1.1  christos         (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits));
    286      1.1  christos     }
    287      1.1  christos     (void)inflateSetDictionary(&strm, here->window, WINSIZE);
    288      1.1  christos 
    289      1.1  christos     /* skip uncompressed bytes until offset reached, then satisfy request */
    290      1.1  christos     offset -= here->out;
    291      1.1  christos     strm.avail_in = 0;
    292      1.1  christos     skip = 1;                               /* while skipping to offset */
    293      1.1  christos     do {
    294      1.1  christos         /* define where to put uncompressed data, and how much */
    295      1.1  christos         if (offset > WINSIZE) {             /* skip WINSIZE bytes */
    296      1.1  christos             strm.avail_out = WINSIZE;
    297      1.1  christos             strm.next_out = discard;
    298      1.1  christos             offset -= WINSIZE;
    299      1.1  christos         }
    300  1.1.1.3  christos         else if (offset > 0) {              /* last skip */
    301      1.1  christos             strm.avail_out = (unsigned)offset;
    302      1.1  christos             strm.next_out = discard;
    303      1.1  christos             offset = 0;
    304      1.1  christos         }
    305  1.1.1.3  christos         else if (skip) {                    /* at offset now */
    306  1.1.1.3  christos             strm.avail_out = len;
    307  1.1.1.3  christos             strm.next_out = buf;
    308  1.1.1.3  christos             skip = 0;                       /* only do this once */
    309  1.1.1.3  christos         }
    310      1.1  christos 
    311      1.1  christos         /* uncompress until avail_out filled, or end of stream */
    312      1.1  christos         do {
    313      1.1  christos             if (strm.avail_in == 0) {
    314      1.1  christos                 strm.avail_in = fread(input, 1, CHUNK, in);
    315      1.1  christos                 if (ferror(in)) {
    316      1.1  christos                     ret = Z_ERRNO;
    317  1.1.1.3  christos                     goto deflate_index_extract_ret;
    318      1.1  christos                 }
    319      1.1  christos                 if (strm.avail_in == 0) {
    320      1.1  christos                     ret = Z_DATA_ERROR;
    321  1.1.1.3  christos                     goto deflate_index_extract_ret;
    322      1.1  christos                 }
    323      1.1  christos                 strm.next_in = input;
    324      1.1  christos             }
    325      1.1  christos             ret = inflate(&strm, Z_NO_FLUSH);       /* normal inflate */
    326      1.1  christos             if (ret == Z_NEED_DICT)
    327      1.1  christos                 ret = Z_DATA_ERROR;
    328      1.1  christos             if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
    329  1.1.1.3  christos                 goto deflate_index_extract_ret;
    330  1.1.1.3  christos             if (ret == Z_STREAM_END) {
    331  1.1.1.3  christos                 /* the raw deflate stream has ended */
    332  1.1.1.3  christos                 if (index->gzip == 0)
    333  1.1.1.3  christos                     /* this is a zlib stream that has ended -- done */
    334  1.1.1.3  christos                     break;
    335  1.1.1.3  christos 
    336  1.1.1.3  christos                 /* near the end of a gzip member, which might be followed by
    337  1.1.1.3  christos                    another gzip member -- skip the gzip trailer and see if
    338  1.1.1.3  christos                    there is more input after it */
    339  1.1.1.3  christos                 if (strm.avail_in < 8) {
    340  1.1.1.3  christos                     fseeko(in, 8 - strm.avail_in, SEEK_CUR);
    341  1.1.1.3  christos                     strm.avail_in = 0;
    342  1.1.1.3  christos                 }
    343  1.1.1.3  christos                 else {
    344  1.1.1.3  christos                     strm.avail_in -= 8;
    345  1.1.1.3  christos                     strm.next_in += 8;
    346  1.1.1.3  christos                 }
    347  1.1.1.3  christos                 if (strm.avail_in == 0 && ungetc(getc(in), in) == EOF)
    348  1.1.1.3  christos                     /* the input ended after the gzip trailer -- done */
    349  1.1.1.3  christos                     break;
    350  1.1.1.3  christos 
    351  1.1.1.3  christos                 /* there is more input, so another gzip member should follow --
    352  1.1.1.3  christos                    validate and skip the gzip header */
    353  1.1.1.3  christos                 ret = inflateReset2(&strm, 31);
    354  1.1.1.3  christos                 if (ret != Z_OK)
    355  1.1.1.3  christos                     goto deflate_index_extract_ret;
    356  1.1.1.3  christos                 do {
    357  1.1.1.3  christos                     if (strm.avail_in == 0) {
    358  1.1.1.3  christos                         strm.avail_in = fread(input, 1, CHUNK, in);
    359  1.1.1.3  christos                         if (ferror(in)) {
    360  1.1.1.3  christos                             ret = Z_ERRNO;
    361  1.1.1.3  christos                             goto deflate_index_extract_ret;
    362  1.1.1.3  christos                         }
    363  1.1.1.3  christos                         if (strm.avail_in == 0) {
    364  1.1.1.3  christos                             ret = Z_DATA_ERROR;
    365  1.1.1.3  christos                             goto deflate_index_extract_ret;
    366  1.1.1.3  christos                         }
    367  1.1.1.3  christos                         strm.next_in = input;
    368  1.1.1.3  christos                     }
    369  1.1.1.3  christos                     ret = inflate(&strm, Z_BLOCK);
    370  1.1.1.3  christos                     if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
    371  1.1.1.3  christos                         goto deflate_index_extract_ret;
    372  1.1.1.3  christos                 } while ((strm.data_type & 128) == 0);
    373  1.1.1.3  christos 
    374  1.1.1.3  christos                 /* set up to continue decompression of the raw deflate stream
    375  1.1.1.3  christos                    that follows the gzip header */
    376  1.1.1.3  christos                 ret = inflateReset2(&strm, -15);
    377  1.1.1.3  christos                 if (ret != Z_OK)
    378  1.1.1.3  christos                     goto deflate_index_extract_ret;
    379  1.1.1.3  christos             }
    380  1.1.1.3  christos 
    381  1.1.1.3  christos             /* continue to process the available input before reading more */
    382      1.1  christos         } while (strm.avail_out != 0);
    383      1.1  christos 
    384      1.1  christos         if (ret == Z_STREAM_END)
    385  1.1.1.3  christos             /* reached the end of the compressed data -- return the data that
    386  1.1.1.3  christos                was available, possibly less than requested */
    387      1.1  christos             break;
    388      1.1  christos 
    389  1.1.1.3  christos         /* do until offset reached and requested data read */
    390      1.1  christos     } while (skip);
    391      1.1  christos 
    392  1.1.1.3  christos     /* compute the number of uncompressed bytes read after the offset */
    393      1.1  christos     ret = skip ? 0 : len - strm.avail_out;
    394      1.1  christos 
    395  1.1.1.3  christos     /* clean up and return the bytes read, or the negative error */
    396  1.1.1.3  christos   deflate_index_extract_ret:
    397      1.1  christos     (void)inflateEnd(&strm);
    398      1.1  christos     return ret;
    399      1.1  christos }
    400      1.1  christos 
    401  1.1.1.3  christos #ifdef TEST
    402  1.1.1.3  christos 
    403  1.1.1.3  christos #define SPAN 1048576L       /* desired distance between access points */
    404  1.1.1.3  christos #define LEN 16384           /* number of bytes to extract */
    405  1.1.1.3  christos 
    406  1.1.1.3  christos /* Demonstrate the use of deflate_index_build() and deflate_index_extract() by
    407  1.1.1.3  christos    processing the file provided on the command line, and extracting LEN bytes
    408  1.1.1.3  christos    from 2/3rds of the way through the uncompressed output, writing that to
    409  1.1.1.3  christos    stdout. An offset can be provided as the second argument, in which case the
    410  1.1.1.3  christos    data is extracted from there instead. */
    411      1.1  christos int main(int argc, char **argv)
    412      1.1  christos {
    413      1.1  christos     int len;
    414  1.1.1.3  christos     off_t offset = -1;
    415      1.1  christos     FILE *in;
    416  1.1.1.3  christos     struct deflate_index *index = NULL;
    417  1.1.1.3  christos     unsigned char buf[LEN];
    418      1.1  christos 
    419      1.1  christos     /* open input file */
    420  1.1.1.3  christos     if (argc < 2 || argc > 3) {
    421  1.1.1.3  christos         fprintf(stderr, "usage: zran file.gz [offset]\n");
    422      1.1  christos         return 1;
    423      1.1  christos     }
    424      1.1  christos     in = fopen(argv[1], "rb");
    425      1.1  christos     if (in == NULL) {
    426      1.1  christos         fprintf(stderr, "zran: could not open %s for reading\n", argv[1]);
    427      1.1  christos         return 1;
    428      1.1  christos     }
    429      1.1  christos 
    430  1.1.1.3  christos     /* get optional offset */
    431  1.1.1.3  christos     if (argc == 3) {
    432  1.1.1.3  christos         char *end;
    433  1.1.1.3  christos         offset = strtoll(argv[2], &end, 10);
    434  1.1.1.3  christos         if (*end || offset < 0) {
    435  1.1.1.3  christos             fprintf(stderr, "zran: %s is not a valid offset\n", argv[2]);
    436  1.1.1.3  christos             return 1;
    437  1.1.1.3  christos         }
    438  1.1.1.3  christos     }
    439  1.1.1.3  christos 
    440      1.1  christos     /* build index */
    441  1.1.1.3  christos     len = deflate_index_build(in, SPAN, &index);
    442      1.1  christos     if (len < 0) {
    443      1.1  christos         fclose(in);
    444      1.1  christos         switch (len) {
    445      1.1  christos         case Z_MEM_ERROR:
    446      1.1  christos             fprintf(stderr, "zran: out of memory\n");
    447      1.1  christos             break;
    448      1.1  christos         case Z_DATA_ERROR:
    449      1.1  christos             fprintf(stderr, "zran: compressed data error in %s\n", argv[1]);
    450      1.1  christos             break;
    451      1.1  christos         case Z_ERRNO:
    452      1.1  christos             fprintf(stderr, "zran: read error on %s\n", argv[1]);
    453      1.1  christos             break;
    454      1.1  christos         default:
    455      1.1  christos             fprintf(stderr, "zran: error %d while building index\n", len);
    456      1.1  christos         }
    457      1.1  christos         return 1;
    458      1.1  christos     }
    459      1.1  christos     fprintf(stderr, "zran: built index with %d access points\n", len);
    460      1.1  christos 
    461      1.1  christos     /* use index by reading some bytes from an arbitrary offset */
    462  1.1.1.3  christos     if (offset == -1)
    463  1.1.1.3  christos         offset = (index->length << 1) / 3;
    464  1.1.1.3  christos     len = deflate_index_extract(in, index, offset, buf, LEN);
    465      1.1  christos     if (len < 0)
    466      1.1  christos         fprintf(stderr, "zran: extraction failed: %s error\n",
    467      1.1  christos                 len == Z_MEM_ERROR ? "out of memory" : "input corrupted");
    468      1.1  christos     else {
    469      1.1  christos         fwrite(buf, 1, len, stdout);
    470      1.1  christos         fprintf(stderr, "zran: extracted %d bytes at %llu\n", len, offset);
    471      1.1  christos     }
    472      1.1  christos 
    473      1.1  christos     /* clean up and exit */
    474  1.1.1.3  christos     deflate_index_free(index);
    475      1.1  christos     fclose(in);
    476      1.1  christos     return 0;
    477      1.1  christos }
    478  1.1.1.3  christos 
    479  1.1.1.3  christos #endif
    480