From Jakub Zawadzki:
authorGuy Harris <guy@alum.mit.edu>
Fri, 8 Apr 2011 00:28:37 +0000 (00:28 -0000)
committerGuy Harris <guy@alum.mit.edu>
Fri, 8 Apr 2011 00:28:37 +0000 (00:28 -0000)
Steal file_wrappers functions from zlib v2.

svn path=/trunk/; revision=36513

wiretap/file_access.c
wiretap/file_wrappers.c
wiretap/file_wrappers.h
wiretap/wtap-int.h
wiretap/wtap.c

index 60a4eac110fcdf13ad34202c4bb649d34055e26f..9ac254a27f0b3ad7ce1abe23a6bb073fac78b72d 100644 (file)
@@ -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");
 }
index b8682ff205ff433146868b9ffcb7a5cfafb9a5e8..fce6d1b9a78e3e2e841716134541eb2b638e8d25 100644 (file)
  * 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 */
index 96e0c3482e50e7f5c98e7f274cd863bba88b9067..249de02a2efd0f9b21ee0a3a7a662ee512074c75 100644 (file)
 #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__ */
index ac206b06ebd3bb4b1e34342d64287d21decd0f70..feab2df258df44612ca17131375cb991cd1faf64 100644 (file)
 
 #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;
index cdf2cd8d1054902196cd6a9f21f8a98aaab152f4..80d5a204fa2be85117dd24eb3f632541d6a2f513 100644 (file)
@@ -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) {