Make sure the packet length isn't > WTAP_MAX_PACKET_SIZE.
[metze/wireshark/wip.git] / wiretap / peektagged.c
index 9792ffd7b75a178e58aaafa58f039ab21587b89e..24eaad1406076c5b3ddcff4ca545248cc4d7e041 100644 (file)
@@ -1,19 +1,17 @@
 /* peektagged.c
- * Routines for opening files in what WildPackets calls the tagged file
- * format in the description of their "PeekRdr Sample Application" (C++
- * source code to read their capture files, downloading of which requires
- * a maintenance contract, so it's not free as in beer and probably not
- * as in speech, either).
+ * Routines for opening files in what Savvius (formerly WildPackets) calls
+ * the tagged file format in the description of their "PeekRdr Sample
+ * Application" (C++ source code to read their capture files, downloading
+ * of which requires a maintenance contract, so it's not free as in beer
+ * and probably not as in speech, either).
  *
  * As that description says, it's used by AiroPeek and AiroPeek NX 2.0
  * and later, EtherPeek 6.0 and later, EtherPeek NX 3.0 and later,
  * EtherPeek VX 1.0 and later, GigaPeek NX 1.0 and later, Omni3 1.0
  * and later (both OmniPeek and the Remote Engine), and WANPeek NX
- * 1.0 and later.  They also say it'll be used by future WildPackets
+ * 1.0 and later.  They also say it'll be used by future Savvius
  * products.
  *
- * $Id$
- *
  * Wiretap Library
  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
  *
 #include <stdlib.h>
 #include "wtap-int.h"
 #include "file_wrappers.h"
-#include "buffer.h"
 #include "peektagged.h"
+#include <wsutil/frequency-utils.h>
 
 /* CREDITS
  *
  * This file decoder could not have been writen without examining
- * http://www.varsanofiev.com/inside/peektagged.htm, the help from
+ * http://www.varsanofiev.com/inside/airopeekv9.htm, the help from
  * Martin Regner and Guy Harris, and the etherpeek.c file (as it
  * was called before renaming it to peekclassic.c).
  */
 
-/* section header */
+/*
+ * Section header.
+ *
+ * A Peek tagged file consists of multiple sections, each of which begins
+ * with a header in the following format.
+ *
+ * The section ID is a 4-character string saying what type of section
+ * it is.  The section length is a little-endian field giving the
+ * length of the section, in bytes, including the section header
+ * itself.  The other field of the section header is a little-endian
+ * constant that always appears to be 0x00000200.
+ *
+ * Files we've seen have the following sections, in order:
+ *
+ * "\177vers" - version information.  The contents are XML, giving
+ * the file format version and application version information.
+ *
+ * "sess" - capture session information.  The contents are XML, giving
+ * various information about the capture session.
+ *
+ * "pkts" - captured packets.  The contents are binary records, one for
+ * each packet, with the record being a list of tagged values followed
+ * by the raw packet data.
+ */
 typedef struct peektagged_section_header {
-       gint8   section_id[4];
-       guint32 section_len;
-       guint32 section_const;
+        gint8   section_id[4];          /* string identifying the section */
+        guint32 section_len;            /* little-endian section length */
+        guint32 section_const;          /* little-endian 0x00000200 */
 } peektagged_section_header_t;
 
 /*
@@ -63,43 +84,92 @@ typedef struct peektagged_section_header {
  * network adapter types, with some adapters supplying the FCS and others
  * not supplying the FCS?
  */
-#define PEEKTAGGED_NST_ETHERNET                0
-#define PEEKTAGGED_NST_802_11          1       /* 802.11 with 0's at the end */
-#define PEEKTAGGED_NST_802_11_2                2       /* 802.11 with 0's at the end */
-#define PEEKTAGGED_NST_802_11_WITH_FCS 3       /* 802.11 with FCS at the end */
+#define PEEKTAGGED_NST_ETHERNET         0
+#define PEEKTAGGED_NST_802_11           1       /* 802.11 with 0's at the end */
+#define PEEKTAGGED_NST_802_11_2         2       /* 802.11 with 0's at the end */
+#define PEEKTAGGED_NST_802_11_WITH_FCS  3       /* 802.11 with FCS at the end */
 
 /* tags for fields in packet header */
