The common merge code merely needs to offer the abstraction of routines
[obnox/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index 2877876f9275661d7a66fdb377ea2ee1a3f995ce..e624f6d09668e3fb1ca41a559e56a8df7a219186 100644 (file)
--- a/file.c
+++ b/file.c
@@ -1,7 +1,7 @@
 /* file.c
  * File I/O routines
  *
- * $Id: file.c,v 1.366 2004/02/22 22:33:59 guy Exp $
+ * $Id$
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+/* With MSVC and a libethereal.dll this file needs to import some variables 
+   in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */
+#define _NEED_VAR_IMPORT_
+
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 #include <epan/filesystem.h>
 
 #include "color.h"
-#include "column.h"
+#include "color_filters.h"
+#include <epan/column.h>
 #include <epan/packet.h>
-#include "range.h"
+#include "packet-range.h"
 #include "print.h"
 #include "file.h"
 #include "menu.h"
 #include "util.h"
+#include "merge.h"
 #include "alert_box.h"
 #include "simple_dialog.h"
 #include "progress_dlg.h"
 #include "ui_util.h"
 #include "statusbar.h"
-#include "prefs.h"
+#include <epan/prefs.h>
 #include <epan/dfilter/dfilter.h>
 #include <epan/conversation.h>
 #include "globals.h"
 #include <epan/epan_dissect.h>
-#include "tap.h"
+#include <epan/tap.h>
 #include "tap_dfilter_dlg.h"
-#include "packet-data.h"
+#include <epan/dissectors/packet-data.h>
+
+/* Win32 needs the O_BINARY flag for open() */
+#ifndef O_BINARY
+#define O_BINARY       0
+#endif
 
 #ifdef HAVE_LIBPCAP
 gboolean auto_scroll_live;
@@ -131,15 +142,6 @@ 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
 cf_open(char *fname, gboolean is_tempfile, capture_file *cf)
@@ -288,6 +290,8 @@ cf_close(capture_file *cf)
   set_menus_for_capture_in_progress(FALSE);
   set_menus_for_selected_tree_row(cf);
 
+  reset_tap_listeners();
+
   /* We have no file open. */
   cf->state = FILE_CLOSED;
 }
@@ -381,10 +385,6 @@ cf_read(capture_file *cf)
      bump that value by this amount. */
   progbar_quantum = cf->f_len/N_PROGBAR_UPDATES;
 
-#ifndef O_BINARY
-#define O_BINARY       0
-#endif
-
   packet_list_freeze();
 
   stop_flag = FALSE;
@@ -490,11 +490,15 @@ cf_read(capture_file *cf)
     switch (err) {
 
     case WTAP_ERR_UNSUPPORTED_ENCAP:
-      errmsg = "The capture file is for a network type that Ethereal doesn't support.";
+      snprintf(errmsg_errno, sizeof(errmsg_errno),
+               "The capture file has a packet with a network type that Ethereal doesn't support.\n(%s)",
+               err_info);
+      g_free(err_info);
+      errmsg = errmsg_errno;
       break;
 
     case WTAP_ERR_CANT_READ:
-      errmsg = "An attempt to read from the file failed for"
+      errmsg = "An attempt to read from the capture file failed for"
                " some unknown reason.";
       break;
 
@@ -507,6 +511,7 @@ cf_read(capture_file *cf)
       snprintf(errmsg_errno, sizeof(errmsg_errno),
                "The capture file appears to be damaged or corrupt.\n(%s)",
                err_info);
+      g_free(err_info);
       errmsg = errmsg_errno;
       break;
 
@@ -529,6 +534,7 @@ int
 cf_start_tail(char *fname, gboolean is_tempfile, capture_file *cf)
 {
   int     err;
+  gchar *capture_msg;
 
   err = cf_open(fname, is_tempfile, cf);
   if (err == 0) {
@@ -540,7 +546,11 @@ cf_start_tail(char *fname, gboolean is_tempfile, capture_file *cf)
        packets (yes, I know, we don't have any *yet*). */
     set_menus_for_captured_packets(TRUE);
 
-    statusbar_push_file_msg(" <live capture in progress>");
+    capture_msg = g_strdup_printf(" %s: <live capture in progress>", cf->iface);
+
+    statusbar_push_file_msg(capture_msg);
+
+    g_free(capture_msg);
   }
   return err;
 }
@@ -960,17 +970,199 @@ read_packet(capture_file *cf, long offset)
   }
 }
 
