Update the Win32 Nmakefiles to reflect the move of the color code into
[obnox/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index 69fb4281620f4f6d3aab1a91e778926318c01143..65b9d81449c1ee1ae7f986a91d2c08351e4eadfd 100644 (file)
--- a/file.c
+++ b/file.c
@@ -1,7 +1,7 @@
 /* file.c
  * File I/O routines
  *
- * $Id: file.c,v 1.83 1999/08/26 07:01:42 gram Exp $
+ * $Id: file.c,v 1.162 2000/02/03 06:35:27 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
 #endif
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 
 #ifdef NEED_SNPRINTF_H
 # ifdef HAVE_STDARG_H
 # include <netinet/in.h>
 #endif
 
-#include "ethereal.h"
+#include "gtk/main.h"
 #include "column.h"
-#include "menu.h"
 #include "packet.h"
 #include "print.h"
 #include "file.h"
+#include "menu.h"
 #include "util.h"
-#include "gtkpacket.h"
+#include "simple_dialog.h"
+#include "ui_util.h"
+#include "gtk/proto_draw.h"
 #include "dfilter.h"
-#include "timestamp.h"
+#include "conversation.h"
+#include "globals.h"
 
-#include "packet-ncp.h"
+#include "plugins.h"
 
 extern GtkWidget *packet_list, *prog_bar, *info_bar, *byte_view, *tree_view;
 extern guint      file_ctx;
-extern int       sync_mode;
-extern int        sync_pipe[];
 
-guint cap_input_id;
+gboolean auto_scroll_live = FALSE;
 
 static guint32 firstsec, firstusec;
 static guint32 prevsec, prevusec;
@@ -97,13 +100,19 @@ static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
 static void freeze_clist(capture_file *cf);
 static void thaw_clist(capture_file *cf);
 
-static gint dfilter_progress_cb(gpointer p);
+static char *file_rename_error_message(int err);
+static char *file_close_error_message(int err);
+
+/* Update the progress bar this many times when reading a file. */
+#define N_PROGBAR_UPDATES      100
 
 int
-open_cap_file(char *fname, capture_file *cf) {
+open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
+{
   wtap       *wth;
   int         err;
-  FILE       *fh;
+  FILE_T      fh;
+  int         fd;
   struct stat cf_stat;
 
   wth = wtap_open_offline(fname, &err);
@@ -112,7 +121,8 @@ open_cap_file(char *fname, capture_file *cf) {
 
   /* Find the size of the file. */
   fh = wtap_file(wth);
-  if (fstat(fileno(fh), &cf_stat) < 0) {
+  fd = wtap_fd(wth);
+  if (fstat(fd, &cf_stat) < 0) {
     err = errno;
     wtap_close(wth);
     goto fail;
@@ -120,25 +130,39 @@ open_cap_file(char *fname, capture_file *cf) {
 
   /* The open succeeded.  Close whatever capture file we had open,
      and fill in the information for this file. */
-  close_cap_file(cf, info_bar, file_ctx);
+  close_cap_file(cf, info_bar);
+
+  /* Initialize the table of conversations. */
+  conversation_init();
 
   /* Initialize protocol-specific variables */
-  ncp_init_protocol();
+  init_all_protocols();
 
   cf->wth = wth;
   cf->fh = fh;
+  cf->filed = fd;
   cf->f_len = cf_stat.st_size;
 
-  /* set the file name because we need it to set the follow stream filter */
+  /* Set the file name because we need it to set the follow stream filter.
+     XXX - is that still true?  We need it for other reasons, though,
+     in any case. */
   cf->filename = g_strdup(fname);
 
+  /* Indicate whether it's a permanent or temporary file. */
+  cf->is_tempfile = is_tempfile;
+
+  /* If it's a temporary capture buffer file, mark it as not saved. */
+  cf->user_saved = !is_tempfile;
+
   cf->cd_t      = wtap_file_type(cf->wth);
-  cf->cd_t_desc = wtap_file_type_string(cf->wth);
   cf->count     = 0;
   cf->drops     = 0;
   cf->esec      = 0;
   cf->eusec     = 0;
   cf->snap      = wtap_snapshot_length(cf->wth);
+  cf->update_progbar = FALSE;
+  cf->progbar_quantum = 0;
+  cf->progbar_nextstep = 0;
   firstsec = 0, firstusec = 0;
   prevsec = 0, prevusec = 0;
  
@@ -152,17 +176,29 @@ fail:
 
 /* Reset everything to a pristine state */
 void
-close_cap_file(capture_file *cf, void *w, guint context) {
+close_cap_file(capture_file *cf, void *w)
+{
   frame_data *fd, *fd_next;
 
   if (cf->fh) {
-    fclose(cf->fh);
+    file_close(cf->fh);
     cf->fh = NULL;
   }
   if (cf->wth) {
     wtap_close(cf->wth);
     cf->wth = NULL;
   }
+  /* We have no file open... */
+  if (cf->filename != NULL) {
+    /* If it's a temporary file, remove it. */
+    if (cf->is_tempfile)
+      unlink(cf->filename);
+    g_free(cf->filename);
+    cf->filename = NULL;
+  }
+  /* ...which means we have nothing to save. */
+  cf->user_saved = FALSE;
+
   for (fd = cf->plist; fd != NULL; fd = fd_next) {
     fd_next = fd->next;
     g_free(fd);
@@ -174,75 +210,128 @@ close_cap_file(capture_file *cf, void *w, guint context) {
   cf->plist = NULL;
   cf->plist_end = NULL;
   unselect_packet(cf); /* nothing to select */
+  cf->first_displayed = NULL;
+  cf->last_displayed = 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));
-  gtk_statusbar_pop(GTK_STATUSBAR(w), context);
+
+  /* Clear any file-related status bar messages.
+     XXX - should be "clear *ALL* file-related status bar messages;
+     will there ever be more than one on the stack? */
+  gtk_statusbar_pop(GTK_STATUSBAR(w), file_ctx);
+
+  /* Restore the standard title bar message. */
+  set_main_window_name("The Ethereal Network Analyzer");
 
   /* Disable all menu items that make sense only if you have a capture. */
-  set_menu_sensitivity("/File/Save", FALSE);
-  set_menu_sensitivity("/File/Save As...", FALSE);
-  set_menu_sensitivity("/File/Close", FALSE);
-  set_menu_sensitivity("/File/Reload", FALSE);
-  set_menu_sensitivity("/File/Print...", FALSE);
-  set_menu_sensitivity("/Display/Options...", FALSE);
-  set_menu_sensitivity("/Tools/Summary", FALSE);
+  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_capture_in_progress(FALSE);
+}
+
+/* Set the file name in the status line, in the name for the main window,
+   and in the name for the main window's icon. */
+static void
+set_display_filename(capture_file *cf)
+{
+  gchar  *name_ptr;
+  size_t  msg_len;
+  gchar  *done_fmt = " File: %s  Drops: %u";
+  gchar  *done_msg;
+  gchar  *win_name_fmt = "%s - Ethereal";
+  gchar  *win_name;
+
+  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);
+  } 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>";
+  }
+
+  msg_len = strlen(name_ptr) + strlen(done_fmt) + 64;
+  done_msg = g_malloc(msg_len);
+  snprintf(done_msg, msg_len, done_fmt, name_ptr, cf->drops);
+  gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, done_msg);
+  g_free(done_msg);
+
+  msg_len = strlen(name_ptr) + strlen(win_name_fmt) + 1;
+  win_name = g_malloc(msg_len);
+  snprintf(win_name, msg_len, win_name_fmt, name_ptr);
+  set_main_window_name(win_name);
+  g_free(win_name);
 }
 
 int
-read_cap_file(capture_file *cf) {
+read_cap_file(capture_file *cf)
+{
   gchar  *name_ptr, *load_msg, *load_fmt = " Loading: %s...";
-  gchar  *done_fmt = " File: %s  Drops: %d";
   int     success;
   int     err;
-  gint    timeout;
   size_t  msg_len;
   char   *errmsg;
   char    errmsg_errno[1024+1];
   gchar   err_str[2048+1];
 
-  if ((name_ptr = (gchar *) strrchr(cf->filename, '/')) == NULL)
-    name_ptr = cf->filename;
-  else
-    name_ptr++;
+  name_ptr = get_basename(cf->filename);
 
-  load_msg = g_malloc(strlen(name_ptr) + strlen(load_fmt) + 2);
-  sprintf(load_msg, load_fmt, name_ptr);
+  msg_len = strlen(name_ptr) + strlen(load_fmt) + 2;
+  load_msg = g_malloc(msg_len);
+  snprintf(load_msg, msg_len, load_fmt, name_ptr);
   gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
-  
-  timeout = gtk_timeout_add(250, file_progress_cb, (gpointer) cf);
+  g_free(load_msg);
+
+  cf->update_progbar = TRUE;
+  /* Update the progress bar when it gets to this value. */
+  cf->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;
+
+#ifndef O_BINARY
+#define O_BINARY       0
+#endif
 
   freeze_clist(cf);
   proto_tree_is_visible = FALSE;
   success = wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
+  /* Set the file encapsulation type now; we don't know what it is until
+     we've looked at all the packets, as we don't know until then whether
+     there's more than one type (and thus whether it's
+     WTAP_ENCAP_PER_PACKET). */
+  cf->lnk_t = wtap_file_encap(cf->wth);
   wtap_close(cf->wth);
   cf->wth = NULL;
-  cf->fh = fopen(cf->filename, "r");
+  cf->filed = open(cf->filename, O_RDONLY|O_BINARY);
+  cf->fh = filed_open(cf->filed, "rb");
+  cf->current_frame = cf->first_displayed;
   thaw_clist(cf);
-  
-  gtk_timeout_remove(timeout);
-  gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), 0);
+
+  gtk_progress_set_activity_mode(GTK_PROGRESS(prog_bar), FALSE);
+  gtk_progress_set_value(GTK_PROGRESS(prog_bar), 0);
 
   gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+  set_display_filename(cf);
 
-  msg_len = strlen(name_ptr) + strlen(done_fmt) + 64;
-  load_msg = g_realloc(load_msg, msg_len);
+  /* Enable menu items that make sense if you have a capture file you've
+     finished reading. */
+  set_menus_for_capture_file(TRUE);
+  set_menus_for_unsaved_capture_file(!cf->user_saved);
 
-  if (cf->user_saved || !cf->save_file)
-    snprintf(load_msg, msg_len, done_fmt, name_ptr, cf->drops);
-  else
-    snprintf(load_msg, msg_len, done_fmt, "<none>", cf->drops);
+  /* Enable menu items that make sense if you have some captured packets. */
+  set_menus_for_captured_packets(TRUE);
 
-  gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
-  g_free(load_msg);
-
-  /* Enable menu items that make sense if you have a capture. */
-  set_menu_sensitivity("/File/Close", TRUE);
-  set_menu_sensitivity("/File/Reload", TRUE);
-  set_menu_sensitivity("/File/Print...", TRUE);
-  set_menu_sensitivity("/Display/Options...", TRUE);
-  set_menu_sensitivity("/Tools/Summary", TRUE);
+  /* 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);
 
   if (!success) {
     /* Put up a message box noting that the read failed somewhere along
@@ -278,93 +367,21 @@ read_cap_file(capture_file *cf) {
 }
 
 #ifdef HAVE_LIBPCAP
-void 
-cap_file_input_cb (gpointer data, gint source, GdkInputCondition condition) {
-  
-  capture_file *cf = (capture_file *)data;
-  char buffer[256], *p = buffer, *q = buffer;
-  int  nread;
-  int  to_read = 0;
-  gboolean exit_loop = FALSE;
-  int  err;
-
-  /* avoid reentrancy problems and stack overflow */
-  gtk_input_remove(cap_input_id);
-
-  if ((nread = read(sync_pipe[0], buffer, 256)) <= 0) {
-
-    /* The child has closed the sync pipe, meaning it's not going to be
-       capturing any more packets.  Read what remains of the capture file,
-       and stop capture (restore menu items) */
-    gtk_clist_freeze(GTK_CLIST(packet_list));
-
-    /* XXX - do something if this fails? */
-    wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
-
-    thaw_clist(cf);
-
-    wtap_close(cf->wth);
-    cf->wth = NULL;
-    set_menu_sensitivity("/File/Open...", TRUE);
-    set_menu_sensitivity("/File/Close", TRUE);
-    set_menu_sensitivity("/File/Save As...", TRUE);
-    set_menu_sensitivity("/File/Print...", TRUE);
-    set_menu_sensitivity("/File/Reload", TRUE);
-#ifdef HAVE_LIBPCAP
-    set_menu_sensitivity("/Capture/Start...", TRUE);
-#endif
-    set_menu_sensitivity("/Tools/Summary", TRUE);
-    gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, " File: <none>");
-    return;
-  }
-
-  buffer[nread] = '\0';
-
-  while(!exit_loop) {
-    /* look for (possibly multiple) '*' */
-    switch (*q) {
-    case '*' :
-      to_read += atoi(p);
-      p = q + 1; 
-      q++;
-      break;
-    case '\0' :
-      /* XXX should handle the case of a pipe full (i.e. no star found) */
-      exit_loop = TRUE;
-      break;
-    default :
-      q++;
-      break;
-    } 
-  }
-
-  gtk_clist_freeze(GTK_CLIST(packet_list));
-  /* XXX - do something if this fails? */
-  wtap_loop(cf->wth, to_read, wtap_dispatch_cb, (u_char *) cf, &err);
-  gtk_clist_thaw(GTK_CLIST(packet_list));
-
-  /* restore pipe handler */
-  cap_input_id = gtk_input_add_full (sync_pipe[0],
-                                    GDK_INPUT_READ,
-                                    cap_file_input_cb,
-                                    NULL,
-                                    (gpointer) cf,
-                                    NULL);
-}
-
 int
-tail_cap_file(char *fname, capture_file *cf) {
+start_tail_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
+{
   int     err;
   int     i;
 
-  err = open_cap_file(fname, cf);
-  if ((err == 0) && (cf->cd_t != WTAP_FILE_UNKNOWN)) {
+  err = open_cap_file(fname, is_tempfile, cf);
+  if (err == 0) {
+    /* Disable menu items that make no sense if you're currently running
+       a capture. */
+    set_menus_for_capture_in_progress(TRUE);
 
-    set_menu_sensitivity("/File/Open...", FALSE);
-    set_menu_sensitivity("/Display/Options...", TRUE);
-#ifdef HAVE_LIBPCAP
-    set_menu_sensitivity("/Capture/Start...", FALSE);
-#endif
+    /* Enable menu items that make sense if you have some captured
+       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)
@@ -377,79 +394,108 @@ tail_cap_file(char *fname, capture_file *cf) {
       }
     }
 
-    cf->fh = fopen(fname, "r");
+    /* Yes, "open_cap_file()" set this - but it set it to a file handle
+       from Wiretap, which will be closed when we close the file; we
+       want it to remain open even after that, so that we can read
+       packet data from it. */
+    cf->fh = file_open(fname, "rb");
 
-    cap_input_id = gtk_input_add_full (sync_pipe[0],
-                                      GDK_INPUT_READ,
-                                      cap_file_input_cb,
-                                      NULL,
-                                      (gpointer) cf,
-                                      NULL);
     gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, 
                       " <live capture in progress>");
   }
-  else {
-    close(sync_pipe[0]);
-  }
   return err;
 }
-#endif
-
-/* To do: Add check_col checks to the col_add* routines */
 
-static void
-col_add_abs_time(frame_data *fd, gint el)
+int
+continue_tail_cap_file(capture_file *cf, int to_read)
 {
-  struct tm *tmp;
-  time_t then;
-
-  then = fd->abs_secs;
-  tmp = localtime(&then);
-  col_add_fstr(fd, el, "%02d:%02d:%02d.%04ld",
-    tmp->tm_hour,
-    tmp->tm_min,
-    tmp->tm_sec,
-    (long)fd->abs_usecs/100);
-}
+  int err;
 
-static void
-col_add_rel_time(frame_data *fd, gint el)
-{
-  col_add_fstr(fd, el, "%d.%06d", fd->rel_secs, fd->rel_usecs);
+  gtk_clist_freeze(GTK_CLIST(packet_list));
+
+  wtap_loop(cf->wth, to_read, wtap_dispatch_cb, (u_char *) cf, &err);
+
+  gtk_clist_thaw(GTK_CLIST(packet_list));
+  if (auto_scroll_live && cf->plist_end != NULL)
+    gtk_clist_moveto(GTK_CLIST(packet_list), 
+                      cf->plist_end->row, -1, 1.0, 1.0);
+  return err;
 }
 
-static void
-col_add_delta_time(frame_data *fd, gint el)
+int
+finish_tail_cap_file(capture_file *cf)
 {
-  col_add_fstr(fd, el, "%d.%06d", fd->del_secs, fd->del_usecs);
-}
+  int err;
+
+  gtk_clist_freeze(GTK_CLIST(packet_list));
+
+  wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
+
+  thaw_clist(cf);
+  if (auto_scroll_live && cf->plist_end != NULL)
+    gtk_clist_moveto(GTK_CLIST(packet_list), 
+                      cf->plist_end->row, -1, 1.0, 1.0);
+
+  /* Set the file encapsulation type now; we don't know what it is until
+     we've looked at all the packets, as we don't know until then whether
+     there's more than one type (and thus whether it's
+     WTAP_ENCAP_PER_PACKET). */
+  cf->lnk_t = wtap_file_encap(cf->wth);
+
+  /* There's nothing more to read from the capture file - close it. */
+  wtap_close(cf->wth);
+  cf->wth = NULL;
 
-/* Add "command-line-specified" time. */
+  /* Pop the "<live capture in progress>" message off the status bar. */
+  gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+
+  set_display_filename(cf);
+
+  /* Enable menu items that make sense if you're not currently running
+     a capture. */
+  set_menus_for_capture_in_progress(FALSE);
+
+  /* Enable menu items that make sense if you have a capture file
+     you've finished reading. */
+  set_menus_for_capture_file(TRUE);
+  set_menus_for_unsaved_capture_file(!cf->user_saved);
+
+  return err;
+}
+#endif /* HAVE_LIBPCAP */
+
+typedef struct {
+  color_filter_t *colorf;
+  proto_tree   *protocol_tree;
+  guint8       *pd;
+} apply_color_filter_args;
+
+/*
+ * If no color filter has been applied, apply this one.
+ * (The "if no color filter has been applied" is to handle the case where
+ * more than one color filter matches the packet.)
+ */
 static void
-col_add_cls_time(frame_data *fd)
+apply_color_filter(gpointer filter_arg, gpointer argp)
 {
-  switch (timestamp_type) {
-    case ABSOLUTE:
-      col_add_abs_time(fd, COL_CLS_TIME);
-      break;
+  color_filter_t *colorf = filter_arg;
+  apply_color_filter_args *args = argp;
 
-    case RELATIVE:
-      col_add_rel_time(fd, COL_CLS_TIME);
-      break;
-
-    case DELTA:
-      col_add_delta_time(fd, COL_CLS_TIME);
-      break;
+  if (colorf->c_colorfilter != NULL && args->colorf == NULL) {
+    if (dfilter_apply(colorf->c_colorfilter, args->protocol_tree, args->pd))
+      args->colorf = colorf;
   }
 }
 
 static void
 add_packet_to_packet_list(frame_data *fdata, capture_file *cf, const u_char *buf)
 {
+  apply_color_filter_args args;
   gint          i, row;
-  gint         crow;
-  gint                 color;
-  proto_tree   *protocol_tree;
+  proto_tree   *protocol_tree = NULL;
+
+  /* We don't yet have a color filter to apply. */
+  args.colorf = NULL;
 
   /* If we don't have the time stamp of the first packet in the
      capture, it's because this is the first packet.  Save the time
@@ -474,52 +520,61 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf, const u_char *buf
   }
 
   /* Apply the filters */
-  if (DFILTER_CONTAINS_FILTER(cf->dfcode) ||
-      CFILTERS_CONTAINS_FILTER(cf)) {
-       protocol_tree = proto_tree_create_root();
-       dissect_packet(buf, fdata, protocol_tree);
-       if( DFILTER_CONTAINS_FILTER(cf->dfcode) )
-               fdata->passed_dfilter = dfilter_apply(cf->dfcode, protocol_tree, cf->pd);
-       else
-               fdata->passed_dfilter = TRUE;
-       /* Apply color filters.  The debuggery can come out real soon! */
-#ifdef DEBUG_COLOR_FILTERS
-       fprintf(stderr, "Processing %d filters...\n",cf->colors->num_of_filters);
-       fflush(stderr);
-#endif
-       color = -1;
-        for(crow = 0; cf->colors->num_of_filters && 
-             crow < cf->colors->num_of_filters; crow++) {
-#ifdef DEBUG_COLOR_FILTERS
-            fprintf(stderr, "Does it match filter %s (%d)? ",
-              get_color_filter_name(cf,crow),crow);
-            fflush(stderr);
-#endif
-            if(dfilter_apply(color_filter(cf,crow)->c_colorfilter, protocol_tree,
-                cf->pd)){
-                color = crow;
-#ifdef DEBUG_COLOR_FILTERS
-                fprintf(stderr,"yes\n");
-#endif
-            }
-#ifdef DEBUG_COLOR_FILTERS
-            else {
-                fprintf(stderr,"no\n") ;
-                fflush(stderr);
-           }
-#endif
-        }
+  if (cf->dfcode != NULL || filter_list != NULL) {
+    protocol_tree = proto_tree_create_root();
+    dissect_packet(buf, fdata, protocol_tree);
+    if (cf->dfcode != NULL)
+      fdata->passed_dfilter = dfilter_apply(cf->dfcode, protocol_tree, cf->pd);
+    else
+      fdata->passed_dfilter = TRUE;
 
-       proto_tree_free(protocol_tree);
+    /* Apply color filters, if we have any. */
+    if (filter_list != NULL) {
+      args.protocol_tree = protocol_tree;
+      args.pd = cf->pd;
+      g_slist_foreach(filter_list, apply_color_filter, &args);
+    }
+    proto_tree_free(protocol_tree);
   }
   else {
-       dissect_packet(buf, fdata, NULL);
+#ifdef HAVE_PLUGINS
+       if (plugin_list)
+           protocol_tree = proto_tree_create_root();
+#endif
+       dissect_packet(buf, fdata, protocol_tree);
        fdata->passed_dfilter = TRUE;
-       color = -1;
+#ifdef HAVE_PLUGINS
+       if (protocol_tree)
+           proto_tree_free(protocol_tree);
+#endif
   }
+
   if (fdata->passed_dfilter) {
-    if (check_col(fdata, COL_NUMBER))
-      col_add_fstr(fdata, COL_NUMBER, "%d", cf->count);
+    /* XXX - if a GtkCList's selection mode is GTK_SELECTION_BROWSE, when
+       the first entry is added to it by "real_insert_row()", that row
+       is selected (see "real_insert_row()", in "gtk/gtkclist.c", in both
+       our version and the vanilla GTK+ version).
+
+       This means that a "select-row" signal is emitted; this causes
+       "packet_list_select_cb()" to be called, which causes "select_packet()"
+       to be called.
+
+       "select_packet()" searches the list of frames for a frame with the
+       row number passed into it; however, as "gtk_clist_append()", which
+       called "real_insert_row()", hasn't yet returned, we don't know what
+       the row number is, so we can't correctly set "fd->row" for that frame
+       yet.
+
+       This means that we won't find the frame for that row.
+
+       We can't assume that there's only one frame in the frame list,
+       either, as we may be filtering the display.
+
+       Therefore, we set "fdata->row" to 0, under the assumption that
+       the row number passed to "select_packet()" will be 0 (as we're
+       adding the first row to the list; it gets set to the proper
+       value later. */
+    fdata->row = 0;
 
     /* 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
@@ -546,45 +601,32 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf, const u_char *buf
     prevsec = fdata->abs_secs;
     prevusec = fdata->abs_usecs;
 
-    /* Set any time stamp columns. */
-    if (check_col(fdata, COL_CLS_TIME))
-      col_add_cls_time(fdata);
-    if (check_col(fdata, COL_ABS_TIME))
-      col_add_abs_time(fdata, COL_ABS_TIME);
-    if (check_col(fdata, COL_REL_TIME))
-      col_add_rel_time(fdata, COL_REL_TIME);
-    if (check_col(fdata, COL_DELTA_TIME))
-      col_add_delta_time(fdata, COL_DELTA_TIME);
-
-    if (check_col(fdata, COL_PACKET_LENGTH))
-      col_add_fstr(fdata, COL_PACKET_LENGTH, "%d", fdata->pkt_len);
+    fill_in_columns(fdata);
 
     row = gtk_clist_append(GTK_CLIST(packet_list), fdata->cinfo->col_data);
     fdata->row = row;
 
-        if (cf->colors->color_filters && (color != -1)){
-                gtk_clist_set_background(GTK_CLIST(packet_list), row,
-                   &(color_filter(cf,color)->bg_color));
-                gtk_clist_set_foreground(GTK_CLIST(packet_list), row,
-                   &(color_filter(cf,color)->fg_color));
-       } else {
-                gtk_clist_set_background(GTK_CLIST(packet_list), row,
-                   &WHITE);
-                gtk_clist_set_foreground(GTK_CLIST(packet_list), row,
-                   &BLACK);
+    if (filter_list != NULL && (args.colorf != NULL)) {
+        gtk_clist_set_background(GTK_CLIST(packet_list), row,
+                   &args.colorf->bg_color);
+        gtk_clist_set_foreground(GTK_CLIST(packet_list), row,
+                   &args.colorf->fg_color);
+    } else {
+        gtk_clist_set_background(GTK_CLIST(packet_list), row, &WHITE);
+        gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &BLACK);
+    }
 
-       }
+    /* If we haven't yet seen the first frame, this is it. */
+    if (cf->first_displayed == NULL)
+      cf->first_displayed = fdata;
 
-#ifdef DEBUG_COLOR_FILTERS
-        fprintf(stderr,"Now row %d\n", row+2);
-        fflush(stderr);    
-#endif
+    /* This is the last frame we've seen so far. */
+    cf->last_displayed = fdata;
 
-    /* If this was the selected packet, remember the row it's in, so
-       we can re-select it.  ("selected_packet" is 0-origin, as it's
-       a GList index; "count", however, is 1-origin.) */
-    if (cf->selected_packet == cf->count - 1)
-      cf->selected_row = row;
+    /* If this was the current frame, remember the row it's in, so
+       we can arrange that it's on the screen when we're done. */
+    if (cf->current_frame == fdata)
+      cf->current_row = row;
   } else
     fdata->row = -1;   /* not in the display */
   fdata->cinfo = NULL;
@@ -598,34 +640,54 @@ wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
   int           passed;
   proto_tree   *protocol_tree;
   frame_data   *plist_end;
-
-  while (gtk_events_pending())
-    gtk_main_iteration();
+  int file_pos;
+  float prog_val;
+
+  /* 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.
+     
+     Do so only if we were told to do so; when reading a capture file
+     being updated by a live capture, we don't do so (as we're not
+     "done" until the capture stops, so we don't know how close to
+     "done" we are. */
+
+  if (cf->update_progbar && offset >= cf->progbar_nextstep) {
+      file_pos = lseek(cf->filed, 0, SEEK_CUR);
+      prog_val = (gfloat) file_pos / (gfloat) cf->f_len;
+      gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), prog_val);
+      cf->progbar_nextstep += cf->progbar_quantum;
+      while (gtk_events_pending())
+      gtk_main_iteration();
+  }
 
   /* Allocate the next list entry, and add it to the list. */
   fdata = (frame_data *) g_malloc(sizeof(frame_data));
 
   fdata->next = NULL;
+  fdata->prev = NULL;
   fdata->pkt_len  = phdr->len;
   fdata->cap_len  = phdr->caplen;
   fdata->file_off = offset;
   fdata->lnk_t = phdr->pkt_encap;
   fdata->abs_secs  = phdr->ts.tv_sec;
   fdata->abs_usecs = phdr->ts.tv_usec;
+  fdata->encoding = CHAR_ASCII;
   fdata->pseudo_header = phdr->pseudo_header;
   fdata->cinfo = NULL;
 
   passed = TRUE;
   if (cf->rfcode) {
-         if (DFILTER_CONTAINS_FILTER(cf->rfcode)) {
-           protocol_tree = proto_tree_create_root();
-           dissect_packet(buf, fdata, protocol_tree);
-           passed = dfilter_apply(cf->rfcode, protocol_tree, cf->pd);
-           proto_tree_free(protocol_tree);
-         }
+    protocol_tree = proto_tree_create_root();
+    dissect_packet(buf, fdata, protocol_tree);
+    passed = dfilter_apply(cf->rfcode, protocol_tree, cf->pd);
+    proto_tree_free(protocol_tree);
   }   
   if (passed) {
     plist_end = cf->plist_end;
+    fdata->prev = plist_end;
     if (plist_end != NULL)
       plist_end->next = fdata;
     else
@@ -633,30 +695,74 @@ wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
     cf->plist_end = fdata;
 
     cf->count++;
+    fdata->num = cf->count;
     add_packet_to_packet_list(fdata, cf, buf);
   } else
     g_free(fdata);
 }
 
-void
-filter_packets(capture_file *cf)
+int
+filter_packets(capture_file *cf, gchar *dftext)
 {
-/*  gint timeout;*/
-  frame_data *fd;
+  dfilter *dfcode;
 
-  if (cf->dfilter == NULL) {
-       dfilter_clear_filter(cf->dfcode);
-  }
-  else {
+  if (dftext == NULL) {
+    /* The new filter is an empty filter (i.e., display all packets). */
+    dfcode = NULL;
+  else {
     /*
-     * Compile the filter.
+     * We have a filter; try to compile it.
      */
-    if (dfilter_compile(cf->dfcode, cf->dfilter) != 0) {
+    if (dfilter_compile(dftext, &dfcode) != 0) {
+      /* The attempt failed; report an error. */
       simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
-      return;
+      return 0;
+    }
+
+    /* Was it empty? */
+    if (dfcode == NULL) {
+      /* Yes - free the filter text, and set it to null. */
+      g_free(dftext);
+      dftext = NULL;
     }
   }
 
+  /* We have a valid filter.  Replace the current filter. */
+  if (cf->dfilter != NULL)
+    g_free(cf->dfilter);
+  cf->dfilter = dftext;
+  if (cf->dfcode != NULL)
+    dfilter_destroy(cf->dfcode);
+  cf->dfcode = dfcode;
+
+  /* Now go through the list of packets we've read from the capture file,
+     applying the current display filter, and, if the packet passes the
+     display filter, add it to the summary display, appropriately
+     colored.  (That's how we colorize the display - it's like filtering
+     the display, only we don't install a new filter.) */
+  colorize_packets(cf);
+  return 1;
+}
+
+void
+colorize_packets(capture_file *cf)
+{
+  frame_data *fd;
+  guint32 progbar_quantum;
+  guint32 progbar_nextstep;
+  int count;
+
+  /* We need to re-initialize all the state information that protocols
+     keep, because we're making a fresh pass through all the packets. */
+
+  /* Initialize the table of conversations. */
+  conversation_init();
+
+  /* Initialize protocol-specific variables */
+  init_all_protocols();
+
+  gtk_progress_set_activity_mode(GTK_PROGRESS(prog_bar), FALSE);
+
   /* 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));
@@ -664,9 +770,13 @@ filter_packets(capture_file *cf)
   /* Clear it out. */
   gtk_clist_clear(GTK_CLIST(packet_list));
 
+  /* We don't yet know which will be the first and last frames displayed. */
+  cf->first_displayed = NULL;
+  cf->last_displayed = NULL;
+
   /* If a packet was selected, we don't know yet what row, if any, it'll
      get. */
-  cf->selected_row = -1;
+  cf->current_row = -1;
 
   /* Iterate through the list of packets, calling a routine
      to run the filter on the packet, see if it matches, and
@@ -675,105 +785,222 @@ filter_packets(capture_file *cf)
   firstusec = 0;
   prevsec = 0;
   prevusec = 0;
-  cf->unfiltered_count = cf->count;
-  cf->count = 0;
 
   proto_tree_is_visible = FALSE;
 
-/*  timeout = gtk_timeout_add(250, dfilter_progress_cb, 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;
+
+  gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(prog_bar), GTK_PROGRESS_LEFT_TO_RIGHT);
+
   for (fd = cf->plist; fd != NULL; fd = fd->next) {
-    cf->count++;
+    /* 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);
+
+      gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar),
+               (gfloat) count / cf->count);
+
+      progbar_nextstep += progbar_quantum;
+      while (gtk_events_pending())
+        gtk_main_iteration();
+    }
 
-    fseek(cf->fh, fd->file_off, SEEK_SET);
-    fread(cf->pd, sizeof(guint8), fd->cap_len, cf->fh);
+    count++;
 
-    add_packet_to_packet_list(fd, cf, cf->pd);
+    wtap_seek_read (cf->cd_t, cf->fh, fd->file_off, cf->pd, fd->cap_len);
 
-    if (cf->count % 20 == 0) {
-      dfilter_progress_cb(cf);
-    }
+    add_packet_to_packet_list(fd, cf, cf->pd);
   }
-/*  gtk_timeout_remove(timeout);*/
  
   gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), 0);
 
-  if (cf->selected_row != -1) {
-    /* We had a selected packet and it passed the filter. */
-    gtk_clist_select_row(GTK_CLIST(packet_list), cf->selected_row, -1);
+  if (cf->current_row != -1) {
+    /* The current frame passed the filter; make sure it's visible. */
+    if (!gtk_clist_row_is_visible(GTK_CLIST(packet_list), cf->current_row))
+      gtk_clist_moveto(GTK_CLIST(packet_list), cf->current_row, -1, 0.0, 0.0);
+    if (cf->current_frame_is_selected) {
+      /* It was selected, so re-select it. */
+      gtk_clist_select_row(GTK_CLIST(packet_list), cf->current_row, -1);
+    }
+    finfo_selected = NULL;
   } else {
-    /* If we had one, it didn't pass the filter. */
+    /* The current frame didn't pass the filter; make the first frame
+       the current frame, and leave it unselected. */
     unselect_packet(cf);
+    cf->current_frame = cf->first_displayed;
   }
 
   /* Unfreeze the packet list. */
   gtk_clist_thaw(GTK_CLIST(packet_list));
 }
 
-/* Update the progress bar */
-static gint
-dfilter_progress_cb(gpointer p) {
-  capture_file *cf = (capture_file*)p;
-
-  /* let's not divide by zero. I should never be started
-   * with unfiltered_count == 0, so let's assert that
-   */
-  g_assert(cf->unfiltered_count > 0);
-
-  gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar),
-    (gfloat) cf->count / cf->unfiltered_count);
-
-  /* Have GTK+ repaint what is pending */
-  while (gtk_events_pending ()) {
-         gtk_main_iteration();
-  }
-  return TRUE;
-}
-
+#define        MAX_LINE_LENGTH 256
 
 int
-print_packets(capture_file *cf, int to_file, const char *dest)
+print_packets(capture_file *cf, print_args_t *print_args)
 {
+  int         i;
   frame_data *fd;
+  guint32     progbar_quantum;
+  guint32     progbar_nextstep;
+  guint32     count;
   proto_tree *protocol_tree;
-
-  cf->print_fh = open_print_dest(to_file, dest);
+  gint       *col_widths = NULL;
+  gint        data_width;
+  gboolean    print_separator;
+  char        line_buf[MAX_LINE_LENGTH+1];     /* static-sized buffer! */
+  char        *cp;
+  int         sprintf_len;
+
+  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 */
 
-  /* XXX - printing multiple frames in PostScript looks as if it's
-     tricky - you have to deal with page boundaries, I think -
-     and I'll have to spend some time learning enough about
-     PostScript to figure it out, so, for now, we only print
-     multiple frames as text. */
-#if 0
-  print_preamble(cf->print_fh);
-#endif
+  print_preamble(cf->print_fh, print_args->format);
 
-  proto_tree_is_visible = TRUE;
+  if (print_args->print_summary) {
+    /* We're printing packet summaries.
 
-  /* Iterate through the list of packets, printing each of them.  */
-  cf->count = 0;
-  for (fd = cf->plist; fd != NULL; fd = fd->next) {
-    cf->count++;
+       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];
+    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;
+      }
 
-    fseek(cf->fh, fd->file_off, SEEK_SET);
-    fread(cf->pd, sizeof(guint8), fd->cap_len, cf->fh);
+      /* Right-justify the packet number column. */
+      if (cf->cinfo.col_fmt[i] == COL_NUMBER)
+        sprintf_len = sprintf(cp, "%*s", col_widths[i], cf->cinfo.col_title[i]);
+      else
+        sprintf_len = sprintf(cp, "%-*s", col_widths[i], cf->cinfo.col_title[i]);
+      cp += sprintf_len;
+      if (i == cf->cinfo.num_cols - 1)
+        *cp++ = '\n';
+      else
+        *cp++ = ' ';
+    }
+    *cp = '\0';
+    print_line(cf->print_fh, print_args->format, line_buf);
+  }
 
