various string related changes, mainly replace sprintf/snprintf by g_snprintf
[obnox/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index a08c3f0cdec74fe247f236655603acd71752b059..22a2ff778349addd219b347a0d7868d238aee2e1 100644 (file)
--- a/file.c
+++ b/file.c
@@ -1,22 +1,22 @@
 /* file.c
  * File I/O routines
  *
- * $Id: file.c,v 1.282 2002/07/16 07:15:04 guy Exp $
+ * $Id: file.c,v 1.370 2004/03/08 23:45:24 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * 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.
@@ -26,8 +26,6 @@
 # include "config.h"
 #endif
 
-#include <gtk/gtk.h>
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -41,6 +39,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 #include <errno.h>
 #include <signal.h>
 
 #include "strerror.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
 #include <epan/epan.h>
 #include <epan/filesystem.h>
 
-#include "gtk/main.h"
 #include "color.h"
-#include "gtk/color_utils.h"
 #include "column.h"
 #include <epan/packet.h>
+#include "range.h"
 #include "print.h"
 #include "file.h"
 #include "menu.h"
 #include "util.h"
+#include "alert_box.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 "gtk/packet_win.h"
 #include <epan/dfilter/dfilter.h>
 #include <epan/conversation.h>
 #include "globals.h"
-#include "gtk/colors.h"
 #include <epan/epan_dissect.h>
-
-extern GtkWidget *packet_list, *byte_nb_ptr, *tree_view;
+#include "tap.h"
+#include "tap_dfilter_dlg.h"
+#include "packet-data.h"
 
 #ifdef HAVE_LIBPCAP
 gboolean auto_scroll_live;
@@ -101,20 +90,39 @@ gboolean auto_scroll_live;
 
 static guint32 firstsec, firstusec;
 static guint32 prevsec, prevusec;
+static guint32 cum_bytes = 0;
 
 static void read_packet(capture_file *cf, long offset);
 
-static void rescan_packets(capture_file *cf, const char *action,
+static void rescan_packets(capture_file *cf, const char *action, const char *action_item,
        gboolean refilter, gboolean redissect);
 
-static void set_selected_row(int row);
-
-static void freeze_clist(capture_file *cf);
-static void thaw_clist(capture_file *cf);
-
+static gboolean match_protocol_tree(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static void match_subtree_text(proto_node *node, gpointer data);
+static gboolean match_summary_line(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static gboolean match_ascii_and_unicode(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static gboolean match_ascii(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static gboolean match_unicode(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static gboolean match_binary(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static gboolean match_dfilter(capture_file *cf, frame_data *fdata,
+       void *criterion);
+static gboolean find_packet(capture_file *cf,
+       gboolean (*match_function)(capture_file *, frame_data *, void *),
+       void *criterion);
+
+static void cf_open_failure_alert_box(const char *filename, int err,
+                                     gchar *err_info, gboolean for_writing,
+                                     int file_type);
 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);
+static void cf_write_failure_alert_box(const char *filename, int err);
+static void cf_close_failure_alert_box(const char *filename, 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
@@ -123,15 +131,26 @@ static gboolean copy_binary_file(char *from_filename, char *to_filename);
    XXX - is this the right number? */
 #define        FRAME_DATA_CHUNK_SIZE   1024
 
+typedef struct {
+       int             level;
+       FILE            *fh;
+       GSList          *src_list;
+       gboolean        print_all_levels;
+       gboolean        print_hex_for_data;
+       char_enc        encoding;
+       gint            format;         /* text or PostScript */
+} print_data;
+
 int
-open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
+cf_open(char *fname, gboolean is_tempfile, capture_file *cf)
 {
   wtap       *wth;
   int         err;
+  gchar       *err_info;
   int         fd;
   struct stat cf_stat;
 
-  wth = wtap_open_offline(fname, &err, TRUE);
+  wth = wtap_open_offline(fname, &err, &err_info, TRUE);
   if (wth == NULL)
     goto fail;
 
@@ -145,7 +164,7 @@ 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);
+  cf_close(cf);
 
   /* Initialize all data structures used for dissection. */
   init_dissection();
@@ -170,6 +189,7 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
 
   cf->cd_t      = wtap_file_type(cf->wth);
   cf->count     = 0;
+  cf->displayed_count = 0;
   cf->marked_count = 0;
   cf->drops_known = FALSE;
   cf->drops     = 0;
@@ -182,11 +202,9 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
     cf->snap = WTAP_MAX_PACKET_SIZE;
   } else
     cf->has_snap = TRUE;
-  cf->progbar_quantum = 0;
-  cf->progbar_nextstep = 0;
   firstsec = 0, firstusec = 0;
   prevsec = 0, prevusec = 0;
+
   cf->plist_chunk = g_mem_chunk_new("frame_data_chunk",
        sizeof(frame_data),
        FRAME_DATA_CHUNK_SIZE * sizeof(frame_data),
@@ -196,21 +214,20 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
   return (0);
 
 fail:
-  simple_dialog(ESD_TYPE_CRIT, NULL,
-                       file_open_error_message(err, FALSE, 0), fname);
+  cf_open_failure_alert_box(fname, err, err_info, FALSE, 0);
   return (err);
 }
 
 /* Reset everything to a pristine state */
 void
-close_cap_file(capture_file *cf)
+cf_close(capture_file *cf)
 {
   /* Die if we're in the middle of reading a file. */
   g_assert(cf->state != FILE_READ_IN_PROGRESS);
 
-  /* Destroy all popup packet windows, as they refer to packets in the
+  /* Destroy all windows, which refer to the
      capture file we're closing. */
-  destroy_packet_wins();
+  destroy_cfile_wins();
 
   if (cf->wth) {
     wtap_close(cf->wth);
@@ -241,10 +258,19 @@ close_cap_file(capture_file *cf)
   cf->first_displayed = NULL;
   cf->last_displayed = NULL;
 
+  /* No frame selected, no field in that frame selected. */
+  cf->current_frame = NULL;
+  cf->finfo_selected = NULL;
+
   /* Clear the packet list. */
-  gtk_clist_freeze(GTK_CLIST(packet_list));
-  gtk_clist_clear(GTK_CLIST(packet_list));
-  gtk_clist_thaw(GTK_CLIST(packet_list));
+  packet_list_freeze();
+  packet_list_clear();
+  packet_list_thaw();
+
+  cf->f_len = 0;
+  cf->count = 0;
+  cf->esec  = 0;
+  cf->eusec = 0;
 
   /* Clear any file-related status bar messages.
      XXX - should be "clear *ALL* file-related status bar messages;
@@ -258,9 +284,9 @@ close_cap_file(capture_file *cf)
   set_menus_for_capture_file(FALSE);
   set_menus_for_unsaved_capture_file(FALSE);
   set_menus_for_captured_packets(FALSE);
-  set_menus_for_selected_packet(FALSE);
+  set_menus_for_selected_packet(cf);
   set_menus_for_capture_in_progress(FALSE);
-  set_menus_for_selected_tree_row(FALSE);
+  set_menus_for_selected_tree_row(cf);
 
   /* We have no file open. */
   cf->state = FILE_CLOSED;
@@ -273,30 +299,34 @@ set_display_filename(capture_file *cf)
 {
   gchar  *name_ptr;
   size_t  msg_len;
-  static const gchar done_fmt_nodrops[] = " File: %s";
-  static const gchar done_fmt_drops[] = " File: %s  Drops: %u";
+  static const gchar done_fmt_nodrops[] = " File: %s %s %02u:%02u:%02u";
+  static const gchar done_fmt_drops[] = " File: %s %s %02u:%02u:%02u Drops: %u";
   gchar  *done_msg;
   gchar  *win_name_fmt = "%s - Ethereal";
   gchar  *win_name;
+  gchar  *size_str;
 
+  name_ptr = cf_get_display_name(cf);
+       
   if (!cf->is_tempfile) {
-    /* Get the last component of the file name, and put that in the
-       status bar. */
-    name_ptr = get_basename(cf->filename);
+    /* Add this filename to the list of recent files in the "Recent Files" submenu */
+    add_menu_recent_capture_file(cf->filename);
+  }
+
+  if (cf->f_len/1024/1024 > 10) {
+    size_str = g_strdup_printf("%ld MB", cf->f_len/1024/1024);
+  } else if (cf->f_len/1024 > 10) {
+    size_str = g_strdup_printf("%ld KB", cf->f_len/1024);
   } else {
-    /* The file we read is a temporary file from a live capture;
-       we don't mention its name in the status bar. */
-    name_ptr = "<capture>";
+    size_str = g_strdup_printf("%ld bytes", cf->f_len);
   }
 
   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);
+    done_msg = g_strdup_printf(done_fmt_drops, name_ptr, size_str, 
+        cf->esec/3600, cf->esec%3600/60, cf->esec%60, 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);
+    done_msg = g_strdup_printf(done_fmt_nodrops, name_ptr, size_str,
+        cf->esec/3600, cf->esec%3600/60, cf->esec%60);
   }
   statusbar_push_file_msg(done_msg);
   g_free(done_msg);
@@ -309,51 +339,64 @@ set_display_filename(capture_file *cf)
 }
 
 read_status_t
