Store pointers to previously displayed and captured packet, not nstime_t deltas.
[metze/wireshark/wip.git] / tshark.c
index 7a836f94062278b8ac9272c0852b274bd2da34f9..118c75e2058d672b2a5ad40a3668056403d9c94a 100644 (file)
--- a/tshark.c
+++ b/tshark.c
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "config.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -70,7 +68,7 @@
 #include <epan/column.h>
 #include "print.h"
 #include <epan/addr_resolv.h>
-#include "util.h"
+#include "ui/util.h"
 #include "clopts_common.h"
 #include "console_io.h"
 #include "cmdarg_err.h"
 #include "capture-pcap-util.h"
 #ifdef _WIN32
 #include "capture-wpcap.h"
-#include "capture_errs.h"
 #include <wsutil/unicode-utils.h>
 #endif /* _WIN32 */
 #include "capture_sync.h"
 #endif /* HAVE_LIBPCAP */
 #include "log.h"
 #include <epan/funnel.h>
+#include "capture_opts.h"
 
 /*
  * This is the template for the decode as option; it is shared between the
@@ -105,10 +103,13 @@ static const gchar decode_as_arg_template[] = "<layer_type>==<selector>,<decode_
 
 static guint32 cum_bytes;
 static nstime_t first_ts;
-static nstime_t prev_dis_ts;
-static nstime_t prev_cap_ts;
+static frame_data *prev_dis;
+static frame_data prev_dis_frame;
+static frame_data *prev_cap;
+static frame_data prev_cap_frame;
 
 static gboolean print_packet_info;      /* TRUE if we're to print packet information */
+static const char* prev_display_dissector_name = NULL;
 
 static gboolean perform_two_pass_analysis;
 
@@ -270,7 +271,10 @@ print_usage(gboolean print_ver)
   fprintf(output, "                           filesize:NUM - switch to next file after NUM KB\n");
   fprintf(output, "                              files:NUM - ringbuffer: replace after NUM files\n");
 #endif  /* HAVE_LIBPCAP */
-
+#ifdef HAVE_PCAP_REMOTE
+  fprintf(output, "RPCAP options:\n");
+  fprintf(output, "  -A <user>:<password>     use RPCAP password authentication\n");
+#endif
   /*fprintf(output, "\n");*/
   fprintf(output, "Input file:\n");
   fprintf(output, "  -r <infile>              set the filename to read from (no pipes or stdin!)\n");
@@ -284,13 +288,15 @@ print_usage(gboolean print_ver)
   fprintf(output, "  -d %s ...\n", decode_as_arg_template);
   fprintf(output, "                           \"Decode As\", see the man page for details\n");
   fprintf(output, "                           Example: tcp.port==8888,http\n");
+  fprintf(output, "  -H <hosts file>          read a list of entries from a hosts file, which will\n");
+  fprintf(output, "                           then be written to a capture file. (Implies -W n)\n");
 
   /*fprintf(output, "\n");*/
   fprintf(output, "Output:\n");
   fprintf(output, "  -w <outfile|->           write packets to a pcap-format file named \"outfile\"\n");
   fprintf(output, "                           (or to the standard output for \"-\")\n");
   fprintf(output, "  -C <config profile>      start with specified configuration profile\n");
-  fprintf(output, "  -F <output file type>    set the output file type, default is libpcap\n");
+  fprintf(output, "  -F <output file type>    set the output file type, default is pcapng\n");
   fprintf(output, "                           an empty \"-F\" option will list the file types\n");
   fprintf(output, "  -V                       add output of packet tree        (Packet Details)\n");
   fprintf(output, "  -O <protocols>           Only show packet details of these protocols, comma\n");
@@ -313,6 +319,8 @@ print_usage(gboolean print_ver)
   fprintf(output, "  -u s|hms                 output format of seconds (def: s: seconds)\n");
   fprintf(output, "  -l                       flush standard output after each packet\n");
   fprintf(output, "  -q                       be more quiet on stdout (e.g. when using statistics)\n");
+  fprintf(output, "  -W n                     Save extra information in the file, if supported.\n");
+  fprintf(output, "                           n = write network address resolution information\n");
   fprintf(output, "  -X <key>:<value>         eXtension options, see the man page for details\n");
   fprintf(output, "  -z <statistics>          various statistics, see the man page for details\n");
 