-    /* create the logical protocol tree */
-    protocol_tree = proto_tree_create_root();
-    dissect_packet(cf->pd, fd, protocol_tree);
+  print_separator = FALSE;
+  proto_tree_is_visible = TRUE;
 
-    /* Print the packet */
-    proto_tree_print(cf->count, (GNode *)protocol_tree, cf->pd, fd, cf->print_fh);
+  /* 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;
 
-    proto_tree_free(protocol_tree);
+  gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(prog_bar), GTK_PROGRESS_LEFT_TO_RIGHT);
+
+  /* Iterate through the list of packets, printing the packets that
+     were selected by the current display filter.  */
+  for (fd = cf->plist; fd != NULL; fd = fd->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);
+
+      gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar),
+        (gfloat) count / cf->count);
+      progbar_nextstep += progbar_quantum;
+      while (gtk_events_pending())
+        gtk_main_iteration();
+    }
+    count++;
+
+    if (fd->passed_dfilter) {
+      wtap_seek_read (cf->cd_t, cf->fh, fd->file_off, cf->pd, fd->cap_len);
+      if (print_args->print_summary) {
+        /* Fill in the column information, but don't bother creating
+           the logical protocol tree. */
+        fd->cinfo = &cf->cinfo;
+        for (i = 0; i < fd->cinfo->num_cols; i++) {
+          fd->cinfo->col_data[i][0] = '\0';
+        }
+        dissect_packet(cf->pd, fd, NULL);
+        fill_in_columns(fd);
+        cp = &line_buf[0];
+        for (i = 0; i < cf->cinfo.num_cols; i++) {
+          /* Right-justify the packet number column. */
+          if (cf->cinfo.col_fmt[i] == COL_NUMBER)
+            sprintf_len = sprintf(cp, "%*s", col_widths[i], cf->cinfo.col_data[i]);
+          else
+            sprintf_len = sprintf(cp, "%-*s", col_widths[i], cf->cinfo.col_data[i]);
+          cp += sprintf_len;
+          if (i == cf->cinfo.num_cols - 1)
+            *cp++ = '\n';
+          else
+            *cp++ = ' ';
+        }
+        *cp = '\0';
+        print_line(cf->print_fh, print_args->format, line_buf);
+      } else {
+        if (print_separator)
+          print_line(cf->print_fh, print_args->format, "\n");
+
+        /* Create the logical protocol tree. */
+        protocol_tree = proto_tree_create_root();
+        dissect_packet(cf->pd, fd, protocol_tree);
+
+        /* Print the information in that tree. */
+        proto_tree_print(FALSE, print_args, (GNode *)protocol_tree,
+                       cf->pd, fd, cf->print_fh);
+
+        proto_tree_free(protocol_tree);
+
+       if (print_args->print_hex) {
+         /* Print the full packet data as hex. */
+         print_hex_data(cf->print_fh, print_args->format, cf->pd,
+                       fd->cap_len, fd->encoding);
+       }
+
+        /* Print a blank line if we print anything after this. */
+        print_separator = TRUE;
+      }
+    }
   }
 