-read_cap_file(capture_file *cf, int *err)
+cf_read(capture_file *cf)
 {
-  gchar      *name_ptr, *load_msg, *load_fmt = " Loading: %s...";
-  size_t      msg_len;
+  int        err;
+  gchar      *err_info;
+  gchar      *name_ptr, *load_msg, *load_fmt = "%s";
   char       *errmsg;
   char        errmsg_errno[1024+1];
   gchar       err_str[2048+1];
   long        data_offset;
-  progdlg_t  *progbar;
+  progdlg_t  *progbar = NULL;
   gboolean    stop_flag;
-  int         file_pos;
+  /*
+   * XXX - should be "off_t", but Wiretap would need more work to handle
+   * the full size of "off_t" on platforms where it's more than a "long"
+   * as well.
+   */
+  long        file_pos;
   float       prog_val;
   int         fd;
   struct stat cf_stat;
-
+  GTimeVal    start_time;
+  gchar       status_str[100];
+  int         progbar_nextstep;
+  int         progbar_quantum;
+
+  cum_bytes=0;
+  reset_tap_listeners();
+  tap_dfilter_dlg_update();
   name_ptr = get_basename(cf->filename);
 
-  msg_len = strlen(name_ptr) + strlen(load_fmt) + 2;
-  load_msg = g_malloc(msg_len);
-  snprintf(load_msg, msg_len, load_fmt, name_ptr);
+  load_msg = g_strdup_printf(" Loading: %s", name_ptr);
   statusbar_push_file_msg(load_msg);
+  g_free(load_msg);
+
+  load_msg = g_strdup_printf(load_fmt, name_ptr);
 
   /* Update the progress bar when it gets to this value. */
-  cf->progbar_nextstep = 0;
+  progbar_nextstep = 0;
   /* When we reach the value that triggers a progress bar update,
      bump that value by this amount. */
-  cf->progbar_quantum = cf->f_len/N_PROGBAR_UPDATES;
+  progbar_quantum = cf->f_len/N_PROGBAR_UPDATES;
 
 #ifndef O_BINARY
 #define O_BINARY       0
 #endif
 
-  freeze_clist(cf);
+  packet_list_freeze();
 
   stop_flag = FALSE;
-  progbar = create_progress_dlg(load_msg, "Stop", &stop_flag);
-  g_free(load_msg);
+  g_get_current_time(&start_time);
 
-  while ((wtap_read(cf->wth, err, &data_offset))) {
+  while ((wtap_read(cf->wth, &err, &err_info, &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()"
        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 (data_offset >= cf->progbar_nextstep) {
+    if (data_offset >= progbar_nextstep) {
         file_pos = lseek(cf->filed, 0, SEEK_CUR);
         prog_val = (gfloat) file_pos / (gfloat) cf->f_len;
         if (prog_val > 1.0) {
@@ -371,8 +414,19 @@ read_cap_file(capture_file *cf, int *err)
           if (prog_val > 1.0)
             prog_val = 1.0;
         }
-        update_progress_dlg(progbar, prog_val);
-        cf->progbar_nextstep += cf->progbar_quantum;
+        if (progbar == NULL) {
+          /* Create the progress bar if necessary */
+          progbar = delayed_create_progress_dlg("Loading", load_msg,
+            &stop_flag, &start_time, prog_val);
+          if (progbar != NULL)
+            g_free(load_msg);
+        }
+        if (progbar != NULL) {
+          g_snprintf(status_str, sizeof(status_str),
+                     "%luKB of %luKB", file_pos / 1024, cf->f_len / 1024);
+          update_progress_dlg(progbar, prog_val, status_str);
+        }
+        progbar_nextstep += progbar_quantum;
     }
 
     if (stop_flag) {
@@ -381,15 +435,18 @@ read_cap_file(capture_file *cf, int *err)
         can do whatever is appropriate when that happens. */
       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);
+      packet_list_thaw();              /* undo our freeze */
+      cf_close(cf);
       return (READ_ABORTED);
     }
     read_packet(cf, data_offset);
   }
 
-  /* We're done reading the file; destroy the progress bar. */
-  destroy_progress_dlg(progbar);
+  /* We're done reading the file; destroy the progress bar if it was created. */
+  if (progbar == NULL)
+    g_free(load_msg);
+  else
+    destroy_progress_dlg(progbar);
 
   /* We're done reading sequentially through the file. */
   cf->state = FILE_READ_DONE;
@@ -408,7 +465,7 @@ read_cap_file(capture_file *cf, int *err)
   cf->lnk_t = wtap_file_encap(cf->wth);
 
   cf->current_frame = cf->first_displayed;
-  thaw_clist(cf);
+  packet_list_thaw();
 
   statusbar_pop_file_msg();
   set_display_filename(cf);
@@ -424,13 +481,13 @@ read_cap_file(capture_file *cf, int *err)
   /* If we have any displayed packets to select, select the first of those
      packets by making the first row the selected row. */
   if (cf->first_displayed != NULL)
-    gtk_signal_emit_by_name(GTK_OBJECT(packet_list), "select_row", 0);
+    packet_list_select_row(0);
 
-  if (*err != 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. */
-    switch (*err) {
+    switch (err) {
 
     case WTAP_ERR_UNSUPPORTED_ENCAP:
       errmsg = "The capture file is for a network type that Ethereal doesn't support.";
@@ -447,18 +504,21 @@ read_cap_file(capture_file *cf, int *err)
       break;
 
     case WTAP_ERR_BAD_RECORD:
-      errmsg = "The capture file appears to be damaged or corrupt.";
+      snprintf(errmsg_errno, sizeof(errmsg_errno),
+               "The capture file appears to be damaged or corrupt.\n(%s)",
+               err_info);
+      errmsg = errmsg_errno;
       break;
 
     default:
       snprintf(errmsg_errno, sizeof(errmsg_errno),
               "An error occurred while reading the"
-              " capture file: %s.", wtap_strerror(*err));
+              " capture file: %s.", wtap_strerror(err));
       errmsg = errmsg_errno;
       break;
     }
     snprintf(err_str, sizeof err_str, errmsg);
-    simple_dialog(ESD_TYPE_CRIT, NULL, err_str);
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, err_str);
     return (READ_ERROR);
   } else
     return (READ_SUCCESS);
@@ -466,12 +526,11 @@ read_cap_file(capture_file *cf, int *err)
 
 #ifdef HAVE_LIBPCAP
 int
-start_tail_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
+cf_start_tail(char *fname, gboolean is_tempfile, capture_file *cf)
 {
   int     err;
-  int     i;
 
-  err = open_cap_file(fname, is_tempfile, cf);
+  err = cf_open(fname, is_tempfile, cf);
   if (err == 0) {
     /* Disable menu items that make no sense if you're currently running
        a capture. */
@@ -481,30 +540,22 @@ start_tail_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
        packets (yes, I know, we don't have any *yet*). */
     set_menus_for_captured_packets(TRUE);
 
-    for (i = 0; i < cf->cinfo.num_cols; i++) {
-      if (get_column_resize_type(cf->cinfo.col_fmt[i]) == RESIZE_LIVE)
-        gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
-      else {
-        gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, FALSE);
-        gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
-                               cf->cinfo.col_width[i]);
-        gtk_clist_set_column_resizeable(GTK_CLIST(packet_list), i, TRUE);
-      }
-    }
-
     statusbar_push_file_msg(" <live capture in progress>");
   }
   return err;
 }
 
 read_status_t
-continue_tail_cap_file(capture_file *cf, int to_read, int *err)
+cf_continue_tail(capture_file *cf, int to_read, int *err)
 {
   long data_offset = 0;
+  gchar *err_info;
 
-  gtk_clist_freeze(GTK_CLIST(packet_list));
+  *err = 0;
 
-  while (to_read != 0 && (wtap_read(cf->wth, err, &data_offset))) {
+  packet_list_freeze();
+
+  while (to_read != 0 && (wtap_read(cf->wth, err, &err_info, &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
@@ -515,19 +566,18 @@ continue_tail_cap_file(capture_file *cf, int to_read, int *err)
     to_read--;
   }
 
-  gtk_clist_thaw(GTK_CLIST(packet_list));
+  packet_list_thaw();
 
   /* XXX - this cheats and looks inside the packet list to find the final
      row number. */
   if (auto_scroll_live && cf->plist_end != NULL)
-    gtk_clist_moveto(GTK_CLIST(packet_list), 
-                      GTK_CLIST(packet_list)->rows - 1, -1, 1.0, 1.0);
+    packet_list_moveto_end();
 
   if (cf->state == FILE_READ_ABORTED) {
     /* Well, the user decided to exit Ethereal.  Return READ_ABORTED
        so that our caller can kill off the capture child process;
        this will cause an EOF on the pipe from the child, so
-       "finish_tail_cap_file()" will be called, and it will clean up
+       "cf_finish_tail()" will be called, and it will clean up
        and exit. */
     return READ_ABORTED;
   } else if (*err != 0) {
@@ -539,13 +589,16 @@ continue_tail_cap_file(capture_file *cf, int to_read, int *err)
 }
 
 read_status_t
-finish_tail_cap_file(capture_file *cf, int *err)
+cf_finish_tail(capture_file *cf, int *err)
 {
+  gchar *err_info;
   long data_offset;
+  int         fd;
+  struct stat cf_stat;
 
-  gtk_clist_freeze(GTK_CLIST(packet_list));
+  packet_list_freeze();
 
-  while ((wtap_read(cf->wth, err, &data_offset))) {
+  while ((wtap_read(cf->wth, err, &err_info, &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
@@ -561,20 +614,26 @@ 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);
+    cf_close(cf);
     return READ_ABORTED;
   }
 
-  thaw_clist(cf);
+  packet_list_thaw();
   if (auto_scroll_live && 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), 
-                      GTK_CLIST(packet_list)->rows - 1, -1, 1.0, 1.0);
+    packet_list_moveto_end();
 
   /* We're done reading sequentially through the file. */
   cf->state = FILE_READ_DONE;
 
+  /* we have to update the f_len field */
+  /* Find the size of the file. */
+  fd = wtap_fd(cf->wth);
+  if (fstat(fd, &cf_stat) >= 0) {
+      cf->f_len = cf_stat.st_size;
+  }
+
   /* We're done reading sequentially through the file; close the
      sequential I/O side, to free up memory it requires. */
   wtap_sequential_close(cf->wth);
@@ -607,11 +666,36 @@ finish_tail_cap_file(capture_file *cf, int *err)
     /* We got an error reading the capture file.
        XXX - pop up a dialog box? */
     return (READ_ERROR);
-  } else
+  } else {
     return (READ_SUCCESS);
+  }
 }
 #endif /* HAVE_LIBPCAP */
 
+gchar *
+cf_get_display_name(capture_file *cf)
+{
+  gchar *displayname;
+
+  /* Return a name to use in displays */
+  if (!cf->is_tempfile) {
+    /* Get the last component of the file name, and use that. */
+    if (cf->filename){
+      displayname = get_basename(cf->filename);
+      
+      /* Add this filename to the list of recent files in the "Recent Files" submenu */
+      add_menu_recent_capture_file(cf->filename);
+    } else {
+      displayname="(No file)";
+    }
+  } else {
+    /* The file we read is a temporary file from a live capture;
+       we don't mention its name. */
+    displayname = "(Untitled)";
+  }
+  return displayname;
+}
+
 typedef struct {
   color_filter_t *colorf;
   epan_dissect_t *edt;
@@ -636,14 +720,16 @@ apply_color_filter(gpointer filter_arg, gpointer argp)
 
 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 guchar *buf,
        gboolean refilter)
 {
   apply_color_filter_args args;
   gint          row;
   gboolean     create_proto_tree = FALSE;
   epan_dissect_t *edt;
-  GdkColor      fg, bg;
+
+  /* just add some value here until we know if it is being displayed or not */
+  fdata->cum_bytes  = cum_bytes + fdata->pkt_len;
 
   /* We don't yet have a color filter to apply. */
   args.colorf = NULL;
@@ -655,6 +741,39 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     firstsec  = fdata->abs_secs;
     firstusec = fdata->abs_usecs;
   }
+  /* if this frames is marked as a reference time frame, reset
+     firstsec and firstusec to this frame */
+  if(fdata->flags.ref_time){
+    firstsec  = fdata->abs_secs;
+    firstusec = fdata->abs_usecs;
+  }
+
+  /* If we don't have the time stamp of the previous displayed packet,
+     it's because this is the first displayed packet.  Save the time
+     stamp of this packet as the time stamp of the previous displayed
+     packet. */
+  if (!prevsec && !prevusec) {
+    prevsec  = fdata->abs_secs;
+    prevusec = fdata->abs_usecs;
+  }
+
+  /* Get the time elapsed between the first packet and this packet. */
+  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. */
+  compute_timestamp_diff(&fdata->del_secs, &fdata->del_usecs,
+       fdata->abs_secs, fdata->abs_usecs, prevsec, prevusec);
 
   /* If either
 
@@ -662,10 +781,13 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
 
        we have a list of color filters;
 
+       we have tap listeners;
+
      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)
+  if ((cf->dfcode != NULL && refilter) || filter_list != NULL
+        || num_tap_filters != 0)
          create_proto_tree = TRUE;
 
   /* Dissect the frame. */
@@ -677,8 +799,9 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
   if (filter_list) {
       filter_list_prime_edt(edt);
   }
+  tap_queue_init(edt);
   epan_dissect_run(edt, pseudo_header, buf, fdata, &cf->cinfo);
-
+  tap_push_tapped_queue(edt);
 
   /* If we have a display filter, apply it if we're refiltering, otherwise
      leave the "passed_dfilter" flag alone.
@@ -704,37 +827,19 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
   }
 
 
-  if (fdata->flags.passed_dfilter) {
-    /* This frame passed the display filter, so add it to the clist. */
-
-    /* If we don't have the time stamp of the previous displayed packet,
-       it's because this is the first displayed packet.  Save the time
-       stamp of this packet as the time stamp of the previous displayed
-       packet. */
-    if (!prevsec && !prevusec) {
-      prevsec  = fdata->abs_secs;
-      prevusec = fdata->abs_usecs;
+  if( (fdata->flags.passed_dfilter) 
+   || (edt->pi.fd->flags.ref_time) ){
+    /* This frame either passed the display filter list or is marked as
+       a time reference frame.  All time reference frames are displayed
+       even if they dont pass the display filter */
+    /* if this was a TIME REF frame we should reset the cul bytes field */
+    if(edt->pi.fd->flags.ref_time){
+      cum_bytes = fdata->pkt_len;
+      fdata->cum_bytes  = cum_bytes;
     }
 
-    /* Get the time elapsed between the first packet and this packet. */
-    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. */
-    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;
+    /* increase cum_bytes with this packets length */
+    cum_bytes += fdata->pkt_len;
 
     epan_dissect_fill_in_columns(edt);
 
@@ -757,21 +862,34 @@ 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), cf->cinfo.col_data);
-    gtk_clist_set_row_data(GTK_CLIST(packet_list), row, fdata);
-
-    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;
+    row = packet_list_append(cf->cinfo.col_data, fdata);
+
+    /* If the packet matches a color filter,
+     * store matching color_filter_t object in frame data. */
+    if (filter_list != NULL && (args.colorf != NULL)) {
+      /* add the matching colorfilter to the frame data */
+      fdata->color_filter = args.colorf;
+      /* If packet is marked, use colors from preferences */
+      if (fdata->flags.marked) {
+          packet_list_set_colors(row, &prefs.gui_marked_fg, &prefs.gui_marked_bg);
+      } else /* if (filter_list != NULL && (args.colorf != NULL)) */ {
+          packet_list_set_colors(row, &(args.colorf->fg_color),
+             &(args.colorf->bg_color));
+      }
     } else {
-       bg = WHITE;
-       fg = BLACK;
+      /* No color filter match */
+      fdata->color_filter = NULL;
+      if (fdata->flags.marked) {
+          packet_list_set_colors(row, &prefs.gui_marked_fg, &prefs.gui_marked_bg);
+      }
     }
-    gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
-    gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
+
+    /* Set the time of the previous displayed frame to the time of this
+       frame. */
+    prevsec = fdata->abs_secs;
+    prevusec = fdata->abs_usecs;
+
+    cf->displayed_count++;
   } else {
     /* This frame didn't pass the display filter, so it's not being added
        to the clist, and thus has no row. */
@@ -786,7 +904,7 @@ 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);
+  const guchar *buf = wtap_buf_ptr(cf->wth);
   frame_data   *fdata;
   int           passed;
   frame_data   *plist_end;
@@ -807,6 +925,7 @@ read_packet(capture_file *cf, long offset)
   fdata->flags.encoding = CHAR_ASCII;
   fdata->flags.visited = 0;
   fdata->flags.marked = 0;
+  fdata->flags.ref_time = 0;
 
   passed = TRUE;
   if (cf->rfcode) {
@@ -815,7 +934,7 @@ read_packet(capture_file *cf, long offset)
     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;
     fdata->prev = plist_end;
@@ -841,10 +960,17 @@ read_packet(capture_file *cf, long offset)
   }
 }
 
-int
-filter_packets(capture_file *cf, gchar *dftext)
+gboolean
+filter_packets(capture_file *cf, gchar *dftext, gboolean force)
 {
   dfilter_t *dfcode;
+  char      *filter_new = dftext ? dftext : "";
+  char      *filter_old = cf->dfilter ? cf->dfilter : "";
+
+  /* if new filter equals old one, do nothing unless told to do so */
+  if (!force && strcmp(filter_new, filter_old) == 0) {
+    return TRUE;
+  }
 
   if (dftext == NULL) {
     /* The new filter is an empty filter (i.e., display all packets). */
@@ -857,8 +983,15 @@ filter_packets(capture_file *cf, gchar *dftext)
     dftext = g_strdup(dftext);
     if (!dfilter_compile(dftext, &dfcode)) {
       /* The attempt failed; report an error. */
-      simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
-      return 0;
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, 
+          "%s%s%s\n"
+          "\n"
+          "The display filter \"%s\" is not a valid display filter.\n"
+          "See the help for a description of the display filter syntax.",
+          simple_dialog_primary_start(), dfilter_error_msg,
+          simple_dialog_primary_end(), dftext);
+      g_free(dftext);
+      return FALSE;
     }
 
     /* Was it empty? */
@@ -879,20 +1012,30 @@ filter_packets(capture_file *cf, gchar *dftext)
 
   /* 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;
+  if (dftext == NULL) {
+    rescan_packets(cf, "Resetting", "Filter", TRUE, FALSE);
+  } else {
+    rescan_packets(cf, "Filtering", dftext, TRUE, FALSE);
+  }
+  return TRUE;
 }
 
 void
 colorize_packets(capture_file *cf)
 {
-  rescan_packets(cf, "Colorizing", FALSE, FALSE);
+  rescan_packets(cf, "Colorizing", "all packets", FALSE, FALSE);
+}
+
+void
+reftime_packets(capture_file *cf)
+{
+  rescan_packets(cf, "Updating Reftime", "all packets", FALSE, FALSE);
 }
 
 void
 redissect_packets(capture_file *cf)
 {
-  rescan_packets(cf, "Reprocessing", TRUE, TRUE);
+  rescan_packets(cf, "Reprocessing", "all packets", TRUE, TRUE);
 }
 
 /* Rescan the list of packets, reconstructing the CList.
@@ -900,6 +1043,9 @@ redissect_packets(capture_file *cf)
    "action" describes why we're doing this; it's used in the progress
    dialog box.
 
+   "action_item" describes what we're doing; 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
@@ -907,20 +1053,27 @@ redissect_packets(capture_file *cf)
    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)
+rescan_packets(capture_file *cf, const char *action, const char *action_item,
+               gboolean refilter, gboolean redissect)
 {
   frame_data *fdata;
-  progdlg_t *progbar;
-  gboolean stop_flag;
-  guint32 progbar_quantum;
-  guint32 progbar_nextstep;
-  unsigned int count;
-  int err;
-  frame_data *selected_frame;
-  int selected_row;
-  int row;
+  progdlg_t  *progbar = NULL;
+  gboolean    stop_flag;
+  int         count;
+  int         err;
+  gchar      *err_info;
+  frame_data *selected_frame, *preceding_frame, *following_frame, *prev_frame;
+  int         selected_row, prev_row, preceding_row, following_row;
+  gboolean    selected_frame_seen;
+  int         row;
+  float       prog_val;
+  GTimeVal    start_time;
+  gchar       status_str[100];
+  int         progbar_nextstep;
+  int         progbar_quantum;
 
+  cum_bytes=0;
+  reset_tap_listeners();
   /* Which frame, if any, is the currently selected frame?
      XXX - should the selected frame or the focus frame be the "current"
      frame, that frame being the one from which "Find Frame" searches
@@ -943,15 +1096,18 @@ rescan_packets(capture_file *cf, const char *action, gboolean refilter,
 
   /* Freeze the packet list while we redo it, so we don't get any
      screen updates while it happens. */
-  gtk_clist_freeze(GTK_CLIST(packet_list));
+  packet_list_freeze();
 
   /* Clear it out. */
-  gtk_clist_clear(GTK_CLIST(packet_list));
+  packet_list_clear();
 
   /* We don't yet know which will be the first and last frames displayed. */
   cf->first_displayed = NULL;
   cf->last_displayed = NULL;
 
+  /* We currently don't display any packets */
+  cf->displayed_count = 0;
+
   /* 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. */
@@ -969,7 +1125,18 @@ rescan_packets(capture_file *cf, const char *action, gboolean refilter,
   count = 0;
 
   stop_flag = FALSE;
-  progbar = create_progress_dlg(action, "Stop", &stop_flag);
+  g_get_current_time(&start_time);
+
+  row = -1;            /* no previous row yet */
+  prev_row = -1;
+  prev_frame = NULL;
+
+  preceding_row = -1;
+  preceding_frame = NULL;
+  following_row = -1;
+  following_frame = NULL;
+
+  selected_frame_seen = FALSE;
 
   for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
     /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
@@ -982,8 +1149,18 @@ rescan_packets(capture_file *cf, const char *action, gboolean refilter,
        * with count == 0, so let's assert that
        */
       g_assert(cf->count > 0);
+      prog_val = (gfloat) count / cf->count;
+
+      if (progbar == NULL)
+        /* Create the progress bar if necessary */
+        progbar = delayed_create_progress_dlg(action, action_item, &stop_flag,
+          &start_time, prog_val);
 
-      update_progress_dlg(progbar, (gfloat) count / cf->count);
+      if (progbar != NULL) {
+        g_snprintf(status_str, sizeof(status_str),
+                  "%4u of %u frames", count, cf->count);
+        update_progress_dlg(progbar, prog_val, status_str);
+      }
 
       progbar_nextstep += progbar_quantum;
     }
@@ -1018,16 +1195,42 @@ rescan_packets(capture_file *cf, const char *action, gboolean refilter,
       }
     }
 
-    /* XXX - do something with "err" */
-    wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
-       cf->pd, fdata->cap_len, &err);
+    if (!wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
+       cf->pd, fdata->cap_len, &err, &err_info)) {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     cf_read_error_message(err, err_info), cf->filename);
+       break;
+    }
 
+    /* If the previous frame is displayed, and we haven't yet seen the
+       selected frame, remember that frame - it's the closest one we've
+       yet seen before the selected frame. */
+    if (prev_row != -1 && !selected_frame_seen) {
+      preceding_row = prev_row;
+      preceding_frame = prev_frame;
+    }
     row = add_packet_to_packet_list(fdata, cf, &cf->pseudo_header, cf->pd,
                                        refilter);
-    if (fdata == selected_frame)
+
+    /* If this frame is displayed, and this is the first frame we've
+       seen displayed after the selected frame, remember this frame -
+       it's the closest one we've yet seen at or after the selected
+       frame. */
+    if (row != -1 && selected_frame_seen && following_row == -1) {
+      following_row = row;
+      following_frame = fdata;
+    }
+    if (fdata == selected_frame) {
       selected_row = row;
+      selected_frame_seen = TRUE;
+    }
+
+    /* Remember this row/frame - it'll be the previous row/frame
+       on the next pass through the loop. */
+    prev_row = row;
+    prev_frame = fdata;
   }
