checkhf: handle C++-style comments
[metze/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index d7f13bb7a2b46c40dbf989d82041544809916712..ff39a9dffe493cde4da6a3a28ce76c08938d4c45 100644 (file)
--- a/file.c
+++ b/file.c
@@ -33,7 +33,7 @@
 #include <wsutil/tempfile.h>
 #include <wsutil/file_util.h>
 #include <wsutil/filesystem.h>
-#include <wsutil/ws_version_info.h>
+#include <ws_version_info.h>
 
 #include <wiretap/merge.h>
 
@@ -118,6 +118,8 @@ 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);
+static match_result match_regex(capture_file *cf, frame_data *fdata,
+    void *criterion);
 static match_result match_dfilter(capture_file *cf, frame_data *fdata,
     void *criterion);
 static match_result match_marked(capture_file *cf, frame_data *fdata,
@@ -136,11 +138,12 @@ static void cf_open_failure_alert_box(const char *filename, int err,
 static void cf_rename_failure_alert_box(const char *filename, int err);
 static void cf_close_failure_alert_box(const char *filename, int err);
 static void ref_time_packets(capture_file *cf);
-/* Update the progress bar this many times when reading a file. */
-#define N_PROGBAR_UPDATES   100
-/* We read around 200k/100ms don't update the progress bar more often than that */
-#define MIN_QUANTUM         200000
-#define MIN_NUMBER_OF_PACKET 1500
+
+/* Seconds spent processing packets between pushing UI updates. */
+#define PROGBAR_UPDATE_INTERVAL 0.150
+
+/* Show the progress bar after this many seconds. */
+#define PROGBAR_SHOW_DELAY 0.5
 
 /*
  * We could probably use g_signal_...() instead of the callbacks below but that
@@ -280,6 +283,7 @@ ws_epan_new(capture_file *cf)
   epan->data = cf;
   epan->get_frame_ts = ws_get_frame_ts;
   epan->get_interface_name = cap_file_get_interface_name;
+  epan->get_interface_description = cap_file_get_interface_description;
   epan->get_user_comment = ws_get_user_comment;
 
   return epan;
@@ -358,9 +362,6 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp
   cf->prev_cap = NULL;
   cf->cum_bytes = 0;
 
-  /* Adjust timestamp precision if auto is selected, col width will be adjusted */
-  cf_timestamp_auto_precision(cf);
-  /* XXX needed ? */
   packet_list_queue_draw();
   cf_callback_invoke(cf_cb_file_opened, cf);
 
@@ -486,6 +487,24 @@ cf_close(capture_file *cf)
   cf_callback_invoke(cf_cb_file_closed, cf);
 }
 
+/*
+ * TRUE if the progress dialog doesn't exist and it looks like we'll
+ * take > 2s to load, FALSE otherwise.
+ */
+static inline gboolean
+progress_is_slow(progdlg_t *progdlg, GTimer *prog_timer, gint64 size, gint64 pos)
+{
+  double elapsed;
+
+  if (progdlg) return FALSE;
+  elapsed = g_timer_elapsed(prog_timer, NULL);
+  if ((elapsed / 2 > PROGBAR_SHOW_DELAY && (size / pos) > 2) /* It looks like we're going to be slow. */
+      || elapsed > PROGBAR_SHOW_DELAY) { /* We are indeed slow. */
+    return TRUE;
+  }
+  return FALSE;
+}
+
 static float
 calc_progbar_val(capture_file *cf, gint64 size, gint64 file_pos, gchar *status_str, gulong status_size)
 {
@@ -524,7 +543,8 @@ cf_read(capture_file *cf, gboolean reloading)
   int                  err = 0;
   gchar               *err_info = NULL;
   gchar               *name_ptr;
-  progdlg_t           *progbar        = NULL;
+  progdlg_t           *volatile progbar = NULL;
+  GTimer              *prog_timer = g_timer_new();
   GTimeVal             start_time;
   epan_dissect_t       edt;
   dfilter_t           *dfcode;
@@ -572,8 +592,6 @@ cf_read(capture_file *cf, gboolean reloading)
     gint64  file_pos;
     gint64  data_offset;
 
-    gint64  progbar_quantum;
-    gint64  progbar_nextstep;
     float   progbar_val;
     gchar   status_str[100];
 
@@ -584,26 +602,15 @@ cf_read(capture_file *cf, gboolean reloading)
     /* 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;
+    g_timer_start(prog_timer);
 
     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)) {
+        /* Create the progress bar if necessary. */
+        if (progress_is_slow(progbar, prog_timer, size, file_pos)) {
           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,
@@ -613,19 +620,19 @@ cf_read(capture_file *cf, gboolean reloading)
                 TRUE, &cf->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 */
-            update_progress_dlg(progbar, progbar_val, status_str);
-            packets_bar_update();
-          }
-          progbar_nextstep += progbar_quantum;
+        /*
+         * Update the progress bar, but do it only after
+         * PROGBAR_UPDATE_INTERVAL has elapsed. Calling update_progress_dlg
+         * and packets_bar_update will likely trigger UI paint events, which
+         * might take a while depending on the platform and display. Reset
+         * our timer *after* painting.
+         */
+        if (progbar && g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
+          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 */
+          update_progress_dlg(progbar, progbar_val, status_str);
+          packets_bar_update();
+          g_timer_start(prog_timer);
         }
       }
 
@@ -668,6 +675,7 @@ cf_read(capture_file *cf, gboolean reloading)
   /* We're done reading the file; destroy the progress bar if it was created. */
   if (progbar != NULL)
     destroy_progress_dlg(progbar);
+  g_timer_destroy(prog_timer);
 
   /* We're done reading sequentially through the file. */
   cf->state = FILE_READ_DONE;
@@ -1099,6 +1107,15 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
   if (dfcode != NULL) {
       epan_dissect_prime_dfilter(edt, dfcode);
   }
+#if 0
+  /* Prepare coloring rules, this ensures that display filter rules containing
+   * frame.color_rule references are still processed.
+   * TODO: actually detect that situation or maybe apply other optimizations? */
+  if (edt->tree && color_filters_used()) {
+    color_filters_prime_edt(edt);
+    fdata->flags.need_colorize = 1;
+  }
+#endif
 
   /* Dissect the frame. */
   epan_dissect_run_with_taps(edt, cf->cd_t, phdr, frame_tvbuff_new(fdata, buf), fdata, cinfo);
@@ -1130,19 +1147,7 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     frame_data_set_after_dissect(fdata, &cf->cum_bytes);
     cf->prev_dis = fdata;
 
-    /* If we haven't yet seen the first frame, this is it.
-
-       XXX - we must do this before we add the row to the display,
-       as, if the display's GtkCList's selection mode is
-       GTK_SELECTION_BROWSE, when the first entry is added to it,
-       "cf_select_packet()" will be called, and it will fetch the row
-       data for the 0th row, and will get a null pointer rather than
-       "fdata", as "gtk_clist_append()" won't yet have returned and
-       thus "gtk_clist_set_row_data()" won't yet have been called.
-
-       We thus need to leave behind bread crumbs so that
-       "cf_select_packet()" can find this frame.  See the comment
-       in "cf_select_packet()". */
+    /* If we haven't yet seen the first frame, this is it. */
     if (cf->first_displayed == 0)
       cf->first_displayed = fdata->num;
 
@@ -1213,11 +1218,11 @@ read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
 
 
 typedef struct _callback_data_t {
+  gpointer         pd_window;
   gint64           f_len;
-  gint64           progbar_nextstep;
-  gint64           progbar_quantum;
   GTimeVal         start_time;
   progdlg_t       *progbar;
+  GTimer          *prog_timer;
   gboolean         stop_flag;
 } callback_data_t;
 
@@ -1247,9 +1252,8 @@ merge_callback(merge_event event, int num _U_,
       for (i = 0; i < in_file_count; i++)
         cb_data->f_len += in_files[i].size;
 
-      /* When we reach the value that triggers a progress bar update,
-         bump that value by this amount. */
-      cb_data->progbar_quantum = cb_data->f_len / N_PROGBAR_UPDATES;
+      cb_data->prog_timer = g_timer_new();
+      g_timer_start(cb_data->prog_timer);
 
       g_get_current_time(&cb_data->start_time);
       break;
@@ -1269,16 +1273,18 @@ merge_callback(merge_event event, int num _U_,
            large file, we might take considerably longer than that standard
            time in order to get to the next progress bar step). */
         if (cb_data->progbar == NULL) {
-          cb_data->progbar = delayed_create_progress_dlg(NULL, "Merging", "files",
+          cb_data->progbar = delayed_create_progress_dlg(cb_data->pd_window, "Merging", "files",
             FALSE, &cb_data->stop_flag, &cb_data->start_time, 0.0f);
         }
 
-        /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
-           when we update it, we have to run the GTK+ main loop to get it
-           to repaint what's pending, and doing so may involve an "ioctl()"
-           to see if there's any pending input from an X server, and doing
-           that for every packet can be costly, especially on a big file. */
-        if (data_offset >= cb_data->progbar_nextstep) {
+        /*
+         * Update the progress bar, but do it only after
+         * PROGBAR_UPDATE_INTERVAL has elapsed. Calling update_progress_dlg
+         * and packets_bar_update will likely trigger UI paint events, which
+         * might take a while depending on the platform and display. Reset
+         * our timer *after* painting.
+         */
+        if (g_timer_elapsed(cb_data->prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
             float  progbar_val;
             gint64 file_pos = 0;
             /* Get the sum of the seek positions in all of the files. */
@@ -1300,7 +1306,7 @@ merge_callback(merge_event event, int num _U_,
                          file_pos / 1024, cb_data->f_len / 1024);
               update_progress_dlg(cb_data->progbar, progbar_val, status_str);
             }
-            cb_data->progbar_nextstep += cb_data->progbar_quantum;
+            g_timer_start(cb_data->prog_timer);
         }
       }
       break;
@@ -1309,6 +1315,7 @@ merge_callback(merge_event event, int num _U_,
       /* We're done merging the files; destroy the progress bar if it was created. */
       if (cb_data->progbar != NULL)
         destroy_progress_dlg(cb_data->progbar);
+      g_timer_destroy(cb_data->prog_timer);
       break;
   }
 
@@ -1318,45 +1325,31 @@ merge_callback(merge_event event, int num _U_,
 
 
 cf_status_t
-cf_merge_files(char **out_filenamep, int in_file_count,
-               char *const *in_filenames, int file_type, gboolean do_append)
+cf_merge_files_to_tempfile(gpointer pd_window, char **out_filenamep,
+                           int in_file_count, char *const *in_filenames,
+                           int file_type, gboolean do_append)
 {
-  char                      *out_filename;
-  char                      *tmpname;
-  int                        out_fd;
   int                        err      = 0;
   gchar                     *err_info = NULL;
-  int                        err_fileno;
+  guint                      err_fileno;
   merge_result               status;
   merge_progress_callback_t  cb;
-
-
-  if (*out_filenamep != NULL) {
-    out_filename = *out_filenamep;
-    out_fd = ws_open(out_filename, O_CREAT|O_TRUNC|O_BINARY, 0600);
-    if (out_fd == -1)
-      err = errno;
-  } else {
-    out_fd = create_tempfile(&tmpname, "wireshark");
-    if (out_fd == -1)
-      err = errno;
-    out_filename = g_strdup(tmpname);
-    *out_filenamep = out_filename;
-  }
-  if (out_fd == -1) {
-    cf_open_failure_alert_box(out_filename, err, NULL, TRUE, file_type);
-    return CF_ERROR;
-  }
+  callback_data_t           *cb_data = g_new0(callback_data_t, 1);
 
   /* prepare our callback routine */
+  cb_data->pd_window = pd_window;
   cb.callback_func = merge_callback;
-  cb.data = g_malloc0(sizeof(callback_data_t));
+  cb.data = cb_data;
+
+  cf_callback_invoke(cf_cb_file_merge_started, NULL);
 
   /* merge the files */
-  status = merge_files(out_fd, out_filename, file_type,
-                       (const char *const *) in_filenames, in_file_count,
-                       do_append, IDB_MERGE_MODE_ALL_SAME, 0 /* snaplen */,
-                       "Wireshark", &cb, &err, &err_info, &err_fileno);
+  status = merge_files_to_tempfile(out_filenamep, "wireshark", file_type,
+                                   (const char *const *) in_filenames,
+                                   in_file_count, do_append,
+                                   IDB_MERGE_MODE_ALL_SAME, 0 /* snaplen */,
+                                   "Wireshark", &cb, &err, &err_info,
+                                   &err_fileno);
 
   g_free(cb.data);
 
@@ -1371,13 +1364,11 @@ cf_merge_files(char **out_filenamep, int in_file_count,
     case MERGE_ERR_CANT_OPEN_INFILE:
       cf_open_failure_alert_box(in_filenames[err_fileno], err, err_info,
                                 FALSE, 0);
-      ws_close(out_fd);
       break;
 
     case MERGE_ERR_CANT_OPEN_OUTFILE:
-      cf_open_failure_alert_box(out_filename, err, err_info, TRUE,
+      cf_open_failure_alert_box(*out_filenamep, err, err_info, TRUE,
                                 file_type);
-      ws_close(out_fd);
       break;
 
     case MERGE_ERR_CANT_READ_INFILE:      /* fall through */
@@ -1390,8 +1381,8 @@ cf_merge_files(char **out_filenamep, int in_file_count,
   }
 
   g_free(err_info);
-  /* for general case, no need to close out_fd: file handle associated to this file
-     descriptor was already closed by the call to wtap_dump_close() in merge_files() */
+
+  cf_callback_invoke(cf_cb_file_merge_finished, NULL);
 
   if (status != MERGE_OK) {
     /* Callers aren't expected to treat an error or an explicit abort
@@ -1556,6 +1547,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
   guint32     framenum;
   frame_data *fdata;
   progdlg_t  *progbar = NULL;
+  GTimer     *prog_timer = g_timer_new();
   int         count;
   frame_data *selected_frame, *preceding_frame, *following_frame, *prev_frame;
   int         selected_frame_num, preceding_frame_num, following_frame_num, prev_frame_num;
@@ -1563,8 +1555,6 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
   float       progbar_val;
   GTimeVal    start_time;
   gchar       status_str[100];
-  int         progbar_nextstep;
-  int         progbar_quantum;
   epan_dissect_t  edt;
   dfilter_t  *dfcode;
   column_info *cinfo;
@@ -1622,6 +1612,12 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
     cf->epan = ws_epan_new(cf);
     cf->cinfo.epan = cf->epan;
 
+    /* A new Lua tap listener may be registered in lua_prime_all_fields()
+       called via epan_new() / init_dissection() when reloading Lua plugins. */
+    if (!create_proto_tree && have_filtering_tap_listeners()) {
+      create_proto_tree = TRUE;
+    }
+
     /* We need to redissect the packets so we have to discard our old
      * packet list store. */
     packet_list_clear();
@@ -1645,11 +1641,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
 
   cf_callback_invoke(cf_cb_file_rescan_started, 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;
+  g_timer_start(prog_timer);
   /* Count of packets at which we've looked. */
   count = 0;
   /* Progress so far. */
@@ -1687,12 +1679,13 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
                                             &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 (count >= progbar_nextstep) {
+    /*
+     * Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
+     * has elapsed. Calling update_progress_dlg and packets_bar_update will
+     * likely trigger UI paint events, which might take a while depending on
+     * the platform and display. Reset our timer *after* painting.
+     */
+    if (g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
       /* let's not divide by zero. I should never be started
        * with count == 0, so let's assert that
        */
@@ -1705,7 +1698,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
         update_progress_dlg(progbar, progbar_val, status_str);
       }
 
-      progbar_nextstep += progbar_quantum;
+      g_timer_start(prog_timer);
     }
 
     if (cf->stop_flag) {
@@ -1800,6 +1793,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
      was created. */
   if (progbar != NULL)
     destroy_progress_dlg(progbar);
+  g_timer_destroy(prog_timer);
 
   /* Unfreeze the packet list. */
   if (!add_to_packet_list)
@@ -1877,7 +1871,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
 
 
 /*
- * Scan trough all frame data and recalculate the ref time
+ * Scan through all frame data and recalculate the ref time
  * without rereading the file.
  * XXX - do we need a progres bar or is this fast enough?
  */
@@ -1969,7 +1963,8 @@ process_specified_records(capture_file *cf, packet_range_t *range,
     const char *string1, const char *string2, gboolean terminate_is_stop,
     gboolean (*callback)(capture_file *, frame_data *,
                          struct wtap_pkthdr *, const guint8 *, void *),
-    void *callback_args)
+    void *callback_args,
+    gboolean show_progress_bar)
 {
   guint32          framenum;
   frame_data      *fdata;
@@ -1977,23 +1972,18 @@ process_specified_records(capture_file *cf, packet_range_t *range,
   psp_return_t     ret     = PSP_FINISHED;
 
   progdlg_t       *progbar = NULL;
+  GTimer          *prog_timer = g_timer_new();
   int              progbar_count;
   float            progbar_val;
   GTimeVal         progbar_start_time;
   gchar            progbar_status_str[100];
-  int              progbar_nextstep;
-  int              progbar_quantum;
   range_process_e  process_this;
   struct wtap_pkthdr phdr;
 
   wtap_phdr_init(&phdr);
   ws_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,
-     bump that value by this amount. */
-  progbar_quantum = cf->count/N_PROGBAR_UPDATES;
+  g_timer_start(prog_timer);
   /* Count of packets at which we've looked. */
   progbar_count = 0;
   /* Progress so far. */
@@ -2015,32 +2005,31 @@ process_specified_records(capture_file *cf, packet_range_t *range,
        longer than the standard time to create it (otherwise, for a
        large file, we might take considerably longer than that standard
        time in order to get to the next progress bar step). */
-    if (progbar == NULL)
+    if (show_progress_bar && progbar == NULL)
       progbar = delayed_create_progress_dlg(cf->window, string1, string2,
                                             terminate_is_stop,
                                             &cf->stop_flag,
                                             &progbar_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 (progbar_count >= progbar_nextstep) {
+    /*
+     * Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
+     * has elapsed. Calling update_progress_dlg and packets_bar_update will
+     * likely trigger UI paint events, which might take a while depending on
+     * the platform and display. Reset our timer *after* painting.
+     */
+    if (progbar && g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
       /* let's not divide by zero. I should never be started
        * with count == 0, so let's assert that
        */
       g_assert(cf->count > 0);
       progbar_val = (gfloat) progbar_count / cf->count;
 
-      if (progbar != NULL) {
-        g_snprintf(progbar_status_str, sizeof(progbar_status_str),
-                   "%4u of %u packets", progbar_count, cf->count);
-        update_progress_dlg(progbar, progbar_val, progbar_status_str);
-      }
+      g_snprintf(progbar_status_str, sizeof(progbar_status_str),
+                  "%4u of %u packets", progbar_count, cf->count);
+      update_progress_dlg(progbar, progbar_val, progbar_status_str);
 
-      progbar_nextstep += progbar_quantum;
+      g_timer_start(prog_timer);
     }
 
     if (cf->stop_flag) {
@@ -2083,6 +2072,7 @@ process_specified_records(capture_file *cf, packet_range_t *range,
      it was created. */
   if (progbar != NULL)
     destroy_progress_dlg(progbar);
+  g_timer_destroy(prog_timer);
 
   wtap_phdr_cleanup(&phdr);
   ws_buffer_free(&buf);
@@ -2150,7 +2140,7 @@ cf_retap_packets(capture_file *cf)
 
   ret = process_specified_records(cf, &range, "Recalculating statistics on",
                                   "all packets", TRUE, retap_packet,
-                                  &callback_args);
+                                  &callback_args, TRUE);
 
   epan_dissect_cleanup(&callback_args.edt);
 
@@ -2339,7 +2329,8 @@ fail:
 }
 
 cf_print_status_t
-cf_print_packets(capture_file *cf, print_args_t *print_args)
+cf_print_packets(capture_file *cf, print_args_t *print_args,
+                 gboolean show_progress_bar)
 {
   print_callback_args_t callback_args;
   gint          data_width;
@@ -2460,14 +2451,14 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
   proto_tree_needed =
       callback_args.print_args->print_dissections != print_dissections_none ||
       callback_args.print_args->print_hex ||
-      have_custom_cols(&cf->cinfo);
+      have_custom_cols(&cf->cinfo) || have_field_extractors();
   epan_dissect_init(&callback_args.edt, cf->epan, proto_tree_needed, proto_tree_needed);
 
   /* Iterate through the list of packets, printing the packets we were
      told to print. */
   ret = process_specified_records(cf, &print_args->range, "Printing",
                                   "selected packets", TRUE, print_packet,
-                                  &callback_args);
+                                  &callback_args, show_progress_bar);
   epan_dissect_cleanup(&callback_args.edt);
   g_free(callback_args.header_line_buf);
   g_free(callback_args.line_buf);
@@ -2514,6 +2505,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args)
 typedef struct {
   FILE *fh;
   epan_dissect_t edt;
+  print_args_t *print_args;
 } write_packet_callback_args_t;
 
 static gboolean
@@ -2527,7 +2519,7 @@ write_pdml_packet(capture_file *cf, frame_data *fdata,
   epan_dissect_run(&args->edt, cf->cd_t, phdr, frame_tvbuff_new(fdata, pd), fdata, NULL);
 
   /* Write out the information in that tree. */
-  write_pdml_proto_tree(&args->edt, args->fh);
+  write_pdml_proto_tree(NULL, NULL, PF_NONE, &args->edt, args->fh);
 
   epan_dissect_reset(&args->edt);
 
@@ -2552,13 +2544,14 @@ cf_write_pdml_packets(capture_file *cf, print_args_t *print_args)
   }
 
   callback_args.fh = fh;
+  callback_args.print_args = print_args;
   epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
 
   /* Iterate through the list of packets, printing the packets we were
      told to print. */
   ret = process_specified_records(cf, &print_args->range, "Writing PDML",
                                   "selected packets", TRUE,
-                                  write_pdml_packet, &callback_args);
+                                  write_pdml_packet, &callback_args, TRUE);
 
   epan_dissect_cleanup(&callback_args.edt);
 
@@ -2630,17 +2623,18 @@ cf_write_psml_packets(capture_file *cf, print_args_t *print_args)
   }
 
   callback_args.fh = fh;
+  callback_args.print_args = print_args;
 
   /* Fill in the column information, only create the protocol tree
-     if having custom columns. */
-  proto_tree_needed = have_custom_cols(&cf->cinfo);
+     if having custom columns or field extractors. */
+  proto_tree_needed = have_custom_cols(&cf->cinfo) || have_field_extractors();
   epan_dissect_init(&callback_args.edt, cf->epan, proto_tree_needed, proto_tree_needed);
 
   /* Iterate through the list of packets, printing the packets we were
      told to print. */
   ret = process_specified_records(cf, &print_args->range, "Writing PSML",
                                   "selected packets", TRUE,
-                                  write_psml_packet, &callback_args);
+                                  write_psml_packet, &callback_args, TRUE);
 
   epan_dissect_cleanup(&callback_args.edt);
 
@@ -2711,16 +2705,17 @@ cf_write_csv_packets(capture_file *cf, print_args_t *print_args)
   }
 
   callback_args.fh = fh;
+  callback_args.print_args = print_args;
 
-  /* only create the protocol tree if having custom columns. */
-  proto_tree_needed = have_custom_cols(&cf->cinfo);
+  /* only create the protocol tree if having custom columns or field extractors. */
+  proto_tree_needed = have_custom_cols(&cf->cinfo) || have_field_extractors();
   epan_dissect_init(&callback_args.edt, cf->epan, proto_tree_needed, proto_tree_needed);
 
   /* Iterate through the list of packets, printing the packets we were
      told to print. */
   ret = process_specified_records(cf, &print_args->range, "Writing CSV",
                                   "selected packets", TRUE,
-                                  write_csv_packet, &callback_args);
+                                  write_csv_packet, &callback_args, TRUE);
 
   epan_dissect_cleanup(&callback_args.edt);
 
@@ -2778,31 +2773,107 @@ cf_write_carrays_packets(capture_file *cf, print_args_t *print_args)
   }
 
   callback_args.fh = fh;
+  callback_args.print_args = print_args;
   epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
 
   /* Iterate through the list of packets, printing the packets we were
      told to print. */
   ret = process_specified_records(cf, &print_args->range,
-                  "Writing C Arrays",
-                  "selected packets", TRUE,
-                                  carrays_write_packet, &callback_args);
+                                  "Writing C Arrays",
+                                  "selected packets", TRUE,
+                                  carrays_write_packet, &callback_args, TRUE);
+
+  epan_dissect_cleanup(&callback_args.edt);
+
+  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 CF_PRINT_WRITE_ERROR;
+  }
+
+  fclose(fh);
+  return CF_PRINT_OK;
+}
+
+static gboolean
+write_json_packet(capture_file *cf, frame_data *fdata,
+                  struct wtap_pkthdr *phdr, const guint8 *pd,
+          void *argsp)
+{
+  write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
+
+  /* Create the protocol tree, but don't fill in the column information. */
+  epan_dissect_run(&args->edt, cf->cd_t, phdr, frame_tvbuff_new(fdata, pd), fdata, NULL);
+
+  /* Write out the information in that tree. */
+  write_json_proto_tree(NULL, args->print_args, NULL, PF_NONE, &args->edt, args->fh);
+
+  epan_dissect_reset(&args->edt);
+
+  return !ferror(args->fh);
+}
+
+cf_print_status_t
+cf_write_json_packets(capture_file *cf, print_args_t *print_args)
+{
+  write_packet_callback_args_t callback_args;
+  FILE         *fh;
+  psp_return_t  ret;
+
+  fh = ws_fopen(print_args->file, "w");
+  if (fh == NULL)
+    return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
+
+  write_json_preamble(fh);
+  if (ferror(fh)) {
+    fclose(fh);
+    return CF_PRINT_WRITE_ERROR;
+  }
+
+  callback_args.fh = fh;
+  callback_args.print_args = print_args;
+  epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
+
+  /* Iterate through the list of packets, printing the packets we were
+     told to print. */
+  ret = process_specified_records(cf, &print_args->range, "Writing PDML",
+                                  "selected packets", TRUE,
+                                  write_json_packet, &callback_args, TRUE);
 
   epan_dissect_cleanup(&callback_args.edt);
 
   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 CF_PRINT_WRITE_ERROR;
   }
 
+  write_json_finale(fh);
+  if (ferror(fh)) {
+    fclose(fh);
+    return CF_PRINT_WRITE_ERROR;
+  }
+
+  /* XXX - check for an error */
   fclose(fh);
+
   return CF_PRINT_OK;
 }
 
@@ -2890,22 +2961,30 @@ match_subtree_text(proto_node *node, gpointer data)
     proto_item_fill_label(fi, label_str);
   }
 
-  /* Does that label match? */
-  label_len = strlen(label_ptr);
-  for (i = 0; i < label_len; i++) {
-    c_char = label_ptr[i];
-    if (cf->case_type)
-      c_char = g_ascii_toupper(c_char);
-    if (c_char == string[c_match]) {
-      c_match++;
-      if (c_match == string_len) {
-        /* No need to look further; we have a match */
-        mdata->frame_matched = TRUE;
-        mdata->finfo = fi;
-        return;
-      }
-    } else
-      c_match = 0;
+  if (cf->regex) {
+    if (g_regex_match(cf->regex, label_ptr, (GRegexMatchFlags) 0, NULL)) {
+      mdata->frame_matched = TRUE;
+      mdata->finfo = fi;
+      return;
+    }
+  } else {
+    /* Does that label match? */
+    label_len = strlen(label_ptr);
+    for (i = 0; i < label_len; i++) {
+      c_char = label_ptr[i];
+      if (cf->case_type)
+        c_char = g_ascii_toupper(c_char);
+      if (c_char == string[c_match]) {
+        c_match++;
+        if (c_match == string_len) {
+          /* No need to look further; we have a match */
+          mdata->frame_matched = TRUE;
+          mdata->finfo = fi;
+          return;
+        }
+      } else
+        c_match = 0;
+    }
   }
 
   /* Recurse into the subtree, if it exists */
@@ -2957,18 +3036,25 @@ match_summary_line(capture_file *cf, frame_data *fdata, void *criterion)
       /* Found it.  See if we match. */
       info_column = edt.pi.cinfo->columns[colx].col_data;
       info_column_len = strlen(info_column);
-      for (i = 0; i < info_column_len; i++) {
-        c_char = info_column[i];
-        if (cf->case_type)
-          c_char = g_ascii_toupper(c_char);
-        if (c_char == string[c_match]) {
-          c_match++;
-          if (c_match == string_len) {
-            result = MR_MATCHED;
-            break;
-          }
-        } else
-          c_match = 0;
+      if (cf->regex) {
+        if (g_regex_match(cf->regex, info_column, (GRegexMatchFlags) 0, NULL)) {
+          result = MR_MATCHED;
+          break;
+        }
+      } else {
+        for (i = 0; i < info_column_len; i++) {
+          c_char = info_column[i];
+          if (cf->case_type)
+            c_char = g_ascii_toupper(c_char);
+          if (c_char == string[c_match]) {
+            c_match++;
+            if (c_match == string_len) {
+              result = MR_MATCHED;
+              break;
+            }
+          } else
+            c_match = 0;
+        }
       }
       break;
     }
@@ -3002,8 +3088,11 @@ cf_find_packet_data(capture_file *cf, const guint8 *string, size_t string_size,
   info.data = string;
   info.data_len = string_size;
 
-  /* String or hex search? */
-  if (cf->string) {
+  /* Regex, String or hex search? */
+  if (cf->regex) {
+    /* Regular Expression search */
+    return find_packet(cf, match_regex, NULL, dir);
+  } else if (cf->string) {
     /* String search - what type of string? */
     switch (cf->scs_type) {
 
@@ -3058,6 +3147,7 @@ match_narrow_and_wide(capture_file *cf, frame_data *fdata, void *criterion)
           result = MR_MATCHED;
           cf->search_pos = i; /* Save the position of the last character
                                  for highlighting the field. */
+          cf->search_len = (guint32)textlen;
           break;
         }
       }
@@ -3105,6 +3195,7 @@ match_narrow(capture_file *cf, frame_data *fdata, void *criterion)
         result = MR_MATCHED;
         cf->search_pos = i; /* Save the position of the last character
                                for highlighting the field. */
+        cf->search_len = (guint32)textlen;
         break;
       }
     }
@@ -3152,6 +3243,7 @@ match_wide(capture_file *cf, frame_data *fdata, void *criterion)
         result = MR_MATCHED;
         cf->search_pos = i; /* Save the position of the last character
                                for highlighting the field. */
+        cf->search_len = (guint32)textlen;
         break;
       }
       i += 1;
@@ -3195,6 +3287,7 @@ match_binary(capture_file *cf, frame_data *fdata, void *criterion)
         result = MR_MATCHED;
         cf->search_pos = i; /* Save the position of the last character
                                for highlighting the field. */
+        cf->search_len = (guint32)datalen;
         break;
       }
     }