-#if 0
-  print_finale(cf->print_fh);
-#endif
+  if (col_widths != NULL)
+    g_free(col_widths);
+
+  print_finale(cf->print_fh, print_args->format);
+
+  close_print_dest(print_args->to_file, cf->print_fh);
+  gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), 0);
 
-  close_print_dest(to_file, cf->print_fh);
   cf->print_fh = NULL;
   return TRUE;
 }
@@ -808,14 +1035,12 @@ change_time_formats(capture_file *cf)
       if (check_col(fd, COL_CLS_TIME)) {
         /* There are columns that show the time in the "command-line-specified"
            format; update them. */
-        for (i = 0; i < cf->cinfo.num_cols; i++) {
-          cf->cinfo.col_data[i][0] = '\0';
-        }
-        col_add_cls_time(fd);
         for (i = 0; i < cf->cinfo.num_cols; i++) {
           if (cf->cinfo.fmt_matx[i][COL_CLS_TIME]) {
             /* This is one of the columns that shows the time in
                "command-line-specified" format; update it. */
+            cf->cinfo.col_data[i][0] = '\0';
+            col_set_cls_time(fd, i);
             gtk_clist_set_text(GTK_CLIST(packet_list), fd->row, i,
                          cf->cinfo.col_data[i]);
          }
@@ -830,7 +1055,7 @@ change_time_formats(capture_file *cf)
   for (i = 0; i < cf->cinfo.num_cols; i++) {
     if (cf->cinfo.fmt_matx[i][COL_CLS_TIME]) {
       gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
-        get_column_width(COL_CLS_TIME, pl_style->font));
+        gdk_string_width(pl_style->font, get_column_longest_string(COL_CLS_TIME)));
     }
   }
 