+
   if (redissect) {
     /* Clear out what remains of the visited flags and per-frame data
        pointers.
@@ -1047,101 +1250,96 @@ rescan_packets(capture_file *cf, const char *action, gboolean refilter,
     }
   }
 
-  /* We're done filtering the packets; destroy the progress bar. */
-  destroy_progress_dlg(progbar);
+  /* We're done filtering the packets; destroy the progress bar if it
+     was created. */
+  if (progbar != NULL)
+    destroy_progress_dlg(progbar);
 
   /* Unfreeze the packet list. */
-  gtk_clist_thaw(GTK_CLIST(packet_list));
+  packet_list_thaw();
+
+  if (selected_row == -1) {
+    /* The selected frame didn't pass the filter. */
+    if (selected_frame == NULL) {
+      /* That's because there *was* no selected frame.  Make the first
+         displayed frame the current frame. */
+      selected_row = 0;
+    } else {
+      /* Find the nearest displayed frame to the selected frame (whether
+         it's before or after that frame) and make that the current frame.
+         If the next and previous displayed frames are equidistant from the
+         selected frame, choose the next one. */
+      g_assert(following_frame == NULL ||
+               following_frame->num >= selected_frame->num);
+      g_assert(preceding_frame == NULL ||
+               preceding_frame->num <= selected_frame->num);
+      if (following_frame == NULL) {
+        /* No frame after the selected frame passed the filter, so we
+           have to select the last displayed frame before the selected
+           frame. */
+        selected_row = preceding_row;
+      } else if (preceding_frame == NULL) {
+        /* No frame before the selected frame passed the filter, so we
+           have to select the first displayed frame after the selected
+           frame. */
+        selected_row = following_row;
+      } else {
+        /* Choose the closer of the last displayed frame before the
+           selected frame and the first displayed frame after the
+           selected frame; in case of a tie, choose the first displayed
+           frame after the selected frame. */
+        if (following_frame->num - selected_frame->num <=
+            selected_frame->num - preceding_frame->num) {
+          selected_row = following_row;
+        } else {
+          /* The previous frame is closer to the selected frame than the
+             next frame. */
+          selected_row = preceding_row;
+        }
+      }
+    }
+  }
 
-  if (selected_row != -1) {
-    /* The frame that was selected passed the filter; select it, make it
-       the focus row, and make it visible. */
-    set_selected_row(selected_row);
-    finfo_selected = NULL;
-  } else {
-    /* The selected frame didn't pass the filter; make the first frame
-       the current frame, and leave it unselected. */
+  if (selected_row == -1) {
+    /* There are no frames displayed at all. */
     unselect_packet(cf);
-    cf->current_frame = cf->first_displayed;
+  } else {
+    /* Either the frame that was selected passed the filter, or we've
+       found the nearest displayed frame to that frame.  Select it, make
+       it the focus row, and make it visible. */
+    packet_list_set_selected_row(selected_row);
   }
 }
 
-int
-print_packets(capture_file *cf, print_args_t *print_args)
+typedef enum {
+  PSP_FINISHED,
+  PSP_STOPPED,
+  PSP_FAILED
+} psp_return_t;
+
+psp_return_t
+process_specified_packets(capture_file *cf, packet_range_t *range,
+    const char *string1, const char *string2,
+    gboolean (*callback)(capture_file *, frame_data *,
+                         union wtap_pseudo_header *, const guint8 *, void *),
+    void *callback_args)
 {
-  int         i;
   frame_data *fdata;
-  progdlg_t  *progbar;
-  gboolean    stop_flag;
-  guint32     progbar_quantum;
-  guint32     progbar_nextstep;
-  guint32     count;
   int         err;
-  gint       *col_widths = NULL;
-  gint        data_width;
-  gboolean    print_separator;
-  char       *line_buf = NULL;
-  int         line_buf_len = 256;
-  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)
-    return FALSE;      /* attempt to open destination failed */
-
-  print_preamble(cf->print_fh, print_args->format);
-
-  if (print_args->print_summary) {
-    /* We're printing packet summaries.  Allocate the line buffer at
-       its initial length. */
-    line_buf = g_malloc(line_buf_len + 1);
-
-    /* Find the widths for each of the columns - maximum of the
-       width of the title and the width of the data - and print
-       the column titles. */
-    col_widths = (gint *) g_malloc(sizeof(gint) * cf->cinfo.num_cols);
-    cp = &line_buf[0];
-    line_len = 0;
-    for (i = 0; i < cf->cinfo.num_cols; i++) {
-      /* Don't pad the last column. */
-      if (i == cf->cinfo.num_cols - 1)
-        col_widths[i] = 0;
-      else {
-        col_widths[i] = strlen(cf->cinfo.col_title[i]);
-        data_width = get_column_char_width(get_column_format(i));
-        if (data_width > col_widths[i])
-          col_widths[i] = data_width;
-      }
-
-      /* Find the length of the string for this column. */
-      column_len = strlen(cf->cinfo.col_title[i]);
-      if (col_widths[i] > column_len)
-        column_len = col_widths[i];
-
-      /* Make sure there's room in the line buffer for the column; if not,
-         double its length. */
-      line_len += column_len + 1;      /* "+1" for space */
-      if (line_len > line_buf_len) {
-        line_buf_len *= 2;
-        line_buf = g_realloc(line_buf, line_buf_len + 1);
-      }
-
-      /* Right-justify the packet number column. */
-      if (cf->cinfo.col_fmt[i] == COL_NUMBER)
-        sprintf(cp, "%*s", col_widths[i], cf->cinfo.col_title[i]);
-      else
-        sprintf(cp, "%-*s", col_widths[i], cf->cinfo.col_title[i]);
-      cp += column_len;
-      if (i != cf->cinfo.num_cols - 1)
-        *cp++ = ' ';
-    }
-    *cp = '\0';
-    print_line(cf->print_fh, 0, print_args->format, line_buf);
-  }
-
-  print_separator = FALSE;
+  gchar      *err_info;
+  union wtap_pseudo_header pseudo_header;
+  guint8      pd[WTAP_MAX_PACKET_SIZE+1];
+  psp_return_t ret = PSP_FINISHED;
+
+  progdlg_t  *progbar = NULL;
+  int         progbar_count;
+  float       progbar_val;
+  gboolean    progbar_stop_flag;
+  GTimeVal    progbar_start_time;
+  gchar       progbar_status_str[100];
+  int         progbar_nextstep;
+  int         progbar_quantum;
+  range_process_e process_this;
 
   /* Update the progress bar when it gets to this value. */
   progbar_nextstep = 0;
@@ -1149,10 +1347,12 @@ print_packets(capture_file *cf, print_args_t *print_args)
      bump that value by this amount. */
   progbar_quantum = cf->count/N_PROGBAR_UPDATES;
   /* Count of packets at which we've looked. */
-  count = 0;
+  progbar_count = 0;
 
-  stop_flag = FALSE;
-  progbar = create_progress_dlg("Printing", "Stop", &stop_flag);
+  progbar_stop_flag = FALSE;
+  g_get_current_time(&progbar_start_time);
+
+  packet_range_process_init(range);
 
   /* Iterate through the list of packets, printing the packets that
      were selected by the current display filter.  */
@@ -1162,174 +1362,437 @@ print_packets(capture_file *cf, print_args_t *print_args)
        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) {
+    if (progbar_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_val = (gfloat) progbar_count / cf->count;
+
+      if (progbar == NULL)
+        /* Create the progress bar if necessary */
+        progbar = delayed_create_progress_dlg(string1, string2,
+                                              &progbar_stop_flag,
+                                              &progbar_start_time,
+                                              progbar_val);
+
+      if (progbar != NULL) {
+        g_snprintf(progbar_status_str, sizeof(progbar_status_str),
+                   "%4u of %u packets", progbar_count, cf->count);
+        update_progress_dlg(progbar, progbar_val, progbar_status_str);
+      }
 
       progbar_nextstep += progbar_quantum;
     }
 
-    if (stop_flag) {
-      /* Well, the user decided to abort the printing.  Just stop.
-
-         XXX - note that what got generated before they did that
-        will get printed, as we're piping to a print program; we'd
-        have to write to a file and then hand that to the print
-        program to make it actually not print anything. */
+    if (progbar_stop_flag) {
+      /* Well, the user decided to abort the operation.  Just stop,
+         and arrange to return TRUE to our caller, so they know it
+         was stopped explicitly. */
+      ret = PSP_STOPPED;
       break;
     }
 
-    count++;
-    /* 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) {
-      /* XXX - do something with "err" */
-      wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
-                       cf->pd, fdata->cap_len, &err);
-      if (print_args->print_summary) {
-        /* Fill in the column information, but don't bother creating
-           the logical protocol tree. */
-        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++) {
-          /* Find the length of the string for this column. */
-          column_len = strlen(cf->cinfo.col_data[i]);
-          if (col_widths[i] > column_len)
-            column_len = col_widths[i];
-
-          /* Make sure there's room in the line buffer for the column; if not,
-             double its length. */
-          line_len += column_len + 1;  /* "+1" for space */
-          if (line_len > line_buf_len) {
-            line_buf_len *= 2;
-            line_buf = g_realloc(line_buf, line_buf_len + 1);
-          }
-
-          /* Right-justify the packet number column. */
-          if (cf->cinfo.col_fmt[i] == COL_NUMBER)
-            sprintf(cp, "%*s", col_widths[i], cf->cinfo.col_data[i]);
-          else
-            sprintf(cp, "%-*s", col_widths[i], cf->cinfo.col_data[i]);
-          cp += column_len;
-          if (i != cf->cinfo.num_cols - 1)
-            *cp++ = ' ';
-        }
-        *cp = '\0';
-        print_line(cf->print_fh, 0, print_args->format, line_buf);
-      } else {
-        if (print_separator)
-          print_line(cf->print_fh, 0, print_args->format, "");
-
-        /* 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(print_args, edt, cf->print_fh);
+    progbar_count++;
 
-       if (print_args->print_hex) {
-         /* Print the full packet data as hex. */
-         print_hex_data(cf->print_fh, print_args->format, edt);
-       }
+    /* do we have to process this packet? */
+    process_this = packet_range_process_packet(range, fdata);
+    if (process_this == range_process_next) {
+        /* this packet uninteresting, continue with next one */
+        continue;
+    } else if (process_this == range_processing_finished) {
+        /* all interesting packets processed, stop the loop */
+        break;
+    }
 