-int
-filter_packets(capture_file *cf, gchar *dftext)
+gboolean
+cf_merge_files(const char *out_filename, int out_fd, int in_file_count,
+               char *const *in_filenames, int file_type, gboolean do_append)
+{
+  merge_in_file_t  *in_files;
+  wtap             *wth;
+  wtap_dumper      *pdh;
+  int               open_err, read_err, write_err, close_err;
+  gchar            *err_info;
+  int               err_fileno; 
+  int               i;
+  char              errmsg_errno[1024+1];
+  gchar             err_str[2048+1];
+  char             *errmsg;
+  gboolean          got_read_error = FALSE, got_write_error = FALSE;
+  long              data_offset;
+  progdlg_t        *progbar = NULL;
+  gboolean          stop_flag;
+  /*
+   * 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        f_len, file_pos;
+  float       prog_val;
+  GTimeVal    start_time;
+  gchar       status_str[100];
+  int         progbar_nextstep;
+  int         progbar_quantum;
+
+  /* open the input files */
+  if (!merge_open_in_files(in_file_count, in_filenames, &in_files,
+                           &open_err, &err_info, &err_fileno)) {
+    free(in_files);
+    cf_open_failure_alert_box(in_filenames[err_fileno], open_err, err_info,
+                              FALSE, 0);
+    return FALSE;
+  }
+
+  pdh = wtap_dump_fdopen(out_fd, file_type,
+      merge_select_frame_type(in_file_count, in_files),
+      merge_max_snapshot_length(in_file_count, in_files), &open_err);
+  if (pdh == NULL) {
+    merge_close_in_files(in_file_count, in_files);
+    free(in_files);
+    cf_open_failure_alert_box(out_filename, open_err, err_info, TRUE,
+                              file_type);
+    return FALSE;
+  }
+
+  /* Get the sum of the sizes of all the files. */
+  f_len = 0;
+  for (i = 0; i < in_file_count; i++)
+    f_len += in_files[i].size;
+
+  /* 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 = f_len/N_PROGBAR_UPDATES;
+
+  stop_flag = FALSE;
+  g_get_current_time(&start_time);
+
+  /* do the merge (or append) */
+  for (;;) {
+    if (do_append)
+      wth = merge_append_read_packet(in_file_count, in_files, &read_err,
+                                     &err_info);
+    else
+      wth = merge_read_packet(in_file_count, in_files, &read_err,
+                              &err_info);
+    if (wth == NULL) {
+      if (read_err != 0)
+        got_read_error = TRUE;
+      break;
+    }
+
+    /* Get the sum of the data offsets in all of the files. */
+    data_offset = 0;
+    for (i = 0; i < in_file_count; i++)
+      data_offset += in_files[i].data_offset;
+
+    if (data_offset >= progbar_nextstep) {
+        /* Get the sum of the seek positions in all of the files. */
+        file_pos = 0;
+        for (i = 0; i < in_file_count; i++)
+          file_pos += lseek(wtap_fd(in_files[i].wth), 0, SEEK_CUR);
+        prog_val = (gfloat) file_pos / (gfloat) f_len;
+        if (prog_val > 1.0) {
+          /* Some file probably grew while we were reading it.
+             That "shouldn't happen", so we'll just clip the progress
+             value at 1.0. */
+          prog_val = 1.0;
+        }
+        if (progbar == NULL) {
+          /* Create the progress bar if necessary */
+          progbar = delayed_create_progress_dlg("Merging", "files",
+            &stop_flag, &start_time, prog_val);
+        }
+        if (progbar != NULL) {
+          g_snprintf(status_str, sizeof(status_str),
+                     "%luKB of %luKB", file_pos / 1024, f_len / 1024);
+          update_progress_dlg(progbar, prog_val, status_str);
+        }
+        progbar_nextstep += progbar_quantum;
+    }
+
+    if (!wtap_dump(pdh, wtap_phdr(wth), wtap_pseudoheader(wth),
+         wtap_buf_ptr(wth), &write_err)) {
+      got_write_error = TRUE;
+      break;
+    }
+  }
+
+  /* We're done merging the files; destroy the progress bar if it was created. */
+  if (progbar != NULL)
+    destroy_progress_dlg(progbar);
+
+  merge_close_in_files(in_file_count, in_files);
+  if (!got_read_error && !got_write_error) {
+    if (!wtap_dump_close(pdh, &write_err))
+      got_write_error = TRUE;
+  } else
+    wtap_dump_close(pdh, &close_err);
+
+  if (got_read_error) {
+    /*
+     * Find the file on which we got the error, and report the error.
+     */
+    for (i = 0; i < in_file_count; i++) {
+      if (in_files[i].state == GOT_ERROR) {
+       /* Put up a message box noting that a read failed somewhere along
+          the line. */
+       switch (read_err) {
+
+       case WTAP_ERR_UNSUPPORTED_ENCAP:
+         snprintf(errmsg_errno, sizeof(errmsg_errno),
+                  "The capture file %%s has a packet with a network type that Ethereal doesn't support.\n(%s)",
+                  err_info);
+         g_free(err_info);
+         errmsg = errmsg_errno;
+         break;
+
+       case WTAP_ERR_CANT_READ:
+         errmsg = "An attempt to read from the capture file %s failed for"
+                  " some unknown reason.";
+         break;
+
+       case WTAP_ERR_SHORT_READ:
+         errmsg = "The capture file %s appears to have been cut short"
+                  " in the middle of a packet.";
+         break;
+
+       case WTAP_ERR_BAD_RECORD:
+         snprintf(errmsg_errno, sizeof(errmsg_errno),
+                  "The capture file %%s appears to be damaged or corrupt.\n(%s)",
+                  err_info);
+         g_free(err_info);
+         errmsg = errmsg_errno;
+         break;
+
+       default:
+         snprintf(errmsg_errno, sizeof(errmsg_errno),
+                  "An error occurred while reading the"
+                  " capture file %%s: %s.", wtap_strerror(read_err));
+         errmsg = errmsg_errno;
+         break;
+       }
+       snprintf(err_str, sizeof err_str, errmsg, in_files[i].filename);
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, err_str);
+      }
+    }
+  }
+
+  if (got_write_error) {
+    /* Put up an alert box for the write error. */
+    cf_write_failure_alert_box(out_filename, write_err);
+  }
+
+  return (!got_read_error && !got_write_error);
+}
+
+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 */
-  if (strcmp(filter_new, filter_old) == 0) {
-    return 1;
+  /* 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) {
@@ -984,15 +1176,20 @@ filter_packets(capture_file *cf, gchar *dftext)
     dftext = g_strdup(dftext);
     if (!dfilter_compile(dftext, &dfcode)) {
       /* The attempt failed; report an error. */
+      gchar *safe_dftext = simple_dialog_format_message(dftext);
+      gchar *safe_dfilter_error_msg = simple_dialog_format_message(
+         dfilter_error_msg);
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, 
           "%s%s%s\n"
           "\n"
-          "The display filter \"%s\" is not a valid display filter.\n"
+          "The following display filter is not a valid display filter:\n%s\n"
           "See the help for a description of the display filter syntax.",
-          simple_dialog_primary_start(), dfilter_error_msg,
-          simple_dialog_primary_end(), dftext);
+          simple_dialog_primary_start(), safe_dfilter_error_msg,
+          simple_dialog_primary_end(), safe_dftext);
+      g_free(safe_dfilter_error_msg);
+      g_free(safe_dftext);
       g_free(dftext);