@@ -841,9 +1066,6 @@ change_time_formats(capture_file *cf)
 static void
 clear_tree_and_hex_views(void)
 {
-  GList *selection;
-  GtkWidget *tmp_item;
-
   /* Clear the hex dump. */
   gtk_text_freeze(GTK_TEXT(byte_view));
   gtk_text_set_point(GTK_TEXT(byte_view), 0);
@@ -851,27 +1073,142 @@ clear_tree_and_hex_views(void)
     gtk_text_get_length(GTK_TEXT(byte_view)));
   gtk_text_thaw(GTK_TEXT(byte_view));
 
-  /* Deselect any selected tree item. gtktree.c should
-   * do this when we clear_items, but it doesn't. I copied
-   * this while() loop from gtktree.c, gtk_real_tree_select_child()
-   */
-  if (GTK_TREE(tree_view)->root_tree) {
-         selection = GTK_TREE(tree_view)->root_tree->selection;
-         while (selection) {
-                 tmp_item = selection->data;
-                 gtk_tree_item_deselect(GTK_TREE_ITEM(tmp_item));
-                 gtk_widget_unref(tmp_item);
-                 selection = selection->next;
-         }
-         g_list_free(GTK_TREE(tree_view)->root_tree->selection);
-         GTK_TREE(tree_view)->root_tree->selection = NULL;
+  /* Remove all nodes in ctree. This is how it's done in testgtk.c in GTK+ */
+  gtk_clist_clear ( GTK_CLIST(tree_view) );
+
+}
+
+gboolean
+find_packet(capture_file *cf, dfilter *sfcode)
+{
+  frame_data *start_fd;
+  frame_data *fd;
+  frame_data *new_fd = NULL;
+  guint32 progbar_quantum;
+  guint32 progbar_nextstep;
+  int count;
+  proto_tree *protocol_tree;
+
+  start_fd = cf->current_frame;
+  if (start_fd != NULL)  {
+    gtk_progress_set_activity_mode(GTK_PROGRESS(prog_bar), FALSE);
+
+    /* Iterate through the list of packets, starting at the packet we've
+       picked, calling a routine to run the filter on the packet, see if
+       it matches, and stop if so.  */
+    count = 0;
+    fd = start_fd;
+
+    proto_tree_is_visible = FALSE;
+
+    /* 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;
+    gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(prog_bar), GTK_PROGRESS_LEFT_TO_RIGHT);
+
+    fd = start_fd;
+    for (;;) {
+      /* 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);
+
+        gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar),
+               (gfloat) count / cf->count);
+
+        progbar_nextstep += progbar_quantum;
+        while (gtk_events_pending())
+          gtk_main_iteration();
+      }
+
+      /* Go past the current frame. */
+      if (cf->sbackward) {
+        /* Go on to the previous frame. */
+        fd = fd->prev;
+        if (fd == NULL)
+          fd = cf->plist_end;  /* wrap around */
+      } else {
+        /* Go on to the next frame. */
+        fd = fd->next;
+        if (fd == NULL)
+          fd = cf->plist;      /* wrap around */
+      }
+
+      if (fd == start_fd) {
+        /* We're back to the frame we were on originally.  The search
+           failed. */
+        break;
+      }
+
+      count++;
+
+      /* Is this packet in the display? */
+      if (fd->passed_dfilter) {
+        /* Yes.  Does it match the search filter? */
+        protocol_tree = proto_tree_create_root();
+        wtap_seek_read(cf->cd_t, cf->fh, fd->file_off, cf->pd, fd->cap_len);
+        dissect_packet(cf->pd, fd, protocol_tree);
+        if (dfilter_apply(sfcode, protocol_tree, cf->pd)) {
+          new_fd = fd;
+          break;       /* found it! */
+        }
+      }
+    }
+
+    gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), 0);
   }
 
