If the rename in a safe "Save As" fails, the current file's name has
[metze/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index afdaa0d41065ce5a6685afb35ec00527ccb05ee9..2088d3319572621e5f3d4e4cac07a088288618e1 100644 (file)
--- a/file.c
+++ b/file.c
 #include "fileset.h"
 #include "tempfile.h"
 #include "merge.h"
-#include "alert_box.h"
-#include "simple_dialog.h"
-#include "main_statusbar.h"
-#include "progress_dlg.h"
-#include "ui_util.h"
+
 #include <epan/prefs.h>
 #include <epan/dfilter/dfilter.h>
 #include <epan/epan_dissect.h>
 #include <epan/strutil.h>
 #include <epan/addr_resolv.h>
 
+#include "ui/alert_box.h"
+#include "ui/simple_dialog.h"
+#include "ui/main_statusbar.h"
+#include "ui/progress_dlg.h"
+#include "ui/ui_util.h"
+
 #ifdef HAVE_LIBPCAP
 gboolean auto_scroll_live;
 #endif
@@ -126,7 +128,6 @@ static void cf_open_failure_alert_box(const char *filename, int err,
                       gchar *err_info, gboolean for_writing,
                       int file_type);
 static const char *file_rename_error_message(int err);
-static void cf_write_failure_alert_box(const char *filename, int err);
 static void cf_close_failure_alert_box(const char *filename, int err);
 static void ref_time_packets(capture_file *cf);
 /* Update the progress bar this many times when reading a file. */
@@ -135,11 +136,6 @@ static void ref_time_packets(capture_file *cf);
 #define MIN_QUANTUM         200000
 #define MIN_NUMBER_OF_PACKET 1500
 
-/* Number of "frame_data" structures per memory chunk.
-   XXX - is this the right number? */
-#define FRAME_DATA_CHUNK_SIZE   1024
-
-
 /*
  * We could probably use g_signal_...() instead of the callbacks below but that
  * would require linking our CLI programs to libgobject and creating an object
@@ -274,7 +270,7 @@ static void compute_elapsed(GTimeVal *start_time)
   delta_time = (time_now.tv_sec - start_time->tv_sec) * 1e6 +
     time_now.tv_usec - start_time->tv_usec;
 
-  computed_elapsed = (gulong) (delta_time / 1000); /* ms*/
+  computed_elapsed = (gulong) (delta_time / 1000); /* ms */
 }
 
 cf_status_t
@@ -289,7 +285,7 @@ cf_open(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
 
   /* The open succeeded.  Close whatever capture file we had open,
      and fill in the information for this file. */
-  cf_reset_state(cf);
+  cf_close(cf);
 
   /* Cleanup all data structures used for dissection. */
   cleanup_dissection();
@@ -310,8 +306,8 @@ cf_open(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
   /* Indicate whether it's a permanent or temporary file. */
   cf->is_tempfile = is_tempfile;
 
-  /* If it's a temporary capture buffer file, mark it as not saved. */
-  cf->user_saved = !is_tempfile;
+  /* No user changes yet. */
+  cf->unsaved_changes = FALSE;
 
   reset_elapsed();
 
@@ -368,6 +364,9 @@ fail:
  * want the UI to go from "file open" to "file closed" back to
  * "file open", we want it to go from "old file open" to "new file
  * open and being read".
+ *
+ * XXX - currently, cf_open() calls cf_close(), rather than
+ * cf_reset_state().
  */
 static void
 cf_reset_state(capture_file *cf)
@@ -387,8 +386,8 @@ cf_reset_state(capture_file *cf)
     g_free(cf->filename);
     cf->filename = NULL;
   }
-  /* ...which means we have nothing to save. */
-  cf->user_saved = FALSE;
+  /* ...which means we have no changes to that file to save. */
+  cf->unsaved_changes = FALSE;
 
   dfilter_free(cf->rfcode);
   cf->rfcode = NULL;
@@ -432,18 +431,16 @@ cf_reset_state(capture_file *cf)
 void
 cf_close(capture_file *cf)
 {
-  /* do GUI things even if file is already closed,
-   * e.g. to cleanup things if a capture couldn't be started */
-  cf_callback_invoke(cf_cb_file_closing, cf);
+  if(cf->state != FILE_CLOSED) {
+    cf_callback_invoke(cf_cb_file_closing, cf);
 
   /* close things, if not already closed before */
-  if(cf->state != FILE_CLOSED) {
     color_filters_cleanup();
     cf_reset_state(cf);
     cleanup_dissection();
-  }
 
-  cf_callback_invoke(cf_cb_file_closed, cf);
+    cf_callback_invoke(cf_cb_file_closed, cf);
+  }
 }
 
 /* an out of memory exception occured, wait for a user button press to exit */
@@ -478,14 +475,14 @@ calc_progbar_val(capture_file *cf, gint64 size, gint64 file_pos, gchar *status_s
   }
 
   g_snprintf(status_str, status_size,
-            "%" G_GINT64_MODIFIER "dKB of %" G_GINT64_MODIFIER "dKB",
-            file_pos / 1024, size / 1024);
+             "%" G_GINT64_MODIFIER "dKB of %" G_GINT64_MODIFIER "dKB",
+             file_pos / 1024, size / 1024);
 
   return progbar_val;
 }
 
 cf_read_status_t