-      return 0;
+      return FALSE;
     }
 
     /* Was it empty? */
@@ -1018,7 +1215,7 @@ filter_packets(capture_file *cf, gchar *dftext)
   } else {
     rescan_packets(cf, "Filtering", dftext, TRUE, FALSE);
   }
-  return 1;
+  return TRUE;
 }
 
 void
@@ -1415,6 +1612,7 @@ process_specified_packets(capture_file *cf, packet_range_t *range,
       ret = PSP_FAILED;
       break;
     }
+    /* Process the packet */
     if (!callback(cf, fdata, &pseudo_header, pd, callback_args)) {
       /* Callback failed.  We assume it reported the error appropriately. */
       ret = PSP_FAILED;
@@ -1483,7 +1681,10 @@ retap_packets(capture_file *cf)
 
 typedef struct {
   print_args_t *print_args;
-  FILE         *print_fh;
+  gboolean      print_header_line;
+  char         *header_line_buf;
+  int           header_line_buf_len;
+  gboolean      print_formfeed;
   gboolean      print_separator;
   char         *line_buf;
   int           line_buf_len;
@@ -1502,13 +1703,47 @@ print_packet(capture_file *cf, frame_data *fdata,
   int             line_len;
   int             column_len;
   int             cp_off;
-
+  gboolean        proto_tree_needed;
+  char            bookmark_name[9+10+1];       /* "__frameNNNNNNNNNN__\0" */
+  char            bookmark_title[6+10+1];      /* "Frame NNNNNNNNNN__\0" */
+
+  /* Create the protocol tree, and make it visible, if we're printing
+     the dissection or the hex data.
+     XXX - do we need it if we're just printing the hex data? */
+  proto_tree_needed = 
+      args->print_args->print_dissections != print_dissections_none || args->print_args->print_hex;
+  edt = epan_dissect_new(proto_tree_needed, proto_tree_needed);
+
+  /* Fill in the column information if we're printing the summary
+     information. */
   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);
+  } else
+    epan_dissect_run(edt, pseudo_header, pd, fdata, NULL);
+
+  if (args->print_formfeed) {
+    if (!new_page(args->print_args->stream))
+      goto fail;
+  } else {
+      if (args->print_separator) {
+        if (!print_line(args->print_args->stream, 0, ""))
+          goto fail;
+      }
+  }
+
+  /*
+   * We generate bookmarks, if the output format supports them.
+   * The name is "__frameN__".
+   */
+  sprintf(bookmark_name, "__frame%u__", fdata->num);
+
+  if (args->print_args->print_summary) {
+    if (args->print_header_line) {
+      if (!print_line(args->print_args->stream, 0, args->header_line_buf))
+        goto fail;
+      args->print_header_line = FALSE; /* we might not need to print any more */
+    }
     cp = &args->line_buf[0];
     line_len = 0;
     for (i = 0; i < cf->cinfo.num_cols; i++) {
@@ -1537,31 +1772,69 @@ print_packet(capture_file *cf, frame_data *fdata,
         *cp++ = ' ';
     }
     *cp = '\0';
-    print_line(args->print_fh, 0, args->print_args->format, args->line_buf);
+
+    /*
+     * Generate a bookmark, using the summary line as the title.
+     */
+    if (!print_bookmark(args->print_args->stream, bookmark_name,
+                        args->line_buf))
+      goto fail;
+
+    if (!print_line(args->print_args->stream, 0, args->line_buf))
+      goto fail;
   } else {
-    if (args->print_separator)
-      print_line(args->print_fh, 0, args->print_args->format, "");
+    /*
+     * Generate a bookmark, using "Frame N" as the title, as we're not
+     * printing the summary line.
+     */
+    sprintf(bookmark_title, "Frame %u", fdata->num);
+    if (!print_bookmark(args->print_args->stream, bookmark_name,
+                        bookmark_title))
+      goto fail;
+  } /* if (print_summary) */
 
-    /* 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);
+  if (args->print_args->print_dissections != print_dissections_none) {
+    if (args->print_args->print_summary) {
+      /* Separate the summary line from the tree with a blank line. */
+      if (!print_line(args->print_args->stream, 0, ""))
+        goto fail;
+    }
 
     /* Print the information in that tree. */
-    proto_tree_print(args->print_args, edt, args->print_fh);
+    if (!proto_tree_print(args->print_args, edt, args->print_args->stream))
+      goto fail;
 
-    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 (aka more than one packet). */
+    args->print_separator = TRUE;
+
+    /* Print a header line if we print any more packet summaries */
+    args->print_header_line = TRUE;
+  }
 