-  /* Clear the protocol tree view. The length arg of -1
-   * means to clear all items up to the end. */
-  gtk_tree_clear_items(GTK_TREE(tree_view), 0, -1);
+  if (new_fd != NULL) {
+    /* We found a frame.  Make it visible, and select it. */
+    if (!gtk_clist_row_is_visible(GTK_CLIST(packet_list), new_fd->row))
+      gtk_clist_moveto(GTK_CLIST(packet_list), new_fd->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 = new_fd->row;
+    gtk_clist_select_row(GTK_CLIST(packet_list), new_fd->row, -1);
+    return TRUE;       /* success */
+  } else
+    return FALSE;      /* failure */
 }
 
+goto_result_t
+goto_frame(capture_file *cf, guint fnumber)
+{
+  frame_data *fd;
+
+  for (fd = cf->plist; fd != NULL && fd->num < fnumber; fd = fd->next)
+    ;
+
+  if (fd == NULL)
+    return NO_SUCH_FRAME;      /* we didn't find that frame */
+  if (!fd->passed_dfilter)
+    return FRAME_NOT_DISPLAYED;        /* the frame with that number isn't displayed */
+
+  /* We found that frame, and it's currently being displayed.
+     Make it visible, and select it. */
+  if (!gtk_clist_row_is_visible(GTK_CLIST(packet_list), fd->row))
+    gtk_clist_moveto(GTK_CLIST(packet_list), fd->row, -1, 0.0, 0.0);
+
+  /* See above complaint about the lack of "gtk_clist_set_focus_row()". */
+  GTK_CLIST(packet_list)->focus_row = fd->row;
+  gtk_clist_select_row(GTK_CLIST(packet_list), fd->row, -1);
+  return FOUND_FRAME;
+}
 
 /* Select the packet on a given row. */
 void
