print.c and ps.c are in libwirshark now.
[metze/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index 3e061fb08faa7bd4c7fe9f1c8785495869628c81..6a0d2ba86de1972529e4d48027532f65071afe6f 100644 (file)
--- a/file.c
+++ b/file.c
@@ -42,6 +42,7 @@
 #endif
 
 #include <epan/epan.h>
+#include <epan/expert.h>
 #include <epan/filesystem.h>
 
 #include "color.h"
 #include <epan/column.h>
 #include <epan/packet.h>
 #include <epan/column-utils.h>
-#include "packet-range.h"
-#include "print.h"
 #include "file.h"
 #include "fileset.h"
-#include "tempfile.h"
+#include "frame_tvbuff.h"
+#include "wsutil/tempfile.h"
 #include "merge.h"
 
 #include <epan/prefs.h>
 #include "ui/progress_dlg.h"
 #include "ui/ui_util.h"
 
+#include "version_info.h"
+
+/* Needed for addrinfo */
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+#endif
+
+#if defined(_WIN32) && defined(INET6)
+# include <ws2tcpip.h>
+#endif
+
 #ifdef HAVE_LIBPCAP
 gboolean auto_scroll_live;
 #endif
@@ -89,7 +116,7 @@ static gulong computed_elapsed;
 static void cf_reset_state(capture_file *cf);
 
 static int read_packet(capture_file *cf, dfilter_t *dfcode,
-    epan_dissect_t *edt, column_info *cinfo, gint64 offset);
+    gboolean create_proto_tree, column_info *cinfo, gint64 offset);
 
 static void rescan_packets(capture_file *cf, const char *action, const char *action_item, gboolean redissect);
 