-cf_read(capture_file *cf, gboolean from_save)
+cf_read(capture_file *cf, gboolean reloading)
 {
   int         err;
   gchar       *err_info;
@@ -528,10 +525,13 @@ cf_read(capture_file *cf, gboolean from_save)
 
   name_ptr = get_basename(cf->filename);
 
-  if (from_save == FALSE)
-    cf_callback_invoke(cf_cb_file_read_started, cf);
+  if (reloading)
+    cf_callback_invoke(cf_cb_file_reload_started, cf);
   else
-    cf_callback_invoke(cf_cb_file_save_started, (gpointer)name_ptr);
+    cf_callback_invoke(cf_cb_file_read_started, cf);
+
+  /* Record whether the file is compressed. */
+  cf->iscompressed = wtap_iscompressed(cf->wth);
 
   /* Find the size of the file. */
   size = wtap_file_size(cf->wth, NULL);
@@ -565,11 +565,11 @@ cf_read(capture_file *cf, gboolean from_save)
        */
       if ((progbar == NULL) && !(count % MIN_NUMBER_OF_PACKET)){
         progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
-        if (from_save == FALSE)
-          progbar = delayed_create_progress_dlg("Loading", name_ptr,
+        if (reloading)
+          progbar = delayed_create_progress_dlg("Reloading", name_ptr,
                                                 TRUE, &stop_flag, &start_time, progbar_val);
         else
-          progbar = delayed_create_progress_dlg("Saving", name_ptr,
+          progbar = delayed_create_progress_dlg("Loading", name_ptr,
                                                 TRUE, &stop_flag, &start_time, progbar_val);
       }
 
@@ -663,10 +663,10 @@ cf_read(capture_file *cf, gboolean from_save)
   cf->current_row = 0;
 
   new_packet_list_thaw();
-  if (from_save == FALSE)
-    cf_callback_invoke(cf_cb_file_read_finished, cf);
+  if (reloading)
+    cf_callback_invoke(cf_cb_file_reload_finished, cf);
   else
-    cf_callback_invoke(cf_cb_file_save_finished, cf);
+    cf_callback_invoke(cf_cb_file_read_finished, cf);
 
   /* If we have any displayed packets to select, select the first of those
      packets by making the first row the selected row. */
@@ -692,6 +692,14 @@ cf_read(capture_file *cf, gboolean from_save)
        if any. */
     switch (err) {
 
+    case WTAP_ERR_UNSUPPORTED:
+      g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                 "The capture file contains record data that TShark doesn't support.\n(%s)",
+                 err_info);
+      g_free(err_info);
+      errmsg = errmsg_errno;
+      break;
+
     case WTAP_ERR_UNSUPPORTED_ENCAP:
       g_snprintf(errmsg_errno, sizeof(errmsg_errno),
                  "The capture file has a packet with a network type that Wireshark doesn't support.\n(%s)",
@@ -710,7 +718,7 @@ cf_read(capture_file *cf, gboolean from_save)
         " in the middle of a packet.";
       break;
 
-    case WTAP_ERR_BAD_RECORD:
+    case WTAP_ERR_BAD_FILE:
       g_snprintf(errmsg_errno, sizeof(errmsg_errno),
                  "The capture file appears to be damaged or corrupt.\n(%s)",
                  err_info);
@@ -1062,6 +1070,17 @@ 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, gboolean filtering_tap_listeners,
@@ -1110,6 +1129,14 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
   if (dfcode != NULL) {
     if (refilter) {
       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);
+      }
     }
   } else
     fdata->flags.passed_dfilter = 1;
@@ -1202,8 +1229,7 @@ cf_status_t
 cf_merge_files(char **out_filenamep, int in_file_count,
                char *const *in_filenames, int file_type, gboolean do_append)
 {
-  merge_in_file_t  *in_files;
-  wtap             *wth;
+  merge_in_file_t  *in_files, *in_file;
   char             *out_filename;
   char             *tmpname;
   int               out_fd;
@@ -1286,14 +1312,19 @@ cf_merge_files(char **out_filenamep, int in_file_count,
   /* do the merge (or append) */
   for (;;) {
     if (do_append)
-      wth = merge_append_read_packet(in_file_count, in_files, &read_err,
-                                     &err_info);
+      in_file = merge_append_read_packet(in_file_count, in_files, &read_err,
+                                         &err_info);
     else
-      wth = merge_read_packet(in_file_count, in_files, &read_err,
-                              &err_info);
-    if (wth == NULL) {
-      if (read_err != 0)
-        got_read_error = TRUE;
+      in_file = merge_read_packet(in_file_count, in_files, &read_err,
+                                  &err_info);
+    if (in_file == NULL) {
+      /* EOF */
+      break;
+    }
+
+    if (read_err != 0) {
+      /* I/O error reading from in_file */
+      got_read_error = TRUE;
       break;
     }
 
@@ -1343,8 +1374,8 @@ cf_merge_files(char **out_filenamep, int in_file_count,
       break;
     }
 
-    if (!wtap_dump(pdh, wtap_phdr(wth), wtap_pseudoheader(wth),
-         wtap_buf_ptr(wth), &write_err)) {
+    if (!wtap_dump(pdh, wtap_phdr(in_file->wth), wtap_pseudoheader(in_file->wth),
+         wtap_buf_ptr(in_file->wth), &write_err)) {
       got_write_error = TRUE;
       break;
     }
@@ -1367,51 +1398,51 @@ cf_merge_files(char **out_filenamep, int in_file_count,
      */
     for (i = 0; i < in_file_count; i++) {
       if (in_files[i].state == GOT_ERROR) {
-    /* Put up a message box noting that a read failed somewhere along
-       the line. */
-    switch (read_err) {
-
-    case WTAP_ERR_UNSUPPORTED_ENCAP:
-      g_snprintf(errmsg_errno, sizeof(errmsg_errno),
-           "The capture file %%s has a packet with a network type that Wireshark doesn't support.\n(%s)",
-           err_info);
-      g_free(err_info);
-      errmsg = errmsg_errno;
-      break;
+        /* Put up a message box noting that a read failed somewhere along
+           the line. */
+        switch (read_err) {
+
+        case WTAP_ERR_UNSUPPORTED_ENCAP:
+          g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                     "The capture file %%s has a packet with a network type that Wireshark doesn't support.\n(%s)",
+                     err_info);
+          g_free(err_info);
+          errmsg = errmsg_errno;
+          break;
 
-    case WTAP_ERR_CANT_READ:
-      errmsg = "An attempt to read from the capture file %s failed for"
-           " some unknown reason.";
-      break;
+        case WTAP_ERR_CANT_READ:
+          errmsg = "An attempt to read from the capture file %s failed for"
+                   " some unknown reason.";
+          break;
 
-    case WTAP_ERR_SHORT_READ:
-      errmsg = "The capture file %s appears to have been cut short"
-           " in the middle of a packet.";
-      break;
+        case WTAP_ERR_SHORT_READ:
+          errmsg = "The capture file %s appears to have been cut short"
+                   " in the middle of a packet.";
+          break;
 
-    case WTAP_ERR_BAD_RECORD:
-      g_snprintf(errmsg_errno, sizeof(errmsg_errno),
-           "The capture file %%s appears to be damaged or corrupt.\n(%s)",
-           err_info);
-      g_free(err_info);
-      errmsg = errmsg_errno;
-      break;
+        case WTAP_ERR_BAD_FILE:
+          g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                     "The capture file %%s appears to be damaged or corrupt.\n(%s)",
+                     err_info);
+          g_free(err_info);
+          errmsg = errmsg_errno;
+          break;
 
-    case WTAP_ERR_DECOMPRESS:
-      g_snprintf(errmsg_errno, sizeof(errmsg_errno),
-                 "The compressed capture file %%s appears to be damaged or corrupt.\n"
-                 "(%s)", err_info);
-      g_free(err_info);
-      errmsg = errmsg_errno;
-      break;
+        case WTAP_ERR_DECOMPRESS:
+          g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                     "The compressed capture file %%s appears to be damaged or corrupt.\n"
+                     "(%s)", err_info);
+          g_free(err_info);
+          errmsg = errmsg_errno;
+          break;
 
-    default:
-      g_snprintf(errmsg_errno, sizeof(errmsg_errno),
-           "An error occurred while reading the"
-           " capture file %%s: %s.", wtap_strerror(read_err));
-      errmsg = errmsg_errno;
-      break;
-    }
+        default:
+          g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                     "An error occurred while reading the"
+                     " capture file %%s: %s.", wtap_strerror(read_err));
+          errmsg = errmsg_errno;
+          break;
+        }
         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, errmsg, in_files[i].filename);
       }
     }
@@ -1419,7 +1450,31 @@ cf_merge_files(char **out_filenamep, int in_file_count,
 
   if (got_write_error) {
     /* Put up an alert box for the write error. */
-    cf_write_failure_alert_box(out_filename, write_err);
+    if (write_err < 0) {
+      /* Wiretap error. */
+      switch (write_err) {
+
+      case WTAP_ERR_UNSUPPORTED_ENCAP:
+        /*
+         * This is a problem with the particular frame we're writing;
+         * note that, and give the frame number.
+         */
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                      "Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.",
+                      in_file->packet_num, in_file->filename,
+                      wtap_file_type_string(file_type));
+        break;
+
+      default:
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                      "An error occurred while writing to the file \"%s\": %s.",
+                      out_filename, wtap_strerror(write_err));
+        break;
+      }
+    } else {
+      /* OS error. */
+      write_failure_alert_box(out_filename, write_err);
+    }
   }
 
   if (got_read_error || got_write_error || stop_flag) {
@@ -1550,7 +1605,7 @@ cf_read_frame_r(capture_file *cf, frame_data *fdata,
       g_free(err_info);
       break;
 
-    case WTAP_ERR_BAD_RECORD:
+    case WTAP_ERR_BAD_FILE:
       g_snprintf(errmsg_errno, sizeof(errmsg_errno),
                  "An error occurred while reading from the file \"%%s\": %s.\n(%s)",
                  wtap_strerror(err), err_info);
@@ -1761,6 +1816,13 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item,
       frame_data_cleanup(fdata);
     }
 
+    if (redissect || refilter) {
+      /* If we're redissecting or refiltering then any frame dependencies 
+       * from the previous dissection/filtering are no longer valid.
+       */
+      fdata->flags.dependent_of_displayed = 0;
+    }
+
     if (!cf_read_frame(cf, fdata))
       break; /* error reading the frame */
 
@@ -1951,11 +2013,12 @@ ref_time_packets(capture_file *cf)
         cf->elapsed_time = fdata->rel_ts;
     }
 
-    /* Get the time elapsed between the previous displayed packet and
-     this packet. */
-    nstime_delta(&fdata->del_dis_ts, &fdata->abs_ts, &prev_dis_ts);
-
-    prev_dis_ts = fdata->abs_ts;
+    /* If this frame is displayed, get the time elapsed between the
+     previous displayed packet and this packet. */
+    if( fdata->flags.passed_dfilter ) {
+        nstime_delta(&fdata->del_dis_ts, &fdata->abs_ts, &prev_dis_ts);
+        prev_dis_ts = fdata->abs_ts;
+    }
 
     /*
      * Byte counts
@@ -2489,7 +2552,7 @@ cf_write_pdml_packets(capture_file *cf, print_args_t *print_args)
   if (fh == NULL)
     return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
 
-  write_pdml_preamble(fh);
+  write_pdml_preamble(fh, cf->filename);
   if (ferror(fh)) {
     fclose(fh);
     return CF_PRINT_WRITE_ERROR;
@@ -2681,12 +2744,17 @@ cf_write_csv_packets(capture_file *cf, print_args_t *print_args)
 
 static gboolean
 write_carrays_packet(capture_file *cf _U_, frame_data *fdata,
-             union wtap_pseudo_header *pseudo_header _U_,
+             union wtap_pseudo_header *pseudo_header,
              const guint8 *pd, void *argsp)
 {
   FILE *fh = argsp;
+  epan_dissect_t edt;
+
+  epan_dissect_init(&edt, TRUE, TRUE);
+  epan_dissect_run(&edt, pseudo_header, pd, fdata, NULL);
+  proto_tree_write_carrays(fdata->num, fh, &edt);
+  epan_dissect_cleanup(&edt);
 
-  proto_tree_write_carrays(pd, fdata->cap_len, fdata->num, fh);
   return !ferror(fh);
 }
 
@@ -2758,7 +2826,7 @@ cf_find_string_protocol_tree(capture_file *cf, proto_tree *tree,  match_data *md
   mdata->cf = cf;
   /* Iterate through all the nodes looking for matching text */
   proto_tree_children_foreach(tree, match_subtree_text, mdata);
-  return mdata->frame_matched ? MR_MATCHED : MR_NOTMATCHED; 
+  return mdata->frame_matched ? MR_MATCHED : MR_NOTMATCHED;
 }
 
 static match_result
@@ -2789,7 +2857,7 @@ match_protocol_tree(capture_file *cf, frame_data *fdata, void *criterion)
 static void
 match_subtree_text(proto_node *node, gpointer data)
 {
-  match_data    *mdata = (match_data*) data;
+  match_data    *mdata = (match_data *) data;
   const gchar   *string = mdata->string;
   size_t        string_len = mdata->string_len;
   capture_file  *cf = mdata->cf;
@@ -2963,22 +3031,28 @@ match_ascii_and_unicode(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->pkt_len;
-  for (i = 0; i < buf_len; i++) {
+  i = 0;
+  while (i < buf_len) {
     c_char = cf->pd[i];
     if (cf->case_type)
       c_char = toupper(c_char);
-    if (c_char != 0) {
+    if (c_char != '\0') {
       if (c_char == ascii_text[c_match]) {
-        c_match++;
+        c_match += 1;
         if (c_match == textlen) {
           result = MR_MATCHED;
           cf->search_pos = i; /* Save the position of the last character
                                  for highlighting the field. */
           break;
         }
-      } else
+      }
+      else {
+        g_assert(i>=c_match);
+        i -= (guint32)c_match;
         c_match = 0;
+      }
     }
+    i += 1;
   }
   return result;
 }
@@ -3003,21 +3077,28 @@ match_ascii(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->pkt_len;
-  for (i = 0; i < buf_len; i++) {
+  i = 0;
+  while (i < buf_len) {
     c_char = cf->pd[i];
     if (cf->case_type)
       c_char = toupper(c_char);
     if (c_char == ascii_text[c_match]) {
-      c_match++;
+      c_match += 1;
       if (c_match == textlen) {
         result = MR_MATCHED;
         cf->search_pos = i; /* Save the position of the last character
                                for highlighting the field. */
         break;
       }
-    } else
+    }
+    else {
+      g_assert(i>=c_match);
+      i -= (guint32)c_match;
       c_match = 0;
+    }
+    i += 1;
   }
+
   return result;
 }
 
@@ -3041,21 +3122,27 @@ match_unicode(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->pkt_len;
-  for (i = 0; i < buf_len; i++) {
+  i = 0;
+  while (i < buf_len) {
     c_char = cf->pd[i];
     if (cf->case_type)
       c_char = toupper(c_char);
     if (c_char == ascii_text[c_match]) {
-      c_match++;
-      i++;
+      c_match += 1;
       if (c_match == textlen) {
         result = MR_MATCHED;
         cf->search_pos = i; /* Save the position of the last character
                                for highlighting the field. */
         break;
       }
-    } else
+      i += 1;
+    }
+    else {
+      g_assert(i>=(c_match*2));
+      i -= (guint32)c_match*2;
       c_match = 0;
+    }
+    i += 1;
   }
   return result;
 }
@@ -3079,17 +3166,23 @@ match_binary(capture_file *cf, frame_data *fdata, void *criterion)
 
   result = MR_NOTMATCHED;
   buf_len = fdata->pkt_len;
-  for (i = 0; i < buf_len; i++) {
+  i = 0;
+  while (i < buf_len) {
     if (cf->pd[i] == binary_data[c_match]) {
-      c_match++;
+      c_match += 1;
       if (c_match == datalen) {
         result = MR_MATCHED;
         cf->search_pos = i; /* Save the position of the last character
                                for highlighting the field. */
         break;
       }
-    } else
+    }
+    else {
+      g_assert(i>=c_match);
+      i -= (guint32)c_match;
       c_match = 0;
+    }
+    i += 1;
   }
   return result;
 }
@@ -3428,7 +3521,7 @@ cf_select_packet(capture_file *cf, int 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 "gtk/gtkclist.c", in both
+       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
@@ -3567,9 +3660,56 @@ cf_unignore_frame(capture_file *cf, frame_data *frame)
   }
 }
 
+/*
+ * Read the comment in SHB block
+ */
+
+const gchar *
+cf_read_shb_comment(capture_file *cf)
+{
+  wtapng_section_t *shb_inf;
+  const gchar *temp_str;
+
+  /* Get info from SHB */
+  shb_inf = wtap_file_get_shb_info(cf->wth);
+  if(shb_inf == NULL)
+        return NULL;
+  temp_str = shb_inf->opt_comment;
+  g_free(shb_inf);
+
+  return temp_str;
+
+}
+
+void
+cf_update_capture_comment(capture_file *cf, gchar *comment)
+{
+  wtapng_section_t *shb_inf;
+
+  /* Get info from SHB */
+  shb_inf = wtap_file_get_shb_info(cf->wth);
+
+  /* See if the comment has changed or not */
+  if (shb_inf && shb_inf->opt_comment) {
+    if (strcmp(shb_inf->opt_comment, comment) == 0) {
+      g_free(comment);
+      g_free(shb_inf);
+      return;
+    }
+  }
+
+  g_free(shb_inf);
+
+  /* The comment has changed, let's update it */
+  wtap_write_shb_comment(cf->wth, comment);
+  /* Mark the file as having unsaved changes */
+  cf->unsaved_changes = TRUE;
+}
+
 typedef struct {
   wtap_dumper *pdh;
   const char  *fname;
+  int          file_type;
 } save_callback_args_t;
 
 /*
@@ -3589,15 +3729,61 @@ save_packet(capture_file *cf _U_, frame_data *fdata,
   int           err;
 
   /* init the wtap header for saving */
-  hdr.ts.secs    = fdata->abs_ts.secs;
-  hdr.ts.nsecs   = fdata->abs_ts.nsecs;
-  hdr.caplen     = fdata->cap_len;
-  hdr.len        = fdata->pkt_len;
-  hdr.pkt_encap  = fdata->lnk_t;
-
+  /* XXX - these are the only flags that correspond to data that we have
+     in the frame_data structure and that matter on a per-packet basis.
+
+     For WTAP_HAS_CAP_LEN, either the file format has separate "captured"
+     and "on the wire" lengths, or it doesn't.
+
+     For WTAP_HAS_DROP_COUNT, Wiretap doesn't actually supply the value
+     to its callers.
+
+     For WTAP_HAS_PACK_FLAGS, we currently don't save the FCS length
+     from the packet flags. */
+  hdr.presence_flags = 0;
+  if (fdata->flags.has_ts)
+    hdr.presence_flags |= WTAP_HAS_TS;
+  if (fdata->flags.has_ts)
+    hdr.presence_flags |= WTAP_HAS_INTERFACE_ID;
+  hdr.ts.secs      = fdata->abs_ts.secs;
+  hdr.ts.nsecs     = fdata->abs_ts.nsecs;
+  hdr.caplen       = fdata->cap_len;
+  hdr.len          = fdata->pkt_len;
+  hdr.pkt_encap    = fdata->lnk_t;
+  /* pcapng */
+  hdr.interface_id = fdata->interface_id;   /* identifier of the interface. */
+  /* options */
+  hdr.opt_comment  = fdata->opt_comment; /* NULL if not available */
+#if 0
+  hdr.drop_count   =
+  hdr.pack_flags   =     /* XXX - 0 for now (any value for "we don't have it"?) */
+#endif
   /* and save the packet */
   if (!wtap_dump(args->pdh, &hdr, pseudo_header, pd, &err)) {
-    cf_write_failure_alert_box(args->fname, err);
+    if (err < 0) {
+      /* Wiretap error. */
+      switch (err) {
+
+      case WTAP_ERR_UNSUPPORTED_ENCAP:
+        /*
+         * This is a problem with the particular frame we're writing;
+         * note that, and give the frame number.
+         */
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                      "Frame %u has a network type that can't be saved in a \"%s\" file.",
+                      fdata->num, wtap_file_type_string(args->file_type));
+        break;
+
+      default:
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                      "An error occurred while writing to the file \"%s\": %s.",
+                      args->fname, wtap_strerror(err));
+        break;
+      }
+    } else {
+      /* OS error. */
+      write_failure_alert_box(args->fname, err);
+    }
     return FALSE;
   }
   return TRUE;
