From Jakub Zawadski: some small fixes.
[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 _U_)
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 or error */
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            We don't fail immediately here, we just set an error
375            indication, so that we try to process what data we
376            got before the error.  The next attempt to read
377            something past that data will get the error. */
378         if (ret == Z_STREAM_END) {
379                 if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1)
380                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
381                 else if (crc != strm->adler)
382                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
383                 else if (len != (strm->total_out & 0xffffffffL))
384                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
385                 state->compression = UNKNOWN;      /* ready for next stream, once have is 0 */
386                 g_free(state->fast_seek_cur);
387                 state->fast_seek_cur = NULL;
388         }
389 }
390 #endif
391
392 static int
393 gz_head(FILE_T state)
394 {
395         /* get some data in the input buffer */
396         if (state->avail_in == 0) {
397                 if (fill_in_buffer(state) == -1)
398                         return -1;
399                 if (state->avail_in == 0)
400                         return 0;
401         }
402
403         /* look for the gzip magic header bytes 31 and 139 */
404 #ifdef HAVE_LIBZ
405         if (state->next_in[0] == 31) {
406                 state->avail_in--;
407                 state->next_in++;
408                 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
409                         return -1;
410                 if (state->avail_in && state->next_in[0] == 139) {
411                         unsigned len;
412                         int flags;
413
414                         /* we have a gzip header, woo hoo! */
415                         state->avail_in--;
416                         state->next_in++;
417
418                         /* skip rest of header */
419                         if (NEXT() != 8) {      /* compression method */
420                                 state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
421                                 return -1;
422                         }
423                         flags = NEXT();
424                         if (flags & 0xe0) {     /* reserved flag bits */
425                                 state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
426                                 return -1;
427                         }
428                         NEXT();                 /* modification time */
429                         NEXT();
430                         NEXT();
431                         NEXT();
432                         NEXT();                 /* extra flags */
433                         NEXT();                 /* operating system */
434                         if (flags & 4) {        /* extra field */
435                                 len = (unsigned)NEXT();
436                                 len += (unsigned)NEXT() << 8;
437                                 while (len--)
438                                         if (NEXT() < 0)
439                                                 break;
440                         }
441                         if (flags & 8)          /* file name */
442                                 while (NEXT() > 0)
443                                         ;
444                         if (flags & 16)         /* comment */
445                                 while (NEXT() > 0)
446                                         ;
447                         if (flags & 2) {        /* header crc */
448                                 NEXT();
449                                 NEXT();
450                         }
451                         /* an unexpected end of file is not checked for here -- it will be
452                            noticed on the first request for uncompressed data */
453
454                         /* set up for decompression */
455                         inflateReset(&(state->strm));
456                         state->strm.adler = crc32(0L, Z_NULL, 0);
457                         state->compression = ZLIB;
458
459                         if (state->fast_seek) {
460                                 struct zlib_cur_seek_point *cur = g_malloc(sizeof(struct zlib_cur_seek_point));
461
462                                 cur->pos = cur->have = 0;
463                                 g_free(state->fast_seek_cur);
464                                 state->fast_seek_cur = cur;
465                                 fast_seek_header(state, state->raw_pos - state->avail_in, state->pos, GZIP_AFTER_HEADER);
466                         }
467                         return 0;
468                 }
469                 else {
470                         /* not a gzip file -- save first byte (31) and fall to raw i/o */
471                         state->out[0] = 31;
472                         state->have = 1;
473                 }
474         }
475 #endif
476 #ifdef HAVE_LIBXZ
477         /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
478         /* FD 37 7A 58 5A 00 */
479 #endif
480         if (state->fast_seek)
481                 fast_seek_header(state, state->raw_pos - state->avail_in - state->have, state->pos, UNCOMPRESSED);
482
483         /* doing raw i/o, save start of raw data for seeking, copy any leftover
484            input to output -- this assumes that the output buffer is larger than
485            the input buffer, which also assures space for gzungetc() */
486         state->raw = state->pos;
487         state->next = state->out;
488         if (state->avail_in) {
489                 memcpy(state->next + state->have, state->next_in, state->avail_in);
490                 state->have += state->avail_in;
491                 state->avail_in = 0;
492         }
493         state->compression = UNCOMPRESSED;
494         return 0;
495 }
496
497 static int /* gz_make */
498 fill_out_buffer(FILE_T state)
499 {
500         if (state->compression == UNKNOWN) {           /* look for gzip header */
501                 if (gz_head(state) == -1)
502                         return -1;
503                 if (state->have)                /* got some data from gz_head() */
504                         return 0;
505         }
506         if (state->compression == UNCOMPRESSED) {           /* straight copy */
507                 if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1)
508                         return -1;
509                 state->next = state->out;
510         }
511 #ifdef HAVE_LIBZ
512         else if (state->compression == ZLIB) {      /* decompress */
513                 zlib_read(state, state->out, state->size << 1);
514         }
515 #endif
516         return 0;
517 }
518
519 static int
520 gz_skip(FILE_T state, gint64 len)
521 {
522         unsigned n;
523
524         /* skip over len bytes or reach end-of-file, whichever comes first */
525         while (len)
526                 if (state->have) {
527                         /* We have stuff in the output buffer; skip over
528                            it. */
529                         n = (gint64)state->have > len ? (unsigned)len : state->have;
530                         state->have -= n;
531                         state->next += n;
532                         state->pos += n;
533                         len -= n;
534                 } else if (state->err) {
535                         /* We have nothing in the output buffer, and
536                            we have an error that may not have been
537                            reported yet; that means we can't generate
538                            any more data into the output buffer, so
539                            return an error indication. */
540                         return -1;
541                 } else if (state->eof && state->avail_in == 0) {
542                         /* We have nothing in the output buffer, and
543                            we're at the end of the input; just return. */
544                         break;
545                 } else {
546                         /* We have nothing in the output buffer, and
547                            we can generate more data; get more output,
548                            looking for header if required. */
549                         if (fill_out_buffer(state) == -1)
550                                 return -1;
551                 }
552         return 0;
553 }
554
555 static void
556 gz_reset(FILE_T state)
557 {
558         state->have = 0;              /* no output data available */
559         state->eof = 0;               /* not at end of file */
560         state->compression = UNKNOWN; /* look for gzip header */
561
562         state->seek = 0;              /* no seek request pending */
563         state->err = 0;               /* clear error */
564         state->pos = 0;               /* no uncompressed data yet */
565         state->avail_in = 0;          /* no input data yet */
566 }
567
568 FILE_T
569 filed_open(int fd)
570 {
571 #ifdef _STATBUF_ST_BLKSIZE      /* XXX, _STATBUF_ST_BLKSIZE portable? */
572         struct stat st;
573 #endif
574         int want = GZBUFSIZE;
575         FILE_T state;
576
577         if (fd == -1)
578                 return NULL;
579
580         /* allocate FILE_T structure to return */
581         state = g_try_malloc(sizeof *state);
582         if (state == NULL)
583                 return NULL;
584
585         state->fast_seek_cur = NULL;
586         state->fast_seek = NULL;
587
588         /* open the file with the appropriate mode (or just use fd) */
589         state->fd = fd;
590
591         /* save the current position for rewinding (only if reading) */
592         state->start = ws_lseek64(state->fd, 0, SEEK_CUR);
593         if (state->start == -1) state->start = 0;
594         state->raw_pos = state->start;
595
596         /* initialize stream */
597         gz_reset(state);
598
599 #ifdef _STATBUF_ST_BLKSIZE
600         if (fstat(fd, &st) >= 0) {
601                 want = st.st_blksize;
602                 /* XXX, verify result? */
603         }
604 #endif
605
606         /* allocate buffers */
607         state->in = g_try_malloc(want);
608         state->out = g_try_malloc(want << 1);
609         state->size = want;
610         if (state->in == NULL || state->out == NULL) {
611                 g_free(state->out);
612                 g_free(state->in);
613                 g_free(state);
614                 errno = ENOMEM;
615                 return NULL;
616         }
617
618 #ifdef HAVE_LIBZ
619         /* allocate inflate memory */
620         state->strm.zalloc = Z_NULL;
621         state->strm.zfree = Z_NULL;
622         state->strm.opaque = Z_NULL;
623         state->strm.avail_in = 0;
624         state->strm.next_in = Z_NULL;
625         if (inflateInit2(&(state->strm), -15) != Z_OK) {    /* raw inflate */
626                 g_free(state->out);
627                 g_free(state->in);
628                 g_free(state);
629                 errno = ENOMEM;
630                 return NULL;
631         }
632 #endif
633         /* return stream */
634         return state;
635 }
636
637 FILE_T
638 file_open(const char *path)
639 {
640         int fd;
641         FILE_T ft;
642
643         /* open file and do correct filename conversions.
644
645            XXX - do we need O_LARGEFILE?  On UN*X, if we need to do
646            something special to get large file support, the configure
647            script should have set us up with the appropriate #defines,
648            so we should be getting a large-file-enabled file descriptor
649            here.  Pre-Large File Summit UN*Xes, and possibly even some
650            post-LFS UN*Xes, might require O_LARGEFILE here, though.
651            If so, we should probably handle that in ws_open(). */
652         if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0666)) == -1)
653                 return NULL;
654
655         /* open file handle */
656         ft = filed_open(fd);
657         if (ft == NULL) {
658                 ws_close(fd);
659                 return NULL;
660         }
661
662         return ft;
663 }
664
665 void 
666 file_set_random_access(FILE_T stream, gboolean random _U_, GPtrArray *seek)
667 {
668         stream->fast_seek = seek;
669 }
670
671 gint64
672 file_seek(FILE_T file, gint64 offset, int whence, int *err)
673 {
674         struct fast_seek_point *here;
675         unsigned n;
676
677         /* can only seek from start or relative to current position */
678         if (whence != SEEK_SET && whence != SEEK_CUR) {
679                 g_assert_not_reached();
680 /*
681                 *err = EINVAL;
682                 return -1;
683  */
684         }
685
686         /* normalize offset to a SEEK_CUR specification */
687         if (whence == SEEK_SET)
688                 offset -= file->pos;
689         else if (file->seek)
690                 offset += file->skip;
691         file->seek = 0;
692
693         /* XXX, profile */
694         if ((here = fast_seek_find(file, file->pos + offset)) && (offset < 0 || offset > SPAN || here->compression == UNCOMPRESSED)) {
695                 gint64 off, off2;
696
697 #ifdef HAVE_LIBZ
698                 if (here->compression == ZLIB) {
699                         off = here->in - (here->data.zlib.bits ? 1 : 0);
700                         off2 = here->out;
701                 } else if (here->compression == GZIP_AFTER_HEADER) {
702                         off = here->in;
703                         off2 = here->out;
704                 } else
705 #endif
706                 {
707                         off2 = (file->pos + offset);
708                         off = here->in + (off2 - here->out);
709                 }
710
711                 if (ws_lseek64(file->fd, off, SEEK_SET) == -1) {
712                         *err = errno;
713                         return -1;
714                 }
715                 fast_seek_reset(file);
716
717                 file->raw_pos = off;
718                 file->have = 0;
719                 file->eof = 0;
720                 file->seek = 0;
721                 file->err = 0;
722                 file->avail_in = 0;
723
724 #ifdef HAVE_LIBZ
725                 if (here->compression == ZLIB) {
726                         z_stream *strm = &file->strm;
727                         FILE_T state = file;
728
729                         inflateReset(strm);
730                         strm->adler = here->data.zlib.adler;
731                         strm->total_out = here->data.zlib.total_out;
732                         if (here->data.zlib.bits) {
733                                 int ret = NEXT();
734
735                                 if (ret == -1) {
736                                         /* *err = ???; */
737                                         return -1;
738                                 }
739
740                                 (void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
741                         }
742                         (void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE);
743                         file->compression = ZLIB;
744                 } else if (here->compression == GZIP_AFTER_HEADER) {
745                         z_stream *strm = &file->strm;
746
747                         inflateReset(strm);
748                         strm->adler = crc32(0L, Z_NULL, 0);
749                         file->compression = ZLIB;
750                 } else
751 #endif
752                         file->compression = here->compression;
753
754                 offset = (file->pos + offset) - off2;
755                 file->pos = off2;
756                 /* g_print("OK! %ld\n", offset); */
757
758                 if (offset) {
759                         file->seek = 1;
760                         file->skip = offset;
761                 }
762                 return file->pos + offset;
763         }
764
765         /* if within raw area while reading, just go there */
766         if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw) {
767                 if (ws_lseek64(file->fd, offset - file->have, SEEK_CUR) == -1) {
768                         *err = errno;
769                         return -1;
770                 }
771                 file->raw_pos += (offset - file->have);
772                 file->have = 0;
773                 file->eof = 0;
774                 file->seek = 0;
775                 file->err = 0;
776                 file->avail_in = 0;
777                 file->pos += offset;
778                 return file->pos;
779         }
780
781         /* calculate skip amount, rewinding if needed for back seek when reading */
782         if (offset < 0) {
783                 offset += file->pos;
784                 if (offset < 0) {                    /* before start of file! */
785                         /* *err = ???; */
786                         return -1;
787                 }
788                 /* rewind, then skip to offset */
789
790                 /* back up and start over */
791                 if (ws_lseek64(file->fd, file->start, SEEK_SET) == -1) {
792                         *err = errno;
793                         return -1;
794                 }
795                 fast_seek_reset(file);
796                 file->raw_pos = file->start;
797                 gz_reset(file);
798         }
799
800         /* skip what's in output buffer (one less gzgetc() check) */
801         n = (gint64)file->have > offset ? (unsigned)offset : file->have;
802         file->have -= n;
803         file->next += n;
804         file->pos += n;
805         offset -= n;
806
807         /* request skip (if not zero) */
808         if (offset) {
809                 file->seek = 1;
810                 file->skip = offset;
811         }
812         return file->pos + offset;
813 }
814
815 gint64
816 file_tell(FILE_T stream)
817 {
818         /* return position */
819         return stream->pos + (stream->seek ? stream->skip : 0);
820 }
821
822 int 
823 file_read(void *buf, unsigned int len, FILE_T file)
824 {
825         unsigned got, n;
826
827         /* if len is zero, avoid unnecessary operations */
828         if (len == 0)
829                 return 0;
830
831         /* process a skip request */
832         if (file->seek) {
833                 file->seek = 0;
834                 if (gz_skip(file, file->skip) == -1)
835                         return -1;
836         }
837
838         /* get len bytes to buf, or less than len if at the end */
839         got = 0;
840         do {
841                 if (file->have) {
842                         /* We have stuff in the output buffer; copy
843                            what we have. */
844                         n = file->have > len ? len : file->have;
845                         memcpy(buf, file->next, n);
846                         file->next += n;
847                         file->have -= n;
848                 } else if (file->err) {
849                         /* We have nothing in the output buffer, and
850                            we have an error that may not have been
851                            reported yet; that means we can't generate
852                            any more data into the output buffer, so
853                            return an error indication. */
854                         return -1;
855                 } else if (file->eof && file->avail_in == 0) {
856                         /* We have nothing in the output buffer, and
857                            we're at the end of the input; just return
858                            with what we've gotten so far. */
859                         break;
860                 } else {
861                         /* We have nothing in the output buffer, and
862                            we can generate more data; get more output,
863                            looking for header if required, and
864                            keep looping to process the new stuff
865                            in the output buffer. */
866                         if (fill_out_buffer(file) == -1)
867                                 return -1;
868                         continue;       /* no progress yet -- go back to memcpy() above */
869                 }
870                 /* update progress */
871                 len -= n;
872                 buf = (char *)buf + n;
873                 got += n;
874                 file->pos += n;
875         } while (len);
876
877         return (int)got;
878 }
879
880 int
881 file_getc(FILE_T file)
882 {
883         unsigned char buf[1];
884         int ret;
885
886         /* check that we're reading and that there's no error */
887         if (file->err)
888                 return -1;
889
890         /* try output buffer (no need to check for skip request) */
891         if (file->have) {
892                 file->have--;
893                 file->pos++;
894                 return *(file->next)++;
895         }
896
897         ret = file_read(buf, 1, file);
898         return ret < 1 ? -1 : buf[0];
899 }
900
901 char *
902 file_gets(char *buf, int len, FILE_T file)
903 {
904         unsigned left, n;
905         char *str;
906         unsigned char *eol;
907
908         /* check parameters */
909         if (buf == NULL || len < 1)
910                 return NULL;
911
912         /* check that there's no error */
913         if (file->err)
914                 return NULL;
915
916         /* process a skip request */
917         if (file->seek) {
918                 file->seek = 0;
919                 if (gz_skip(file, file->skip) == -1)
920                         return NULL;
921         }
922
923         /* copy output bytes up to new line or len - 1, whichever comes first --
924            append a terminating zero to the string (we don't check for a zero in
925            the contents, let the user worry about that) */
926         str = buf;
927         left = (unsigned)len - 1;
928         if (left) do {
929                 /* assure that something is in the output buffer */
930                 if (file->have == 0) {
931                         /* We have nothing in the output buffer. */
932                         if (file->err) {
933                                 /* We have an error that may not have
934                                    been reported yet; that means we
935                                    can't generate any more data into
936                                    the output buffer, so return an
937                                    error indication. */
938                                 return NULL;
939                         }
940                         if (fill_out_buffer(file) == -1)
941                                 return NULL;            /* error */
942                         if (file->have == 0)  {     /* end of file */
943                                 if (buf == str)         /* got bupkus */
944                                         return NULL;
945                                 break;                  /* got something -- return it */
946                         }
947                 }
948
949                 /* look for end-of-line in current output buffer */
950                 n = file->have > left ? left : file->have;
951                 eol = memchr(file->next, '\n', n);
952                 if (eol != NULL)
953                         n = (unsigned)(eol - file->next) + 1;
954
955                 /* copy through end-of-line, or remainder if not found */
956                 memcpy(buf, file->next, n);
957                 file->have -= n;
958                 file->next += n;
959                 file->pos += n;
960                 left -= n;
961                 buf += n;
962         } while (left && eol == NULL);
963
964         /* found end-of-line or out of space -- terminate string and return it */
965         buf[0] = 0;
966         return str;
967 }
968
969 int 
970 file_eof(FILE_T file)
971 {
972         /* return end-of-file state */
973         return (file->eof && file->avail_in == 0 && file->have == 0);
974 }
975
976 /*
977  * Routine to return a Wiretap error code (0 for no error, an errno
978  * for a file error, or a WTAP_ERR_ code for other errors) for an
979  * I/O stream.
980  */
981 int
982 file_error(FILE_T fh)
983 {
984         return fh->err;
985 }
986
987 void
988 file_clearerr(FILE_T stream)
989 {
990         /* clear error and end-of-file */
991         stream->err = 0;
992         stream->eof = 0;
993 }
994
995 int 
996 file_close(FILE_T file)
997 {
998         int fd = file->fd;
999
1000         /* free memory and close file */
1001         if (file->size) {
1002 #ifdef HAVE_LIBZ
1003                 inflateEnd(&(file->strm));
1004 #endif
1005                 g_free(file->out);
1006                 g_free(file->in);
1007         }
1008         g_free(file->fast_seek_cur);
1009         file->err = 0;
1010         g_free(file);
1011         return close(fd);
1012 }
1013
1014 #ifdef HAVE_LIBZ
1015 /* internal gzip file state data structure for writing */
1016 struct wtap_writer {
1017     int fd;                 /* file descriptor */
1018     gint64 pos;             /* current position in uncompressed data */
1019     unsigned size;          /* buffer size, zero if not allocated yet */
1020     unsigned want;          /* requested buffer size, default is GZBUFSIZE */
1021     unsigned char *in;      /* input buffer */
1022     unsigned char *out;     /* output buffer (double-sized when reading) */
1023     unsigned char *next;    /* next output data to deliver or write */
1024     int level;              /* compression level */
1025     int strategy;           /* compression strategy */
1026     int err;                /* error code */
1027         /* zlib deflate stream */
1028     z_stream strm;          /* stream structure in-place (not a pointer) */
1029 };
1030
1031 GZWFILE_T
1032 gzwfile_open(const char *path)
1033 {
1034     int fd;
1035     GZWFILE_T state;
1036     int save_errno;
1037
1038     fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
1039     if (fd == -1)
1040         return NULL;
1041     state = gzwfile_fdopen(fd);
1042     if (state == NULL) {
1043         save_errno = errno;
1044         close(fd);
1045         errno = save_errno;
1046     }
1047     return state;
1048 }
1049
1050 GZWFILE_T
1051 gzwfile_fdopen(int fd)
1052 {
1053     GZWFILE_T state;
1054
1055     /* allocate wtap_writer structure to return */
1056     state = g_try_malloc(sizeof *state);
1057     if (state == NULL)
1058         return NULL;
1059     state->fd = fd;
1060     state->size = 0;            /* no buffers allocated yet */
1061     state->want = GZBUFSIZE;    /* requested buffer size */
1062
1063     state->level = Z_DEFAULT_COMPRESSION;
1064     state->strategy = Z_DEFAULT_STRATEGY;
1065
1066     /* initialize stream */
1067     state->err = Z_OK;              /* clear error */
1068     state->pos = 0;                 /* no uncompressed data yet */
1069     state->strm.avail_in = 0;       /* no input data yet */
1070
1071     /* return stream */
1072     return state;
1073 }
1074
1075 /* Initialize state for writing a gzip file.  Mark initialization by setting
1076    state->size to non-zero.  Return -1, and set state->err, on failure;
1077    return 0 on success. */
1078 static int
1079 gz_init(GZWFILE_T state)
1080 {
1081     int ret;
1082     z_streamp strm = &(state->strm);
1083
1084     /* allocate input and output buffers */
1085     state->in = g_try_malloc(state->want);
1086     state->out = g_try_malloc(state->want);
1087     if (state->in == NULL || state->out == NULL) {
1088         g_free(state->out);
1089         g_free(state->in);
1090         state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR;       /* ENOMEM? */
1091         return -1;
1092     }
1093
1094     /* allocate deflate memory, set up for gzip compression */
1095     strm->zalloc = Z_NULL;
1096     strm->zfree = Z_NULL;
1097     strm->opaque = Z_NULL;
1098     ret = deflateInit2(strm, state->level, Z_DEFLATED,
1099                        15 + 16, 8, state->strategy);
1100     if (ret != Z_OK) {
1101         g_free(state->out);
1102         g_free(state->in);
1103         state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR;       /* ENOMEM? */
1104         return -1;
1105     }
1106
1107     /* mark state as initialized */
1108     state->size = state->want;
1109
1110     /* initialize write buffer */
1111     strm->avail_out = state->size;
1112     strm->next_out = state->out;
1113     state->next = strm->next_out;
1114     return 0;
1115 }
1116
1117 /* Compress whatever is at avail_in and next_in and write to the output file.
1118    Return -1, and set state->err, if there is an error writing to the output
1119    file; return 0 on success.
1120    flush is assumed to be a valid deflate() flush value.  If flush is Z_FINISH,
1121    then the deflate() state is reset to start a new gzip stream. */
1122 static int
1123 gz_comp(GZWFILE_T state, int flush)
1124 {
1125     int ret, got;
1126     unsigned have;
1127     z_streamp strm = &(state->strm);
1128
1129     /* allocate memory if this is the first time through */
1130     if (state->size == 0 && gz_init(state) == -1)
1131         return -1;
1132
1133     /* run deflate() on provided input until it produces no more output */
1134     ret = Z_OK;
1135     do {
1136         /* write out current buffer contents if full, or if flushing, but if
1137            doing Z_FINISH then don't write until we get to Z_STREAM_END */
1138         if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
1139             (flush != Z_FINISH || ret == Z_STREAM_END))) {
1140             have = (unsigned)(strm->next_out - state->next);
1141             if (have) {
1142                 got = write(state->fd, state->next, have);
1143                 if (got < 0) {
1144                     state->err = errno;
1145                     return -1;
1146                 }
1147                 if ((unsigned)got != have) {
1148                     state->err = WTAP_ERR_SHORT_WRITE;
1149                     return -1;
1150                 }
1151             }
1152             if (strm->avail_out == 0) {
1153                 strm->avail_out = state->size;
1154                 strm->next_out = state->out;
1155             }
1156             state->next = strm->next_out;
1157         }
1158
1159         /* compress */
1160         have = strm->avail_out;
1161         ret = deflate(strm, flush);
1162         if (ret == Z_STREAM_ERROR) {
1163             state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
1164             return -1;
1165         }
1166         have -= strm->avail_out;
1167     } while (have);
1168
1169     /* if that completed a deflate stream, allow another to start */
1170     if (flush == Z_FINISH)
1171         deflateReset(strm);
1172
1173     /* all done, no errors */
1174     return 0;
1175 }
1176
1177 /* Write out len bytes from buf.  Return 0, and set state->err, on
1178    failure or on an attempt to write 0 bytes (in which case state->err
1179    is Z_OK); return the number of bytes written on success. */
1180 unsigned
1181 gzwfile_write(GZWFILE_T state, const void *buf, unsigned len)
1182 {
1183     unsigned put = len;
1184     unsigned n;
1185     z_streamp strm;
1186
1187     strm = &(state->strm);
1188
1189     /* check that there's no error */
1190     if (state->err != Z_OK)
1191         return 0;
1192
1193     /* if len is zero, avoid unnecessary operations */
1194     if (len == 0)
1195         return 0;
1196
1197     /* allocate memory if this is the first time through */
1198     if (state->size == 0 && gz_init(state) == -1)
1199         return 0;
1200
1201     /* for small len, copy to input buffer, otherwise compress directly */
1202     if (len < state->size) {
1203         /* copy to input buffer, compress when full */
1204         do {
1205             if (strm->avail_in == 0)
1206                 strm->next_in = state->in;
1207             n = state->size - strm->avail_in;
1208             if (n > len)
1209                 n = len;
1210             memcpy(strm->next_in + strm->avail_in, buf, n);
1211             strm->avail_in += n;
1212             state->pos += n;
1213             buf = (char *)buf + n;
1214             len -= n;
1215             if (len && gz_comp(state, Z_NO_FLUSH) == -1)
1216                 return 0;
1217         } while (len);
1218     }
1219     else {
1220         /* consume whatever's left in the input buffer */
1221         if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
1222             return 0;
1223
1224         /* directly compress user buffer to file */
1225         strm->avail_in = len;
1226         strm->next_in = (voidp)buf;
1227         state->pos += len;
1228         if (gz_comp(state, Z_NO_FLUSH) == -1)
1229             return 0;
1230     }
1231
1232     /* input was all buffered or compressed (put will fit in int) */
1233     return (int)put;
1234 }
1235
1236 /* Flush out what we've written so far.  Returns -1, and sets state->err,
1237    on failure; returns 0 on success. */
1238 int
1239 gzwfile_flush(GZWFILE_T state)
1240 {
1241     /* check that there's no error */
1242     if (state->err != Z_OK)
1243         return -1;
1244
1245     /* compress remaining data with Z_SYNC_FLUSH */
1246     gz_comp(state, Z_SYNC_FLUSH);
1247     if (state->err != Z_OK)
1248         return -1;
1249     return 0;
1250 }
1251
1252 /* Flush out all data written, and close the file.  Returns a Wiretap
1253    error on failure; returns 0 on success. */
1254 int
1255 gzwfile_close(GZWFILE_T state)
1256 {
1257     int ret = 0;
1258
1259     /* flush, free memory, and close file */
1260     if (gz_comp(state, Z_FINISH) == -1 && ret == 0)
1261         ret = state->err;
1262     (void)deflateEnd(&(state->strm));
1263     g_free(state->out);
1264     g_free(state->in);
1265     state->err = Z_OK;
1266     if (close(state->fd) == -1 && ret == 0)
1267         ret = errno;
1268     g_free(state);
1269     return ret;
1270 }
1271
1272 int
1273 gzwfile_geterr(GZWFILE_T state)
1274 {
1275     return state->err;
1276 }
1277 #endif