HACK: 1. try to match RowsetProperties
[wireshark-wip.git] / wiretap / file_wrappers.c
blob3b996f5d3c44d2983eb164291a3cf5f9a51f6425
1 /* file_wrappers.c
3 * $Id$
5 * Wiretap Library
6 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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:
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.
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:
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.
44 #include "config.h"
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif /* HAVE_UNISTD_H */
50 #include <errno.h>
51 #include <stdio.h>
52 #ifdef HAVE_FCNTL_H
53 #include <fcntl.h>
54 #endif /* HAVE_FCNTL_H */
55 #include <string.h>
56 #include "wtap-int.h"
57 #include "file_wrappers.h"
58 #include <wsutil/file_util.h>
60 #ifdef HAVE_LIBZ
61 #include <zlib.h>
62 #endif /* HAVE_LIBZ */
65 * See RFC 1952 for a description of the gzip file format.
67 * Some other compressed file formats we might want to support:
69 * XZ format: http://tukaani.org/xz/
71 * Bzip2 format: http://bzip.org/
75 * List of extensions for compressed files.
76 * If we add support for more compressed file types, this table
77 * might be expanded to include routines to handle the various
78 * compression types.
80 static const char *compressed_file_extensions[] = {
81 #ifdef HAVE_LIBZ
82 "gz",
83 #endif
84 NULL
88 * Return a GSList of all the compressed file extensions.
89 * The data pointers all point to items in compressed_file_extensions[],
90 * so the GSList can just be freed with g_slist_free().
92 GSList *
93 wtap_get_compressed_file_extensions(void)
95 const char **extension;
96 GSList *extensions;
98 extensions = NULL;
99 for (extension = &compressed_file_extensions[0]; *extension != NULL;
100 extension++)
101 extensions = g_slist_append(extensions, (gpointer)(*extension));
102 return extensions;
105 /* #define GZBUFSIZE 8192 */
106 #define GZBUFSIZE 4096
108 /* values for wtap_reader compression */
109 typedef enum {
110 UNKNOWN, /* unknown - look for a gzip header */
111 UNCOMPRESSED, /* uncompressed - copy input directly */
112 #ifdef HAVE_LIBZ
113 ZLIB, /* decompress a zlib stream */
114 GZIP_AFTER_HEADER
115 #endif
116 } compression_t;
118 struct wtap_reader {
119 int fd; /* file descriptor */
120 gint64 raw_pos; /* current position in file (just to not call lseek()) */
121 gint64 pos; /* current position in uncompressed data */
122 guint size; /* buffer size */
123 unsigned char *in; /* input buffer */
124 unsigned char *out; /* output buffer (double-sized when reading) */
125 unsigned char *next; /* next output data to deliver or write */
127 guint have; /* amount of output data unused at next */
128 gboolean eof; /* TRUE if end of input file reached */
129 gint64 start; /* where the gzip data started, for rewinding */
130 gint64 raw; /* where the raw data started, for seeking */
131 compression_t compression; /* type of compression, if any */
132 gboolean is_compressed; /* FALSE if completely uncompressed, TRUE otherwise */
133 /* seek request */
134 gint64 skip; /* amount to skip (already rewound if backwards) */
135 gboolean seek_pending; /* TRUE if seek request pending */
136 /* error information */
137 int err; /* error code */
138 const char *err_info; /* additional error information string for some errors */
140 guint avail_in; /* number of bytes available at next_in */
141 unsigned char *next_in; /* next input byte */
142 #ifdef HAVE_LIBZ
143 /* zlib inflate stream */
144 z_stream strm; /* stream structure in-place (not a pointer) */
145 gboolean dont_check_crc; /* TRUE if we aren't supposed to check the CRC */
146 #endif
147 /* fast seeking */
148 GPtrArray *fast_seek;
149 void *fast_seek_cur;
152 static int /* gz_load */
153 raw_read(FILE_T state, unsigned char *buf, unsigned int count, guint *have)
155 ssize_t ret;
157 *have = 0;
158 do {
159 ret = read(state->fd, buf + *have, count - *have);
160 if (ret <= 0)
161 break;
162 *have += (unsigned)ret;
163 state->raw_pos += ret;
164 } while (*have < count);
165 if (ret < 0) {
166 state->err = errno;
167 state->err_info = NULL;
168 return -1;
170 if (ret == 0)
171 state->eof = TRUE;
172 return 0;
175 static int /* gz_avail */
176 fill_in_buffer(FILE_T state)
178 if (state->err)
179 return -1;
180 if (state->eof == 0) {
181 if (raw_read(state, state->in, state->size, &(state->avail_in)) == -1)
182 return -1;
183 state->next_in = state->in;
185 return 0;
188 #define ZLIB_WINSIZE 32768
190 struct fast_seek_point {
191 gint64 out; /* corresponding offset in uncompressed data */
192 gint64 in; /* offset in input file of first full byte */
194 compression_t compression;
195 union {
196 struct {
197 #ifdef HAVE_INFLATEPRIME
198 int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
199 #endif
200 unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */
202 /* be gentle with Z_STREAM_END, 8 bytes more... Another solution would be to comment checks out */
203 guint32 adler;
204 guint32 total_out;
205 } zlib;
206 } data;
209 struct zlib_cur_seek_point {
210 unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */
211 unsigned int pos;
212 unsigned int have;
215 #define SPAN G_GINT64_CONSTANT(1048576)
216 static struct fast_seek_point *
217 fast_seek_find(FILE_T file, gint64 pos)
219 struct fast_seek_point *smallest = NULL;
220 struct fast_seek_point *item;
221 guint low, i, max;
223 if (!file->fast_seek)
224 return NULL;
226 for (low = 0, max = file->fast_seek->len; low < max; ) {
227 i = (low + max) / 2;
228 item = (struct fast_seek_point *)file->fast_seek->pdata[i];
230 if (pos < item->out)
231 max = i;
232 else if (pos > item->out) {
233 smallest = item;
234 low = i + 1;
235 } else {
236 return item;
239 return smallest;
242 static void
243 fast_seek_header(FILE_T file, gint64 in_pos, gint64 out_pos,
244 compression_t compression)
246 struct fast_seek_point *item = NULL;
248 if (file->fast_seek->len != 0)
249 item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
251 if (!item || item->out < out_pos) {
252 struct fast_seek_point *val = g_new(struct fast_seek_point,1);
253 val->in = in_pos;
254 val->out = out_pos;
255 val->compression = compression;
257 g_ptr_array_add(file->fast_seek, val);
261 static void
262 fast_seek_reset(FILE_T state _U_)
264 #ifdef HAVE_LIBZ
265 if (state->compression == ZLIB && state->fast_seek_cur) {
266 struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
268 cur->have = 0;
270 #endif
273 #ifdef HAVE_LIBZ
275 /* Get next byte from input, or -1 if end or error.
277 * Note:
279 * 1) errors from raw_read(), and thus from fill_in_buffer(), are
280 * "sticky", and fill_in_buffer() won't do any reading if there's
281 * an error;
283 * 2) GZ_GETC() returns -1 on an EOF;
285 * so it's safe to make multiple GZ_GETC() calls and only check the
286 * last one for an error. */
287 #define GZ_GETC() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \
288 (state->avail_in == 0 ? -1 : \
289 (state->avail_in--, *(state->next_in)++)))
291 /* Get a one-byte integer and return 0 on success and the value in *ret.
292 Otherwise -1 is returned, state->err is set, and *ret is not modified. */
293 static int
294 gz_next1(FILE_T state, guint8 *ret)
296 int ch;
298 ch = GZ_GETC();
299 if (ch == -1) {
300 if (state->err == 0) {
301 /* EOF */
302 state->err = WTAP_ERR_SHORT_READ;
303 state->err_info = NULL;
305 return -1;
307 *ret = ch;
308 return 0;
311 /* Get a two-byte little-endian integer and return 0 on success and the value
312 in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
313 modified. */
314 static int
315 gz_next2(FILE_T state, guint16 *ret)
317 guint16 val;
318 int ch;
320 val = GZ_GETC();
321 ch = GZ_GETC();
322 if (ch == -1) {
323 if (state->err == 0) {
324 /* EOF */
325 state->err = WTAP_ERR_SHORT_READ;
326 state->err_info = NULL;
328 return -1;
330 val += (guint16)ch << 8;
331 *ret = val;
332 return 0;
335 /* Get a four-byte little-endian integer and return 0 on success and the value
336 in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
337 modified. */
338 static int
339 gz_next4(FILE_T state, guint32 *ret)
341 guint32 val;
342 int ch;
344 val = GZ_GETC();
345 val += (unsigned)GZ_GETC() << 8;
346 val += (guint32)GZ_GETC() << 16;
347 ch = GZ_GETC();
348 if (ch == -1) {
349 if (state->err == 0) {
350 /* EOF */
351 state->err = WTAP_ERR_SHORT_READ;
352 state->err_info = NULL;
354 return -1;
356 val += (guint32)ch << 24;
357 *ret = val;
358 return 0;
361 /* Skip the specified number of bytes and return 0 on success. Otherwise -1
362 is returned. */
363 static int
364 gz_skipn(FILE_T state, size_t n)
366 while (n != 0) {
367 if (GZ_GETC() == -1) {
368 if (state->err == 0) {
369 /* EOF */
370 state->err = WTAP_ERR_SHORT_READ;
371 state->err_info = NULL;
373 return -1;
375 n--;
377 return 0;
380 /* Skip a null-terminated string and return 0 on success. Otherwise -1
381 is returned. */
382 static int
383 gz_skipzstr(FILE_T state)
385 int ch;
387 /* It's null-terminated, so scan until we read a byte with
388 the value 0 or get an error. */
389 while ((ch = GZ_GETC()) > 0)
391 if (ch == -1) {
392 if (state->err == 0) {
393 /* EOF */
394 state->err = WTAP_ERR_SHORT_READ;
395 state->err_info = NULL;
397 return -1;
399 return 0;
402 static void
403 zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gint64 in_pos, gint64 out_pos)
405 /* it's for sure after gzip header, so file->fast_seek->len != 0 */
406 struct fast_seek_point *item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
408 #ifndef HAVE_INFLATEPRIME
409 if (bits)
410 return;
411 #endif
413 /* 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)
414 * Inserting value in middle of sorted array is expensive, so we want to add only in the end.
415 * It's not big deal, cause first-read don't usually invoke seeking
417 if (item->out + SPAN < out_pos) {
418 struct fast_seek_point *val = g_new(struct fast_seek_point,1);
419 val->in = in_pos;
420 val->out = out_pos;
421 val->compression = ZLIB;
422 #ifdef HAVE_INFLATEPRIME
423 val->data.zlib.bits = bits;
424 #endif
425 if (point->pos != 0) {
426 unsigned int left = ZLIB_WINSIZE - point->pos;
428 memcpy(val->data.zlib.window, point->window + point->pos, left);
429 memcpy(val->data.zlib.window + left, point->window, point->pos);
430 } else
431 memcpy(val->data.zlib.window, point->window, ZLIB_WINSIZE);
434 * XXX - strm.adler is a uLong in at least some versions
435 * of zlib, and uLong is an unsigned long in at least
436 * some of those versions, which means it's 64-bit
437 * on LP64 platforms, even though the checksum is
438 * 32-bit. We assume the actual Adler checksum
439 * is in the lower 32 bits of strm.adler; as the
440 * checksum in the file is only 32 bits, we save only
441 * those lower 32 bits, and cast away any additional
442 * bits to squelch warnings.
444 * The same applies to strm.total_out.
446 val->data.zlib.adler = (guint32) file->strm.adler;
447 val->data.zlib.total_out = (guint32) file->strm.total_out;
448 g_ptr_array_add(file->fast_seek, val);
452 static void /* gz_decomp */
453 zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
455 int ret = 0; /* XXX */
456 guint32 crc, len;
457 z_streamp strm = &(state->strm);
459 unsigned char *buf2 = buf;
460 unsigned int count2 = count;
462 strm->avail_out = count;
463 strm->next_out = buf;
465 /* fill output buffer up to end of deflate stream or error */
466 do {
467 /* get more input for inflate() */
468 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
469 break;
470 if (state->avail_in == 0) {
471 /* EOF */
472 state->err = WTAP_ERR_SHORT_READ;
473 state->err_info = NULL;
474 break;
477 strm->avail_in = state->avail_in;
478 strm->next_in = state->next_in;
479 /* decompress and handle errors */
480 #ifdef Z_BLOCK
481 ret = inflate(strm, Z_BLOCK);
482 #else
483 ret = inflate(strm, Z_NO_FLUSH);
484 #endif
485 state->avail_in = strm->avail_in;
486 state->next_in = strm->next_in;
487 if (ret == Z_STREAM_ERROR) {
488 state->err = WTAP_ERR_DECOMPRESS;
489 state->err_info = strm->msg;
490 break;
492 if (ret == Z_NEED_DICT) {
493 state->err = WTAP_ERR_DECOMPRESS;
494 state->err_info = "preset dictionary needed";
495 break;
497 if (ret == Z_MEM_ERROR) {
498 /* This means "not enough memory". */
499 state->err = ENOMEM;
500 state->err_info = NULL;
501 break;
503 if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
504 state->err = WTAP_ERR_DECOMPRESS;
505 state->err_info = strm->msg;
506 break;
509 * XXX - Z_BUF_ERROR?
512 strm->adler = crc32(strm->adler, buf2, count2 - strm->avail_out);
513 #ifdef Z_BLOCK
514 if (state->fast_seek_cur) {
515 struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
516 unsigned int ready = count2 - strm->avail_out;
518 if (ready < ZLIB_WINSIZE) {
519 guint left = ZLIB_WINSIZE - cur->pos;
521 if (ready >= left) {
522 memcpy(cur->window + cur->pos, buf2, left);
523 if (ready != left)
524 memcpy(cur->window, buf2 + left, ready - left);
526 cur->pos = ready - left;
527 cur->have += ready;
528 } else {
529 memcpy(cur->window + cur->pos, buf2, ready);
530 cur->pos += ready;
531 cur->have += ready;
534 if (cur->have >= ZLIB_WINSIZE)
535 cur->have = ZLIB_WINSIZE;
537 } else {
538 memcpy(cur->window, buf2 + (ready - ZLIB_WINSIZE), ZLIB_WINSIZE);
539 cur->pos = 0;
540 cur->have = ZLIB_WINSIZE;
543 if (cur->have >= ZLIB_WINSIZE && ret != Z_STREAM_END && (strm->data_type & 128) && !(strm->data_type & 64))
544 zlib_fast_seek_add(state, cur, (strm->data_type & 7), state->raw_pos - strm->avail_in, state->pos + (count - strm->avail_out));
546 #endif
547 buf2 = (buf2 + count2 - strm->avail_out);
548 count2 = strm->avail_out;
550 } while (strm->avail_out && ret != Z_STREAM_END);
552 /* update available output and crc check value */
553 state->next = buf;
554 state->have = count - strm->avail_out;
556 /* Check gzip trailer if at end of deflate stream.
557 We don't fail immediately here, we just set an error
558 indication, so that we try to process what data we
559 got before the error. The next attempt to read
560 something past that data will get the error. */
561 if (ret == Z_STREAM_END) {
562 if (gz_next4(state, &crc) != -1 &&
563 gz_next4(state, &len) != -1) {
564 if (crc != strm->adler && !state->dont_check_crc) {
565 state->err = WTAP_ERR_DECOMPRESS;
566 state->err_info = "bad CRC";
567 } else if (len != (strm->total_out & 0xffffffffL)) {
568 state->err = WTAP_ERR_DECOMPRESS;
569 state->err_info = "length field wrong";
572 state->compression = UNKNOWN; /* ready for next stream, once have is 0 */
573 g_free(state->fast_seek_cur);
574 state->fast_seek_cur = NULL;
577 #endif
579 static int
580 gz_head(FILE_T state)
582 /* get some data in the input buffer */
583 if (state->avail_in == 0) {
584 if (fill_in_buffer(state) == -1)
585 return -1;
586 if (state->avail_in == 0)
587 return 0;
590 /* look for the gzip magic header bytes 31 and 139 */
591 #ifdef HAVE_LIBZ
592 if (state->next_in[0] == 31) {
593 state->avail_in--;
594 state->next_in++;
595 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
596 return -1;
597 if (state->avail_in && state->next_in[0] == 139) {
598 guint8 cm;
599 guint8 flags;
600 guint16 len;
601 guint16 hcrc;
603 /* we have a gzip header, woo hoo! */
604 state->avail_in--;
605 state->next_in++;
607 /* read rest of header */
609 /* compression method (CM) */
610 if (gz_next1(state, &cm) == -1)
611 return -1;
612 if (cm != 8) {
613 state->err = WTAP_ERR_DECOMPRESS;
614 state->err_info = "unknown compression method";
615 return -1;
618 /* flags (FLG) */
619 if (gz_next1(state, &flags) == -1)
620 return -1;
621 if (flags & 0xe0) { /* reserved flag bits */
622 state->err = WTAP_ERR_DECOMPRESS;
623 state->err_info = "reserved flag bits set";
624 return -1;
627 /* modification time (MTIME) */
628 if (gz_skipn(state, 4) == -1)
629 return -1;
631 /* extra flags (XFL) */
632 if (gz_skipn(state, 1) == -1)
633 return -1;
635 /* operating system (OS) */
636 if (gz_skipn(state, 1) == -1)
637 return -1;
639 if (flags & 4) {
640 /* extra field - get XLEN */
641 if (gz_next2(state, &len) == -1)
642 return -1;
644 /* skip the extra field */
645 if (gz_skipn(state, len) == -1)
646 return -1;
648 if (flags & 8) {
649 /* file name */
650 if (gz_skipzstr(state) == -1)
651 return -1;
653 if (flags & 16) {
654 /* comment */
655 if (gz_skipzstr(state) == -1)
656 return -1;
658 if (flags & 2) {
659 /* header crc */
660 if (gz_next2(state, &hcrc) == -1)
661 return -1;
662 /* XXX - check the CRC? */
665 /* set up for decompression */
666 inflateReset(&(state->strm));
667 state->strm.adler = crc32(0L, Z_NULL, 0);
668 state->compression = ZLIB;
669 state->is_compressed = TRUE;
670 #ifdef Z_BLOCK
671 if (state->fast_seek) {
672 struct zlib_cur_seek_point *cur = g_new(struct zlib_cur_seek_point,1);
674 cur->pos = cur->have = 0;
675 g_free(state->fast_seek_cur);
676 state->fast_seek_cur = cur;
677 fast_seek_header(state, state->raw_pos - state->avail_in, state->pos, GZIP_AFTER_HEADER);
679 #endif
680 return 0;
682 else {
683 /* not a gzip file -- save first byte (31) and fall to raw i/o */
684 state->out[0] = 31;
685 state->have = 1;
688 #endif
689 #ifdef HAVE_LIBXZ
690 /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
691 /* FD 37 7A 58 5A 00 */
692 #endif
693 if (state->fast_seek)
694 fast_seek_header(state, state->raw_pos - state->avail_in - state->have, state->pos, UNCOMPRESSED);
696 /* doing raw i/o, save start of raw data for seeking, copy any leftover
697 input to output -- this assumes that the output buffer is larger than
698 the input buffer, which also assures space for gzungetc() */
699 state->raw = state->pos;
700 state->next = state->out;
701 if (state->avail_in) {
702 memcpy(state->next + state->have, state->next_in, state->avail_in);
703 state->have += state->avail_in;
704 state->avail_in = 0;
706 state->compression = UNCOMPRESSED;
707 return 0;
710 static int /* gz_make */
711 fill_out_buffer(FILE_T state)
713 if (state->compression == UNKNOWN) { /* look for gzip header */
714 if (gz_head(state) == -1)
715 return -1;
716 if (state->have) /* got some data from gz_head() */
717 return 0;
719 if (state->compression == UNCOMPRESSED) { /* straight copy */
720 if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1)
721 return -1;
722 state->next = state->out;
724 #ifdef HAVE_LIBZ
725 else if (state->compression == ZLIB) { /* decompress */
726 zlib_read(state, state->out, state->size << 1);
728 #endif
729 return 0;
732 static int
733 gz_skip(FILE_T state, gint64 len)
735 guint n;
737 /* skip over len bytes or reach end-of-file, whichever comes first */
738 while (len)
739 if (state->have) {
740 /* We have stuff in the output buffer; skip over
741 it. */
742 n = (gint64)state->have > len ? (unsigned)len : state->have;
743 state->have -= n;
744 state->next += n;
745 state->pos += n;
746 len -= n;
747 } else if (state->err) {
748 /* We have nothing in the output buffer, and
749 we have an error that may not have been
750 reported yet; that means we can't generate
751 any more data into the output buffer, so
752 return an error indication. */
753 return -1;
754 } else if (state->eof && state->avail_in == 0) {
755 /* We have nothing in the output buffer, and
756 we're at the end of the input; just return. */
757 break;
758 } else {
759 /* We have nothing in the output buffer, and
760 we can generate more data; get more output,
761 looking for header if required. */
762 if (fill_out_buffer(state) == -1)
763 return -1;
765 return 0;
768 static void
769 gz_reset(FILE_T state)
771 state->have = 0; /* no output data available */
772 state->eof = FALSE; /* not at end of file */
773 state->compression = UNKNOWN; /* look for gzip header */
775 state->seek_pending = FALSE; /* no seek request pending */
776 state->err = 0; /* clear error */
777 state->err_info = NULL;
778 state->pos = 0; /* no uncompressed data yet */
779 state->avail_in = 0; /* no input data yet */
782 FILE_T
783 file_fdopen(int fd)
785 #ifdef _STATBUF_ST_BLKSIZE /* XXX, _STATBUF_ST_BLKSIZE portable? */
786 struct stat st;
787 #endif
788 int want = GZBUFSIZE;
789 FILE_T state;
791 if (fd == -1)
792 return NULL;
794 /* allocate FILE_T structure to return */
795 state = (FILE_T)g_try_malloc(sizeof *state);
796 if (state == NULL)
797 return NULL;
799 state->fast_seek_cur = NULL;
800 state->fast_seek = NULL;
802 /* open the file with the appropriate mode (or just use fd) */
803 state->fd = fd;
805 /* we don't yet know whether it's compressed */
806 state->is_compressed = FALSE;
808 /* save the current position for rewinding (only if reading) */
809 state->start = ws_lseek64(state->fd, 0, SEEK_CUR);
810 if (state->start == -1) state->start = 0;
811 state->raw_pos = state->start;
813 /* initialize stream */
814 gz_reset(state);
816 #ifdef _STATBUF_ST_BLKSIZE
817 if (fstat(fd, &st) >= 0) {
819 * Yes, st_blksize can be bigger than an int; apparently,
820 * it's a long on LP64 Linux, for example.
822 * If the value is too big to fit into an int, just
823 * use the default.
825 if (st.st_blksize <= G_MAXINT)
826 want = (int)st.st_blksize;
827 /* XXX, verify result? */
829 #endif
831 /* allocate buffers */
832 state->in = (unsigned char *)g_try_malloc(want);
833 state->out = (unsigned char *)g_try_malloc(want << 1);
834 state->size = want;
835 if (state->in == NULL || state->out == NULL) {
836 g_free(state->out);
837 g_free(state->in);
838 g_free(state);
839 errno = ENOMEM;
840 return NULL;
843 #ifdef HAVE_LIBZ
844 /* allocate inflate memory */
845 state->strm.zalloc = Z_NULL;
846 state->strm.zfree = Z_NULL;
847 state->strm.opaque = Z_NULL;
848 state->strm.avail_in = 0;
849 state->strm.next_in = Z_NULL;
850 if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */
851 g_free(state->out);
852 g_free(state->in);
853 g_free(state);
854 errno = ENOMEM;
855 return NULL;
858 /* for now, assume we should check the crc */
859 state->dont_check_crc = FALSE;
860 #endif
861 /* return stream */
862 return state;
865 FILE_T
866 file_open(const char *path)
868 int fd;
869 FILE_T ft;
870 #ifdef HAVE_LIBZ
871 const char *suffixp;
872 #endif
874 /* open file and do correct filename conversions.
876 XXX - do we need O_LARGEFILE? On UN*X, if we need to do
877 something special to get large file support, the configure
878 script should have set us up with the appropriate #defines,
879 so we should be getting a large-file-enabled file descriptor
880 here. Pre-Large File Summit UN*Xes, and possibly even some
881 post-LFS UN*Xes, might require O_LARGEFILE here, though.
882 If so, we should probably handle that in ws_open(). */
883 if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1)
884 return NULL;
886 /* open file handle */
887 ft = file_fdopen(fd);
888 if (ft == NULL) {
889 ws_close(fd);
890 return NULL;
893 #ifdef HAVE_LIBZ
895 * If this file's name ends in ".caz", it's probably a compressed
896 * Windows Sniffer file. The compression is gzip, but if we
897 * process the CRC as specified by RFC 1952, the computed CRC
898 * doesn't match the stored CRC.
900 * Compressed Windows Sniffer files don't all have the same CRC
901 * value; is it just random crap, or are they running the CRC on
902 * a different set of data than you're supposed to (e.g., not
903 * CRCing some of the data), or something such as that?
905 * For now, we just set a flag to ignore CRC errors.
907 suffixp = strrchr(path, '.');
908 if (suffixp != NULL) {
909 if (g_ascii_strcasecmp(suffixp, ".caz") == 0)
910 ft->dont_check_crc = TRUE;
912 #endif
914 return ft;
917 void
918 file_set_random_access(FILE_T stream, gboolean random_flag _U_, GPtrArray *seek)
920 stream->fast_seek = seek;
923 gint64
924 file_seek(FILE_T file, gint64 offset, int whence, int *err)
926 struct fast_seek_point *here;
927 guint n;
929 /* can only seek from start or relative to current position */
930 if (whence != SEEK_SET && whence != SEEK_CUR) {
931 g_assert_not_reached();
933 *err = EINVAL;
934 return -1;
938 /* normalize offset to a SEEK_CUR specification */
939 if (whence == SEEK_SET)
940 offset -= file->pos;
941 else if (file->seek_pending)
942 offset += file->skip;
943 file->seek_pending = FALSE;
945 if (offset < 0 && file->next) {
947 * This is guaranteed to fit in an unsigned int.
948 * To squelch compiler warnings, we cast the
949 * result.
951 guint had = (unsigned)(file->next - file->out);
952 if (-offset <= had) {
954 * Offset is negative, so -offset is
955 * non-negative, and -offset is
956 * <= an unsigned and thus fits in an
957 * unsigned. Get that value and
958 * adjust appropriately.
960 * (Casting offset to unsigned makes
961 * it positive, which is not what we
962 * would want, so we cast -offset
963 * instead.)
965 guint adjustment = (unsigned)(-offset);
966 file->have += adjustment;
967 file->next -= adjustment;
968 file->pos -= adjustment;
969 return file->pos;
973 /* XXX, profile */
974 if ((here = fast_seek_find(file, file->pos + offset)) && (offset < 0 || offset > SPAN || here->compression == UNCOMPRESSED)) {
975 gint64 off, off2;
977 #ifdef HAVE_LIBZ
978 if (here->compression == ZLIB) {
979 #ifdef HAVE_INFLATEPRIME
980 off = here->in - (here->data.zlib.bits ? 1 : 0);
981 #else
982 off = here->in;
983 #endif
984 off2 = here->out;
985 } else if (here->compression == GZIP_AFTER_HEADER) {
986 off = here->in;
987 off2 = here->out;
988 } else
989 #endif
991 off2 = (file->pos + offset);
992 off = here->in + (off2 - here->out);
995 if (ws_lseek64(file->fd, off, SEEK_SET) == -1) {
996 *err = errno;
997 return -1;
999 fast_seek_reset(file);
1001 file->raw_pos = off;
1002 file->have = 0;
1003 file->eof = FALSE;
1004 file->seek_pending = FALSE;
1005 file->err = 0;
1006 file->err_info = NULL;
1007 file->avail_in = 0;
1009 #ifdef HAVE_LIBZ
1010 if (here->compression == ZLIB) {
1011 z_stream *strm = &file->strm;
1013 inflateReset(strm);
1014 strm->adler = here->data.zlib.adler;
1015 strm->total_out = here->data.zlib.total_out;
1016 #ifdef HAVE_INFLATEPRIME
1017 if (here->data.zlib.bits) {
1018 FILE_T state = file;
1019 int ret = GZ_GETC();
1021 if (ret == -1) {
1022 if (state->err == 0) {
1023 /* EOF */
1024 *err = WTAP_ERR_SHORT_READ;
1025 } else
1026 *err = state->err;
1027 return -1;
1029 (void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
1031 #endif
1032 (void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE);
1033 file->compression = ZLIB;
1034 } else if (here->compression == GZIP_AFTER_HEADER) {
1035 z_stream *strm = &file->strm;
1037 inflateReset(strm);
1038 strm->adler = crc32(0L, Z_NULL, 0);
1039 file->compression = ZLIB;
1040 } else
1041 #endif
1042 file->compression = here->compression;
1044 offset = (file->pos + offset) - off2;
1045 file->pos = off2;
1046 /* g_print("OK! %ld\n", offset); */
1048 if (offset) {
1049 file->seek_pending = TRUE;
1050 file->skip = offset;
1052 return file->pos + offset;
1055 /* if within raw area while reading, just go there */
1056 if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw
1057 && (offset < 0 || offset >= file->have) /* seek only when we don't have that offset in buffer */)
1059 if (ws_lseek64(file->fd, offset - file->have, SEEK_CUR) == -1) {
1060 *err = errno;
1061 return -1;
1063 file->raw_pos += (offset - file->have);
1064 file->have = 0;
1065 file->eof = FALSE;
1066 file->seek_pending = FALSE;
1067 file->err = 0;
1068 file->err_info = NULL;
1069 file->avail_in = 0;
1070 file->pos += offset;
1071 return file->pos;
1074 /* calculate skip amount, rewinding if needed for back seek when reading */
1075 if (offset < 0) {
1076 offset += file->pos;
1077 if (offset < 0) { /* before start of file! */
1078 *err = EINVAL;
1079 return -1;
1081 /* rewind, then skip to offset */
1083 /* back up and start over */
1084 if (ws_lseek64(file->fd, file->start, SEEK_SET) == -1) {
1085 *err = errno;
1086 return -1;
1088 fast_seek_reset(file);
1089 file->raw_pos = file->start;
1090 gz_reset(file);
1093 /* skip what's in output buffer (one less gzgetc() check) */
1094 n = (gint64)file->have > offset ? (unsigned)offset : file->have;
1095 file->have -= n;
1096 file->next += n;
1097 file->pos += n;
1098 offset -= n;
1100 /* request skip (if not zero) */
1101 if (offset) {
1102 file->seek_pending = TRUE;
1103 file->skip = offset;
1105 return file->pos + offset;
1109 * Skip forward the specified number of bytes in the file.
1110 * Currently implemented as a wrapper around file_seek(),
1111 * but if, for example, we ever add support for reading
1112 * sequentially from a pipe, this could instead just skip
1113 * forward by reading the bytes in question.
1115 gboolean
1116 file_skip(FILE_T file, gint64 delta, int *err)
1118 if (file_seek(file, delta, SEEK_CUR, err) == -1)
1119 return FALSE;
1120 return TRUE;
1123 gint64
1124 file_tell(FILE_T stream)
1126 /* return position */
1127 return stream->pos + (stream->seek_pending ? stream->skip : 0);
1130 gint64
1131 file_tell_raw(FILE_T stream)
1133 return stream->raw_pos;
1137 file_fstat(FILE_T stream, ws_statb64 *statb, int *err)
1139 if (ws_fstat64(stream->fd, statb) == -1) {
1140 if (err != NULL)
1141 *err = errno;
1142 return -1;
1144 return 0;
1147 gboolean
1148 file_iscompressed(FILE_T stream)
1150 return stream->is_compressed;
1154 file_read(void *buf, unsigned int len, FILE_T file)
1156 guint got, n;
1158 /* if len is zero, avoid unnecessary operations */
1159 if (len == 0)
1160 return 0;
1162 /* process a skip request */
1163 if (file->seek_pending) {
1164 file->seek_pending = FALSE;
1165 if (gz_skip(file, file->skip) == -1)
1166 return -1;
1169 /* get len bytes to buf, or less than len if at the end */
1170 got = 0;
1171 do {
1172 if (file->have) {
1173 /* We have stuff in the output buffer; copy
1174 what we have. */
1175 n = file->have > len ? len : file->have;
1176 memcpy(buf, file->next, n);
1177 file->next += n;
1178 file->have -= n;
1179 } else if (file->err) {
1180 /* We have nothing in the output buffer, and
1181 we have an error that may not have been
1182 reported yet; that means we can't generate
1183 any more data into the output buffer, so
1184 return an error indication. */
1185 return -1;
1186 } else if (file->eof && file->avail_in == 0) {
1187 /* We have nothing in the output buffer, and
1188 we're at the end of the input; just return
1189 with what we've gotten so far. */
1190 break;
1191 } else {
1192 /* We have nothing in the output buffer, and
1193 we can generate more data; get more output,
1194 looking for header if required, and
1195 keep looping to process the new stuff
1196 in the output buffer. */
1197 if (fill_out_buffer(file) == -1)
1198 return -1;
1199 continue; /* no progress yet -- go back to memcpy() above */
1201 /* update progress */
1202 len -= n;
1203 buf = (char *)buf + n;
1204 got += n;
1205 file->pos += n;
1206 } while (len);
1208 return (int)got;
1212 * XXX - this gets a byte, not a character.
1215 file_getc(FILE_T file)
1217 unsigned char buf[1];
1218 int ret;
1220 /* check that we're reading and that there's no error */
1221 if (file->err)
1222 return -1;
1224 /* try output buffer (no need to check for skip request) */
1225 if (file->have) {
1226 file->have--;
1227 file->pos++;
1228 return *(file->next)++;
1231 ret = file_read(buf, 1, file);
1232 return ret < 1 ? -1 : buf[0];
1235 char *
1236 file_gets(char *buf, int len, FILE_T file)
1238 guint left, n;
1239 char *str;
1240 unsigned char *eol;
1242 /* check parameters */
1243 if (buf == NULL || len < 1)
1244 return NULL;
1246 /* check that there's no error */
1247 if (file->err)
1248 return NULL;
1250 /* process a skip request */
1251 if (file->seek_pending) {
1252 file->seek_pending = FALSE;
1253 if (gz_skip(file, file->skip) == -1)
1254 return NULL;
1257 /* copy output bytes up to new line or len - 1, whichever comes first --
1258 append a terminating zero to the string (we don't check for a zero in
1259 the contents, let the user worry about that) */
1260 str = buf;
1261 left = (unsigned)len - 1;
1262 if (left) do {
1263 /* assure that something is in the output buffer */
1264 if (file->have == 0) {
1265 /* We have nothing in the output buffer. */
1266 if (file->err) {
1267 /* We have an error that may not have
1268 been reported yet; that means we
1269 can't generate any more data into
1270 the output buffer, so return an
1271 error indication. */
1272 return NULL;
1274 if (fill_out_buffer(file) == -1)
1275 return NULL; /* error */
1276 if (file->have == 0) { /* end of file */
1277 if (buf == str) /* got bupkus */
1278 return NULL;
1279 break; /* got something -- return it */
1283 /* look for end-of-line in current output buffer */
1284 n = file->have > left ? left : file->have;
1285 eol = (unsigned char *)memchr(file->next, '\n', n);
1286 if (eol != NULL)
1287 n = (unsigned)(eol - file->next) + 1;
1289 /* copy through end-of-line, or remainder if not found */
1290 memcpy(buf, file->next, n);
1291 file->have -= n;
1292 file->next += n;
1293 file->pos += n;
1294 left -= n;
1295 buf += n;
1296 } while (left && eol == NULL);
1298 /* found end-of-line or out of space -- terminate string and return it */
1299 buf[0] = 0;
1300 return str;
1304 file_eof(FILE_T file)
1306 /* return end-of-file state */
1307 return (file->eof && file->avail_in == 0 && file->have == 0);
1311 * Routine to return a Wiretap error code (0 for no error, an errno
1312 * for a file error, or a WTAP_ERR_ code for other errors) for an
1313 * I/O stream. Also returns an error string for some errors.
1316 file_error(FILE_T fh, gchar **err_info)
1318 if (fh->err!=0 && err_info) {
1319 /* g_strdup() returns NULL for NULL argument */
1320 *err_info = g_strdup(fh->err_info);
1322 return fh->err;
1325 void
1326 file_clearerr(FILE_T stream)
1328 /* clear error and end-of-file */
1329 stream->err = 0;
1330 stream->err_info = NULL;
1331 stream->eof = FALSE;
1334 void
1335 file_fdclose(FILE_T file)
1337 ws_close(file->fd);
1338 file->fd = -1;
1341 gboolean
1342 file_fdreopen(FILE_T file, const char *path)
1344 int fd;
1346 if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1)
1347 return FALSE;
1348 file->fd = fd;
1349 return TRUE;
1352 void
1353 file_close(FILE_T file)
1355 int fd = file->fd;
1357 /* free memory and close file */
1358 if (file->size) {
1359 #ifdef HAVE_LIBZ
1360 inflateEnd(&(file->strm));
1361 #endif
1362 g_free(file->out);
1363 g_free(file->in);
1365 g_free(file->fast_seek_cur);
1366 file->err = 0;
1367 file->err_info = NULL;
1368 g_free(file);
1370 * If fd is -1, somebody's done a file_closefd() on us, so
1371 * we don't need to close the FD itself, and shouldn't do
1372 * so.
1374 if (fd != -1)
1375 ws_close(fd);
1378 #ifdef HAVE_LIBZ
1379 /* internal gzip file state data structure for writing */
1380 struct wtap_writer {
1381 int fd; /* file descriptor */
1382 gint64 pos; /* current position in uncompressed data */
1383 guint size; /* buffer size, zero if not allocated yet */
1384 guint want; /* requested buffer size, default is GZBUFSIZE */
1385 unsigned char *in; /* input buffer */
1386 unsigned char *out; /* output buffer (double-sized when reading) */
1387 unsigned char *next; /* next output data to deliver or write */
1388 int level; /* compression level */
1389 int strategy; /* compression strategy */
1390 int err; /* error code */
1391 /* zlib deflate stream */
1392 z_stream strm; /* stream structure in-place (not a pointer) */
1395 GZWFILE_T
1396 gzwfile_open(const char *path)
1398 int fd;
1399 GZWFILE_T state;
1400 int save_errno;
1402 fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
1403 if (fd == -1)
1404 return NULL;
1405 state = gzwfile_fdopen(fd);
1406 if (state == NULL) {
1407 save_errno = errno;
1408 close(fd);
1409 errno = save_errno;
1411 return state;
1414 GZWFILE_T
1415 gzwfile_fdopen(int fd)
1417 GZWFILE_T state;
1419 /* allocate wtap_writer structure to return */
1420 state = (GZWFILE_T)g_try_malloc(sizeof *state);
1421 if (state == NULL)
1422 return NULL;
1423 state->fd = fd;
1424 state->size = 0; /* no buffers allocated yet */
1425 state->want = GZBUFSIZE; /* requested buffer size */
1427 state->level = Z_DEFAULT_COMPRESSION;
1428 state->strategy = Z_DEFAULT_STRATEGY;
1430 /* initialize stream */
1431 state->err = Z_OK; /* clear error */
1432 state->pos = 0; /* no uncompressed data yet */
1433 state->strm.avail_in = 0; /* no input data yet */
1435 /* return stream */
1436 return state;
1439 /* Initialize state for writing a gzip file. Mark initialization by setting
1440 state->size to non-zero. Return -1, and set state->err, on failure;
1441 return 0 on success. */
1442 static int
1443 gz_init(GZWFILE_T state)
1445 int ret;
1446 z_streamp strm = &(state->strm);
1448 /* allocate input and output buffers */
1449 state->in = (unsigned char *)g_try_malloc(state->want);
1450 state->out = (unsigned char *)g_try_malloc(state->want);
1451 if (state->in == NULL || state->out == NULL) {
1452 g_free(state->out);
1453 g_free(state->in);
1454 state->err = ENOMEM;
1455 return -1;
1458 /* allocate deflate memory, set up for gzip compression */
1459 strm->zalloc = Z_NULL;
1460 strm->zfree = Z_NULL;
1461 strm->opaque = Z_NULL;
1462 ret = deflateInit2(strm, state->level, Z_DEFLATED,
1463 15 + 16, 8, state->strategy);
1464 if (ret != Z_OK) {
1465 g_free(state->out);
1466 g_free(state->in);
1467 if (ret == Z_MEM_ERROR) {
1468 /* This means "not enough memory". */
1469 state->err = ENOMEM;
1470 } else {
1471 /* This "shouldn't happen". */
1472 state->err = WTAP_ERR_INTERNAL;
1474 return -1;
1477 /* mark state as initialized */
1478 state->size = state->want;
1480 /* initialize write buffer */
1481 strm->avail_out = state->size;
1482 strm->next_out = state->out;
1483 state->next = strm->next_out;
1484 return 0;
1487 /* Compress whatever is at avail_in and next_in and write to the output file.
1488 Return -1, and set state->err, if there is an error writing to the output
1489 file; return 0 on success.
1490 flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH,
1491 then the deflate() state is reset to start a new gzip stream. */
1492 static int
1493 gz_comp(GZWFILE_T state, int flush)
1495 int ret;
1496 ssize_t got;
1497 ptrdiff_t have;
1498 z_streamp strm = &(state->strm);
1500 /* allocate memory if this is the first time through */
1501 if (state->size == 0 && gz_init(state) == -1)
1502 return -1;
1504 /* run deflate() on provided input until it produces no more output */
1505 ret = Z_OK;
1506 do {
1507 /* write out current buffer contents if full, or if flushing, but if
1508 doing Z_FINISH then don't write until we get to Z_STREAM_END */
1509 if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
1510 (flush != Z_FINISH || ret == Z_STREAM_END))) {
1511 have = strm->next_out - state->next;
1512 if (have) {
1513 got = write(state->fd, state->next, (unsigned int)have);
1514 if (got < 0) {
1515 state->err = errno;
1516 return -1;
1518 if ((ptrdiff_t)got != have) {
1519 state->err = WTAP_ERR_SHORT_WRITE;
1520 return -1;
1523 if (strm->avail_out == 0) {
1524 strm->avail_out = state->size;
1525 strm->next_out = state->out;
1527 state->next = strm->next_out;
1530 /* compress */
1531 have = strm->avail_out;
1532 ret = deflate(strm, flush);
1533 if (ret == Z_STREAM_ERROR) {
1534 /* This "shouldn't happen". */
1535 state->err = WTAP_ERR_INTERNAL;
1536 return -1;
1538 have -= strm->avail_out;
1539 } while (have);
1541 /* if that completed a deflate stream, allow another to start */
1542 if (flush == Z_FINISH)
1543 deflateReset(strm);
1545 /* all done, no errors */
1546 return 0;
1549 /* Write out len bytes from buf. Return 0, and set state->err, on
1550 failure or on an attempt to write 0 bytes (in which case state->err
1551 is Z_OK); return the number of bytes written on success. */
1552 unsigned
1553 gzwfile_write(GZWFILE_T state, const void *buf, guint len)
1555 guint put = len;
1556 guint n;
1557 z_streamp strm;
1559 strm = &(state->strm);
1561 /* check that there's no error */
1562 if (state->err != Z_OK)
1563 return 0;
1565 /* if len is zero, avoid unnecessary operations */
1566 if (len == 0)
1567 return 0;
1569 /* allocate memory if this is the first time through */
1570 if (state->size == 0 && gz_init(state) == -1)
1571 return 0;
1573 /* for small len, copy to input buffer, otherwise compress directly */
1574 if (len < state->size) {
1575 /* copy to input buffer, compress when full */
1576 do {
1577 if (strm->avail_in == 0)
1578 strm->next_in = state->in;
1579 n = state->size - strm->avail_in;
1580 if (n > len)
1581 n = len;
1582 memcpy(strm->next_in + strm->avail_in, buf, n);
1583 strm->avail_in += n;
1584 state->pos += n;
1585 buf = (const char *)buf + n;
1586 len -= n;
1587 if (len && gz_comp(state, Z_NO_FLUSH) == -1)
1588 return 0;
1589 } while (len);
1591 else {
1592 /* consume whatever's left in the input buffer */
1593 if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
1594 return 0;
1596 /* directly compress user buffer to file */
1597 strm->avail_in = len;
1598 strm->next_in = (Bytef *)buf;
1599 state->pos += len;
1600 if (gz_comp(state, Z_NO_FLUSH) == -1)
1601 return 0;
1604 /* input was all buffered or compressed (put will fit in int) */
1605 return (int)put;
1608 /* Flush out what we've written so far. Returns -1, and sets state->err,
1609 on failure; returns 0 on success. */
1611 gzwfile_flush(GZWFILE_T state)
1613 /* check that there's no error */
1614 if (state->err != Z_OK)
1615 return -1;
1617 /* compress remaining data with Z_SYNC_FLUSH */
1618 gz_comp(state, Z_SYNC_FLUSH);
1619 if (state->err != Z_OK)
1620 return -1;
1621 return 0;
1624 /* Flush out all data written, and close the file. Returns a Wiretap
1625 error on failure; returns 0 on success. */
1627 gzwfile_close(GZWFILE_T state)
1629 int ret = 0;
1631 /* flush, free memory, and close file */
1632 if (gz_comp(state, Z_FINISH) == -1 && ret == 0)
1633 ret = state->err;
1634 (void)deflateEnd(&(state->strm));
1635 g_free(state->out);
1636 g_free(state->in);
1637 state->err = Z_OK;
1638 if (close(state->fd) == -1 && ret == 0)
1639 ret = errno;
1640 g_free(state);
1641 return ret;
1645 gzwfile_geterr(GZWFILE_T state)
1647 return state->err;
1649 #endif