@@ -345,7 +353,9 @@ glossary_option_help(void)
   fprintf(output, "  -G fields3               dump glossary in format 3 and exit\n");
   fprintf(output, "  -G protocols             dump protocols in registration database and exit\n");
   fprintf(output, "  -G values                dump value, range, true/false strings and exit\n");
+  fprintf(output, "  -G ftypes                dump field type basic and descriptive names\n");
   fprintf(output, "  -G decodes               dump \"layer type\"/\"decode as\" associations and exit\n");
+  fprintf(output, "  -G heuristic-decodes     dump heuristic dissector tables\n");
   fprintf(output, "\n");
   fprintf(output, "Preference reports:\n");
   fprintf(output, "  -G defaultprefs          dump default preferences and exit\n");
@@ -363,7 +373,11 @@ static void
 display_dissector_table_names(const char *table_name, const char *ui_name,
                               gpointer output)
 {
-  fprintf((FILE *)output, "\t%s (%s)\n", table_name, ui_name);
+  if ((prev_display_dissector_name == NULL) ||
+      (strcmp(prev_display_dissector_name, table_name) != 0)) {
+     fprintf((FILE *)output, "\t%s (%s)\n", table_name, ui_name);
+     prev_display_dissector_name = table_name;
+  }
 }
 
 /*
@@ -386,9 +400,13 @@ display_dissector_names(const gchar *table _U_, gpointer handle, gpointer output
     g_assert(proto_filter_name != NULL);
     g_assert(proto_ui_name != NULL);
 
-    fprintf((FILE *)output, "\t%s (%s)\n",
-            proto_filter_name,
-            proto_ui_name);
+    if ((prev_display_dissector_name == NULL) ||
+        (strcmp(prev_display_dissector_name, proto_filter_name) != 0)) {
+      fprintf((FILE *)output, "\t%s (%s)\n",
+              proto_filter_name,
+              proto_ui_name);
+       prev_display_dissector_name = proto_filter_name;
+    }
   }
 }
 
@@ -439,6 +457,16 @@ find_protocol_name_func(const gchar *table _U_, gpointer handle, gpointer user_d
   }
 }
 
+/*
+ * Allow dissector key names to be sorted alphabetically
+ */
+
+static gint
+compare_dissector_key_name(gconstpointer dissector_a, gconstpointer dissector_b)
+{
+  return strcmp((const char*)dissector_a, (const char*)dissector_b);
+}
+
 /*
  * Print all layer type names supported.
  * We send the output to the stream described by the handle output.
@@ -448,7 +476,8 @@ static void
 fprint_all_layer_types(FILE *output)
 
 {
-  dissector_all_tables_foreach_table(display_dissector_table_names, (gpointer)output);
+  prev_display_dissector_name = NULL;
+  dissector_all_tables_foreach_table(display_dissector_table_names, (gpointer)output, (GCompareFunc)compare_dissector_key_name);
 }
 
 /*
@@ -461,6 +490,7 @@ static void
 fprint_all_protocols_for_layer_types(FILE *output, gchar *table_name)
 
 {
+  prev_display_dissector_name = NULL;
   dissector_table_foreach_handle(table_name,
                                  display_dissector_names,
                                  (gpointer)output);
@@ -834,6 +864,11 @@ main(int argc, char *argv[])
   int                  optind_initial;
   gchar               *output_only = NULL;
 
+#ifdef HAVE_PCAP_REMOTE
+#define OPTSTRING_A "A:"
+#else
+#define OPTSTRING_A ""
+#endif
 #ifdef HAVE_LIBPCAP
 #if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
 #define OPTSTRING_B "B:"
@@ -850,7 +885,7 @@ main(int argc, char *argv[])
 #define OPTSTRING_I ""
 #endif
 
-#define OPTSTRING "2a:A:b:" OPTSTRING_B "c:C:d:De:E:f:F:G:hH:i:" OPTSTRING_I "K:lLnN:o:O:pPqr:R:s:S:t:T:u:vVw:W:xX:y:z:"
+#define OPTSTRING "2a:" OPTSTRING_A "b:" OPTSTRING_B "c:C:d:De:E:f:F:G:hH:i:" OPTSTRING_I "K:lLnN:o:O:pPqr:R:s:S:t:T:u:vVw:W:xX:y:z:"
 
   static const char    optstring[] = OPTSTRING;
 
@@ -961,10 +996,6 @@ main(int argc, char *argv[])
 #endif
   register_all_tap_listeners();
 
-  /* Now register the preferences for any non-dissector modules.
-     We must do that before we read the preferences as well. */
-  prefs_register_modules();
-
   /* If invoked with the "-G" flag, we dump out information based on
      the argument to the "-G" flag; if no argument is specified,
      for backwards compatibility we dump out a glossary of display
@@ -989,8 +1020,12 @@ main(int argc, char *argv[])
         proto_registrar_dump_protocols();
       else if (strcmp(argv[2], "values") == 0)
         proto_registrar_dump_values();
+      else if (strcmp(argv[2], "ftypes") == 0)
+        proto_registrar_dump_ftypes();
       else if (strcmp(argv[2], "decodes") == 0)
         dissector_dump_decodes();
+      else if (strcmp(argv[2], "heuristic-decodes") == 0)
+        dissector_dump_heur_decodes();
       else if (strcmp(argv[2], "defaultprefs") == 0)
         write_prefs(NULL);
       else if (strcmp(argv[2], "plugins") == 0)
@@ -1039,9 +1074,6 @@ main(int argc, char *argv[])
     pf_path = NULL;
   }
 
-  /* Set the name resolution code's flags from the preferences. */
-  gbl_resolv_flags = prefs_p->name_resolve;
-
   /* Read the disabled protocols file. */
   read_disabled_protos_list(&gdp_path, &gdp_open_errno, &gdp_read_errno,
                             &dp_path, &dp_open_errno, &dp_read_errno);
