Fix crash when exporting packet dissection as plain text on Windows (regression intro...
[metze/wireshark/wip.git] / capinfos.c
index ec2243a581efdb66215bc0f3857e2331dfb5699f..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.
  */
 
 /*
@@ -32,7 +32,7 @@
  * Continue processing additional files after
  * a wiretap open failure.  The new -C option
  * reverts to capinfos' original behavior which
- * is to cancels any further file processing at
+ * is to cancel any further file processing at
  * first file open failure.
  *
  * Change the behavior of how the default display
  * into a tab delimited text file, or to a comma
  * separated variables file (*.csv) instead of the
  * original "long" format.
+ *
+ * 2011-04-05: wmeier
+ * behaviour changed: Upon exit capinfos will return
+ *  an error status if an error occurred at any
+ *  point during "continuous" file processing.
+ *  (Previously a success status was always
+ *   returned if the -C option was not used).
+ *
+
  */
 
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <wsutil/file_util.h>
 #endif
 
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#else
+#ifndef HAVE_GETOPT
 #include "wsutil/wsgetopt.h"
 #endif
 
 #ifdef _WIN32
-#include <shellapi.h>
+#include <wsutil/unicode-utils.h>
 #endif /* _WIN32 */
 
 #include "svnversion.h"
@@ -141,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
@@ -165,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;
@@ -186,9 +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)
 {
@@ -204,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;
@@ -231,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;
@@ -243,42 +363,60 @@ disable_all_infos(void)
 #endif /* HAVE_LIBGCRYPT */
 }
 
-/*
- * ctime_no_lf()
- *
- * This function simply truncates the string returned
- * from the ctime() function to remove the trailing
- * '\n' character.
- *
- * The ctime() function returns a string formatted as:
- *   "Www Mmm dd hh:mm:ss yyyy\n"
- * The unwanted '\n' is the 24th character.
- */
-
-static gchar *
-ctime_no_lf(const time_t* timer)
+static const gchar *
+order_string(order_t order)
 {
-  gchar *time_string;
-  time_string = ctime(timer);
-  time_string[24] = '\0';
-  return(time_string);
+  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(const time_t *timer, capture_info *cf_info, gboolean want_lf)
+time_string(time_t timer, capture_info *cf_info, gboolean want_lf)
 {
   const gchar *lf = want_lf ? "\n" : "";
-  static gchar time_string_buf[15];
+  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, 15, "%lu%s", (unsigned long) *timer, lf);
+      g_snprintf(time_string_buf, 20, "%lu%s", (unsigned long)timer, lf);
       return time_string_buf;
-    } else if (want_lf) {
-      return ctime(timer);
     } else {
-      return ctime_no_lf(timer);
+#ifdef _MSC_VER
+      /* calling localtime(), and thus ctime(), on MSVC 2005 with huge values causes it to crash */
+      /* XXX - find the exact value that still does work */
+      /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
+      if (timer > 2000000000) {
+        time_string_ctime = NULL;
+      } else
+#endif
+      time_string_ctime = ctime(&timer);
+      if (time_string_ctime == NULL) {
+       g_snprintf(time_string_buf, 20, "Not representable%s", lf);
+        return time_string_buf;
+      }
+      if (!want_lf) {
+        /*
+         * The ctime() function returns a string formatted as:
+         *   "Www Mmm dd hh:mm:ss yyyy\n"
+         * The unwanted '\n' is the 24th character.
+         */
+        time_string_ctime[24] = '\0';
+      }
+      return time_string_ctime;
     }
   }
 
@@ -313,8 +451,17 @@ 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;
+    for (i=0; i<WTAP_NUM_ENCAP_TYPES; i++) {
+      if (cf_info->encap_counts[i] > 0)
+        printf("                       %s\n", wtap_encap_string(i));
+    }
+  }
   if (cap_snaplen && cf_info->snap_set)
                           printf     ("Packet size limit:   file hdr: %u bytes\n", cf_info->snaplen);
   else if(cap_snaplen && !cf_info->snap_set)
@@ -329,21 +476,31 @@ 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);
                           printf     ("RIPEMD160:           %s\n", file_rmd160);
                           printf     ("MD5:                 %s\n", file_md5);
   }
-  if (cap_in_order)       printf     ("Strict time order:   %s\n", (cf_info->in_order) ? "True" : "False");
 #endif /* HAVE_LIBGCRYPT */
+  if (cap_order)          printf     ("Strict time order:   %s\n", order_string(cf_info->order));
 }
 
 static void
@@ -395,8 +552,8 @@ print_stats_table_header(void)
                           print_stats_table_header_label("RIPEMD160");
                           print_stats_table_header_label("MD5");
   }
-  if (cap_in_order)       print_stats_table_header_label("Strict time order");
 #endif /* HAVE_LIBGCRYPT */
+  if (cap_order)          print_stats_table_header_label("Strict time order");
 
   printf("\n");
 }
@@ -427,6 +584,11 @@ print_stats_table(const gchar *filename, capture_info *cf_info)
     putquote();
   }
 
+  /* ToDo: If WTAP_ENCAP_PER_PACKET, show the list of encapsulations encountered;
+   *       Output a line for each different encap with all fields repeated except
+   *        the encapsulation field which has "Per Packet: ..." for each
+   *        encapsulation type seen ?
+   */
   if (cap_file_encap) {
     putsep();
     putquote();
@@ -488,35 +650,44 @@ 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();
   }
 
   if (cap_start_time) {
     putsep();
     putquote();
-    printf("%s", time_string(&start_time_t, cf_info, FALSE));
+    printf("%s", time_string(start_time_t, cf_info, FALSE));
     putquote();
   }
 
   if (cap_end_time) {
     putsep();
     putquote();
-    printf("%s", time_string(&stop_time_t, cf_info, FALSE));
+    printf("%s", time_string(stop_time_t, cf_info, FALSE));
     putquote();
   }
 
   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();
   }
 
