From Lars Roland: add routines to use when creating statistics windows
[obnox/wireshark/wip.git] / capture.c
index 44e2cd7853af9658006249d1c06d7225c50fda0c..fffc3c2949b773cec5c5c6fe159c84bbe9ba77e1 100644 (file)
--- a/capture.c
+++ b/capture.c
@@ -1,23 +1,22 @@
 /* capture.c
  * Routines for packet capture windows
  *
- * $Id: capture.c,v 1.138 2001/02/10 09:08:14 guy Exp $
+ * $Id: capture.c,v 1.206 2003/04/24 09:07:36 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
  *
- * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #ifdef HAVE_LIBPCAP
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
 #ifdef HAVE_SYS_STAT_H
 # include <sys/stat.h>
 #endif
  * XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't
  * want to include it if it's not present on this platform, however.
  */
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__)
 #ifndef BSD
 #define BSD
 #endif /* BSD */
-#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) */
 
 /*
  * We don't want to do a "select()" on the pcap_t's file descriptor on
 # define MUST_DO_SELECT
 #endif
 
-#include "gtk/main.h"
-#include "gtk/gtkglobals.h"
-#include "packet.h"
+#include <epan/packet.h>
 #include "file.h"
 #include "capture.h"
 #include "util.h"
+#include "pcap-util.h"
 #include "simple_dialog.h"
 #include "prefs.h"
 #include "globals.h"
+#include "conditions.h"
+#include "capture_stop_conditions.h"
+#include "ringbuffer.h"
 
 #include "wiretap/libpcap.h"
 #include "wiretap/wtap.h"
+#include "wiretap/wtap-capture.h"
 
+#include "packet-atalk.h"
+#include "packet-atm.h"
 #include "packet-clip.h"
 #include "packet-eth.h"
 #include "packet-fddi.h"
 #include "packet-sll.h"
 #include "packet-tr.h"
 #include "packet-ieee80211.h"
+#include "packet-chdlc.h"
+#include "packet-prism.h"
+#include "packet-ipfc.h"
+#include "packet-arcnet.h"
+
+#ifdef _WIN32
+#include "capture-wpcap.h"
+#endif
+
+/*
+ * Capture options.
+ */
+capture_options capture_opts;
 
-int promisc_mode = TRUE; /* capture in promiscuous mode */
-int sync_mode; /* fork a child to do the capture, and sync between them */
 static int sync_pipe[2]; /* used to sync father */
 enum PIPES { READ, WRITE }; /* Constants 0 and 1 for READ and WRITE */
 int quit_after_cap; /* Makes a "capture only mode". Implies -k */
 gboolean capture_child;        /* if this is the child for "-S" */
-static int fork_child; /* In parent, process ID of child */
+static int fork_child = -1;    /* If not -1, in parent, process ID of child */
 static guint cap_input_id;
 
+/*
+ * Indications sent out on the sync pipe.
+ */
+#define SP_CAPSTART    ';'     /* capture start message */
+#define SP_PACKET_COUNT        '*'     /* followed by count of packets captured since last message */
+#define SP_ERROR_MSG   '!'     /* followed by length of error message that follows */
+#define SP_DROPS       '#'     /* followed by count of packets dropped in capture */
+
 #ifdef _WIN32
 static guint cap_timer_id;
 static int cap_timer_cb(gpointer); /* Win32 kludge to check for pipe input */
@@ -180,30 +199,42 @@ static char *signame(int);
 #endif
 static void capture_delete_cb(GtkWidget *, GdkEvent *, gpointer);
 static void capture_stop_cb(GtkWidget *, gpointer);
-static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
-  const u_char *);
-static void show_capture_file_io_error(const char *, int, gboolean);
+static void capture_pcap_cb(guchar *, const struct pcap_pkthdr *,
+  const guchar *);
+static void get_capture_file_io_error(char *, int, const char *, int, gboolean);
+static void popup_errmsg(const char *);
 static void send_errmsg_to_parent(const char *);
 static float pct(gint, gint);
 static void stop_capture(int signo);
 
 typedef struct _loop_data {
-  gint           go;           /* TRUE as long as we're supposed to keep capturing */
+  gboolean       go;           /* TRUE as long as we're supposed to keep capturing */
   gint           max;          /* Number of packets we're supposed to capture - 0 means infinite */
   int            err;          /* if non-zero, error seen while capturing */
   gint           linktype;
   gint           sync_packets;
+  gboolean       pcap_err;     /* TRUE if error from pcap */
   gboolean       from_pipe;    /* TRUE if we are capturing data from a pipe */
-  gboolean       modified;     /* TRUE if data in the pipe uses modified pcap headers */
-  gboolean       byte_swapped; /* TRUE if data in the pipe is byte swapped */
   packet_counts  counts;
   wtap_dumper   *pdh;
+#ifndef _WIN32
+  gboolean       modified;     /* TRUE if data in the pipe uses modified pcap headers */
+  gboolean       byte_swapped; /* TRUE if data in the pipe is byte swapped */
+  unsigned int   bytes_to_read, bytes_read; /* Used by pipe_dispatch */
+  enum {
+         STATE_EXPECT_REC_HDR, STATE_READ_REC_HDR,
+         STATE_EXPECT_DATA,     STATE_READ_DATA
+       } pipe_state;
+
+  enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } pipe_err;
+#endif
 } loop_data;
 
 #ifndef _WIN32
 static void adjust_header(loop_data *, struct pcap_hdr *, struct pcaprec_hdr *);
-static int pipe_open_live(char *, struct pcap_hdr *, loop_data *, char *);
-static int pipe_dispatch(int, loop_data *, struct pcap_hdr *);
+static int pipe_open_live(char *, struct pcap_hdr *, loop_data *, char *, int);
+static int pipe_dispatch(int, loop_data *, struct pcap_hdr *, \
+               struct pcaprec_modified_hdr *, guchar *, char *, int);
 #endif
 
 /* Win32 needs the O_BINARY flag for open() */
@@ -258,20 +289,35 @@ quote_encapsulate(const char *string)
 /* Open a specified file, or create a temporary file, and start a capture
    to the file in question. */
 void
-do_capture(char *capfile_name)
+do_capture(const char *save_file)
 {
   char tmpname[128+1];
   gboolean is_tempfile;
-  u_char c;
+  guchar c;
   int i;
   guint byte_count;
   char *msg;
   int err;
   int capture_succeeded;
-
-  if (capfile_name != NULL) {
-    /* Try to open/create the specified file for use as a capture buffer. */
-    cfile.save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
+  gboolean stats_known;
+  struct pcap_stat stats;
+  gchar *capfile_name;
+
+  if (save_file != NULL) {
+    /* If the Sync option is set, we return to the caller while the capture
+     * is in progress.  Therefore we need to take a copy of save_file in
+     * case the caller destroys it after we return.
+     */
+    capfile_name = g_strdup(save_file);
+    if (capture_opts.ringbuffer_on) {
+      /* ringbuffer is enabled */
+      cfile.save_file_fd = ringbuf_init(capfile_name,
+                                       capture_opts.ringbuffer_num_files);
+    } else {
+      /* Try to open/create the specified file for use as a capture buffer. */
+      cfile.save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
+                               0600);
+    }
     is_tempfile = FALSE;
   } else {
     /* Choose a random name for the capture buffer */
@@ -285,18 +331,26 @@ do_capture(char *capfile_name)
        "The temporary file to which the capture would be saved (\"%s\")"
        "could not be opened: %s.", capfile_name, strerror(errno));
     } else {
+      if (capture_opts.ringbuffer_on) {
+        ringbuf_error_cleanup();
+      }
       simple_dialog(ESD_TYPE_CRIT, NULL,
-       file_open_error_message(errno, TRUE), capfile_name);
+       file_open_error_message(errno, TRUE, WTAP_FILE_PCAP), capfile_name);
     }
+    g_free(capfile_name);
     return;
   }
-  close_cap_file(&cfile, info_bar);
+  close_cap_file(&cfile);
   g_assert(cfile.save_file == NULL);
   cfile.save_file = capfile_name;
+  /* cfile.save_file is "g_free"ed below, which is equivalent to
+     "g_free(capfile_name)". */
 