-#define TAG_PEEKTAGGED_LENGTH                  0x0000
-#define TAG_PEEKTAGGED_TIMESTAMP_LOWER         0x0001
-#define TAG_PEEKTAGGED_TIMESTAMP_UPPER         0x0002
-#define TAG_PEEKTAGGED_FLAGS_AND_STATUS                0x0003
-#define TAG_PEEKTAGGED_CHANNEL                 0x0004
-#define TAG_PEEKTAGGED_RATE                    0x0005
-#define TAG_PEEKTAGGED_SIGNAL_PERC             0x0006
-#define TAG_PEEKTAGGED_SIGNAL_DBM              0x0007
-#define TAG_PEEKTAGGED_NOISE_PERC              0x0008
-#define TAG_PEEKTAGGED_NOISE_DBM               0x0009
-#define TAG_PEEKTAGGED_UNKNOWN_0x000D          0x000D
-#define TAG_PEEKTAGGED_SLICE_LENGTH            0xffff
+#define TAG_PEEKTAGGED_LENGTH                   0x0000
+#define TAG_PEEKTAGGED_TIMESTAMP_LOWER          0x0001
+#define TAG_PEEKTAGGED_TIMESTAMP_UPPER          0x0002
+#define TAG_PEEKTAGGED_FLAGS_AND_STATUS         0x0003  /* upper 24 bits unused? */
+#define TAG_PEEKTAGGED_CHANNEL                  0x0004
+#define TAG_PEEKTAGGED_DATA_RATE_OR_MCS_INDEX   0x0005
+#define TAG_PEEKTAGGED_SIGNAL_PERC              0x0006
+#define TAG_PEEKTAGGED_SIGNAL_DBM               0x0007
+#define TAG_PEEKTAGGED_NOISE_PERC               0x0008
+#define TAG_PEEKTAGGED_NOISE_DBM                0x0009
+#define TAG_PEEKTAGGED_UNKNOWN_0x000A           0x000A
+#define TAG_PEEKTAGGED_CENTER_FREQUENCY         0x000D  /* Frequency */
+#define TAG_PEEKTAGGED_UNKNOWN_0x000E           0x000E  /* "Band"? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x000F           0x000F  /* antenna 2 signal dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0010           0x0010  /* antenna 3 signal dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0011           0x0011  /* antenna 4 signal dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0012           0x0012  /* antenna 2 noise dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0013           0x0013  /* antenna 3 noise dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0014           0x0014  /* antenna 4 noise dBm? */
+#define TAG_PEEKTAGGED_EXT_FLAGS                0x0015  /* Extended flags for 802.11n and beyond */
+
+#define TAG_PEEKTAGGED_SLICE_LENGTH             0xffff
+
+/*
+ * Flags.
+ *
+ * We're assuming here that the "remote Peek" flags from bug 9586 are
+ * the same as the "Peek tagged" flags.
+ */
+#define FLAGS_CONTROL_FRAME     0x01    /* Frame is a control frame */
+#define FLAGS_HAS_CRC_ERROR     0x02    /* Frame has a CRC error */
+#define FLAGS_HAS_FRAME_ERROR   0x04    /* Frame has a frame error */
+
+/*
+ * Status.
+ *
+ * Is this in the next 8 bits of the "flags and status" field?
+ */
+#define STATUS_PROTECTED        0x0400  /* Frame is protected (encrypted) */
+#define STATUS_DECRYPT_ERROR    0x0800  /* Error decrypting protected frame */
+#define STATUS_SHORT_PREAMBLE   0x4000  /* Short preamble */
+
+/*
+ * Extended flags.
+ *
+ * Some determined from bug 10637, some determined from bug 9586,
+ * and the ones present in both agree, so we're assuming that
+ * the "remote Peek" protocol and the "Peek tagged" file format
+ * use the same bits (which wouldn't be too surprising, as they
+ * both come from Wildpackets).
+ */
+#define EXT_FLAG_20_MHZ_LOWER                   0x00000001
+#define EXT_FLAG_20_MHZ_UPPER                   0x00000002
+#define EXT_FLAG_40_MHZ                         0x00000004
+#define EXT_FLAGS_BANDWIDTH                     0x00000007
+#define EXT_FLAG_HALF_GI                        0x00000008
+#define EXT_FLAG_FULL_GI                        0x00000010
+#define EXT_FLAGS_GI                            0x00000018
+#define EXT_FLAG_AMPDU                          0x00000020
+#define EXT_FLAG_AMSDU                          0x00000040
+#define EXT_FLAG_802_11ac                       0x00000080
+#define EXT_FLAG_MCS_INDEX_USED                 0x00000100
 
 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
 typedef struct peektagged_utime {
-       guint32 upper;
-       guint32 lower;
+        guint32 upper;
+        guint32 lower;
 } peektagged_utime;
 
 typedef struct {
-       gboolean        has_fcs;
+        gboolean        has_fcs;
 } peektagged_t;
 
 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
     gint64 *data_offset);
 static gboolean peektagged_seek_read(wtap *wth, gint64 seek_off,
-    struct wtap_pkthdr *phdr, guint8 *pd, int length,
-    int *err, gchar **err_info);
+    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
 
 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
-                               gchar **err_info)
+                                gchar **err_info)
 {
     int c;
     const char *cp;
@@ -107,31 +177,31 @@ static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
     cp = pattern;
     while (*cp)
     {
-       c = file_getc(wth->fh);
-       if (c == EOF)
-       {
-           *err = file_error(wth->fh, err_info);
-           if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
-               return -1;      /* error */
-           return 0;   /* EOF */
-       }
-       if (c == *cp)
-           cp++;
-       else
-       {
-           if (c == pattern[0])
-               cp = &pattern[1];
-           else
-               cp = pattern;
-       }
+        c = file_getc(wth->fh);
+        if (c == EOF)
+        {
+            *err = file_error(wth->fh, err_info);
+            if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+                return -1;      /* error */
+            return 0;   /* EOF */
+        }
+        if (c == *cp)
+            cp++;
+        else
+        {
+            if (c == pattern[0])
+                cp = &pattern[1];
+            else
+                cp = pattern;
+        }
     }
     return (*cp == '\0' ? 1 : 0);
 }
 
 
 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