@@ -3626,47 +3812,56 @@ cf_can_save_as(capture_file *cf)
 }
 
 cf_status_t
-cf_save(capture_file *cf, const char *fname, packet_range_t *range, guint save_format, gboolean compressed)
+cf_save_packets(capture_file *cf, const char *fname, guint save_format,
+                gboolean compressed, gboolean dont_reopen)
 {
-  gchar        *from_filename;
+  gchar        *fname_new = NULL;
   int           err;
   gboolean      do_copy;
   wtap_dumper  *pdh;
+  packet_range_t range;
   save_callback_args_t callback_args;
 
   cf_callback_invoke(cf_cb_file_save_started, (gpointer)fname);
 
-  /* don't write over an existing file. */
-  /* this should've been already checked by our caller, just to be sure... */
-  if (file_exists(fname)) {
-    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-      "%sCapture file: \"%s\" already exists!%s\n\n"
-      "Please choose a different filename.",
-      simple_dialog_primary_start(), fname, simple_dialog_primary_end());
-    goto fail;
-  }
-
-  packet_range_process_init(range);
-
-  if (packet_range_process_all(range) && save_format == cf->cd_t) {
-    /* We're not filtering packets, and we're saving it in the format
-       it's already in, so we can just move or copy the raw data. */
+  if (save_format == cf->cd_t && compressed == cf->iscompressed
+      && !cf->unsaved_changes) {
+    /* We're saving in the format it's already in, 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. */
 
     if (cf->is_tempfile) {
       /* The file being saved is a temporary file from a live
          capture, so it doesn't need to stay around under that name;
-         first, try renaming the capture buffer file to the new name. */
+         first, try renaming the capture buffer file to the new name.
+         This acts as a "safe save", in that, if the file already
+         exists, the existing file will be removed only if the rename
+         succeeds.
+
+         Sadly, on Windows, as we have the current capture file
+         open, even MoveFileEx() with MOVEFILE_REPLACE_EXISTING
+         (to cause the rename to remove an existing target), as
+         done by ws_stdio_rename() (ws_rename() is #defined to
+         be ws_stdio_rename() on Windows) will fail.
+
+         According to the MSDN documentation for CreateFile(), if,
+         when we open a capture file, we were to directly do a CreateFile(),
+         opening with FILE_SHARE_DELETE|FILE_SHARE_READ, and then
+         convert it to a file descriptor with _open_osfhandle(),
+         that would allow the file to be renamed out from under us.
+
+         However, that doesn't work in practice.  Perhaps the problem
+         is that the process doing the rename is the process that
+         has the file open. */
 #ifndef _WIN32
       if (ws_rename(cf->filename, fname) == 0) {
         /* That succeeded - there's no need to copy the source file. */
-        from_filename = NULL;
-    do_copy = FALSE;
+        do_copy = FALSE;
       } else {
         if (errno == EXDEV) {
           /* They're on different file systems, so we have to copy the
              file. */
           do_copy = TRUE;
-          from_filename = cf->filename;
         } else {
           /* The rename failed, but not because they're on different
              file systems - put up an error message.  (Or should we
@@ -3682,47 +3877,75 @@ cf_save(capture_file *cf, const char *fname, packet_range_t *range, guint save_f
       }
 #else
       do_copy = TRUE;
-      from_filename = cf->filename;
 #endif
     } else {
       /* It's a permanent file, so we should copy it, and not remove the
          original. */
       do_copy = TRUE;
-      from_filename = cf->filename;
     }
 
     if (do_copy) {
-      /* Copy the file, if we haven't moved it. */
-      if (!copy_file_binary_mode(from_filename, fname))
-    goto fail;
+      /* Copy the file, if we haven't moved it.  If we're overwriting
+         an existing file, we do it with a "safe save", by writing
+         to a new file and, if the write succeeds, renaming the
+         new file on top of the old file. */
+      if (file_exists(fname)) {
+        fname_new = g_strdup_printf("%s~", fname);
+        if (!copy_file_binary_mode(cf->filename, fname_new))
+          goto fail;
+      } else {
+        if (!copy_file_binary_mode(cf->filename, fname))
+          goto fail;
+      }
     }
   } else {
-    /* Either we're filtering packets, or we're saving in a different
-       format; we can't do that by copying or moving the capture file,
-       we have to do it by writing the packets out in Wiretap. */
-    pdh = wtap_dump_open(fname, save_format, cf->lnk_t, cf->snap,
-        compressed, &err);
+    /* Either we're saving in a different format or we're saving changes,
+       such as added, modified, or removed comments, that haven't yet
+       been written to the underlying file; we can't do that by copying
+       or moving the capture file, we have to do it by writing the packets
+       out in Wiretap. */
+
+    wtapng_section_t *shb_hdr = NULL;
+    wtapng_iface_descriptions_t *idb_inf = NULL;
+
+    shb_hdr = wtap_file_get_shb_info(cf->wth);
+    idb_inf = wtap_file_get_idb_info(cf->wth);
+
+    if (file_exists(fname)) {
+      /* We're overwriting an existing file; write out to a new file,
+         and, if that succeeds, rename the new file on top of the
+         old file.  That makes this a "safe save", so that we don't
+         lose the old file if we have a problem writing out the new
+         file.  (If the existing file is the current capture file,
+         we *HAVE* to do that, otherwise we're overwriting the file
+         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, cf->lnk_t, cf->snap,
+                              compressed, shb_hdr, idb_inf, &err);
+    } else {
+      pdh = wtap_dump_open_ng(fname, save_format, cf->lnk_t, cf->snap,
+                              compressed, shb_hdr, idb_inf, &err);
+    }
+    g_free(idb_inf);
+    idb_inf = NULL;
+
     if (pdh == NULL) {
       cf_open_failure_alert_box(fname, err, NULL, TRUE, save_format);
       goto fail;
     }
 
-    /* XXX - we let the user save a subset of the packets.
-
-       If we do that, should we make that file the current file?  If so,
-       it means we can no longer get at the other packets.  What does
-       NetMon do? */
+    /* Add address resolution */
+    wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list());
 
-    /* Iterate through the list of packets, processing the packets we were
-       told to process.
+    /* Create a packet range that's set to the default "save everything"
+       state. */
+    packet_range_init(&range);
 
-       XXX - we've already called "packet_range_process_init(range)", but
-       "process_specified_packets()" will do it again.  Fortunately,
-       that's harmless in this case, as we haven't done anything to
-       "range" since we initialized it. */
+    /* Iterate through the list of packets, processing all the packets. */
     callback_args.pdh = pdh;
     callback_args.fname = fname;
-    switch (process_specified_packets(cf, range, "Saving", "selected packets",
+    callback_args.file_type = save_format;
+    switch (process_specified_packets(cf, &range, "Saving", "selected packets",
                                       TRUE, save_packet, &callback_args)) {
 
     case PSP_FINISHED:
@@ -3746,11 +3969,38 @@ cf_save(capture_file *cf, const char *fname, packet_range_t *range, guint save_f
     }
   }
 
+  if (fname_new != NULL) {
+    /* We wrote out to fname_new, and should rename it on top of
+       fname.  fname_new is now closed, so that should be possible even
+       on Windows.  However, on Windows, we first need to close whatever
+       file descriptors we have open for fname. */
+#ifdef _WIN32
+    wtap_fdclose(cf->wth);
+#endif
+    /* Now do the rename. */
+    if (ws_rename(fname_new, fname) == -1) {
+      /* Well, the rename failed. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                    file_rename_error_message(errno), fname);
+#ifdef _WIN32
+      /* Attempt to reopen the random file descriptor using the
+         current file's filename.  (At this point, the sequential
+         file descriptor is closed.) */
+      if (!wtap_fdreopen(cf->wth, cf->filename, &err)) {
+        /* Oh, well, we're screwed. */
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                      file_open_error_message(err, FALSE), cf->filename);
+      }
+#endif
+      goto fail;
+    }
+  }
+
   cf_callback_invoke(cf_cb_file_save_finished, NULL);
+  cf->unsaved_changes = FALSE;
 
-  if (packet_range_process_all(range)) {
-    /* We saved the entire capture, not just some packets from it.
-       Open and read the file we saved it to.
+  if (!dont_reopen) {
+    /* Open and read the file we saved to.
 
        XXX - this is somewhat of a waste; we already have the
        packets, all this gets us is updated file type information
@@ -3758,8 +4008,12 @@ cf_save(capture_file *cf, const char *fname, packet_range_t *range, guint save_f
        file be the one we have opened and from which we're reading
        the data, and it means we have to spend time opening and
        reading the file, which could be a significant amount of
-       time if the file is large. */
-    cf->user_saved = TRUE;
+       time if the file is large.
+
+       If the capture-file-writing code were to return the
+       seek offset of each packet it writes, we could save that
+       in the frame_data structure for the frame, and just open
+       the file without reading it again. */
 
     if ((cf_open(cf, fname, FALSE, &err)) == CF_OK) {
       /* XXX - report errors if this fails?
@@ -3769,24 +4023,146 @@ cf_save(capture_file *cf, const char *fname, packet_range_t *range, guint save_f
 
       case CF_READ_OK:
       case CF_READ_ERROR:
-    /* Just because we got an error, that doesn't mean we were unable
-       to read any of the file; we handle what we could get from the
-       file. */
-    break;
+        /* Just because we got an error, that doesn't mean we were unable
+           to read any of the file; we handle what we could get from the
+           file. */
+        break;
 
       case CF_READ_ABORTED:
-    /* The user bailed out of re-reading the capture file; the
-       capture file has been closed - just return (without
-       changing any menu settings; "cf_close()" set them
-       correctly for the "no capture file open" state). */
-    break;
+        /* The user bailed out of re-reading the capture file; the
+           capture file has been closed - just return (without
+           changing any menu settings; "cf_close()" set them
+           correctly for the "no capture file open" state). */
+        break;
       }
-      cf_callback_invoke(cf_cb_file_save_reload_finished, cf);
     }
   }
   return CF_OK;
 
 fail:
+  if (fname_new != NULL) {
+    /* We were trying to write to a temporary file; get rid of it if it
+       exists.  (We don't care whether this fails, as, if it fails,
+       there's not much we can do about it.  I guess if it failed for
+       a reason other than "it doesn't exist", we could report an
+       error, so the user knows there's a junk file that they might
+       want to clean up.) */
+    ws_unlink(fname_new);
+    g_free(fname_new);
+  }
+  cf_callback_invoke(cf_cb_file_save_failed, NULL);
+  return CF_ERROR;
+}
+
+cf_status_t
+cf_export_specified_packets(capture_file *cf, const char *fname,
+                            packet_range_t *range, guint save_format,
+                            gboolean compressed)
+{
+  gchar        *fname_new = NULL;
+  int           err;
+  wtap_dumper  *pdh;
+  save_callback_args_t callback_args;
+  wtapng_section_t *shb_hdr = NULL;
+  wtapng_iface_descriptions_t *idb_inf = NULL;
+
+  cf_callback_invoke(cf_cb_file_save_started, (gpointer)fname);
+
+  packet_range_process_init(range);
+
+  /* We're writing out specified packets from the specified capture
+     file to another file.  Even if all captured packets are to be
+     written, don't special-case the operation - read each packet
+     and then write it out if it's one of the specified ones. */
+
+  shb_hdr = wtap_file_get_shb_info(cf->wth);
+  idb_inf = wtap_file_get_idb_info(cf->wth);
+
+  if (file_exists(fname)) {
+    /* We're overwriting an existing file; write out to a new file,
+       and, if that succeeds, rename the new file on top of the
+       old file.  That makes this a "safe save", so that we don't
+       lose the old file if we have a problem writing out the new
+       file.  (If the existing file is the current capture file,
+       we *HAVE* to do that, otherwise we're overwriting the file
+       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, cf->lnk_t, cf->snap,
+                            compressed, shb_hdr, idb_inf, &err);
+  } else {
+    pdh = wtap_dump_open_ng(fname, save_format, cf->lnk_t, cf->snap,
+                            compressed, shb_hdr, idb_inf, &err);
+  }
+  g_free(idb_inf);
+  idb_inf = NULL;
+
+  if (pdh == NULL) {
+    cf_open_failure_alert_box(fname, err, NULL, TRUE, save_format);
+    goto fail;
+  }
+
+  /* Add address resolution */
+  wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list());
+
+  /* Iterate through the list of packets, processing the packets we were
+     told to process.
+
+     XXX - we've already called "packet_range_process_init(range)", but
+     "process_specified_packets()" will do it again.  Fortunately,
+     that's harmless in this case, as we haven't done anything to
+     "range" since we initialized it. */
+  callback_args.pdh = pdh;
+  callback_args.fname = fname;
+  callback_args.file_type = save_format;
+  switch (process_specified_packets(cf, range, "Writing", "specified packets",
+                                    TRUE, save_packet, &callback_args)) {
+
+  case PSP_FINISHED:
+    /* Completed successfully. */
+    break;
+
+  case PSP_STOPPED:
+    /* The user decided to abort the writing.
+       XXX - remove the output file? */
+    break;
+
+  case PSP_FAILED:
+    /* Error while writing. */
+    wtap_dump_close(pdh, &err);
+    goto fail;
+  }
+
+  if (!wtap_dump_close(pdh, &err)) {
+    cf_close_failure_alert_box(fname, err);
+    goto fail;
+  }
+
+  if (fname_new != NULL) {
+    /* We wrote out to fname_new, and should rename it on top of
+       fname; fname is now closed, so that should be possible even
+       on Windows.  Do the rename. */
+    if (ws_rename(fname_new, fname) == -1) {
+      /* Well, the rename failed. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                    file_rename_error_message(errno), fname);
+      goto fail;
+    }
+  }
+
+  cf_callback_invoke(cf_cb_file_save_finished, NULL);
+  return CF_OK;
+
+fail:
+  if (fname_new != NULL) {
+    /* We were trying to write to a temporary file; get rid of it if it
+       exists.  (We don't care whether this fails, as, if it fails,
+       there's not much we can do about it.  I guess if it failed for
+       a reason other than "it doesn't exist", we could report an
+       error, so the user knows there's a junk file that they might
+       want to clean up.) */
+    ws_unlink(fname_new);
+    g_free(fname_new);
+  }
   cf_callback_invoke(cf_cb_file_save_failed, NULL);
   return CF_ERROR;
 }
@@ -3808,7 +4184,8 @@ cf_open_failure_alert_box(const char *filename, int err, gchar *err_info,
     case WTAP_ERR_RANDOM_OPEN_PIPE:
       /* Seen only when opening a capture file for reading. */
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-            "The file \"%s\" is a pipe or FIFO; Wireshark can't read pipe or FIFO files.",
+            "The file \"%s\" is a pipe or FIFO; Wireshark can't read pipe or FIFO files.\n"
+            "To capture from a pipe or FIFO use wireshark -i -",
             filename);
       break;
 
@@ -3866,7 +4243,7 @@ cf_open_failure_alert_box(const char *filename, int err, gchar *err_info,
       }
       break;
 
-    case WTAP_ERR_BAD_RECORD:
+    case WTAP_ERR_BAD_FILE:
       /* Seen only when opening a capture file for reading. */
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
             "The file \"%s\" appears to be damaged or corrupt.\n"
@@ -3902,7 +4279,7 @@ cf_open_failure_alert_box(const char *filename, int err, gchar *err_info,
 
     case WTAP_ERR_COMPRESSION_NOT_SUPPORTED:
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-            "Gzip compression not supported by this file type.");
+            "This file type cannot be written as a compressed file.");
       break;
 
     case WTAP_ERR_DECOMPRESS:
@@ -3926,6 +4303,11 @@ cf_open_failure_alert_box(const char *filename, int err, gchar *err_info,
   }
 }
 
+/*
+ * XXX - whether we mention the source pathname, the target pathname,
+ * or both depends on the error and on what we find if we look for
+ * one or both of them.
+ */
 static const char *
 file_rename_error_message(int err)
 {
@@ -3935,14 +4317,21 @@ file_rename_error_message(int err)
   switch (err) {
 
   case ENOENT:
+    /* XXX - should check whether the source exists and, if not,
+       report it as the problem and, if so, report the destination
+       as the problem. */
     errmsg = "The path to the file \"%s\" doesn't exist.";
     break;
 
   case EACCES:
+    /* XXX - if we're doing a rename after a safe save, we should
+       probably say something else. */
     errmsg = "You don't have permission to move the capture file to \"%s\".";
     break;
 
   default:
+    /* XXX - this should probably mention both the source and destination
+       pathnames. */
     g_snprintf(errmsg_errno, sizeof(errmsg_errno),
             "The file \"%%s\" could not be moved: %s.",
                 wtap_strerror(err));
@@ -3952,20 +4341,6 @@ file_rename_error_message(int err)
   return errmsg;
 }
 
-static void
-cf_write_failure_alert_box(const char *filename, int err)
-{
-  if (err < 0) {
-    /* Wiretap error. */
-    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-          "An error occurred while writing to the file \"%s\": %s.",
-          filename, wtap_strerror(err));
-  } else {
-    /* OS error. */
-    write_failure_alert_box(filename, err);
-  }
-}
-
 /* Check for write errors - if the file is being written to an NFS server,
    a write error may not show up until the file is closed, as NFS clients
    might not send writes to the server until the "write()" call finishes,
@@ -4023,7 +4398,7 @@ cf_reload(capture_file *cf) {
   is_tempfile = cf->is_tempfile;
   cf->is_tempfile = FALSE;
   if (cf_open(cf, filename, is_tempfile, &err) == CF_OK) {
-    switch (cf_read(cf, FALSE)) {
+    switch (cf_read(cf, TRUE)) {
 
     case CF_READ_OK:
     case CF_READ_ERROR:
@@ -4063,6 +4438,6 @@ cf_reload(capture_file *cf) {
  * indent-tabs-mode: nil
  * End:
  *
- * ex: set shiftwidth=2 tabstop=8 expandtab
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
  * :indentSize=2:tabSize=8:noTabs=true:
  */