-  if (sync_mode) {     /* do the capture in a child process */
+  if (capture_opts.sync_mode) {        /* do the capture in a child process */
     char ssnap[24];
-    char scount[24];   /* need a constant for len of numbers */
+    char scount[24];                   /* need a constant for len of numbers */
+    char sautostop_filesize[24];       /* need a constant for len of numbers */
+    char sautostop_duration[24];       /* need a constant for len of numbers */
     char save_file_fd[24];
     char errmsg[1024+1];
     int error;
@@ -327,15 +381,31 @@ do_capture(char *capfile_name)
     sprintf(save_file_fd,"%d",cfile.save_file_fd);     /* in lieu of itoa */
     argv = add_arg(argv, &argc, save_file_fd);
 
-    argv = add_arg(argv, &argc, "-c");
-    sprintf(scount,"%d",cfile.count);
-    argv = add_arg(argv, &argc, scount);
+    if (capture_opts.has_autostop_count) {
+      argv = add_arg(argv, &argc, "-c");
+      sprintf(scount,"%d",capture_opts.autostop_count);
+      argv = add_arg(argv, &argc, scount);
+    }
 
-    argv = add_arg(argv, &argc, "-s");
-    sprintf(ssnap,"%d",cfile.snap);
-    argv = add_arg(argv, &argc, ssnap);
+    if (capture_opts.has_snaplen) {
+      argv = add_arg(argv, &argc, "-s");
+      sprintf(ssnap,"%d",capture_opts.snaplen);
+      argv = add_arg(argv, &argc, ssnap);
+    }
 
-    if (!promisc_mode)
+    if (capture_opts.has_autostop_filesize) {
+      argv = add_arg(argv, &argc, "-a");
+      sprintf(sautostop_filesize,"filesize:%d",capture_opts.autostop_filesize);
+      argv = add_arg(argv, &argc, sautostop_filesize);
+    }
+
+    if (capture_opts.has_autostop_duration) {
+      argv = add_arg(argv, &argc, "-a");
+      sprintf(sautostop_duration,"duration:%d",capture_opts.autostop_duration);
+      argv = add_arg(argv, &argc, sautostop_duration);
+    }
+
+    if (!capture_opts.promisc_mode)
       argv = add_arg(argv, &argc, "-p");
 
 #ifdef _WIN32
@@ -363,6 +433,7 @@ do_capture(char *capfile_name)
     argv = add_arg(argv, &argc, sync_pipe_fd);
 
     /* Convert filter string to a quote delimited string and pass to child */
+    filterstring = NULL;
     if (cfile.cfilter != NULL && strlen(cfile.cfilter) != 0) {
       argv = add_arg(argv, &argc, "-f");
       filterstring = quote_encapsulate(cfile.cfilter);
@@ -372,11 +443,12 @@ do_capture(char *capfile_name)
     /* Spawn process */
     fork_child = spawnvp(_P_NOWAIT, ethereal_path, argv);
     g_free(fontstring);
-    g_free(filterstring);
+    if (filterstring) {
+      g_free(filterstring);
+    }
     /* Keep a copy for later evaluation by _cwait() */
     child_process = fork_child;
 #else
-    signal(SIGCHLD, SIG_IGN);
     if (pipe(sync_pipe) < 0) {
       /* Couldn't create the pipe between parent and child. */
       error = errno;
@@ -471,7 +543,7 @@ do_capture(char *capfile_name)
        wait_for_child(TRUE);
        return;
       }
-      if (c == ';')
+      if (c == SP_CAPSTART || c == SP_ERROR_MSG)
        break;
       if (!isdigit(c)) {
        /* Child process handed us crap.
@@ -487,7 +559,7 @@ do_capture(char *capfile_name)
       }
       byte_count = byte_count*10 + c - '0';
     }
-    if (byte_count == 0) {
+    if (c == SP_CAPSTART) {
       /* Success.  Open the capture file, and set up to read it. */
       err = start_tail_cap_file(cfile.save_file, is_tempfile, &cfile);
       if (err == 0) {
@@ -511,12 +583,9 @@ do_capture(char *capfile_name)
                                       NULL);
 #endif
       } else {
-       /* We weren't able to open the capture file; complain, and
-          close the sync pipe. */
-       simple_dialog(ESD_TYPE_CRIT, NULL,
-                       file_open_error_message(err, FALSE), cfile.save_file);
+       /* We weren't able to open the capture file; user has been
+          alerted. Close the sync pipe. */
 
-       /* Close the sync pipe. */
        close(sync_pipe[READ]);
 
        /* Don't unlink the save file - leave it around, for debugging
@@ -527,23 +596,30 @@ do_capture(char *capfile_name)
     } else {
       /* Failure - the child process sent us a message indicating
         what the problem was. */
-      msg = g_malloc(byte_count + 1);
-      if (msg == NULL) {
+      if (byte_count == 0) {
+       /* Zero-length message? */
        simple_dialog(ESD_TYPE_WARN, NULL,
-               "Capture child process failed, but its error message was too big.");
+               "Capture child process failed, but its error message was empty.");
       } else {
-       i = read(sync_pipe[READ], msg, byte_count);
-       if (i < 0) {
+       msg = g_malloc(byte_count + 1);
+       if (msg == NULL) {
          simple_dialog(ESD_TYPE_WARN, NULL,
+               "Capture child process failed, but its error message was too big.");
+       } else {
+         i = read(sync_pipe[READ], msg, byte_count);
+         msg[byte_count] = '\0';
+         if (i < 0) {
+           simple_dialog(ESD_TYPE_WARN, NULL,
                  "Capture child process failed: Error %s reading its error message.",
                  strerror(errno));
-       } else if (i == 0) {
-         simple_dialog(ESD_TYPE_WARN, NULL,
+         } else if (i == 0) {
+           simple_dialog(ESD_TYPE_WARN, NULL,
                  "Capture child process failed: EOF reading its error message.");
-         wait_for_child(FALSE);
-       } else
-         simple_dialog(ESD_TYPE_WARN, NULL, msg);
-       g_free(msg);
+           wait_for_child(FALSE);
+         } else
+           simple_dialog(ESD_TYPE_CRIT, NULL, msg);
+         g_free(msg);
+       }
 
        /* Close the sync pipe. */
        close(sync_pipe[READ]);
@@ -556,7 +632,7 @@ do_capture(char *capfile_name)
     }
   } else {
     /* Not sync mode. */
-    capture_succeeded = capture();
+    capture_succeeded = capture(&stats_known, &stats);
     if (quit_after_cap) {
       /* DON'T unlink the save file.  Presumably someone wants it. */
       gtk_exit(0);
@@ -566,6 +642,39 @@ do_capture(char *capfile_name)
       if ((err = open_cap_file(cfile.save_file, is_tempfile, &cfile)) == 0) {
         /* Set the read filter to NULL. */
         cfile.rfcode = NULL;
+
+        /* Get the packet-drop statistics.
+
+           XXX - there are currently no packet-drop statistics stored
+           in libpcap captures, and that's what we're reading.
+
+           At some point, we will add support in Wiretap to return
+          packet-drop statistics for capture file formats that store it,
+          and will make "read_cap_file()" get those statistics from
+          Wiretap.  We clear the statistics (marking them as "not known")
+          in "open_cap_file()", and "read_cap_file()" will only fetch
+          them and mark them as known if Wiretap supplies them, so if
+          we get the statistics now, after calling "open_cap_file()" but
+          before calling "read_cap_file()", the values we store will
+          be used by "read_cap_file()".
+
+           If a future libpcap capture file format stores the statistics,
+           we'll put them into the capture file that we write, and will
+          thus not have to set them here - "read_cap_file()" will get
+          them from the file and use them. */
+        if (stats_known) {
+          cfile.drops_known = TRUE;
+
+          /* XXX - on some systems, libpcap doesn't bother filling in
+             "ps_ifdrop" - it doesn't even set it to zero - so we don't
+             bother looking at it.
+
+             Ideally, libpcap would have an interface that gave us
+             several statistics - perhaps including various interface
+             error statistics - and would tell us which of them it
+             supplies, allowing us to display only the ones it does. */
+          cfile.drops = stats.ps_drop;
+        }
         switch (read_cap_file(&cfile, &err)) {
 
         case READ_SUCCESS:
@@ -578,14 +687,19 @@ do_capture(char *capfile_name)
         case READ_ABORTED:
           /* Exit by leaving the main loop, so that any quit functions
              we registered get called. */
-          gtk_main_quit();
+          if (gtk_main_level() > 0)
+            gtk_main_quit();
           return;
         }
       }
     }
     /* We're not doing a capture any more, so we don't have a save
        file. */
-    g_free(cfile.save_file);
+    if (capture_opts.ringbuffer_on) {
+      ringbuf_free();
+    } else {
+      g_free(cfile.save_file);
+    }
     cfile.save_file = NULL;
   }
 }
@@ -633,14 +747,15 @@ cap_timer_cb(gpointer data)
 /* There's stuff to read from the sync pipe, meaning the child has sent
    us a message, or the sync pipe has closed, meaning the child has
    closed it (perhaps because it exited). */
-static void 
-cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
+static void
+cap_file_input_cb(gpointer data, gint source _U_,
+  GdkInputCondition condition _U_)
 {
   capture_file *cf = (capture_file *)data;
-  char buffer[256+1], *p = buffer, *q = buffer;
-  int  nread;
+#define BUFSIZE        4096
+  char buffer[BUFSIZE+1], *p = buffer, *q = buffer, *msg, *r;
+  int  nread, msglen, chars_to_copy;
   int  to_read = 0;
-  gboolean exit_loop = FALSE;
   int  err;
 
 #ifndef _WIN32
@@ -648,12 +763,12 @@ cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
   gtk_input_remove(cap_input_id);
 #endif
 
-  if ((nread = read(sync_pipe[READ], buffer, 256)) <= 0) {
+  if ((nread = read(sync_pipe[READ], buffer, BUFSIZE)) <= 0) {
     /* The child has closed the sync pipe, meaning it's not going to be
        capturing any more packets.  Pick up its exit status, and
        complain if it did anything other than exit with status 0. */
     wait_for_child(FALSE);
-      
+
     /* Read what remains of the capture file, and finish the capture.
        XXX - do something if this fails? */
     switch (finish_tail_cap_file(cf, &err)) {
@@ -682,22 +797,57 @@ cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
 
   buffer[nread] = '\0';
 
-  while(!exit_loop) {
-    /* look for (possibly multiple) '*' */
+  while (nread != 0) {
+    /* look for (possibly multiple) indications */
     switch (*q) {
-    case '*' :
+    case SP_PACKET_COUNT :
       to_read += atoi(p);
-      p = q + 1; 
+      p = q + 1;
       q++;
+      nread--;
       break;
-    case '\0' :
-      /* XXX should handle the case of a pipe full (i.e. no star found) */
-      exit_loop = TRUE;
+    case SP_DROPS :
+      cf->drops_known = TRUE;
+      cf->drops = atoi(p);
+      p = q + 1;
+      q++;
+      nread--;
+      break;
+    case SP_ERROR_MSG :
+      msglen = atoi(p);
+      p = q + 1;
+      q++;
+      nread--;
+
+      /* Read the entire message.
+         XXX - if the child hasn't sent it all yet, this could cause us
+         to hang until they do. */
+      msg = g_malloc(msglen + 1);
+      r = msg;
+      while (msglen != 0) {
+       if (nread == 0) {
+         /* Read more. */
+          if ((nread = read(sync_pipe[READ], buffer, BUFSIZE)) <= 0)
+            break;
+          p = buffer;
+          q = buffer;
+        }
+       chars_to_copy = MIN(msglen, nread);
+        memcpy(r, q, chars_to_copy);
+        r += chars_to_copy;
+        q += chars_to_copy;
+        nread -= chars_to_copy;
+        msglen -= chars_to_copy;
+      }
+      *r = '\0';
+      simple_dialog(ESD_TYPE_CRIT, NULL, msg);
+      g_free(msg);
       break;
     default :
       q++;
+      nread--;
       break;
-    } 
+    }
   }
 
   /* Read from the capture file the number of records the child told us
@@ -777,6 +927,9 @@ wait_for_child(gboolean always_report)
                    "Child capture process died: wait status %#o", wstatus);
     }
   }
+
+  /* No more child process. */
+  fork_child = -1;
 #endif
 }
 
@@ -829,7 +982,7 @@ signame(int sig)
     sigmsg = "Segmentation violation";
     break;
 
-  /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO 
+  /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO
      Linux is POSIX compliant.  These are not POSIX-defined signals ---
      ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez:
 
@@ -880,7 +1033,7 @@ signame(int sig)
 #define        CAP_READ_TIMEOUT        250
 
 #ifndef _WIN32
-/* Take carre of byte order in the libpcap headers read from pipes.
+/* Take care of byte order in the libpcap headers read from pipes.
  * (function taken from wiretap/libpcap.c) */
 static void
 adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
@@ -911,29 +1064,91 @@ adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
   }
 }
 
-/* Mimic pcap_open_live() for pipe captures 
+/* Mimic pcap_open_live() for pipe captures
  * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the
  * header.
  * N.B. : we can't read the libpcap formats used in RedHat 6.1 or SuSE 6.3
  * because we can't seek on pipes (see wiretap/libpcap.c for details) */
 static int
-pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, char *ebuf)
+pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld,
+                 char *errmsg, int errmsgl)
 {
   struct stat pipe_stat;
   int         fd;
   guint32     magic;
-  int         bytes_read, b;
+  int         b, sel_ret;
+  unsigned int bytes_read;
+  fd_set      rfds;
+  struct timeval timeout;
 
-  if (strcmp(pipename, "-") == 0) fd = 0; /* read from stdin */
-  else if (stat(pipename, &pipe_stat) == 0 && S_ISFIFO(pipe_stat.st_mode)) {
-    if ((fd = open(pipename, O_RDONLY)) == -1) return -1;
-  } else return -1;
+  /*
+   * XXX Ethereal blocks until we return
+   */
+  if (strcmp(pipename, "-") == 0)
+    fd = 0; /* read from stdin */
+  else {
+    if (stat(pipename, &pipe_stat) < 0) {
+      if (errno == ENOENT || errno == ENOTDIR)
+        ld->pipe_err = PIPNEXIST;
+      else {
+        snprintf(errmsg, errmsgl,
+          "The capture session could not be initiated "
+          "due to error on pipe: %s", strerror(errno));
+        ld->pipe_err = PIPERR;
+      }
+      return -1;
+    }
+    if (! S_ISFIFO(pipe_stat.st_mode)) {
+      if (S_ISCHR(pipe_stat.st_mode)) {
+        /*
+         * Assume the user specified an interface on a system where
+         * interfaces are in /dev.  Pretend we haven't seen it.
+         */
+         ld->pipe_err = PIPNEXIST;
+      } else {
+        snprintf(errmsg, errmsgl,
+            "The capture session could not be initiated because\n"
+            "\"%s\" is neither an interface nor a pipe", pipename);
+        ld->pipe_err = PIPERR;
+      }
+      return -1;
+    }
+    fd = open(pipename, O_RDONLY | O_NONBLOCK);
+    if (fd == -1) {
+      snprintf(errmsg, errmsgl,
+          "The capture session could not be initiated "
+          "due to error on pipe open: %s", strerror(errno));
+      ld->pipe_err = PIPERR;
+      return -1;
+    }
+  }
 
   ld->from_pipe = TRUE;
+
   /* read the pcap header */
-  if (read(fd, &magic, sizeof magic) != sizeof magic) {
-    close(fd);
-    return -1;
+  FD_ZERO(&rfds);
+  bytes_read = 0;
+  while (bytes_read < sizeof magic) {
+    FD_SET(fd, &rfds);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+    sel_ret = select(fd+1, &rfds, NULL, NULL, &timeout);
+    if (sel_ret < 0) {
+      snprintf(errmsg, errmsgl,
+        "Unexpected error from select: %s", strerror(errno));
+      goto error;
+    } else if (sel_ret > 0) {
+      b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read);
+      if (b <= 0) {
+        if (b == 0)
+          snprintf(errmsg, errmsgl, "End of file on pipe during open");
+        else
+          snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+            strerror(errno));
+        goto error;
+      }
+      bytes_read += b;
+    }
   }
 
   switch (magic) {
@@ -965,25 +1180,36 @@ pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, char *ebuf)
     break;
   default:
     /* Not a "libpcap" type we know about. */
-    close(fd);
-    return -1;
+    snprintf(errmsg, errmsgl, "Unrecognized libpcap format");
+    goto error;
   }
 
   /* Read the rest of the header */
-  bytes_read = read(fd, hdr, sizeof(struct pcap_hdr));
-  if (bytes_read <= 0) {
-    close(fd);
-    return -1;
-  }
-  while (bytes_read < sizeof(struct pcap_hdr))
-  {
-    b = read(fd, ((char *)&hdr)+bytes_read, sizeof(struct pcap_hdr) - bytes_read);
-    if (b <= 0) {
-      close(fd);
-      return -1;
+  bytes_read = 0;
+  while (bytes_read < sizeof(struct pcap_hdr)) {
+    FD_SET(fd, &rfds);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+    sel_ret = select(fd+1, &rfds, NULL, NULL, &timeout);
+    if (sel_ret < 0) {
+      snprintf(errmsg, errmsgl,
+        "Unexpected error from select: %s", strerror(errno));
+      goto error;
+    } else if (sel_ret > 0) {
+      b = read(fd, ((char *)hdr)+bytes_read,
+            sizeof(struct pcap_hdr) - bytes_read);
+      if (b <= 0) {
+        if (b == 0)
+          snprintf(errmsg, errmsgl, "End of file on pipe during open");
+        else
+          snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+            strerror(errno));
+        goto error;
+      }
+      bytes_read += b;
     }
-    bytes_read += b;
   }
+
   if (ld->byte_swapped) {
     /* Byte-swap the header fields about which we care. */
     hdr->version_major = BSWAP16(hdr->version_major);
@@ -991,114 +1217,128 @@ pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, char *ebuf)
     hdr->snaplen = BSWAP32(hdr->snaplen);
     hdr->network = BSWAP32(hdr->network);
   }
+
   if (hdr->version_major < 2) {
-    close(fd);
-    return -1;
+    snprintf(errmsg, errmsgl, "Unable to read old libpcap format");
+    goto error;
   }
 
+  ld->pipe_state = STATE_EXPECT_REC_HDR;
+  ld->pipe_err = PIPOK;
   return fd;
+
+error:
+  ld->pipe_err = PIPERR;
+  close(fd);
+  return -1;
+
 }
 
 /* We read one record from the pipe, take care of byte order in the record
  * header, write the record in the capture file, and update capture statistics. */