@@ -880,50 +1217,44 @@ select_packet(capture_file *cf, int row)
   frame_data *fd;
   int i;
 
-  /* Clear out whatever's currently in the hex dump. */
-  gtk_text_freeze(GTK_TEXT(byte_view));
-  gtk_text_set_point(GTK_TEXT(byte_view), 0);
-  gtk_text_forward_delete(GTK_TEXT(byte_view),
-    gtk_text_get_length(GTK_TEXT(byte_view)));
-
   /* Search through the list of frames to see which one is in
      this row. */
   for (fd = cf->plist, i = 0; fd != NULL; fd = fd->next, i++) {
     if (fd->row == row)
       break;
   }
-  cf->fd = fd;
 
-  /* Remember the ordinal number of that frame. */
-  cf->selected_packet = i;
+  g_assert(fd != NULL);
+
+  /* Record that this frame is the current frame, and that it's selected. */
+  cf->current_frame = fd;
+  cf->current_frame_is_selected = TRUE;
 
   /* Get the data in that frame. */
-  fseek(cf->fh, cf->fd->file_off, SEEK_SET);
-  fread(cf->pd, sizeof(guint8), cf->fd->cap_len, cf->fh);
+  wtap_seek_read (cf->cd_t, cf->fh, fd->file_off, cf->pd, fd->cap_len);
 
   /* Create the logical protocol tree. */
   if (cf->protocol_tree)
       proto_tree_free(cf->protocol_tree);
   cf->protocol_tree = proto_tree_create_root();
   proto_tree_is_visible = TRUE;