-        /* Print a blank line if we print anything after this. */
-        print_separator = TRUE;
-      }
-      epan_dissect_free(edt);
+    /* Get the packet */
+    if (!wtap_seek_read(cf->wth, fdata->file_off, &pseudo_header,
+                        pd, fdata->cap_len, &err, &err_info)) {
+      /* Attempt to get the packet failed. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                    cf_read_error_message(err, err_info), cf->filename);
+      ret = PSP_FAILED;
+      break;
+    }
+    if (!callback(cf, fdata, &pseudo_header, pd, callback_args)) {
+      /* Callback failed.  We assume it reported the error appropriately. */
+      ret = PSP_FAILED;
+      break;
     }
   }
 
-  /* We're done printing the packets; destroy the progress bar. */
-  destroy_progress_dlg(progbar);
+  /* We're done printing the packets; destroy the progress bar if
+     it was created. */
+  if (progbar != NULL)
+    destroy_progress_dlg(progbar);
 
-  if (col_widths != NULL)
-    g_free(col_widths);
-  if (line_buf != NULL)
-    g_free(line_buf);
+  return ret;
+}
 
-  print_finale(cf->print_fh, print_args->format);
+static gboolean
+retap_packet(capture_file *cf _U_, frame_data *fdata,
+             union wtap_pseudo_header *pseudo_header, const guint8 *pd,
+             void *argsp _U_)
+{
+  epan_dissect_t *edt;
 
-  close_print_dest(print_args->to_file, cf->print_fh);
-  cf->print_fh = NULL;
+  /* If we have tap listeners, allocate a protocol tree root node, so that
+     we'll construct a protocol tree against which a filter expression can
+     be evaluated. */
+  edt = epan_dissect_new(num_tap_filters != 0, FALSE);
+  tap_queue_init(edt);
+  epan_dissect_run(edt, pseudo_header, pd, fdata, NULL);
+  tap_push_tapped_queue(edt);
+  epan_dissect_free(edt);
 
   return TRUE;
 }
 