+
 static int
-pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr)
+pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr,
+               struct pcaprec_modified_hdr *rechdr, guchar *data,
+               char *errmsg, int errmsgl)
 {
-  struct wtap_pkthdr whdr;
-  struct pcaprec_modified_hdr rechdr;
-  int bytes_to_read, bytes_read, b;
-  u_char pd[WTAP_MAX_PACKET_SIZE];
-  int err;
-
-  /* read the record header */
-  bytes_to_read = ld->modified ? sizeof rechdr : sizeof rechdr.hdr;
-  bytes_read = read(fd, &rechdr, bytes_to_read);
-  if (bytes_read <= 0) {
-    close(fd);
-    ld->go = FALSE;
-    return 0;
-  }
-  while (bytes_read < bytes_to_read)
-  {
-    b = read(fd, ((char *)&rechdr)+bytes_read, bytes_to_read - bytes_read);
+  struct pcap_pkthdr phdr;
+  int b;
+  enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
+          PD_ERR } result;
+
+  switch (ld->pipe_state) {
+
+  case STATE_EXPECT_REC_HDR:
+    ld->bytes_to_read = ld->modified ?
+      sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
+    ld->bytes_read = 0;
+    ld->pipe_state = STATE_READ_REC_HDR;
+    /* Fall through */
+
+  case STATE_READ_REC_HDR:
+    b = read(fd, ((char *)rechdr)+ld->bytes_read,
+      ld->bytes_to_read - ld->bytes_read);
     if (b <= 0) {
-      close(fd);
-      ld->go = FALSE;
-      return 0;
+      if (b == 0)
+        result = PD_PIPE_EOF;
+      else
+        result = PD_PIPE_ERR;
+      break;
     }
-    bytes_read += b;
-  }
-  /* take care of byte order */
-  adjust_header(ld, hdr, &rechdr.hdr);
-  if (rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
-    close(fd);
-    ld->go = FALSE;
-    return 0;
-  }
-  /* read the packet data */
-  bytes_read = read(fd, pd, rechdr.hdr.incl_len);
-  if (bytes_read <= 0) {
-    close(fd);
-    ld->go = FALSE;
-    return 0;
-  }
-  while (bytes_read < rechdr.hdr.incl_len)
-  {
-    b = read(fd, pd+bytes_read, rechdr.hdr.incl_len - bytes_read);
+    if ((ld->bytes_read += b) < ld->bytes_to_read)
+        return 0;
+    result = PD_REC_HDR_READ;
+    break;
+
+  case STATE_EXPECT_DATA:
+    ld->bytes_read = 0;
+    ld->pipe_state = STATE_READ_DATA;
+    /* Fall through */
+
+  case STATE_READ_DATA:
+    b = read(fd, data+ld->bytes_read, rechdr->hdr.incl_len - ld->bytes_read);
     if (b <= 0) {
-      close(fd);
-      ld->go = FALSE;
-      return 0;
-    }
-    bytes_read += b;
-  }
-  /* dump the packet data to the capture file */
-  whdr.ts.tv_sec = rechdr.hdr.ts_sec;
-  whdr.ts.tv_usec = rechdr.hdr.ts_usec;
-  whdr.caplen = rechdr.hdr.incl_len;
-  whdr.len = rechdr.hdr.orig_len;
-  whdr.pkt_encap = ld->linktype;
-  wtap_dump(ld->pdh, &whdr, NULL, pd, &err);
-
-  /* Set the initial payload to the packet length, and the initial
-     captured payload to the capture length (other protocols may
-     reduce them if their headers say they're less). */
-  pi.len = whdr.len;
-  pi.captured_len = whdr.caplen;
-    
-  /* update capture statistics */
-  switch (ld->linktype) {
-    case WTAP_ENCAP_ETHERNET:
-      capture_eth(pd, 0, &ld->counts);
-      break;
-    case WTAP_ENCAP_FDDI:
-    case WTAP_ENCAP_FDDI_BITSWAPPED:
-      capture_fddi(pd, &ld->counts);
-      break;
-    case WTAP_ENCAP_TOKEN_RING:
-      capture_tr(pd, 0, &ld->counts);
-      break;
-    case WTAP_ENCAP_NULL:
-      capture_null(pd, &ld->counts);
-      break;
-    case WTAP_ENCAP_PPP:
-      capture_ppp(pd, 0, &ld->counts);
-      break;
-    case WTAP_ENCAP_RAW_IP:
-      capture_raw(pd, &ld->counts);
-      break;
-    case WTAP_ENCAP_LINUX_ATM_CLIP:
-      capture_clip(pd, &ld->counts);
+      if (b == 0)
+        result = PD_PIPE_EOF;
+      else
+        result = PD_PIPE_ERR;
       break;
-    case WTAP_ENCAP_IEEE_802_11:
-      capture_ieee80211(pd,0,&ld->counts);
+    }
+    if ((ld->bytes_read += b) < rechdr->hdr.incl_len)
+      return 0;
+    result = PD_DATA_READ;
+    break;
+
+  default:
+    snprintf(errmsg, errmsgl, "pipe_dispatch: invalid state");
+    result = PD_ERR;
+
+  } /* switch (ld->pipe_state) */
+
+  /*
+   * We've now read as much data as we were expecting, so process it.
+   */
+  switch (result) {
+
+  case PD_REC_HDR_READ:
+    /* We've read the header. Take care of byte order. */
+    adjust_header(ld, hdr, &rechdr->hdr);
+    if (rechdr->hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+      snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)",
+        ld->counts.total+1, rechdr->hdr.incl_len);
       break;