-  dissect_packet(cf->pd, cf->fd, cf->protocol_tree);
+  dissect_packet(cf->pd, cf->current_frame, cf->protocol_tree);
 
   /* Display the GUI protocol tree and hex dump. */
   clear_tree_and_hex_views();
   proto_tree_draw(cf->protocol_tree, tree_view);
-  packet_hex_print(GTK_TEXT(byte_view), cf->pd, cf->fd->cap_len, -1, -1);
-  gtk_text_thaw(GTK_TEXT(byte_view));
+  packet_hex_print(GTK_TEXT(byte_view), cf->pd, cf->current_frame->cap_len,
+                       -1, -1, cf->current_frame->encoding);
 
-  /* A packet is selected, so "File/Print Packet" has something to print. */
-  set_menu_sensitivity("/File/Print Packet", TRUE);
+  /* A packet is selected. */
+  set_menus_for_selected_packet(TRUE);
 }
 
 /* Unselect the selected packet, if any. */
 void
 unselect_packet(capture_file *cf)
 {
-  cf->selected_packet = -1;    /* nothing there to be selected */
-  cf->selected_row = -1;
+  cf->current_frame_is_selected = FALSE;
 
   /* Destroy the protocol tree for that packet. */
   if (cf->protocol_tree != NULL) {
@@ -931,11 +1262,13 @@ unselect_packet(capture_file *cf)
     cf->protocol_tree = NULL;
   }
 
+  finfo_selected = NULL;
+
   /* Clear out the display of that packet. */
   clear_tree_and_hex_views();
 
-  /* No packet is selected, so "File/Print Packet" has nothing to print. */
-  set_menu_sensitivity("/File/Print Packet", FALSE);
+  /* No packet is selected. */
+  set_menus_for_selected_packet(FALSE);
 }
 
 static void
@@ -976,92 +1309,192 @@ thaw_clist(capture_file *cf)
     gtk_clist_set_column_resizeable(GTK_CLIST(packet_list), i, TRUE);
 }
 
-/* Tries to mv a file. If unsuccessful, tries to cp the file.
- * Returns 0 on failure to do either, 1 on success of either
- */
 int