-    /* Print a blank line if we print anything after this. */
+  if (args->print_args->print_hex) {
+    /* Print the full packet data as hex. */
+    if (!print_hex_data(args->print_args->stream, edt))
+      goto fail;
+
+    /* Print a blank line if we print anything after this (aka more than one packet). */
     args->print_separator = TRUE;
-  } /* if (print_summary) */
+
+    /* Print a header line if we print any more packet summaries */
+    args->print_header_line = TRUE;
+  } /* if (args->print_args->print_dissections != print_dissections_none) */
+
   epan_dissect_free(edt);
 
-  return !ferror(args->print_fh);
+  /* do we want to have a formfeed between each packet from now on? */
+  if(args->print_args->print_formfeed) {
+    args->print_formfeed = TRUE;
+  }
+
+  return TRUE;
+
+fail:
+  epan_dissect_free(edt);
+  return FALSE;
 }
 
 pp_return_t
@@ -1576,32 +1849,31 @@ print_packets(capture_file *cf, print_args_t *print_args)
   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_header_line = TRUE;
+  callback_args.header_line_buf = NULL;
+  callback_args.header_line_buf_len = 256;
+  callback_args.print_formfeed = FALSE;
   callback_args.print_separator = FALSE;
   callback_args.line_buf = NULL;
   callback_args.line_buf_len = 256;
   callback_args.col_widths = NULL;