-/* Scan through the packet list and change all columns that use the
-   "command-line-specified" time stamp format to use the current
-   value of that format. */
-void
-change_time_formats(capture_file *cf)
+int
+retap_packets(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;
+  packet_range_t range;
+
+  /* Reset the tap listeners. */
+  reset_tap_listeners();
+
+  /* Iterate through the list of packets, dissecting all packets and
+     re-running the taps. */
+  packet_range_init(&range);
+  packet_range_process_init(&range);
+  switch (process_specified_packets(cf, &range, "Refiltering statistics on",
+                                    "all packets", retap_packet,
+                                    NULL)) {
+  case PSP_FINISHED:
+    /* Completed successfully. */
+    break;
 
-  /* Are there any columns with time stamps in the "command-line-specified"
-     format?
+  case PSP_STOPPED:
+    /* Well, the user decided to abort the refiltering.
+       Return FALSE so our caller knows they did that. */
+    return FALSE;
 
-     XXX - we have to force the "column is writable" flag on, as it
-     might be off from the last frame that was dissected. */
-  col_set_writable(&cf->cinfo, TRUE);
-  if (!check_col(&cf->cinfo, COL_CLS_TIME)) {
-    /* No, there aren't any columns in that format, so we have no work
-       to do. */
-    return;
+  case PSP_FAILED:
+    /* Error while retapping. */
+    return FALSE;
   }
 
-  /* Freeze the packet list while we redo it, so we don't get any
-     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;
+  return TRUE;
+}
 
-  stop_flag = FALSE;
-  progbar = create_progress_dlg("Changing time display", "Stop", &stop_flag);
+typedef struct {
+  print_args_t *print_args;
+  FILE         *print_fh;
+  gboolean      print_separator;
+  char         *line_buf;
+  int           line_buf_len;
+  gint         *col_widths;
+} print_callback_args_t;
 
-  /* 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);
+static gboolean
+print_packet(capture_file *cf, frame_data *fdata,
+             union wtap_pseudo_header *pseudo_header, const guint8 *pd,
+             void *argsp)
+{
+  print_callback_args_t *args = argsp;
+  epan_dissect_t *edt;
+  int             i;
+  char           *cp;
+  int             line_len;
+  int             column_len;
+  int             cp_off;
+
+  if (args->print_args->print_summary) {
+    /* Fill in the column information, but don't bother creating
+       the logical protocol tree. */
+    edt = epan_dissect_new(FALSE, FALSE);
+    epan_dissect_run(edt, pseudo_header, pd, fdata, &cf->cinfo);
+    epan_dissect_fill_in_columns(edt);
+    cp = &args->line_buf[0];
+    line_len = 0;
+    for (i = 0; i < cf->cinfo.num_cols; i++) {
+      /* Find the length of the string for this column. */
+      column_len = strlen(cf->cinfo.col_data[i]);
+      if (args->col_widths[i] > column_len)
+         column_len = args->col_widths[i];
 
-      update_progress_dlg(progbar, (gfloat) count / cf->count);
+      /* Make sure there's room in the line buffer for the column; if not,
+         double its length. */
+      line_len += column_len + 1;      /* "+1" for space */
+      if (line_len > args->line_buf_len) {
+        cp_off = cp - args->line_buf;
+        args->line_buf_len = 2 * line_len;
+        args->line_buf = g_realloc(args->line_buf, args->line_buf_len + 1);
+        cp = args->line_buf + cp_off;
+      }
 
-      progbar_nextstep += progbar_quantum;
-    }
+      /* Right-justify the packet number column. */
+      if (cf->cinfo.col_fmt[i] == COL_NUMBER)
+        sprintf(cp, "%*s", args->col_widths[i], cf->cinfo.col_data[i]);
+      else
+        sprintf(cp, "%-*s", args->col_widths[i], cf->cinfo.col_data[i]);
+      cp += column_len;
+      if (i != cf->cinfo.num_cols - 1)
+        *cp++ = ' ';
+    }
+    *cp = '\0';
+    print_line(args->print_fh, 0, args->print_args->format, args->line_buf);
+  } else {
+    if (args->print_separator)
+      print_line(args->print_fh, 0, args->print_args->format, "");
+
+    /* 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, pseudo_header, pd, fdata, NULL);
+
+    /* Print the information in that tree. */
+    proto_tree_print(args->print_args, edt, args->print_fh);
+
+    if (args->print_args->print_hex) {
+      /* Print the full packet data as hex. */
+      print_hex_data(args->print_fh, args->print_args->format, edt);
+    }
+
+    /* Print a blank line if we print anything after this. */
+    args->print_separator = TRUE;
+  } /* if (print_summary) */
+  epan_dissect_free(edt);
+
+  return !ferror(args->print_fh);
+}
+
+pp_return_t
+print_packets(capture_file *cf, print_args_t *print_args)
+{
+  int         i;
+  print_callback_args_t callback_args;
+  gint        data_width;
+  char        *cp;
+  int         cp_off;
+  int         column_len;
+  int         line_len;
+  psp_return_t ret;
+
+  callback_args.print_fh = open_print_dest(print_args->to_file,
+                                           print_args->dest);
+  if (callback_args.print_fh == NULL)
+    return PP_OPEN_ERROR;      /* attempt to open destination failed */
+
+  print_preamble(callback_args.print_fh, print_args->format);
+  if (ferror(callback_args.print_fh)) {
+    close_print_dest(print_args->to_file, callback_args.print_fh);
+    return PP_WRITE_ERROR;
+  }
+
+  callback_args.print_args = print_args;
+  callback_args.print_separator = FALSE;
+  callback_args.line_buf = NULL;
+  callback_args.line_buf_len = 256;
+  callback_args.col_widths = NULL;
+  if (print_args->print_summary) {
+    /* We're printing packet summaries.  Allocate the line buffer at
+       its initial length. */
+    callback_args.line_buf = g_malloc(callback_args.line_buf_len + 1);
+
+    /* Find the widths for each of the columns - maximum of the
+       width of the title and the width of the data - and print
+       the column titles. */
+    callback_args.col_widths = (gint *) g_malloc(sizeof(gint) * cf->cinfo.num_cols);
+    cp = &callback_args.line_buf[0];
+    line_len = 0;
+    for (i = 0; i < cf->cinfo.num_cols; i++) {
+      /* Don't pad the last column. */
+      if (i == cf->cinfo.num_cols - 1)
+        callback_args.col_widths[i] = 0;
+      else {
+        callback_args.col_widths[i] = strlen(cf->cinfo.col_title[i]);
+        data_width = get_column_char_width(get_column_format(i));
+        if (data_width > callback_args.col_widths[i])
+          callback_args.col_widths[i] = data_width;
+      }
+
+      /* Find the length of the string for this column. */
+      column_len = strlen(cf->cinfo.col_title[i]);
+      if (callback_args.col_widths[i] > column_len)
+        column_len = callback_args.col_widths[i];
+
+      /* Make sure there's room in the line buffer for the column; if not,
+         double its length. */
+      line_len += column_len + 1;      /* "+1" for space */
+      if (line_len > callback_args.line_buf_len) {
+        cp_off = cp - callback_args.line_buf;
+        callback_args.line_buf_len = 2 * line_len;
+        callback_args.line_buf = g_realloc(callback_args.line_buf,
+                                           callback_args.line_buf_len + 1);
+        cp = callback_args.line_buf + cp_off;
+      }
+
+      /* Right-justify the packet number column. */
+      if (cf->cinfo.col_fmt[i] == COL_NUMBER)
+        sprintf(cp, "%*s", callback_args.col_widths[i], cf->cinfo.col_title[i]);
+      else
+        sprintf(cp, "%-*s", callback_args.col_widths[i], cf->cinfo.col_title[i]);
+      cp += column_len;
+      if (i != cf->cinfo.num_cols - 1)
+        *cp++ = ' ';
+    }
+    *cp = '\0';
+    print_line(callback_args.print_fh, 0, print_args->format,
+               callback_args.line_buf);
+  } /* if (print_summary) */
+
+  /* Iterate through the list of packets, printing the packets we were
+     told to print. */
+  ret = process_specified_packets(cf, &print_args->range, "Printing",
+                                  "selected packets", print_packet,
+                                  &callback_args);
+
+  if (callback_args.col_widths != NULL)
+    g_free(callback_args.col_widths);
+  if (callback_args.line_buf != NULL)
+    g_free(callback_args.line_buf);
+
+  switch (ret) {
+
+  case PSP_FINISHED:
+    /* Completed successfully. */
+    break;
+
+  case PSP_STOPPED:
+    /* Well, the user decided to abort the printing.
+
+       XXX - note that what got generated before they did that
+       will get printed if we're piping to a print program; we'd
+       have to write to a file and then hand that to the print
+       program to make it actually not print anything. */
+    break;
+
+  case PSP_FAILED:
+    /* Error while printing.
+
+       XXX - note that what got generated before they did that
+       will get printed if we're piping to a print program; we'd
+       have to write to a file and then hand that to the print
+       program to make it actually not print anything. */
+    close_print_dest(print_args->to_file, callback_args.print_fh);
+    return PP_WRITE_ERROR;
+  }
+
+  print_finale(callback_args.print_fh, print_args->format);
+  if (ferror(callback_args.print_fh)) {
+    close_print_dest(print_args->to_file, callback_args.print_fh);
+    return PP_WRITE_ERROR;
+  }
+
+  close_print_dest(print_args->to_file, callback_args.print_fh);
+
+  return PP_OK;
+}
+
+/* Scan through the packet list and change all columns that use the
+   "command-line-specified" time stamp format to use the current
+   value of that format. */
+void
+change_time_formats(capture_file *cf)
+{
+  frame_data *fdata;
+  progdlg_t  *progbar = NULL;
+  gboolean    stop_flag;
+  int         count;
+  int         row;
+  int         i;
+  float       prog_val;
+  GTimeVal    start_time;
+  gchar       status_str[100];
+  int         progbar_nextstep;
+  int         progbar_quantum;
+  int         first, last;
+  gboolean    sorted_by_frame_column;
+
+  /* Are there any columns with time stamps in the "command-line-specified"
+     format?
+
+     XXX - we have to force the "column is writable" flag on, as it
+     might be off from the last frame that was dissected. */
+  col_set_writable(&cf->cinfo, TRUE);
+  if (!check_col(&cf->cinfo, COL_CLS_TIME)) {
+    /* No, there aren't any columns in that format, so we have no work
+       to do. */
+    return;
+  }
+  first = cf->cinfo.col_first[COL_CLS_TIME];
+  g_assert(first >= 0);
+  last = cf->cinfo.col_last[COL_CLS_TIME];
+
+  /* Freeze the packet list while we redo it, so we don't get any
+     screen updates while it happens. */
+  packet_list_freeze();
+
+  /* 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;
+
+  /*  If the rows are currently sorted by the frame column then we know
+   *  the row number of each packet: it's the row number of the previously
+   *  displayed packet + 1.
+   *
+   *  Otherwise, if the display is sorted by a different column then we have
+   *  to use the O(N) packet_list_find_row_from_data() (thus making the job
+   *  of changing the time display format O(N**2)).
+   *
+   *  (XXX - In fact it's still O(N**2) because gtk_clist_set_text() takes
+   *  the row number and walks that many elements down the clist to find
+   *  the appropriate element.)
+   */
+  sorted_by_frame_column = FALSE;
+  for (i = 0; i < cf->cinfo.num_cols; i++) {
+    if (cf->cinfo.col_fmt[i] == COL_NUMBER)
+    {
+      sorted_by_frame_column = (i == packet_list_get_sort_column());
+      break;
+    }
+  }
+
+  stop_flag = FALSE;
+  g_get_current_time(&start_time);
+
+  /* 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, row = -1; 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);
+
+      prog_val = (gfloat) count / cf->count;
+
+      if (progbar == NULL)
+        /* Create the progress bar if necessary */
+        progbar = delayed_create_progress_dlg("Changing", "time display", 
+          &stop_flag, &start_time, prog_val);
+
+      if (progbar != NULL) {
+        g_snprintf(status_str, sizeof(status_str),
+                   "%4u of %u packets", count, cf->count);
+        update_progress_dlg(progbar, prog_val, status_str);
+      }
+
+      progbar_nextstep += progbar_quantum;
+    }
 
     if (stop_flag) {
       /* Well, the user decided to abort the redisplay.  Just stop.
@@ -1343,56 +1806,394 @@ change_time_formats(capture_file *cf)
     count++;
 
     /* Find what row this packet is in. */
-    row = gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata);
+    if (!sorted_by_frame_column) {
+      /* This function is O(N), so we try to avoid using it... */
+      row = packet_list_find_row_from_data(fdata);
+    } else {
+      /* ...which we do by maintaining a count of packets that are
+         being displayed (i.e., that have passed the display filter),
+         and using the current value of that count as the row number
+         (which is why we can only do it when the display is sorted
+         by the frame number). */
+      if (fdata->flags.passed_dfilter)
+       row++;
+      else
+       continue;
+    }
 
     if (row != -1) {
       /* This packet is in the summary list, on row "row". */
 
-      for (i = 0; i < cf->cinfo.num_cols; i++) {
+      for (i = first; i <= last; 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_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]);
+          packet_list_set_text(row, i, cf->cinfo.col_data[i]);
         }
       }
     }
   }
 
-  /* We're done redisplaying the packets; destroy the progress bar. */
-  destroy_progress_dlg(progbar);
+  /* We're done redisplaying the packets; destroy the progress bar if it
+     was created. */
+  if (progbar != NULL)
+    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);
-  for (i = 0; i < cf->cinfo.num_cols; i++) {
+  for (i = first; i <= last; i++) {
     if (cf->cinfo.fmt_matx[i][COL_CLS_TIME]) {
-      gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
-        gdk_string_width(pl_style->font, get_column_longest_string(COL_CLS_TIME)));
+      packet_list_set_cls_time_width(i);
     }
   }
 
   /* Unfreeze the packet list. */
-  thaw_clist(cf);
+  packet_list_thaw();
 }
 
+typedef struct {
+       const char      *string;
+       size_t          string_len;
+       capture_file    *cf;
+       gboolean        frame_matched;
+} match_data;
+
 gboolean
