From Jakub Zawadzki: speed up random access to gzipped files, as per the
[obnox/wireshark/wip.git] / wiretap / file_wrappers.c
1 /* file_wrappers.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 /* file_access interface based heavily on zlib gzread.c and gzlib.c from zlib
24  * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
25  * under licence:
26  *
27  *  This software is provided 'as-is', without any express or implied
28  *  warranty.  In no event will the authors be held liable for any damages
29  *  arising from the use of this software.
30  *
31  *  Permission is granted to anyone to use this software for any purpose,
32  *  including commercial applications, and to alter it and redistribute it
33  *  freely, subject to the following restrictions:
34  *
35  *  1. The origin of this software must not be misrepresented; you must not
36  *     claim that you wrote the original software. If you use this software
37  *     in a product, an acknowledgment in the product documentation would be
38  *     appreciated but is not required.
39  *  2. Altered source versions must be plainly marked as such, and must not be
40  *     misrepresented as being the original software.
41  *  3. This notice may not be removed or altered from any source distribution.
42 */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif /* HAVE_UNISTD_H */
51
52 #include <errno.h>
53 #include <stdio.h>
54 #ifdef HAVE_FCNTL_H
55 #include <fcntl.h>
56 #endif /* HAVE_FCNTL_H */
57 #include <string.h>
58 #include "wtap-int.h"
59 #include "file_wrappers.h"
60 #include <wsutil/file_util.h>
61
62 /*
63  * See RFC 1952 for a description of the gzip file format.
64  *
65  * Some other compressed file formats we might want to support:
66  *
67  *      XZ format: http://tukaani.org/xz/
68  *
69  *      Bzip2 format: http://bzip.org/
70  */
71
72 /* #define GZBUFSIZE 8192 */
73 #define GZBUFSIZE 4096
74
75 struct wtap_reader {
76         int fd;                 /* file descriptor */
77         gint64 raw_pos;         /* current position in file (just to not call lseek()) */
78         gint64 pos;             /* current position in uncompressed data */
79         unsigned size;          /* buffer size */
80         unsigned char *in;      /* input buffer */
81         unsigned char *out;     /* output buffer (double-sized when reading) */
82         unsigned char *next;    /* next output data to deliver or write */
83
84         unsigned have;          /* amount of output data unused at next */
85         int eof;                /* true if end of input file reached */
86         gint64 start;           /* where the gzip data started, for rewinding */
87         gint64 raw;             /* where the raw data started, for seeking */
88         int compression;        /* 0: ?, 1: uncompressed, 2: zlib */
89         /* seek request */
90         gint64 skip;            /* amount to skip (already rewound if backwards) */
91         int seek;               /* true if seek request pending */
92         /* error information */
93         int err;                /* error code */
94
95         unsigned int  avail_in;  /* number of bytes available at next_in */
96         unsigned char *next_in;  /* next input byte */
97 #ifdef HAVE_LIBZ
98         /* zlib inflate stream */
99         z_stream strm;          /* stream structure in-place (not a pointer) */
100 #endif
101         /* fast seeking */
102         GPtrArray *fast_seek;
103         void *fast_seek_cur;
104 };
105
106 /* values for gz_state compression */
107 #define UNKNOWN         0       /* look for a gzip header */
108 #define UNCOMPRESSED    1       /* copy input directly */
109 #ifdef HAVE_LIBZ
110 #define ZLIB            2       /* decompress a zlib stream */
111 #define GZIP_AFTER_HEADER 3
112 #endif
113
114 static int      /* gz_load */
115 raw_read(FILE_T state, unsigned char *buf, unsigned int count, unsigned *have)
116 {
117         int ret;
118
119         *have = 0;
120         do {
121                 ret = read(state->fd, buf + *have, count - *have);
122                 if (ret <= 0)
123                         break;
124                 *have += ret;
125                 state->raw_pos += ret;
126         } while (*have < count);
127         if (ret < 0) {
128                 state->err = errno;
129                 return -1;
130         }
131         if (ret == 0)
132                 state->eof = 1;
133         return 0;
134 }
135
136 static int /* gz_avail */
137 fill_in_buffer(FILE_T state)
138 {
139         if (state->err)
140                 return -1;
141         if (state->eof == 0) {
142                 if (raw_read(state, state->in, state->size, (unsigned *)&(state->avail_in)) == -1)
143                         return -1;
144                 state->next_in = state->in;
145         }
146         return 0;
147 }
148
149 #define ZLIB_WINSIZE 32768
150
151 struct fast_seek_point {
152         gint64 out;     /* corresponding offset in uncompressed data */
153         gint64 in;              /* offset in input file of first full byte */
154
155         int compression;
156         union {
157                 struct {
158                         int bits;               /* number of bits (1-7) from byte at in - 1, or 0 */
159                         unsigned char window[ZLIB_WINSIZE];     /* preceding 32K of uncompressed data */
160
161                         /* be gentle with Z_STREAM_END, 8 bytes more... Another solution would be to comment checks out */
162                         guint32 adler;
163                         guint32 total_out;
164                 } zlib;
165         } data;
166 };
167
168 struct zlib_cur_seek_point {
169         unsigned char window[ZLIB_WINSIZE];     /* preceding 32K of uncompressed data */
170         unsigned int pos;
171         unsigned int have;
172 };
173
174 #define SPAN G_GINT64_CONSTANT(1048576)
175 static struct fast_seek_point *
176 fast_seek_find(FILE_T file, gint64 pos)
177 {
178         struct fast_seek_point *smallest = NULL;
179         struct fast_seek_point *item;
180         guint low, i, max;
181
182         if (!file->fast_seek)
183                 return NULL;
184
185         for (low = 0, max = file->fast_seek->len; low < max; ) {
186                 i = (low + max) / 2;
187                 item = file->fast_seek->pdata[i];
188
189                 if (pos < item->out)
190                         max = i;
191                 else if (pos > item->out) {
192                         smallest = item;
193                         low = i + 1;
194                 } else {
195                         return item;
196                 }
197         }
198         return smallest;
199 }
200
201 static void
202 fast_seek_header(FILE_T file, gint64 in_pos, gint64 out_pos, int compression)
203 {
204         struct fast_seek_point *item = NULL;
205
206         if (file->fast_seek->len != 0)
207                 item = file->fast_seek->pdata[file->fast_seek->len - 1];
208
209         if (!item || item->out < out_pos) {
210                 struct fast_seek_point *val = g_malloc(sizeof(struct fast_seek_point));
211                 val->in = in_pos;
212                 val->out = out_pos;
213                 val->compression = compression;
214
215                 g_ptr_array_add(file->fast_seek, val);
216         }
217 }
218
219 static void
220 fast_seek_reset(FILE_T state)
221 {
222 #ifdef HAVE_LIBZ
223         if (state->compression == ZLIB && state->fast_seek_cur) {
224                 struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
225
226                 cur->have = 0;
227         }
228 #endif
229 }
230
231 #ifdef HAVE_LIBZ
232
233 /* Get next byte from input, or -1 if end or error. */
234 #define NEXT() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \
235                 (state->avail_in == 0 ? -1 : \
236                  (state->avail_in--, *(state->next_in)++)))
237
238 /* Get a four-byte little-endian integer and return 0 on success and the value
239    in *ret.  Otherwise -1 is returned and *ret is not modified. */
240 static int
241 gz_next4(FILE_T state, guint32 *ret)
242 {
243         guint32 val;
244         int ch;
245
246         val = NEXT();
247         val += (unsigned)NEXT() << 8;
248         val += (guint32)NEXT() << 16;
249         ch = NEXT();
250         if (ch == -1)
251                 return -1;
252         val += (guint32)ch << 24;
253         *ret = val;
254         return 0;
255 }
256
257 static void
258 zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gint64 in_pos, gint64 out_pos)
259 {
260         /* it's for sure after gzip header, so file->fast_seek->len != 0 */
261         struct fast_seek_point *item = file->fast_seek->pdata[file->fast_seek->len - 1];;
262
263         /* Glib has got Balanced Binary Trees (GTree) but I couldn't find a way to do quick search for nearest (and smaller) value to seek (It's what fast_seek_find() do)
264          *      Inserting value in middle of sorted array is expensive, so we want to add only in the end.
265          *      It's not big deal, cause first-read don't usually invoke seeking
266          */
267         if (item->out + SPAN < out_pos) {
268                 struct fast_seek_point *val = g_malloc(sizeof(struct fast_seek_point));
269                 val->in = in_pos;
270                 val->out = out_pos;
271                 val->compression = ZLIB;
272
273                 val->data.zlib.bits = bits;
274                 if (point->pos != 0) {
275                         unsigned int left = ZLIB_WINSIZE - point->pos;
276
277                         memcpy(val->data.zlib.window, point->window + point->pos, left);
278                         memcpy(val->data.zlib.window + left, point->window, point->pos);
279                 } else
280                         memcpy(val->data.zlib.window, point->window, ZLIB_WINSIZE);
281
282                 val->data.zlib.adler = file->strm.adler;
283                 val->data.zlib.total_out = file->strm.total_out;
284                 g_ptr_array_add(file->fast_seek, val);
285         }
286 }
287
288 static void /* gz_decomp */
289 zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
290 {
291         int ret = 0;    /* XXX */
292         guint32 crc, len;
293         z_streamp strm = &(state->strm);
294
295         unsigned char *buf2 = buf;
296         unsigned int count2 = count;
297
298         strm->avail_out = count;
299         strm->next_out = buf;
300
301         /* fill output buffer up to end of deflate stream */
302         do {
303                 /* get more input for inflate() */
304                 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
305                         break;
306                 if (state->avail_in == 0) {
307                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
308                         break;
309                 }
310
311                 strm->avail_in = state->avail_in;
312                 strm->next_in = state->next_in;
313                 /* decompress and handle errors */
314                 /* ret = inflate(strm, Z_NO_FLUSH); */
315                 ret = inflate(strm, Z_BLOCK);
316                 state->avail_in = strm->avail_in;
317                 state->next_in = strm->next_in;
318                 if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
319                         state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
320                         break;
321                 }
322                 if (ret == Z_MEM_ERROR) {
323                         state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */
324                         break;
325                 }
326                 if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
327                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
328                         break;
329                 }
330
331                 strm->adler = crc32(strm->adler, buf2, count2 - strm->avail_out);
332                 if (state->fast_seek_cur) {
333                         struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
334                         unsigned int ready = count2 - strm->avail_out;
335
336                         if (ready < ZLIB_WINSIZE) {
337                                 unsigned left = ZLIB_WINSIZE - cur->pos;
338
339                                 if (ready >= left) {
340                                         memcpy(cur->window + cur->pos, buf2, left);
341                                         if (ready != left)
342                                                 memcpy(cur->window, buf2 + left, ready - left);
343
344                                         cur->pos = ready - left;
345                                         cur->have += ready;
346                                 } else {
347                                         memcpy(cur->window + cur->pos, buf2, ready);
348                                         cur->pos += ready;
349                                         cur->have += ready;
350                                 }
351
352                                 if (cur->have >= ZLIB_WINSIZE)
353                                         cur->have = ZLIB_WINSIZE;
354
355                         } else {
356                                 memcpy(cur->window, buf2 + (ready - ZLIB_WINSIZE), ZLIB_WINSIZE);
357                                 cur->pos = 0;
358                                 cur->have = ZLIB_WINSIZE;
359                         }
360
361                         if (cur->have >= ZLIB_WINSIZE && ret != Z_STREAM_END && (strm->data_type & 128) && !(strm->data_type & 64))
362                                 zlib_fast_seek_add(state, cur, (strm->data_type & 7), state->raw_pos - strm->avail_in, state->pos + (count - strm->avail_out));
363                 }
364                 buf2 = (buf2 + count2 - strm->avail_out);
365                 count2 = strm->avail_out;
366
367         } while (strm->avail_out && ret != Z_STREAM_END);
368
369         /* update available output and crc check value */
370         state->next = buf;
371         state->have = count - strm->avail_out;
372
373         /* check gzip trailer if at end of deflate stream */
374         if (ret == Z_STREAM_END) {
375                 if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1)
376                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
377                 if (crc != strm->adler)
378                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
379                 if (len != (strm->total_out & 0xffffffffL))
380                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
381                 state->compression = UNKNOWN;      /* ready for next stream, once have is 0 */
382                 g_free(state->fast_seek_cur);
383                 state->fast_seek_cur = NULL;
384         }
385 }
386 #endif
387
388 static int
389 gz_head(FILE_T state)
390 {
391         /* get some data in the input buffer */
392         if (state->avail_in == 0) {
393                 if (fill_in_buffer(state) == -1)
394                         return -1;
395                 if (state->avail_in == 0)
396                         return 0;
397         }
398
399         /* look for the gzip magic header bytes 31 and 139 */
400 #ifdef HAVE_LIBZ
401         if (state->next_in[0] == 31) {
402                 state->avail_in--;
403                 state->next_in++;
404                 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
405                         return -1;
406                 if (state->avail_in && state->next_in[0] == 139) {
407                         unsigned len;
408                         int flags;
409
410                         /* we have a gzip header, woo hoo! */
411                         state->avail_in--;
412                         state->next_in++;
413
414                         /* skip rest of header */
415                         if (NEXT() != 8) {      /* compression method */
416                                 state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
417                                 return -1;
418                         }
419                         flags = NEXT();
420                         if (flags & 0xe0) {     /* reserved flag bits */
421                                 state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
422                                 return -1;
423                         }
424                         NEXT();                 /* modification time */
425                         NEXT();
426                         NEXT();
427                         NEXT();
428                         NEXT();                 /* extra flags */
429                         NEXT();                 /* operating system */
430                         if (flags & 4) {        /* extra field */
431                                 len = (unsigned)NEXT();
432                                 len += (unsigned)NEXT() << 8;
433                                 while (len--)
434                                         if (NEXT() < 0)
435                                                 break;
436                         }
437                         if (flags & 8)          /* file name */
438                                 while (NEXT() > 0)
439                                         ;
440                         if (flags & 16)         /* comment */
441                                 while (NEXT() > 0)
442                                         ;
443                         if (flags & 2) {        /* header crc */
444                                 NEXT();
445                                 NEXT();
446                         }
447                         /* an unexpected end of file is not checked for here -- it will be
448                            noticed on the first request for uncompressed data */
449
450                         /* set up for decompression */
451                         inflateReset(&(state->strm));
452                         state->strm.adler = crc32(0L, Z_NULL, 0);
453                         state->compression = ZLIB;
454
455                         if (state->fast_seek) {
456                                 struct zlib_cur_seek_point *cur = g_malloc(sizeof(struct zlib_cur_seek_point));
457
458                                 cur->pos = cur->have = 0;
459                                 g_free(state->fast_seek_cur);
460                                 state->fast_seek_cur = cur;
461                                 fast_seek_header(state, state->raw_pos - state->avail_in, state->pos, GZIP_AFTER_HEADER);
462                         }
463                         return 0;
464                 }
465                 else {
466                         /* not a gzip file -- save first byte (31) and fall to raw i/o */
467                         state->out[0] = 31;
468                         state->have = 1;
469                 }
470         }
471 #endif
472 #ifdef HAVE_LIBXZ
473         /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
474         /* FD 37 7A 58 5A 00 */
475 #endif
476         if (state->fast_seek)
477                 fast_seek_header(state, state->raw_pos - state->avail_in - state->have, state->pos, UNCOMPRESSED);
478
479         /* doing raw i/o, save start of raw data for seeking, copy any leftover
480            input to output -- this assumes that the output buffer is larger than
481            the input buffer, which also assures space for gzungetc() */
482         state->raw = state->pos;
483         state->next = state->out;
484         if (state->avail_in) {
485                 memcpy(state->next + state->have, state->next_in, state->avail_in);
486                 state->have += state->avail_in;
487                 state->avail_in = 0;
488         }
489         state->compression = UNCOMPRESSED;
490         return 0;
491 }
492
493 static int /* gz_make */
494 fill_out_buffer(FILE_T state)
495 {
496         if (state->compression == UNKNOWN) {           /* look for gzip header */
497                 if (gz_head(state) == -1)
498                         return -1;
499                 if (state->have)                /* got some data from gz_head() */
500                         return 0;
501         }
502         if (state->compression == UNCOMPRESSED) {           /* straight copy */
503                 if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1)
504                         return -1;
505                 state->next = state->out;
506         }
507 #ifdef HAVE_LIBZ
508         else if (state->compression == ZLIB) {      /* decompress */
509                 zlib_read(state, state->out, state->size << 1);
510         }
511 #endif
512         return 0;
513 }
514
515 static int
516 gz_skip(FILE_T state, gint64 len)
517 {
518         unsigned n;
519
520         /* skip over len bytes or reach end-of-file, whichever comes first */
521         while (len)
522                 /* skip over whatever is in output buffer */
523                 if (state->have) {
524                         n = (gint64)state->have > len ? (unsigned)len : state->have;
525                         state->have -= n;
526                         state->next += n;
527                         state->pos += n;
528                         len -= n;
529                 }
530
531                 /* delayed error reporting */
532                 else if (state->err)
533                         return -1;
534
535         /* output buffer empty -- return if we're at the end of the input */
536                 else if (state->eof && state->avail_in == 0)
537                         break;
538
539         /* need more data to skip -- load up output buffer */
540                 else {
541                         /* get more output, looking for header if required */
542                         if (fill_out_buffer(state) == -1)
543                                 return -1;
544                 }
545         return 0;
546 }
547
548 static void
549 gz_reset(FILE_T state)
550 {
551         state->have = 0;              /* no output data available */
552         state->eof = 0;               /* not at end of file */
553         state->compression = UNKNOWN; /* look for gzip header */
554
555         state->seek = 0;              /* no seek request pending */
556         state->err = 0;               /* clear error */
557         state->pos = 0;               /* no uncompressed data yet */
558         state->avail_in = 0;          /* no input data yet */
559 }
560
561 FILE_T
562 filed_open(int fd)
563 {
564 #ifdef _STATBUF_ST_BLKSIZE      /* XXX, _STATBUF_ST_BLKSIZE portable? */
565         struct stat st;
566 #endif
567         int want = GZBUFSIZE;
568         FILE_T state;
569
570         if (fd == -1)
571                 return NULL;
572
573         /* allocate FILE_T structure to return */
574         state = g_try_malloc(sizeof *state);
575         if (state == NULL)
576                 return NULL;
577
578         state->fast_seek_cur = NULL;
579         state->fast_seek = NULL;
580
581         /* open the file with the appropriate mode (or just use fd) */
582         state->fd = fd;
583
584         /* save the current position for rewinding (only if reading) */
585         state->start = ws_lseek64(state->fd, 0, SEEK_CUR);
586         if (state->start == -1) state->start = 0;
587         state->raw_pos = state->start;
588
589         /* initialize stream */
590         gz_reset(state);
591
592 #ifdef _STATBUF_ST_BLKSIZE
593         if (fstat(fd, &st) >= 0) {
594                 want = st.st_blksize;
595                 /* XXX, verify result? */
596         }
597 #endif
598
599         /* allocate buffers */
600         state->in = g_try_malloc(want);
601         state->out = g_try_malloc(want << 1);
602         state->size = want;
603         if (state->in == NULL || state->out == NULL) {
604                 g_free(state->out);
605                 g_free(state->in);
606                 g_free(state);
607                 errno = ENOMEM;
608                 return NULL;
609         }
610
611 #ifdef HAVE_LIBZ
612         /* allocate inflate memory */
613         state->strm.zalloc = Z_NULL;
614         state->strm.zfree = Z_NULL;
615         state->strm.opaque = Z_NULL;
616         state->strm.avail_in = 0;
617         state->strm.next_in = Z_NULL;
618         if (inflateInit2(&(state->strm), -15) != Z_OK) {    /* raw inflate */
619                 g_free(state->out);
620                 g_free(state->in);
621                 g_free(state);
622                 errno = ENOMEM;
623                 return NULL;
624         }
625 #endif
626         /* return stream */
627         return state;
628 }
629
630 FILE_T
631 file_open(const char *path)
632 {
633         int fd;
634         FILE_T ft;
635         int oflag;
636
637         /* O_LARGEFILE? */
638         oflag = O_RDONLY;
639 #ifdef _WIN32
640         oflag |= O_BINARY;
641 #endif
642
643         /* open file and do correct filename conversions */
644         if ((fd = ws_open(path, oflag, 0666)) == -1)
645                 return NULL;
646
647         /* open file handle */
648         ft = filed_open(fd);
649         if (ft == NULL) {
650                 ws_close(fd);
651                 return NULL;
652         }
653
654         return ft;
655 }
656
657 void 
658 file_set_random_access(FILE_T stream, gboolean random _U_, GPtrArray *seek)
659 {
660         stream->fast_seek = seek;
661 }
662
663 gint64
664 file_seek(FILE_T file, gint64 offset, int whence, int *err)
665 {
666         struct fast_seek_point *here;
667         unsigned n;
668
669         /* can only seek from start or relative to current position */
670         if (whence != SEEK_SET && whence != SEEK_CUR) {
671                 g_assert_not_reached();
672 /*
673                 *err = EINVAL;
674                 return -1;
675  */
676         }
677
678         /* normalize offset to a SEEK_CUR specification */
679         if (whence == SEEK_SET)
680                 offset -= file->pos;
681         else if (file->seek)
682                 offset += file->skip;
683         file->seek = 0;
684
685         /* XXX, profile */
686         if ((here = fast_seek_find(file, file->pos + offset)) && (offset < 0 || offset > SPAN || here->compression == UNCOMPRESSED)) {
687                 gint64 off, off2;
688
689 #ifdef HAVE_LIBZ
690                 if (here->compression == ZLIB) {
691                         off = here->in - (here->data.zlib.bits ? 1 : 0);
692                         off2 = here->out;
693                 } else if (here->compression == GZIP_AFTER_HEADER) {
694                         off = here->in;
695                         off2 = here->out;
696                 } else
697 #endif
698                 {
699                         off2 = (file->pos + offset);
700                         off = here->in + (off2 - here->out);
701                 }
702
703                 if (ws_lseek64(file->fd, off, SEEK_SET) == -1) {
704                         *err = errno;
705                         return -1;
706                 }
707                 fast_seek_reset(file);
708
709                 file->raw_pos = off;
710                 file->have = 0;
711                 file->eof = 0;
712                 file->seek = 0;
713                 file->err = 0;
714                 file->avail_in = 0;
715
716 #ifdef HAVE_LIBZ
717                 if (here->compression == ZLIB) {
718                         z_stream *strm = &file->strm;
719                         FILE_T state = file;
720
721                         inflateReset(strm);
722                         strm->adler = here->data.zlib.adler;
723                         strm->total_out = here->data.zlib.total_out;
724                         if (here->data.zlib.bits) {
725                                 int ret = NEXT();
726
727                                 if (ret == -1) {
728                                         /* *err = ???; */
729                                         return -1;
730                                 }
731
732                                 (void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
733                         }
734                         (void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE);
735                         file->compression = ZLIB;
736                 } else if (here->compression == GZIP_AFTER_HEADER) {
737                         z_stream *strm = &file->strm;
738
739                         inflateReset(strm);
740                         strm->adler = crc32(0L, Z_NULL, 0);
741                         file->compression = ZLIB;
742                 } else
743 #endif
744                         file->compression = here->compression;
745
746                 offset = (file->pos + offset) - off2;
747                 file->pos = off2;
748                 /* g_print("OK! %ld\n", offset); */
749
750                 if (offset) {
751                         file->seek = 1;
752                         file->skip = offset;
753                 }
754                 return file->pos + offset;
755         }
756
757         /* if within raw area while reading, just go there */
758         if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw) {
759                 if (ws_lseek64(file->fd, offset - file->have, SEEK_CUR) == -1) {
760                         *err = errno;
761                         return -1;
762                 }
763                 file->raw_pos += (offset - file->have);
764                 file->have = 0;
765                 file->eof = 0;
766                 file->seek = 0;
767                 file->err = 0;
768                 file->avail_in = 0;
769                 file->pos += offset;
770                 return file->pos;
771         }
772
773         /* calculate skip amount, rewinding if needed for back seek when reading */
774         if (offset < 0) {
775                 offset += file->pos;
776                 if (offset < 0) {                    /* before start of file! */
777                         /* *err = ???; */
778                         return -1;
779                 }
780                 /* rewind, then skip to offset */
781
782                 /* back up and start over */
783                 if (ws_lseek64(file->fd, file->start, SEEK_SET) == -1) {
784                         *err = errno;
785                         return -1;
786                 }
787                 fast_seek_reset(file);
788                 file->raw_pos = file->start;
789                 gz_reset(file);
790         }
791
792         /* skip what's in output buffer (one less gzgetc() check) */
793         n = (gint64)file->have > offset ? (unsigned)offset : file->have;
794         file->have -= n;
795         file->next += n;
796         file->pos += n;
797         offset -= n;
798
799         /* request skip (if not zero) */
800         if (offset) {
801                 file->seek = 1;
802                 file->skip = offset;
803         }
804         return file->pos + offset;
805 }
806
807 gint64
808 file_tell(FILE_T stream)
809 {
810         /* return position */
811         return stream->pos + (stream->seek ? stream->skip : 0);
812 }
813
814 int 
815 file_read(void *buf, unsigned int len, FILE_T file)
816 {
817         unsigned got, n;
818
819         /* if len is zero, avoid unnecessary operations */
820         if (len == 0)
821                 return 0;
822
823         /* process a skip request */
824         if (file->seek) {
825                 file->seek = 0;
826                 if (gz_skip(file, file->skip) == -1)
827                         return -1;
828         }
829
830         /* get len bytes to buf, or less than len if at the end */
831         got = 0;
832         do {
833                 /* first just try copying data from the output buffer */
834                 if (file->have) {
835                         n = file->have > len ? len : file->have;
836                         memcpy(buf, file->next, n);
837                         file->next += n;
838                         file->have -= n;
839                 }
840                 /* delayed error reporting (for zlib) */
841                 else if (file->err)
842                         return -1;
843
844                 /* output buffer empty -- return if we're at the end of the input */
845                 else if (file->eof && file->avail_in == 0)
846                         break;
847
848                 /* need output data */
849                 else {
850                         /* get more output, looking for header if required */
851                         if (fill_out_buffer(file) == -1)
852                                 return -1;
853                         continue;       /* no progress yet -- go back to memcpy() above */
854                 }
855                 /* update progress */
856                 len -= n;
857                 buf = (char *)buf + n;
858                 got += n;
859                 file->pos += n;
860         } while (len);
861
862         return (int)got;
863 }
864
865 int
866 file_getc(FILE_T file)
867 {
868         unsigned char buf[1];
869         int ret;
870
871         /* check that we're reading and that there's no error */
872         if (file->err)
873                 return -1;
874
875         /* try output buffer (no need to check for skip request) */
876         if (file->have) {
877                 file->have--;
878                 file->pos++;
879                 return *(file->next)++;
880         }
881
882         ret = file_read(buf, 1, file);
883         return ret < 1 ? -1 : buf[0];
884 }
885
886 char *
887 file_gets(char *buf, int len, FILE_T file)
888 {
889         unsigned left, n;
890         char *str;
891         unsigned char *eol;
892
893         /* check parameters */
894         if (buf == NULL || len < 1)
895                 return NULL;
896
897         /* check that there's no error */
898         if (file->err)
899                 return NULL;
900
901         /* process a skip request */
902         if (file->seek) {
903                 file->seek = 0;
904                 if (gz_skip(file, file->skip) == -1)
905                         return NULL;
906         }
907
908         /* copy output bytes up to new line or len - 1, whichever comes first --
909            append a terminating zero to the string (we don't check for a zero in
910            the contents, let the user worry about that) */
911         str = buf;
912         left = (unsigned)len - 1;
913         if (left) do {
914                 /* assure that something is in the output buffer */
915                 if (file->have == 0) {
916                         if (fill_out_buffer(file) == -1)
917                                 return NULL;            /* error */
918                         if (file->have == 0)  {     /* end of file */
919                                 if (buf == str)         /* got bupkus */
920                                         return NULL;
921                                 break;                  /* got something -- return it */
922                         }
923                 }
924
925                 /* look for end-of-line in current output buffer */
926                 n = file->have > left ? left : file->have;
927                 eol = memchr(file->next, '\n', n);
928                 if (eol != NULL)
929                         n = (unsigned)(eol - file->next) + 1;
930
931                 /* copy through end-of-line, or remainder if not found */
932                 memcpy(buf, file->next, n);
933                 file->have -= n;
934                 file->next += n;
935                 file->pos += n;
936                 left -= n;
937                 buf += n;
938         } while (left && eol == NULL);
939
940         /* found end-of-line or out of space -- terminate string and return it */
941         buf[0] = 0;
942         return str;
943 }
944
945 int 
946 file_eof(FILE_T file)
947 {
948         /* return end-of-file state */
949         return (file->eof && file->avail_in == 0 && file->have == 0);
950 }
951
952 /*
953  * Routine to return a Wiretap error code (0 for no error, an errno
954  * for a file error, or a WTAP_ERR_ code for other errors) for an
955  * I/O stream.
956  */
957 int
958 file_error(FILE_T fh)
959 {
960         return fh->err;
961 }
962
963 void
964 file_clearerr(FILE_T stream)
965 {
966         /* clear error and end-of-file */
967         stream->err = 0;
968         stream->eof = 0;
969 }
970
971 int 
972 file_close(FILE_T file)
973 {
974         int fd = file->fd;
975
976         /* free memory and close file */
977         if (file->size) {
978 #ifdef HAVE_LIBZ
979                 inflateEnd(&(file->strm));
980 #endif
981                 g_free(file->out);
982                 g_free(file->in);
983         }
984         g_free(file->fast_seek_cur);
985         file->err = 0;
986         g_free(file);
987         return close(fd);
988 }
989
990 #ifdef HAVE_LIBZ
991 /* internal gzip file state data structure for writing */
992 struct wtap_writer {
993     int fd;                 /* file descriptor */
994     gint64 pos;             /* current position in uncompressed data */
995     unsigned size;          /* buffer size, zero if not allocated yet */
996     unsigned want;          /* requested buffer size, default is GZBUFSIZE */
997     unsigned char *in;      /* input buffer */
998     unsigned char *out;     /* output buffer (double-sized when reading) */
999     unsigned char *next;    /* next output data to deliver or write */
1000     int level;              /* compression level */
1001     int strategy;           /* compression strategy */
1002     int err;                /* error code */
1003         /* zlib deflate stream */
1004     z_stream strm;          /* stream structure in-place (not a pointer) */
1005 };
1006
1007 GZWFILE_T
1008 gzwfile_open(const char *path)
1009 {
1010     int fd;
1011     GZWFILE_T state;
1012     int save_errno;
1013
1014     fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
1015     if (fd == -1)
1016         return NULL;
1017     state = gzwfile_fdopen(fd);
1018     if (state == NULL) {
1019         save_errno = errno;
1020         close(fd);
1021         save_errno = errno;
1022     }
1023     return state;
1024 }
1025
1026 GZWFILE_T
1027 gzwfile_fdopen(int fd)
1028 {
1029     GZWFILE_T state;
1030
1031     /* allocate wtap_writer structure to return */
1032     state = g_try_malloc(sizeof *state);
1033     if (state == NULL)
1034         return NULL;
1035     state->fd = fd;
1036     state->size = 0;            /* no buffers allocated yet */
1037     state->want = GZBUFSIZE;    /* requested buffer size */
1038
1039     state->level = Z_DEFAULT_COMPRESSION;
1040     state->strategy = Z_DEFAULT_STRATEGY;
1041
1042     /* initialize stream */
1043     state->err = Z_OK;              /* clear error */
1044     state->pos = 0;                 /* no uncompressed data yet */
1045     state->strm.avail_in = 0;       /* no input data yet */
1046
1047     /* return stream */
1048     return state;
1049 }
1050
1051 /* Initialize state for writing a gzip file.  Mark initialization by setting
1052    state->size to non-zero.  Return -1, and set state->err, on failure;
1053    return 0 on success. */
1054 static int
1055 gz_init(GZWFILE_T state)
1056 {
1057     int ret;
1058     z_streamp strm = &(state->strm);
1059
1060     /* allocate input and output buffers */
1061     state->in = g_try_malloc(state->want);
1062     state->out = g_try_malloc(state->want);
1063     if (state->in == NULL || state->out == NULL) {
1064         if (state->out != NULL)
1065             g_free(state->out);
1066         if (state->in != NULL)
1067             g_free(state->in);
1068         state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR;       /* ENOMEM? */
1069         return -1;
1070     }
1071
1072     /* allocate deflate memory, set up for gzip compression */
1073     strm->zalloc = Z_NULL;
1074     strm->zfree = Z_NULL;
1075     strm->opaque = Z_NULL;
1076     ret = deflateInit2(strm, state->level, Z_DEFLATED,
1077                        15 + 16, 8, state->strategy);
1078     if (ret != Z_OK) {
1079         g_free(state->in);
1080         state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR;       /* ENOMEM? */
1081         return -1;
1082     }
1083
1084     /* mark state as initialized */
1085     state->size = state->want;
1086
1087     /* initialize write buffer */
1088     strm->avail_out = state->size;
1089     strm->next_out = state->out;
1090     state->next = strm->next_out;
1091     return 0;
1092 }
1093
1094 /* Compress whatever is at avail_in and next_in and write to the output file.
1095    Return -1, and set state->err, if there is an error writing to the output
1096    file; return 0 on success.
1097    flush is assumed to be a valid deflate() flush value.  If flush is Z_FINISH,
1098    then the deflate() state is reset to start a new gzip stream. */
1099 static int
1100 gz_comp(GZWFILE_T state, int flush)
1101 {
1102     int ret, got;
1103     unsigned have;
1104     z_streamp strm = &(state->strm);
1105
1106     /* allocate memory if this is the first time through */
1107     if (state->size == 0 && gz_init(state) == -1)
1108         return -1;
1109
1110     /* run deflate() on provided input until it produces no more output */
1111     ret = Z_OK;
1112     do {
1113         /* write out current buffer contents if full, or if flushing, but if
1114            doing Z_FINISH then don't write until we get to Z_STREAM_END */
1115         if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
1116             (flush != Z_FINISH || ret == Z_STREAM_END))) {
1117             have = (unsigned)(strm->next_out - state->next);
1118             if (have) {
1119                 got = write(state->fd, state->next, have);
1120                 if (got < 0) {
1121                     state->err = errno;
1122                     return -1;
1123                 }
1124                 if ((unsigned)got != have) {
1125                     state->err = WTAP_ERR_SHORT_WRITE;
1126                     return -1;
1127                 }
1128             }
1129             if (strm->avail_out == 0) {
1130                 strm->avail_out = state->size;
1131                 strm->next_out = state->out;
1132             }
1133             state->next = strm->next_out;
1134         }
1135
1136         /* compress */
1137         have = strm->avail_out;
1138         ret = deflate(strm, flush);
1139         if (ret == Z_STREAM_ERROR) {
1140             state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
1141             return -1;
1142         }
1143         have -= strm->avail_out;
1144     } while (have);
1145
1146     /* if that completed a deflate stream, allow another to start */
1147     if (flush == Z_FINISH)
1148         deflateReset(strm);
1149
1150     /* all done, no errors */
1151     return 0;
1152 }
1153
1154 /* Write out len bytes from buf.  Return 0, and set state->err, on
1155    failure or on an attempt to write 0 bytes (in which case state->err
1156    is Z_OK); return the number of bytes written on success. */
1157 unsigned
1158 gzwfile_write(GZWFILE_T state, const void *buf, unsigned len)
1159 {
1160     unsigned put = len;
1161     unsigned n;
1162     z_streamp strm;
1163
1164     strm = &(state->strm);
1165
1166     /* check that there's no error */
1167     if (state->err != Z_OK)
1168         return 0;
1169
1170     /* if len is zero, avoid unnecessary operations */
1171     if (len == 0)
1172         return 0;
1173
1174     /* allocate memory if this is the first time through */
1175     if (state->size == 0 && gz_init(state) == -1)
1176         return 0;
1177
1178     /* for small len, copy to input buffer, otherwise compress directly */
1179     if (len < state->size) {
1180         /* copy to input buffer, compress when full */
1181         do {
1182             if (strm->avail_in == 0)
1183                 strm->next_in = state->in;
1184             n = state->size - strm->avail_in;
1185             if (n > len)
1186                 n = len;
1187             memcpy(strm->next_in + strm->avail_in, buf, n);
1188             strm->avail_in += n;
1189             state->pos += n;
1190             buf = (char *)buf + n;
1191             len -= n;
1192             if (len && gz_comp(state, Z_NO_FLUSH) == -1)
1193                 return 0;
1194         } while (len);
1195     }
1196     else {
1197         /* consume whatever's left in the input buffer */
1198         if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
1199             return 0;
1200
1201         /* directly compress user buffer to file */
1202         strm->avail_in = len;
1203         strm->next_in = (voidp)buf;
1204         state->pos += len;
1205         if (gz_comp(state, Z_NO_FLUSH) == -1)
1206             return 0;
1207     }
1208
1209     /* input was all buffered or compressed (put will fit in int) */
1210     return (int)put;
1211 }
1212
1213 /* Flush out what we've written so far.  Returns -1, and sets state->err,
1214    on failure; returns 0 on success. */
1215 int
1216 gzwfile_flush(GZWFILE_T state)
1217 {
1218     /* check that there's no error */
1219     if (state->err != Z_OK)
1220         return -1;
1221
1222     /* compress remaining data with Z_SYNC_FLUSH */
1223     gz_comp(state, Z_SYNC_FLUSH);
1224     if (state->err != Z_OK)
1225         return -1;
1226     return 0;
1227 }
1228
1229 /* Flush out all data written, and close the file.  Returns a Wiretap
1230    error on failure; returns 0 on success. */
1231 int
1232 gzwfile_close(GZWFILE_T state)
1233 {
1234     int ret = 0;
1235
1236     /* flush, free memory, and close file */
1237     if (gz_comp(state, Z_FINISH) == -1 && ret == 0)
1238         ret = state->err;
1239     (void)deflateEnd(&(state->strm));
1240     g_free(state->out);
1241     g_free(state->in);
1242     state->err = Z_OK;
1243     if (close(state->fd) == -1 && ret == 0)
1244         ret = errno;
1245     g_free(state);
1246     return ret;
1247 }
1248
1249 int
1250 gzwfile_geterr(GZWFILE_T state)
1251 {
1252     return state->err;
1253 }
1254 #endif