+
+  if (!print_preamble(print_args->stream, cf->filename)) {
+    destroy_print_stream(print_args->stream);
+    return PP_WRITE_ERROR;
+  }
+
   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);
+    /* We're printing packet summaries.  Allocate the header line buffer
+       and get the column widths. */
+    callback_args.header_line_buf = g_malloc(callback_args.header_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. */
+       width of the title and the width of the data - and construct
+       a buffer with a line containing the column titles. */
     callback_args.col_widths = (gint *) g_malloc(sizeof(gint) * cf->cinfo.num_cols);
-    cp = &callback_args.line_buf[0];
+    cp = &callback_args.header_line_buf[0];
     line_len = 0;
     for (i = 0; i < cf->cinfo.num_cols; i++) {
       /* Don't pad the last column. */
@@ -1622,26 +1894,29 @@ print_packets(capture_file *cf, print_args_t *print_args)
       /* 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;
+      if (line_len > callback_args.header_line_buf_len) {
+        cp_off = cp - callback_args.header_line_buf;
+        callback_args.header_line_buf_len = 2 * line_len;
+        callback_args.header_line_buf = g_realloc(callback_args.header_line_buf,
+                                                  callback_args.header_line_buf_len + 1);
+        cp = callback_args.header_line_buf + cp_off;
       }
 
       /* Right-justify the packet number column. */
-      if (cf->cinfo.col_fmt[i] == COL_NUMBER)
+/*      if (cf->cinfo.col_fmt[i] == COL_NUMBER)
         sprintf(cp, "%*s", callback_args.col_widths[i], cf->cinfo.col_title[i]);
-      else
+      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);
+
+    /* Now start out the main line buffer with the same length as the
+       header line buffer. */
+    callback_args.line_buf_len = callback_args.header_line_buf_len;
+    callback_args.line_buf = g_malloc(callback_args.line_buf_len + 1);
   } /* if (print_summary) */
 
   /* Iterate through the list of packets, printing the packets we were
@@ -1650,10 +1925,12 @@ print_packets(capture_file *cf, print_args_t *print_args)
                                   "selected packets", print_packet,
                                   &callback_args);
 
-  if (callback_args.col_widths != NULL)
-    g_free(callback_args.col_widths);
+  if (callback_args.header_line_buf != NULL)
+    g_free(callback_args.header_line_buf);
   if (callback_args.line_buf != NULL)
     g_free(callback_args.line_buf);
+  if (callback_args.col_widths != NULL)
+    g_free(callback_args.col_widths);
 
   switch (ret) {
 
@@ -1677,17 +1954,157 @@ print_packets(capture_file *cf, print_args_t *print_args)
        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);
+    destroy_print_stream(print_args->stream);
     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);
+  if (!print_finale(print_args->stream)) {
+    destroy_print_stream(print_args->stream);
     return PP_WRITE_ERROR;
   }
 
-  close_print_dest(print_args->to_file, callback_args.print_fh);
+  if (!destroy_print_stream(print_args->stream))
+    return PP_WRITE_ERROR;
+
+  return PP_OK;
+}
+
+static gboolean
+write_pdml_packet(capture_file *cf _U_, frame_data *fdata,
+                  union wtap_pseudo_header *pseudo_header, const guint8 *pd,
+                 void *argsp)
+{
+  FILE *fh = argsp;
+  epan_dissect_t *edt;
+
+  /* Create the protocol tree, but don't fill in the column information. */
+  edt = epan_dissect_new(TRUE, TRUE);
+  epan_dissect_run(edt, pseudo_header, pd, fdata, NULL);
+
+  /* Write out the information in that tree. */
+  proto_tree_write_pdml(edt, fh);
+
+  epan_dissect_free(edt);
+
+  return !ferror(fh);
+}
+
+pp_return_t
+write_pdml_packets(capture_file *cf, print_args_t *print_args)
+{
+  FILE        *fh;
+  psp_return_t ret;
+
+  fh = fopen(print_args->file, "w");
+  if (fh == NULL)
+    return PP_OPEN_ERROR;      /* attempt to open destination failed */
+
+  write_pdml_preamble(fh);
+  if (ferror(fh)) {
+    fclose(fh);
+    return PP_WRITE_ERROR;
+  }
+
+  /* Iterate through the list of packets, printing the packets we were
+     told to print. */
+  ret = process_specified_packets(cf, &print_args->range, "Writing PDML",
+                                  "selected packets", write_pdml_packet,
+                                  fh);
+
+  switch (ret) {
+
+  case PSP_FINISHED:
+    /* Completed successfully. */
+    break;
+
+  case PSP_STOPPED:
+    /* Well, the user decided to abort the printing. */
+    break;
+
+  case PSP_FAILED:
+    /* Error while printing. */
+    fclose(fh);
+    return PP_WRITE_ERROR;
+  }
+
+  write_pdml_finale(fh);
+  if (ferror(fh)) {
+    fclose(fh);
+    return PP_WRITE_ERROR;
+  }
+
+  /* XXX - check for an error */
+  fclose(fh);
+
+  return PP_OK;
+}
+
+static gboolean
+write_psml_packet(capture_file *cf, frame_data *fdata,
+                  union wtap_pseudo_header *pseudo_header, const guint8 *pd,
+                 void *argsp)
+{
+  FILE *fh = argsp;
+  epan_dissect_t *edt;
+
+  /* Fill in the column information, but don't create the protocol tree. */
+  edt = epan_dissect_new(FALSE, FALSE);
+  epan_dissect_run(edt, pseudo_header, pd, fdata, &cf->cinfo);
+
+  /* Write out the information in that tree. */
+  proto_tree_write_psml(edt, fh);
+
+  epan_dissect_free(edt);
+
+  return !ferror(fh);
+}
+
+pp_return_t
+write_psml_packets(capture_file *cf, print_args_t *print_args)
+{
+  FILE        *fh;
+  psp_return_t ret;
+
+  fh = fopen(print_args->file, "w");
+  if (fh == NULL)
+    return PP_OPEN_ERROR;      /* attempt to open destination failed */
+
+  write_psml_preamble(fh);
+  if (ferror(fh)) {
+    fclose(fh);
+    return PP_WRITE_ERROR;
+  }
+
+  /* Iterate through the list of packets, printing the packets we were
+     told to print. */
+  ret = process_specified_packets(cf, &print_args->range, "Writing PSML",
+                                  "selected packets", write_psml_packet,
+                                  fh);
+
+  switch (ret) {
+
+  case PSP_FINISHED:
+    /* Completed successfully. */
+    break;
+
+  case PSP_STOPPED:
+    /* Well, the user decided to abort the printing. */
+    break;
+
+  case PSP_FAILED:
+    /* Error while printing. */
+    fclose(fh);
+    return PP_WRITE_ERROR;
+  }
+
+  write_psml_finale(fh);
+  if (ferror(fh)) {
+    fclose(fh);
+    return PP_WRITE_ERROR;
+  }
+
+  /* XXX - check for an error */
+  fclose(fh);
 
   return PP_OK;
 }