-                                       const char *separators, int *err,
-                                       gchar **err_info)
+                                        const char *separators, int *err,
+                                        gchar **err_info)
 {
     int c;
     char *cp;
@@ -139,28 +209,28 @@ static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
 
     for (cp = buffer, i = 0; i < buflen; i++, cp++)
     {
-       c = file_getc(wth->fh);
-       if (c == EOF)
-       {
-           *err = file_error(wth->fh, err_info);
-           if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
-               return -1;      /* error */
-           return 0;   /* EOF */
-       }
-       if (strchr (separators, c) != NULL)
-       {
-           *cp = '\0';
-           break;
-       }
-       else
-           *cp = c;
+        c = file_getc(wth->fh);
+        if (c == EOF)
+        {
+            *err = file_error(wth->fh, err_info);
+            if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+                return -1;      /* error */
+            return 0;   /* EOF */
+        }
+        if (strchr (separators, c) != NULL)
+        {
+            *cp = '\0';
+            break;
+        }
+        else
+            *cp = c;
     }
     return i;
 }
 
 
 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
-                               gchar **err_info)
+                                gchar **err_info)
 {
     int ret;
     char str_num[12];
@@ -168,48 +238,45 @@ static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
     char *p;
 
     ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
-                                        err, err_info);
-    if (ret != 1) {
-       /* 0 means EOF, which means "not a valid Peek tagged file";
-          -1 means error, and "err" has been set. */
-       return ret;
+                                         err, err_info);
+    if (ret == 0 || ret == -1) {
+        /* 0 means EOF, which means "not a valid Peek tagged file";
+           -1 means error, and "err" has been set. */
+        return ret;
     }
     value = strtoul (str_num, &p, 10);
     if (p == str_num || value > G_MAXUINT32)
-       return 0;
+        return 0;
     *num = (guint32)value;
     return 1;
 }
 
 
-int peektagged_open(wtap *wth, int *err, gchar **err_info)
+wtap_open_return_val peektagged_open(wtap *wth, int *err, gchar **err_info)
 {
     peektagged_section_header_t ap_hdr;
-    int bytes_read;
     int ret;
-    guint32 fileVersion;
+    guint32 fileVersion = 0;
     guint32 mediaType;
     guint32 mediaSubType = 0;
     int file_encap;
     static const int peektagged_encap[] = {
-       WTAP_ENCAP_ETHERNET,
-       WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
-       WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
-       WTAP_ENCAP_IEEE_802_11_WITH_RADIO
+        WTAP_ENCAP_ETHERNET,
+        WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
+        WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
+        WTAP_ENCAP_IEEE_802_11_WITH_RADIO
     };
     #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
     peektagged_t *peektagged;
 
-    bytes_read = file_read(&ap_hdr, (int)sizeof(ap_hdr), wth->fh);
-    if (bytes_read != (int)sizeof(ap_hdr)) {
-       *err = file_error(wth->fh, err_info);
-        if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
-            return -1;
-        return 0;
+    if (!wtap_read_bytes(wth->fh, &ap_hdr, (int)sizeof(ap_hdr), err, err_info)) {
+        if (*err != WTAP_ERR_SHORT_READ)
+            return WTAP_OPEN_ERROR;
+        return WTAP_OPEN_NOT_MINE;
     }
 
     if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
-       return 0;       /* doesn't begin with a "\177ver" section */
+        return WTAP_OPEN_NOT_MINE;      /* doesn't begin with a "\177ver" section */
 
     /*
      * XXX - we should get the length of the "\177ver" section, check
@@ -220,25 +287,27 @@ int peektagged_open(wtap *wth, int *err, gchar **err_info)
      * tags are properly opened and closed).
      */
     ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
-    if (ret != 1) {
-       /* 0 means EOF, which means "not a valid Peek tagged file";
-          -1 means error, and "err" has been set. */
-       return ret;
+    if (ret == -1)
+        return WTAP_OPEN_ERROR;
+    if (ret == 0) {
+        /* 0 means EOF, which means "not a valid Peek tagged file" */
+        return WTAP_OPEN_NOT_MINE;
     }
     ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
-    if (ret != 1) {
-       /* 0 means EOF, which means "not a valid Peek tagged file";
-          -1 means error, and "err" has been set. */
-       return ret;
+    if (ret == -1)
+        return WTAP_OPEN_ERROR;
+    if (ret == 0) {
+        /* 0 means EOF, which means "not a valid Peek tagged file" */
+        return WTAP_OPEN_NOT_MINE;
     }
 
     /* If we got this far, we assume it's a Peek tagged file. */
     if (fileVersion != 9) {
-       /* We only support version 9. */
-       *err = WTAP_ERR_UNSUPPORTED;
-       *err_info = g_strdup_printf("peektagged: version %u unsupported",
-           fileVersion);
-       return -1;
+        /* We only support version 9. */
+        *err = WTAP_ERR_UNSUPPORTED;
+        *err_info = g_strdup_printf("peektagged: version %u unsupported",
+            fileVersion);
+        return WTAP_OPEN_ERROR;
     }
 
     /*
@@ -253,69 +322,69 @@ int peektagged_open(wtap *wth, int *err, gchar **err_info)
      */
     ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
     if (ret == -1)
-       return -1;
+        return WTAP_OPEN_ERROR;
     if (ret == 0) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: <MediaType> tag not found");
-       return -1;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: <MediaType> tag not found");
+        return WTAP_OPEN_ERROR;
     }
     /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
        files we've seen; should we require it to be 0? */
     ret = wtap_file_read_number (wth, &mediaType, err, err_info);
     if (ret == -1)
-       return -1;
+        return WTAP_OPEN_ERROR;
     if (ret == 0) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: <MediaType> value not found");
-       return -1;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: <MediaType> value not found");
+        return WTAP_OPEN_ERROR;
     }
 
     ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
     if (ret == -1)
-       return -1;
+        return WTAP_OPEN_ERROR;
     if (ret == 0) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
-       return -1;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
+        return WTAP_OPEN_ERROR;
     }
     ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
     if (ret == -1)
-       return -1;
+        return WTAP_OPEN_ERROR;
     if (ret == 0) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: <MediaSubType> value not found");
-       return -1;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: <MediaSubType> value not found");
+        return WTAP_OPEN_ERROR;
     }
     if (mediaSubType >= NUM_PEEKTAGGED_ENCAPS
         || peektagged_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
-       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
-       *err_info = g_strdup_printf("peektagged: network type %u unknown or unsupported",
-           mediaSubType);
-       return -1;
+        *err = WTAP_ERR_UNSUPPORTED;
+        *err_info = g_strdup_printf("peektagged: network type %u unknown or unsupported",
+            mediaSubType);
+        return WTAP_OPEN_ERROR;
     }
 
     ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
     if (ret == -1)