-file_mv(char *from, char *to)
+save_cap_file(char *fname, capture_file *cf, gboolean save_filtered,
+               guint save_format)
 {
-
-#define COPY_BUFFER_SIZE       8192
-
-       int retval;
-
-#ifndef WIN32
-       /* try a hard link */
-       retval = link(from, to);
-
-       /* or try a copy */
-       if (retval < 0) {
-#endif
-               retval = file_cp(from, to);
-               if (!retval) {
-                       return 0;
-               }
-#ifndef WIN32
+  gchar        *from_filename;
+  gchar        *name_ptr, *save_msg, *save_fmt = " Saving: %s...";
+  size_t        msg_len;
+  int           err;
+  gboolean      do_copy;
+  int           from_fd, to_fd, nread, nwritten;
+  wtap_dumper  *pdh;
+  frame_data   *fd;
+  struct wtap_pkthdr hdr;
+  guint8        pd[65536];
+
+  name_ptr = get_basename(fname);
+  msg_len = strlen(name_ptr) + strlen(save_fmt) + 2;
+  save_msg = g_malloc(msg_len);
+  snprintf(save_msg, msg_len, save_fmt, name_ptr);
+  gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, save_msg);
+  g_free(save_msg);
+
+  if (!save_filtered && 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. */
+
+    /* In this branch, we set "err" only if we get an error, so we
+       must first clear it. */
+    err = 0;
+    if (cf->is_tempfile) {
+      /* The file being saved is a temporary file from a live
+         capture, so it doesn't need to stay around under that name;
+        first, try renaming the capture buffer file to the new name. */
+      if (rename(cf->filename, fname) == 0) {
+       /* That succeeded - there's no need to copy the source file. */
+       from_filename = NULL;
+       do_copy = FALSE;
+      } else {
+       if (errno == EXDEV) {
+         /* They're on different file systems, so we have to copy the
+            file. */
+         do_copy = TRUE;
+          from_filename = cf->filename;
+       } else {
+         /* The rename failed, but not because they're on different
+            file systems - put up an error message.  (Or should we
+            just punt and try to copy?  The only reason why I'd
+            expect the rename to fail and the copy to succeed would
+            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?) */
+         err = errno;
+         simple_dialog(ESD_TYPE_WARN, NULL,
+                               file_rename_error_message(err), fname);
+         goto done;
        }
-#endif
-
-       unlink(from);
-       return 1;
-}
-
-/* Copies a file.
- * Returns 0 on failure to do either, 1 on success of either
- */
-int
-file_cp(char *from, char *to)
-{
-
-#define COPY_BUFFER_SIZE       8192
+      }
+    } else {
+      /* It's a permanent file, so we should copy it, and not remove the
+         original. */
+      do_copy = TRUE;
+      from_filename = cf->filename;
+    }
 
-       int from_fd, to_fd, nread, nwritten;
-       char *buffer;
+    /* Copy the file, if we haven't moved it. */
+    if (do_copy) {
+      /* Copy the raw bytes of the file. */
+      from_fd = open(from_filename, O_RDONLY | O_BINARY);
+      if (from_fd < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_WARN, NULL,
+                       file_open_error_message(err, TRUE), from_filename);
+       goto done;
+      }
 
-       buffer = g_malloc(COPY_BUFFER_SIZE);
+      to_fd = creat(fname, 0644);
+      if (to_fd < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_WARN, NULL,
+                       file_open_error_message(err, TRUE), fname);
+       close(from_fd);
+       goto done;
+      }
 
-       from_fd = open(from, O_RDONLY);
-       if (from_fd < 0) {
-               simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_open_error_message(errno, TRUE), from);
-               return 0;
+      while ((nread = read(from_fd, pd, sizeof pd)) > 0) {
+       nwritten = write(to_fd, pd, nread);
+       if (nwritten < nread) {
+         if (nwritten < 0)
+           err = errno;
+         else
+           err = WTAP_ERR_SHORT_WRITE;
+         simple_dialog(ESD_TYPE_WARN, NULL,
+                               file_write_error_message(err), fname);
+         close(from_fd);
+         close(to_fd);
+         goto done;
        }
+      }
+      if (nread < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_WARN, NULL,
+                       file_read_error_message(err), from_filename);
+       close(from_fd);
+       close(to_fd);
+       goto done;
+      }
+      close(from_fd);
+      if (close(to_fd) < 0) {
+       err = errno;
+       simple_dialog(ESD_TYPE_WARN, NULL,
+               file_close_error_message(err), fname);
+       goto done;
+      }
+    }
+  } else {
+    /* Either we're filtering packets, or we're saving in a different
+       format; we can't do that by copying or moving the capture file,
+       we have to do it by writing the packets out in Wiretap. */
+    pdh = wtap_dump_open(fname, save_format, cf->lnk_t, cf->snap, &err);
+    if (pdh == NULL) {
+      simple_dialog(ESD_TYPE_WARN, NULL,
+                       file_open_error_message(err, TRUE), fname);
+      goto done;
+    }
 
-       to_fd = creat(to, 0644);
-       if (to_fd < 0) {
-               simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_open_error_message(errno, TRUE), to);
-               close(from_fd);
-               return 0;
+    /* XXX - have a way to save only the packets currently selected by
+       the display filter.
+
+       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 (fd = cf->plist; fd != NULL; fd = fd->next) {
+      /* XXX - do a progress bar */
+      if (!save_filtered || fd->passed_dfilter) {
+       /* Either we're saving all frames, or we're saving filtered frames
+          and this one passed the display filter - save it. */
+        hdr.ts.tv_sec = fd->abs_secs;
+        hdr.ts.tv_usec = fd->abs_usecs;
+        hdr.caplen = fd->cap_len;
+        hdr.len = fd->pkt_len;
+        hdr.pkt_encap = fd->lnk_t;
+        hdr.pseudo_header = fd->pseudo_header;
+       wtap_seek_read(cf->cd_t, cf->fh, fd->file_off, pd, fd->cap_len);
+
+        if (!wtap_dump(pdh, &hdr, pd, &err)) {
+           simple_dialog(ESD_TYPE_WARN, NULL,
+                               file_write_error_message(err), fname);
+           wtap_dump_close(pdh, &err);
+           goto done;
        }
+      }
+    }
 
-       while( (nread = read(from_fd, buffer, COPY_BUFFER_SIZE)) > 0) {
-               nwritten = write(to_fd, buffer, nread);
-               if (nwritten < nread) {
-                       if (nwritten < 0) {
-                               simple_dialog(ESD_TYPE_WARN, NULL,
-                                       file_write_error_message(errno), to);
-                       } else {
-                               simple_dialog(ESD_TYPE_WARN, NULL,
-"The file \"%s\" could not be saved: tried writing %d, wrote %d.\n",
-                                       to, nread, nwritten);
-                       }
-                       close(from_fd);
-                       close(to_fd);
-                       return 0;
-               }
-       }
-       if (nread < 0) {
-               simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_read_error_message(errno), from);
-               close(from_fd);
-               close(to_fd);
-               return 0;
-       }
-       close(from_fd);
-       close(to_fd);
+    if (!wtap_dump_close(pdh, &err)) {
+      simple_dialog(ESD_TYPE_WARN, NULL,
+               file_close_error_message(err), fname);
+      goto done;
+    }
+  }
 
-       return 1;
+done:
+
+  /* Pop the "Saving:" message off the status bar. */
+  gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+  if (err == 0) {
+    if (!save_filtered) {
+      /* We saved the entire capture, not just some packets from it.
+         Open and read the file we saved it to.
+
+        XXX - this is somewhat of a waste; we already have the
+        packets, all this gets us is updated file type information
+        (which we could just stuff into "cf"), and having the new
+        file be the one we have opened and from which we're reading
+        the data, and it means we have to spend time opening and
+        reading the file, which could be a significant amount of
+        time if the file is large. */
+      cf->user_saved = TRUE;
+
+      if ((err = open_cap_file(fname, FALSE, cf)) == 0) {
+       /* XXX - report errors if this fails? */
+       err = read_cap_file(cf);
+       set_menus_for_unsaved_capture_file(FALSE);
+      }
+    }
+  }
+  return err;
 }
 
 char *
@@ -1078,9 +1511,21 @@ file_open_error_message(int err, int for_writing)
 
   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_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_ENCAP:
+  case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
+    /* Seen only when opening a capture file for writing. */
+    errmsg = "Ethereal cannot save this capture in that format.";
+    break;
+
   case WTAP_ERR_BAD_RECORD:
     errmsg = "The file \"%s\" appears to be damaged or corrupt.";
     break;
@@ -1097,6 +1542,10 @@ file_open_error_message(int err, int for_writing)
              " in the middle of a packet.";
     break;
 
+  case WTAP_ERR_SHORT_WRITE:
+    errmsg = "A full header couldn't be written to the file \"%s\".";
+    break;
+
   case ENOENT:
     if (for_writing)
       errmsg = "The path to the file \"%s\" does not exist.";
@@ -1120,6 +1569,31 @@ file_open_error_message(int err, int for_writing)
   return errmsg;
 }
 
+static char *
+file_rename_error_message(int err)
+{
+  char *errmsg;
+  static char errmsg_errno[1024+1];
+
+  switch (err) {
+
+  case ENOENT:
+    errmsg = "The path to the file \"%s\" does not exist.";
+    break;
+
+  case EACCES:
+    errmsg = "You do not have permission to move the capture file to \"%s\".";
+    break;
+
+  default:
+    sprintf(errmsg_errno, "The file \"%%s\" could not be moved: %s.",
+                               wtap_strerror(err));
+    errmsg = errmsg_errno;
+    break;
+  }
+  return errmsg;
+}
+
 char *
 file_read_error_message(int err)
 {
@@ -1156,3 +1630,42 @@ file_write_error_message(int err)
   }
   return errmsg;
 }
+
+/* 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)
+{
+  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 data 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;
+
+#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
+
+  default:
+    sprintf(errmsg_errno, "An error occurred while closing the file \"%%s\": %s.",
+                               wtap_strerror(err));
+    errmsg = errmsg_errno;
+    break;
+  }
+  return errmsg;
+}