-find_packet(capture_file *cf, dfilter_t *sfcode)
+find_packet_protocol_tree(capture_file *cf, const char *string)
+{
+  match_data           mdata;
+
+  mdata.string = string;
+  mdata.string_len = strlen(string);
+  return find_packet(cf, match_protocol_tree, &mdata);
+}
+
+static gboolean
+match_protocol_tree(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  match_data           *mdata = criterion;
+  epan_dissect_t       *edt;
+
+  /* Construct the protocol tree, including the displayed text */
+  edt = epan_dissect_new(TRUE, TRUE);
+  /* We don't need the column information */
+  epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+
+  /* Iterate through all the nodes, seeing if they have text that matches. */
+  mdata->cf = cf;
+  mdata->frame_matched = FALSE;
+  proto_tree_children_foreach(edt->tree, match_subtree_text, mdata);
+  epan_dissect_free(edt);
+  return mdata->frame_matched;
+}
+
+static void
+match_subtree_text(proto_node *node, gpointer data)
+{
+  match_data   *mdata = (match_data*) data;
+  const gchar  *string = mdata->string;
+  size_t       string_len = mdata->string_len;
+  capture_file *cf = mdata->cf;
+  field_info   *fi = PITEM_FINFO(node);
+  gchar                label_str[ITEM_LABEL_LENGTH];
+  gchar                *label_ptr;
+  size_t       label_len;
+  guint32      i;
+  guint8       c_char;
+  size_t       c_match = 0;
+
+  if (mdata->frame_matched) {
+    /* We already had a match; don't bother doing any more work. */
+    return;
+  }
+
+  /* Don't match invisible entries. */
+  if (!fi->visible)
+    return;
+
+  /* was a free format label produced? */
+  if (fi->rep) {
+    label_ptr = fi->rep->representation;
+  } else {
+    /* no, make a generic label */
+    label_ptr = label_str;
+    proto_item_fill_label(fi, label_str);
+  }
+    
+  /* Does that label match? */
+  label_len = strlen(label_ptr);
+  for (i = 0; i < label_len; i++) {
+    c_char = label_ptr[i];
+    if (cf->case_type)
+      c_char = toupper(c_char);
+    if (c_char == string[c_match]) {
+      c_match++;
+      if (c_match == string_len) {
+       /* No need to look further; we have a match */
+       mdata->frame_matched = TRUE;
+       return;
+      }
+    } else
+      c_match = 0;
+  }
+  
+  /* Recurse into the subtree, if it exists */
+  if (node->first_child != NULL)
+    proto_tree_children_foreach(node, match_subtree_text, mdata);
+}
+
+gboolean
+find_packet_summary_line(capture_file *cf, const char *string)
+{
+  match_data           mdata;
+
+  mdata.string = string;
+  mdata.string_len = strlen(string);
+  return find_packet(cf, match_summary_line, &mdata);
+}
+
+static gboolean
+match_summary_line(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  match_data           *mdata = criterion;
+  const gchar          *string = mdata->string;
+  size_t               string_len = mdata->string_len;
+  epan_dissect_t       *edt;
+  const char           *info_column;
+  size_t               info_column_len;
+  gboolean             frame_matched = FALSE;
+  gint                 colx;
+  guint32              i;
+  guint8               c_char;
+  size_t               c_match = 0;
+
+  /* Don't bother constructing the protocol tree */
+  edt = epan_dissect_new(FALSE, FALSE);
+  /* Get the column information */
+  epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
+
+  /* Find the Info column */
+  for (colx = 0; colx < cf->cinfo.num_cols; colx++) {
+    if (cf->cinfo.fmt_matx[colx][COL_INFO]) {
+      /* Found it.  See if we match. */
+      info_column = edt->pi.cinfo->col_data[colx];
+      info_column_len = strlen(info_column);
+      for (i = 0; i < info_column_len; i++) {
+       c_char = info_column[i];
+       if (cf->case_type)
+         c_char = toupper(c_char);
+       if (c_char == string[c_match]) {
+         c_match++;
+         if (c_match == string_len) {
+           frame_matched = TRUE;
+           break;
+         }
+       } else
+         c_match = 0;
+      }
+      break;
+    }
+  }
+  epan_dissect_free(edt);
+  return frame_matched;
+}
+
+typedef struct {
+       const guint8 *data;
+       size_t data_len;
+} cbs_t;       /* "Counted byte string" */
+
+gboolean
+find_packet_data(capture_file *cf, const guint8 *string, size_t string_size)
+{
+  cbs_t info;
+
+  info.data = string;
+  info.data_len = string_size;
+
+  /* String or hex search? */
+  if (cf->ascii) {
+    /* String search - what type of string? */
+    switch (cf->scs_type) {
+
+    case SCS_ASCII_AND_UNICODE:
+      return find_packet(cf, match_ascii_and_unicode, &info);
+
+    case SCS_ASCII:
+      return find_packet(cf, match_ascii, &info);
+
+    case SCS_UNICODE:
+      return find_packet(cf, match_unicode, &info);
+
+    default:
+      g_assert_not_reached();
+      return FALSE;
+    }
+  } else
+    return find_packet(cf, match_binary, &info);
+}
+
+static gboolean
+match_ascii_and_unicode(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  cbs_t                *info = criterion;
+  const char   *ascii_text = info->data;
+  size_t       textlen = info->data_len;
+  gboolean     frame_matched;
+  guint32      buf_len;
+  guint32      i;
+  guint8       c_char;
+  size_t       c_match = 0;
+
+  frame_matched = FALSE;
+  buf_len = fdata->pkt_len;
+  for (i = 0; i < buf_len; i++) {
+    c_char = cf->pd[i];
+    if (cf->case_type)
+      c_char = toupper(c_char);
+    if (c_char != 0) {
+      if (c_char == ascii_text[c_match]) {
+       c_match++;
+       if (c_match == textlen) {
+         frame_matched = TRUE;
+         break;
+       }
+      } else
+       c_match = 0;
+    }
+  }
+  return frame_matched;
+}
+
+static gboolean
+match_ascii(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  cbs_t                *info = criterion;
+  const char   *ascii_text = info->data;
+  size_t       textlen = info->data_len;
+  gboolean     frame_matched;
+  guint32      buf_len;
+  guint32      i;
+  guint8       c_char;
+  size_t       c_match = 0;
+
+  frame_matched = FALSE;
+  buf_len = fdata->pkt_len;
+  for (i = 0; i < buf_len; i++) {
+    c_char = cf->pd[i];
+    if (cf->case_type)
+      c_char = toupper(c_char);
+    if (c_char == ascii_text[c_match]) {
+      c_match++;
+      if (c_match == textlen) {
+       frame_matched = TRUE;
+       break;
+      }
+    } else
+      c_match = 0;
+  }
+  return frame_matched;
+}
+
+static gboolean
+match_unicode(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  cbs_t                *info = criterion;
+  const char   *ascii_text = info->data;
+  size_t       textlen = info->data_len;
+  gboolean     frame_matched;
+  guint32      buf_len;
+  guint32      i;
+  guint8       c_char;
+  size_t       c_match = 0;
+
+  frame_matched = FALSE;
+  buf_len = fdata->pkt_len;
+  for (i = 0; i < buf_len; i++) {
+    c_char = cf->pd[i];
+    if (cf->case_type)
+      c_char = toupper(c_char);
+    if (c_char == ascii_text[c_match]) {
+      c_match++;
+      i++;
+      if (c_match == textlen) {
+       frame_matched = TRUE;
+       break;
+      }
+    } else
+      c_match = 0;
+  }
+  return frame_matched;
+}
+
+static gboolean
+match_binary(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  cbs_t                *info = criterion;
+  const guint8 *binary_data = info->data;
+  size_t       datalen = info->data_len;
+  gboolean     frame_matched;
+  guint32      buf_len;
+  guint32      i;
+  size_t       c_match = 0;
+
+  frame_matched = FALSE;
+  buf_len = fdata->pkt_len;
+  for (i = 0; i < buf_len; i++) {
+    if (cf->pd[i] == binary_data[c_match]) {
+      c_match++;
+      if (c_match == datalen) {
+       frame_matched = TRUE;
+       break;
+      }
+    } else
+      c_match = 0;
+  }
+  return frame_matched;
+}
+
+gboolean
+find_packet_dfilter(capture_file *cf, dfilter_t *sfcode)
+{
+  return find_packet(cf, match_dfilter, sfcode);
+}
+
+static gboolean
+match_dfilter(capture_file *cf, frame_data *fdata, void *criterion)
+{
+  dfilter_t            *sfcode = criterion;
+  epan_dissect_t       *edt;
+  gboolean             frame_matched;
+
+  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);
+  return frame_matched;
+}
+
+static gboolean
+find_packet(capture_file *cf,
+            gboolean (*match_function)(capture_file *, frame_data *, void *),
+            void *criterion)
 {
   frame_data *start_fd;
   frame_data *fdata;
   frame_data *new_fd = NULL;
-  progdlg_t *progbar = NULL;
-  gboolean stop_flag;
-  guint32 progbar_quantum;
-  guint32 progbar_nextstep;
-  unsigned int count;
-  int err;
-  gboolean frame_matched;
-  int row;
-  epan_dissect_t       *edt;
+  progdlg_t  *progbar = NULL;
+  gboolean    stop_flag;
+  int         count;
+  int         err;
+  gchar      *err_info;
+  int         row;
+  float       prog_val;
+  GTimeVal    start_time;
+  gchar       status_str[100];
+  int         progbar_nextstep;
+  int         progbar_quantum;
 
   start_fd = cf->current_frame;
   if (start_fd != NULL)  {
@@ -1402,18 +2203,13 @@ find_packet(capture_file *cf, dfilter_t *sfcode)
     count = 0;
     fdata = start_fd;
 
-    /* Update the progress bar when it gets to this value.  We start at
-       20, not 0, so that we don't get a progress bar until we've
-       checked at least that many frames, so that a very quick search
-       doesn't pop up and immediately destroy a progress bar.
-
-       XXX - should use a timer?  Like 50 ms. */
-    progbar_nextstep = 20;
+    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;
 
     stop_flag = FALSE;
+    g_get_current_time(&start_time);
 
     fdata = start_fd;
     for (;;) {
@@ -1428,13 +2224,18 @@ find_packet(capture_file *cf, dfilter_t *sfcode)
          */
         g_assert(cf->count > 0);
 
-        /* Create the progress bar if it doesn't exist; we don't create it
-           immediately, so that we don't have it appear and immediately
-           disappear if the search is quick. */
+        prog_val = (gfloat) count / cf->count;
+
+        /* Create the progress bar if necessary */
         if (progbar == NULL)
-           progbar = create_progress_dlg("Searching", "Cancel", &stop_flag);
-        
-        update_progress_dlg(progbar, (gfloat) count / cf->count);
+           progbar = delayed_create_progress_dlg("Searching", cf->sfilter, 
+             &stop_flag, &start_time, prog_val);
+
+        if (progbar != NULL) {
+          g_snprintf(status_str, sizeof(status_str),
+                     "%4u of %u packets", count, cf->count);
+          update_progress_dlg(progbar, prog_val, status_str);
+        }
 
         progbar_nextstep += progbar_quantum;
       }
@@ -1450,29 +2251,49 @@ find_packet(capture_file *cf, dfilter_t *sfcode)
       if (cf->sbackward) {
         /* Go on to the previous frame. */
         fdata = fdata->prev;
-        if (fdata == NULL)
+        if (fdata == NULL) {
+          /*
+           * XXX - other apps have a bit more of a detailed message
+           * for this, and instead of offering "OK" and "Cancel",
+           * they offer things such as "Continue" and "Cancel";
+           * we need an API for popping up alert boxes with
+           * {Verb} and "Cancel".
+           */
+          simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+                        "%sBeginning of capture exceeded!%s\n\n"
+                        "Search is continued from the end of the capture.",
+                        simple_dialog_primary_start(), simple_dialog_primary_end());
           fdata = cf->plist_end;       /* wrap around */
+        }
       } else {
         /* Go on to the next frame. */
         fdata = fdata->next;
-        if (fdata == NULL)
+        if (fdata == NULL) {
+          simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+                        "%sEnd of capture exceeded!%s\n\n"
+                        "Search is continued from the start of the capture.",
+                        simple_dialog_primary_start(), simple_dialog_primary_end());
           fdata = cf->plist;   /* wrap around */
+        }
       }
 
       count++;
 
       /* Is this packet in the display? */
       if (fdata->flags.passed_dfilter) {
-        /* Yes.  Does it match the search filter? */
-        /* XXX - do something with "err" */
-        wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
-                       cf->pd, fdata->cap_len, &err);
-        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) {
+       /* Yes.  Load its data. */
+        if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
+                       cf->pd, fdata->cap_len, &err, &err_info)) {
+          /* Read error.  Report the error, and go back to the frame
+             where we started. */
+          simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                       cf_read_error_message(err, err_info), cf->filename);
+          new_fd = start_fd;
+          break;
+        }
+
+       /* Does it match the search criterion? */
+       if ((*match_function)(cf, fdata, criterion)) {
           new_fd = fdata;
           break;       /* found it! */
         }
@@ -1485,25 +2306,25 @@ find_packet(capture_file *cf, dfilter_t *sfcode)
       }
     }
 
-    /* We're done scanning the packets; destroy the progress bar, if
-       we created it. */
+    /* We're done scanning the packets; destroy the progress bar if it
+       was created. */
     if (progbar != NULL)
       destroy_progress_dlg(progbar);
   }
 
   if (new_fd != NULL) {
     /* We found a frame.  Find what row it's in. */
-    row = gtk_clist_find_row_from_data(GTK_CLIST(packet_list), new_fd);
+    row = packet_list_find_row_from_data(new_fd);
     g_assert(row != -1);
 
     /* Select that row, make it the focus row, and make it visible. */
-    set_selected_row(row);
+    packet_list_set_selected_row(row);
     return TRUE;       /* success */
   } else
     return FALSE;      /* failure */
 }
 
-goto_result_t
+gboolean
 goto_frame(capture_file *cf, guint fnumber)
 {
   frame_data *fdata;
@@ -1512,19 +2333,83 @@ goto_frame(capture_file *cf, guint fnumber)
   for (fdata = cf->plist; fdata != NULL && fdata->num < fnumber; fdata = fdata->next)
     ;
 
-  if (fdata == NULL)
-    return NO_SUCH_FRAME;      /* we didn't find that frame */
-  if (!fdata->flags.passed_dfilter)
-    return FRAME_NOT_DISPLAYED;        /* the frame with that number isn't displayed */
+  if (fdata == NULL) {
+    /* we didn't find a packet with that packet number */
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                 "There is no packet with that packet number.");
+    return FALSE;      /* we failed to go to that packet */
+  }
+  if (!fdata->flags.passed_dfilter) {
+    /* that packet currently isn't displayed */
+    /* XXX - add it to the set of displayed packets? */
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                 "That packet is not currently being displayed.");
+    return FALSE;      /* we failed to go to that packet */
+  }
 
-  /* We found that frame, and it's currently being displayed.
+  /* We found that packet, and it's currently being displayed.
      Find what row it's in. */
-  row = gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata);
+  row = packet_list_find_row_from_data(fdata);
   g_assert(row != -1);
 
   /* Select that row, make it the focus row, and make it visible. */