@@ -103,11 +130,11 @@ static match_result match_protocol_tree(capture_file *cf, frame_data *fdata,
 static void match_subtree_text(proto_node *node, gpointer data);
 static match_result match_summary_line(capture_file *cf, frame_data *fdata,
     void *criterion);
-static match_result match_ascii_and_unicode(capture_file *cf, frame_data *fdata,
+static match_result match_narrow_and_wide(capture_file *cf, frame_data *fdata,
     void *criterion);
-static match_result match_ascii(capture_file *cf, frame_data *fdata,
+static match_result match_narrow(capture_file *cf, frame_data *fdata,
     void *criterion);
-static match_result match_unicode(capture_file *cf, frame_data *fdata,
+static match_result match_wide(capture_file *cf, frame_data *fdata,
     void *criterion);
 static match_result match_binary(capture_file *cf, frame_data *fdata,
     void *criterion);
@@ -155,7 +182,7 @@ cf_callback_invoke(int event, gpointer data)
   g_assert(cb_item != NULL);
 
   while (cb_item != NULL) {
-    cb = cb_item->data;
+    cb = (cf_callback_data_t *)cb_item->data;
     cb->cb_fct(event, data, cb->user_data);
     cb_item = g_list_next(cb_item);
   }
@@ -167,7 +194,7 @@ cf_callback_add(cf_callback_t func, gpointer user_data)
 {
   cf_callback_data_t *cb;
 
-  cb = g_malloc(sizeof(cf_callback_data_t));
+  cb = g_new(cf_callback_data_t,1);
   cb->cb_fct = func;
   cb->user_data = user_data;
 
@@ -181,7 +208,7 @@ cf_callback_remove(cf_callback_t func)
   GList              *cb_item = cf_callbacks;
 
   while (cb_item != NULL) {
-    cb = cb_item->data;
+    cb = (cf_callback_data_t *)cb_item->data;
     if (cb->cb_fct == func) {
       cf_callbacks = g_list_remove(cf_callbacks, cb);
       g_free(cb);
@@ -288,6 +315,10 @@ cf_open(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
      and fill in the information for this file. */
   cf_close(cf);
 
+  /* XXX - we really want to initialize this after we've read all
+     the packets, so we know how much we'll ultimately need. */
+  buffer_init(&cf->buf, 1500);
+
   /* Cleanup all data structures used for dissection. */
   cleanup_dissection();
   /* Initialize all data structures used for dissection. */
@@ -363,7 +394,7 @@ fail:
 /*
  * Add an encapsulation type to cf->linktypes.
  */
-void
+static void
 cf_add_encapsulation_type(capture_file *cf, int encap)
 {
   guint i;
@@ -407,6 +438,9 @@ cf_reset_state(capture_file *cf)
   /* ...which means we have no changes to that file to save. */
   cf->unsaved_changes = FALSE;
 
+  /* Free up the packet buffer. */
+  buffer_free(&cf->buf);
+
   dfilter_free(cf->rfcode);
   cf->rfcode = NULL;
   if (cf->frames != NULL) {
@@ -495,38 +529,18 @@ calc_progbar_val(capture_file *cf, gint64 size, gint64 file_pos, gchar *status_s
   return progbar_val;
 }
 
-static void
-epan_dissect_finish(epan_dissect_t *edt)
-{
-       if (edt->tree)
-               proto_tree_free(edt->tree);
-}
-
 cf_read_status_t
 cf_read(capture_file *cf, gboolean reloading)
 {
   int                  err;
   gchar               *err_info;
   gchar               *name_ptr;
-  gint64               data_offset;
-  gint64               file_pos;
   progdlg_t           *progbar        = NULL;
   gboolean             stop_flag;
-  gint64               size;
-  float                progbar_val;
   GTimeVal             start_time;
-  gchar                status_str[100];
-  gint64               progbar_nextstep;
-  volatile gint64      progbar_quantum;
   dfilter_t           *dfcode;
-  column_info         *cinfo;
-  epan_dissect_t       edt;
-  gboolean             create_proto_tree;
+  volatile gboolean    create_proto_tree;
   guint                tap_flags;
-  int                  count          = 0;
-#ifdef HAVE_LIBPCAP
-  int                  displayed_once = 0;
-#endif
   gboolean             compiled;
 
   /* Compile the current display filter.
@@ -538,12 +552,8 @@ cf_read(capture_file *cf, gboolean reloading)
 
   /* Get the union of the flags for all tap listeners. */
   tap_flags = union_of_tap_listener_flags();
-  cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
   create_proto_tree =
     (dfcode != NULL || have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
-  epan_dissect_init(&edt, create_proto_tree, FALSE);
-  if (dfcode != NULL)
-    epan_dissect_prime_dfilter(&edt, dfcode);
 
   reset_tap_listeners();
 
@@ -558,81 +568,98 @@ cf_read(capture_file *cf, gboolean reloading)
      XXX - do we know this at open time? */
   cf->iscompressed = wtap_iscompressed(cf->wth);
 
-  /* Find the size of the file. */
-  size = wtap_file_size(cf->wth, NULL);
-
-  /* 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. */
-  if (size >= 0) {
-    progbar_quantum = size/N_PROGBAR_UPDATES;
-    if (progbar_quantum < MIN_QUANTUM)
-      progbar_quantum = MIN_QUANTUM;
-  }else
-    progbar_quantum = 0;
-  /* Progress so far. */
-  progbar_val = 0.0f;
-
   /* The packet list window will be empty until the file is completly loaded */
   packet_list_freeze();
 
   stop_flag = FALSE;
   g_get_current_time(&start_time);
 
-  TRY
-  while ((wtap_read(cf->wth, &err, &err_info, &data_offset))) {
-    if (size >= 0) {
-      count++;
-      file_pos = wtap_read_so_far(cf->wth);
+  TRY {
+#ifdef HAVE_LIBPCAP
+    int     displayed_once    = 0;
+#endif
+    int     count             = 0;
 
-      /* Create the progress bar if necessary.
-       * Check whether it should be created or not every MIN_NUMBER_OF_PACKET
-       */
-      if ((progbar == NULL) && !(count % MIN_NUMBER_OF_PACKET)) {
-        progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
-        if (reloading)
-          progbar = delayed_create_progress_dlg(cf->window, "Reloading", name_ptr,
-                                                TRUE, &stop_flag, &start_time, progbar_val);
-        else
-          progbar = delayed_create_progress_dlg(cf->window, "Loading", name_ptr,
-                                                TRUE, &stop_flag, &start_time, progbar_val);
-      }
+    gint64  size;
+    gint64  file_pos;
+    gint64  data_offset;
 
-      /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
-         when we update it, we have to run the GTK+ main loop to get it
-         to repaint what's pending, and doing so may involve an "ioctl()"
-         to see if there's any pending input from an X server, and doing
-         that for every packet can be costly, especially on a big file. */
-      if (file_pos >= progbar_nextstep) {
-        if (progbar != NULL) {
+    gint64  progbar_quantum;
+    gint64  progbar_nextstep;
+    float   progbar_val;
+    gchar   status_str[100];
+
+    column_info *cinfo;
+
+    cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
+
+    /* Find the size of the file. */
+    size = wtap_file_size(cf->wth, NULL);
+
+    /* 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. */
+    if (size >= 0) {
+      progbar_quantum = size/N_PROGBAR_UPDATES;
+      if (progbar_quantum < MIN_QUANTUM)
+        progbar_quantum = MIN_QUANTUM;
+    }else
+      progbar_quantum = 0;
+
+    while ((wtap_read(cf->wth, &err, &err_info, &data_offset))) {
+      if (size >= 0) {
+        count++;
+        file_pos = wtap_read_so_far(cf->wth);
+
+        /* Create the progress bar if necessary.
+         * Check whether it should be created or not every MIN_NUMBER_OF_PACKET
+         */
+        if ((progbar == NULL) && !(count % MIN_NUMBER_OF_PACKET)) {
           progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
-          /* update the packet bar content on the first run or frequently on very large files */
+          if (reloading)
+            progbar = delayed_create_progress_dlg(cf->window, "Reloading", name_ptr,
+                TRUE, &stop_flag, &start_time, progbar_val);
+          else
+            progbar = delayed_create_progress_dlg(cf->window, "Loading", name_ptr,
+                TRUE, &stop_flag, &start_time, progbar_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. */
+        if (file_pos >= progbar_nextstep) {
+          if (progbar != NULL) {
+            progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
+            /* update the packet bar content on the first run or frequently on very large files */
 #ifdef HAVE_LIBPCAP
-          if (progbar_quantum > 500000 || displayed_once == 0) {
-            if ((auto_scroll_live || displayed_once == 0 || cf->displayed_count < 1000) && cf->count != 0) {
-              displayed_once = 1;
-              packets_bar_update();
+            if (progbar_quantum > 500000 || displayed_once == 0) {
+              if ((auto_scroll_live || displayed_once == 0 || cf->displayed_count < 1000) && cf->count != 0) {
+                displayed_once = 1;
+                packets_bar_update();
+              }
             }
-          }
 #endif /* HAVE_LIBPCAP */
-          update_progress_dlg(progbar, progbar_val, status_str);
+            update_progress_dlg(progbar, progbar_val, status_str);
+          }
+          progbar_nextstep += progbar_quantum;
         }
-        progbar_nextstep += progbar_quantum;
       }
-    }
 
-    if (stop_flag) {
-      /* Well, the user decided to abort the read. He/She will be warned and
-         it might be enough for him/her to work with the already loaded
-         packets.
-         This is especially true for very large capture files, where you don't
-         want to wait loading the whole file (which may last minutes or even
-         hours even on fast machines) just to see that it was the wrong file. */
-      break;
+      if (stop_flag) {
+        /* Well, the user decided to abort the read. He/She will be warned and
+           it might be enough for him/her to work with the already loaded
+           packets.
+           This is especially true for very large capture files, where you don't
+           want to wait loading the whole file (which may last minutes or even
+           hours even on fast machines) just to see that it was the wrong file. */
+        break;
+      }
+      read_packet(cf, dfcode, create_proto_tree, cinfo, data_offset);
     }
-    read_packet(cf, dfcode, &edt, cinfo, data_offset);
-  } 
+  }
   CATCH(OutOfMemoryError) {
     simple_message_box(ESD_TYPE_ERROR, NULL,
                    "Some infos / workarounds can be found at:\n"
@@ -646,7 +673,6 @@ cf_read(capture_file *cf, gboolean reloading)
 #endif
   }
   ENDTRY;
-  epan_dissect_finish(&edt);
 
   /* Free the display name */
   g_free(name_ptr);
@@ -772,17 +798,14 @@ cf_start_tail(capture_file *cf, const char *fname, gboolean is_tempfile, int *er
 }
 
 cf_read_status_t
-cf_continue_tail(capture_file *cf, int to_read, int *err)
+cf_continue_tail(capture_file *cf, volatile int to_read, int *err)
 {
-  gint64        data_offset             = 0;
-  gchar        *err_info;
-  int           newly_displayed_packets = 0;
-  dfilter_t    *dfcode;
-  column_info  *cinfo;
-  epan_dissect_t edt;
-  gboolean      create_proto_tree;
-  guint         tap_flags;
-  gboolean      compiled;
+  gchar            *err_info;
+  int               newly_displayed_packets = 0;
+  dfilter_t        *dfcode;
+  volatile gboolean create_proto_tree;
+  guint             tap_flags;
+  gboolean          compiled;
 
   /* Compile the current display filter.
    * We assume this will not fail since cf->dfilter is only set in
@@ -793,12 +816,8 @@ cf_continue_tail(capture_file *cf, int to_read, int *err)
 
   /* Get the union of the flags for all tap listeners. */
   tap_flags = union_of_tap_listener_flags();
-  cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
   create_proto_tree =
     (dfcode != NULL || have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
-  epan_dissect_init(&edt, create_proto_tree, FALSE);
-  if (dfcode != NULL)
-    epan_dissect_prime_dfilter(&edt, dfcode);
 
   *err = 0;
 
@@ -808,22 +827,28 @@ cf_continue_tail(capture_file *cf, int to_read, int *err)
 
   /*g_log(NULL, G_LOG_LEVEL_MESSAGE, "cf_continue_tail: %u new: %u", cf->count, to_read);*/
 
-  TRY
-  while (to_read != 0) {
-    wtap_cleareof(cf->wth);
-    if (!wtap_read(cf->wth, err, &err_info, &data_offset)) {
-      break;
-    }
-    if (cf->state == FILE_READ_ABORTED) {
-      /* Well, the user decided to exit Wireshark.  Break out of the
-         loop, and let the code below (which is called even if there
-         aren't any packets left to read) exit. */
-      break;
-    }
-    if (read_packet(cf, dfcode, &edt, (column_info *) cinfo, data_offset) != -1) {
-      newly_displayed_packets++;
+  TRY {
+    gint64 data_offset = 0;
+    column_info *cinfo;
+
+    cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
+
+    while (to_read != 0) {
+      wtap_cleareof(cf->wth);
+      if (!wtap_read(cf->wth, err, &err_info, &data_offset)) {
+        break;
+      }
+      if (cf->state == FILE_READ_ABORTED) {
+        /* Well, the user decided to exit Wireshark.  Break out of the
+           loop, and let the code below (which is called even if there
+           aren't any packets left to read) exit. */
+        break;
+      }
+      if (read_packet(cf, dfcode, create_proto_tree, (column_info *) cinfo, data_offset) != -1) {
+        newly_displayed_packets++;
+      }
+      to_read--;
     }
-    to_read--;
   }
   CATCH(OutOfMemoryError) {
     simple_message_box(ESD_TYPE_ERROR, NULL,
@@ -839,7 +864,6 @@ cf_continue_tail(capture_file *cf, int to_read, int *err)
 #endif
   }
   ENDTRY;
-  epan_dissect_finish(&edt);
 
   /* Update the file encapsulation; it might have changed based on the
      packets we've read. */
@@ -898,7 +922,6 @@ cf_finish_tail(capture_file *cf, int *err)
   dfilter_t *dfcode;
   column_info *cinfo;
   gboolean   create_proto_tree;
-  epan_dissect_t edt;
   guint      tap_flags;
   gboolean   compiled;
 
@@ -914,9 +937,6 @@ cf_finish_tail(capture_file *cf, int *err)
   cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
   create_proto_tree =
     (dfcode != NULL || have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
-  epan_dissect_init(&edt, create_proto_tree, FALSE);
-  if (dfcode != NULL)
-    epan_dissect_prime_dfilter(&edt, dfcode);
 
   if (cf->wth == NULL) {
     cf_close(cf);
@@ -934,9 +954,8 @@ cf_finish_tail(capture_file *cf, int *err)
          aren't any packets left to read) exit. */
       break;
     }
-    read_packet(cf, dfcode, &edt, cinfo, data_offset);
+    read_packet(cf, dfcode, create_proto_tree, cinfo, data_offset);
   }
-  epan_dissect_finish(&edt);
 
   /* Cleanup and release all dfilter resources */
   if (dfcode != NULL) {
@@ -1094,41 +1113,37 @@ void cf_set_rfcode(capture_file *cf, dfilter_t *rfcode)
   cf->rfcode = rfcode;
 }
 
-static void
-find_and_mark_frame_depended_upon(gpointer data, gpointer user_data)
-{
-  frame_data   *dependent_fd;
-  guint32       dependent_frame = GPOINTER_TO_UINT(data);
-  capture_file *cf              = (capture_file *)user_data;
-
-  dependent_fd = frame_data_sequence_find(cf->frames, dependent_frame);
-  dependent_fd->flags.dependent_of_displayed = 1;
-}
-
 static int
 add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
-    dfilter_t *dfcode, epan_dissect_t *edt, column_info *cinfo,
-    struct wtap_pkthdr *phdr, const guchar *buf,
-    gboolean add_to_packet_list)
+    dfilter_t *dfcode, gboolean create_proto_tree, column_info *cinfo,
+    struct wtap_pkthdr *phdr, const guint8 *buf, gboolean add_to_packet_list)
 {
-  gint row = -1;
+  epan_dissect_t  edt;
+  gint            row               = -1;
 
   frame_data_set_before_dissect(fdata, &cf->elapsed_time,
                                 &first_ts, prev_dis, prev_cap);
   prev_cap = fdata;
 
-  epan_dissect_run_with_taps(edt, phdr, buf, fdata, cinfo);
+  /* Dissect the frame. */
+  epan_dissect_init(&edt, create_proto_tree, FALSE);
+
+  if (dfcode != NULL) {
+      epan_dissect_prime_dfilter(&edt, dfcode);
+  }
+
+  epan_dissect_run_with_taps(&edt, phdr, frame_tvbuff_new(fdata, buf), fdata, cinfo);
 
   /* If we don't have a display filter, set "passed_dfilter" to 1. */
   if (dfcode != NULL) {
-    fdata->flags.passed_dfilter = dfilter_apply_edt(dfcode, edt) ? 1 : 0;
+    fdata->flags.passed_dfilter = dfilter_apply_edt(dfcode, &edt) ? 1 : 0;
 
     if (fdata->flags.passed_dfilter) {
       /* This frame passed the display filter but it may depend on other
        * (potentially not displayed) frames.  Find those frames and mark them
        * as depended upon.
        */
-      g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf);
+      g_slist_foreach(edt.pi.dependent_frames, find_and_mark_frame_depended_upon, cf->frames);
     }
   } else
     fdata->flags.passed_dfilter = 1;
@@ -1138,7 +1153,7 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
 
   if (add_to_packet_list) {
     /* We fill the needed columns from new_packet_list */
-      row = packet_list_append(cinfo, fdata, &edt->pi);
+      row = packet_list_append(cinfo, fdata, &edt.pi);
   }
 
   if (fdata->flags.passed_dfilter || fdata->flags.ref_time)
@@ -1166,7 +1181,7 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     cf->last_displayed = fdata->num;
   }
 
-  epan_dissect_reset(edt);
+  epan_dissect_cleanup(&edt);
   return row;
 }
 
@@ -1174,10 +1189,10 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
 /* returns the row of the new packet in the packet list or -1 if not displayed */
 static int
 read_packet(capture_file *cf, dfilter_t *dfcode,
-            epan_dissect_t *edt, column_info *cinfo, gint64 offset)
+            gboolean create_proto_tree, column_info *cinfo, gint64 offset)
 {
   struct wtap_pkthdr *phdr = wtap_phdr(cf->wth);
-  const guchar *buf = wtap_buf_ptr(cf->wth);
+  const guint8 *buf = wtap_buf_ptr(cf->wth);
   frame_data    fdlocal;
   guint32       framenum;
   frame_data   *fdata;
@@ -1203,7 +1218,7 @@ read_packet(capture_file *cf, dfilter_t *dfcode,
     epan_dissect_t edt;
     epan_dissect_init(&edt, TRUE, FALSE);
     epan_dissect_prime_dfilter(&edt, cf->rfcode);
-    epan_dissect_run(&edt, phdr, buf, &fdlocal, NULL);
+    epan_dissect_run(&edt, phdr, frame_tvbuff_new(&fdlocal, buf), &fdlocal, NULL);
     passed = dfilter_apply_edt(cf->rfcode, &edt);
     epan_dissect_cleanup(&edt);
   }
@@ -1219,7 +1234,7 @@ read_packet(capture_file *cf, dfilter_t *dfcode,
 
     if (!cf->redissecting) {
       row = add_packet_to_packet_list(fdata, cf, dfcode,
-                                      edt, cinfo,
+                                      create_proto_tree, cinfo,
                                       phdr, buf, TRUE);
     }
   }
@@ -1299,11 +1314,10 @@ cf_merge_files(char **out_filenamep, int in_file_count,
     wtapng_iface_descriptions_t *idb_inf, *idb_inf_merge_file;
     wtapng_if_descr_t            int_data, *file_int_data;
     GString                     *comment_gstr;
-    int                          i;
 
     fake_interface_ids = TRUE;
     /* Create SHB info */
-    shb_hdr = wtap_file_get_shb_info(in_files[0].wth);
+    shb_hdr      = wtap_file_get_shb_info(in_files[0].wth);
     comment_gstr = g_string_new("");
     g_string_append_printf(comment_gstr, "%s \n",shb_hdr->opt_comment);
     g_string_append_printf(comment_gstr, "File created by merging: \n");
@@ -1672,7 +1686,7 @@ cf_redissect_packets(capture_file *cf)
 
 gboolean
 cf_read_frame_r(capture_file *cf, frame_data *fdata,
-                struct wtap_pkthdr *phdr, guint8 *pd)
+                struct wtap_pkthdr *phdr, Buffer *buf)
 {
   int    err;
   gchar *err_info;
@@ -1689,12 +1703,13 @@ cf_read_frame_r(capture_file *cf, frame_data *fdata,
     }
 
     *phdr = frame->phdr;
-    memcpy(pd, frame->pd, fdata->cap_len);
+    buffer_assure_space(buf, frame->phdr.caplen);
+    memcpy(buffer_start_ptr(buf), frame->pd, frame->phdr.caplen);
     return TRUE;
   }
 #endif
 
-  if (!wtap_seek_read(cf->wth, fdata->file_off, phdr, pd,
+  if (!wtap_seek_read(cf->wth, fdata->file_off, phdr, buf,
                       fdata->cap_len, &err, &err_info)) {
     display_basename = g_filename_display_basename(cf->filename);
     switch (err) {
@@ -1726,7 +1741,7 @@ cf_read_frame_r(capture_file *cf, frame_data *fdata,
 gboolean
 cf_read_frame(capture_file *cf, frame_data *fdata)
 {
-  return cf_read_frame_r(cf, fdata, &cf->phdr, cf->pd);
+  return cf_read_frame_r(cf, fdata, &cf->phdr, &cf->buf);
 }
 
 /* Rescan the list of packets, reconstructing the CList.
@@ -1760,7 +1775,6 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
   int         progbar_quantum;
   dfilter_t  *dfcode;
   column_info *cinfo;
-  epan_dissect_t edt;
   gboolean    create_proto_tree;
   guint       tap_flags;
   gboolean    add_to_packet_list = FALSE;
@@ -1779,9 +1793,6 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
   cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
   create_proto_tree =
     (dfcode != NULL || have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
-  epan_dissect_init(&edt, create_proto_tree, FALSE);
-  if (dfcode != NULL)
-    epan_dissect_prime_dfilter(&edt, dfcode);
 
   reset_tap_listeners();
   /* Which frame, if any, is the currently selected frame?
@@ -1915,8 +1926,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
        * as not visited, free the GSList referring to the state
        * data (the per-frame data itself was freed by
        * "init_dissection()"), and null out the GSList pointer. */
-      fdata->flags.visited = 0;
-      frame_data_cleanup(fdata);
+      frame_data_reset(fdata);
       frames_count = cf->count;
     }
 
@@ -1933,8 +1943,9 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
       preceding_frame_num = prev_frame_num;
       preceding_frame = prev_frame;
     }
-    add_packet_to_packet_list(fdata, cf, dfcode, &edt,
-                                    cinfo, &cf->phdr, cf->pd,
+    add_packet_to_packet_list(fdata, cf, dfcode, create_proto_tree,
+                                    cinfo, &cf->phdr,
+                                    buffer_start_ptr(&cf->buf),
                                     add_to_packet_list);
 
     /* If this frame is displayed, and this is the first frame we've
@@ -1956,7 +1967,6 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
     prev_frame_num = fdata->num;
     prev_frame = fdata;
   }
-  epan_dissect_finish(&edt);
 
   /* We are done redissecting the packet list. */
   cf->redissecting = FALSE;
@@ -1974,8 +1984,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
        until it finishes.  Should we just stick them with that? */
     for (; framenum <= frames_count; framenum++) {
       fdata = frame_data_sequence_find(cf->frames, framenum);
-      fdata->flags.visited = 0;
-      frame_data_cleanup(fdata);
+      frame_data_reset(fdata);
     }
   }
 
@@ -2154,7 +2163,7 @@ process_specified_packets(capture_file *cf, packet_range_t *range,
 {
   guint32          framenum;
   frame_data      *fdata;
-  guint8           pd[WTAP_MAX_PACKET_SIZE+1];
+  Buffer           buf;
   psp_return_t     ret     = PSP_FINISHED;
 
   progdlg_t       *progbar = NULL;
@@ -2168,6 +2177,8 @@ process_specified_packets(capture_file *cf, packet_range_t *range,
   range_process_e  process_this;
   struct wtap_pkthdr phdr;
 
+  buffer_init(&buf, 1500);
+
   /* Update the progress bar when it gets to this value. */
   progbar_nextstep = 0;
   /* When we reach the value that triggers a progress bar update,
@@ -2245,13 +2256,13 @@ process_specified_packets(capture_file *cf, packet_range_t *range,
     }
 
     /* Get the packet */
-    if (!cf_read_frame_r(cf, fdata, &phdr, pd)) {
+    if (!cf_read_frame_r(cf, fdata, &phdr, &buf)) {
       /* Attempt to get the packet failed. */
       ret = PSP_FAILED;
       break;
     }
     /* Process the packet */
-    if (!callback(cf, fdata, &phdr, pd, callback_args)) {
+    if (!callback(cf, fdata, &phdr, buffer_start_ptr(&buf), callback_args)) {
       /* Callback failed.  We assume it reported the error appropriately. */
       ret = PSP_FAILED;
       break;
@@ -2263,6 +2274,8 @@ process_specified_packets(capture_file *cf, packet_range_t *range,
   if (progbar != NULL)
     destroy_progress_dlg(progbar);
 
+  buffer_free(&buf);
+
   return ret;
 }
 
@@ -2276,11 +2289,11 @@ retap_packet(capture_file *cf _U_, frame_data *fdata,
              struct wtap_pkthdr *phdr, const guint8 *pd,
              void *argsp)
 {
-  retap_callback_args_t *args = argsp;
+  retap_callback_args_t *args = (retap_callback_args_t *)argsp;
   epan_dissect_t         edt;
 
   epan_dissect_init(&edt, args->construct_protocol_tree, FALSE);
-  epan_dissect_run_with_taps(&edt, phdr, pd, fdata, args->cinfo);
+  epan_dissect_run_with_taps(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, args->cinfo);
   epan_dissect_cleanup(&edt);
 
   return TRUE;
@@ -2354,7 +2367,7 @@ print_packet(capture_file *cf, frame_data *fdata,
              struct wtap_pkthdr *phdr, const guint8 *pd,
              void *argsp)
 {
-  print_callback_args_t *args = argsp;
+  print_callback_args_t *args = (print_callback_args_t *)argsp;
   epan_dissect_t  edt;
   int             i;
   char           *cp;
@@ -2376,10 +2389,10 @@ print_packet(capture_file *cf, frame_data *fdata,
      information. */
   if (args->print_args->print_summary) {
     col_custom_prime_edt(&edt, &cf->cinfo);
-    epan_dissect_run(&edt, phdr, pd, fdata, &cf->cinfo);
+    epan_dissect_run(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, &cf->cinfo);
     epan_dissect_fill_in_columns(&edt, FALSE, TRUE);
   } else
-    epan_dissect_run(&edt, phdr, pd, fdata, NULL);
+    epan_dissect_run(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, NULL);
 
   if (args->print_formfeed) {
     if (!new_page(args->print_args->stream))
@@ -2398,6 +2411,8 @@ print_packet(capture_file *cf, frame_data *fdata,
   g_snprintf(bookmark_name, sizeof bookmark_name, "__frame%u__", fdata->num);
 
   if (args->print_args->print_summary) {
+    if (!args->print_args->print_col_headings)
+        args->print_header_line = FALSE;
     if (args->print_header_line) {
       if (!print_line(args->print_args->stream, 0, args->header_line_buf))
         goto fail;
@@ -2417,7 +2432,7 @@ print_packet(capture_file *cf, frame_data *fdata,
       if (line_len > args->line_buf_len) {
         cp_off = (int) (cp - args->line_buf);
         args->line_buf_len = 2 * line_len;
-        args->line_buf = g_realloc(args->line_buf, args->line_buf_len + 1);
+        args->line_buf = (char *)g_realloc(args->line_buf, args->line_buf_len + 1);
         cp = args->line_buf + cp_off;
       }
 
@@ -2467,7 +2482,8 @@ print_packet(capture_file *cf, frame_data *fdata,
     args->print_separator = TRUE;
 
     /* Print a header line if we print any more packet summaries */
-    args->print_header_line = TRUE;
+    if (args->print_args->print_col_headings)
+        args->print_header_line = TRUE;
   }
 
   if (args->print_args->print_hex) {
@@ -2483,7 +2499,8 @@ print_packet(capture_file *cf, frame_data *fdata,
     args->print_separator = TRUE;
 
     /* Print a header line if we print any more packet summaries */
-    args->print_header_line = TRUE;
+    if (args->print_args->print_col_headings)
+        args->print_header_line = TRUE;
   } /* if (args->print_args->print_dissections != print_dissections_none) */
 
   epan_dissect_cleanup(&edt);
@@ -2513,7 +2530,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
   fmt_data     *cfmt;
 
   callback_args.print_args = print_args;
-  callback_args.print_header_line = TRUE;
+  callback_args.print_header_line = print_args->print_col_headings;
   callback_args.header_line_buf = NULL;
   callback_args.header_line_buf_len = 256;
   callback_args.print_formfeed = FALSE;
@@ -2524,7 +2541,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
   callback_args.num_visible_cols = 0;
   callback_args.visible_cols = NULL;
 
-  if (!print_preamble(print_args->stream, cf->filename)) {
+  if (!print_preamble(print_args->stream, cf->filename, wireshark_svnversion)) {
     destroy_print_stream(print_args->stream);
     return CF_PRINT_WRITE_ERROR;
   }
@@ -2532,7 +2549,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
   if (print_args->print_summary) {
     /* 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);
+    callback_args.header_line_buf = (char *)g_malloc(callback_args.header_line_buf_len + 1);
 
     /* Find the number of visible columns and the last visible column */
     for (i = 0; i < prefs.num_cols; i++) {
@@ -2591,7 +2608,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
       if (line_len > callback_args.header_line_buf_len) {
         cp_off = (int) (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 = (char *)g_realloc(callback_args.header_line_buf,
                                                   callback_args.header_line_buf_len + 1);
         cp = callback_args.header_line_buf + cp_off;
       }
@@ -2612,7 +2629,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
     /* 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);
+    callback_args.line_buf = (char *)g_malloc(callback_args.line_buf_len + 1);
   } /* if (print_summary) */
 
   /* Iterate through the list of packets, printing the packets we were
@@ -2668,12 +2685,12 @@ write_pdml_packet(capture_file *cf _U_, frame_data *fdata,
                   struct wtap_pkthdr *phdr, const guint8 *pd,
           void *argsp)
 {
-  FILE           *fh = argsp;
+  FILE           *fh = (FILE *)argsp;
   epan_dissect_t  edt;
 
   /* Create the protocol tree, but don't fill in the column information. */
   epan_dissect_init(&edt, TRUE, TRUE);
-  epan_dissect_run(&edt, phdr, pd, fdata, NULL);
+  epan_dissect_run(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, NULL);
 
   /* Write out the information in that tree. */
   proto_tree_write_pdml(&edt, fh);
@@ -2738,7 +2755,7 @@ write_psml_packet(capture_file *cf, frame_data *fdata,
                   struct wtap_pkthdr *phdr, const guint8 *pd,
           void *argsp)
 {
-  FILE           *fh = argsp;
+  FILE           *fh = (FILE *)argsp;
   epan_dissect_t  edt;
   gboolean        proto_tree_needed;
 
@@ -2747,7 +2764,7 @@ write_psml_packet(capture_file *cf, frame_data *fdata,
   proto_tree_needed = have_custom_cols(&cf->cinfo);
   epan_dissect_init(&edt, proto_tree_needed, proto_tree_needed);
   col_custom_prime_edt(&edt, &cf->cinfo);
-  epan_dissect_run(&edt, phdr, pd, fdata, &cf->cinfo);
+  epan_dissect_run(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, &cf->cinfo);
   epan_dissect_fill_in_columns(&edt, FALSE, TRUE);
 
   /* Write out the information in that tree. */
@@ -2813,7 +2830,7 @@ write_csv_packet(capture_file *cf, frame_data *fdata,
                  struct wtap_pkthdr *phdr, const guint8 *pd,
                  void *argsp)
 {
-  FILE           *fh = argsp;
+  FILE           *fh = (FILE *)argsp;
   epan_dissect_t  edt;
   gboolean        proto_tree_needed;
 
@@ -2822,7 +2839,7 @@ write_csv_packet(capture_file *cf, frame_data *fdata,
   proto_tree_needed = have_custom_cols(&cf->cinfo);
   epan_dissect_init(&edt, proto_tree_needed, proto_tree_needed);
   col_custom_prime_edt(&edt, &cf->cinfo);
-  epan_dissect_run(&edt, phdr, pd, fdata, &cf->cinfo);
+  epan_dissect_run(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, &cf->cinfo);
   epan_dissect_fill_in_columns(&edt, FALSE, TRUE);
 
   /* Write out the information in that tree. */
@@ -2888,11 +2905,11 @@ write_carrays_packet(capture_file *cf _U_, frame_data *fdata,
              struct wtap_pkthdr *phdr,
              const guint8 *pd, void *argsp)
 {
-  FILE           *fh = argsp;
+  FILE           *fh = (FILE *)argsp;
   epan_dissect_t  edt;
 
   epan_dissect_init(&edt, TRUE, TRUE);
-  epan_dissect_run(&edt, phdr, pd, fdata, NULL);
+  epan_dissect_run(&edt, phdr, frame_tvbuff_new(fdata, pd), fdata, NULL);
   proto_tree_write_carrays(fdata->num, fh, &edt);
   epan_dissect_cleanup(&edt);
 
@@ -2973,7 +2990,7 @@ cf_find_string_protocol_tree(capture_file *cf, proto_tree *tree,  match_data *md
 static match_result
 match_protocol_tree(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  match_data     *mdata = criterion;
+  match_data     *mdata = (match_data *)criterion;
   epan_dissect_t  edt;
 
   /* Load the frame's data. */
@@ -2985,7 +3002,7 @@ match_protocol_tree(capture_file *cf, frame_data *fdata, void *criterion)
   /* Construct the protocol tree, including the displayed text */
   epan_dissect_init(&edt, TRUE, TRUE);
   /* We don't need the column information */
-  epan_dissect_run(&edt, &cf->phdr, cf->pd, fdata, NULL);
+  epan_dissect_run(&edt, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
 
   /* Iterate through all the nodes, seeing if they have text that matches. */
   mdata->cf = cf;
@@ -3068,7 +3085,7 @@ cf_find_packet_summary_line(capture_file *cf, const char *string,
 static match_result
 match_summary_line(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  match_data     *mdata      = criterion;
+  match_data     *mdata      = (match_data *)criterion;
   const gchar    *string     = mdata->string;
   size_t          string_len = mdata->string_len;
   epan_dissect_t  edt;
@@ -3089,7 +3106,8 @@ match_summary_line(capture_file *cf, frame_data *fdata, void *criterion)
   /* Don't bother constructing the protocol tree */
   epan_dissect_init(&edt, FALSE, FALSE);
   /* Get the column information */
-  epan_dissect_run(&edt, &cf->phdr, cf->pd, fdata, &cf->cinfo);
+  epan_dissect_run(&edt, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata,
+                   &cf->cinfo);
 
   /* Find the Info column */
   for (colx = 0; colx < cf->cinfo.num_cols; colx++) {
@@ -3122,6 +3140,17 @@ typedef struct {
     size_t        data_len;
 } cbs_t;    /* "Counted byte string" */
 
+
+/*
+ * The current match_* routines only support ASCII case insensitivity and don't
+ * convert UTF-8 inputs to UTF-16 for matching.
+ *
+ * We could modify them to use the GLib Unicode routines or the International
+ * Components for Unicode library but it's not apparent that we could do so
+ * without consuming a lot more CPU and memory or that searching would be
+ * significantly better.
+ */
+
 gboolean
 cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size,
                     search_direction dir)
@@ -3136,14 +3165,14 @@ cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size,
     /* String search - what type of string? */
     switch (cf->scs_type) {
 
-    case SCS_ASCII_AND_UNICODE:
-      return find_packet(cf, match_ascii_and_unicode, &info, dir);
+    case SCS_NARROW_AND_WIDE:
+      return find_packet(cf, match_narrow_and_wide, &info, dir);
 
-    case SCS_ASCII:
-      return find_packet(cf, match_ascii, &info, dir);
+    case SCS_NARROW:
+      return find_packet(cf, match_narrow, &info, dir);
 
-    case SCS_UNICODE:
-      return find_packet(cf, match_unicode, &info, dir);
+    case SCS_WIDE:
+      return find_packet(cf, match_wide, &info, dir);
 
     default:
       g_assert_not_reached();
@@ -3154,13 +3183,14 @@ cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size,
 }
 
 static match_result
-match_ascii_and_unicode(capture_file *cf, frame_data *fdata, void *criterion)
+match_narrow_and_wide(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  cbs_t        *info       = criterion;
+  cbs_t        *info       = (cbs_t *)criterion;
   const guint8 *ascii_text = info->data;
   size_t        textlen    = info->data_len;
   match_result  result;
   guint32       buf_len;
+  guint8       *pd;
   guint32       i;
   guint8        c_char;
   size_t        c_match    = 0;
@@ -3173,9 +3203,10 @@ match_ascii_and_unicode(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->cap_len;
+  pd = buffer_start_ptr(&cf->buf);
   i = 0;
   while (i < buf_len) {
-    c_char = cf->pd[i];
+    c_char = pd[i];
     if (cf->case_type)
       c_char = toupper(c_char);
     if (c_char != '\0') {
@@ -3200,9 +3231,10 @@ match_ascii_and_unicode(capture_file *cf, frame_data *fdata, void *criterion)
 }
 
 static match_result
-match_ascii(capture_file *cf, frame_data *fdata, void *criterion)
+match_narrow(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  cbs_t        *info       = criterion;
+  guint8       *pd;
+  cbs_t        *info       = (cbs_t *)criterion;
   const guint8 *ascii_text = info->data;
   size_t        textlen    = info->data_len;
   match_result  result;
@@ -3219,9 +3251,10 @@ match_ascii(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->cap_len;
+  pd = buffer_start_ptr(&cf->buf);
   i = 0;
   while (i < buf_len) {
-    c_char = cf->pd[i];
+    c_char = pd[i];
     if (cf->case_type)
       c_char = toupper(c_char);
     if (c_char == ascii_text[c_match]) {
@@ -3245,13 +3278,14 @@ match_ascii(capture_file *cf, frame_data *fdata, void *criterion)
 }
 
 static match_result
-match_unicode(capture_file *cf, frame_data *fdata, void *criterion)
+match_wide(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  cbs_t        *info       = criterion;
+  cbs_t        *info       = (cbs_t *)criterion;
   const guint8 *ascii_text = info->data;
   size_t        textlen    = info->data_len;
   match_result  result;
   guint32       buf_len;
+  guint8       *pd;
   guint32       i;
   guint8        c_char;
   size_t        c_match    = 0;
@@ -3264,9 +3298,10 @@ match_unicode(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->cap_len;
+  pd = buffer_start_ptr(&cf->buf);
   i = 0;
   while (i < buf_len) {
-    c_char = cf->pd[i];
+    c_char = pd[i];
     if (cf->case_type)
       c_char = toupper(c_char);
     if (c_char == ascii_text[c_match]) {
@@ -3292,11 +3327,12 @@ match_unicode(capture_file *cf, frame_data *fdata, void *criterion)
 static match_result
 match_binary(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  cbs_t        *info        = criterion;
+  cbs_t        *info        = (cbs_t *)criterion;
   const guint8 *binary_data = info->data;
   size_t        datalen     = info->data_len;
   match_result  result;
   guint32       buf_len;
+  guint8       *pd;
   guint32       i;
   size_t        c_match     = 0;
 
@@ -3308,9 +3344,10 @@ match_binary(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->cap_len;
+  pd = buffer_start_ptr(&cf->buf);
   i = 0;
   while (i < buf_len) {
-    if (cf->pd[i] == binary_data[c_match]) {
+    if (pd[i] == binary_data[c_match]) {
       c_match += 1;
       if (c_match == datalen) {
         result = MR_MATCHED;
@@ -3365,7 +3402,7 @@ cf_find_packet_dfilter_string(capture_file *cf, const char *filter,
 static match_result
 match_dfilter(capture_file *cf, frame_data *fdata, void *criterion)
 {
-  dfilter_t      *sfcode = criterion;
+  dfilter_t      *sfcode = (dfilter_t *)criterion;
   epan_dissect_t  edt;
   match_result    result;
 
@@ -3377,7 +3414,7 @@ match_dfilter(capture_file *cf, frame_data *fdata, void *criterion)
 
   epan_dissect_init(&edt, TRUE, FALSE);
   epan_dissect_prime_dfilter(&edt, sfcode);
-  epan_dissect_run(&edt, &cf->phdr, cf->pd, fdata, NULL);
+  epan_dissect_run(&edt, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
   result = dfilter_apply_edt(sfcode, &edt) ? MR_MATCHED : MR_NOTMATCHED;
   epan_dissect_cleanup(&edt);
   return result;
@@ -3710,8 +3747,8 @@ cf_select_packet(capture_file *cf, int row)
   cf->edt = epan_dissect_new(TRUE, TRUE);
 
   tap_build_interesting(cf->edt);
-  epan_dissect_run(cf->edt, &cf->phdr, cf->pd, cf->current_frame, 
-          NULL);
+  epan_dissect_run(cf->edt, &cf->phdr, frame_tvbuff_new_buffer(cf->current_frame, &cf->buf),
+                   cf->current_frame, NULL);
 
   dfilter_macro_build_ftv_cache(cf->edt->tree);
 
@@ -3866,17 +3903,25 @@ cf_update_packet_comment(capture_file *cf, frame_data *fdata, gchar *comment)
     cf->packet_comment_count++;
   }
 
+  expert_update_comment_count(cf->packet_comment_count);
+
   /* OK, we have unsaved changes. */
   cf->unsaved_changes = TRUE;
 }
 
 /*
- * Does this capture file have any comments?
+ * What types of comments does this capture file have?
  */
-gboolean
-cf_has_comments(capture_file *cf)
+guint32
+cf_comment_types(capture_file *cf)
 {
-  return (cf_read_shb_comment(cf) != NULL || cf->packet_comment_count != 0);
+  guint32 comment_types = 0;
+
+  if (cf_read_shb_comment(cf) != NULL)
+    comment_types |= WTAP_COMMENT_PER_SECTION;
+  if (cf->packet_comment_count != 0)
+    comment_types |= WTAP_COMMENT_PER_PACKET;
+  return comment_types;
 }
 
 typedef struct {
@@ -3897,7 +3942,7 @@ save_packet(capture_file *cf _U_, frame_data *fdata,
             struct wtap_pkthdr *phdr, const guint8 *pd,
             void *argsp)
 {
-  save_callback_args_t *args = argsp;
+  save_callback_args_t *args = (save_callback_args_t *)argsp;
   struct wtap_pkthdr    hdr;
   int           err;
   gchar        *display_basename;
@@ -3920,6 +3965,8 @@ save_packet(capture_file *cf _U_, frame_data *fdata,
     hdr.presence_flags |= WTAP_HAS_TS;
   if (fdata->flags.has_if_id)
     hdr.presence_flags |= WTAP_HAS_INTERFACE_ID;
+  if (fdata->flags.has_pack_flags)
+    hdr.presence_flags |= WTAP_HAS_PACK_FLAGS;
   hdr.ts.secs      = fdata->abs_ts.secs;
   hdr.ts.nsecs     = fdata->abs_ts.nsecs;
   hdr.caplen       = fdata->cap_len;
@@ -3928,6 +3975,7 @@ save_packet(capture_file *cf _U_, frame_data *fdata,
   /* pcapng */
   hdr.interface_id = fdata->interface_id;   /* identifier of the interface. */
   /* options */
+  hdr.pack_flags   = fdata->pack_flags;
   hdr.opt_comment  = fdata->opt_comment; /* NULL if not available */
   /* pseudo */
   hdr.pseudo_header = phdr->pseudo_header;
@@ -3975,22 +4023,104 @@ save_packet(capture_file *cf _U_, frame_data *fdata,
 gboolean
 cf_can_write_with_wiretap(capture_file *cf)
 {
-  int ft;
-
-  for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
-    /* To save a file with Wiretap, Wiretap has to handle that format,
-       and its code to handle that format must be able to write a file
-       with this file's encapsulation types. */
-    if (wtap_dump_can_write_encaps(ft, cf->linktypes)) {
-      /* OK, we can write it out in this type. */
-      return TRUE;
-    }
+  /* We don't care whether we support the comments in this file or not;
+     if we can't, we'll offer the user the option of discarding the
+     comments. */
+  return wtap_dump_can_write(cf->linktypes, 0);
+}
+
+/*
+ * Should we let the user do a save?
+ *
+ * We should if:
+ *
+ *  the file has unsaved changes, and we can save it in some
+ *  format through Wiretap
+ *
+ * or
+ *
+ *  the file is a temporary file and has no unsaved changes (so
+ *  that "saving" it just means copying it).
+ *
+ * XXX - we shouldn't allow files to be edited if they can't be saved,
+ * so cf->unsaved_changes should be true only if the file can be saved.
+ *
+ * We don't care whether we support the comments in this file or not;
+ * if we can't, we'll offer the user the option of discarding the
+ * comments.
+ */
+gboolean
+cf_can_save(capture_file *cf)
+{
+  if (cf->unsaved_changes && wtap_dump_can_write(cf->linktypes, 0)) {
+    /* Saved changes, and we can write it out with Wiretap. */
+    return TRUE;
   }
 
-  /* No, we couldn't save it in any format. */
+  if (cf->is_tempfile && !cf->unsaved_changes) {
+    /*
+     * Temporary file with no unsaved changes, so we can just do a
+     * raw binary copy.
+     */
+    return TRUE;
+  }
+
+  /* Nothing to save. */
   return FALSE;
 }
 
+/*
+ * Should we let the user do a "save as"?
+ *
+ * That's true if:
+ *
+ *  we can save it in some format through Wiretap
+ *
+ * or
+ *
+ *  the file is a temporary file and has no unsaved changes (so
+ *  that "saving" it just means copying it).
+ *
+ * XXX - we shouldn't allow files to be edited if they can't be saved,
+ * so cf->unsaved_changes should be true only if the file can be saved.
+ *
+ * We don't care whether we support the comments in this file or not;
+ * if we can't, we'll offer the user the option of discarding the
+ * comments.
+ */
+gboolean
+cf_can_save_as(capture_file *cf)
+{
+  if (wtap_dump_can_write(cf->linktypes, 0)) {
+    /* We can write it out with Wiretap. */
+    return TRUE;
+  }
+
+  if (cf->is_tempfile && !cf->unsaved_changes) {
+    /*
+     * Temporary file with no unsaved changes, so we can just do a
+     * raw binary copy.
+     */
+    return TRUE;
+  }
+
+  /* Nothing to save. */
+  return FALSE;
+}
+
+/*
+ * Does this file have unsaved data?
+ */
+gboolean
+cf_has_unsaved_data(capture_file *cf)
+{
+  /*
+   * If this is a temporary file, or a file with unsaved changes, it
+   * has unsaved data.
+   */
+  return cf->is_tempfile || cf->unsaved_changes;
+}
+
 /*
  * Quick scan to find packet offsets.
  */
@@ -4074,8 +4204,6 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
       progbar_quantum = MIN_QUANTUM;
   }else
     progbar_quantum = 0;
-  /* Progress so far. */
-  progbar_val = 0.0f;
 
   stop_flag = FALSE;
   g_get_current_time(&start_time);
@@ -4229,30 +4357,35 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format,
                 gboolean compressed, gboolean discard_comments,
                 gboolean dont_reopen)
 {
-  gchar        *fname_new = NULL;
-  int           err;
-  gchar        *err_info;
+  gchar           *err_info;
+  gchar           *fname_new = NULL;
+  wtap_dumper     *pdh;
+  frame_data      *fdata;
+  struct addrinfo *addrs;
+  guint            framenum;
+  int              err;
+#ifdef _WIN32
+  gchar           *display_basename;
+#endif
   enum {
      SAVE_WITH_MOVE,
      SAVE_WITH_COPY,
      SAVE_WITH_WTAP
-  }             how_to_save;
-  wtap_dumper  *pdh;
+  }                    how_to_save;
   save_callback_args_t callback_args;
-#ifdef _WIN32
-  gchar        *display_basename;
-#endif
-  guint         framenum;
-  frame_data   *fdata;
 
   cf_callback_invoke(cf_cb_file_save_started, (gpointer)fname);
 
+  addrs = get_addrinfo_list();
+
   if (save_format == cf->cd_t && compressed == cf->iscompressed
-      && !discard_comments && !cf->unsaved_changes) {
+      && !discard_comments && !cf->unsaved_changes
+      && !(addrs && addrs->ai_next &&
+           wtap_dump_has_name_resolution(save_format))) {
     /* We're saving in the format it's already in, and we're
        not discarding comments, and there are no changes we have
-       in memory that aren't saved to the file, so we can just move
-       or copy the raw data. */
+       in memory that aren't saved to the file, and we have no name
+       resolution blocks to write, so we can just move or copy the raw data. */
 
     if (cf->is_tempfile) {
       /* The file being saved is a temporary file from a live
@@ -4362,7 +4495,7 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format,
     }
 
     /* Add address resolution */
-    wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list());
+    wtap_dump_set_addrinfo_list(pdh, addrs);
 
     /* Iterate through the list of packets, processing all the packets. */
     callback_args.pdh = pdh;