-       return -1;
+        return WTAP_OPEN_ERROR;
     if (ret == 0) {
-       *err = WTAP_ERR_SHORT_READ;
-       return -1;
+        *err = WTAP_ERR_SHORT_READ;
+        return WTAP_OPEN_ERROR;
     }
 
     /* skip 8 zero bytes */
     if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
-       return 0;
+        return WTAP_OPEN_NOT_MINE;
 
     /*
      * This is an Peek tagged file.
      */
     file_encap = peektagged_encap[mediaSubType];
 
-    wth->file_type = WTAP_FILE_PEEKTAGGED;
+    wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PEEKTAGGED;
     wth->file_encap = file_encap;
     wth->subtype_read = peektagged_read;
     wth->subtype_seek_read = peektagged_seek_read;
-    wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
+    wth->file_tsprec = WTAP_TSPREC_NSEC;
 
     peektagged = (peektagged_t *)g_malloc(sizeof(peektagged_t));
     wth->priv = (void *)peektagged;
@@ -324,300 +393,497 @@ int peektagged_open(wtap *wth, int *err, gchar **err_info)
     case PEEKTAGGED_NST_ETHERNET:
     case PEEKTAGGED_NST_802_11:
     case PEEKTAGGED_NST_802_11_2:
-       peektagged->has_fcs = FALSE;
-       break;
+        peektagged->has_fcs = FALSE;
+        break;
 
     case PEEKTAGGED_NST_802_11_WITH_FCS:
-       peektagged->has_fcs = TRUE;
-       break;
+        peektagged->has_fcs = TRUE;
+        break;
     }
 
     wth->snapshot_length   = 0; /* not available in header */
 
-    return 1;
+    return WTAP_OPEN_MINE;
 }
 
-typedef struct {
-    guint32 length;
-    guint32 sliceLength;
-    peektagged_utime timestamp;
-    struct ieee_802_11_phdr ieee_802_11;
-} hdr_info_t;
-
 /*
- * Process the packet header.
+ * Read the packet.
  *
  * XXX - we should supply the additional radio information;
  * the pseudo-header should probably be supplied in a fashion
- * similar to the new BSD radio header, so that the 802.11
+ * similar to the radiotap radio header, so that the 802.11
  * dissector can determine which, if any, information items
  * are present.
  */
 static int
