Fix crash when exporting packet dissection as plain text on Windows (regression intro...
[metze/wireshark/wip.git] / capinfos.c
index c2f2e8ed4c04da18cc7eb57256684514bc7fbf98..a799be22bf5ecc02c9c4d4c22e8225caf272fe06 100644 (file)
@@ -21,7 +21,7 @@
  *
  * 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.
  */
 
 /*
@@ -56,9 +56,7 @@
  */
 
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -148,12 +146,105 @@ static gboolean cap_data_rate_byte = TRUE;  /* Report data rate bytes/sec */
 static gboolean cap_data_rate_bit = TRUE;   /* Report data rate bites/sec */
 static gboolean cap_packet_size = TRUE;     /* Report average packet size */
 static gboolean cap_packet_rate = TRUE;     /* Report average packet rate */
-static gboolean cap_in_order = TRUE;        /* Report if packets are in chronological order (True/False) */
+static gboolean cap_order = TRUE;           /* Report if packets are in chronological order (True/False) */
 
 #ifdef HAVE_LIBGCRYPT
 static gboolean cap_file_hashes = TRUE;     /* Calculate file hashes */
 #endif
 
+#ifdef USE_GOPTION
+static gboolean cap_help = FALSE;
+static gboolean table_report = FALSE;
+static GOptionEntry general_entries[] =
+{
+/* General */
+  { "type", 't', 0, G_OPTION_ARG_NONE, &cap_file_type,
+    "display the capture file type", NULL },
+  { "Encapsulation", 'E', 0, G_OPTION_ARG_NONE, &cap_file_encap,
+    "display the capture file encapsulation", NULL },
+#ifdef HAVE_LIBGCRYPT
+  { "Hash", 'H', 0, G_OPTION_ARG_NONE, &cap_file_hashes,
+    "display the SHA1, RMD160, and MD5 hashes of the file", NULL },
+#endif /* HAVE_LIBGCRYPT */
+ { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+static GOptionEntry size_entries[] =
+{
+/* Size */
+  { "packets", 'c', 0, G_OPTION_ARG_NONE, &cap_packet_count,
+    "display the number of packets", NULL },
+  { "size", 's', 0, G_OPTION_ARG_NONE, &cap_file_size,
+    "display the size of the file (in bytes)", NULL },
+  { "tot-len-of-pkts", 'd', 0, G_OPTION_ARG_NONE, &cap_data_size,
+    "display the total length of all packets (in bytes)", NULL },
+  { "snap", 'l', 0, G_OPTION_ARG_NONE, &cap_snaplen,
+    "display the packet size limit (snapshot length)", NULL },
+  { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+static GOptionEntry time_entries[] =
+{
+/* Time */
+  { "duration", 'u', 0, G_OPTION_ARG_NONE, &cap_duration,
+    "display the capture duration (in seconds)", NULL },
+  { "start", 'a', 0, G_OPTION_ARG_NONE, &cap_start_time,
+    "display the capture start time", NULL },
+  { "end", 'e', 0, G_OPTION_ARG_NONE, &cap_end_time,
+    "display the capture end time", NULL },
+  { "cron", 'o', 0, G_OPTION_ARG_NONE, &cap_order,
+    "display the capture file chronological status (True/False)", NULL },
+  { "start-end-time-sec", 'S', 0, G_OPTION_ARG_NONE, &time_as_secs,
+    "display start and end times as seconds", NULL },
+  { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+
+static GOptionEntry stats_entries[] =
+{
+/* Statistics */
+  { "bytes", 'y', 0, G_OPTION_ARG_NONE, &cap_data_rate_byte,
+    "display average data rate (in bytes/s)", NULL },
+  { "bits", 'i', 0, G_OPTION_ARG_NONE, &cap_data_rate_bit,
+    "display average data rate (in bits/s)", NULL },
+  { "packet-bytes", 'z', 0, G_OPTION_ARG_NONE, &cap_packet_size,
+    "display average packet size (in bytes)", NULL },
+  { "packets", 'x', 0, G_OPTION_ARG_NONE, &cap_packet_rate,
+    "display average packet rate (in packets/s)", NULL },
+  { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+
+static GOptionEntry output_format_entries[] =
+{
+/* Output format */
+  { "long", 'L', 0, G_OPTION_ARG_NONE, &long_report,
+    "generate long report (default)", NULL },
+  { "Table", 'T', 0, G_OPTION_ARG_NONE, &table_report,
+    "generate table report", NULL },
+  { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+
+static GOptionEntry table_report_entries[] =
+{
+/* Table report */
+  { "header-rec", 'R', 0, G_OPTION_ARG_NONE, &table_report_header,
+    "generate header record (default)", NULL },
+  { "no-table", 'r', 0, G_OPTION_ARG_NONE, &table_report_header,
+    "do not generate header record", NULL },
+  { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+
+static GOptionEntry misc_entries[] =
+{
+  { "helpcompat", 'h', 0, G_OPTION_ARG_NONE, &cap_help,
+    "display help", NULL },
+ { NULL,'\0',0,G_OPTION_ARG_NONE,NULL,NULL,NULL }
+};
+
+GOptionContext *ctx;
+GOptionGroup *general_grp, *size_grp, *time_grp, *stats_grp, *output_grp, *table_report_grp;
+GError *parse_err = NULL;
+
+#endif /* USE_GOPTION */
+
 #ifdef HAVE_LIBGCRYPT
 #define HASH_SIZE_SHA1 20
 #define HASH_SIZE_RMD160 20
@@ -172,13 +263,31 @@ static gchar file_md5[HASH_STR_SIZE];
 #define FILE_HASH_OPT ""
 #endif /* HAVE_LIBGCRYPT */
 
+/*
+ * If we have at least two packets with time stamps, and they're not in
+ * order - i.e., the later packet has a time stamp older than the earlier
+ * packet - the time stamps are known not to be in order.
+ *
+ * If every packet has a time stamp, and they're all in order, the time
+ * stamp is known to be in order.
+ *
+ * Otherwise, we have no idea.
+ */
+typedef enum {
+  IN_ORDER,
+  NOT_IN_ORDER,
+  ORDER_UNKNOWN
+} order_t;
+
 typedef struct _capture_info {
   const char    *filename;
   guint16       file_type;
+  gboolean      iscompressed;
   int           file_encap;
   gint64        filesize;
 
   guint64       packet_bytes;
+  gboolean      times_known;
   double        start_time;
   double        stop_time;
   guint32       packet_count;
@@ -193,11 +302,13 @@ typedef struct _capture_info {
   double        packet_rate;
   double        packet_size;
   double        data_rate;              /* in bytes */
-  gboolean      in_order;
+  gboolean      know_order;
+  order_t       order;
 
   int          *encap_counts;           /* array of per_packet encap counts; array has one entry per wtap_encap type */
 } capture_info;
 
+
 static void
 enable_all_infos(void)
 {
@@ -213,7 +324,7 @@ enable_all_infos(void)
   cap_duration = TRUE;
   cap_start_time = TRUE;
   cap_end_time = TRUE;
-  cap_in_order = TRUE;
+  cap_order = TRUE;
 
   cap_data_rate_byte = TRUE;
   cap_data_rate_bit = TRUE;
@@ -240,7 +351,7 @@ disable_all_infos(void)
   cap_duration       = FALSE;
   cap_start_time     = FALSE;
   cap_end_time       = FALSE;
-  cap_in_order       = FALSE;
+  cap_order          = FALSE;
 
   cap_data_rate_byte = FALSE;
   cap_data_rate_bit  = FALSE;
@@ -252,6 +363,25 @@ disable_all_infos(void)
 #endif /* HAVE_LIBGCRYPT */
 }
 
+static const gchar *
+order_string(order_t order)
+{
+  switch (order) {
+
+  case IN_ORDER:
+    return "True";
+
+  case NOT_IN_ORDER:
+    return "False";
+
+  case ORDER_UNKNOWN:
+    return "Unknown";
+
+  default:
+    return "???";  /* "cannot happen" (the next step is "Profit!") */
+  }
+}
+
 static gchar *
 time_string(time_t timer, capture_info *cf_info, gboolean want_lf)
 {
@@ -259,7 +389,7 @@ time_string(time_t timer, capture_info *cf_info, gboolean want_lf)
   static gchar time_string_buf[20];
   char *time_string_ctime;
 
-  if (cf_info->packet_count > 0) {
+  if (cf_info->times_known && cf_info->packet_count > 0) {
     if (time_as_secs) {
       /* XXX - Would it be useful to show sub-second precision? */
       g_snprintf(time_string_buf, 20, "%lu%s", (unsigned long)timer, lf);
@@ -321,7 +451,9 @@ print_stats(const gchar *filename, capture_info *cf_info)
   stop_time_t = (time_t)cf_info->stop_time;
 
   if (filename)           printf     ("File name:           %s\n", filename);
-  if (cap_file_type)      printf     ("File type:           %s\n", file_type_string);
+  if (cap_file_type)      printf     ("File type:           %s%s\n",
+                                      file_type_string,
+                                      cf_info->iscompressed ? " (gzip compressed)" : "");
   if (cap_file_encap)     printf     ("File encapsulation:  %s\n", file_encap_string);
   if (cap_file_encap && (cf_info->file_encap == WTAP_ENCAP_PER_PACKET)) {
     int i;
@@ -344,13 +476,23 @@ print_stats(const gchar *filename, capture_info *cf_info)
   if (cap_packet_count)   printf     ("Number of packets:   %u\n", cf_info->packet_count);
   if (cap_file_size)      printf     ("File size:           %" G_GINT64_MODIFIER "d bytes\n", cf_info->filesize);
   if (cap_data_size)      printf     ("Data size:           %" G_GINT64_MODIFIER "u bytes\n", cf_info->packet_bytes);
-  if (cap_duration)       print_value("Capture duration:    ", 0, " seconds",   cf_info->duration);
-  if (cap_start_time)     printf     ("Start time:          %s", time_string(start_time_t, cf_info, TRUE));
-  if (cap_end_time)       printf     ("End time:            %s", time_string(stop_time_t, cf_info, TRUE));
-  if (cap_data_rate_byte) print_value("Data byte rate:      ", 2, " bytes/sec",   cf_info->data_rate);
-  if (cap_data_rate_bit)  print_value("Data bit rate:       ", 2, " bits/sec",    cf_info->data_rate*8);
+  if (cf_info->times_known) {
+    if (cap_duration)
+                          print_value("Capture duration:    ", 0, " seconds",   cf_info->duration);
+    if (cap_start_time)
+                          printf     ("Start time:          %s", time_string(start_time_t, cf_info, TRUE));
+    if (cap_end_time)
+                          printf     ("End time:            %s", time_string(stop_time_t, cf_info, TRUE));
+    if (cap_data_rate_byte)
+                          print_value("Data byte rate:      ", 2, " bytes/sec",   cf_info->data_rate);
+    if (cap_data_rate_bit)
+                          print_value("Data bit rate:       ", 2, " bits/sec",    cf_info->data_rate*8);
+  }
   if (cap_packet_size)    printf     ("Average packet size: %.2f bytes\n",        cf_info->packet_size);
-  if (cap_packet_rate)    print_value("Average packet rate: ", 2, " packets/sec", cf_info->packet_rate);
+  if (cf_info->times_known) {
+    if (cap_packet_rate) 
+                          print_value("Average packet rate: ", 2, " packets/sec", cf_info->packet_rate);
+  }
 #ifdef HAVE_LIBGCRYPT
   if (cap_file_hashes) {
                           printf     ("SHA1:                %s\n", file_sha1);
@@ -358,7 +500,7 @@ print_stats(const gchar *filename, capture_info *cf_info)
                           printf     ("MD5:                 %s\n", file_md5);
   }
 #endif /* HAVE_LIBGCRYPT */
-  if (cap_in_order)       printf     ("Strict time order:   %s\n", (cf_info->in_order) ? "True" : "False");
+  if (cap_order)          printf     ("Strict time order:   %s\n", order_string(cf_info->order));
 }
 
 static void
@@ -411,7 +553,7 @@ print_stats_table_header(void)
                           print_stats_table_header_label("MD5");
   }
 #endif /* HAVE_LIBGCRYPT */
-  if (cap_in_order)       print_stats_table_header_label("Strict time order");
+  if (cap_order)          print_stats_table_header_label("Strict time order");
 
   printf("\n");
 }
@@ -508,7 +650,10 @@ print_stats_table(const gchar *filename, capture_info *cf_info)
   if (cap_duration) {
     putsep();
     putquote();
-    printf("%f", cf_info->duration);
+    if (cf_info->times_known)
+      printf("%f", cf_info->duration);
+    else
+      printf("n/a");
     putquote();
   }
 
@@ -529,14 +674,20 @@ print_stats_table(const gchar *filename, capture_info *cf_info)
   if (cap_data_rate_byte) {
     putsep();
     putquote();
-    printf("%.2f", cf_info->data_rate);
+    if (cf_info->times_known)
+      printf("%.2f", cf_info->data_rate);
+    else
+      printf("n/a");
     putquote();
   }
 
   if (cap_data_rate_bit) {
     putsep();
     putquote();
-    printf("%.2f", cf_info->data_rate*8);
+    if (cf_info->times_known)
+      printf("%.2f", cf_info->data_rate*8);
+    else
+      printf("n/a");
     putquote();
   }
 
@@ -550,7 +701,10 @@ print_stats_table(const gchar *filename, capture_info *cf_info)
   if (cap_packet_rate) {
     putsep();
     putquote();
-    printf("%.2f", cf_info->packet_rate);
+    if (cf_info->times_known)
+      printf("%.2f", cf_info->packet_rate);
+    else
+      printf("n/a");
     putquote();
   }
 
@@ -573,10 +727,10 @@ print_stats_table(const gchar *filename, capture_info *cf_info)
   }
 #endif /* HAVE_LIBGCRYPT */
 
-  if (cap_in_order) {
+  if (cap_order) {
     putsep();
     putquote();
-    printf("%s", (cf_info->in_order) ? "True" : "False");
+    printf("%s", order_string(cf_info->order));
     putquote();
   }
 
@@ -597,32 +751,40 @@ process_cap_file(wtap *wth, const char *filename)
   guint32               snaplen_max_inferred =          0;
   const struct wtap_pkthdr *phdr;
   capture_info          cf_info;
+  gboolean             have_times = TRUE;
   double                start_time = 0;
   double                stop_time  = 0;
   double                cur_time   = 0;
   double               prev_time = 0;
-  gboolean             in_order = TRUE;
+  gboolean             know_order = FALSE;
+  order_t              order = IN_ORDER;
 
   cf_info.encap_counts = g_malloc0(WTAP_NUM_ENCAP_TYPES * sizeof(int));
 
   /* Tally up data that we need to parse through the file to find */
   while (wtap_read(wth, &err, &err_info, &data_offset))  {
     phdr = wtap_phdr(wth);
-    prev_time = cur_time;
-    cur_time = secs_nsecs(&phdr->ts);
-    if(packet==0) {
-      start_time = cur_time;
-      stop_time = cur_time;
+    if (phdr->presence_flags & WTAP_HAS_TS) {
       prev_time = cur_time;
-    }
-    if (cur_time < prev_time) {
-      in_order = FALSE;
-    }
-    if (cur_time < start_time) {
-      start_time = cur_time;
-    }
-    if (cur_time > stop_time) {
-      stop_time = cur_time;
+      cur_time = secs_nsecs(&phdr->ts);
+      if(packet==0) {
+        start_time = cur_time;
+        stop_time = cur_time;
+        prev_time = cur_time;
+      }
+      if (cur_time < prev_time) {
+        order = NOT_IN_ORDER;
+      }
+      if (cur_time < start_time) {
+        start_time = cur_time;
+      }
+      if (cur_time > stop_time) {
+        stop_time = cur_time;
+      }
+    } else {
+      have_times = FALSE; /* at least one packet has no time stamp */
+      if (order != NOT_IN_ORDER)
+        order = ORDER_UNKNOWN;
     }
 
     bytes+=phdr->len;
@@ -682,6 +844,7 @@ process_cap_file(wtap *wth, const char *filename)
 
   /* File Type */
   cf_info.file_type = wtap_file_type(wth);
+  cf_info.iscompressed = wtap_iscompressed(wth);
 
   /* File Encapsulation */
   cf_info.file_encap = wtap_file_encap(wth);
@@ -700,10 +863,12 @@ process_cap_file(wtap *wth, const char *filename)
   cf_info.packet_count = packet;
 
   /* File Times */
+  cf_info.times_known = have_times;
   cf_info.start_time = start_time;
   cf_info.stop_time = stop_time;
   cf_info.duration = stop_time-start_time;
-  cf_info.in_order = in_order;
+  cf_info.know_order = know_order;
+  cf_info.order = order;
 
   /* Number of packet bytes */
   cf_info.packet_bytes = bytes;
@@ -877,7 +1042,54 @@ main(int argc, char *argv[])
 #endif
 
   /* Process the options */
+#ifdef USE_GOPTION
+  ctx = g_option_context_new(" <infile> ... - print information about capture file(s)");
+  general_grp = g_option_group_new("gen", "General infos:", 
+                               "Show general options", NULL, NULL);
+  size_grp = g_option_group_new("size", "Size infos:",
+                              "Show size options", NULL, NULL);
+  time_grp = g_option_group_new("time", "Time infos:",
+                              "Show time options", NULL, NULL);
+  stats_grp = g_option_group_new("stats", "Statistics infos:",
+                                "Show statistics options", NULL, NULL);
+  output_grp = g_option_group_new("output", "Output format:",
+                                "Show output format options", NULL, NULL);
+  table_report_grp = g_option_group_new("table", "Table report options:",
+                                "Show table report options", NULL, NULL);
+  g_option_group_add_entries(general_grp, general_entries);
+  g_option_group_add_entries(size_grp, size_entries);
+  g_option_group_add_entries(time_grp, time_entries);
+  g_option_group_add_entries(stats_grp, stats_entries);
+  g_option_group_add_entries(output_grp, output_format_entries);
+  g_option_group_add_entries(table_report_grp, table_report_entries);
+  g_option_context_add_main_entries(ctx, misc_entries, NULL);
+  g_option_context_add_group(ctx, general_grp);
+  g_option_context_add_group(ctx, size_grp);
+  g_option_context_add_group(ctx, time_grp);
+  g_option_context_add_group(ctx, stats_grp);
+  g_option_context_add_group(ctx, output_grp);
+  g_option_context_add_group(ctx, table_report_grp);
+  /* There's probably a better way to do this, but this works for now.
+     GOptions displays the name in argv[0] as the name of the
+     application.  This is reasonable, but because we actually have a
+     script wrapper that calls the executable.  The name that gets
+     displayed is not exactly the same as the command the user used
+     ran.
+   */
+  argv[0] = "capinfos";
+  ;
+  if( !g_option_context_parse(ctx, &argc, &argv, &parse_err) ) {
+    if(parse_err) g_print ("option parsing failed: %s\n", parse_err->message);
+    g_print("%s", g_option_context_get_help (ctx, TRUE, NULL));
+    exit(1);
+  }
+  if( cap_help || (argc < 2) ) {
+    g_print("%s", g_option_context_get_help (ctx, FALSE, NULL));
+    exit(0);
+  }
+  g_option_context_free(ctx);
 
+#endif /* USE_GOPTION */
   while ((opt = getopt(argc, argv, "tEcs" FILE_HASH_OPT "dluaeyizvhxoCALTRrSNqQBmb")) !=-1) {
 
     switch (opt) {
@@ -960,7 +1172,7 @@ main(int argc, char *argv[])
 
     case 'o':
       if (report_all_infos) disable_all_infos();
-      cap_in_order = TRUE;
+      cap_order = TRUE;
       break;
 
     case 'C':