@@ -530,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();
   }
 
@@ -553,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();
   }
 
@@ -577,30 +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;
@@ -617,7 +801,16 @@ process_cap_file(wtap *wth, const char *filename)
         snaplen_max_inferred = phdr->caplen;
     }
 
-  }
+    /* Per-packet encapsulation */
+    if (wtap_file_encap(wth) == WTAP_ENCAP_PER_PACKET) {
+        if ((phdr->pkt_encap > 0) && (phdr->pkt_encap < WTAP_NUM_ENCAP_TYPES)) {
+            cf_info.encap_counts[phdr->pkt_encap] += 1;
+        } else {
+            fprintf(stderr, "capinfos: Unknown per-packet encapsulation: %d [frame number: %d]\n", phdr->pkt_encap, packet);
+        }
+    }
+
+  } /* while */
 
   if (err != 0) {
     fprintf(stderr,
@@ -627,11 +820,13 @@ process_cap_file(wtap *wth, const char *filename)
 
     case WTAP_ERR_UNSUPPORTED:
     case WTAP_ERR_UNSUPPORTED_ENCAP:
-    case WTAP_ERR_BAD_RECORD:
+    case WTAP_ERR_BAD_FILE:
+    case WTAP_ERR_DECOMPRESS:
       fprintf(stderr, "(%s)\n", err_info);
       g_free(err_info);
       break;
     }
+    g_free(cf_info.encap_counts);
     return 1;
   }
 
@@ -640,7 +835,8 @@ process_cap_file(wtap *wth, const char *filename)
   if (size == -1) {
     fprintf(stderr,
             "capinfos: Can't get size of \"%s\": %s.\n",
-            filename, strerror(err));
+            filename, g_strerror(err));
+    g_free(cf_info.encap_counts);
     return 1;
   }
 
@@ -648,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);
@@ -666,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;
@@ -692,6 +891,8 @@ process_cap_file(wtap *wth, const char *filename)
     print_stats_table(filename, &cf_info);
   }
 
+  g_free(cf_info.encap_counts);
+
   return 0;
 }
 
@@ -794,7 +995,7 @@ hash_to_str(const unsigned char *hash, size_t length, char *str) {
   int i;
 
   for (i = 0; i < (int) length; i++) {
-    sprintf(str+(i*2), "%02x", hash[i]);
+    g_snprintf(str+(i*2), 3, "%02x", hash[i]);
   }
 }
 #endif /* HAVE_LIBGCRYPT */
@@ -802,35 +1003,25 @@ hash_to_str(const unsigned char *hash, size_t length, char *str) {
 int
 main(int argc, char *argv[])
 {
-  wtap *wth;
-  int err;
+  wtap  *wth;
+  int    err;
   gchar *err_info;
-  int opt;
-
-#ifdef _WIN32
-  LPWSTR              *wc_argv;
-  int                  wc_argc, i;
-#endif  /* _WIN32 */
+  int    opt;
+  int    overall_error_status;
 
   int status = 0;
 #ifdef HAVE_PLUGINS
-  charinit_progfile_dir_error;
+  char  *init_progfile_dir_error;
 #endif
 #ifdef HAVE_LIBGCRYPT
-  FILE *fh;
-  char *hash_buf = NULL;
+  FILE  *fh;
+  char  *hash_buf = NULL;
   gcry_md_hd_t hd = NULL;
   size_t hash_bytes;
 #endif
 
 #ifdef _WIN32
-  /* Convert our arg list to UTF-8. */
-  wc_argv = CommandLineToArgvW(GetCommandLineW(), &wc_argc);
-  if (wc_argv && wc_argc == argc) {
-    for (i = 0; i < argc; i++) {
-      argv[i] = g_utf16_to_utf8(wc_argv[i], -1, NULL, NULL, NULL);
-    }
-  } /* XXX else bail because something is horribly, horribly wrong? */
+  arg_list_utf_16to8(argc, argv);
 #endif /* _WIN32 */
 
   /*
@@ -851,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) {
@@ -934,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':
@@ -1018,12 +1256,14 @@ main(int argc, char *argv[])
   }
 #endif
 
+  overall_error_status = 0;
+
   for (opt = optind; opt < argc; opt++) {
 
 #ifdef HAVE_LIBGCRYPT
-    strcpy(file_sha1, "<unknown>");
-    strcpy(file_rmd160, "<unknown>");
-    strcpy(file_md5, "<unknown>");
+    g_strlcpy(file_sha1, "<unknown>", HASH_STR_SIZE);
+    g_strlcpy(file_rmd160, "<unknown>", HASH_STR_SIZE);
+    g_strlcpy(file_md5, "<unknown>", HASH_STR_SIZE);
 
     if (cap_file_hashes) {
       fh = ws_fopen(argv[opt], "rb");
@@ -1050,13 +1290,14 @@ main(int argc, char *argv[])
 
       case WTAP_ERR_UNSUPPORTED:
       case WTAP_ERR_UNSUPPORTED_ENCAP:
-      case WTAP_ERR_BAD_RECORD:
+      case WTAP_ERR_BAD_FILE:
         fprintf(stderr, "(%s)\n", err_info);
         g_free(err_info);
         break;
       }
+      overall_error_status = 1; /* remember that an error has occurred */
       if(!continue_after_wtap_open_offline_failure)
-        exit(1);
+        exit(1); /* error status */
     }
 
     if(wth) {
@@ -1069,6 +1310,5 @@ main(int argc, char *argv[])
         exit(status);
     }
   }
-  return 0;
+  return overall_error_status;
 }
-