-peektagged_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
-    gchar **err_info)
+peektagged_read_packet(wtap *wth, FILE_T fh, struct wtap_pkthdr *phdr,
+                       Buffer *buf, int *err, gchar **err_info)
 {
-    int header_len = 0;
-    int bytes_read;
+    peektagged_t *peektagged = (peektagged_t *)wth->priv;
+    gboolean read_a_tag = FALSE;
     guint8 tag_value[6];
     guint16 tag;
     gboolean saw_length = FALSE;
+    guint32 length = 0;
+    guint32 sliceLength = 0;
     gboolean saw_timestamp_lower = FALSE;
     gboolean saw_timestamp_upper = FALSE;
+    peektagged_utime timestamp;
+    guint32 ext_flags = 0;
+    gboolean saw_data_rate_or_mcs_index = FALSE;
+    guint32 data_rate_or_mcs_index = 0;
+    gint channel;
+    guint frequency;
+    struct ieee_802_11_phdr ieee_802_11;
+    guint i;
+    int skip_len = 0;
+    guint64 t;
+
+    timestamp.upper = 0;
+    timestamp.lower = 0;
+    memset(&ieee_802_11, 0, sizeof ieee_802_11);
+    ieee_802_11.fcs_len = -1; /* Unknown */
+    ieee_802_11.decrypted = FALSE;
+    ieee_802_11.datapad = FALSE;
+    ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
 
     /* Extract the fields from the packet header */
     do {
-       /* Get the tag and value.
-          XXX - this assumes all values are 4 bytes long. */
-       bytes_read = file_read(tag_value, sizeof tag_value, fh);
-       if (bytes_read != (int) sizeof tag_value) {
-           *err = file_error(fh, err_info);
-           if (*err == 0) {
-               if (bytes_read > 0)
-                   *err = WTAP_ERR_SHORT_READ;
-               else if (bytes_read == 0) {
-                   /*
-                    * Short read if we've read something already;
-                    * just an EOF if we haven't.
-                    */
-                   if (header_len != 0)
-                       *err = WTAP_ERR_SHORT_READ;
-               }
-           }
-           return 0;
-       }
-       header_len += (int) sizeof(tag_value);
-       tag = pletohs(&tag_value[0]);
-       switch (tag) {
-
-       case TAG_PEEKTAGGED_LENGTH:
-           if (saw_length) {
-               *err = WTAP_ERR_BAD_FILE;
-               *err_info = g_strdup("peektagged: record has two length fields");
-               return 0;
-           }
-           hdr_info->length = pletohl(&tag_value[2]);
-           saw_length = TRUE;
-           break;
-    
-       case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
-           if (saw_timestamp_lower) {
-               *err = WTAP_ERR_BAD_FILE;
-               *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
-               return 0;
-           }
-           hdr_info->timestamp.lower = pletohl(&tag_value[2]);
-           saw_timestamp_lower = TRUE;
-           break;
-
-       case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
-           if (saw_timestamp_upper) {
-               *err = WTAP_ERR_BAD_FILE;
-               *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
-               return 0;
-           }
-           hdr_info->timestamp.upper = pletohl(&tag_value[2]);
-           saw_timestamp_upper = TRUE;
-           break;
-
-       case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
-           /* XXX - not used yet */
-           break;
-
-       case TAG_PEEKTAGGED_CHANNEL:
-           hdr_info->ieee_802_11.channel = pletohl(&tag_value[2]);
-           break;
-
-       case TAG_PEEKTAGGED_RATE:
-           hdr_info->ieee_802_11.data_rate = pletohl(&tag_value[2]);
-           break;
-
-       case TAG_PEEKTAGGED_SIGNAL_PERC:
-           hdr_info->ieee_802_11.signal_level = pletohl(&tag_value[2]);
-           break;
-
-       case TAG_PEEKTAGGED_SIGNAL_DBM:
-           /* XXX - not used yet */
-           break;
-
-       case TAG_PEEKTAGGED_NOISE_PERC:
-           /* XXX - not used yet */
-           break;
-
-       case TAG_PEEKTAGGED_NOISE_DBM:
-           /* XXX - not used yet */
-           break;
-
-       case TAG_PEEKTAGGED_UNKNOWN_0x000D:
-           /* XXX - seen in an EtherPeek capture; value unknown */
-           break;
-
-       case TAG_PEEKTAGGED_SLICE_LENGTH:
-           hdr_info->sliceLength = pletohl(&tag_value[2]);
-           break;
-
-       default:
-           break;
+        /* Get the tag and value.
+           XXX - this assumes all values are 4 bytes long. */
+        if (!wtap_read_bytes_or_eof(fh, tag_value, sizeof tag_value, err, err_info)) {
+            if (*err == 0) {
+                /*
+                 * Short read if we've read something already;
+                 * just an EOF if we haven't.
+                 */
+                if (read_a_tag)
+                    *err = WTAP_ERR_SHORT_READ;
+            }
+            return -1;
         }
-    } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH);      /* last tag */
+        read_a_tag = TRUE;
+        tag = pletoh16(&tag_value[0]);
+        switch (tag) {
+
+        case TAG_PEEKTAGGED_LENGTH:
+            if (saw_length) {
+                *err = WTAP_ERR_BAD_FILE;
+                *err_info = g_strdup("peektagged: record has two length fields");
+                return -1;
+            }
+            length = pletoh32(&tag_value[2]);
+            saw_length = TRUE;
+            break;
+
+        case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
+            if (saw_timestamp_lower) {
+                *err = WTAP_ERR_BAD_FILE;
+                *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
+                return -1;
+            }
+            timestamp.lower = pletoh32(&tag_value[2]);
+            saw_timestamp_lower = TRUE;
+            break;
+
+        case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
+            if (saw_timestamp_upper) {
+                *err = WTAP_ERR_BAD_FILE;
+                *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
+                return -1;
+            }
+            timestamp.upper = pletoh32(&tag_value[2]);
+            saw_timestamp_upper = TRUE;
+            break;
+
+        case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
+            /* XXX - not used yet */
+            break;
+
+        case TAG_PEEKTAGGED_CHANNEL:
+            ieee_802_11.has_channel = TRUE;
+            ieee_802_11.channel = pletoh32(&tag_value[2]);
+            break;
+
+        case TAG_PEEKTAGGED_DATA_RATE_OR_MCS_INDEX:
+            data_rate_or_mcs_index = pletoh32(&tag_value[2]);
+            saw_data_rate_or_mcs_index = TRUE;
+            break;
+
+        case TAG_PEEKTAGGED_SIGNAL_PERC:
+            ieee_802_11.has_signal_percent = TRUE;
+            ieee_802_11.signal_percent = pletoh32(&tag_value[2]);
+            break;
+
+        case TAG_PEEKTAGGED_SIGNAL_DBM:
+            ieee_802_11.has_signal_dbm = TRUE;
+            ieee_802_11.signal_dbm = pletoh32(&tag_value[2]);
+            break;
+
+        case TAG_PEEKTAGGED_NOISE_PERC:
+            ieee_802_11.has_noise_percent = TRUE;
+            ieee_802_11.noise_percent = pletoh32(&tag_value[2]);
+            break;
+
+        case TAG_PEEKTAGGED_NOISE_DBM:
+            ieee_802_11.has_noise_dbm = TRUE;
+            ieee_802_11.noise_dbm = pletoh32(&tag_value[2]);
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x000A:
+            /*
+             * XXX - seen in some 802.11 captures.
+             * Always seems to have the value 0 or 5.
+             */
+            break;
+
+        case TAG_PEEKTAGGED_CENTER_FREQUENCY:
+            /* XXX - also seen in an EtherPeek capture; value unknown */
+            ieee_802_11.has_frequency = TRUE;
+            ieee_802_11.frequency = pletoh32(&tag_value[2]);
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x000E:
+            /*
+             * XXX - seen in some 802.11 captures.
+             * Usually has the value 4, but, in some packets, has the
+             * values 6 or 302.
+             *
+             * Is this the mysterious "band" field that shows up in
+             * some "Peek remote" protocol captures, with values in
+             * the 30x or 40x ranges?  It's not always associated
+             * with the "extended flags" tag for HT/VHT information,
+             * so it's probably not 11n/11ac-specific.  Values other
+             * than 4 appear, in my captures, only in packets with
+             * the "extended flags" tag.  302 appeared in a packet
+             * with EXT_FLAG_MCS_INDEX_USED; 6 appeared in packets
+             * without EXT_FLAG_MCS_INDEX_USED.
+             */
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x000F:
+            /*
+             * XXX - seen in some 802.11 captures; dB or dBm value?
+             * Multiple antennas?
+             */
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x0010:
+            /*
+             * XXX - seen in some 802.11 captures; dB or dBm value?
+             * Multiple antennas?
+             */
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x0011:
+            /*
+             * XXX - seen in some 802.11 captures; dB or dBm value?
+             * Multiple antennas?
+             */
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x0012:
+            /*
+             * XXX - seen in some 802.11 captures; dB or dBm value?
+             * Multiple antennas?
+             */
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x0013:
+            /*
+             * XXX - seen in some 802.11 captures; dB or dBm value?
+             * Multiple antennas?
+             */
+            break;
+
+        case TAG_PEEKTAGGED_UNKNOWN_0x0014:
+            /*
+             * XXX - seen in some 802.11 captures; dB or dBm value?
+             * Multiple antennas?
+             */
+            break;
+
+        case TAG_PEEKTAGGED_EXT_FLAGS:
+            /*
+             * We assume this is present for HT and VHT frames and absent
+             * for other frames.
+             */
+            ext_flags = pletoh32(&tag_value[2]);
+            if (ext_flags & EXT_FLAG_802_11ac) {
+                ieee_802_11.phy = PHDR_802_11_PHY_11AC;
+                /*
+                 * XXX - this probably has only one user, so only
+                 * one MCS index and only one NSS, but where's the
+                 * NSS?
+                 */
+                for (i = 0; i < 4; i++)
+                    ieee_802_11.phy_info.info_11ac.nss[i] = 0;
+
+                switch (ext_flags & EXT_FLAGS_GI) {
+
+                case EXT_FLAG_HALF_GI:
+                    ieee_802_11.phy_info.info_11ac.has_short_gi = TRUE;
+                    ieee_802_11.phy_info.info_11ac.short_gi = 1;
+                    break;
+
+                case EXT_FLAG_FULL_GI:
+                    ieee_802_11.phy_info.info_11ac.has_short_gi = TRUE;
+                    ieee_802_11.phy_info.info_11ac.short_gi = 0;
+                    break;
+
+                default:
+                    /* Mutually exclusive flags set or nothing set */
+                    break;
+                }
+            } else {
+                ieee_802_11.phy = PHDR_802_11_PHY_11N;
+                switch (ext_flags & EXT_FLAGS_BANDWIDTH) {
+
+                case 0:
+                    ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+                    ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_MHZ;
+                    break;
+
+                case EXT_FLAG_20_MHZ_LOWER:
+                    ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+                    ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_20L;
+                    break;
+
+                case EXT_FLAG_20_MHZ_UPPER:
+                    ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+                    ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_20U;
+                    break;
+
+                case EXT_FLAG_40_MHZ:
+                    ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+                    ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_40_MHZ;
+                    break;
+
+                default:
+                    /* Mutually exclusive flags set */
+                    break;
+                }
+
+                switch (ext_flags & EXT_FLAGS_GI) {
+
+                case EXT_FLAG_HALF_GI:
+                    ieee_802_11.phy_info.info_11n.has_short_gi = TRUE;
+                    ieee_802_11.phy_info.info_11n.short_gi = 1;
+                    break;
+
+                case EXT_FLAG_FULL_GI:
+                    ieee_802_11.phy_info.info_11n.has_short_gi = TRUE;
+                    ieee_802_11.phy_info.info_11n.short_gi = 0;
+                    break;
+
+                default:
+                    /* Mutually exclusive flags set or nothing set */
+                    break;
+                }
+            }
+            break;
+
+        case TAG_PEEKTAGGED_SLICE_LENGTH:
+            sliceLength = pletoh32(&tag_value[2]);
+            break;
+
+        default:
+            break;
+        }
+    } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH);       /* last tag */
 
     if (!saw_length) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: record has no length field");