-  set_selected_row(row);
-  return FOUND_FRAME;
+  packet_list_set_selected_row(row);
+  return TRUE; /* we got to that packet */
+}
+
+gboolean
+goto_top_frame(capture_file *cf)
+{
+  frame_data *fdata;
+  int row;
+  frame_data *lowest_fdata = NULL;
+
+  for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
+    if (fdata->flags.passed_dfilter) {
+        lowest_fdata = fdata;
+        break;
+    }
+  }
+
+  if (lowest_fdata == NULL) {
+      return FALSE;
+  }
+
+  /* We found that packet, and it's currently being displayed.
+     Find what row it's in. */
+  row = packet_list_find_row_from_data(lowest_fdata);
+  g_assert(row != -1);
+
+  /* Select that row, make it the focus row, and make it visible. */
+  packet_list_set_selected_row(row);
+  return TRUE; /* we got to that packet */
+}
+
+gboolean
+goto_bottom_frame(capture_file *cf)
+{
+  frame_data *fdata;
+  int row;
+  frame_data *highest_fdata = NULL;
+
+  for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
+    if (fdata->flags.passed_dfilter) {
+        highest_fdata = fdata;
+    }
+  }
+
+  if (highest_fdata == NULL) {
+      return FALSE;
+  }
+
+  /* We found that packet, and it's currently being displayed.
+     Find what row it's in. */
+  row = packet_list_find_row_from_data(highest_fdata);
+  g_assert(row != -1);
+
+  /* Select that row, make it the focus row, and make it visible. */
+  packet_list_set_selected_row(row);
+  return TRUE; /* we got to that packet */
 }
 
 /* Select the packet on a given row. */
@@ -1533,9 +2418,10 @@ select_packet(capture_file *cf, int row)
 {
   frame_data *fdata;
   int err;
+  gchar *err_info;
 
   /* Get the frame data struct pointer for this frame */
-  fdata = (frame_data *) gtk_clist_get_row_data(GTK_CLIST(packet_list), row);
+  fdata = (frame_data *)packet_list_get_row_data(row);
 
   if (fdata == NULL) {
     /* XXX - if a GtkCList's selection mode is GTK_SELECTION_BROWSE, when
@@ -1568,14 +2454,17 @@ select_packet(capture_file *cf, int row)
          fdata = cf->first_displayed;
   }
 
+  /* Get the data in that frame. */
+  if (!wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
+                      cf->pd, fdata->cap_len, &err, &err_info)) {
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                 cf_read_error_message(err, err_info), cf->filename);
+    return;
+  }
+
   /* Record that this frame is the current frame. */
   cf->current_frame = fdata;
 
-  /* Get the data in that frame. */
-  /* XXX - do something with "err" */
-  wtap_seek_read (cf->wth, fdata->file_off, &cf->pseudo_header,
-                       cf->pd, fdata->cap_len, &err);
-
   /* Create the logical protocol tree. */
   if (cf->edt != NULL) {
     epan_dissect_free(cf->edt);
@@ -1589,11 +2478,11 @@ select_packet(capture_file *cf, int row)
   /* Display the GUI protocol tree and hex dump.
      XXX - why do we dump core if we call "proto_tree_draw()"
      before calling "add_byte_views()"? */
-  add_byte_views(cf->edt, tree_view, byte_nb_ptr);
-  proto_tree_draw(cf->edt->tree, tree_view);
+  add_main_byte_views(cf->edt);
+  main_proto_tree_draw(cf->edt->tree);
 
   /* A packet is selected. */
-  set_menus_for_selected_packet(TRUE);
+  set_menus_for_selected_packet(cf);
 }
 
 /* Unselect the selected packet, if any. */
@@ -1610,40 +2499,20 @@ unselect_packet(capture_file *cf)
   clear_tree_and_hex_views();
 
   /* No packet is selected. */
-  set_menus_for_selected_packet(FALSE);
+  cf->current_frame = NULL;
+  set_menus_for_selected_packet(cf);
 
   /* No protocol tree means no selected field. */
-  unselect_field();
-}
-
-/* Set the selected row and the focus row of the packet list to the specified
-   row, and make it visible if it's not currently visible. */
-static void
-set_selected_row(int row)
-{
-  if (gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) != GTK_VISIBILITY_FULL)
-    gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.0, 0.0);
-
-  /* XXX - why is there no "gtk_clist_set_focus_row()", so that we
-     can make the row for the frame we found the focus row?
-
-     See
-
- http://www.gnome.org/mailing-lists/archives/gtk-list/2000-January/0038.shtml
-
-     */
-  GTK_CLIST(packet_list)->focus_row = row;
-
-  gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
+  unselect_field(cf);
 }
 
 /* Unset the selected protocol tree field, if any. */
 void
-unselect_field(void)
+unselect_field(capture_file *cf)
 {
   statusbar_pop_field_msg();
-  finfo_selected = NULL;
-  set_menus_for_selected_tree_row(FALSE);
+  cf->finfo_selected = NULL;
+  set_menus_for_selected_tree_row(cf);
 }
 
 /*
@@ -1666,43 +2535,10 @@ unmark_frame(capture_file *cf, frame_data *frame)
   cf->marked_count--;
 }
 
-static void
-freeze_clist(capture_file *cf)
-{
-  int i;
-
-  /* Make the column sizes static, so they don't adjust while
-     we're reading the capture file (freezing the clist doesn't
-     seem to suffice). */
-  for (i = 0; i < cf->cinfo.num_cols; i++)
-    gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, FALSE);
-  gtk_clist_freeze(GTK_CLIST(packet_list));
-}
-
-static void
-thaw_clist(capture_file *cf)
-{
-  int i;
-
-  for (i = 0; i < cf->cinfo.num_cols; i++) {
-    if (get_column_resize_type(cf->cinfo.col_fmt[i]) == RESIZE_MANUAL) {
-      /* Set this column's width to the appropriate value. */
-      gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
-                               cf->cinfo.col_width[i]);
-    } else {
-      /* Make this column's size dynamic, so that it adjusts to the
-         appropriate size. */
-      gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
-    }
-  }
-  gtk_clist_thaw(GTK_CLIST(packet_list));
-
-  /* Hopefully, the columns have now gotten their appropriate sizes;
-     make them resizeable - a column that auto-resizes cannot be
-     resized by the user, and *vice versa*. */
-  for (i = 0; i < cf->cinfo.num_cols; i++)
-    gtk_clist_set_column_resizeable(GTK_CLIST(packet_list), i, TRUE);
-}
+typedef struct {
+  wtap_dumper *pdh;
+  const char  *fname;
+} save_callback_args_t;
 
 /*
  * Save a capture to a file, in a particular format, saving either
@@ -1711,9 +2547,32 @@ thaw_clist(capture_file *cf)
  * Returns TRUE if it succeeds, FALSE otherwise; if it fails, it pops
  * up a message box for the failure.
  */
+static gboolean
+save_packet(capture_file *cf _U_, frame_data *fdata,
+            union wtap_pseudo_header *pseudo_header, const guint8 *pd,
+            void *argsp)
+{
+  save_callback_args_t *args = argsp;
+  struct wtap_pkthdr hdr;
+  int           err;
+
+  /* init the wtap header for saving */
+  hdr.ts.tv_sec  = fdata->abs_secs;
+  hdr.ts.tv_usec = fdata->abs_usecs;
+  hdr.caplen     = fdata->cap_len;
+  hdr.len        = fdata->pkt_len;
+  hdr.pkt_encap  = fdata->lnk_t;
+
+  /* and save the packet */
+  if (!wtap_dump(args->pdh, &hdr, pseudo_header, pd, &err)) {
+    cf_write_failure_alert_box(args->fname, err);
+    return FALSE;
+  }
+  return TRUE;
+}
+
 gboolean
-save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
-               gboolean save_marked, guint save_format)
+cf_save(char *fname, capture_file *cf, packet_range_t *range, guint save_format)
 {
   gchar        *from_filename;
   gchar        *name_ptr, *save_msg, *save_fmt = " Saving: %s...";
@@ -1721,10 +2580,8 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
   int           err;
   gboolean      do_copy;
   wtap_dumper  *pdh;
-  frame_data   *fdata;
-  struct wtap_pkthdr hdr;
-  union wtap_pseudo_header pseudo_header;
-  guint8        pd[65536];
+  struct stat   infile, outfile;
+  save_callback_args_t callback_args;
 
   name_ptr = get_basename(fname);
   msg_len = strlen(name_ptr) + strlen(save_fmt) + 2;
@@ -1733,18 +2590,38 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
   statusbar_push_file_msg(save_msg);
   g_free(save_msg);
 
-  /* 
-   * Check that the from file is not the same as to file 
+  /*
+   * Check that the from file is not the same as to file
    * We do it here so we catch all cases ...
+   * Unfortunately, the file requester gives us an absolute file
+   * name and the read file name may be relative (if supplied on
+   * the command line). From Joerg Mayer.
+   *
+   * This is a bit tricky on win32. The st_ino field is documented as:
+   * "The inode, and therefore st_ino, has no meaning in the FAT, ..."
+   * but it *is* set to zero if stat() returns without an error,
+   * so this is working, but maybe not quite the way expected. ULFL
    */
-  if (strcmp(cf->filename, fname) == 0) {
-    simple_dialog(ESD_TYPE_CRIT, NULL, 
-                     "Can't save over current capture file: %s!",
-                     cf->filename);
+   infile.st_ino = 1;   /* These prevent us from getting equality         */
+   outfile.st_ino = 2;  /* If one or other of the files is not accessible */
+   stat(cf->filename, &infile);
+   stat(fname, &outfile);
+   if (infile.st_ino == outfile.st_ino) {
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+      "%sCapture file: \"%s\" already exists!%s\n\n"
+      "Please choose a different filename.",
+      simple_dialog_primary_start(), fname, simple_dialog_primary_end());
     goto fail;
   }
 
-  if (!save_filtered && !save_marked && save_format == cf->cd_t) {
+  packet_range_process_init(range);
+
+  /* Used to be :
+   * if (!save_filtered && !save_marked && !save_manual_range && 
+   *     !save_marked_range && !save_curr && save_format == cf->cd_t) {
+   */ 
+       
+  if (packet_range_process_all(range) && 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. */
 
@@ -1771,7 +2648,7 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
             be if we didn't have permission to remove the file from
             the temporary directory, and that might be fixable - but
             is it worth requiring the user to go off and fix it?) */
-         simple_dialog(ESD_TYPE_CRIT, NULL,
+         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
                                file_rename_error_message(errno), fname);
          goto fail;
        }
@@ -1798,62 +2675,54 @@ 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_CRIT, NULL,
-                       file_open_error_message(err, TRUE, save_format), fname);
+      cf_open_failure_alert_box(fname, err, NULL, TRUE, save_format);
       goto fail;
     }
 
-    /* XXX - have a way to save only the packets currently selected by
-       the display filter or the marked ones.
+    /* XXX - we let the user save a subset of the packets.
 
        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 && !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;
-        hdr.len = fdata->pkt_len;
-        hdr.pkt_encap = fdata->lnk_t;
-       if (!wtap_seek_read(cf->wth, fdata->file_off, &pseudo_header,
-               pd, fdata->cap_len, &err)) {
-         simple_dialog(ESD_TYPE_CRIT, NULL,
-                               file_read_error_message(err), cf->filename);
-         wtap_dump_close(pdh, &err);
-          goto fail;
-       }
 
-        if (!wtap_dump(pdh, &hdr, &pseudo_header, pd, &err)) {
-         simple_dialog(ESD_TYPE_CRIT, NULL,
-                               file_write_error_message(err), fname);
-         wtap_dump_close(pdh, &err);
-          goto fail;
-       }
-      }
+    /* Iterate through the list of packets, processing the packets we were
+       told to process.
+
+       XXX - we've already called "packet_range_process_init(range)", but
+       "process_specified_packets()" will do it again.  Fortunately,
+       that's harmless in this case, as we haven't done anything to
+       "range" since we initialized it. */
+    callback_args.pdh = pdh;
+    callback_args.fname = fname;
+    switch (process_specified_packets(cf, range, "Saving",
+                                      "selected packets", save_packet,
+                                      &callback_args)) {
+
+    case PSP_FINISHED:
+      /* Completed successfully. */
+      break;
+
+    case PSP_STOPPED:
+      /* The user decided to abort the saving.
+         XXX - remove the output file? */
+      break;
+
+    case PSP_FAILED:
+      /* Error while saving. */
+      wtap_dump_close(pdh, &err);
+      goto fail;
     }
 
     if (!wtap_dump_close(pdh, &err)) {
-      simple_dialog(ESD_TYPE_WARN, NULL,
-               file_close_error_message(err), fname);
+      cf_close_failure_alert_box(fname, err);
       goto fail;
     }
   }
 
   /* Pop the "Saving:" message off the status bar. */
   statusbar_pop_file_msg();