@@ -1911,7 +2328,7 @@ match_subtree_text(proto_node *node, gpointer data)
   }
 
   /* Don't match invisible entries. */
-  if (!fi->visible)
+  if (PROTO_ITEM_IS_HIDDEN(node))
     return;
 
   /* was a free format label produced? */
@@ -2015,7 +2432,7 @@ find_packet_data(capture_file *cf, const guint8 *string, size_t string_size)
   info.data_len = string_size;
 
   /* String or hex search? */
-  if (cf->ascii) {
+  if (cf->string) {
     /* String search - what type of string? */
     switch (cf->scs_type) {
 
@@ -2260,19 +2677,44 @@ find_packet(capture_file *cf,
            * we need an API for popping up alert boxes with
            * {Verb} and "Cancel".
            */
-          simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTN_OK,
-                        "You have searched up to the beginning of the capture.\n"
-                        "Do you want to continue the search from the end of the capture?");
-          fdata = cf->plist_end;       /* wrap around */
+
+          if (prefs.gui_find_wrap)
+          {
+              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
+          {
+              simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+                            "%sBeginning of capture exceeded!%s\n\n"
+                            "Try searching forwards.",
+                            simple_dialog_primary_start(), simple_dialog_primary_end());
+              fdata = start_fd;        /* stay on previous packet */
+          }
         }
       } else {
         /* Go on to the next frame. */
         fdata = fdata->next;
         if (fdata == NULL) {
-          simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTN_OK,
-                        "You have searched up to the end of the capture.\n"
-                        "Do you want to continue the search from the beginning of the capture?");
-          fdata = cf->plist;   /* wrap around */
+          if (prefs.gui_find_wrap)
+          {
+              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 */
+          }
+          else
+          {
+              simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+                            "%sEnd of capture exceeded!%s\n\n"
+                            "Try searching backwards.",
+                            simple_dialog_primary_start(), simple_dialog_primary_end());
+              fdata = start_fd;     /* stay on previous packet */
+          }
         }
       }
 