-    /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
-       with LLC header following; we should implement it at some
-       point. */
+    }
+    ld->pipe_state = STATE_EXPECT_DATA;
+    return 0;
+
+  case PD_DATA_READ:
+    /* Fill in a "struct pcap_pkthdr", and process the packet. */
+    phdr.ts.tv_sec = rechdr->hdr.ts_sec;
+    phdr.ts.tv_usec = rechdr->hdr.ts_usec;
+    phdr.caplen = rechdr->hdr.incl_len;
+    phdr.len = rechdr->hdr.orig_len;
+
+    capture_pcap_cb((guchar *)ld, &phdr, data);
+
+    ld->pipe_state = STATE_EXPECT_REC_HDR;
+    return 1;
+
+  case PD_PIPE_EOF:
+    ld->pipe_err = PIPEOF;
+    return -1;
+
+  case PD_PIPE_ERR:
+    snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
+      strerror(errno));
+    /* Fall through */
+  case PD_ERR:
+    break;
   }
 
-  return 1;
+  ld->pipe_err = PIPERR;
+  /* Return here rather than inside the switch to prevent GCC warning */
+  return -1;
 }
 #endif
 
@@ -1111,73 +1351,93 @@ static loop_data   ld;
 /* Do the low-level work of a capture.
    Returns TRUE if it succeeds, FALSE otherwise. */
 int
-capture(void)
+capture(gboolean *stats_known, struct pcap_stat *stats)
 {
   GtkWidget  *cap_w, *main_vb, *stop_bt, *counts_tb;
+  GtkWidget  *counts_fr, *running_tb, *running_label, *running_time;
   pcap_t     *pch;
   int         pcap_encap;
-  int         snaplen;
-  gchar       err_str[PCAP_ERRBUF_SIZE], label_str[64];
+  int         file_snaplen;
+  gchar       open_err_str[PCAP_ERRBUF_SIZE];
+  gchar       lookup_net_err_str[PCAP_ERRBUF_SIZE];
+  gchar       label_str[64];
   bpf_u_int32 netnum, netmask;
   struct bpf_program fcode;
   time_t      upd_time, cur_time;
-  int         err, inpkts, i;
+  time_t      start_time;
+  int         err, inpkts;
+  condition  *cnd_stop_capturesize = NULL;
+  condition  *cnd_stop_timeout = NULL;
+  unsigned int i;
+  static const char capstart_msg = SP_CAPSTART;
   char        errmsg[4096+1];
-#ifndef _WIN32
-  static const char ppamsg[] = "can't find PPA for ";
-  char       *libpcap_warn;
-#endif
+  gboolean    write_ok;
+  gboolean    close_ok;
   fd_set      set1;
   struct timeval timeout;
-#ifdef MUST_DO_SELECT
-  int         pcap_fd = 0;
-#endif
-#ifdef _WIN32 
-  WORD wVersionRequested; 
-  WSADATA wsaData; 
-#endif
-#ifndef _WIN32
-  int         pipe_fd = -1;
-  struct pcap_hdr hdr;
-#endif
   struct {
       const gchar *title;
       gint *value_ptr;
       GtkWidget *label, *value, *percent;
-  } stats[] = {
-      { "Total", &ld.counts.total },
-      { "SCTP", &ld.counts.sctp },
-      { "TCP", &ld.counts.tcp },
-      { "UDP", &ld.counts.udp },
-      { "ICMP", &ld.counts.icmp },
-      { "OSPF", &ld.counts.ospf },
-      { "GRE", &ld.counts.gre },
-      { "NetBIOS", &ld.counts.netbios },
-      { "IPX", &ld.counts.ipx },
-      { "VINES", &ld.counts.vines },
-      { "Other", &ld.counts.other }
+  } counts[] = {
+      { "Total", &ld.counts.total, NULL, NULL, NULL },
+      { "SCTP", &ld.counts.sctp, NULL, NULL, NULL },
+      { "TCP", &ld.counts.tcp, NULL, NULL, NULL },
+      { "UDP", &ld.counts.udp, NULL, NULL, NULL },
+      { "ICMP", &ld.counts.icmp, NULL, NULL, NULL },
+      { "ARP", &ld.counts.arp, NULL, NULL, NULL },
+      { "OSPF", &ld.counts.ospf, NULL, NULL, NULL },
+      { "GRE", &ld.counts.gre, NULL, NULL, NULL },
+      { "NetBIOS", &ld.counts.netbios, NULL, NULL, NULL },
+      { "IPX", &ld.counts.ipx, NULL, NULL, NULL },
+      { "VINES", &ld.counts.vines, NULL, NULL, NULL },
+      { "Other", &ld.counts.other, NULL, NULL, NULL }
   };
 
-#define N_STATS (sizeof stats / sizeof stats[0])
+#define N_COUNTS (sizeof counts / sizeof counts[0])
+
+#ifdef _WIN32
+  WORD wVersionRequested;
+  WSADATA wsaData;
+#else
+  static const char ppamsg[] = "can't find PPA for ";
+  char       *libpcap_warn;
+  int         sel_ret;
+  int         pipe_fd = -1;
+  struct pcap_hdr hdr;
+  struct pcaprec_modified_hdr rechdr;
+  guchar pcap_data[WTAP_MAX_PACKET_SIZE];
+#endif
+#ifdef MUST_DO_SELECT
+  int         pcap_fd = 0;
+#endif
+
+/* Size of buffer to hold decimal representation of
+   signed/unsigned 64-bit int */
+#define DECISIZE 20
 
-  /* Initialize Windows Socket if we are in a WIN32 OS 
+  /* Initialize Windows Socket if we are in a WIN32 OS
      This needs to be done before querying the interface for network/netmask */
-#ifdef _WIN32 
-  wVersionRequested = MAKEWORD( 1, 1 ); 
-  err = WSAStartup( wVersionRequested, &wsaData ); 
-  if (err!=0) { 
-    snprintf(errmsg, sizeof errmsg, 
-      "Couldn't initialize Windows Sockets."); 
-       pch=NULL; 
-    goto error; 
-  } 
-#endif 
+#ifdef _WIN32
+  wVersionRequested = MAKEWORD( 1, 1 );
+  err = WSAStartup( wVersionRequested, &wsaData );
+  if (err!=0) {
+    snprintf(errmsg, sizeof errmsg,
+      "Couldn't initialize Windows Sockets.");
+       pch=NULL;
+    goto error;
+  }
+#endif
 
   ld.go             = TRUE;
   ld.counts.total   = 0;
-  ld.max            = cfile.count;
+  if (capture_opts.has_autostop_count)
+    ld.max          = capture_opts.autostop_count;
+  else
+    ld.max          = 0;       /* no limit */
   ld.err            = 0;       /* no error seen yet */
   ld.linktype       = WTAP_ENCAP_UNKNOWN;
+  ld.pcap_err       = FALSE;
   ld.from_pipe      = FALSE;
   ld.sync_packets   = 0;
   ld.counts.sctp    = 0;
@@ -1190,15 +1450,27 @@ capture(void)
   ld.counts.netbios = 0;
   ld.counts.vines   = 0;
   ld.counts.other   = 0;
+  ld.counts.arp     = 0;
   ld.pdh            = NULL;
 
-  /* Open the network interface to capture from it. */
-  pch = pcap_open_live(cfile.iface, cfile.snap, promisc_mode,
-                       CAP_READ_TIMEOUT, err_str);
+  /* We haven't yet gotten the capture statistics. */
+  *stats_known      = FALSE;
+
+  /* Open the network interface to capture from it.
+     Some versions of libpcap may put warnings into the error buffer
+     if they succeed; to tell if that's happened, we have to clear
+     the error buffer, and check if it's still a null string.  */
+  open_err_str[0] = '\0';
+  pch = pcap_open_live(cfile.iface,
+                      capture_opts.has_snaplen ? capture_opts.snaplen :
+                                                 WTAP_MAX_PACKET_SIZE,
+                      capture_opts.promisc_mode, CAP_READ_TIMEOUT,
+                      open_err_str);
 
   if (pch == NULL) {
+    /* We couldn't open "cfile.iface" as a network device. */
 #ifdef _WIN32
-    /* Well, we couldn't start the capture.
+    /* On Windows, we don't support capturing on pipes, so we give up.
        If this is a child process that does the capturing in sync
        mode or fork mode, it shouldn't do any UI stuff until we pop up the
        capture-progress window, and, since we couldn't start the
@@ -1210,66 +1482,74 @@ capture(void)
     /* On Win32 OSes, the capture devices are probably available to all
        users; don't warn about permissions problems.
 
-       Do, however, warn that Token Ring and PPP devices aren't supported. */
+       Do, however, warn that WAN devices aren't supported. */
     snprintf(errmsg, sizeof errmsg,
        "The capture session could not be initiated (%s).\n"
        "Please check that you have the proper interface specified.\n"
        "\n"
-       "Note that the driver Ethereal uses for packet capture on Windows\n"
-       "doesn't support capturing on Token Ring interfaces, and doesn't\n"
-       "support capturing on PPP/WAN interfaces in Windows NT/2000.\n",
-       err_str);
+       "Note that the driver Ethereal uses for packet capture on Windows doesn't\n"
+       "support capturing on PPP/WAN interfaces in Windows NT/2000/XP/.NET Server.\n",
+       open_err_str);
     goto error;
 #else
     /* try to open cfile.iface as a pipe */
-    pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, err_str);
+    pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, errmsg, sizeof errmsg);
 
     if (pipe_fd == -1) {
-      /* Well, we couldn't start the capture.
-        If this is a child process that does the capturing in sync
-        mode or fork mode, it shouldn't do any UI stuff until we pop up the
-        capture-progress window, and, since we couldn't start the
-        capture, we haven't popped it up. */
+
+      /* If this is a child process that does the capturing in sync
+       * mode or fork mode, it shouldn't do any UI stuff until we pop up the
+       * capture-progress window, and, since we couldn't start the
+       * capture, we haven't popped it up.
+       */
       if (!capture_child) {
        while (gtk_events_pending()) gtk_main_iteration();
       }
 
-      /* If we got a "can't find PPA for XXX" message, warn the user (who
-         is running Ethereal on HP-UX) that they don't have a version
-        of libpcap patched to properly handle HP-UX (the patched version
-        says "can't find /dev/dlpi PPA for XXX" rather than "can't find
-        PPA for XXX"). */
-      if (strncmp(err_str, ppamsg, sizeof ppamsg - 1) == 0)
-       libpcap_warn =
-         "\n\n"
-         "You are running Ethereal with a version of the libpcap library\n"
-         "that doesn't handle HP-UX network devices well; this means that\n"
-         "Ethereal may not be able to capture packets.\n"
-         "\n"
-         "To fix this, you will need to download the source to Ethereal\n"
-         "from www.ethereal.com if you have not already done so, read\n"
-         "the instructions in the \"README.hpux\" file in the source\n"
-         "distribution, download the source to libpcap if you have not\n"
-         "already done so, patch libpcap as per the instructions, rebuild\n"
-         "and install libpcap, and then build Ethereal (if you have already\n"
-         "built Ethereal from source, do a \"make distclean\" and re-run\n"
-         "configure before building).";
-      else
-       libpcap_warn = "";
-      snprintf(errmsg, sizeof errmsg,
+      if (ld.pipe_err == PIPNEXIST) {
+       /* Pipe doesn't exist, so output message for interface */
+
+       /* If we got a "can't find PPA for XXX" message, warn the user (who
+          is running Ethereal on HP-UX) that they don't have a version
+          of libpcap that properly handles HP-UX (libpcap 0.6.x and later
+          versions, which properly handle HP-UX, say "can't find /dev/dlpi
+          PPA for XXX" rather than "can't find PPA for XXX"). */
+       if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
+         libpcap_warn =
+           "\n\n"
+           "You are running Ethereal with a version of the libpcap library\n"
+           "that doesn't handle HP-UX network devices well; this means that\n"
+           "Ethereal may not be able to capture packets.\n"
+           "\n"
+           "To fix this, you should install libpcap 0.6.2, or a later version\n"
+           "of libpcap, rather than libpcap 0.4 or 0.5.x.  It is available in\n"
+           "packaged binary form from the Software Porting And Archive Centre\n"
+           "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n"
+           "at the URL lists a number of mirror sites.";
+       else
+         libpcap_warn = "";
+       snprintf(errmsg, sizeof errmsg,
          "The capture session could not be initiated (%s).\n"
          "Please check to make sure you have sufficient permissions, and that\n"
-         "you have the proper interface or pipe specified.%s", err_str,
+         "you have the proper interface or pipe specified.%s", open_err_str,
          libpcap_warn);
+      }
+      /*
+       * Else pipe (or file) does exist and pipe_open_live() has
+       * filled in errmsg
+       */
       goto error;
-    }
+    } else
+      /* pipe_open_live() succeeded; don't want
+         error message from pcap_open_live() */
+      open_err_str[0] = '\0';
 #endif
   }
 
   /* capture filters only work on real interfaces */
   if (cfile.cfilter && !ld.from_pipe) {
     /* A capture filter was specified; set it up. */
-    if (pcap_lookupnet (cfile.iface, &netnum, &netmask, err_str) < 0) {
+    if (pcap_lookupnet(cfile.iface, &netnum, &netmask, lookup_net_err_str) < 0) {
       /*
        * Well, we can't get the netmask for this interface; it's used
        * only for filters that check for broadcast IP addresses, so
@@ -1297,12 +1577,12 @@ capture(void)
 #ifndef _WIN32
   if (ld.from_pipe) {
     pcap_encap = hdr.network;
-    snaplen = hdr.snaplen;
+    file_snaplen = hdr.snaplen;
   } else
 #endif
   {
-    pcap_encap = pcap_datalink(pch);
-    snaplen = pcap_snapshot(pch);
+    pcap_encap = get_pcap_linktype(pch, cfile.iface);
+    file_snaplen = pcap_snapshot(pch);
   }
   ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_encap);
   if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
@@ -1311,8 +1591,13 @@ capture(void)
        " that Ethereal doesn't support (data link type %d).", pcap_encap);
     goto error;
   }
-  ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
-      ld.linktype, snaplen, &err);
+  if (capture_opts.ringbuffer_on) {
+    ld.pdh = ringbuf_init_wtap_dump_fdopen(WTAP_FILE_PCAP, ld.linktype,
+      file_snaplen, &err);
+  } else {
+    ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
+      ld.linktype, file_snaplen, &err);
+  }
 
   if (ld.pdh == NULL) {
     /* We couldn't set up to write to the capture file. */
@@ -1345,6 +1630,11 @@ capture(void)
     goto error;
   }
 
+  /* Does "open_err_str" contain a non-empty string?  If so, "pcap_open_live()"
+     returned a warning; print it, but keep capturing. */
+  if (open_err_str[0] != '\0')
+    g_warning("%s.", open_err_str);
+
   /* XXX - capture SIGTERM and close the capture, in case we're on a
      Linux 2.0[.x] system and you have to explicitly close the capture
      stream in order to turn promiscuous mode off?  We need to do that
@@ -1361,7 +1651,7 @@ capture(void)
        update its windows to indicate that we have a live capture in
        progress. */
     fflush(wtap_dump_file(ld.pdh));
-    write(1, "0;", 2);
+    write(1, &capstart_msg, 1);
   }
 
   cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -1374,36 +1664,59 @@ capture(void)
   gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
   gtk_widget_show(main_vb);
 
+  counts_fr = gtk_frame_new("Captured Frames");
+  gtk_box_pack_start(GTK_BOX(main_vb), counts_fr, FALSE, FALSE, 3);
+  gtk_widget_show(counts_fr);
+
   /* Individual statistic elements */
-  counts_tb = gtk_table_new(N_STATS, 3, TRUE);
-  gtk_box_pack_start(GTK_BOX(main_vb), counts_tb, TRUE, TRUE, 3);
+  counts_tb = gtk_table_new(N_COUNTS, 3, TRUE);
+  gtk_container_add(GTK_CONTAINER(counts_fr), counts_tb);
+  gtk_container_border_width(GTK_CONTAINER(counts_tb), 5);
   gtk_widget_show(counts_tb);
 
-  for (i = 0; i < N_STATS; i++) {
-      stats[i].label = gtk_label_new(stats[i].title);
-      gtk_misc_set_alignment(GTK_MISC(stats[i].label), 0.0f, 0.0f);
+  for (i = 0; i < N_COUNTS; i++) {
+      counts[i].label = gtk_label_new(counts[i].title);
+      gtk_misc_set_alignment(GTK_MISC(counts[i].label), 0.0f, 0.0f);
 
-      stats[i].value = gtk_label_new("0");
-      gtk_misc_set_alignment(GTK_MISC(stats[i].value), 0.0f, 0.0f);
+      counts[i].value = gtk_label_new("0");
+      gtk_misc_set_alignment(GTK_MISC(counts[i].value), 0.0f, 0.0f);
 
-      stats[i].percent = gtk_label_new("0.0%");
-      gtk_misc_set_alignment(GTK_MISC(stats[i].percent), 0.0f, 0.0f);
+      counts[i].percent = gtk_label_new("0.0%");
+      gtk_misc_set_alignment(GTK_MISC(counts[i].percent), 0.0f, 0.0f);
 
       gtk_table_attach_defaults(GTK_TABLE(counts_tb),
-                                stats[i].label, 0, 1, i, i + 1);
+                                counts[i].label, 0, 1, i, i + 1);
 
       gtk_table_attach(GTK_TABLE(counts_tb),
-                       stats[i].value,
+                       counts[i].value,
                        1, 2, i, i + 1, 0, 0, 5, 0);
 
       gtk_table_attach_defaults(GTK_TABLE(counts_tb),
-                                stats[i].percent, 2, 3, i, i + 1);
+                                counts[i].percent, 2, 3, i, i + 1);
 
-      gtk_widget_show(stats[i].label);
-      gtk_widget_show(stats[i].value);
-      gtk_widget_show(stats[i].percent);
+      gtk_widget_show(counts[i].label);
+      gtk_widget_show(counts[i].value);
+      gtk_widget_show(counts[i].percent);
   }
 
+  /* Running time */
+  running_tb = gtk_table_new(1, 3, TRUE);
+  gtk_box_pack_start(GTK_BOX(main_vb), running_tb, FALSE, FALSE, 3);
+  gtk_widget_show(running_tb);
+
+  running_label = gtk_label_new("Running");
+  gtk_misc_set_alignment(GTK_MISC(running_label), 0.0f, 0.0f);
+  gtk_widget_show(running_label);
+  gtk_table_attach_defaults(GTK_TABLE(running_tb),
+                                running_label, 0, 1, 0, 1);
+
+  running_time = gtk_label_new("00:00:00");
+  gtk_misc_set_alignment(GTK_MISC(running_time), 0.0f, 0.0f);
+  gtk_widget_show(running_time);
+  gtk_table_attach(GTK_TABLE(running_tb),
+                       running_time,
+                       1, 2, 0, 1, 0, 0, 5, 0);
+
   /* allow user to either click a stop button, or the close button on
        the window to stop a capture in progress. */
   stop_bt = gtk_button_new_with_label ("Stop");
@@ -1411,7 +1724,7 @@ capture(void)
     GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
   gtk_signal_connect(GTK_OBJECT(cap_w), "delete_event",
        GTK_SIGNAL_FUNC(capture_delete_cb), (gpointer) &ld);
-  gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
+  gtk_box_pack_start(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
   GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
   gtk_widget_grab_default(stop_bt);
   GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
@@ -1420,6 +1733,7 @@ capture(void)
 
   gtk_widget_show(cap_w);
 
+  start_time = time(NULL);
   upd_time = time(NULL);
 #ifdef MUST_DO_SELECT
   if (!ld.from_pipe) pcap_fd = pcap_fileno(pch);
@@ -1430,8 +1744,19 @@ capture(void)
    * Catch SIGUSR1, so that we exit cleanly if the parent process
    * kills us with it due to the user selecting "Capture->Stop".
    */
-  signal(SIGUSR1, stop_capture);
+  if (capture_child)
+    signal(SIGUSR1, stop_capture);
 #endif
+  /* initialize capture stop conditions */
+  init_capture_stop_conditions();
+  /* create stop conditions */
+  if (capture_opts.has_autostop_filesize)
+    cnd_stop_capturesize =
+        cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts.autostop_filesize * 1000);
+  if (capture_opts.has_autostop_duration)
+    cnd_stop_timeout =
+        cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts.autostop_duration);
+
   while (ld.go) {
     while (gtk_events_pending()) gtk_main_iteration();
 
@@ -1441,16 +1766,25 @@ capture(void)
       FD_SET(pipe_fd, &set1);
       timeout.tv_sec = 0;
       timeout.tv_usec = CAP_READ_TIMEOUT*1000;
-      if (select(pipe_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+      sel_ret = select(pipe_fd+1, &set1, NULL, NULL, &timeout);
+      if (sel_ret <= 0) {
+       inpkts = 0;
+        if (sel_ret < 0 && errno != EINTR) {
+          snprintf(errmsg, sizeof(errmsg),
+            "Unexpected error from select: %s", strerror(errno));
+          popup_errmsg(errmsg);
+          ld.go = FALSE;
+        }
+      } else {
        /*
-        * "select()" says we can read from the pipe without blocking; go for
-        * it. We are not sure we can read a whole record, but at least the
-        * begninning of one. pipe_dispatch() will block reading the whole
-        * record.
+        * "select()" says we can read from the pipe without blocking
         */
-       inpkts = pipe_dispatch(pipe_fd, &ld, &hdr);
-      } else
-       inpkts = 0;
+       inpkts = pipe_dispatch(pipe_fd, &ld, &hdr, &rechdr, pcap_data,
+          errmsg, sizeof errmsg);
+       if (inpkts < 0) {
+         ld.go = FALSE;
+        }
+      }
     }
     else
 #endif
@@ -1487,83 +1821,182 @@ capture(void)
       FD_SET(pcap_fd, &set1);
       timeout.tv_sec = 0;
       timeout.tv_usec = CAP_READ_TIMEOUT*1000;
-      if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+      sel_ret = select(pcap_fd+1, &set1, NULL, NULL, &timeout);
+      if (sel_ret > 0) {
        /*
         * "select()" says we can read from it without blocking; go for
         * it.
         */
-       inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
-      } else
-       inpkts = 0;
+       inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (guchar *) &ld);
+       if (inpkts < 0) {
+         ld.pcap_err = TRUE;
+         ld.go = FALSE;
+       }
+      } else {
+        inpkts = 0;
+        if (sel_ret < 0 && errno != EINTR) {
+          snprintf(errmsg, sizeof(errmsg),
+            "Unexpected error from select: %s", strerror(errno));
+          popup_errmsg(errmsg);
+          ld.go = FALSE;
+        }
+      }
 #else
-      inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+      inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (guchar *) &ld);
+      if (inpkts < 0) {
+        ld.pcap_err = TRUE;
+        ld.go = FALSE;
+      }
 #endif
     }
-    if (inpkts > 0)
+
+    if (inpkts > 0) {
       ld.sync_packets += inpkts;
+      /* check capture stop conditons */
+      if (cnd_stop_capturesize != NULL && cnd_eval(cnd_stop_capturesize,
+                    (guint32)wtap_get_bytes_dumped(ld.pdh))){
+        /* Capture file reached its maximum size. */
+        if (capture_opts.ringbuffer_on) {
+          /* Switch to the next ringbuffer file */
+          if (ringbuf_switch_file(&cfile, &ld.pdh, &ld.err)) {
+            /* File switch succeeded: reset the condition */
+            cnd_reset(cnd_stop_capturesize);
+          } else {
+            /* File switch failed: stop here */
+            ld.go = FALSE;
+            continue;
+          }
+        } else {
+          /* no ringbuffer - just stop */
+          ld.go = FALSE;
+        }
+      }
+    }
+
     /* Only update once a second so as not to overload slow displays */
     cur_time = time(NULL);
     if (cur_time > upd_time) {
       upd_time = cur_time;
 
-      for (i = 0; i < N_STATS; i++) {
-          snprintf(label_str, sizeof(label_str), "%d",
-                   *stats[i].value_ptr);
+      /* calculate and display running time */
+      cur_time -= start_time;
+      snprintf(label_str, sizeof(label_str), "%02ld:%02ld:%02ld", 
+               (long)(cur_time/3600), (long)((cur_time%3600)/60),
+               (long)(cur_time%60));
+      gtk_label_set(GTK_LABEL(running_time), label_str);
 
-          gtk_label_set(GTK_LABEL(stats[i].value), label_str);
+      if (ld.sync_packets) {
 
-          snprintf(label_str, sizeof(label_str), "(%.1f%%)",
-                   pct(*stats[i].value_ptr, ld.counts.total));
+        for (i = 0; i < N_COUNTS; i++) {
+            snprintf(label_str, sizeof(label_str), "%d",
+                     *counts[i].value_ptr);
 
-          gtk_label_set(GTK_LABEL(stats[i].percent), label_str);
-      }
+            gtk_label_set(GTK_LABEL(counts[i].value), label_str);
+
+            snprintf(label_str, sizeof(label_str), "(%.1f%%)",
+                     pct(*counts[i].value_ptr, ld.counts.total));
+
+            gtk_label_set(GTK_LABEL(counts[i].percent), label_str);
+        }
+
+        /* do sync here, too */
+        fflush(wtap_dump_file(ld.pdh));
+
+        if (capture_child) {
+         /* This is the child process for a sync mode capture, so send
+            our parent a message saying we've written out "ld.sync_packets"
+            packets to the capture file. */
+         char tmp[DECISIZE+1+1];
+         sprintf(tmp, "%d%c", ld.sync_packets, SP_PACKET_COUNT);
+         write(1, tmp, strlen(tmp));
+        }
 
-      /* do sync here, too */
-      fflush(wtap_dump_file(ld.pdh));
-      if (capture_child && ld.sync_packets) {
-       /* This is the child process for a sync mode capture, so send
-          our parent a message saying we've written out "ld.sync_packets"
-          packets to the capture file. */
-       char tmp[20];
-       sprintf(tmp, "%d*", ld.sync_packets);
-       write(1, tmp, strlen(tmp));
        ld.sync_packets = 0;
+
+      }
+
+      if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) {
+        /* The specified capture time has elapsed; stop the capture. */
+        ld.go = FALSE;
       }
     }
+  } /* while (ld.go) */
+
+  /* delete stop conditions */
+  if (cnd_stop_capturesize != NULL)
+    cnd_delete(cnd_stop_capturesize);
+  if (cnd_stop_timeout != NULL)
+    cnd_delete(cnd_stop_timeout);
+
+  if (ld.pcap_err) {
+    snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s",
+      pcap_geterr(pch));
+    popup_errmsg(errmsg);
+#ifdef _WIN32
+  }
+#else
+  } else if (ld.from_pipe && ld.pipe_err == PIPERR)
+      popup_errmsg(errmsg);
+#endif
+
+  if (ld.err == 0)
+    write_ok = TRUE;
+  else {
+    get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, ld.err,
+                             FALSE);
+    popup_errmsg(errmsg);
+    write_ok = FALSE;
   }
-    
-  if (ld.err != 0) {
-    /* XXX - in fork mode, this may not pop up, or, if it does,
-       it may disappear as soon as we exit.
-
-       We should have the parent process, while it's reading
-       the packet count update messages, catch error messages
-       and pop up a message box if it sees one. */
-    show_capture_file_io_error(cfile.save_file, ld.err, FALSE);
-
-    /* A write failed, so we've already told the user there's a problem;
-       if the close fails, there's no point in telling them about that
-       as well. */
-    wtap_dump_close(ld.pdh, &err);
+
+  if (capture_opts.ringbuffer_on) {
+    close_ok = ringbuf_wtap_dump_close(&cfile, &err);
   } else {
-    if (!wtap_dump_close(ld.pdh, &err)) {
-      /* XXX - in fork mode, this may not pop up, or, if it does,
-         it may disappear as soon as we exit.
-
-         We should have the parent process, while it's reading
-         the packet count update messages, catch error messages
-         and pop up a message box if it sees one. */
-      show_capture_file_io_error(cfile.save_file, err, TRUE);
-    }
+    close_ok = wtap_dump_close(ld.pdh, &err);
+  }
+  /* If we've displayed a message about a write error, there's no point
+     in displaying another message about an error on close. */
+  if (!close_ok && write_ok) {
+    get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, err,
+               TRUE);
+    popup_errmsg(errmsg);
   }
+  /* Set write_ok to mean the write and the close were successful. */
+  write_ok = (write_ok && close_ok);
+
 #ifndef _WIN32
-  if (ld.from_pipe)
+  /*
+   * XXX We exhibit different behaviour between normal mode and sync mode
+   * when the pipe is stdin and not already at EOF.  If we're a child, the
+   * parent's stdin isn't closed, so if the user starts another capture,
+   * pipe_open_live() will very likely not see the expected magic bytes and
+   * will say "Unrecognized libpcap format".  On the other hand, in normal
+   * mode, pipe_open_live() will say "End of file on pipe during open".
+   */
+  if (ld.from_pipe && pipe_fd >= 0)
     close(pipe_fd);
   else
 #endif
+  {
+    /* Get the capture statistics, so we know how many packets were
+       dropped. */
+    if (pcap_stats(pch, stats) >= 0) {
+      *stats_known = TRUE;
+      if (capture_child) {
+       /* Let the parent process know. */
+       char tmp[DECISIZE+1+1];
+       sprintf(tmp, "%d%c", stats->ps_drop, SP_DROPS);
+       write(1, tmp, strlen(tmp));
+      }
+    } else {
+      snprintf(errmsg, sizeof(errmsg),
+               "Can't get packet-drop statistics: %s",
+               pcap_geterr(pch));
+      popup_errmsg(errmsg);
+    }
     pcap_close(pch);
+  }
 
-#ifdef WIN32
+#ifdef _WIN32
   /* Shut down windows sockets */
   WSACleanup();
 #endif
@@ -1571,40 +2004,47 @@ capture(void)
   gtk_grab_remove(GTK_WIDGET(cap_w));
   gtk_widget_destroy(GTK_WIDGET(cap_w));
 
-  return TRUE;
+  return write_ok;
 
 error:
-  /* We can't use the save file, and we have no wtap_dump stream
-     to close in order to close it, so close the FD directly. */
-  close(cfile.save_file_fd);
-
-  /* We couldn't even start the capture, so get rid of the capture
-     file. */
-  unlink(cfile.save_file); /* silently ignore error */
-  g_free(cfile.save_file);
-  cfile.save_file = NULL;
-  if (capture_child) {
-    /* This is the child process for a sync mode capture.
-       Send the error message to our parent, so they can display a
-       dialog box containing it. */
-    send_errmsg_to_parent(errmsg);
+  if (capture_opts.ringbuffer_on) {
+    /* cleanup ringbuffer */
+    ringbuf_error_cleanup();
   } else {
-    /* Display the dialog box ourselves; there's no parent. */
-    simple_dialog(ESD_TYPE_CRIT, NULL, errmsg);
+    /* We can't use the save file, and we have no wtap_dump stream
+       to close in order to close it, so close the FD directly. */
+    close(cfile.save_file_fd);
+
+    /* We couldn't even start the capture, so get rid of the capture
+       file. */
+    unlink(cfile.save_file); /* silently ignore error */
+    g_free(cfile.save_file);
+  }
+  cfile.save_file = NULL;
+  popup_errmsg(errmsg);
+
+#ifndef _WIN32
+  if (ld.from_pipe) {
+    if (pipe_fd >= 0)
+      close(pipe_fd);
+  } else
+#endif
+  {
+    if (pch != NULL)
+      pcap_close(pch);
   }
-  if (pch != NULL && !ld.from_pipe)
-    pcap_close(pch);
 
   return FALSE;
 }
 
 static void
-show_capture_file_io_error(const char *fname, int err, gboolean is_close)
+get_capture_file_io_error(char *errmsg, int errmsglen, const char *fname,
+                         int err, gboolean is_close)
 {
   switch (err) {
 
   case ENOSPC:
-    simple_dialog(ESD_TYPE_WARN, NULL,
+    snprintf(errmsg, errmsglen,
                "Not all the packets could be written to the file"
                " to which the capture was being saved\n"
                "(\"%s\") because there is no space left on the file system\n"
@@ -1614,7 +2054,7 @@ show_capture_file_io_error(const char *fname, int err, gboolean is_close)
 
 #ifdef EDQUOT
   case EDQUOT:
-    simple_dialog(ESD_TYPE_WARN, NULL,
+    snprintf(errmsg, errmsglen,
                "Not all the packets could be written to the file"
                " to which the capture was being saved\n"
                "(\"%s\") because you are too close to, or over,"
@@ -1625,13 +2065,13 @@ show_capture_file_io_error(const char *fname, int err, gboolean is_close)
 #endif
 
   case WTAP_ERR_CANT_CLOSE:
-    simple_dialog(ESD_TYPE_WARN, NULL,
+    snprintf(errmsg, errmsglen,
                "The file to which the capture was being saved"
                " couldn't be closed for some unknown reason.");
     break;
 
   case WTAP_ERR_SHORT_WRITE:
-    simple_dialog(ESD_TYPE_WARN, NULL,
+    snprintf(errmsg, errmsglen,
                "Not all the packets could be written to the file"
                " to which the capture was being saved\n"
                "(\"%s\").",
@@ -1640,12 +2080,12 @@ show_capture_file_io_error(const char *fname, int err, gboolean is_close)
 
   default:
     if (is_close) {
-      simple_dialog(ESD_TYPE_WARN, NULL,
+      snprintf(errmsg, errmsglen,
                "The file to which the capture was being saved\n"
                "(\"%s\") could not be closed: %s.",
                fname, wtap_strerror(err));
     } else {
-      simple_dialog(ESD_TYPE_WARN, NULL,
+      snprintf(errmsg, errmsglen,
                "An error occurred while writing to the file"
                " to which the capture was being saved\n"
                "(\"%s\"): %s.",
@@ -1655,13 +2095,27 @@ show_capture_file_io_error(const char *fname, int err, gboolean is_close)
   }
 }
 
+static void
+popup_errmsg(const char *errmsg)
+{
+  if (capture_child) {
+    /* This is the child process for a sync mode capture.
+       Send the error message to our parent, so they can display a
+       dialog box containing it. */
+    send_errmsg_to_parent(errmsg);
+  } else {
+    /* Display the dialog box ourselves; there's no parent. */
+    simple_dialog(ESD_TYPE_CRIT, NULL, "%s", errmsg);
+  }
+}
+
 static void
 send_errmsg_to_parent(const char *errmsg)
 {
     int msglen = strlen(errmsg);
-    char lenbuf[10+1+1];
+    char lenbuf[DECISIZE+1+1];
 
-    sprintf(lenbuf, "%u;", msglen);
+    sprintf(lenbuf, "%u%c", msglen, SP_ERROR_MSG);
     write(1, lenbuf, strlen(lenbuf));
     write(1, errmsg, msglen);
 }
@@ -1676,20 +2130,20 @@ pct(gint num, gint denom) {
 }
 
 static void
-stop_capture(int signo)
+stop_capture(int signo _U_)
 {
   ld.go = FALSE;
 }
 
 static void
-capture_delete_cb(GtkWidget *w, GdkEvent *event, gpointer data) {
+capture_delete_cb(GtkWidget *w _U_, GdkEvent *event _U_, gpointer data) {
   capture_stop_cb(NULL, data);
 }
 
 static void
-capture_stop_cb(GtkWidget *w, gpointer data) {
+capture_stop_cb(GtkWidget *w _U_, gpointer data) {
   loop_data *ld = (loop_data *) data;
-  
+
   ld->go = FALSE;
 }
 
@@ -1705,71 +2159,107 @@ capture_stop(void)
 #endif
 }
 
+void
+kill_capture_child(void)
+{
+  /*
+   * XXX - find some way of signaling the child in Win32.
+   */
+#ifndef _WIN32
+  if (fork_child != -1)
+    kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
+#endif
+}
+
 static void
-capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
-  const u_char *pd) {
+capture_pcap_cb(guchar *user, const struct pcap_pkthdr *phdr,
+  const guchar *pd)
+{
   struct wtap_pkthdr whdr;
+  union wtap_pseudo_header pseudo_header;
   loop_data *ld = (loop_data *) user;
   int err;
 
-  if ((++ld->counts.total >= ld->max) && (ld->max > 0)) 
+  if ((++ld->counts.total >= ld->max) && (ld->max > 0))
   {
      ld->go = FALSE;
   }
+
+  /* Convert from libpcap to Wiretap format.
+     If that fails, set "ld->go" to FALSE, to stop the capture, and set
+     "ld->err" to the error. */
+  pd = wtap_process_pcap_packet(ld->linktype, phdr, pd, &pseudo_header,
+                               &whdr, &err);
+  if (pd == NULL) {
+    ld->go = FALSE;
+    ld->err = err;
+    return;
+  }
+
   if (ld->pdh) {
-     /* "phdr->ts" may not necessarily be a "struct timeval" - it may
-        be a "struct bpf_timeval", with member sizes wired to 32
-       bits - and we may go that way ourselves in the future, so
-       copy the members individually. */
-     whdr.ts.tv_sec = phdr->ts.tv_sec;
-     whdr.ts.tv_usec = phdr->ts.tv_usec;
-     whdr.caplen = phdr->caplen;
-     whdr.len = phdr->len;
-     whdr.pkt_encap = ld->linktype;
-
-     /* If this fails, set "ld->go" to FALSE, to stop the capture, and set
-        "ld->err" to the error. */
-     if (!wtap_dump(ld->pdh, &whdr, NULL, pd, &err)) {
-       ld->go = FALSE;
-       ld->err = err;
-     }
+    /* We're supposed to write the packet to a file; do so.
+       If this fails, set "ld->go" to FALSE, to stop the capture, and set
+       "ld->err" to the error. */
+    if (!wtap_dump(ld->pdh, &whdr, &pseudo_header, pd, &err)) {
+      ld->go = FALSE;
+      ld->err = err;
+    }
   }
 
-  /* Set the initial payload to the packet length, and the initial
-     captured payload to the capture length (other protocols may
-     reduce them if their headers say they're less). */
-  pi.len = phdr->len;
-  pi.captured_len = phdr->caplen;
-    
   switch (ld->linktype) {
     case WTAP_ENCAP_ETHERNET:
-      capture_eth(pd, 0, &ld->counts);
+      capture_eth(pd, 0, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_FDDI:
     case WTAP_ENCAP_FDDI_BITSWAPPED:
-      capture_fddi(pd, &ld->counts);
+      capture_fddi(pd, whdr.caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_PRISM_HEADER:
+      capture_prism(pd, 0, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_TOKEN_RING:
-      capture_tr(pd, 0, &ld->counts);
+      capture_tr(pd, 0, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_NULL:
-      capture_null(pd, &ld->counts);
+      capture_null(pd, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_PPP:
-      capture_ppp(pd, 0, &ld->counts);
+      capture_ppp_hdlc(pd, 0, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_RAW_IP:
-      capture_raw(pd, &ld->counts);
+      capture_raw(pd, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_SLL:
-      capture_sll(pd, &ld->counts);
+      capture_sll(pd, whdr.caplen, &ld->counts);
       break;
     case WTAP_ENCAP_LINUX_ATM_CLIP:
-      capture_clip(pd, &ld->counts);
+      capture_clip(pd, whdr.caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_IEEE_802_11:
+    case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+      capture_ieee80211(pd, 0, whdr.caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_CHDLC:
+      capture_chdlc(pd, 0, whdr.caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_LOCALTALK:
+      capture_llap(&ld->counts);
+      break;
+    case WTAP_ENCAP_ATM_PDUS:
+      capture_atm(&pseudo_header, pd, whdr.caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_IP_OVER_FC:
+      capture_ipfc(pd, whdr.caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_ARCNET:
+      capture_arcnet(pd, whdr.caplen, &ld->counts, FALSE, TRUE);
+      break;
+    case WTAP_ENCAP_ARCNET_LINUX:
+      capture_arcnet(pd, whdr.caplen, &ld->counts, TRUE, FALSE);
       break;
-    /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
-       with LLC header following; we should implement it at some
-       point. */
+    /* XXX - some ATM drivers on FreeBSD might prepend a 4-byte ATM
+       pseudo-header to DLT_ATM_RFC1483, with LLC header following;
+       we might have to implement that at some point. */
   }
 }