@@ -1091,6 +1123,9 @@ main(int argc, char *argv[])
     case 'f':        /* capture filter */
     case 'i':        /* Use interface x */
     case 'p':        /* Don't capture in promiscuous mode */
+#ifdef HAVE_PCAP_REMOTE
+    case 'A':        /* Authentication */
+#endif
 #ifdef HAVE_PCAP_CREATE
     case 'I':        /* Capture in monitor mode, if available */
 #endif
@@ -1128,6 +1163,7 @@ main(int argc, char *argv[])
       if (if_list == NULL) {
         switch (err) {
         case CANT_GET_INTERFACE_LIST:
+        case DONT_HAVE_PCAP:
           cmdarg_err("%s", err_str);
           g_free(err_str);
           break;
@@ -1208,11 +1244,12 @@ main(int argc, char *argv[])
 #endif
       break;
     case 'n':        /* No name resolution */
-      gbl_resolv_flags = RESOLV_NONE;
+      gbl_resolv_flags.mac_name = FALSE;
+      gbl_resolv_flags.network_name = FALSE;
+      gbl_resolv_flags.transport_name = FALSE;
+      gbl_resolv_flags.concurrent_dns = FALSE;
       break;
     case 'N':        /* Select what types of addresses/port #s to resolve */
-      if (gbl_resolv_flags == RESOLV_ALL)
-        gbl_resolv_flags = RESOLV_NONE;
       badopt = string_to_name_resolve(optarg, &gbl_resolv_flags);
       if (badopt != '\0') {
         cmdarg_err("-N specifies unknown resolving option '%c';",
@@ -1327,7 +1364,6 @@ main(int argc, char *argv[])
       g_string_free(comp_info_str, TRUE);
       g_string_free(runtime_info_str, TRUE);
       return 0;
-      break;
     }
     case 'O':        /* Only output these protocols */
       output_only = g_strdup(optarg);
@@ -1774,22 +1810,9 @@ main(int argc, char *argv[])
        (or get a list of link-layer types for a live capture device);
        do we have support for live captures? */
 #ifdef HAVE_LIBPCAP
-
-#ifdef _WIN32
-    if (!has_wpcap) {
-      char *detailed_err;
-
-      cmdarg_err("WinPcap couldn't be found.");
-      detailed_err = cant_load_winpcap_err("TShark");
-      cmdarg_err_cont("%s", detailed_err);
-      g_free(detailed_err);
-      return 2;
-    }
-#endif
-
     /* trim the interface name and exit if that failed */
     if (!capture_opts_trim_iface(&global_capture_opts,
-        (prefs_p->capture_device) ? get_if_name(prefs_p->capture_device) : NULL)) {
+        ((prefs_p->capture_device) && (*prefs_p->capture_device != '\0')) ? get_if_name(prefs_p->capture_device) : NULL)) {
         return 2;
     }
 
@@ -2056,6 +2079,7 @@ capture(void)
      the capture child to finish; it will report that it finished,
      or will exit abnormally, so  we'll stop reading from the sync
      pipe, pick up the exit status, and quit. */
+  memset(&action, 0, sizeof(action));
   action.sa_handler = capture_cleanup;
   action.sa_flags = SA_RESTART;
   sigemptyset(&action.sa_mask);
@@ -2421,15 +2445,17 @@ capture_input_drops(capture_options *capture_opts _U_, guint32 dropped)
 void
 capture_input_closed(capture_options *capture_opts, gchar *msg)
 {
+  capture_file *cf = (capture_file *) capture_opts->cf;
+
   if (msg != NULL)
     fprintf(stderr, "tshark: %s\n", msg);
 
   report_counts();
 
-  if(capture_opts->cf != NULL && ((capture_file *) capture_opts->cf)->wth != NULL) {
-    wtap_close(((capture_file *) capture_opts->cf)->wth);
-    if(((capture_file *) capture_opts->cf)->user_saved == FALSE) {
-      ws_unlink(((capture_file *) capture_opts->cf)->filename);
+  if(cf != NULL && cf->wth != NULL) {
+    wtap_close(cf->wth);
+    if(cf->is_tempfile) {
+      ws_unlink(cf->filename);
     }
   }
 #ifdef USE_BROKEN_G_MAIN_LOOP
@@ -2498,6 +2524,7 @@ process_packet_first_pass(capture_file *cf,
 {
   frame_data fdlocal;
   guint32 framenum;
+  gboolean create_proto_tree = FALSE;
   epan_dissect_t edt;
   gboolean passed;
 
@@ -2516,15 +2543,19 @@ process_packet_first_pass(capture_file *cf,
      run a read filter, or we're going to process taps, set up to
      do a dissection and do so. */
   if (do_dissection) {
-    if (gbl_resolv_flags)
+    if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || 
+        gbl_resolv_flags.transport_name || gbl_resolv_flags.concurrent_dns)
       /* Grab any resolved addresses */
       host_name_lookup_process(NULL);
 
+      if (cf->rfcode)
+        create_proto_tree = TRUE;
+
     /* The protocol tree will be "visible", i.e., printed, only if we're
        printing packet details, which is true if we're printing stuff
        ("print_packet_info" is true) and we're in verbose mode ("verbose"
        is true). */
-    epan_dissect_init(&edt, FALSE, FALSE);
+    epan_dissect_init(&edt, create_proto_tree, FALSE);
 
     /* If we're running a read filter, prime the epan_dissect_t with that
        filter. */
@@ -2532,7 +2563,7 @@ process_packet_first_pass(capture_file *cf,
       epan_dissect_prime_dfilter(&edt, cf->rfcode);
 
     frame_data_set_before_dissect(&fdlocal, &cf->elapsed_time,
-                                  &first_ts, &prev_dis_ts, &prev_cap_ts);
+                                  &first_ts, prev_dis, prev_cap);
 
     epan_dissect_run(&edt, pseudo_header, pd, &fdlocal, NULL);
 
@@ -2542,11 +2573,16 @@ process_packet_first_pass(capture_file *cf,
   }
 
   if (passed) {
-    frame_data_set_after_dissect(&fdlocal, &cum_bytes, &prev_dis_ts);
+    frame_data_set_after_dissect(&fdlocal, &cum_bytes);
+    prev_dis_frame = fdlocal;
+    prev_dis = &prev_dis_frame;
     frame_data_sequence_add(cf->frames, &fdlocal);
     cf->count++;
   }
 
+  prev_cap_frame = fdlocal;
+  prev_cap = &prev_cap_frame;
+
   if (do_dissection)
     epan_dissect_cleanup(&edt);
 
@@ -2572,7 +2608,8 @@ process_packet_second_pass(capture_file *cf, frame_data *fdata,
      run a read filter, or we're going to process taps, set up to
      do a dissection and do so. */
   if (do_dissection) {
-    if (gbl_resolv_flags)
+    if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || 
+        gbl_resolv_flags.transport_name || gbl_resolv_flags.concurrent_dns)
       /* Grab any resolved addresses */
       host_name_lookup_process(NULL);
 
@@ -2672,15 +2709,25 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
   gint         linktype;
   int          snapshot_length;
   wtap_dumper *pdh;
+  guint32      framenum;
   int          err;
   gchar        *err_info = NULL;
   gint64       data_offset;
   char         *save_file_string = NULL;
   gboolean     filtering_tap_listeners;
   guint        tap_flags;
+  wtapng_section_t *shb_hdr;
+  wtapng_iface_descriptions_t *idb_inf;
+  char         appname[100];
 
+  shb_hdr = wtap_file_get_shb_info(cf->wth);
+  idb_inf = wtap_file_get_idb_info(cf->wth);
 #ifdef PCAP_NG_DEFAULT
-  linktype = WTAP_ENCAP_PER_PACKET;
+  if (idb_inf->number_of_interfaces > 0) {
+    linktype = WTAP_ENCAP_PER_PACKET;
+  } else {
+    linktype = wtap_file_encap(cf->wth);
+  }
 #else
   linktype = wtap_file_encap(cf->wth);
 #endif
@@ -2694,8 +2741,17 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
       /* Snapshot length of input file not known. */
       snapshot_length = WTAP_MAX_PACKET_SIZE;
     }
-    pdh = wtap_dump_open(save_file, out_file_type, linktype, snapshot_length,
-                         FALSE /* compressed */, &err);
+    /* If we don't have an application name add Tshark */
+    if(shb_hdr->shb_user_appl == NULL) {
+        g_snprintf(appname, sizeof(appname), "TShark " VERSION "%s", wireshark_svnversion);
+        shb_hdr->shb_user_appl = appname;
+    }
+
+    pdh = wtap_dump_open_ng(save_file, out_file_type, linktype, snapshot_length,
+        FALSE /* compressed */, shb_hdr, idb_inf, &err);
+
+    g_free(idb_inf);
+    idb_inf = NULL;
 
     if (pdh == NULL) {
       /* We couldn't set up to write to the capture file. */
@@ -2707,8 +2763,8 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
 
       case WTAP_ERR_UNSUPPORTED_ENCAP:
       case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
-        cmdarg_err("The capture file being read can't be written in "
-          "the format \"%s\".", wtap_encap_short_string(linktype));
+        cmdarg_err("The capture file being read can't be written as a "
+          "\"%s\" file.", wtap_file_type_short_string(out_file_type));
         break;
 
       case WTAP_ERR_CANT_OPEN:
@@ -2753,7 +2809,6 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
   tap_flags = union_of_tap_listener_flags();
 
   if (perform_two_pass_analysis) {
-    guint32 framenum;
     frame_data *fdata;
     int old_max_packet_count = max_packet_count;
 
@@ -2799,8 +2854,28 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
                            wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
                            &err)) {
               /* Error writing to a capture file */
-              show_capture_file_io_error(save_file, err, FALSE);
+              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.
+                 *
+                 * XXX - framenum is not necessarily the frame number in
+                 * the input file if there was a read filter.
+                 */
+                fprintf(stderr,
+                        "Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.\n",
+                        framenum, cf->filename,
+                        wtap_file_type_short_string(out_file_type));
+                break;
+
+              default:
+                show_capture_file_io_error(save_file, err, FALSE);
+                break;
+              }
               wtap_dump_close(pdh, &err);
+              g_free(shb_hdr);
               exit(2);
             }
           }
@@ -2818,7 +2893,10 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
     }
   }
   else {
+    framenum = 0;
     while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
+      framenum++;
+
       if (process_packet(cf, data_offset, wtap_phdr(cf->wth),
                          wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
                          filtering_tap_listeners, tap_flags)) {
@@ -2830,8 +2908,25 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
                          wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
                          &err)) {
             /* Error writing to a capture file */
-            show_capture_file_io_error(save_file, err, FALSE);
+            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.
+               */
+              fprintf(stderr,
+                      "Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.\n",
+                      framenum, cf->filename,
+                      wtap_file_type_short_string(out_file_type));
+              break;
+
+            default:
+              show_capture_file_io_error(save_file, err, FALSE);
+              break;
+            }
             wtap_dump_close(pdh, &err);
+            g_free(shb_hdr);
             exit(2);
           }
         }
@@ -2875,6 +2970,12 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
 #endif
     switch (err) {
 
+    case WTAP_ERR_UNSUPPORTED:
+      cmdarg_err("The file \"%s\" contains record data that TShark doesn't support.\n(%s)",
+                 cf->filename, err_info);
+      g_free(err_info);
+      break;
+
     case WTAP_ERR_UNSUPPORTED_ENCAP:
       cmdarg_err("The file \"%s\" has a packet with a network type that TShark doesn't support.\n(%s)",
                  cf->filename, err_info);
@@ -2891,7 +2992,7 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
                  cf->filename);
       break;
 
-    case WTAP_ERR_BAD_RECORD:
+    case WTAP_ERR_BAD_FILE:
       cmdarg_err("The file \"%s\" appears to be damaged or corrupt.\n(%s)",
                  cf->filename, err_info);
       g_free(err_info);
@@ -2932,6 +3033,7 @@ out:
   cf->wth = NULL;
 
   g_free(save_file_string);
+  g_free(shb_hdr);
 
   return err;
 }
@@ -2961,7 +3063,8 @@ process_packet(capture_file *cf, gint64 offset, const struct wtap_pkthdr *whdr,
      run a read filter, or we're going to process taps, set up to
      do a dissection and do so. */
   if (do_dissection) {
-    if (print_packet_info && gbl_resolv_flags)
+    if (print_packet_info && (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || 
+        gbl_resolv_flags.transport_name || gbl_resolv_flags.concurrent_dns))
       /* Grab any resolved addresses */
       host_name_lookup_process(NULL);
 
@@ -3000,7 +3103,7 @@ process_packet(capture_file *cf, gint64 offset, const struct wtap_pkthdr *whdr,
       cinfo = NULL;
 
     frame_data_set_before_dissect(&fdata, &cf->elapsed_time,
-                                  &first_ts, &prev_dis_ts, &prev_cap_ts);
+                                  &first_ts, prev_dis, prev_cap);
 
     epan_dissect_run(&edt, pseudo_header, pd, &fdata, cinfo);
 
@@ -3012,7 +3115,9 @@ process_packet(capture_file *cf, gint64 offset, const struct wtap_pkthdr *whdr,
   }
 
   if (passed) {
-    frame_data_set_after_dissect(&fdata, &cum_bytes, &prev_dis_ts);
+    frame_data_set_after_dissect(&fdata, &cum_bytes);
+    prev_dis_frame = fdata;
+    prev_dis = &prev_dis_frame;
 
     /* Process this packet. */
     if (print_packet_info) {
@@ -3053,6 +3158,9 @@ process_packet(capture_file *cf, gint64 offset, const struct wtap_pkthdr *whdr,
     }
   }
 
+  prev_cap_frame = fdata;
+  prev_cap = &prev_cap_frame;
+
   if (do_dissection) {
     epan_dissect_cleanup(&edt);
     frame_data_cleanup(&fdata);
@@ -3350,15 +3458,17 @@ print_packet(capture_file *cf, epan_dissect_t *edt)
     switch (output_action) {
 
     case WRITE_TEXT:
+      /* Only initialize the fields that are actually used in proto_tree_print.
+       * This is particularly important for .range, as that's heap memory which
+       * we would otherwise have to g_free().
       print_args.to_file = TRUE;
       print_args.format = print_format;
       print_args.print_summary = !verbose;
-      print_args.print_hex = verbose && print_hex;
       print_args.print_formfeed = FALSE;
-      print_args.print_dissections = verbose ? print_dissections_expanded : print_dissections_none;
-
-      /* init the packet range */
       packet_range_init(&print_args.range);
+      */
+      print_args.print_hex = verbose && print_hex;
+      print_args.print_dissections = verbose ? print_dissections_expanded : print_dissections_none;
 
       if (!proto_tree_print(&print_args, edt, print_stream))
         return FALSE;
@@ -3463,8 +3573,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;
 
   cf->cd_t      = wtap_file_type(cf->wth);
   cf->count     = 0;
@@ -3479,8 +3589,8 @@ cf_open(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
     cf->has_snap = TRUE;
   nstime_set_zero(&cf->elapsed_time);
   nstime_set_unset(&first_ts);
-  nstime_set_unset(&prev_dis_ts);
-  nstime_set_unset(&prev_cap_ts);
+  prev_dis = NULL;
+  prev_cap = NULL;
 
   cf->state = FILE_READ_IN_PROGRESS;
 
@@ -3581,6 +3691,11 @@ cf_open_error_message(int err, gchar *err_info, gboolean for_writing,
       errmsg = "The file \"%s\" is a \"special file\" or socket or other non-regular file.";
       break;
 
+    case WTAP_ERR_RANDOM_OPEN_PIPE:
+      /* Seen only when opening a capture file for reading. */
+      errmsg = "The file \"%s\" is a pipe or FIFO; TShark can't read pipe or FIFO files in two-pass mode.";
+      break;
+
     case WTAP_ERR_FILE_UNKNOWN_FORMAT:
       /* Seen only when opening a capture file for reading. */
       errmsg = "The file \"%s\" isn't a capture file in a format TShark understands.";
@@ -3598,8 +3713,8 @@ cf_open_error_message(int err, gchar *err_info, gboolean for_writing,
     case WTAP_ERR_CANT_WRITE_TO_PIPE:
       /* Seen only when opening a capture file for writing. */
       g_snprintf(errmsg_errno, sizeof(errmsg_errno),
-                 "The file \"%%s\" is a pipe, and %s capture files can't be "
-                 "written to a pipe.", wtap_file_type_string(file_type));
+                 "The file \"%%s\" is a pipe, and \"%s\" capture files can't be "
+                 "written to a pipe.", wtap_file_type_short_string(file_type));
       errmsg = errmsg_errno;
       break;
 
@@ -3609,25 +3724,30 @@ cf_open_error_message(int err, gchar *err_info, gboolean for_writing,
       break;
 
     case WTAP_ERR_UNSUPPORTED_ENCAP:
-      if (for_writing)
-        errmsg = "TShark can't save this capture in that format.";
-      else {
+      if (for_writing) {
+        g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                   "TShark can't save this capture as a \"%s\" file.",
+                   wtap_file_type_short_string(file_type));
+      } else {
         g_snprintf(errmsg_errno, sizeof(errmsg_errno),
                  "The file \"%%s\" is a capture for a network type that TShark doesn't support.\n"
                  "(%s)", err_info);
         g_free(err_info);
-        errmsg = errmsg_errno;
       }
+      errmsg = errmsg_errno;
       break;
 
     case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
-      if (for_writing)
-        errmsg = "TShark can't save this capture in that format.";
-      else
+      if (for_writing) {
+        g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+                   "TShark can't save this capture as a \"%s\" file.",
+                   wtap_file_type_short_string(file_type));
+        errmsg = errmsg_errno;
+      } else
         errmsg = "The file \"%s\" is a capture for a network type that TShark doesn't support.";
       break;
 
-    case WTAP_ERR_BAD_RECORD:
+    case WTAP_ERR_BAD_FILE:
       /* Seen only when opening a capture file for reading. */
       g_snprintf(errmsg_errno, sizeof(errmsg_errno),
                "The file \"%%s\" appears to be damaged or corrupt.\n"
@@ -3652,6 +3772,10 @@ cf_open_error_message(int err, gchar *err_info, gboolean for_writing,
       errmsg = "A full header couldn't be written to the file \"%s\".";
       break;
 
+    case WTAP_ERR_COMPRESSION_NOT_SUPPORTED:
+      errmsg = "This file type cannot be written as a compressed file.";
+      break;
+
     case WTAP_ERR_DECOMPRESS:
       /* Seen only when opening a capture file for reading. */
       g_snprintf(errmsg_errno, sizeof(errmsg_errno),