-       return 0;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: record has no length field");
+        return -1;
     }
     if (!saw_timestamp_lower) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: record has no timestamp-lower field");
-       return 0;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: record has no timestamp-lower field");
+        return -1;
     }
     if (!saw_timestamp_upper) {
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup("peektagged: record has no timestamp-upper field");
-       return 0;
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup("peektagged: record has no timestamp-upper field");
+        return -1;
     }
 
-    return header_len;
-}
-
-/*
- * Time stamps appear to be in nanoseconds since the Windows epoch
- * as used in FILETIMEs, i.e. midnight, January 1, 1601.
- *
- * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
- * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
- * that are being subtracted.
- */
-#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
-
-static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
-    gint64 *data_offset)
-{
-    peektagged_t *peektagged = (peektagged_t *)wth->priv;
-    hdr_info_t hdr_info;
-    int hdrlen;
-    double  t;
-
-    *data_offset = file_tell(wth->fh);
-
-    /* Process the packet header. */
-    hdrlen = peektagged_process_header(wth->fh, &hdr_info, err, err_info);
-    if (hdrlen == 0)
-       return FALSE;
-
     /*
      * If sliceLength is 0, force it to be the actual length of the packet.
      */
-    if (hdr_info.sliceLength == 0)
-       hdr_info.sliceLength = hdr_info.length;
-
-    if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
-       /*
-        * Probably a corrupt capture file; don't blow up trying
-        * to allocate space for an immensely-large packet.
-        */
-       *err = WTAP_ERR_BAD_FILE;
-       *err_info = g_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
-           hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
-       return FALSE;
+    if (sliceLength == 0)
+        sliceLength = length;
+
+    if (sliceLength > WTAP_MAX_PACKET_SIZE) {
+        /*
+         * Probably a corrupt capture file; don't blow up trying
+         * to allocate space for an immensely-large packet.
+         */
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
+            sliceLength, WTAP_MAX_PACKET_SIZE);
+        return -1;
     }
 
