summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2011-04-08 00:28:37 +0000
committerGuy Harris <guy@alum.mit.edu>2011-04-08 00:28:37 +0000
commitab261a32813b2bd6b5c12fc081aed71bc5b0c833 (patch)
tree19edd070267f48bb82aa4b554762f918be830f26
parentf989235a87861ca214adcf66b880d460fd4104fe (diff)
downloadwireshark-ab261a32813b2bd6b5c12fc081aed71bc5b0c833.tar.gz
From Jakub Zawadzki:
Steal file_wrappers functions from zlib v2. svn path=/trunk/; revision=36513
-rw-r--r--wiretap/file_access.c16
-rw-r--r--wiretap/file_wrappers.c816
-rw-r--r--wiretap/file_wrappers.h37
-rw-r--r--wiretap/wtap-int.h33
-rw-r--r--wiretap/wtap.c2
5 files changed, 675 insertions, 229 deletions
diff --git a/wiretap/file_access.c b/wiretap/file_access.c
index 60a4eac110..9ac254a27f 100644
--- a/wiretap/file_access.c
+++ b/wiretap/file_access.c
@@ -768,15 +768,15 @@ static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
gboolean compressed, int *err);
static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, gboolean compressed, int *err);
-static FILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename);
-static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd);
+static WFILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename);
+static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd);
static int wtap_dump_file_close(wtap_dumper *wdh);
wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap,
int snaplen, gboolean compressed, int *err)
{
wtap_dumper *wdh;
- FILE_T fh;
+ WFILE_T fh;
/* Check whether we can open a capture file with that file type
and that encapsulation. */
@@ -828,7 +828,7 @@ wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen,
gboolean compressed, int *err)
{
wtap_dumper *wdh;
- FILE_T fh;
+ WFILE_T fh;
/* Check whether we can open a capture file with that file type
and that encapsulation. */
@@ -1010,7 +1010,7 @@ gboolean wtap_dump_set_addrinfo_list(wtap_dumper *wdh, struct addrinfo *addrinfo
/* internally open a file for writing (compressed or not) */
#ifdef HAVE_LIBZ
-static FILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename)
+static WFILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename)
{
if(wdh->compressed) {
return gzopen(filename, "wb");
@@ -1019,7 +1019,7 @@ static FILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename)
}
}
#else
-static FILE_T wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename)
+static WFILE_T wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename)
{
return ws_fopen(filename, "wb");
}
@@ -1027,7 +1027,7 @@ static FILE_T wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename)
/* internally open a file for writing (compressed or not) */
#ifdef HAVE_LIBZ
-static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd)
+static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd)
{
if(wdh->compressed) {
return gzdopen(fd, "wb");
@@ -1036,7 +1036,7 @@ static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd)
}
}
#else
-static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh _U_, int fd)
+static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh _U_, int fd)
{
return fdopen(fd, "wb");
}
diff --git a/wiretap/file_wrappers.c b/wiretap/file_wrappers.c
index b8682ff205..fce6d1b9a7 100644
--- a/wiretap/file_wrappers.c
+++ b/wiretap/file_wrappers.c
@@ -20,111 +20,411 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
+
+/* file_access interface based heavily on zlib gzread.c and gzlib.c from zlib
+ * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+ * under licence:
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+*/
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-/*
- * Do this now, to get close() defined, before we muck with the definition
- * of HAVE_UNISTD_H. See below for the full sad story of why we do that.
- */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
-/*
- * OK, now this is tricky.
- *
- * At least on FreeBSD 3.2, "/usr/include/zlib.h" includes
- * "/usr/include/zconf.h", which, if HAVE_UNISTD_H is defined,
- * #defines "z_off_t" to be "off_t", and if HAVE_UNISTD_H is
- * not defined, #defines "z_off_t" to be "long" if it's not
- * already #defined.
- *
- * In 4.4-Lite-derived systems such as FreeBSD, "off_t" is
- * "long long int", not "long int", so the definition of "z_off_t" -
- * and therefore the types of the arguments to routines such as
- * "gzseek()", as declared, with prototypes, in "zlib.h" - depends
- * on whether HAVE_UNISTD_H is defined prior to including "zlib.h"!
- *
- * It's not defined in the FreeBSD 3.2 "zlib", so if we include "zlib.h"
- * after defining HAVE_UNISTD_H, we get a misdeclaration of "gzseek()",
- * and, if we're building with "zlib" support, anything that seeks
- * on a file may not work.
- *
- * Other BSDs may have the same problem, if they haven't done something
- * such as defining HAVE_UNISTD_H in "zconf.h".
- *
- * "config.h" defines HAVE_UNISTD_H, on all systems that have it, and all
- * 4.4-Lite-derived BSDs have it. Therefore, given that "zlib.h" is included
- * by "file_wrappers.h", that means that unless we include "zlib.h" before
- * we include "config.h", we get a misdeclaration of "gzseek()".
- *
- * Unfortunately, it's "config.h" that tells us whether we have "zlib"
- * in the first place, so we don't know whether to include "zlib.h"
- * until we include "config.h"....
- *
- * A similar problem appears to occur with "gztell()", at least on
- * NetBSD.
- *
- * To add further complication, on recent versions, at least, of OpenBSD,
- * the Makefile for zlib defines HAVE_UNISTD_H.
- *
- * So what we do is, on all OSes other than OpenBSD, *undefine* HAVE_UNISTD_H
- * before including "wtap-int.h" (it handles including "zlib.h" if HAVE_ZLIB
- * is defined, and it includes "wtap.h", which we include to get the
- * WTAP_ERR_ZLIB values), and, if we have zlib, make "file_seek()" and
- * "file_tell()" subroutines, so that the only calls to "gzseek()" and
- * "gztell()" are in this file, which, by dint of the hackery described
- * above, manages to correctly declare "gzseek()" and "gztell()".
- *
- * On OpenBSD, we forcibly *define* HAVE_UNISTD_H if it's not defined.
- *
- * Hopefully, the BSDs will, over time, remove the test for HAVE_UNISTD_H
- * from "zconf.h", so that "gzseek()" and "gztell()" will be declared
- * with the correct signature regardless of whether HAVE_UNISTD_H is
- * defined, so that if they change the signature we don't have to worry
- * about making sure it's defined or not defined.
- *
- * DO NOT, UNDER ANY CIRCUMSTANCES, REMOVE THE FOLLOWING LINES, OR MOVE
- * THEM AFTER THE INCLUDE OF "wtap-int.h"! Doing so will cause any program
- * using Wiretap to read capture files to fail miserably on a FreeBSD
- * 3.2 or 3.3 system - and possibly some other BSD systems - if zlib is
- * installed. If you *must* have HAVE_UNISTD_H defined before including
- * "wtap-int.h", put "file_error()" into a file by itself, which can
- * cheerfully include "wtap.h" and get "gzseek()" misdeclared, and include
- * just "zlib.h" in this file - *after* undefining HAVE_UNISTD_H.
- *
- * XXX - this is not working with zlib 1.2.4 and, at least on some non-
- * 4.4-Lite-derived systems (such as some Linux distributions), with
- * some earlier versions of zlib. Perhaps the configure script should
- * check whether off_t is long or not and, if it is, just trust zlib
- * to work without this mess. That won't help with zlib 1.2.4, as
- * if you build and install that on OS X, which *is* 4.4-Lite-derived,
- * it still doesn't work right.
- */
-#ifdef __OpenBSD__
-#ifndef HAVE_UNISTD_H
-#define HAVE_UNISTD_H
-#endif /* HAVE_UNISTD_H */
-#else /* __OpenBSD__ */
-#undef HAVE_UNISTD_H
-#endif /* __OpenBSD__ */
-
#include <errno.h>
#include <stdio.h>
-#ifdef HAVE_LIBZ
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <string.h>
-#endif /* HAVE_LIBZ */
#include "wtap-int.h"
#include "file_wrappers.h"
#include <wsutil/file_util.h>
+/* #define GZBUFSIZE 8192 */
+#define GZBUFSIZE 4096
+
+/* values for gz_state compression */
+#define UNKNOWN 0 /* look for a gzip header */
+#define UNCOMPRESSED 1 /* copy input directly */
+
+#ifdef HAVE_LIBZ
+#define ZLIB 2 /* decompress a zlib stream */
+#endif
+
+/* XXX, lseek64() instead of ws_lseek()? */
+
+static int /* gz_load */
+raw_read(FILE_T state, unsigned char *buf, unsigned int count, unsigned *have)
+{
+ int ret;
+
+ *have = 0;
+ do {
+ ret = read(state->fd, buf + *have, count - *have);
+ if (ret <= 0)
+ break;
+ *have += ret;
+ } while (*have < count);
+ if (ret < 0) {
+ state->err = errno;
+ return -1;
+ }
+ if (ret == 0)
+ state->eof = 1;
+ return 0;
+}
+
+static int /* gz_avail */
+fill_in_buffer(FILE_T state)
+{
+ if (state->err)
+ return -1;
+ if (state->eof == 0) {
+ if (raw_read(state, state->in, state->size, (unsigned *)&(state->avail_in)) == -1)
+ return -1;
+ state->next_in = state->in;
+ }
+ return 0;
+}
+
+#ifdef HAVE_LIBZ
+
+/* Get next byte from input, or -1 if end or error. */
+#define NEXT() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \
+ (state->avail_in == 0 ? -1 : \
+ (state->avail_in--, *(state->next_in)++)))
+
+/* Get a four-byte little-endian integer and return 0 on success and the value
+ in *ret. Otherwise -1 is returned and *ret is not modified. */
+static int
+gz_next4(FILE_T state, guint32 *ret)
+{
+ guint32 val;
+ int ch;
+
+ val = NEXT();
+ val += (unsigned)NEXT() << 8;
+ val += (guint32)NEXT() << 16;
+ ch = NEXT();
+ if (ch == -1)
+ return -1;
+ val += (guint32)ch << 24;
+ *ret = val;
+ return 0;
+}
+
+static int /* gz_decomp */
+zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
+{
+ int ret;
+ guint32 crc, len;
+ z_streamp strm = &(state->strm);
+
+ strm->avail_out = count;
+ strm->next_out = buf;
+
+ /* fill output buffer up to end of deflate stream */
+ do {
+ /* get more input for inflate() */
+ if (state->avail_in == 0 && fill_in_buffer(state) == -1)
+ return -1;
+ if (state->avail_in == 0) {
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+
+ strm->avail_in = state->avail_in;
+ strm->next_in = state->next_in;
+ /* decompress and handle errors */
+ ret = inflate(strm, Z_NO_FLUSH);
+ state->avail_in = strm->avail_in;
+ state->next_in = strm->next_in;
+ if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
+ state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
+ return -1;
+ }
+ if (ret == Z_MEM_ERROR) {
+ state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */
+ return -1;
+ }
+ if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+ } while (strm->avail_out && ret != Z_STREAM_END);
+
+ /* update available output and crc check value */
+ state->next = buf;
+ state->have = count - strm->avail_out;
+ strm->adler = crc32(strm->adler, state->next, state->have);
+
+ /* check gzip trailer if at end of deflate stream */
+ if (ret == Z_STREAM_END) {
+ if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+ if (crc != strm->adler) {
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+ if (len != (strm->total_out & 0xffffffffL)) {
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+ state->compression = UNKNOWN; /* ready for next stream, once have is 0 */
+ }
+
+ /* good decompression */
+ return 0;
+}
+#endif
+
+static int
+gz_head(FILE_T state)
+{
+ /* get some data in the input buffer */
+ if (state->avail_in == 0) {
+ if (fill_in_buffer(state) == -1)
+ return -1;
+ if (state->avail_in == 0)
+ return 0;
+ }
+
+ /* look for the gzip magic header bytes 31 and 139 */
+#ifdef HAVE_LIBZ
+ if (state->next_in[0] == 31) {
+ state->avail_in--;
+ state->next_in++;
+ if (state->avail_in == 0 && fill_in_buffer(state) == -1)
+ return -1;
+ if (state->avail_in && state->next_in[0] == 139) {
+ unsigned len;
+ int flags;
+
+ /* we have a gzip header, woo hoo! */
+ state->avail_in--;
+ state->next_in++;
+
+ /* skip rest of header */
+ if (NEXT() != 8) { /* compression method */
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+ flags = NEXT();
+ if (flags & 0xe0) { /* reserved flag bits */
+ state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ return -1;
+ }
+ NEXT(); /* modification time */
+ NEXT();
+ NEXT();
+ NEXT();
+ NEXT(); /* extra flags */
+ NEXT(); /* operating system */
+ if (flags & 4) { /* extra field */
+ len = (unsigned)NEXT();
+ len += (unsigned)NEXT() << 8;
+ while (len--)
+ if (NEXT() < 0)
+ break;
+ }
+ if (flags & 8) /* file name */
+ while (NEXT() > 0)
+ ;
+ if (flags & 16) /* comment */
+ while (NEXT() > 0)
+ ;
+ if (flags & 2) { /* header crc */
+ NEXT();
+ NEXT();
+ }
+ /* an unexpected end of file is not checked for here -- it will be
+ noticed on the first request for uncompressed data */
+
+ /* set up for decompression */
+ inflateReset(&(state->strm));
+ state->strm.adler = crc32(0L, Z_NULL, 0);
+ state->compression = ZLIB;
+ return 0;
+ }
+ else {
+ /* not a gzip file -- save first byte (31) and fall to raw i/o */
+ state->out[0] = 31;
+ state->have = 1;
+ }
+ }
+#endif
+#ifdef HAVE_LIBXZ
+ /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
+ /* FD 37 7A 58 5A 00 */
+#endif
+
+ /* doing raw i/o, save start of raw data for seeking, copy any leftover
+ input to output -- this assumes that the output buffer is larger than
+ the input buffer, which also assures space for gzungetc() */
+ state->raw = state->pos;
+ state->next = state->out;
+ if (state->avail_in) {
+ memcpy(state->next + state->have, state->next_in, state->avail_in);
+ state->have += state->avail_in;
+ state->avail_in = 0;
+ }
+ state->compression = UNCOMPRESSED;
+ return 0;
+}
+
+static int /* gz_make */
+fill_out_buffer(FILE_T state)
+{
+ if (state->compression == UNKNOWN) { /* look for gzip header */
+ if (gz_head(state) == -1)
+ return -1;
+ if (state->have) /* got some data from gz_head() */
+ return 0;
+ }
+ if (state->compression == UNCOMPRESSED) { /* straight copy */
+ if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1)
+ return -1;
+ state->next = state->out;
+ }
+#ifdef HAVE_LIBZ
+ else if (state->compression == ZLIB) { /* decompress */
+ if (zlib_read(state, state->out, state->size << 1) == -1)
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+static int
+gz_skip(FILE_T state, gint64 len)
+{
+ unsigned n;
+
+ /* skip over len bytes or reach end-of-file, whichever comes first */
+ while (len)
+ /* skip over whatever is in output buffer */
+ if (state->have) {
+ n = (gint64)state->have > len ? (unsigned)len : state->have;
+ state->have -= n;
+ state->next += n;
+ state->pos += n;
+ len -= n;
+ }
+
+ /* output buffer empty -- return if we're at the end of the input */
+ else if (state->eof && state->avail_in == 0)
+ break;
+
+ /* need more data to skip -- load up output buffer */
+ else {
+ /* get more output, looking for header if required */
+ if (fill_out_buffer(state) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+static void
+gz_reset(FILE_T state)
+{
+ state->have = 0; /* no output data available */
+ state->eof = 0; /* not at end of file */
+ state->compression = UNKNOWN; /* look for gzip header */
+
+ state->seek = 0; /* no seek request pending */
+ state->err = 0; /* clear error */
+ state->pos = 0; /* no uncompressed data yet */
+ state->avail_in = 0; /* no input data yet */
+}
+
+FILE_T
+filed_open(int fd)
+{
+#ifdef _STATBUF_ST_BLKSIZE /* XXX, _STATBUF_ST_BLKSIZE portable? */
+ struct stat st;
+#endif
+ int want = GZBUFSIZE;
+ FILE_T state;
+
+ if (fd == -1)
+ return NULL;
+
+ /* allocate gzFile structure to return */
+ state = g_try_malloc(sizeof(wtap_reader));
+ if (state == NULL)
+ return NULL;
+
+ /* open the file with the appropriate mode (or just use fd) */
+ state->fd = fd;
+
+ /* save the current position for rewinding (only if reading) */
+ state->start = ws_lseek(state->fd, 0, SEEK_CUR);
+ if (state->start == -1) state->start = 0;
+
+ /* initialize stream */
+ gz_reset(state);
+
+#ifdef _STATBUF_ST_BLKSIZE
+ if (fstat(fd, &st) >= 0) {
+ want = st.st_blksize;
+ /* XXX, verify result? */
+ }
+#endif
+
+ /* allocate buffers */
+ state->in = g_try_malloc(want);
+ state->out = g_try_malloc(want << 1);
+ state->size = want;
+ if (state->in == NULL || state->out == NULL) {
+ g_free(state->out);
+ g_free(state->in);
+ g_free(state);
+ errno = ENOMEM;
+ return NULL;
+ }
#ifdef HAVE_LIBZ
+ /* allocate inflate memory */
+ state->strm.zalloc = Z_NULL;
+ state->strm.zfree = Z_NULL;
+ state->strm.opaque = Z_NULL;
+ state->strm.avail_in = 0;
+ state->strm.next_in = Z_NULL;
+ if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */
+ g_free(state->out);
+ g_free(state->in);
+ g_free(state);
+ errno = ENOMEM;
+ return NULL;
+ }
+#endif
+ gz_head(state); /* read first chunk */
+
+ /* return stream */
+ return state;
+}
FILE_T
file_open(const char *path)
@@ -133,16 +433,18 @@ file_open(const char *path)
FILE_T ft;
int oflag;
+ /* O_LARGEFILE? */
oflag = O_RDONLY;
#ifdef _WIN32
oflag |= O_BINARY;
#endif
+
/* open file and do correct filename conversions */
if ((fd = ws_open(path, oflag, 0666)) == -1)
return NULL;
- /* open zlib file handle */
- ft = gzdopen(fd, "rb");
+ /* open file handle */
+ ft = filed_open(fd);
if (ft == NULL) {
ws_close(fd);
return NULL;
@@ -152,136 +454,272 @@ file_open(const char *path)
}
gint64
-file_seek(void *stream, gint64 offset, int whence, int *err)
+file_seek(FILE_T file, gint64 offset, int whence, int *err)
{
- gint64 ret;
-
- /* XXX - z_off_t is usually long, won't work >= 2GB! */
- ret = (gint64) gzseek(stream, (z_off_t)offset, whence);
- if (ret == -1) {
- /*
- * XXX - "gzseek()", as of zlib 1.1.4, doesn't set
- * "z_err" for the stream, so "gzerror()" could return
- * a bogus Z_OK.
- *
- * As this call failed, we know "gzerror()" shouldn't
- * return Z_OK; if it does, we assume that "errno" is
- * the real error.
- */
- *err = file_error(stream);
- if (*err == 0)
+ unsigned n;
+
+ /* check that there's no error */
+ if (file->err) {
+ *err = file->err;
+ return -1;
+ }
+
+ /* can only seek from start or relative to current position */
+ if (whence != SEEK_SET && whence != SEEK_CUR) {
+ g_assert_not_reached();
+/*
+ *err = EINVAL;
+ return -1;
+ */
+ }
+
+ /* normalize offset to a SEEK_CUR specification */
+ if (whence == SEEK_SET)
+ offset -= file->pos;
+ else if (file->seek)
+ offset += file->skip;
+ file->seek = 0;
+
+ /* if within raw area while reading, just go there */
+ if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw) {
+ if (ws_lseek(file->fd, offset - file->have, SEEK_CUR) == -1) {
+ *err = errno;
+ return -1;
+ }
+ file->have = 0;
+ file->eof = 0;
+ file->seek = 0;
+ file->err = 0;
+ file->avail_in = 0;
+ file->pos += offset;
+ return file->pos;
+ }
+
+ /* calculate skip amount, rewinding if needed for back seek when reading */
+ if (offset < 0) {
+ offset += file->pos;
+ if (offset < 0) { /* before start of file! */
+ /* *err = ???; */
+ return -1;
+ }
+ /* rewind, then skip to offset */
+
+ /* back up and start over */
+ if (ws_lseek(file->fd, file->start, SEEK_SET) == -1) {
*err = errno;
+ return -1;
+ }
+ gz_reset(file);
}
- return ret;
+
+ /* skip what's in output buffer (one less gzgetc() check) */
+ n = (gint64)file->have > offset ? (unsigned)offset : file->have;
+ file->have -= n;
+ file->next += n;
+ file->pos += n;
+ offset -= n;
+
+ /* request skip (if not zero) */
+ if (offset) {
+ file->seek = 1;
+ file->skip = offset;
+ }
+ return file->pos + offset;
}
gint64
-file_tell(void *stream)
+file_tell(FILE_T stream)
{
- /* XXX - z_off_t is usually long, won't work >= 2GB! */
- return (gint64)gztell(stream);
+ /* return position */
+ return stream->pos + (stream->seek ? stream->skip : 0);
}
-/*
- * Routine to return a Wiretap error code (0 for no error, an errno
- * for a file error, or a WTAP_ERR_ code for other errors) for an
- * I/O stream.
- */
-int
-file_error(void *fh)
+int
+file_read(void *buf, unsigned int len, FILE_T file)
{
- int errnum;
+ unsigned got, n;
- gzerror(fh, &errnum);
- switch (errnum) {
+ /* check that we're reading and that there's no error */
+ if (file->err)
+ return -1;
- case Z_OK: /* no error */
+ /* if len is zero, avoid unnecessary operations */
+ if (len == 0)
return 0;
- case Z_STREAM_END: /* EOF - not an error */
- return 0;
+ /* process a skip request */
+ if (file->seek) {
+ file->seek = 0;
+ if (gz_skip(file, file->skip) == -1)
+ return -1;
+ }
- case Z_ERRNO: /* file I/O error */
- return errno;
+ /* get len bytes to buf, or less than len if at the end */
+ got = 0;
+ do {
+ /* first just try copying data from the output buffer */
+ if (file->have) {
+ n = file->have > len ? len : file->have;
+ memcpy(buf, file->next, n);
+ file->next += n;
+ file->have -= n;
+ }
- default:
- return WTAP_ERR_ZLIB + errnum;
- }
+ /* output buffer empty -- return if we're at the end of the input */
+ else if (file->eof && file->avail_in == 0)
+ break;
+
+ /* need output data -- for small len or new stream load up our output buffer */
+ else if (file->compression == UNKNOWN || len < (file->size << 1)) {
+ /* get more output, looking for header if required */
+ if (fill_out_buffer(file) == -1)
+ return -1;
+ continue; /* no progress yet -- go back to memcpy() above */
+
+ } else if (file->compression == UNCOMPRESSED) { /* large len -- read directly into user buffer */
+ if (raw_read(file, buf, len, &n) == -1)
+ return -1;
+ }
+#ifdef HAVE_LIBZ
+ /* large len -- decompress directly into user buffer */
+ else { /* file->compression == ZLIB */
+ if (zlib_read(file, buf, len) == -1)
+ return -1;
+ n = file->have;
+ file->have = 0;
+ }
+#endif
+ /* update progress */
+ len -= n;
+ buf = (char *)buf + n;
+ got += n;
+ file->pos += n;
+ } while (len);
+
+ return (int)got;
}
-#else /* HAVE_LIBZ */
+int
+file_getc(FILE_T file)
+{
+ unsigned char buf[1];
+ int ret;
-gint64
-file_seek(void *stream, gint64 offset, int whence, int *err)
+ /* check that we're reading and that there's no error */
+ if (file->err)
+ return -1;
+
+ /* try output buffer (no need to check for skip request) */
+ if (file->have) {
+ file->have--;
+ file->pos++;
+ return *(file->next)++;
+ }
+
+ ret = file_read(buf, 1, file);
+ return ret < 1 ? -1 : buf[0];
+}
+
+char *
+file_gets(char *buf, int len, FILE_T file)
{
- gint64 ret;
-#ifdef _WIN32
- gint64 pos;
-#endif
+ unsigned left, n;
+ char *str;
+ unsigned char *eol;
-#ifdef _WIN32
- /* Win32 version using fsetpos/fgetpos */
- /* XXX - using fsetpos/fgetpos this way is UNDOCUMENTED, but I don't see a any better way :-( */
- /* _lseeki64(_fileno(stream)) doesn't work as this will mangle the internal FILE handling data */
- switch(whence) {
- case(SEEK_SET):
- /* do nothing */
- break;
- case(SEEK_CUR):
- /* adjust offset */
- /* XXX - CURRENTLY UNTESTED!!! */
- ret = fgetpos(stream, &pos);
- if(ret != 0) {
- *err = errno;
- return ret;
- }
- offset += pos;
- break;
- case(SEEK_END):
- default:
- g_assert_not_reached();
- }
- ret = fsetpos(stream, &offset);
- if(ret != 0) {
- *err = errno;
+ /* check parameters */
+ if (buf == NULL || len < 1)
+ return NULL;
+
+ /* check that there's no error */
+ if (file->err)
+ return NULL;
+
+ /* process a skip request */
+ if (file->seek) {
+ file->seek = 0;
+ if (gz_skip(file, file->skip) == -1)
+ return NULL;
}
- /* XXX - won't work >= 2GB! */
- /*ret = (gint64) fseek(stream, (long) offset, whence);
- if(ret == -1) {
- *err = errno;
- }*/
-#else
- /* "basic" version using fseek */
- /* XXX - won't work >= 2GB! */
- ret = (gint64) fseek(stream, (long) offset, whence);
- if (ret == -1)
- *err = file_error(stream);
-#endif
- /*g_warning("Seek %" G_GINT64_MODIFIER "d whence %u ret %" G_GINT64_MODIFIER "d size %u", offset, whence, ret, sizeof(fpos_t));*/
- return ret;
+
+ /* copy output bytes up to new line or len - 1, whichever comes first --
+ append a terminating zero to the string (we don't check for a zero in
+ the contents, let the user worry about that) */
+ str = buf;
+ left = (unsigned)len - 1;
+ if (left) do {
+ /* assure that something is in the output buffer */
+ if (file->have == 0) {
+ if (fill_out_buffer(file) == -1)
+ return NULL; /* error */
+ if (file->have == 0) { /* end of file */
+ if (buf == str) /* got bupkus */
+ return NULL;
+ break; /* got something -- return it */
+ }
+ }
+
+ /* look for end-of-line in current output buffer */
+ n = file->have > left ? left : file->have;
+ eol = memchr(file->next, '\n', n);
+ if (eol != NULL)
+ n = (unsigned)(eol - file->next) + 1;
+
+ /* copy through end-of-line, or remainder if not found */
+ memcpy(buf, file->next, n);
+ file->have -= n;
+ file->next += n;
+ file->pos += n;
+ left -= n;
+ buf += n;
+ } while (left && eol == NULL);
+
+ /* found end-of-line or out of space -- terminate string and return it */
+ buf[0] = 0;
+ return str;
}
-gint64
-file_tell(void *stream)
+int
+file_eof(FILE_T file)
{
-#ifdef _WIN32
- /* Win32 version using _telli64 */
- /* XXX - CURRENTLY UNTESTED!!! */
- return _telli64(_fileno((FILE *)stream));
-#else
- /* "basic" version using ftell */
- /* XXX - ftell returns a long - won't work >= 2GB! */
- return (gint64) ftell(stream);
-#endif
+ /* return end-of-file state */
+ return (file->eof && file->avail_in == 0 && file->have == 0);
}
+/*
+ * Routine to return a Wiretap error code (0 for no error, an errno
+ * for a file error, or a WTAP_ERR_ code for other errors) for an
+ * I/O stream.
+ */
int
-file_error(void *fh)
+file_error(FILE_T fh)
{
- if (ferror((FILE *) fh))
- return errno;
- else
- return 0;
+ return fh->err;
+}
+
+void
+file_clearerr(FILE_T stream)
+{
+ /* clear error and end-of-file */
+ stream->err = 0;
+ stream->eof = 0;
+}
+
+int
+file_close(FILE_T file)
+{
+ int fd = file->fd;
+
+ /* free memory and close file */
+ if (file->size) {
+#ifdef HAVE_LIBZ
+ inflateEnd(&(file->strm));
+#endif
+ g_free(file->out);
+ g_free(file->in);
+ }
+ file->err = 0;
+ g_free(file);
+ return close(fd);
}
-#endif /* HAVE_LIBZ */
diff --git a/wiretap/file_wrappers.h b/wiretap/file_wrappers.h
index 96e0c3482e..249de02a2e 100644
--- a/wiretap/file_wrappers.h
+++ b/wiretap/file_wrappers.h
@@ -24,35 +24,18 @@
#ifndef __FILE_H__
#define __FILE_H__
-extern gint64 file_seek(void *stream, gint64 offset, int whence, int *err);
-extern gint64 file_tell(void *stream);
-extern int file_error(void *fh);
-
-#ifdef HAVE_LIBZ
+extern gint64 file_seek(FILE_T stream, gint64 offset, int whence, int *err);
+extern gint64 file_tell(FILE_T stream);
+extern int file_error(FILE_T fh);
extern FILE_T file_open(const char *path);
-#define filed_open(fildes) gzdopen(fildes, "rb")
-#define file_read(buf, count, file) gzread((file),(buf),(unsigned)(count))
-#define file_close gzclose
-#define file_getc gzgetc
-#define file_gets(buf, len, file) gzgets((file), (buf), (len))
-#define file_eof gzeof
-
-#ifdef HAVE_GZCLEARERR
-#define file_clearerr gzclearerr
-#endif
-
-#else /* No zLib */
-
-#define file_open(path) ws_fopen(path, "rb")
-#define filed_open(fildes) fdopen(fildes, "rb")
-#define file_read(buf, count, file) fread((buf), (1), (count), (file))
-#define file_close fclose
-#define file_getc fgetc
-#define file_gets fgets
-#define file_eof feof
-/* #define file_clearerr clearerr */
+extern FILE_T filed_open(int fildes);
+extern int file_read(void *buf, unsigned int count, FILE_T file);
+extern int file_close(FILE_T file);
+extern int file_getc(FILE_T stream);
+extern char *file_gets(char *buf, int len, FILE_T stream);
+extern int file_eof(FILE_T stream);
+extern void file_clearerr(FILE_T stream);
-#endif /* HAVE_LIBZ */
#endif /* __FILE_H__ */
diff --git a/wiretap/wtap-int.h b/wiretap/wtap-int.h
index ac206b06eb..feab2df258 100644
--- a/wiretap/wtap-int.h
+++ b/wiretap/wtap-int.h
@@ -37,11 +37,38 @@
#ifdef HAVE_LIBZ
#include <zlib.h>
-#define FILE_T gzFile
+#define WFILE_T gzFile
#else /* No zLib */
-#define FILE_T FILE *
+#define WFILE_T FILE *
#endif /* HAVE_LIBZ */
+typedef struct {
+ int fd; /* file descriptor */
+ gint64 pos; /* current position in uncompressed data */
+ unsigned size; /* buffer size */
+ unsigned char *in; /* input buffer */
+ unsigned char *out; /* output buffer (double-sized when reading) */
+ unsigned char *next; /* next output data to deliver or write */
+
+ unsigned have; /* amount of output data unused at next */
+ int eof; /* true if end of input file reached */
+ gint64 start; /* where the gzip data started, for rewinding */
+ gint64 raw; /* where the raw data started, for seeking */
+ int compression; /* 0: ?, 1: uncompressed, 2: zlib */
+ /* seek request */
+ gint64 skip; /* amount to skip (already rewound if backwards) */
+ int seek; /* true if seek request pending */
+ /* error information */
+ int err; /* error code */
+
+ unsigned int avail_in; /* number of bytes available at next_in */
+ unsigned char *next_in; /* next input byte */
+#ifdef HAVE_LIBZ
+ /* zlib inflate stream */
+ z_stream strm; /* stream structure in-place (not a pointer) */
+#endif
+} wtap_reader, *FILE_T;
+
#include "wtap.h"
typedef gboolean (*subtype_read_func)(struct wtap*, int*, char**, gint64*);
@@ -83,7 +110,7 @@ typedef gboolean (*subtype_write_func)(struct wtap_dumper*,
typedef gboolean (*subtype_close_func)(struct wtap_dumper*, int*);
struct wtap_dumper {
- FILE* fh;
+ WFILE_T fh;
int file_type;
int snaplen;
int encap;
diff --git a/wiretap/wtap.c b/wiretap/wtap.c
index cdf2cd8d10..80d5a204fa 100644
--- a/wiretap/wtap.c
+++ b/wiretap/wtap.c
@@ -661,11 +661,9 @@ wtap_close(wtap *wth)
void
wtap_cleareof(wtap *wth _U_) {
-#ifdef file_clearerr
/* Reset EOF */
if (file_eof(wth->fh))
file_clearerr(wth->fh);
-#endif
}
void wtap_set_cb_new_ipv4(wtap *wth, wtap_new_ipv4_callback_t add_new_ipv4) {