-  if (!save_filtered && !save_marked) {
+
+  if (packet_range_process_all(range)) {
     /* We saved the entire capture, not just some packets from it.
        Open and read the file we saved it to.
 
@@ -1866,10 +2735,10 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
        time if the file is large. */
     cf->user_saved = TRUE;
 
-    if ((err = open_cap_file(fname, FALSE, cf)) == 0) {
+    if ((err = cf_open(fname, FALSE, cf)) == 0) {
       /* XXX - report errors if this fails?
          What should we return if it fails or is aborted? */
-      switch (read_cap_file(cf, &err)) {
+      switch (cf_read(cf)) {
 
       case READ_SUCCESS:
       case READ_ERROR:
@@ -1881,7 +2750,7 @@ save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
       case READ_ABORTED:
        /* The user bailed out of re-reading the capture file; the
           capture file has been closed - just return (without
-          changing any menu settings; "close_cap_file()" set them
+          changing any menu settings; "cf_close()" set them
           correctly for the "no capture file open" state). */
        break;
       }
@@ -1896,97 +2765,127 @@ fail:
   return FALSE;
 }
 
-char *
-file_open_error_message(int err, gboolean for_writing, int file_type)
+static void
+cf_open_failure_alert_box(const char *filename, int err, gchar *err_info,
+                          gboolean for_writing, int file_type)
 {
-  char *errmsg;
-  static char errmsg_errno[1024+1];
-
-  switch (err) {
-
-  case WTAP_ERR_NOT_REGULAR_FILE:
-    errmsg = "The file \"%s\" is a \"special file\" or socket or other non-regular file.";
-    break;
-
-  case WTAP_ERR_RANDOM_OPEN_PIPE:
-    /* Seen only when opening a capture file for reading. */
-    errmsg = "The file \"%s\" is a pipe or FIFO; Ethereal cannot read pipe or FIFO files.";
-    break;
+  if (err < 0) {
+    /* Wiretap error. */
+    switch (err) {
+
+    case WTAP_ERR_NOT_REGULAR_FILE:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" is a \"special file\" or socket or other non-regular file.",
+                   filename);
+      break;
 
-  case WTAP_ERR_FILE_UNKNOWN_FORMAT:
-  case WTAP_ERR_UNSUPPORTED:
-    /* Seen only when opening a capture file for reading. */
-    errmsg = "The file \"%s\" is not a capture file in a format Ethereal understands.";
-    break;
+    case WTAP_ERR_RANDOM_OPEN_PIPE:
+      /* Seen only when opening a capture file for reading. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" is a pipe or FIFO; Ethereal cannot read pipe or FIFO files.",
+                   filename);
+      break;
 
-  case WTAP_ERR_CANT_WRITE_TO_PIPE:
-    /* Seen only when opening a capture file for writing. */
-    snprintf(errmsg_errno, sizeof(errmsg_errno),
-            "The file \"%%s\" is a pipe, and %s capture files cannot be "
-            "written to a pipe.", wtap_file_type_string(file_type));
-    errmsg = errmsg_errno;
-    break;
+    case WTAP_ERR_FILE_UNKNOWN_FORMAT:
+      /* Seen only when opening a capture file for reading. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" is not a capture file in a format Ethereal understands.",
+                   filename);
+      break;
 
-  case WTAP_ERR_UNSUPPORTED_FILE_TYPE:
-    /* Seen only when opening a capture file for writing. */
-    errmsg = "Ethereal does not support writing capture files in that format.";
-    break;
+    case WTAP_ERR_UNSUPPORTED:
+      /* Seen only when opening a capture file for reading. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" is not a capture file in a format Ethereal understands.\n"
+                   "(%s)",
+                   filename, err_info);
+      g_free(err_info);
+      break;
 
-  case WTAP_ERR_UNSUPPORTED_ENCAP:
-  case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
-    if (for_writing)
-      errmsg = "Ethereal cannot save this capture in that format.";
-    else
-      errmsg = "The file \"%s\" is a capture for a network type that Ethereal doesn't support.";
-    break;
+    case WTAP_ERR_CANT_WRITE_TO_PIPE:
+      /* Seen only when opening a capture file for writing. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" is a pipe, and %s capture files cannot be "
+                   "written to a pipe.",
+                   filename, wtap_file_type_string(file_type));
+      break;
 
-  case WTAP_ERR_BAD_RECORD:
-    errmsg = "The file \"%s\" appears to be damaged or corrupt.";
-    break;
+    case WTAP_ERR_UNSUPPORTED_FILE_TYPE:
+      /* Seen only when opening a capture file for writing. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "Ethereal does not support writing capture files in that format.");
+      break;
 
-  case WTAP_ERR_CANT_OPEN:
-    if (for_writing)
-      errmsg = "The file \"%s\" could not be created for some unknown reason.";
-    else
-      errmsg = "The file \"%s\" could not be opened for some unknown reason.";
-    break;
+    case WTAP_ERR_UNSUPPORTED_ENCAP:
+      if (for_writing) {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "Ethereal cannot save this capture in that format.");
+      } else {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "The file \"%s\" is a capture for a network type that Ethereal doesn't support.\n"
+                     "(%s)",
+                     filename, err_info);
+        g_free(err_info);
+      }
+      break;
 
-  case WTAP_ERR_SHORT_READ:
-    errmsg = "The file \"%s\" appears to have been cut short"
-             " in the middle of a packet or other data.";
-    break;
+    case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
+      if (for_writing) {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "Ethereal cannot save this capture in that format.");
+      } else {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "The file \"%s\" is a capture for a network type that Ethereal doesn't support.",
+                     filename);
+      }
+      break;
 
-  case WTAP_ERR_SHORT_WRITE:
-    errmsg = "A full header couldn't be written to the file \"%s\".";
-    break;
+    case WTAP_ERR_BAD_RECORD:
+      /* Seen only when opening a capture file for reading. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" appears to be damaged or corrupt.\n"
+                   "(%s)",
+                   filename, err_info);
+      g_free(err_info);
+      break;
 
-  case ENOENT:
-    if (for_writing)
-      errmsg = "The path to the file \"%s\" does not exist.";
-    else
-      errmsg = "The file \"%s\" does not exist.";
-    break;
+    case WTAP_ERR_CANT_OPEN:
+      if (for_writing) {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "The file \"%s\" could not be created for some unknown reason.",
+                     filename);
+      } else {
+       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                     "The file \"%s\" could not be opened for some unknown reason.",
+                     filename);
+      }
+      break;
 
-  case EACCES:
-    if (for_writing)
-      errmsg = "You do not have permission to create or write to the file \"%s\".";
-    else
-      errmsg = "You do not have permission to read the file \"%s\".";
-    break;
+    case WTAP_ERR_SHORT_READ:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" appears to have been cut short"
+                   " in the middle of a packet or other data.",
+                   filename);
+      break;
 
-  case EISDIR:
-    errmsg = "\"%s\" is a directory (folder), not a file.";
-    break;
+    case WTAP_ERR_SHORT_WRITE:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "A full header couldn't be written to the file \"%s\".",
+                   filename);
+      break;
 
-  default:
-    snprintf(errmsg_errno, sizeof(errmsg_errno),
-                   "The file \"%%s\" could not be %s: %s.",
-                               for_writing ? "created" : "opened",
-                               wtap_strerror(err));
-    errmsg = errmsg_errno;
-    break;
+    default:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" could not be %s: %s.",
+                   filename,
+                   for_writing ? "created" : "opened",
+                   wtap_strerror(err));
+      break;
+    }
+  } else {
+    /* OS error. */
+    open_failure_alert_box(filename, err, for_writing);
   }
-  return errmsg;
 }
 
 static char *
@@ -2016,82 +2915,77 @@ file_rename_error_message(int err)
 }
 
 char *
-file_read_error_message(int err)
+cf_read_error_message(int err, gchar *err_info)
 {
   static char errmsg_errno[1024+1];
 
-  snprintf(errmsg_errno, sizeof(errmsg_errno),
-                 "An error occurred while reading from the file \"%%s\": %s.",
-                               wtap_strerror(err));
-  return errmsg_errno;
-}
-
-char *
-file_write_error_message(int err)
-{
-  char *errmsg;
-  static char errmsg_errno[1024+1];
-
   switch (err) {
 
-  case ENOSPC:
-    errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
-    break;
-
-#ifdef EDQUOT
-  case EDQUOT:
-    errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
+  case WTAP_ERR_UNSUPPORTED:
+  case WTAP_ERR_UNSUPPORTED_ENCAP:
+  case WTAP_ERR_BAD_RECORD:
+    snprintf(errmsg_errno, sizeof(errmsg_errno),
+            "An error occurred while reading from the file \"%%s\": %s.\n(%s)",
+            wtap_strerror(err), err_info);
     break;
-#endif
 
   default:
     snprintf(errmsg_errno, sizeof(errmsg_errno),
-                   "An error occurred while writing to the file \"%%s\": %s.",
-                               wtap_strerror(err));
-    errmsg = errmsg_errno;
+            "An error occurred while reading from the file \"%%s\": %s.",
+            wtap_strerror(err));
     break;
   }
-  return errmsg;
+  return errmsg_errno;
+}
+
+static void
+cf_write_failure_alert_box(const char *filename, int err)
+{
+  if (err < 0) {
+    /* Wiretap error. */
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                 "An error occurred while writing to the file \"%s\": %s.",
+                 filename, wtap_strerror(err));
+  } else {
+    /* OS error. */
+    write_failure_alert_box(filename, err);
+  }
 }
 
 /* Check for write errors - if the file is being written to an NFS server,
    a write error may not show up until the file is closed, as NFS clients
    might not send writes to the server until the "write()" call finishes,
    so that the write may fail on the server but the "write()" may succeed. */
-static char *
-file_close_error_message(int err)
+static void
+cf_close_failure_alert_box(const char *filename, int err)
 {
-  char *errmsg;
-  static char errmsg_errno[1024+1];
-
-  switch (err) {
-
-  case WTAP_ERR_CANT_CLOSE:
-    errmsg = "The file \"%s\" couldn't be closed for some unknown reason.";
-    break;
-
-  case WTAP_ERR_SHORT_WRITE:
-    errmsg = "Not all the packets could be written to the file \"%s\".";
-    break;
-
-  case ENOSPC:
-    errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
-    break;
+  if (err < 0) {
+    /* Wiretap error. */
+    switch (err) {
+
+    case WTAP_ERR_CANT_CLOSE:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "The file \"%s\" couldn't be closed for some unknown reason.",
+                   filename);
+      break;
 
-#ifdef EDQUOT
-  case EDQUOT:
-    errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
-    break;
-#endif
+    case WTAP_ERR_SHORT_WRITE:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "Not all the packets could be written to the file \"%s\".",
+                    filename);
+      break;
 
-  default:
-    snprintf(errmsg_errno, sizeof(errmsg_errno),
-                   "An error occurred while closing the file \"%%s\": %s.",
-                               wtap_strerror(err));
-    errmsg = errmsg_errno;
-    break;
+    default:
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                   "An error occurred while closing the file \"%s\": %s.",
+                   filename, wtap_strerror(err));
+      break;
+    }
+  } else {
+    /* OS error.
+       We assume that a close error from the OS is really a write error. */
+    write_failure_alert_box(filename, err);
   }
-  return errmsg;
 }
 
 
@@ -2103,65 +2997,58 @@ file_close_error_message(int err)
 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, 0), from_filename);
-       goto done;
-      }
+  int           from_fd, to_fd, nread, nwritten, err;
+  guint8        pd[65536];
 
-      /* 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, 0), to_filename);
-       close(from_fd);
-       goto done;
-      }
+  /* Copy the raw bytes of the file. */
+  from_fd = open(from_filename, O_RDONLY | O_BINARY);
+  if (from_fd < 0) {
+    open_failure_alert_box(from_filename, errno, FALSE);
+    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;
-      }
+  /* 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) {
+    open_failure_alert_box(to_filename, errno, TRUE);
+    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;
+      write_failure_alert_box(to_filename, err);
       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;
-      }
+      close(to_fd);
+      goto done;
+    }
+  }
+  if (nread < 0) {
+    err = errno;
+    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                 "An error occurred while reading from the file \"%s\": %s.",
+                 from_filename, strerror(err));
+    close(from_fd);
+    close(to_fd);
+    goto done;
+  }
+  close(from_fd);
+  if (close(to_fd) < 0) {
+    write_failure_alert_box(to_filename, errno);
+    goto done;
+  }
 
-      return TRUE;
+  return TRUE;
 
-   done:
-      return FALSE;
+done:
+  return FALSE;
 }