-    wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+    phdr->rec_type = REC_TYPE_PACKET;
+    phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+    phdr->len    = length;
+    phdr->caplen = sliceLength;
+
+    /* calculate and fill in packet time stamp */
+    t = (((guint64) timestamp.upper) << 32) + timestamp.lower;
+    if (!nsfiletime_to_nstime(&phdr->ts, t)) {
+        *err = WTAP_ERR_BAD_FILE;
+        *err_info = g_strdup_printf("peektagged: time stamp outside supported range");
+        return -1;
+    }
 
-    /* fill in packet header length values before slicelength may be
-       adjusted */
-    wth->phdr.len    = hdr_info.length;
-    wth->phdr.caplen = hdr_info.sliceLength;
+    switch (wth->file_encap) {
 
-    /* read the frame data */
-    buffer_assure_space(wth->frame_buffer, hdr_info.sliceLength);
-    wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
-                                 hdr_info.sliceLength, wth->fh, err,
-                                 err_info);
+    case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+        if (saw_data_rate_or_mcs_index) {
+            if (ext_flags & EXT_FLAG_MCS_INDEX_USED) {
+                /*
+                 * It's an MCS index.
+                 *
+                 * XXX - what about 11ac?
+                 */
+                if (!(ext_flags & EXT_FLAG_802_11ac)) {
+                    ieee_802_11.phy_info.info_11n.has_mcs_index = TRUE;
+                    ieee_802_11.phy_info.info_11n.mcs_index = data_rate_or_mcs_index;
+                }
+            } else {
+                /* It's a data rate. */
+                ieee_802_11.has_data_rate = TRUE;
+                ieee_802_11.data_rate = data_rate_or_mcs_index;
+            }
+        }
+        if (ieee_802_11.has_frequency && !ieee_802_11.has_channel) {
+            /* Frequency, but no channel; try to calculate the channel. */
+            channel = ieee80211_mhz_to_chan(ieee_802_11.frequency);
+            if (channel != -1) {
+                ieee_802_11.has_channel = TRUE;
+                ieee_802_11.channel = channel;
+            }
+        } else if (ieee_802_11.has_channel && !ieee_802_11.has_frequency) {
+            /*
+             * If it's 11 legacy DHSS, 11b, or 11g, it's 2.4 GHz,
+             * so we can calculate the frequency.
+             *
+             * If it's 11a, it's 5 GHz, so we can calculate the
+             * frequency.
+             */
+            switch (ieee_802_11.phy) {
+
+            case PHDR_802_11_PHY_11_DSSS:
+            case PHDR_802_11_PHY_11B:
+            case PHDR_802_11_PHY_11G:
+                frequency = ieee80211_chan_to_mhz(ieee_802_11.channel, TRUE);
+                break;
+
+            case PHDR_802_11_PHY_11A:
+                frequency = ieee80211_chan_to_mhz(ieee_802_11.channel, FALSE);
+                break;
+
+            default:
+                /* We don't know the band. */
+                frequency = 0;
+                break;
+            }
+            if (frequency != 0) {
+                ieee_802_11.has_frequency = TRUE;
+                ieee_802_11.frequency = frequency;
+            }
+        }
+        phdr->pseudo_header.ieee_802_11 = ieee_802_11;
+        if (peektagged->has_fcs)
+            phdr->pseudo_header.ieee_802_11.fcs_len = 4;
+        else {
+            if (phdr->len < 4 || phdr->caplen < 4) {
+                *err = WTAP_ERR_BAD_FILE;
+                *err_info = g_strdup_printf("peektagged: 802.11 packet has length < 4");
+                return FALSE;
+            }
+            phdr->pseudo_header.ieee_802_11.fcs_len = 0;
+            phdr->len -= 4;
+            phdr->caplen -= 4;
+            skip_len = 4;
+        }
+        phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
+        phdr->pseudo_header.ieee_802_11.datapad = FALSE;
+        break;
 