@@ -3208,6 +3301,30 @@ match_binary(capture_file *cf, frame_data *fdata, void *criterion)
   return result;
 }
 
+static match_result
+match_regex(capture_file *cf, frame_data *fdata, void *criterion _U_)
+{
+    match_result  result = MR_NOTMATCHED;
+    GMatchInfo   *match_info = NULL;
+
+    /* Load the frame's data. */
+    if (!cf_read_record(cf, fdata)) {
+        /* Attempt to get the packet failed. */
+        return MR_ERROR;
+    }
+
+    if (g_regex_match_full(cf->regex, (const gchar *)ws_buffer_start_ptr(&cf->buf), fdata->cap_len,
+                           0, (GRegexMatchFlags) 0, &match_info, NULL))
+    {
+        gint start_pos = 0, end_pos = 0;
+        g_match_info_fetch_pos (match_info, 0, &start_pos, &end_pos);
+        cf->search_pos = end_pos - 1;
+        cf->search_len = end_pos - start_pos;
+        result = MR_MATCHED;
+    }
+    return result;
+}
+
 gboolean
 cf_find_packet_dfilter(capture_file *cf, dfilter_t *sfcode,
                        search_direction dir)
@@ -3296,13 +3413,12 @@ find_packet(capture_file *cf,
   frame_data  *fdata;
   frame_data  *new_fd = NULL;
   progdlg_t   *progbar = NULL;
+  GTimer      *prog_timer = g_timer_new();
   int          count;
   gboolean     found;
   float        progbar_val;
   GTimeVal     start_time;
   gchar        status_str[100];
-  int          progbar_nextstep;
-  int          progbar_quantum;
   const char  *title;
   match_result result;
 
@@ -3314,11 +3430,7 @@ find_packet(capture_file *cf,
     count = 0;
     framenum = start_fd->num;
 
-    /* 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;
+    g_timer_start(prog_timer);
     /* Progress so far. */
     progbar_val = 0.0f;
 
@@ -3336,12 +3448,13 @@ find_packet(capture_file *cf,
          progbar = delayed_create_progress_dlg(cf->window, "Searching", title,
            FALSE, &cf->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 (count >= progbar_nextstep) {
+      /*
+       * Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
+       * has elapsed. Calling update_progress_dlg and packets_bar_update will
+       * likely trigger UI paint events, which might take a while depending on
+       * the platform and display. Reset our timer *after* painting.
+       */
+      if (g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
         /* let's not divide by zero. I should never be started
          * with count == 0, so let's assert that
          */
@@ -3349,13 +3462,11 @@ find_packet(capture_file *cf,
 
         progbar_val = (gfloat) count / cf->count;
 
-        if (progbar != NULL) {
-          g_snprintf(status_str, sizeof(status_str),
-                     "%4u of %u packets", count, cf->count);
-          update_progress_dlg(progbar, progbar_val, status_str);
-        }
+        g_snprintf(status_str, sizeof(status_str),
+                    "%4u of %u packets", count, cf->count);
+        update_progress_dlg(progbar, progbar_val, status_str);
 
-        progbar_nextstep += progbar_quantum;
+        g_timer_start(prog_timer);
       }
 
       if (cf->stop_flag) {
@@ -3436,6 +3547,7 @@ find_packet(capture_file *cf,
        was created. */
     if (progbar != NULL)
       destroy_progress_dlg(progbar);
+    g_timer_destroy(prog_timer);
   }
 
   if (new_fd != NULL) {
@@ -3444,6 +3556,7 @@ find_packet(capture_file *cf,
     found = packet_list_select_row_from_data(new_fd);
     cf->search_in_progress = FALSE;
     cf->search_pos = 0; /* Reset the position */
+    cf->search_len = 0; /* Reset length */
     if (!found) {
       /* We didn't find a row corresponding to this frame.
          This means that the frame isn't being displayed currently,
@@ -3463,8 +3576,8 @@ cf_goto_frame(capture_file *cf, guint fnumber)
 {
   frame_data *fdata;
 
-  if (cf == NULL) {
-    /* we don't have a loaded capture file - fix for bug 11810*/
+  if (cf == NULL || cf->frames == NULL) {
+    /* we don't have a loaded capture file - fix for bugs 11810 & 11989 */
     statusbar_push_temporary_msg("There is no file loaded");
     return FALSE;   /* we failed to go to that packet */
   }
@@ -3495,22 +3608,6 @@ cf_goto_frame(capture_file *cf, guint fnumber)
   return TRUE;  /* we got to that packet */
 }
 
-gboolean
-cf_goto_top_frame(void)
-{
-  /* Find and select */
-  packet_list_select_first_row();
-  return TRUE;  /* we got to that packet */
-}
-
-gboolean
-cf_goto_bottom_frame(void)
-{
-  /* Find and select */
-  packet_list_select_last_row();
-  return TRUE;  /* we got to that packet */
-}
-
 /*
  * Go to frame specified by currently selected protocol tree item.
  */
@@ -3543,38 +3640,6 @@ cf_select_packet(capture_file *cf, int row)
   /* Get the frame data struct pointer for this frame */
   fdata = packet_list_get_row_data(row);
 
-  if (fdata == NULL) {
-    /* 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 "ui/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 "cf_select_packet()"
-       to be called.
-
-       "cf_select_packet()" fetches, above, the data associated with the
-       row that was selected; however, as "gtk_clist_append()", which
-       called "real_insert_row()", hasn't yet returned, we haven't yet
-       associated any data with that row, so we get back a null pointer.
-
-       We can't assume that there's only one frame in the frame list,
-       either, as we may be filtering the display.
-
-       We therefore assume that, if "row" is 0, i.e. the first row
-       is being selected, and "cf->first_displayed" equals
-       "cf->last_displayed", i.e. there's only one frame being
-       displayed, that frame is the frame we want.
-
-       This means we have to set "cf->first_displayed" and
-       "cf->last_displayed" before adding the row to the
-       GtkCList; see the comment in "add_packet_to_packet_list()". */
-
-       if (row == 0 && cf->first_displayed == cf->last_displayed)
-         fdata = frame_data_sequence_find(cf->frames, cf->first_displayed);
-  }
-
-  /* If fdata _still_ isn't set simply give up. */
   if (fdata == NULL) {
     return;
   }
@@ -3696,28 +3761,45 @@ cf_unignore_frame(capture_file *cf, frame_data *frame)
 const gchar *
 cf_read_shb_comment(capture_file *cf)
 {
-  /* Get info from SHB */
-  return wtap_file_get_shb_comment(cf->wth);
+  wtap_block_t shb_inf;
+  char *shb_comment;
+
+  /* Get the SHB. */
+  /* XXX - support multiple SHBs */
+  shb_inf = wtap_file_get_shb(cf->wth);
+
+  /* Get the first comment from the SHB. */
+  /* XXX - support multiple comments */
+  if (wtap_block_get_nth_string_option_value(shb_inf, OPT_COMMENT, 0, &shb_comment) != WTAP_OPTTYPE_SUCCESS)
+    return NULL;
+  return shb_comment;
 }
 
 void
 cf_update_capture_comment(capture_file *cf, gchar *comment)
 {
-  const gchar *shb_comment;
-
-  /* Get info from SHB */
-  shb_comment = wtap_file_get_shb_comment(cf->wth);
-
-  /* See if the comment has changed or not */
-  if (shb_comment) {
+  wtap_block_t shb_inf;
+  gchar *shb_comment;
+
+  /* Get the SHB. */
+  /* XXX - support multiple SHBs */
+  shb_inf = wtap_file_get_shb(cf->wth);
+
+  /* Get the first comment from the SHB. */
+  /* XXX - support multiple comments */
+  if (wtap_block_get_nth_string_option_value(shb_inf, OPT_COMMENT, 0, &shb_comment) != WTAP_OPTTYPE_SUCCESS) {
+    /* There's no comment - add one. */
+    wtap_block_add_string_option(shb_inf, OPT_COMMENT, comment, strlen(comment));
+  } else {
+    /* See if the comment has changed or not */
     if (strcmp(shb_comment, comment) == 0) {
       g_free(comment);
       return;
     }
-  }
 
-  /* The comment has changed, let's update it */
-  wtap_write_shb_comment(cf->wth, comment);
+    /* The comment has changed, let's update it */
+    wtap_block_set_nth_string_option_value(shb_inf, OPT_COMMENT, 0, comment, strlen(comment));
+  }
   /* Mark the file as having unsaved changes */
   cf->unsaved_changes = TRUE;
 }
@@ -3819,6 +3901,24 @@ cf_comment_types(capture_file *cf)
   return comment_types;
 }
 
+/*
+ * Add a resolved address to this file's list of resolved addresses.
+ */
+gboolean
+cf_add_ip_name_from_string(capture_file *cf, const char *addr, const char *name)
+{
+  /*
+   * XXX - support multiple resolved address lists, and add to the one
+   * attached to this file?
+   */
+  if (!add_ip_name_from_string(addr, name))
+    return FALSE;
+
+  /* OK, we have unsaved changes. */
+  cf->unsaved_changes = TRUE;
+  return TRUE;
+}
+
 #ifdef WANT_PACKET_EDITOR
 static gint
 g_direct_compare_func(gconstpointer a, gconstpointer b, gpointer user_data _U_)
@@ -3850,7 +3950,7 @@ cf_set_frame_edited(capture_file *cf, frame_data *fd,
   modified_frame_data *mfd = (modified_frame_data *)g_malloc(sizeof(modified_frame_data));
 
   mfd->phdr = *phdr;
-  mfd->pd = pd;
+  mfd->pd = (char *)pd;
 
   if (cf->edited_frames == NULL)
     cf->edited_frames = g_tree_new_full(g_direct_compare_func, NULL, NULL,
@@ -3914,11 +4014,10 @@ save_record(capture_file *cf, frame_data *fdata,
     hdr.presence_flags |= WTAP_HAS_INTERFACE_ID;
   if (phdr->presence_flags & WTAP_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.ts           = phdr->ts;
   hdr.caplen       = phdr->caplen;
   hdr.len          = phdr->len;
-  hdr.pkt_encap    = fdata->lnk_t;
+  hdr.pkt_encap    = phdr->pkt_encap;
   /* pcapng */
   hdr.interface_id = phdr->interface_id;   /* identifier of the interface. */
   /* options */
@@ -4118,12 +4217,11 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
   gchar               *name_ptr;
   gint64               data_offset;
   progdlg_t           *progbar        = NULL;
+  GTimer              *prog_timer = g_timer_new();
   gint64               size;
   float                progbar_val;
   GTimeVal             start_time;
   gchar                status_str[100];
-  gint64               progbar_nextstep;
-  gint64               progbar_quantum;
   guint32              framenum;
   frame_data          *fdata;
   int                  count          = 0;
@@ -4180,16 +4278,7 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
   /* 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;
+  g_timer_start(prog_timer);
 
   cf->stop_flag = FALSE;
   g_get_current_time(&start_time);
@@ -4204,28 +4293,25 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
       count++;
       cf->f_datalen = 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)) {
+      /* Create the progress bar if necessary. */
+      if (progress_is_slow(progbar, prog_timer, size, cf->f_datalen)) {
         progbar_val = calc_progbar_val(cf, size, cf->f_datalen, status_str, sizeof(status_str));
         progbar = delayed_create_progress_dlg(cf->window, "Rescanning", name_ptr,
                                               TRUE, &cf->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 (cf->f_datalen >= progbar_nextstep) {
-        if (progbar != NULL) {
-          progbar_val = calc_progbar_val(cf, size, cf->f_datalen, status_str, sizeof(status_str));
-          /* update the packet bar content on the first run or frequently on very large files */
-          update_progress_dlg(progbar, progbar_val, status_str);
-          packets_bar_update();
-        }
-        progbar_nextstep += progbar_quantum;
+      /*
+       * Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
+       * has elapsed. Calling update_progress_dlg and packets_bar_update will
+       * likely trigger UI paint events, which might take a while depending on
+       * the platform and display. Reset our timer *after* painting.
+       */
+      if (progbar && g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
+        progbar_val = calc_progbar_val(cf, size, cf->f_datalen, status_str, sizeof(status_str));
+        /* update the packet bar content on the first run or frequently on very large files */
+        update_progress_dlg(progbar, progbar_val, status_str);
+        packets_bar_update();
+        g_timer_start(prog_timer);
       }
     }
 
@@ -4251,6 +4337,7 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
   /* We're done reading the file; destroy the progress bar if it was created. */
   if (progbar != NULL)
     destroy_progress_dlg(progbar);
+  g_timer_destroy(prog_timer);
 
   /* We're done reading sequentially through the file. */
   cf->state = FILE_READ_DONE;
@@ -4427,15 +4514,15 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
        or moving the capture file, we have to do it by writing the packets
        out in Wiretap. */
 
-    wtapng_section_t            *shb_hdr = NULL;
+    GArray                      *shb_hdrs = NULL;
     wtapng_iface_descriptions_t *idb_inf = NULL;
-    wtapng_name_res_t           *nrb_hdr = NULL;
+    GArray                      *nrb_hdrs = NULL;
     int encap;
 
     /* XXX: what free's this shb_hdr? */
-    shb_hdr = wtap_file_get_shb_for_new_file(cf->wth);
+    shb_hdrs = wtap_file_get_shb_for_new_file(cf->wth);
     idb_inf = wtap_file_get_idb_info(cf->wth);
-    nrb_hdr = wtap_file_get_nrb_for_new_file(cf->wth);
+    nrb_hdrs = wtap_file_get_nrb_for_new_file(cf->wth);
 
     /* Determine what file encapsulation type we should use. */
     encap = wtap_dump_file_encap_type(cf->linktypes);
@@ -4450,10 +4537,10 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
          from which we're reading the packets that we're writing!) */
       fname_new = g_strdup_printf("%s~", fname);
       pdh = wtap_dump_open_ng(fname_new, save_format, encap, cf->snap,
-                              compressed, shb_hdr, idb_inf, nrb_hdr, &err);
+                              compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
     } else {
       pdh = wtap_dump_open_ng(fname, save_format, encap, cf->snap,
-                              compressed, shb_hdr, idb_inf, nrb_hdr, &err);
+                              compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
     }
     g_free(idb_inf);
     idb_inf = NULL;
@@ -4471,7 +4558,7 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
     callback_args.fname = fname;
     callback_args.file_type = save_format;
     switch (process_specified_records(cf, NULL, "Saving", "packets",
-                                      TRUE, save_record, &callback_args)) {
+                                      TRUE, save_record, &callback_args, TRUE)) {
 
     case PSP_FINISHED:
       /* Completed successfully. */
@@ -4650,9 +4737,9 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
   int                          err;
   wtap_dumper                 *pdh;
   save_callback_args_t         callback_args;
-  wtapng_section_t            *shb_hdr = NULL;
+  GArray                      *shb_hdrs = NULL;
   wtapng_iface_descriptions_t *idb_inf = NULL;
-  wtapng_name_res_t           *nrb_hdr = NULL;
+  GArray                      *nrb_hdrs = NULL;
   int                          encap;
 
   cf_callback_invoke(cf_cb_file_export_specified_packets_started, (gpointer)fname);
@@ -4665,9 +4752,9 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
      and then write it out if it's one of the specified ones. */
 
   /* XXX: what free's this shb_hdr? */
-  shb_hdr = wtap_file_get_shb_for_new_file(cf->wth);
+  shb_hdrs = wtap_file_get_shb_for_new_file(cf->wth);
   idb_inf = wtap_file_get_idb_info(cf->wth);
-  nrb_hdr = wtap_file_get_nrb_for_new_file(cf->wth);
+  nrb_hdrs = wtap_file_get_nrb_for_new_file(cf->wth);
 
   /* Determine what file encapsulation type we should use. */
   encap = wtap_dump_file_encap_type(cf->linktypes);
@@ -4682,10 +4769,10 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
        from which we're reading the packets that we're writing!) */
     fname_new = g_strdup_printf("%s~", fname);
     pdh = wtap_dump_open_ng(fname_new, save_format, encap, cf->snap,
-                            compressed, shb_hdr, idb_inf, nrb_hdr, &err);
+                            compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
   } else {
     pdh = wtap_dump_open_ng(fname, save_format, encap, cf->snap,
-                            compressed, shb_hdr, idb_inf, nrb_hdr, &err);
+                            compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
   }
   g_free(idb_inf);
   idb_inf = NULL;
@@ -4709,7 +4796,7 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
   callback_args.fname = fname;
   callback_args.file_type = save_format;
   switch (process_specified_records(cf, range, "Writing", "specified records",
-                                    TRUE, save_record, &callback_args)) {
+                                    TRUE, save_record, &callback_args, TRUE)) {
 
   case PSP_FINISHED:
     /* Completed successfully. */