@@ -2411,6 +2853,26 @@ goto_bottom_frame(capture_file *cf)
   return TRUE; /* we got to that packet */
 }
 
+/*
+ * Go to frame specified by currently selected protocol tree item.
+ */
+void
+goto_framenum(capture_file *cf)
+{
+  header_field_info       *hfinfo;
+  guint32                 framenum;
+
+  if (cf->finfo_selected) {
+    hfinfo = cf->finfo_selected->hfinfo;
+    g_assert(hfinfo);
+    if (hfinfo->type == FT_FRAMENUM) {
+      framenum = fvalue_get_integer(&cf->finfo_selected->value);
+      if (framenum != 0)
+        goto_frame(cf, framenum);
+      }
+  }
+}
+
 /* Select the packet on a given row. */
 void
 select_packet(capture_file *cf, int row)
@@ -2520,8 +2982,11 @@ unselect_field(capture_file *cf)
 void
 mark_frame(capture_file *cf, frame_data *frame)
 {
-  frame->flags.marked = TRUE;
-  cf->marked_count++;
+  if (! frame->flags.marked) {
+    frame->flags.marked = TRUE;
+    if (cf->count > cf->marked_count)
+      cf->marked_count++;
+  }
 }
 
 /*
@@ -2530,8 +2995,11 @@ mark_frame(capture_file *cf, frame_data *frame)
 void
 unmark_frame(capture_file *cf, frame_data *frame)
 {
-  frame->flags.marked = FALSE;
-  cf->marked_count--;
+  if (frame->flags.marked) {
+    frame->flags.marked = FALSE;
+    if (cf->marked_count > 0)
+      cf->marked_count--;
+  }
 }
 
 typedef struct {
@@ -2628,7 +3096,7 @@ cf_save(char *fname, capture_file *cf, packet_range_t *range, guint save_format)
       /* 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. */
-#ifndef WIN32
+#ifndef _WIN32
       if (rename(cf->filename, fname) == 0) {
        /* That succeeded - there's no need to copy the source file. */
        from_filename = NULL;
@@ -2920,8 +3388,12 @@ cf_read_error_message(int err, gchar *err_info)
 
   switch (err) {
 
-  case WTAP_ERR_UNSUPPORTED:
   case WTAP_ERR_UNSUPPORTED_ENCAP:
+      snprintf(errmsg_errno, sizeof(errmsg_errno),
+               "The file \"%%s\" has a packet with a network type that Ethereal doesn't support.\n(%s)",
+               err_info);
+      break;
+
   case WTAP_ERR_BAD_RECORD:
     snprintf(errmsg_errno, sizeof(errmsg_errno),
             "An error occurred while reading from the file \"%%s\": %s.\n(%s)",
@@ -2987,6 +3459,56 @@ cf_close_failure_alert_box(const char *filename, int err)
   }
 }
 
+/* Reload the current capture file. */
+void
+cf_reload() {
+  gchar *filename;
+  gboolean is_tempfile;
+
+  /* If the file could be opened, "cf_open()" calls "cf_close()"
+     to get rid of state for the old capture file before filling in state
+     for the new capture file.  "cf_close()" will remove the file if
+     it's a temporary file; we don't want that to happen (for one thing,
+     it'd prevent subsequent reopens from working).  Remember whether it's
+     a temporary file, mark it as not being a temporary file, and then
+     reopen it as the type of file it was.
+
+     Also, "cf_close()" will free "cfile.filename", so we must make
+     a copy of it first. */
+  filename = g_strdup(cfile.filename);
+  is_tempfile = cfile.is_tempfile;
+  cfile.is_tempfile = FALSE;
+  if (cf_open(filename, is_tempfile, &cfile) == 0) {
+    switch (cf_read(&cfile)) {
+
+    case READ_SUCCESS:
+    case READ_ERROR:
+      /* Just because we got an error, that doesn't mean we were unable
+         to read any of the file; we handle what we could get from the
+         file. */
+      break;
+
+    case READ_ABORTED:
+      /* The user bailed out of re-reading the capture file; the
+         capture file has been closed - just free the capture file name
+         string and return (without changing the last containing
+         directory). */
+      g_free(filename);
+      return;
+    }
+  } else {
+    /* The open failed, so "cfile.is_tempfile" wasn't set to "is_tempfile".
+       Instead, the file was left open, so we should restore "cfile.is_tempfile"
+       ourselves.
+
+       XXX - change the menu?  Presumably "cf_open()" will do that;
+       make sure it does! */
+    cfile.is_tempfile = is_tempfile;
+  }
+  /* "cf_open()" made a copy of the file name we handed it, so
+     we should free up our copy. */
+  g_free(filename);
+}
 
 /* Copies a file in binary mode, for those operating systems that care about
  * such things.