-    /* recalculate and fill in packet time stamp */
-    t =  (double) hdr_info.timestamp.lower +
-        (double) hdr_info.timestamp.upper * 4294967296.0;
+    case WTAP_ENCAP_ETHERNET:
+        /*
+         * The last 4 bytes appear to be 0 in the captures I've seen;
+         * are there any captures where it's an FCS?
+         */
+        if (phdr->len < 4 || phdr->caplen < 4) {
+            *err = WTAP_ERR_BAD_FILE;
+            *err_info = g_strdup_printf("peektagged: Ethernet packet has length < 4");
+            return FALSE;
+        }
+        phdr->pseudo_header.eth.fcs_len = 0;
+        phdr->len -= 4;
+        phdr->caplen -= 4;
+        skip_len = 4;
+        break;
+    }
 
-    t *= 1.0e-9;
-    t -= TIME_FIXUP_CONSTANT;
-    wth->phdr.ts.secs  = (time_t) t;
-    wth->phdr.ts.nsecs = (guint32) ((t - wth->phdr.ts.secs)*1000000000);
+    /* Read the packet data. */
+    if (!wtap_read_packet_bytes(fh, buf, phdr->caplen, err, err_info))
+        return -1;
 
-    switch (wth->file_encap) {
+    return skip_len;
+}
 
-    case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
-       /*
-        * The last 4 bytes sometimes contains the FCS but on a lot of
-        * interfaces these are zero.  Is there some way to determine
-        * from the packet header whether it's an FCS or not?
-        *
-        * For now, we just discard those bytes; if we can determine
-        * whether it's an FCS or not, we should use that to determine
-        * whether to supply it as an FCS or discard it.
-        */
-       wth->phdr.pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
-       if (peektagged->has_fcs)
-           wth->phdr.pseudo_header.ieee_802_11.fcs_len = 4;
-       else {
-           wth->phdr.pseudo_header.ieee_802_11.fcs_len = 0;
-           wth->phdr.len -= 4;
-           wth->phdr.caplen -= 4;
-       }
-       wth->phdr.pseudo_header.ieee_802_11.decrypted = FALSE;
-       break;
+static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset)
+{
+    int skip_len;
 
-    case WTAP_ENCAP_ETHERNET:
-       /*
-        * The last 4 bytes appear to be 0 in the captures I've seen;
-        * are there any captures where it's an FCS?
-        */
-       wth->phdr.pseudo_header.eth.fcs_len = 0;
-       wth->phdr.len -= 4;
-       wth->phdr.caplen -= 4;
-       break;
+    *data_offset = file_tell(wth->fh);
+
+    /* Read the packet. */
+    skip_len = peektagged_read_packet(wth, wth->fh, &wth->phdr,
+                                      wth->frame_buffer, err, err_info);
+    if (skip_len == -1)
+        return FALSE;
+
+    if (skip_len != 0) {
+        /* Skip extra junk at the end of the packet data. */
+        if (!file_skip(wth->fh, skip_len, err))
+            return FALSE;
     }
 
     return TRUE;
 }
 
-
 static gboolean
 peektagged_seek_read(wtap *wth, gint64 seek_off,
-    struct wtap_pkthdr *phdr, guint8 *pd, int length,
-    int *err, gchar **err_info)
+    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
 {
-    union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
-    peektagged_t *peektagged = (peektagged_t *)wth->priv;
-    hdr_info_t hdr_info;
-
     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
-       return FALSE;
-
-    /* Process the packet header. */
-    if (peektagged_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
-       return FALSE;
-
-    switch (wth->file_encap) {
+        return FALSE;
 
-    case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
-       pseudo_header->ieee_802_11 = hdr_info.ieee_802_11;
-       if (peektagged->has_fcs)
-           pseudo_header->ieee_802_11.fcs_len = 4;
-       else
-           pseudo_header->ieee_802_11.fcs_len = 0;
-       pseudo_header->ieee_802_11.decrypted = FALSE;
-       break;
-
-    case WTAP_ENCAP_ETHERNET:
-       pseudo_header->eth.fcs_len = 0;
-       break;
+    /* Read the packet. */
+    if (peektagged_read_packet(wth, wth->random_fh, phdr, buf, err, err_info) == -1) {
+        if (*err == 0)
+            *err = WTAP_ERR_SHORT_READ;
+        return FALSE;
     }
-
-    /*
-     * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
-     */
-    errno = WTAP_ERR_CANT_READ;
-    wtap_file_read_expected_bytes(pd, length, wth->random_fh, err, err_info);
     return TRUE;
 }
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */