Add in the SliMP3 data protocol dissector.
[obnox/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index f1595d0737bab894c0e132c2813d2cb34cff6e5f..834356f3e27a5aa21b5db3a39ef5310c5b9c64d9 100644 (file)
--- a/file.c
+++ b/file.c
@@ -1,12 +1,11 @@
 /* file.c
  * File I/O routines
  *
- * $Id: file.c,v 1.196 2000/07/07 23:09:03 guy Exp $
+ * $Id: file.c,v 1.255 2001/12/18 19:09:01 gram 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
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/stat.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <signal.h>
 
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
 #ifdef NEED_SNPRINTF_H
-# ifdef HAVE_STDARG_H
-#  include <stdarg.h>
-# else
-#  include <varargs.h>
-# endif
 # include "snprintf.h"
 #endif
 
 # include <netinet/in.h>
 #endif
 
+#include <epan.h>
+#include <filesystem.h>
+
 #include "gtk/main.h"
+#include "color.h"
+#include "gtk/color_utils.h"
 #include "column.h"
 #include "packet.h"
 #include "print.h"
 #include "menu.h"
 #include "util.h"
 #include "simple_dialog.h"
+#include "progress_dlg.h"
 #include "ui_util.h"
+#include "statusbar.h"
 #include "prefs.h"
 #include "gtk/proto_draw.h"
-#include "dfilter.h"
+#include "gtk/packet_win.h"
+#include "dfilter/dfilter.h"
 #include "conversation.h"
+#include "reassemble.h"
 #include "globals.h"
+#include "gtk/colors.h"
+#include "epan_dissect.h"
 
-#include "plugins.h"
-
-extern GtkWidget *packet_list, *info_bar, *byte_view, *tree_view;
-extern guint      file_ctx;
-
-gboolean auto_scroll_live = FALSE;
+extern GtkWidget *packet_list, *byte_nb_ptr, *tree_view;
 
 static guint32 firstsec, firstusec;
 static guint32 prevsec, prevusec;
 
-static void read_packet(capture_file *cf, int offset);
+static void read_packet(capture_file *cf, long offset);
+
+static void rescan_packets(capture_file *cf, const char *action,
+       gboolean refilter, gboolean redissect);
 
 static void set_selected_row(int row);
 
@@ -102,6 +111,7 @@ static void thaw_clist(capture_file *cf);
 
 static char *file_rename_error_message(int err);
 static char *file_close_error_message(int err);
+static gboolean copy_binary_file(char *from_filename, char *to_filename);
 
 /* Update the progress bar this many times when reading a file. */
 #define N_PROGBAR_UPDATES      100
@@ -115,7 +125,6 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
 {
   wtap       *wth;
   int         err;
-  FILE_T      fh;
   int         fd;
   struct stat cf_stat;
 
@@ -124,7 +133,6 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
     goto fail;
 
   /* Find the size of the file. */
-  fh = wtap_file(wth);
   fd = wtap_fd(wth);
   if (fstat(fd, &cf_stat) < 0) {
     err = errno;
@@ -134,14 +142,20 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
 
   /* The open succeeded.  Close whatever capture file we had open,
      and fill in the information for this file. */
-  close_cap_file(cf, info_bar);
+  close_cap_file(cf);
 
   /* Initialize the table of conversations. */
-  conversation_init();
+  epan_conversation_init();
 
   /* Initialize protocol-specific variables */
   init_all_protocols();
 
+  /* Initialize the common data structures for fragment reassembly.
+     Must be done *after* "init_all_protocols()", as "init_all_protocols()"
+     may free up space for fragments, which it finds by using the
+     data structures that "reassemble_init()" frees. */
+  reassemble_init();
+
   /* We're about to start reading the file. */
   cf->state = FILE_READ_IN_PROGRESS;
 
@@ -162,6 +176,8 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
 
   cf->cd_t      = wtap_file_type(cf->wth);
   cf->count     = 0;
+  cf->marked_count = 0;
+  cf->drops_known = FALSE;
   cf->drops     = 0;
   cf->esec      = 0;
   cf->eusec     = 0;
@@ -180,14 +196,14 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
   return (0);
 
 fail:
-  simple_dialog(ESD_TYPE_WARN, NULL,
+  simple_dialog(ESD_TYPE_CRIT, NULL,
                        file_open_error_message(err, FALSE), fname);
   return (err);
 }
 
 /* Reset everything to a pristine state */
 void
-close_cap_file(capture_file *cf, void *w)
+close_cap_file(capture_file *cf)
 {
   /* Die if we're in the middle of reading a file. */
   g_assert(cf->state != FILE_READ_IN_PROGRESS);
@@ -216,7 +232,7 @@ close_cap_file(capture_file *cf, void *w)
     cf->plist_chunk = NULL;
   }
   if (cf->rfcode != NULL) {
-    dfilter_destroy(cf->rfcode);
+    dfilter_free(cf->rfcode);
     cf->rfcode = NULL;
   }
   cf->plist = NULL;
@@ -233,7 +249,7 @@ close_cap_file(capture_file *cf, void *w)
   /* Clear any file-related status bar messages.
      XXX - should be "clear *ALL* file-related status bar messages;
      will there ever be more than one on the stack? */
-  gtk_statusbar_pop(GTK_STATUSBAR(w), file_ctx);
+  statusbar_pop_file_msg();
 
   /* Restore the standard title bar message. */
   set_main_window_name("The Ethereal Network Analyzer");
@@ -244,6 +260,7 @@ close_cap_file(capture_file *cf, void *w)
   set_menus_for_captured_packets(FALSE);
   set_menus_for_selected_packet(FALSE);
   set_menus_for_capture_in_progress(FALSE);
+  set_menus_for_selected_tree_row(FALSE);
 
   /* We have no file open. */
   cf->state = FILE_CLOSED;
@@ -256,7 +273,8 @@ set_display_filename(capture_file *cf)
 {
   gchar  *name_ptr;
   size_t  msg_len;
-  gchar  *done_fmt = " File: %s  Drops: %u";
+  static const gchar done_fmt_nodrops[] = " File: %s";
+  static const gchar done_fmt_drops[] = " File: %s  Drops: %u";
   gchar  *done_msg;
   gchar  *win_name_fmt = "%s - Ethereal";
   gchar  *win_name;
@@ -271,10 +289,16 @@ set_display_filename(capture_file *cf)
     name_ptr = "<capture>";
   }
 
-  msg_len = strlen(name_ptr) + strlen(done_fmt) + 64;
-  done_msg = g_malloc(msg_len);
-  snprintf(done_msg, msg_len, done_fmt, name_ptr, cf->drops);
-  gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, done_msg);
+  if (cf->drops_known) {
+    msg_len = strlen(name_ptr) + strlen(done_fmt_drops) + 64;
+    done_msg = g_malloc(msg_len);
+    snprintf(done_msg, msg_len, done_fmt_drops, name_ptr, cf->drops);
+  } else {
+    msg_len = strlen(name_ptr) + strlen(done_fmt_nodrops);
+    done_msg = g_malloc(msg_len);
+    snprintf(done_msg, msg_len, done_fmt_nodrops, name_ptr);
+  }
+  statusbar_push_file_msg(done_msg);
   g_free(done_msg);
 
   msg_len = strlen(name_ptr) + strlen(win_name_fmt) + 1;
@@ -292,7 +316,7 @@ read_cap_file(capture_file *cf, int *err)
   char     *errmsg;
   char      errmsg_errno[1024+1];
   gchar     err_str[2048+1];
-  int       data_offset;
+  long      data_offset;
   progdlg_t *progbar;
   gboolean  stop_flag;
   int       file_pos;
@@ -303,7 +327,7 @@ read_cap_file(capture_file *cf, int *err)
   msg_len = strlen(name_ptr) + strlen(load_fmt) + 2;
   load_msg = g_malloc(msg_len);
   snprintf(load_msg, msg_len, load_fmt, name_ptr);
-  gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
+  statusbar_push_file_msg(load_msg);
 
   /* Update the progress bar when it gets to this value. */
   cf->progbar_nextstep = 0;
@@ -321,7 +345,7 @@ read_cap_file(capture_file *cf, int *err)
   progbar = create_progress_dlg(load_msg, "Stop", &stop_flag);
   g_free(load_msg);
 
-  while ((data_offset = wtap_read(cf->wth, err)) > 0) {
+  while ((wtap_read(cf->wth, err, &data_offset))) {
     /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
        when we update it, we have to run the GTK+ main loop to get it
        to repaint what's pending, and doing so may involve an "ioctl()"
@@ -341,7 +365,7 @@ read_cap_file(capture_file *cf, int *err)
       destroy_progress_dlg(progbar);
       cf->state = FILE_READ_ABORTED;   /* so that we're allowed to close it */
       gtk_clist_thaw(GTK_CLIST(packet_list));  /* undo our freeze */
-      close_cap_file(cf, info_bar);
+      close_cap_file(cf);
       return (READ_ABORTED);
     }
     read_packet(cf, data_offset);
@@ -365,7 +389,7 @@ read_cap_file(capture_file *cf, int *err)
   cf->current_frame = cf->first_displayed;
   thaw_clist(cf);
 
-  gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+  statusbar_pop_file_msg();
   set_display_filename(cf);
 
   /* Enable menu items that make sense if you have a capture file you've
@@ -381,7 +405,7 @@ read_cap_file(capture_file *cf, int *err)
   if (cf->first_displayed != NULL)
     gtk_signal_emit_by_name(GTK_OBJECT(packet_list), "select_row", 0);
 
-  if (data_offset < 0) {
+  if (*err != 0) {
     /* Put up a message box noting that the read failed somewhere along
        the line.  Don't throw out the stuff we managed to read, though,
        if any. */
@@ -406,13 +430,14 @@ read_cap_file(capture_file *cf, int *err)
       break;
 
     default:
-      sprintf(errmsg_errno, "An error occurred while reading the"
-                              " capture file: %s.", wtap_strerror(*err));
+      snprintf(errmsg_errno, sizeof(errmsg_errno),
+              "An error occurred while reading the"
+              " capture file: %s.", wtap_strerror(*err));
       errmsg = errmsg_errno;
       break;
     }
     snprintf(err_str, sizeof err_str, errmsg);
-    simple_dialog(ESD_TYPE_WARN, NULL, err_str);
+    simple_dialog(ESD_TYPE_CRIT, NULL, err_str);
     return (READ_ERROR);
   } else
     return (READ_SUCCESS);
@@ -446,8 +471,7 @@ start_tail_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
       }
     }
 
-    gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, 
-                      " <live capture in progress>");
+    statusbar_push_file_msg(" <live capture in progress>");
   }
   return err;
 }
@@ -455,11 +479,11 @@ start_tail_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
 read_status_t
 continue_tail_cap_file(capture_file *cf, int to_read, int *err)
 {
-  int data_offset = 0;
+  long data_offset = 0;
 
   gtk_clist_freeze(GTK_CLIST(packet_list));
 
-  while (to_read != 0 && (data_offset = wtap_read(cf->wth, err)) > 0) {
+  while (to_read != 0 && (wtap_read(cf->wth, err, &data_offset))) {
     if (cf->state == FILE_READ_ABORTED) {
       /* Well, the user decided to exit Ethereal.  Break out of the
          loop, and let the code below (which is called even if there
@@ -474,7 +498,7 @@ continue_tail_cap_file(capture_file *cf, int to_read, int *err)
 
   /* XXX - this cheats and looks inside the packet list to find the final
      row number. */
-  if (auto_scroll_live && cf->plist_end != NULL)
+  if (prefs.capture_auto_scroll && cf->plist_end != NULL)
     gtk_clist_moveto(GTK_CLIST(packet_list), 
                       GTK_CLIST(packet_list)->rows - 1, -1, 1.0, 1.0);
 
@@ -485,7 +509,7 @@ continue_tail_cap_file(capture_file *cf, int to_read, int *err)
        "finish_tail_cap_file()" will be called, and it will clean up
        and exit. */
     return READ_ABORTED;
-  } else if (data_offset < 0) {
+  } else if (*err != 0) {
     /* We got an error reading the capture file.
        XXX - pop up a dialog box? */
     return (READ_ERROR);
@@ -496,11 +520,11 @@ continue_tail_cap_file(capture_file *cf, int to_read, int *err)
 read_status_t
 finish_tail_cap_file(capture_file *cf, int *err)
 {
-  int data_offset;
+  long data_offset;
 
   gtk_clist_freeze(GTK_CLIST(packet_list));
 
-  while ((data_offset = wtap_read(cf->wth, err)) > 0) {
+  while ((wtap_read(cf->wth, err, &data_offset))) {
     if (cf->state == FILE_READ_ABORTED) {
       /* Well, the user decided to abort the read.  Break out of the
          loop, and let the code below (which is called even if there
@@ -516,12 +540,12 @@ finish_tail_cap_file(capture_file *cf, int *err)
        it's probably exited), so we can just close the capture
        file; we return READ_ABORTED so our caller can do whatever
        is appropriate when that happens. */
-    close_cap_file(cf, info_bar);
+    close_cap_file(cf);
     return READ_ABORTED;
   }
 
   thaw_clist(cf);
-  if (auto_scroll_live && cf->plist_end != NULL)
+  if (prefs.capture_auto_scroll && cf->plist_end != NULL)
     /* XXX - this cheats and looks inside the packet list to find the final
        row number. */
     gtk_clist_moveto(GTK_CLIST(packet_list), 
@@ -541,7 +565,7 @@ finish_tail_cap_file(capture_file *cf, int *err)
   cf->lnk_t = wtap_file_encap(cf->wth);
 
   /* Pop the "<live capture in progress>" message off the status bar. */
-  gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+  statusbar_pop_file_msg();
 
   set_display_filename(cf);
 
@@ -554,7 +578,7 @@ finish_tail_cap_file(capture_file *cf, int *err)
   set_menus_for_capture_file(TRUE);
   set_menus_for_unsaved_capture_file(!cf->user_saved);
 
-  if (data_offset < 0) {
+  if (*err != 0) {
     /* We got an error reading the capture file.
        XXX - pop up a dialog box? */
     return (READ_ERROR);
@@ -565,9 +589,7 @@ finish_tail_cap_file(capture_file *cf, int *err)
 
 typedef struct {
   color_filter_t *colorf;
-  proto_tree   *protocol_tree;
-  const guint8 *pd;
-  frame_data   *fdata;
+  epan_dissect_t *edt;
 } apply_color_filter_args;
 
 /*
@@ -582,18 +604,21 @@ apply_color_filter(gpointer filter_arg, gpointer argp)
   apply_color_filter_args *args = argp;
 
   if (colorf->c_colorfilter != NULL && args->colorf == NULL) {
-    if (dfilter_apply(colorf->c_colorfilter, args->protocol_tree, args->pd, args->fdata->cap_len))
+    if (dfilter_apply_edt(colorf->c_colorfilter, args->edt))
       args->colorf = colorf;
   }
 }
 
 static int
 add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
-       union wtap_pseudo_header *pseudo_header, const u_char *buf)
+       union wtap_pseudo_header *pseudo_header, const u_char *buf,
+       gboolean refilter)
 {
   apply_color_filter_args args;
   gint          i, row;
-  proto_tree   *protocol_tree = NULL;
+  gboolean     create_proto_tree = FALSE;
+  epan_dissect_t *edt;
+  GdkColor      fg, bg;
 
   /* We don't yet have a color filter to apply. */
   args.colorf = NULL;
@@ -606,51 +631,54 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     firstusec = fdata->abs_usecs;
   }
 
-  /* Get the time elapsed between the first packet and this packet. */
-  cf->esec = fdata->abs_secs - firstsec;
-  if (firstusec <= fdata->abs_usecs) {
-    cf->eusec = fdata->abs_usecs - firstusec;
-  } else {
-    cf->eusec = (fdata->abs_usecs + 1000000) - firstusec;
-    cf->esec--;
-  }
+  /* If either
+
+       we have a display filter and are re-applying it;
 
-  fdata->cinfo = &cf->cinfo;
-  for (i = 0; i < fdata->cinfo->num_cols; i++) {
-    fdata->cinfo->col_data[i][0] = '\0';
+       we have a list of color filters;
+
+     allocate a protocol tree root node, so that we'll construct
+     a protocol tree against which a filter expression can be
+     evaluated. */
+  if ((cf->dfcode != NULL && refilter) || filter_list != NULL)
+         create_proto_tree = TRUE;
+
+  /* Dissect the frame. */
+  edt = epan_dissect_new(create_proto_tree, FALSE);
+
+  if (cf->dfcode) {
+      epan_dissect_prime_dfilter(edt, cf->dfcode);
+  }
+  if (filter_list) {
+      filter_list_prime_edt(edt);
   }
+  epan_dissect_run(edt, pseudo_header, buf, fdata, &cf->cinfo);
 
-  /* Apply the filters */
-  if (cf->dfcode != NULL || filter_list != NULL) {
-    protocol_tree = proto_tree_create_root();
-    dissect_packet(pseudo_header, buf, fdata, protocol_tree);
-    if (cf->dfcode != NULL)
-      fdata->flags.passed_dfilter = dfilter_apply(cf->dfcode, protocol_tree, buf, fdata->cap_len) ? 1 : 0;
-    else
-      fdata->flags.passed_dfilter = 1;
 
-    /* Apply color filters, if we have any. */
+  /* If we have a display filter, apply it if we're refiltering, otherwise
+     leave the "passed_dfilter" flag alone.
+
+     If we don't have a display filter, set "passed_dfilter" to 1. */
+  if (cf->dfcode != NULL) {
+    if (refilter) {
+      if (cf->dfcode != NULL)
+        fdata->flags.passed_dfilter = dfilter_apply_edt(cf->dfcode, edt) ? 1 : 0;
+      else
+        fdata->flags.passed_dfilter = 1;
+    }
+  } else
+    fdata->flags.passed_dfilter = 1;
+
+  /* If we have color filters, and the frame is to be displayed, apply
+     the color filters. */
+  if (fdata->flags.passed_dfilter) {
     if (filter_list != NULL) {
-      args.protocol_tree = protocol_tree;
-      args.pd = buf;
-      args.fdata = fdata;
+      args.edt = edt;
       g_slist_foreach(filter_list, apply_color_filter, &args);
     }
-    proto_tree_free(protocol_tree);
-  }
-  else {
-#ifdef HAVE_PLUGINS
-       if (enabled_plugins_number > 0)
-           protocol_tree = proto_tree_create_root();
-#endif
-       dissect_packet(pseudo_header, buf, fdata, protocol_tree);
-       fdata->flags.passed_dfilter = 1;
-#ifdef HAVE_PLUGINS
-       if (protocol_tree)
-           proto_tree_free(protocol_tree);
-#endif
   }
 
+
   if (fdata->flags.passed_dfilter) {
     /* This frame passed the display filter, so add it to the clist. */
 
@@ -664,22 +692,26 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     }
 
     /* Get the time elapsed between the first packet and this packet. */
-    fdata->rel_secs = cf->esec;
-    fdata->rel_usecs = cf->eusec;
+    compute_timestamp_diff(&fdata->rel_secs, &fdata->rel_usecs,
+               fdata->abs_secs, fdata->abs_usecs, firstsec, firstusec);
+
+    /* If it's greater than the current elapsed time, set the elapsed time
+       to it (we check for "greater than" so as not to be confused by
+       time moving backwards). */
+    if ((gint32)cf->esec < fdata->rel_secs
+       || ((gint32)cf->esec == fdata->rel_secs && (gint32)cf->eusec < fdata->rel_usecs)) {
+      cf->esec = fdata->rel_secs;
+      cf->eusec = fdata->rel_usecs;
+    }
   
     /* Get the time elapsed between the previous displayed packet and
        this packet. */
-    fdata->del_secs = fdata->abs_secs - prevsec;
-    if (prevusec <= fdata->abs_usecs) {
-      fdata->del_usecs = fdata->abs_usecs - prevusec;
-    } else {
-      fdata->del_usecs = (fdata->abs_usecs + 1000000) - prevusec;
-      fdata->del_secs--;
-    }
+    compute_timestamp_diff(&fdata->del_secs, &fdata->del_usecs,
+               fdata->abs_secs, fdata->abs_usecs, prevsec, prevusec);
     prevsec = fdata->abs_secs;
     prevusec = fdata->abs_usecs;
 
-    fill_in_columns(fdata);
+    epan_dissect_fill_in_columns(edt);
 
     /* If we haven't yet seen the first frame, this is it.
 
@@ -700,37 +732,40 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     /* This is the last frame we've seen so far. */
     cf->last_displayed = fdata;
 
-    row = gtk_clist_append(GTK_CLIST(packet_list), fdata->cinfo->col_data);
+    row = gtk_clist_append(GTK_CLIST(packet_list), cf->cinfo.col_data);
     gtk_clist_set_row_data(GTK_CLIST(packet_list), row, fdata);
 
-    if (filter_list != NULL && (args.colorf != NULL)) {
-        gtk_clist_set_background(GTK_CLIST(packet_list), row,
-                   &args.colorf->bg_color);
-        gtk_clist_set_foreground(GTK_CLIST(packet_list), row,
-                   &args.colorf->fg_color);
+    if (fdata->flags.marked) {
+       color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
+       color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
+    } else if (filter_list != NULL && (args.colorf != NULL)) {
+       bg = args.colorf->bg_color;
+       fg = args.colorf->fg_color;
     } else {
-        gtk_clist_set_background(GTK_CLIST(packet_list), row, &WHITE);
-        gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &BLACK);
+       bg = WHITE;
+       fg = BLACK;
     }
+    gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
+    gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
   } else {
     /* This frame didn't pass the display filter, so it's not being added
        to the clist, and thus has no row. */
     row = -1;
   }
-  fdata->cinfo = NULL;
+  epan_dissect_free(edt);
   return row;
 }
 
 static void
-read_packet(capture_file *cf, int offset)
+read_packet(capture_file *cf, long offset)
 {
   const struct wtap_pkthdr *phdr = wtap_phdr(cf->wth);
   union wtap_pseudo_header *pseudo_header = wtap_pseudoheader(cf->wth);
   const u_char *buf = wtap_buf_ptr(cf->wth);
   frame_data   *fdata;
   int           passed;
-  proto_tree   *protocol_tree;
   frame_data   *plist_end;
+  epan_dissect_t *edt;
 
   /* Allocate the next list entry, and add it to the list. */
   fdata = g_mem_chunk_alloc(cf->plist_chunk);
@@ -738,6 +773,7 @@ read_packet(capture_file *cf, int offset)
   fdata->next = NULL;
   fdata->prev = NULL;
   fdata->pfd  = NULL;
+  fdata->data_src  = NULL;
   fdata->pkt_len  = phdr->len;
   fdata->cap_len  = phdr->caplen;
   fdata->file_off = offset;
@@ -746,14 +782,15 @@ read_packet(capture_file *cf, int offset)
   fdata->abs_usecs = phdr->ts.tv_usec;
   fdata->flags.encoding = CHAR_ASCII;
   fdata->flags.visited = 0;
-  fdata->cinfo = NULL;
+  fdata->flags.marked = 0;
 
   passed = TRUE;
   if (cf->rfcode) {
-    protocol_tree = proto_tree_create_root();
-    dissect_packet(pseudo_header, buf, fdata, protocol_tree);
-    passed = dfilter_apply(cf->rfcode, protocol_tree, buf, fdata->cap_len);
-    proto_tree_free(protocol_tree);
+    edt = epan_dissect_new(TRUE, FALSE);
+    epan_dissect_prime_dfilter(edt, cf->rfcode);
+    epan_dissect_run(edt, pseudo_header, buf, fdata, NULL);
+    passed = dfilter_apply_edt(cf->rfcode, edt);
+    epan_dissect_free(edt);
   }   
   if (passed) {
     plist_end = cf->plist_end;
@@ -766,7 +803,7 @@ read_packet(capture_file *cf, int offset)
 
     cf->count++;
     fdata->num = cf->count;
-    add_packet_to_packet_list(fdata, cf, pseudo_header, buf);
+    add_packet_to_packet_list(fdata, cf, pseudo_header, buf, TRUE);
   } else {
     /* XXX - if we didn't have read filters, or if we could avoid
        allocating the "frame_data" structure until we knew whether
@@ -783,7 +820,7 @@ read_packet(capture_file *cf, int offset)
 int
 filter_packets(capture_file *cf, gchar *dftext)
 {
-  dfilter *dfcode;
+  dfilter_t *dfcode;
 
   if (dftext == NULL) {
     /* The new filter is an empty filter (i.e., display all packets). */
@@ -792,9 +829,9 @@ filter_packets(capture_file *cf, gchar *dftext)
     /*
      * We have a filter; try to compile it.
      */
-    if (dfilter_compile(dftext, &dfcode) != 0) {
+    if (!dfilter_compile(dftext, &dfcode)) {
       /* The attempt failed; report an error. */
-      simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
+      simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
       return 0;
     }
 
@@ -811,27 +848,48 @@ filter_packets(capture_file *cf, gchar *dftext)
     g_free(cf->dfilter);
   cf->dfilter = dftext;
   if (cf->dfcode != NULL)
-    dfilter_destroy(cf->dfcode);
+    dfilter_free(cf->dfcode);
   cf->dfcode = dfcode;
 
-  /* Now go through the list of packets we've read from the capture file,
-     applying the current display filter, and, if the packet passes the
-     display filter, add it to the summary display, appropriately
-     colored.  (That's how we colorize the display - it's like filtering
-     the display, only we don't install a new filter.) */
-  colorize_packets(cf);
+  /* Now rescan the packet list, applying the new filter, but not
+     throwing away information constructed on a previous pass. */
+  rescan_packets(cf, "Filtering", TRUE, FALSE);
   return 1;
 }
 
 void
 colorize_packets(capture_file *cf)
+{
+  rescan_packets(cf, "Colorizing", FALSE, FALSE);
+}
+
+void
+redissect_packets(capture_file *cf)
+{
+  rescan_packets(cf, "Reprocessing", TRUE, TRUE);
+}
+
+/* Rescan the list of packets, reconstructing the CList.
+
+   "action" describes why we're doing this; it's used in the progress
+   dialog box.
+
+   "refilter" is TRUE if we need to re-evaluate the filter expression.
+
+   "redissect" is TRUE if we need to make the dissectors reconstruct
+   any state information they have (because a preference that affects
+   some dissector has changed, meaning some dissector might construct
+   its state differently from the way it was constructed the last time). */
+static void
+rescan_packets(capture_file *cf, const char *action, gboolean refilter,
+               gboolean redissect)
 {
   frame_data *fdata;
   progdlg_t *progbar;
   gboolean stop_flag;
   guint32 progbar_quantum;
   guint32 progbar_nextstep;
-  int count;
+  unsigned int count;
   frame_data *selected_frame;
   int selected_row;
   int row;
@@ -846,14 +904,24 @@ colorize_packets(capture_file *cf)
      rebuild the clist, however. */
   selected_row = -1;
 
-  /* We need to re-initialize all the state information that protocols
-     keep, because we're making a fresh pass through all the packets. */
+  if (redissect) {
+    /* We need to re-initialize all the state information that protocols
+       keep, because some preference that controls a dissector has changed,
+       which might cause the state information to be constructed differently
+       by that dissector. */
 
-  /* Initialize the table of conversations. */
-  conversation_init();
+    /* Initialize the table of conversations. */
+    epan_conversation_init();
 
-  /* Initialize protocol-specific variables */
-  init_all_protocols();
+    /* Initialize protocol-specific variables */
+    init_all_protocols();
+
+    /* Initialize the common data structures for fragment reassembly.
+       Must be done *after* "init_all_protocols()", as "init_all_protocols()"
+       may free up space for fragments, which it finds by using the
+       data structures that "reassemble_init()" frees. */
+    reassemble_init();
+  }
 
   /* Freeze the packet list while we redo it, so we don't get any
      screen updates while it happens. */
@@ -866,9 +934,9 @@ colorize_packets(capture_file *cf)
   cf->first_displayed = NULL;
   cf->last_displayed = NULL;
 
-  /* Iterate through the list of packets, calling a routine
-     to run the filter on the packet, see if it matches, and
-     put it in the display list if so.  */
+  /* Iterate through the list of frames.  Call a routine for each frame
+     to check whether it should be displayed and, if so, add it to
+     the display list. */
   firstsec = 0;
   firstusec = 0;
   prevsec = 0;
@@ -883,7 +951,7 @@ colorize_packets(capture_file *cf)
   count = 0;
 
   stop_flag = FALSE;
-  progbar = create_progress_dlg("Filtering", "Stop", &stop_flag);
+  progbar = create_progress_dlg(action, "Stop", &stop_flag);
 
   for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
     /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
@@ -920,14 +988,54 @@ colorize_packets(capture_file *cf)
 
     count++;
 
+    if (redissect) {
+      /* Since all state for the frame was destroyed, mark the frame
+       * as not visited, free the GSList referring to the state
+       * data (the per-frame data itself was freed by
+       * "init_all_protocols()"), and null out the GSList pointer. */
+      fdata->flags.visited = 0;
+      if (fdata->pfd) {
+       g_slist_free(fdata->pfd);
+      }
+      fdata->pfd = NULL;
+      if (fdata->data_src) {   /* release data source list */
+       g_slist_free(fdata->data_src);
+      }
+      fdata->data_src = NULL;
+    }
+
     wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
        cf->pd, fdata->cap_len);
 
-    row = add_packet_to_packet_list(fdata, cf, &cf->pseudo_header, cf->pd);
+    row = add_packet_to_packet_list(fdata, cf, &cf->pseudo_header, cf->pd,
+                                       refilter);
     if (fdata == selected_frame)
       selected_row = row;
   }
  
+  if (redissect) {
+    /* Clear out what remains of the visited flags and per-frame data
+       pointers.
+
+       XXX - that may cause various forms of bogosity when dissecting
+       these frames, as they won't have been seen by this sequential
+       pass, but the only alternative I see is to keep scanning them
+       even though the user requested that the scan stop, and that
+       would leave the user stuck with an Ethereal grinding on
+       until it finishes.  Should we just stick them with that? */
+    for (; fdata != NULL; fdata = fdata->next) {
+      fdata->flags.visited = 0;
+      if (fdata->pfd) {
+       g_slist_free(fdata->pfd);
+      }
+      fdata->pfd = NULL;
+      if (fdata->data_src) {
+       g_slist_free(fdata->data_src);
+      }
+      fdata->data_src = NULL;
+    }
+  }
+
   /* We're done filtering the packets; destroy the progress bar. */
   destroy_progress_dlg(progbar);
 
@@ -957,7 +1065,6 @@ print_packets(capture_file *cf, print_args_t *print_args)
   guint32     progbar_quantum;
   guint32     progbar_nextstep;
   guint32     count;
-  proto_tree *protocol_tree;
   gint       *col_widths = NULL;
   gint        data_width;
   gboolean    print_separator;
@@ -966,6 +1073,7 @@ print_packets(capture_file *cf, print_args_t *print_args)
   char        *cp;
   int         column_len;
   int         line_len;
+  epan_dissect_t *edt = NULL;
 
   cf->print_fh = open_print_dest(print_args->to_file, print_args->dest);
   if (cf->print_fh == NULL)
@@ -1025,10 +1133,6 @@ print_packets(capture_file *cf, print_args_t *print_args)
 
   print_separator = FALSE;
 
-  /* The protocol tree will be "visible", i.e., printed, only if we're
-     not printing a summary. */
-  proto_tree_is_visible = !print_args->print_summary;
-
   /* Update the progress bar when it gets to this value. */
   progbar_nextstep = 0;
   /* When we reach the value that triggers a progress bar update,
@@ -1070,19 +1174,19 @@ print_packets(capture_file *cf, print_args_t *print_args)
     }
 
     count++;
-
-    if (fdata->flags.passed_dfilter) {
+    /* Check to see if we are suppressing unmarked packets, if so, 
+     * suppress them and then proceed to check for visibility.
+     */
+    if (((print_args->suppress_unmarked && fdata->flags.marked ) ||
+        !(print_args->suppress_unmarked)) && fdata->flags.passed_dfilter) {
       wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
                        cf->pd, fdata->cap_len);
       if (print_args->print_summary) {
         /* Fill in the column information, but don't bother creating
            the logical protocol tree. */
-        fdata->cinfo = &cf->cinfo;
-        for (i = 0; i < fdata->cinfo->num_cols; i++) {
-          fdata->cinfo->col_data[i][0] = '\0';
-        }
-        dissect_packet(&cf->pseudo_header, cf->pd, fdata, NULL);
-        fill_in_columns(fdata);
+        edt = epan_dissect_new(FALSE, FALSE);
+        epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
+        epan_dissect_fill_in_columns(edt);
         cp = &line_buf[0];
         line_len = 0;
         for (i = 0; i < cf->cinfo.num_cols; i++) {
@@ -1116,25 +1220,25 @@ print_packets(capture_file *cf, print_args_t *print_args)
         if (print_separator)
           print_line(cf->print_fh, print_args->format, "\n");
 
-        /* Create the logical protocol tree. */
-        protocol_tree = proto_tree_create_root();
-        dissect_packet(&cf->pseudo_header, cf->pd, fdata, protocol_tree);
+        /* Create the logical protocol tree, complete with the display
+           representation of the items; we don't need the columns here,
+           however. */
+        edt = epan_dissect_new(TRUE, TRUE);
+        epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
 
         /* Print the information in that tree. */
-        proto_tree_print(FALSE, print_args, (GNode *)protocol_tree,
-                       cf->pd, fdata, cf->print_fh);
-
-        proto_tree_free(protocol_tree);
+        proto_tree_print(FALSE, print_args, (GNode *)edt->tree,
+                       fdata, cf->print_fh);
 
        if (print_args->print_hex) {
          /* Print the full packet data as hex. */
-         print_hex_data(cf->print_fh, print_args->format, cf->pd,
-                       fdata->cap_len, fdata->flags.encoding);
+         print_hex_data(cf->print_fh, print_args->format, fdata);
        }
 
         /* Print a blank line if we print anything after this. */
         print_separator = TRUE;
       }
+      epan_dissect_free(edt);
     }
   }
 
@@ -1152,8 +1256,6 @@ print_packets(capture_file *cf, print_args_t *print_args)
  
   cf->print_fh = NULL;
 
-  proto_tree_is_visible = FALSE;
-
   return TRUE;
 }
 
@@ -1164,6 +1266,11 @@ void
 change_time_formats(capture_file *cf)
 {
   frame_data *fdata;
+  progdlg_t *progbar;
+  gboolean stop_flag;
+  guint32 progbar_quantum;
+  guint32 progbar_nextstep;
+  unsigned int count;
   int row;
   int i;
   GtkStyle  *pl_style;
@@ -1172,11 +1279,49 @@ change_time_formats(capture_file *cf)
      screen updates while it happens. */
   freeze_clist(cf);
 
+  /* Update the progress bar when it gets to this value. */
+  progbar_nextstep = 0;
+  /* When we reach the value that triggers a progress bar update,
+     bump that value by this amount. */
+  progbar_quantum = cf->count/N_PROGBAR_UPDATES;
+  /* Count of packets at which we've looked. */
+  count = 0;
+
+  stop_flag = FALSE;
+  progbar = create_progress_dlg("Changing time display", "Stop", &stop_flag);
+
   /* Iterate through the list of packets, checking whether the packet
      is in a row of the summary list and, if so, whether there are
      any columns that show the time in the "command-line-specified"
      format and, if so, update that row. */
   for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
+    /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
+       when we update it, we have to run the GTK+ main loop to get it
+       to repaint what's pending, and doing so may involve an "ioctl()"
+       to see if there's any pending input from an X server, and doing
+       that for every packet can be costly, especially on a big file. */
+    if (count >= progbar_nextstep) {
+      /* let's not divide by zero. I should never be started
+       * with count == 0, so let's assert that
+       */
+      g_assert(cf->count > 0);
+
+      update_progress_dlg(progbar, (gfloat) count / cf->count);
+
+      progbar_nextstep += progbar_quantum;
+    }
+
+    if (stop_flag) {
+      /* Well, the user decided to abort the redisplay.  Just stop.
+
+         XXX - this leaves the time field in the old format in
+        frames we haven't yet processed.  So it goes; should we
+        simply not offer them the option of stopping? */
+      break;
+    }
+
+    count++;
+
     /* Find what row this packet is in. */
     row = gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata);
 
@@ -1187,16 +1332,15 @@ change_time_formats(capture_file *cf)
          the answer isn't going to change from packet to packet, so we should
          simply skip all the "change_time_formats()" work if we're not
          changing anything. */
-      fdata->cinfo = &cf->cinfo;
-      if (check_col(fdata, COL_CLS_TIME)) {
+      if (check_col(&cf->cinfo, COL_CLS_TIME)) {
         /* There are columns that show the time in the "command-line-specified"
            format; update them. */
         for (i = 0; i < cf->cinfo.num_cols; i++) {
           if (cf->cinfo.fmt_matx[i][COL_CLS_TIME]) {
             /* This is one of the columns that shows the time in
                "command-line-specified" format; update it. */
-            cf->cinfo.col_data[i][0] = '\0';
-            col_set_cls_time(fdata, i);
+            cf->cinfo.col_buf[i][0] = '\0';
+            col_set_cls_time(fdata, &cf->cinfo, i);
             gtk_clist_set_text(GTK_CLIST(packet_list), row, i,
                          cf->cinfo.col_data[i]);
          }
@@ -1205,6 +1349,9 @@ change_time_formats(capture_file *cf)
     }
   }
 
+  /* We're done redisplaying the packets; destroy the progress bar. */
+  destroy_progress_dlg(progbar);
+
   /* Set the column widths of those columns that show the time in
      "command-line-specified" format. */
   pl_style = gtk_widget_get_style(packet_list);
@@ -1219,23 +1366,8 @@ change_time_formats(capture_file *cf)
   thaw_clist(cf);
 }
 
-static void
-clear_tree_and_hex_views(void)
-{
-  /* Clear the hex dump. */
-  gtk_text_freeze(GTK_TEXT(byte_view));
-  gtk_text_set_point(GTK_TEXT(byte_view), 0);
-  gtk_text_forward_delete(GTK_TEXT(byte_view),
-    gtk_text_get_length(GTK_TEXT(byte_view)));
-  gtk_text_thaw(GTK_TEXT(byte_view));
-
-  /* Remove all nodes in ctree. This is how it's done in testgtk.c in GTK+ */
-  gtk_clist_clear ( GTK_CLIST(tree_view) );
-
-}
-
 gboolean
-find_packet(capture_file *cf, dfilter *sfcode)
+find_packet(capture_file *cf, dfilter_t *sfcode)
 {
   frame_data *start_fd;
   frame_data *fdata;
@@ -1244,10 +1376,10 @@ find_packet(capture_file *cf, dfilter *sfcode)
   gboolean stop_flag;
   guint32 progbar_quantum;
   guint32 progbar_nextstep;
-  int count;
-  proto_tree *protocol_tree;
+  unsigned int count;
   gboolean frame_matched;
   int row;
+  epan_dissect_t       *edt;
 
   start_fd = cf->current_frame;
   if (start_fd != NULL)  {
@@ -1309,12 +1441,13 @@ find_packet(capture_file *cf, dfilter *sfcode)
       /* Is this packet in the display? */
       if (fdata->flags.passed_dfilter) {
         /* Yes.  Does it match the search filter? */
-        protocol_tree = proto_tree_create_root();
         wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
                        cf->pd, fdata->cap_len);
-        dissect_packet(&cf->pseudo_header, cf->pd, fdata, protocol_tree);
-        frame_matched = dfilter_apply(sfcode, protocol_tree, cf->pd, fdata->cap_len);
-        proto_tree_free(protocol_tree);
+        edt = epan_dissect_new(TRUE, FALSE);
+        epan_dissect_prime_dfilter(edt, sfcode);
+        epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+        frame_matched = dfilter_apply_edt(sfcode, edt);
+        epan_dissect_free(edt);
         if (frame_matched) {
           new_fd = fdata;
           break;       /* found it! */
@@ -1416,19 +1549,22 @@ select_packet(capture_file *cf, int row)
                        cf->pd, fdata->cap_len);
 
   /* Create the logical protocol tree. */
-  if (cf->protocol_tree)
-      proto_tree_free(cf->protocol_tree);
-  cf->protocol_tree = proto_tree_create_root();
-  proto_tree_is_visible = TRUE;
-  dissect_packet(&cf->pseudo_header, cf->pd, cf->current_frame,
-               cf->protocol_tree);
-  proto_tree_is_visible = FALSE;
-
-  /* Display the GUI protocol tree and hex dump. */
+  if (cf->edt != NULL) {
+    epan_dissect_free(cf->edt);
+    cf->edt = NULL;
+  }
+  /* We don't need the columns here. */
+  cf->edt = epan_dissect_new(TRUE, TRUE);
+  epan_dissect_run(cf->edt, &cf->pseudo_header, cf->pd, cf->current_frame,
+          NULL);
+
+  /* Display the GUI protocol tree and hex dump.
+     XXX - why does the protocol tree not show up if we call
+     "proto_tree_draw()" before calling "add_byte_views()"? */
   clear_tree_and_hex_views();
-  proto_tree_draw(cf->protocol_tree, tree_view);
-  packet_hex_print(GTK_TEXT(byte_view), cf->pd, cf->current_frame->cap_len,
-                       -1, -1, cf->current_frame->flags.encoding);
+  add_byte_views(cf->current_frame, cf->edt->tree, tree_view,
+      byte_nb_ptr);
+  proto_tree_draw(cf->edt->tree, tree_view);
 
   /* A packet is selected. */
   set_menus_for_selected_packet(TRUE);
@@ -1438,19 +1574,20 @@ select_packet(capture_file *cf, int row)
 void
 unselect_packet(capture_file *cf)
 {
-  /* Destroy the protocol tree for that packet. */
-  if (cf->protocol_tree != NULL) {
-    proto_tree_free(cf->protocol_tree);
-    cf->protocol_tree = NULL;
+  /* Destroy the epan_dissect_t for the unselected packet. */
+  if (cf->edt != NULL) {
+    epan_dissect_free(cf->edt);
+    cf->edt = NULL;
   }
 
-  finfo_selected = NULL;
-
   /* Clear out the display of that packet. */
   clear_tree_and_hex_views();
 
   /* No packet is selected. */
   set_menus_for_selected_packet(FALSE);
+
+  /* No protocol tree means no selected field. */
+  unselect_field();
 }
 
 /* Set the selected row and the focus row of the packet list to the specified
@@ -1474,6 +1611,35 @@ set_selected_row(int row)
   gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
 }
 
+/* Unset the selected protocol tree field, if any. */
+void
+unselect_field(void)
+{
+  statusbar_pop_field_msg();
+  finfo_selected = NULL;
+  set_menus_for_selected_tree_row(FALSE);
+}
+
+/*
+ * Mark a particular frame.
+ */
+void
+mark_frame(capture_file *cf, frame_data *frame)
+{
+  frame->flags.marked = TRUE;
+  cf->marked_count++;
+}
+
+/*
+ * Unmark a particular frame.
+ */
+void
+unmark_frame(capture_file *cf, frame_data *frame)
+{
+  frame->flags.marked = FALSE;
+  cf->marked_count--;
+}
+
 static void
 freeze_clist(capture_file *cf)
 {
@@ -1513,7 +1679,7 @@ thaw_clist(capture_file *cf)
 }
 
 int
-save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
+save_cap_file(char *fname, capture_file *cf, gboolean save_filtered, gboolean save_marked,
                guint save_format)
 {
   gchar        *from_filename;
@@ -1521,7 +1687,6 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
   size_t        msg_len;
   int           err;
   gboolean      do_copy;
-  int           from_fd, to_fd, nread, nwritten;
   wtap_dumper  *pdh;
   frame_data   *fdata;
   struct wtap_pkthdr hdr;
@@ -1532,10 +1697,10 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
   msg_len = strlen(name_ptr) + strlen(save_fmt) + 2;
   save_msg = g_malloc(msg_len);
   snprintf(save_msg, msg_len, save_fmt, name_ptr);
-  gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, save_msg);
+  statusbar_push_file_msg(save_msg);
   g_free(save_msg);
 
-  if (!save_filtered && save_format == cf->cd_t) {
+  if (!save_filtered && !save_marked && save_format == cf->cd_t) {
     /* We're not filtering packets, and we're saving it in the format
        it's already in, so we can just move or copy the raw data. */
 
@@ -1566,7 +1731,7 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
             the temporary directory, and that might be fixable - but
             is it worth requiring the user to go off and fix it?) */
          err = errno;
-         simple_dialog(ESD_TYPE_WARN, NULL,
+         simple_dialog(ESD_TYPE_CRIT, NULL,
                                file_rename_error_message(err), fname);
          goto done;
        }
@@ -1583,58 +1748,9 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
     }
     /* Copy the file, if we haven't moved it. */
     if (do_copy) {
-      /* Copy the raw bytes of the file. */
-      from_fd = open(from_filename, O_RDONLY | O_BINARY);
-      if (from_fd < 0) {
-       err = errno;
-       simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_open_error_message(err, TRUE), from_filename);
-       goto done;
-      }
-
-      /* Use open() instead of creat() so that we can pass the O_BINARY
-         flag, which is relevant on Win32; it appears that "creat()"
-        may open the file in text mode, not binary mode, but we want
-        to copy the raw bytes of the file, so we need the output file
-        to be open in binary mode. */
-      to_fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
-      if (to_fd < 0) {
-       err = errno;
-       simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_open_error_message(err, TRUE), fname);
-       close(from_fd);
-       goto done;
-      }
-
-      while ((nread = read(from_fd, pd, sizeof pd)) > 0) {
-       nwritten = write(to_fd, pd, nread);
-       if (nwritten < nread) {
-         if (nwritten < 0)
-           err = errno;
-         else
-           err = WTAP_ERR_SHORT_WRITE;
-         simple_dialog(ESD_TYPE_WARN, NULL,
-                               file_write_error_message(err), fname);
-         close(from_fd);
-         close(to_fd);
-         goto done;
-       }
-      }
-      if (nread < 0) {
-       err = errno;
-       simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_read_error_message(err), from_filename);
-       close(from_fd);
-       close(to_fd);
-       goto done;
-      }
-      close(from_fd);
-      if (close(to_fd) < 0) {
-       err = errno;
-       simple_dialog(ESD_TYPE_WARN, NULL,
-               file_close_error_message(err), fname);
-       goto done;
-      }
+           if (!copy_binary_file(from_filename, fname)) {
+               goto done;
+           }
     }
   } else {
     /* Either we're filtering packets, or we're saving in a different
@@ -1642,22 +1758,30 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
        we have to do it by writing the packets out in Wiretap. */
     pdh = wtap_dump_open(fname, save_format, cf->lnk_t, cf->snap, &err);
     if (pdh == NULL) {
-      simple_dialog(ESD_TYPE_WARN, NULL,
+      simple_dialog(ESD_TYPE_CRIT, NULL,
                        file_open_error_message(err, TRUE), fname);
       goto done;
     }
 
     /* XXX - have a way to save only the packets currently selected by
-       the display filter.
+       the display filter or the marked ones.
 
        If we do that, should we make that file the current file?  If so,
        it means we can no longer get at the other packets.  What does
        NetMon do? */
     for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
       /* XXX - do a progress bar */
-      if (!save_filtered || fdata->flags.passed_dfilter) {
-       /* Either we're saving all frames, or we're saving filtered frames
-          and this one passed the display filter - save it. */
+      if ((!save_filtered && !save_marked) ||
+         (save_filtered && fdata->flags.passed_dfilter && !save_marked) ||
+         (save_marked && fdata->flags.marked && !save_filtered) ||
+         (save_filtered && save_marked && fdata->flags.passed_dfilter &&
+          fdata->flags.marked)) {
+       /* Either :
+          - we're saving all frames, or
+          - we're saving filtered frames and this one passed the display filter or
+          - we're saving marked frames (and it has been marked) or
+          - we're saving filtered _and_ marked frames,
+          save it. */
         hdr.ts.tv_sec = fdata->abs_secs;
         hdr.ts.tv_usec = fdata->abs_usecs;
         hdr.caplen = fdata->cap_len;
@@ -1667,7 +1791,7 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
                pd, fdata->cap_len);
 
         if (!wtap_dump(pdh, &hdr, &pseudo_header, pd, &err)) {
-           simple_dialog(ESD_TYPE_WARN, NULL,
+           simple_dialog(ESD_TYPE_CRIT, NULL,
                                file_write_error_message(err), fname);
            wtap_dump_close(pdh, &err);
            goto done;
@@ -1685,9 +1809,9 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
 done:
 
   /* Pop the "Saving:" message off the status bar. */
-  gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+  statusbar_pop_file_msg();
   if (err == 0) {
-    if (!save_filtered) {
+    if (!save_filtered && !save_marked) {
       /* We saved the entire capture, not just some packets from it.
          Open and read the file we saved it to.
 
@@ -1726,7 +1850,7 @@ done:
 }
 
 char *
-file_open_error_message(int err, int for_writing)
+file_open_error_message(int err, gboolean for_writing)
 {
   char *errmsg;
   static char errmsg_errno[1024+1];
@@ -1734,7 +1858,7 @@ file_open_error_message(int err, int for_writing)
   switch (err) {
 
   case WTAP_ERR_NOT_REGULAR_FILE:
-    errmsg = "The file \"%s\" is invalid.";
+    errmsg = "The file \"%s\" is a \"special file\" or socket or other non-regular file.";
     break;
 
   case WTAP_ERR_FILE_UNKNOWN_FORMAT:
@@ -1769,7 +1893,7 @@ file_open_error_message(int err, int for_writing)
 
   case WTAP_ERR_SHORT_READ:
     errmsg = "The file \"%s\" appears to have been cut short"
-             " in the middle of a packet.";
+             " in the middle of a packet or other data.";
     break;
 
   case WTAP_ERR_SHORT_WRITE:
@@ -1790,8 +1914,13 @@ file_open_error_message(int err, int for_writing)
       errmsg = "You do not have permission to read the file \"%s\".";
     break;
 
+  case EISDIR:
+    errmsg = "\"%s\" is a directory (folder), not a file.";
+    break;
+
   default:
-    sprintf(errmsg_errno, "The file \"%%s\" could not be opened: %s.",
+    snprintf(errmsg_errno, sizeof(errmsg_errno),
+                   "The file \"%%s\" could not be opened: %s.",
                                wtap_strerror(err));
     errmsg = errmsg_errno;
     break;
@@ -1816,7 +1945,8 @@ file_rename_error_message(int err)
     break;
 
   default:
-    sprintf(errmsg_errno, "The file \"%%s\" could not be moved: %s.",
+    snprintf(errmsg_errno, sizeof(errmsg_errno),
+                   "The file \"%%s\" could not be moved: %s.",
                                wtap_strerror(err));
     errmsg = errmsg_errno;
     break;
@@ -1829,7 +1959,8 @@ file_read_error_message(int err)
 {
   static char errmsg_errno[1024+1];
 
-  sprintf(errmsg_errno, "An error occurred while reading from the file \"%%s\": %s.",
+  snprintf(errmsg_errno, sizeof(errmsg_errno),
+                 "An error occurred while reading from the file \"%%s\": %s.",
                                wtap_strerror(err));
   return errmsg_errno;
 }
@@ -1853,7 +1984,8 @@ file_write_error_message(int err)
 #endif
 
   default:
-    sprintf(errmsg_errno, "An error occurred while writing to the file \"%%s\": %s.",
+    snprintf(errmsg_errno, sizeof(errmsg_errno),
+                   "An error occurred while writing to the file \"%%s\": %s.",
                                wtap_strerror(err));
     errmsg = errmsg_errno;
     break;
@@ -1878,7 +2010,7 @@ file_close_error_message(int err)
     break;
 
   case WTAP_ERR_SHORT_WRITE:
-    errmsg = "Not all the data could be written to the file \"%s\".";
+    errmsg = "Not all the packets could be written to the file \"%s\".";
     break;
 
   case ENOSPC:
@@ -1892,10 +2024,83 @@ file_close_error_message(int err)
 #endif
 
   default:
-    sprintf(errmsg_errno, "An error occurred while closing the file \"%%s\": %s.",
+    snprintf(errmsg_errno, sizeof(errmsg_errno),
+                   "An error occurred while closing the file \"%%s\": %s.",
                                wtap_strerror(err));
     errmsg = errmsg_errno;
     break;
   }
   return errmsg;
 }
+
+
+/* Copies a file in binary mode, for those operating systems that care about
+ * such things.
+ * Returns TRUE on success, FALSE on failure. If a failure, it also
+ * displays a simple dialog window with the error message.
+ */
+static gboolean
+copy_binary_file(char *from_filename, char *to_filename)
+{
+       int           from_fd, to_fd, nread, nwritten, err;
+       guint8        pd[65536]; /* XXX - Hmm, 64K here, 64K in save_cap_file(),
+                                   perhaps we should make just one 64K buffer. */
+
+      /* Copy the raw bytes of the file. */
+      from_fd = open(from_filename, O_RDONLY | O_BINARY);
+      if (from_fd < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_CRIT, NULL,
+                       file_open_error_message(err, TRUE), from_filename);
+       goto done;
+      }
+
+      /* Use open() instead of creat() so that we can pass the O_BINARY
+         flag, which is relevant on Win32; it appears that "creat()"
+        may open the file in text mode, not binary mode, but we want
+        to copy the raw bytes of the file, so we need the output file
+        to be open in binary mode. */
+      to_fd = open(to_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+      if (to_fd < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_CRIT, NULL,
+                       file_open_error_message(err, TRUE), to_filename);
+       close(from_fd);
+       goto done;
+      }
+
+      while ((nread = read(from_fd, pd, sizeof pd)) > 0) {
+       nwritten = write(to_fd, pd, nread);
+       if (nwritten < nread) {
+         if (nwritten < 0)
+           err = errno;
+         else
+           err = WTAP_ERR_SHORT_WRITE;
+         simple_dialog(ESD_TYPE_CRIT, NULL,
+                               file_write_error_message(err), to_filename);
+         close(from_fd);
+         close(to_fd);
+         goto done;
+       }
+      }
+      if (nread < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_CRIT, NULL,
+                       file_read_error_message(err), from_filename);
+       close(from_fd);
+       close(to_fd);
+       goto done;
+      }
+      close(from_fd);
+      if (close(to_fd) < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_CRIT, NULL,
+               file_close_error_message(err), to_filename);
+       goto done;
+      }
+
+      return TRUE;
+
+   done:
+      return FALSE;
+}