]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add support for xar reader.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Fri, 4 Dec 2009 18:01:26 +0000 (13:01 -0500)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Fri, 4 Dec 2009 18:01:26 +0000 (13:01 -0500)
SVN-Revision: 1699

13 files changed:
CMakeLists.txt
Makefile.am
build/cmake/config.h.in
configure.ac
libarchive/CMakeLists.txt
libarchive/archive.h
libarchive/archive_entry.c
libarchive/archive_entry.h
libarchive/archive_read_private.h
libarchive/archive_read_support_format_all.c
libarchive/archive_read_support_format_xar.c [new file with mode: 0644]
libarchive/test/CMakeLists.txt
libarchive/test/test_read_format_xar.c [new file with mode: 0644]

index b9bd00836ff7f1823f16cdc8105475a44b4b52c3..92d65f72b0b4d0eb20b054700dab977a39f6a03a 100644 (file)
@@ -316,6 +316,46 @@ ELSEIF(HAVE_OPENSSL_SHA_H)
 ENDIF()
 SET(CMAKE_REQUIRED_LIBRARIES "")
 
+#
+# Find Libxml2
+#
+FIND_PACKAGE(LibXml2)
+IF(LIBXML2_FOUND)
+  INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR})
+  LIST(APPEND ADDITIONAL_LIBS ${LIBXML2_LIBRARIES})
+  SET(HAVE_LIBXML2 1)
+  # libxml2's include files use iconv.h
+  # We need a directory path of iconv.h so that it won't fail to check
+  # "libxml/xmlreader.h".
+  FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
+  SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR})
+  LA_CHECK_INCLUDE_FILE("libxml/xmlreader.h" HAVE_LIBXML_XMLREADER_H)
+  SET(CMAKE_REQUIRED_INCLUDES "")
+ELSE(LIBXML2_FOUND)
+  #
+  # Find Expat
+  #
+  IF(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+    #
+    # FreeBSD ported expat as bsdxml into their base system.
+    #
+    LA_CHECK_INCLUDE_FILE("bsdxml.h" HAVE_BSDXML_H)
+    FIND_LIBRARY(BSDXML_LIBRARY NAMES bsdxml libbsdxml)
+    IF(BSDXML_LIBRARY)
+      SET(HAVE_LIBBSDXML 1)
+      LIST(APPEND ADDITIONAL_LIBS ${BSDXML_LIBRARY})
+    ENDIF(BSDXML_LIBRARY)
+  ELSE(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+    FIND_PACKAGE(EXPAT)
+    IF(EXPAT_FOUND)
+      INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR})
+      LIST(APPEND ADDITIONAL_LIBS ${EXPAT_LIBRARIES})
+      SET(HAVE_LIBEXPAT 1)
+      LA_CHECK_INCLUDE_FILE("expat.h" HAVE_EXPAT_H)
+    ENDIF(EXPAT_FOUND)
+  ENDIF(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ENDIF(LIBXML2_FOUND)
+
 #
 # Check functions
 #
index bdc8547df04c7dbe5351c885d0c6662bfc00d6ce..9ea8b64924fd8c07a1bdf6b9d275962381aa703e 100644 (file)
@@ -134,6 +134,7 @@ libarchive_la_SOURCES=                                              \
        libarchive/archive_read_support_format_mtree.c          \
        libarchive/archive_read_support_format_raw.c            \
        libarchive/archive_read_support_format_tar.c            \
+       libarchive/archive_read_support_format_xar.c            \
        libarchive/archive_read_support_format_zip.c            \
        libarchive/archive_string.c                             \
        libarchive/archive_string.h                             \
@@ -286,6 +287,7 @@ libarchive_test_SOURCES=                                    \
        libarchive/test/test_read_format_tlz.c                  \
        libarchive/test/test_read_format_txz.c                  \
        libarchive/test/test_read_format_tz.c                   \
+       libarchive/test/test_read_format_xar.c                  \
        libarchive/test/test_read_format_zip.c                  \
        libarchive/test/test_read_large.c                       \
        libarchive/test/test_read_pax_truncated.c               \
index 98140ffad00161bfb03effa0b89b439d1aa92eb3..2a802694981c2f71585103ac7d35094b8eeb2580 100644 (file)
@@ -45,6 +45,9 @@
 /* Define to 1 if you have the <attr/xattr.h> header file. */
 #cmakedefine HAVE_ATTR_XATTR_H 1
 
+/* Define to 1 if you have the <bsdxml.h> header file. */
+#cmakedefine HAVE_BSDXML_H 1
+
 /* Define to 1 if you have the <bzlib.h> header file. */
 #cmakedefine HAVE_BZLIB_H 1
 
 /* Define to 1 if you have the <errno.h> header file. */
 #cmakedefine HAVE_ERRNO_H 1
 
+/* Define to 1 if you have the <expat.h> header file. */
+#cmakedefine HAVE_EXPAT_H 1
+
 /* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
 #cmakedefine HAVE_EXT2FS_EXT2_FS_H 1
 
 /* Define to 1 if you have the `attr' library (-lattr). */
 #cmakedefine HAVE_LIBATTR 1
 
+/* Define to 1 if you have the `bsdxml' library (-lbsdxml). */
+#cmakedefine HAVE_LIBBSDXML 1
+
 /* Define to 1 if you have the `bz2' library (-lbz2). */
 #cmakedefine HAVE_LIBBZ2 1
 
+/* Define to 1 if you have the `expat' library (-lexpat). */
+#cmakedefine HAVE_LIBEXPAT 1
+
 /* Define to 1 if you have the `lzma' library (-llzma). */
 #cmakedefine HAVE_LIBLZMA 1
 
 /* Define to 1 if you have the `lzmadec' library (-llzmadec). */
 #cmakedefine HAVE_LIBLZMADEC 1
 
+/* Define to 1 if you have the `xml2' library (-lxml2). */
+#cmakedefine HAVE_LIBXML2 1
+
+/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
+#cmakedefine HAVE_LIBXML_XMLREADER_H 1
+
 /* Define to 1 if you have the `z' library (-lz). */
 #cmakedefine HAVE_LIBZ 1
 
index dd759338a995b087b163b53c092d7b28b7e9aa82..e38ebbb00c90066e4584b838be6f245de30646fc 100644 (file)
@@ -234,7 +234,36 @@ if test "x$with_lzma" != "xno"; then
 fi
 
 AC_ARG_WITH([openssl],
-  AS_HELP_STRING([--without-openssl], [Don't build support for mtree hashes through openssl]))
+  AS_HELP_STRING([--without-openssl], [Don't build support for mtree and xar hashes through openssl]))
+
+AC_ARG_WITH([xml2],
+  AS_HELP_STRING([--without-xml2], [Don't build support for xar through libxml2]))
+AC_ARG_WITH([expat],
+  AS_HELP_STRING([--without-expat], [Don't build support for xar through expat]))
+
+if test "x$with_xml2" != "xno"; then
+  AC_PATH_PROG([XML2_CONFIG], [xml2-config],, [${PATH}])
+  if test "x$XML2_CONFIG" != "x"; then
+    CPPFLAGS="${CPPFLAGS} `${XML2_CONFIG} --cflags`"
+  fi
+  AC_CHECK_HEADERS([libxml/xmlreader.h])
+  AC_CHECK_LIB(xml2,xmlInitParser)
+fi
+if test "x$ac_cv_header_libxml_xmlreader_h" != "xyes"; then
+  if test "x$with_expat" != "xno"; then
+    case $host in
+      # FreeBSD has ported expat as bsdxml
+      *freebsd* )
+      AC_CHECK_HEADERS([bsdxml.h])
+      AC_CHECK_LIB(bsdxml,XML_ParserCreate)
+      ;;
+      * )
+      AC_CHECK_HEADERS([expat.h])
+      AC_CHECK_LIB(expat,XML_ParserCreate)
+      ;;
+    esac
+  fi
+fi
 
 AC_CHECK_HEADERS([md5.h ripemd.h rmd160.h sha.h sha1.h sha2.h sha256.h])
 # Common names for libc implementation on NetBSD and OpenBSD
index ed70fdfe5b9132dfad54fa323d5cd8e6fc5bc6d1..587bcfb945e965cca1b2bffdc6e01d5bcfe67f26 100644 (file)
@@ -55,6 +55,7 @@ SET(libarchive_SOURCES
   archive_read_support_format_mtree.c
   archive_read_support_format_raw.c
   archive_read_support_format_tar.c
+  archive_read_support_format_xar.c
   archive_read_support_format_zip.c
   archive_string.c
   archive_string.h
index 6db0242dd5276cceac88aaf0d05fc2b9d2818658..28920a21d91031be1aef54816d9ee14cc26358d0 100644 (file)
@@ -286,6 +286,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data);
 #define        ARCHIVE_FORMAT_AR_BSD                   (ARCHIVE_FORMAT_AR | 2)
 #define        ARCHIVE_FORMAT_MTREE                    0x80000
 #define        ARCHIVE_FORMAT_RAW                      0x90000
+#define        ARCHIVE_FORMAT_XAR                      0xA0000
 
 /*-
  * Basic outline for reading an archive:
@@ -333,6 +334,7 @@ __LA_DECL int                archive_read_support_format_iso9660(struct archive *);
 __LA_DECL int           archive_read_support_format_mtree(struct archive *);
 __LA_DECL int           archive_read_support_format_raw(struct archive *);
 __LA_DECL int           archive_read_support_format_tar(struct archive *);
+__LA_DECL int           archive_read_support_format_xar(struct archive *);
 __LA_DECL int           archive_read_support_format_zip(struct archive *);
 
 
index f4aa096cdd7b029bbe5357f74256b287da5817ca..4a3ce266cebf7275f3d323bf66c4405b2008a3ac 100644 (file)
@@ -867,6 +867,16 @@ archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target
                entry->ae_set &= ~AE_SET_HARDLINK;
 }
 
+int
+archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+       if (target != NULL)
+               entry->ae_set |= AE_SET_HARDLINK;
+       else
+               entry->ae_set &= ~AE_SET_HARDLINK;
+       return (aes_update_utf8(&entry->ae_hardlink, target));
+}
+
 void
 archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
 {
@@ -1115,6 +1125,16 @@ archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linknam
                entry->ae_set &= ~AE_SET_SYMLINK;
 }
 
+int
+archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+       if (linkname != NULL)
+               entry->ae_set |= AE_SET_SYMLINK;
+       else
+               entry->ae_set &= ~AE_SET_SYMLINK;
+       return (aes_update_utf8(&entry->ae_symlink, linkname));
+}
+
 void
 archive_entry_set_uid(struct archive_entry *entry, uid_t u)
 {
@@ -1922,6 +1942,18 @@ static struct flag {
        { "nouunlnk",   L"nouunlnk",            UF_NOUNLINK,    0 },
        { "nouunlink",  L"nouunlink",           UF_NOUNLINK,    0 },
 #endif
+#ifdef EXT2_UNRM_FL
+        { "nouunlink", L"nouunlink",           EXT2_UNRM_FL,   0},
+#endif
+
+#ifdef EXT2_BTREE_FL
+        { "nobtree",   L"nobtree",             EXT2_BTREE_FL,  0 },
+#endif
+
+#ifdef EXT2_ECOMPR_FL
+        { "nocomperr", L"nocomperr",           EXT2_ECOMPR_FL, 0 },
+#endif
+
 #ifdef EXT2_COMPR_FL                           /* 'c' */
         { "nocompress",        L"nocompress",          EXT2_COMPR_FL,  0 },
 #endif
@@ -1929,6 +1961,46 @@ static struct flag {
 #ifdef EXT2_NOATIME_FL                         /* 'A' */
         { "noatime",   L"noatime",             0,              EXT2_NOATIME_FL},
 #endif
+
+#ifdef EXT2_DIRTY_FL
+        { "nocompdirty",L"nocompdirty",                EXT2_DIRTY_FL,          0},
+#endif
+
+#ifdef EXT2_COMPRBLK_FL
+#ifdef EXT2_NOCOMPR_FL
+        { "nocomprblk",        L"nocomprblk",          EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL},
+#else
+        { "nocomprblk",        L"nocomprblk",          EXT2_COMPRBLK_FL,       0},
+#endif
+#endif
+#ifdef EXT2_DIRSYNC_FL
+        { "nodirsync", L"nodirsync",           EXT2_DIRSYNC_FL,        0},
+#endif
+#ifdef EXT2_INDEX_FL
+        { "nohashidx", L"nohashidx",           EXT2_INDEX_FL,          0},
+#endif
+#ifdef EXT2_IMAGIC_FL
+        { "noimagic",  L"noimagic",            EXT2_IMAGIC_FL,         0},
+#endif
+#ifdef EXT3_JOURNAL_DATA_FL
+        { "nojournal", L"nojournal",           EXT3_JOURNAL_DATA_FL,   0},
+#endif
+#ifdef EXT2_SECRM_FL
+        { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL,                0},
+#endif
+#ifdef EXT2_SYNC_FL
+        { "nosync",    L"nosync",              EXT2_SYNC_FL,           0},
+#endif
+#ifdef EXT2_NOTAIL_FL
+        { "notail",    L"notail",              0,              EXT2_NOTAIL_FL},
+#endif
+#ifdef EXT2_TOPDIR_FL
+        { "notopdir",  L"notopdir",            EXT2_TOPDIR_FL,         0},
+#endif
+#ifdef EXT2_RESERVED_FL
+        { "noreserved",        L"noreserved",          EXT2_RESERVED_FL,       0},
+#endif
+
        { NULL,         NULL,                   0,              0 }
 };
 
index 3a12c9fa3a3b305c4b3f07c4f7c8c88feacc27f6..7ac3babfbea7dc0ecd3527beb5e07c82a1aaeadd 100644 (file)
@@ -267,6 +267,7 @@ __LA_DECL int       archive_entry_update_gname_utf8(struct archive_entry *, const char
 __LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *);
 __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *);
 __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int  archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
 #if ARCHIVE_VERSION_NUMBER >= 3000000
 /* Starting with libarchive 3.0, this will be synonym for ino64. */
 __LA_DECL void archive_entry_set_ino(struct archive_entry *, __LA_INT64_T);
@@ -296,6 +297,7 @@ __LA_DECL void      archive_entry_copy_sourcepath(struct archive_entry *, const char
 __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
 __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
 __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int  archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
 __LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_UID_T);
 __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *);
 __LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *);
index e64d69137c692bea0d78c4253d0794415e8bf503..fdd3feba8c256a1b776888d9c5162ea3e0805765 100644 (file)
@@ -164,7 +164,7 @@ struct archive_read {
                int     (*read_data)(struct archive_read *, const void **, size_t *, off_t *);
                int     (*read_data_skip)(struct archive_read *);
                int     (*cleanup)(struct archive_read *);
-       }       formats[8];
+       }       formats[9];
        struct archive_format_descriptor        *format; /* Active format. */
 
        /*
index 24e31ef54f57457a8b47fab9e38ef4b05a20dfd5..5dc65ba82f69390e4287a337cb29861c09f9db7d 100644 (file)
@@ -37,6 +37,7 @@ archive_read_support_format_all(struct archive *a)
        archive_read_support_format_iso9660(a);
        archive_read_support_format_mtree(a);
        archive_read_support_format_tar(a);
+       archive_read_support_format_xar(a);
        archive_read_support_format_zip(a);
        return (ARCHIVE_OK);
 }
diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c
new file mode 100644 (file)
index 0000000..2ec649e
--- /dev/null
@@ -0,0 +1,3139 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_LIBXML_XMLREADER_H
+#include <libxml/xmlreader.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#include <expat.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#include <lzma.h>
+#elif HAVE_LZMADEC_H
+#include <lzmadec.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_hash.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if (!defined(HAVE_LIBXML_XMLREADER_H) && \
+     !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\
+       !defined(HAVE_ZLIB_H) || \
+       !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ *   o libxml2 or expat --- XML parser
+ *   o openssl or MD5/SHA1 hash function
+ *   o zlib
+ *   o bzlib2 (option)
+ *   o liblzma (option)
+ */
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+
+       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+           "Xar not supported on this platform");
+       return (ARCHIVE_WARN);
+}
+
+#else  /* Support xar format */
+
+//#define DEBUG 1
+//#define DEBUG_PRINT_TOC 1
+#if DEBUG_PRINT_TOC
+#define PRINT_TOC(d, outbytes) do {                            \
+       unsigned char *x = (unsigned char *)(uintptr_t)d;       \
+       unsigned char c = x[outbytes-1];                        \
+       x[outbytes - 1] = 0;                                    \
+       fprintf(stderr, "%s", x);                               \
+       fprintf(stderr, "%c", c);                               \
+       x[outbytes - 1] = c;                                    \
+} while (0)
+#else
+#define PRINT_TOC(d, outbytes)
+#endif
+
+#define HEADER_MAGIC   0x78617221
+#define HEADER_SIZE    28
+#define HEADER_VERSION 1
+#define CKSUM_NONE     0
+#define CKSUM_SHA1     1
+#define CKSUM_MD5      2
+
+#define MD5_SIZE       16
+#define SHA1_SIZE      20
+#define MAX_SUM_SIZE   20
+
+enum enctype {
+       NONE,
+       GZIP,
+       BZIP2,
+       LZMA,
+       XZ,
+};
+
+struct chksumval {
+       int                      alg;
+       size_t                   len;
+       unsigned char            val[MAX_SUM_SIZE];
+};
+
+struct chksumwork {
+       int                      alg;
+#ifdef ARCHIVE_HAS_MD5
+       archive_md5_ctx          md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+       archive_sha1_ctx         sha1ctx;
+#endif
+};
+
+struct xattr {
+       struct xattr            *next;
+       struct archive_string    name;
+       uint64_t                 id;
+       uint64_t                 length;
+       uint64_t                 offset;
+       uint64_t                 size;
+       enum enctype             encoding;
+       struct chksumval         a_sum;
+       struct chksumval         e_sum;
+       struct archive_string    fstype;
+};
+
+struct xar_file {
+       struct xar_file         *next;
+       struct xar_file         *hdnext;
+       struct xar_file         *parent;
+       int                      subdirs;
+
+       unsigned int             has;
+#define HAS_DATA               0x00001
+#define HAS_PATHNAME           0x00002
+#define HAS_SYMLINK            0x00004
+#define HAS_TIME               0x00008
+#define HAS_UID                        0x00010
+#define HAS_GID                        0x00020
+#define HAS_MODE               0x00040
+#define HAS_TYPE               0x00080
+#define HAS_RDEV               0x00100
+#define HAS_DEVMAJOR           0x00200
+#define HAS_DEVMINOR           0x00400
+#define HAS_INO                        0x00800
+#define HAS_FFLAGS             0x01000
+#define HAS_XATTR              0x02000
+#define HAS_ACL                        0x04000
+
+       uint64_t                 id;
+       uint64_t                 length;
+       uint64_t                 offset;
+       uint64_t                 size;
+       enum enctype             encoding;
+       struct chksumval         a_sum;
+       struct chksumval         e_sum;
+       struct archive_string    pathname;
+       struct archive_string    symlink;
+       time_t                   ctime;
+       time_t                   mtime;
+       time_t                   atime;
+       struct archive_string    uname;
+       uid_t                    uid;
+       struct archive_string    gname;
+       gid_t                    gid;
+       mode_t                   mode;
+       dev_t                    rdev;
+       dev_t                    devmajor;
+       dev_t                    devminor;
+       int64_t                  ino64;
+       struct archive_string    fflags_text;
+       int                      link;
+       unsigned int             nlink;
+       struct archive_string    hardlink;
+       struct xattr            *xattr_list;
+};
+
+struct hdlink {
+       struct hdlink            *next;
+
+       unsigned int             id;
+       int                      cnt;
+       struct xar_file          *files;
+};
+
+struct heap_queue {
+       struct xar_file         **files;
+       int                      allocated;
+       int                      used;
+};
+
+enum xmlstatus {
+       INIT,
+       XAR,
+       TOC,
+       TOC_CREATION_TIME,
+       TOC_CHECKSUM,
+       TOC_CHECKSUM_OFFSET,
+       TOC_CHECKSUM_SIZE,
+       TOC_FILE,
+       FILE_DATA,
+       FILE_DATA_LENGTH,
+       FILE_DATA_OFFSET,
+       FILE_DATA_SIZE,
+       FILE_DATA_ENCODING,
+       FILE_DATA_A_CHECKSUM,
+       FILE_DATA_E_CHECKSUM,
+       FILE_DATA_CONTENT,
+       FILE_EA,
+       FILE_EA_LENGTH,
+       FILE_EA_OFFSET,
+       FILE_EA_SIZE,
+       FILE_EA_ENCODING,
+       FILE_EA_A_CHECKSUM,
+       FILE_EA_E_CHECKSUM,
+       FILE_EA_NAME,
+       FILE_EA_FSTYPE,
+       FILE_CTIME,
+       FILE_MTIME,
+       FILE_ATIME,
+       FILE_GROUP,
+       FILE_GID,
+       FILE_USER,
+       FILE_UID,
+       FILE_MODE,
+       FILE_DEVICE,
+       FILE_DEVICE_MAJOR,
+       FILE_DEVICE_MINOR,
+       FILE_DEVICENO,
+       FILE_INODE,
+       FILE_LINK,
+       FILE_TYPE,
+       FILE_NAME,
+       FILE_ACL,
+       FILE_ACL_DEFAULT,
+       FILE_ACL_ACCESS,
+       FILE_ACL_APPLEEXTENDED,
+       /* BSD file flags. */
+       FILE_FLAGS,
+       FILE_FLAGS_USER_NODUMP,
+       FILE_FLAGS_USER_IMMUTABLE,
+       FILE_FLAGS_USER_APPEND,
+       FILE_FLAGS_USER_OPAQUE,
+       FILE_FLAGS_USER_NOUNLINK,
+       FILE_FLAGS_SYS_ARCHIVED,
+       FILE_FLAGS_SYS_IMMUTABLE,
+       FILE_FLAGS_SYS_APPEND,
+       FILE_FLAGS_SYS_NOUNLINK,
+       FILE_FLAGS_SYS_SNAPSHOT,
+       /* Linux file flags. */
+       FILE_EXT2,
+       FILE_EXT2_SecureDeletion,
+       FILE_EXT2_Undelete,
+       FILE_EXT2_Compress,
+       FILE_EXT2_Synchronous,
+       FILE_EXT2_Immutable,
+       FILE_EXT2_AppendOnly,
+       FILE_EXT2_NoDump,
+       FILE_EXT2_NoAtime,
+       FILE_EXT2_CompDirty,
+       FILE_EXT2_CompBlock,
+       FILE_EXT2_NoCompBlock,
+       FILE_EXT2_CompError,
+       FILE_EXT2_BTree,
+       FILE_EXT2_HashIndexed,
+       FILE_EXT2_iMagic,
+       FILE_EXT2_Journaled,
+       FILE_EXT2_NoTail,
+       FILE_EXT2_DirSync,
+       FILE_EXT2_TopDir,
+       FILE_EXT2_Reserved,
+       UNKNOWN,
+};
+
+struct unknown_tag {
+       struct unknown_tag      *next;
+       struct archive_string    name;
+};
+
+struct xar {
+       int64_t                  offset; /* Current position in the file. */
+       int64_t                  total;
+       int64_t                  h_base;
+       int                      end_of_file;
+       unsigned char            buff[1024*32];
+
+       enum xmlstatus           xmlsts;
+       enum xmlstatus           xmlsts_unknown;
+       struct unknown_tag      *unknowntags;
+       int                      base64text;
+
+       /*
+        * TOC
+        */
+       uint64_t                 toc_remaining;
+       uint64_t                 toc_total;
+       uint64_t                 toc_chksum_offset;
+       uint64_t                 toc_chksum_size;
+
+       /*
+        * For Decoding data.
+        */
+       enum enctype             rd_encoding;
+       z_stream                 stream;
+       int                      stream_valid;
+#ifdef HAVE_BZLIB_H
+       bz_stream                bzstream;
+       int                      bzstream_valid;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+       lzma_stream              lzstream;
+       int                      lzstream_valid;
+#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC
+       lzmadec_stream           lzstream;
+       int                      lzstream_valid;
+#endif
+       /*
+        * For Checksum data.
+        */
+       struct chksumwork        a_sumwrk;
+       struct chksumwork        e_sumwrk;
+
+       struct xar_file         *file;  /* current reading file. */
+       struct xattr            *xattr; /* current reading extended attribute. */
+       struct heap_queue        file_queue;
+       struct hdlink           *hdlink_list;
+
+       int                      entry_init;
+       uint64_t                 entry_total;
+       uint64_t                 entry_remaining;
+       uint64_t                 entry_size;
+       enum enctype             entry_encoding;
+       struct chksumval         entry_a_sum;
+       struct chksumval         entry_e_sum;
+};
+
+struct xmlattr {
+       struct xmlattr  *next;
+       char            *name;
+       char            *value;
+};
+
+struct xmlattr_list {
+       struct xmlattr  *first;
+       struct xmlattr  **last;
+};
+
+static int     xar_bid(struct archive_read *);
+static int     xar_read_header(struct archive_read *,
+                   struct archive_entry *);
+static int     xar_read_data(struct archive_read *,
+                   const void **, size_t *, off_t *);
+static int     xar_read_data_skip(struct archive_read *);
+static int     xar_cleanup(struct archive_read *);
+static int     move_reading_point(struct archive_read *, uint64_t);
+static int     rd_contents_init(struct archive_read *,
+                   enum enctype, int, int);
+static int     rd_contents(struct archive_read *, const void **,
+                   size_t *, size_t *, uint64_t);
+static uint64_t        atol10(const char *, size_t);
+static int64_t atol8(const char *, size_t);
+static size_t  atohex(unsigned char *, size_t, const char *, size_t);
+static time_t  parse_time(const char *p, size_t n);
+static void    heap_add_entry(struct heap_queue *, struct xar_file *);
+static struct xar_file *heap_get_entry(struct heap_queue *);
+static void    add_link(struct xar *, struct xar_file *);
+static void    checksum_init(struct archive_read *, int, int);
+static void    checksum_update(struct archive_read *, const void *,
+                   size_t, const void *, size_t);
+static int     checksum_final(struct archive_read *, const void *,
+                   size_t, const void *, size_t);
+static int     decompression_init(struct archive_read *, enum enctype);
+static int     decompress(struct archive_read *, const void **,
+                   size_t *, const void *, size_t *);
+static int     decompression_cleanup(struct archive_read *);
+static void    xmlattr_cleanup(struct xmlattr_list *);
+static void    file_new(struct xar *, struct xmlattr_list *);
+static void    file_free(struct xar_file *);
+static void    xattr_new(struct xar *, struct xmlattr_list *);
+static void    xattr_free(struct xattr *);
+static int     getencoding(struct xmlattr_list *);
+static int     getsumalgorithm(struct xmlattr_list *);
+static void    unknowntag_start(struct xar *, const char *);
+static void    unknowntag_end(struct xar *, const char *);
+static void    xml_start(void *, const char *, struct xmlattr_list *);
+static void    xml_end(void *, const char *);
+static void    xml_data(void *, const char *, int);
+static int     xml_parse_file_flags(struct xar *, const char *);
+static int     xml_parse_file_ext2(struct xar *, const char *);
+#if defined(HAVE_LIBXML_XMLREADER_H)
+static int     xml2_xmlattr_setup(struct xmlattr_list *, xmlTextReaderPtr);
+static int     xml2_read_cb(void *, char *, int);
+static int     xml2_close_cb(void *);
+static void    xml2_error_hdr(void *, const char *, xmlParserSeverities,
+                   xmlTextReaderLocatorPtr);
+static int     xml2_read_toc(struct archive_read *);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+static void    expat_xmlattr_setup(struct xmlattr_list *, const XML_Char **);
+static void    expat_start_cb(void *, const XML_Char *, const XML_Char **);
+static void    expat_end_cb(void *, const XML_Char *);
+static void    expat_data_cb(void *, const XML_Char *, int);
+static int     expat_read_toc(struct archive_read *);
+#endif
+
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+       struct xar *xar;
+       struct archive_read *a = (struct archive_read *)_a;
+       int r;
+
+       xar = (struct xar *)calloc(1, sizeof(*xar));
+       if (xar == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate xar data");
+               return (ARCHIVE_FATAL);
+       }
+
+       r = __archive_read_register_format(a,
+           xar,
+           "xar",
+           xar_bid,
+           NULL,
+           xar_read_header,
+           xar_read_data,
+           xar_read_data_skip,
+           xar_cleanup);
+       if (r != ARCHIVE_OK)
+               free(xar);
+       return (r);
+}
+
+static int
+xar_bid(struct archive_read *a)
+{
+       const unsigned char *b;
+       int bid;
+
+       b = __archive_read_ahead(a, HEADER_SIZE, NULL);
+       if (b == NULL)
+               return (-1);
+
+       bid = 0;
+       /*
+        * Verify magic code
+        */
+       if (archive_be32dec(b) != HEADER_MAGIC)
+               return (0);
+       bid += 32;
+       /*
+        * Verify header size
+        */
+       if (archive_be16dec(b+4) != HEADER_SIZE)
+               return (0);
+       bid += 16;
+       /*
+        * Verify header version
+        */
+       if (archive_be16dec(b+6) != HEADER_VERSION)
+               return (0);
+       bid += 16;
+       /*
+        * Verify type of checksum
+        */
+       switch (archive_be32dec(b+24)) {
+       case CKSUM_NONE:
+       case CKSUM_SHA1:
+       case CKSUM_MD5:
+               bid += 32;
+               break;
+       default:
+               return (0);
+       }
+
+       return (bid);
+}
+
+static int
+read_toc(struct archive_read *a)
+{
+       struct xar *xar;
+       const unsigned char *b;
+       uint64_t toc_compressed_size;
+       uint64_t toc_uncompressed_size;
+       uint32_t toc_chksum_alg;
+       ssize_t bytes;
+       int i, r;
+
+       xar = (struct xar *)(a->format->data);
+
+       /*
+        * Read xar header.
+        */
+       b = __archive_read_ahead(a, HEADER_SIZE, &bytes);
+       if (bytes < 0)
+               return ((int)bytes);
+       if (bytes < HEADER_SIZE) {
+               archive_set_error(&a->archive,
+                   ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Truncated archive header");
+               return (ARCHIVE_FATAL);
+       }
+
+       if (archive_be32dec(b) != HEADER_MAGIC) {
+               archive_set_error(&a->archive,
+                   ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Invalid header magic");
+               return (ARCHIVE_FATAL);
+       }
+       if (archive_be16dec(b+6) != HEADER_VERSION) {
+               archive_set_error(&a->archive,
+                   ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Unsupported header version(%d)",
+                   archive_be16dec(b+6));
+               return (ARCHIVE_FATAL);
+       }
+       toc_compressed_size = archive_be64dec(b+8);
+       xar->toc_remaining = toc_compressed_size;
+       toc_uncompressed_size = archive_be64dec(b+16);
+       toc_chksum_alg = archive_be32dec(b+24);
+       __archive_read_consume(a, HEADER_SIZE);
+       xar->offset += HEADER_SIZE;
+       xar->toc_total = 0;
+
+       /*
+        * Read TOC(Table of Contents).
+        */
+       /* Initialize reading contents. */
+       r = move_reading_point(a, HEADER_SIZE);
+       if (r != ARCHIVE_OK)
+               return (r);
+       r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE);
+       if (r != ARCHIVE_OK)
+               return (r);
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+       r = xml2_read_toc(a);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+       r = expat_read_toc(a);
+#endif
+       if (r != ARCHIVE_OK)
+               return (r);
+
+       /* Set 'The HEAP' base. */
+       xar->h_base = xar->offset;
+       if (xar->toc_total != toc_uncompressed_size) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "TOC uncompressed size error");
+               return (ARCHIVE_FATAL);
+       }
+
+       /*
+        * Checksum TOC
+        */
+       if (toc_chksum_alg != CKSUM_NONE) {
+               r = move_reading_point(a, xar->toc_chksum_offset);
+               if (r != ARCHIVE_OK)
+                       return (r);
+               b = __archive_read_ahead(a, xar->toc_chksum_size, &bytes);
+               if (bytes < 0)
+                       return ((int)bytes);
+               if (bytes < xar->toc_chksum_size) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Truncated archive file");
+                       return (ARCHIVE_FATAL);
+               }
+               r = checksum_final(a, b, xar->toc_chksum_size, NULL, 0);
+               __archive_read_consume(a, xar->toc_chksum_size);
+               xar->offset += xar->toc_chksum_size;
+               if (r != ARCHIVE_OK)
+                       return (ARCHIVE_FATAL);
+       }
+
+       /*
+        * Connect hardlinked files.
+        */
+       for (i = 0; i < xar->file_queue.used; i++) {
+               struct hdlink *link;
+               struct xar_file *file;
+
+               file = xar->file_queue.files[i];
+               if (file->link >= 0)
+                       continue;
+               for (link = xar->hdlink_list; link != NULL;
+                   link = link->next) {
+                       if (link->id == file->id) {
+                               struct xar_file *f2;
+                               int nlink = link->cnt + 1;
+
+                               file->nlink = nlink;
+                               for (f2 = link->files; f2 != NULL;
+                                   f2 = f2->hdnext) {
+                                       f2->nlink = nlink;
+                                       archive_string_copy(
+                                           &(f2->hardlink), &(file->pathname));
+                               }
+                       }
+               }
+       }
+       a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+       a->archive.archive_format_name = "xar";
+
+       return (ARCHIVE_OK);
+}
+
+static int
+xar_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+       struct xar *xar;
+       struct xar_file *file;
+       struct xattr *xattr;
+       int r;
+
+       xar = (struct xar *)(a->format->data);
+
+       if (xar->offset == 0) {
+               /* Read TOC. */
+               r = read_toc(a);
+               if (r != ARCHIVE_OK)
+                       return (r);
+       }
+
+       for (;;) {
+               file = xar->file = heap_get_entry(&(xar->file_queue));
+               if (file == NULL) {
+                       xar->end_of_file = 1;
+                       return (ARCHIVE_EOF);
+               }
+               if ((file->mode & AE_IFMT) != AE_IFDIR)
+                       break;
+               if (file->has != (HAS_PATHNAME | HAS_TYPE))
+                       break;
+               /*
+                * If a file type is a directory and it does not have
+                * any metadata, do not export.
+                */
+               file_free(file);
+       }
+       archive_entry_set_atime(entry, file->atime, 0);
+       archive_entry_set_ctime(entry, file->ctime, 0);
+       archive_entry_set_mtime(entry, file->mtime, 0);
+       archive_entry_set_gid(entry, file->gid);
+       if (file->gname.length > 0)
+               archive_entry_update_gname_utf8(entry, file->gname.s);
+       archive_entry_set_uid(entry, file->uid);
+       if (file->uname.length > 0)
+               archive_entry_update_uname_utf8(entry, file->uname.s);
+       archive_entry_set_mode(entry, file->mode);
+       archive_entry_update_pathname_utf8(entry, file->pathname.s);
+       if (file->symlink.length > 0)
+               archive_entry_update_symlink_utf8(entry, file->symlink.s);
+       /* Set proper nlink. */
+       if ((file->mode & AE_IFMT) == AE_IFDIR)
+               archive_entry_set_nlink(entry, file->subdirs + 2);
+       else
+               archive_entry_set_nlink(entry, file->nlink);
+       archive_entry_set_size(entry, file->size);
+       if (archive_strlen(&(file->hardlink)) > 0)
+               archive_entry_update_hardlink_utf8(entry,
+                       file->hardlink.s);
+       archive_entry_set_ino64(entry, file->ino64);
+       if (file->has & HAS_RDEV)
+               archive_entry_set_rdev(entry, file->rdev);
+       if (file->has & HAS_DEVMAJOR)
+               archive_entry_set_devmajor(entry, file->devmajor);
+       if (file->has & HAS_DEVMINOR)
+               archive_entry_set_devminor(entry, file->devminor);
+       if (archive_strlen(&(file->fflags_text)) > 0)
+               archive_entry_copy_fflags_text(entry, file->fflags_text.s);
+
+       xar->entry_init = 1;
+       xar->entry_total = 0;
+       xar->entry_remaining = file->length;
+       xar->entry_size = file->size;
+       xar->entry_encoding = file->encoding;
+       xar->entry_a_sum = file->a_sum;
+       xar->entry_e_sum = file->e_sum;
+       /*
+        * Read extended attributes.
+        */
+       r = ARCHIVE_OK;
+       xattr = file->xattr_list;
+       while (xattr != NULL) {
+               const void *d;
+               size_t outbytes, used;
+
+               r = move_reading_point(a, xattr->offset);
+               if (r != ARCHIVE_OK)
+                       break;
+               r = rd_contents_init(a, xattr->encoding,
+                   xattr->a_sum.alg, xattr->e_sum.alg);
+               if (r != ARCHIVE_OK)
+                       break;
+               d = NULL;
+               r = rd_contents(a, &d, &outbytes, &used, xattr->length);
+               if (r != ARCHIVE_OK)
+                       break;
+               if (outbytes != xattr->size) {
+                       archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+                           "Decompressed size error");
+                       r = ARCHIVE_FATAL;
+                       break;
+               }
+               r = checksum_final(a,
+                   xattr->a_sum.val, xattr->a_sum.len,
+                   xattr->e_sum.val, xattr->e_sum.len);
+               if (r != ARCHIVE_OK)
+                       break;
+               archive_entry_xattr_add_entry(entry,
+                   xattr->name.s, d, outbytes);
+               xattr = xattr->next;
+       }
+       if (r != ARCHIVE_OK) {
+               file_free(file);
+               return (r);
+       }
+
+       if (xar->entry_remaining > 0)
+               /* Move reading point to the beginning of current
+                * file contents. */
+               r = move_reading_point(a, file->offset);
+       else
+               r = ARCHIVE_OK;
+
+       file_free(file);
+       return (r);
+}
+
+static int
+xar_read_data(struct archive_read *a,
+    const void **buff, size_t *size, off_t *offset)
+{
+       struct xar *xar;
+       size_t used;
+       int r;
+
+       xar = (struct xar *)(a->format->data);
+       if (xar->end_of_file || xar->entry_remaining <= 0) {
+               r = ARCHIVE_EOF;
+               goto abort_read_data;
+       }
+
+       if (xar->entry_init) {
+               r = rd_contents_init(a, xar->entry_encoding,
+                   xar->entry_a_sum.alg, xar->entry_e_sum.alg);
+               if (r != ARCHIVE_OK) {
+                       xar->entry_remaining = 0;
+                       return (r);
+               }
+               xar->entry_init = 0;
+       }
+
+       *buff = NULL;
+       r = rd_contents(a, buff, size, &used, xar->entry_remaining);
+       if (r != ARCHIVE_OK)
+               goto abort_read_data;
+
+       *offset = xar->entry_total;
+       xar->entry_total += *size;
+       xar->total += *size;
+       xar->offset += used;
+       xar->entry_remaining -= used;
+       __archive_read_consume(a, used);
+
+       if (xar->entry_remaining == 0) {
+               if (xar->entry_total != xar->entry_size) {
+                       archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+                           "Decompressed size error");
+                       r = ARCHIVE_FATAL;
+                       goto abort_read_data;
+               }
+               r = checksum_final(a,
+                   xar->entry_a_sum.val, xar->entry_a_sum.len,
+                   xar->entry_e_sum.val, xar->entry_e_sum.len);
+               if (r != ARCHIVE_OK)
+                       goto abort_read_data;
+       }
+
+       return (ARCHIVE_OK);
+abort_read_data:
+       *buff = NULL;
+       *size = 0;
+       *offset = xar->total;
+       return (r);
+}
+
+static int
+xar_read_data_skip(struct archive_read *a)
+{
+       struct xar *xar;
+       int64_t bytes_skipped;
+
+       xar = (struct xar *)(a->format->data);
+       if (xar->end_of_file)
+               return (ARCHIVE_EOF);
+       bytes_skipped = __archive_read_skip(a, xar->entry_remaining);
+       if (bytes_skipped < 0)
+               return (ARCHIVE_FATAL);
+       xar->offset += bytes_skipped;
+       return (ARCHIVE_OK);
+}
+
+static int
+xar_cleanup(struct archive_read *a)
+{
+       struct xar *xar;
+       int i;
+       int r = ARCHIVE_OK;
+
+       xar = (struct xar *)(a->format->data);
+       r = decompression_cleanup(a);
+       for (i = 0; i < xar->file_queue.used; i++)
+               file_free(xar->file_queue.files[i]);
+       while (xar->unknowntags != NULL) {
+               struct unknown_tag *tag;
+
+               tag = xar->unknowntags;
+               xar->unknowntags = tag->next;
+               archive_string_free(&(tag->name));
+               free(tag);
+       }
+       free(xar);
+       a->format->data = NULL;
+       return (r);
+}
+
+static int
+move_reading_point(struct archive_read *a, uint64_t offset)
+{
+       struct xar *xar;
+
+       xar = (struct xar *)(a->format->data);
+       if (xar->offset - xar->h_base != offset) {
+               /* Seek forward to the start of file contents. */
+               int64_t step;
+
+               step = offset - (xar->offset - xar->h_base);
+               if (step > 0) {
+                       step = __archive_read_skip(a, step);
+                       if (step < 0)
+                               return ((int)step);
+                       xar->offset += step;
+               } else {
+                       archive_set_error(&(a->archive),
+                           ARCHIVE_ERRNO_MISC,
+                           "Cannot seek.");
+                       return (ARCHIVE_FAILED);
+               }
+       }
+       return (ARCHIVE_OK);
+}
+
+static int
+rd_contents_init(struct archive_read *a, enum enctype encoding,
+    int a_sum_alg, int e_sum_alg)
+{
+       int r;
+
+       /* Init decompress library. */
+       if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)
+               return (r);
+       /* Init checksum library. */
+       checksum_init(a, a_sum_alg, e_sum_alg);
+       return (ARCHIVE_OK);
+}
+
+static int
+rd_contents(struct archive_read *a, const void **buff, size_t *size,
+    size_t *used, uint64_t remaining)
+{
+       const unsigned char *b;
+       ssize_t bytes;
+
+       /* Get whatever bytes are immediately available. */
+       b = __archive_read_ahead(a, 1, &bytes);
+       if (bytes < 0)
+               return ((int)bytes);
+       if (bytes == 0) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Truncated archive file");
+               return (ARCHIVE_FATAL);
+       }
+       if (bytes > remaining)
+               bytes = (ssize_t)remaining;
+
+       /*
+        * Decompress contents of file.
+        */
+       *used = bytes;
+       if (decompress(a, buff, size, b, used) != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       /*
+        * Update checksum of a compressed data and a extracted data.
+        */
+       checksum_update(a, b, *used, *buff, *size);
+
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+
+static uint64_t
+atol10(const char *p, size_t char_cnt)
+{
+       uint64_t l;
+       int digit;
+
+       l = 0;
+       digit = *p - '0';
+       while (digit >= 0 && digit < 10  && char_cnt-- > 0) {
+               l = (l * 10) + digit;
+               digit = *++p - '0';
+       }
+       return (l);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+       int64_t l;
+       int digit;
+        
+       l = 0;
+       while (char_cnt-- > 0) {
+               if (*p >= '0' && *p <= '7')
+                       digit = *p - '0';
+               else
+                       break;
+               p++;
+               l <<= 3;
+               l |= digit;
+       }
+       return (l);
+}
+
+static size_t
+atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)
+{
+       size_t fbsize = bsize;
+
+       while (bsize && psize > 1) {
+               unsigned char x;
+
+               if (p[0] >= 'a' && p[0] <= 'z')
+                       x = (p[0] - 'a' + 0x0a) << 4;
+               else if (p[0] >= 'A' && p[0] <= 'Z')
+                       x = (p[0] - 'A' + 0x0a) << 4;
+               else if (p[0] >= '0' && p[0] <= '9')
+                       x = (p[0] - '0') << 4;
+               else
+                       return (-1);
+               if (p[1] >= 'a' && p[1] <= 'z')
+                       x |= p[1] - 'a' + 0x0a;
+               else if (p[1] >= 'A' && p[1] <= 'Z')
+                       x |= p[1] - 'A' + 0x0a;
+               else if (p[1] >= '0' && p[1] <= '9')
+                       x |= p[1] - '0';
+               else
+                       return (-1);
+               
+               *b++ = x;
+               bsize--;
+               p += 2;
+               psize -= 2;
+       }
+       return (fbsize - bsize);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE_TIMEGM
+       /* Use platform timegm() if available. */
+       return (timegm(t));
+#else
+       /* Else use direct calculation using POSIX assumptions. */
+       /* First, fix up tm_yday based on the year/month/day. */
+       mktime(t);
+       /* Then we can compute timegm() from first principles. */
+       return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600
+           + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000
+           + ((t->tm_year - 69) / 4) * 86400 -
+           ((t->tm_year - 1) / 100) * 86400
+           + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+parse_time(const char *p, size_t n)
+{
+       struct tm tm;
+       time_t t = 0;
+       int64_t data;
+
+       if (n != 20)
+               return (t);
+       data = atol10(p, 4);
+       if (data < 1900)
+               return (t);
+       tm.tm_year = (int)data - 1900;
+       p += 4;
+       if (*p++ != '-')
+               return (t);
+       data = atol10(p, 2);
+       if (data < 1 || data > 12)
+               return (t);
+       tm.tm_mon = (int)data -1;
+       p += 2;
+       if (*p++ != '-')
+               return (t);
+       data = atol10(p, 2);
+       if (data < 1 || data > 31)
+               return (t);
+       tm.tm_mday = (int)data;
+       p += 2;
+       if (*p++ != 'T')
+               return (t);
+       data = atol10(p, 2);
+       if (data < 0 || data > 23)
+               return (t);
+       tm.tm_hour = (int)data;
+       p += 2;
+       if (*p++ != ':')
+               return (t);
+       data = atol10(p, 2);
+       if (data < 0 || data > 59)
+               return (t);
+       tm.tm_min = (int)data;
+       p += 2;
+       if (*p++ != ':')
+               return (t);
+       data = atol10(p, 2);
+       if (data < 0 || data > 60)
+               return (t);
+       tm.tm_sec = (int)data;
+#if 0
+       p += 2;
+       if (*p != 'Z')
+               return (t);
+#endif
+
+       t = time_from_tm(&tm);
+
+       return (t);
+}
+
+static void
+heap_add_entry(struct heap_queue *heap, struct xar_file *file)
+{
+       uint64_t file_id, parent_id;
+       int hole, parent;
+
+       /* Expand our pending files list as necessary. */
+       if (heap->used >= heap->allocated) {
+               struct xar_file **new_pending_files;
+               int new_size = heap->allocated * 2;
+
+               if (heap->allocated < 1024)
+                       new_size = 1024;
+               /* Overflow might keep us from growing the list. */
+               if (new_size <= heap->allocated)
+                       __archive_errx(1, "Out of memory");
+               new_pending_files = (struct xar_file **)
+                   malloc(new_size * sizeof(new_pending_files[0]));
+               if (new_pending_files == NULL)
+                       __archive_errx(1, "Out of memory");
+               memcpy(new_pending_files, heap->files,
+                   heap->allocated * sizeof(new_pending_files[0]));
+               if (heap->files != NULL)
+                       free(heap->files);
+               heap->files = new_pending_files;
+               heap->allocated = new_size;
+       }
+
+       file_id = file->id;
+
+       /*
+        * Start with hole at end, walk it up tree to find insertion point.
+        */
+       hole = heap->used++;
+       while (hole > 0) {
+               parent = (hole - 1)/2;
+               parent_id = heap->files[parent]->id;
+               if (file_id >= parent_id) {
+                       heap->files[hole] = file;
+                       return;
+               }
+               // Move parent into hole <==> move hole up tree.
+               heap->files[hole] = heap->files[parent];
+               hole = parent;
+       }
+       heap->files[0] = file;
+}
+
+static struct xar_file *
+heap_get_entry(struct heap_queue *heap)
+{
+       uint64_t a_id, b_id, c_id;
+       int a, b, c;
+       struct xar_file *r, *tmp;
+
+       if (heap->used < 1)
+               return (NULL);
+
+       /*
+        * The first file in the list is the earliest; we'll return this.
+        */
+       r = heap->files[0];
+
+       /*
+        * Move the last item in the heap to the root of the tree
+        */
+       heap->files[0] = heap->files[--(heap->used)];
+
+       /*
+        * Rebalance the heap.
+        */
+       a = 0; // Starting element and its heap key
+       a_id = heap->files[a]->id;
+       for (;;) {
+               b = a + a + 1; // First child
+               if (b >= heap->used)
+                       return (r);
+               b_id = heap->files[b]->id;
+               c = b + 1; // Use second child if it is smaller.
+               if (c < heap->used) {
+                       c_id = heap->files[c]->id;
+                       if (c_id < b_id) {
+                               b = c;
+                               b_id = c_id;
+                       }
+               }
+               if (a_id <= b_id)
+                       return (r);
+               tmp = heap->files[a];
+               heap->files[a] = heap->files[b];
+               heap->files[b] = tmp;
+               a = b;
+       }
+}
+
+static void
+add_link(struct xar *xar, struct xar_file *file)
+{
+       struct hdlink *link;
+
+       for (link = xar->hdlink_list; link != NULL; link = link->next) {
+               if (link->id == file->link) {
+                       file->hdnext = link->files;
+                       link->cnt++;
+                       link->files = file;
+                       return;
+               }
+       }
+       link = malloc(sizeof(*link));
+       if (link == NULL)
+               __archive_errx(1, "No memory for add_link()");
+       file->hdnext = NULL;
+       link->id = file->link;
+       link->cnt = 1;
+       link->files = file;
+       link->next = xar->hdlink_list;
+       xar->hdlink_list = link;
+}
+
+static void
+_checksum_init(struct chksumwork *sumwrk, int sum_alg)
+{
+       sumwrk->alg = sum_alg;
+       switch (sum_alg) {
+       case CKSUM_NONE:
+               break;
+       case CKSUM_SHA1:
+               archive_sha1_init(&(sumwrk->sha1ctx));
+               break;
+       case CKSUM_MD5:
+               archive_md5_init(&(sumwrk->md5ctx));
+               break;
+       }
+}
+
+static void
+_checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+       switch (sumwrk->alg) {
+       case CKSUM_NONE:
+               break;
+       case CKSUM_SHA1:
+               archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+               break;
+       case CKSUM_MD5:
+               archive_md5_update(&(sumwrk->md5ctx), buff, size);
+               break;
+       }
+}
+
+static int
+_checksum_final(struct chksumwork *sumwrk, const void *val, size_t len)
+{
+       unsigned char sum[MAX_SUM_SIZE];
+       int r = ARCHIVE_OK;
+
+       switch (sumwrk->alg) {
+       case CKSUM_NONE:
+               break;
+       case CKSUM_SHA1:
+               archive_sha1_final(&(sumwrk->sha1ctx), sum);
+               if (len != SHA1_SIZE ||
+                   memcmp(val, sum, SHA1_SIZE) != 0)
+                       r = ARCHIVE_FAILED;
+               break;
+       case CKSUM_MD5:
+               archive_md5_final(&(sumwrk->md5ctx), sum);
+               if (len != MD5_SIZE ||
+                   memcmp(val, sum, MD5_SIZE) != 0)
+                       r = ARCHIVE_FAILED;
+               break;
+       }
+       return (r);
+}
+
+static void
+checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)
+{
+       struct xar *xar;
+
+       xar = (struct xar *)(a->format->data);
+       _checksum_init(&(xar->a_sumwrk), a_sum_alg);
+       _checksum_init(&(xar->e_sumwrk), e_sum_alg);
+}
+
+static void
+checksum_update(struct archive_read *a, const void *abuff, size_t asize,
+    const void *ebuff, size_t esize)
+{
+       struct xar *xar;
+
+       xar = (struct xar *)(a->format->data);
+       _checksum_update(&(xar->a_sumwrk), abuff, asize);
+       _checksum_update(&(xar->e_sumwrk), ebuff, esize);
+}
+
+static int
+checksum_final(struct archive_read *a, const void *a_sum_val,
+    size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)
+{
+       struct xar *xar;
+       int r;
+
+       xar = (struct xar *)(a->format->data);
+       r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);
+       if (r == ARCHIVE_OK)
+               r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);
+       if (r != ARCHIVE_OK)
+               archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+                   "Sumcheck error");
+       return (r);
+}
+
+static int
+decompression_init(struct archive_read *a, enum enctype encoding)
+{
+       struct xar *xar;
+       const char *detail;
+       int r;
+
+       xar = (struct xar *)(a->format->data);
+       xar->rd_encoding = encoding;
+       switch (encoding) {
+       case NONE:
+               break;
+       case GZIP:
+               if (xar->stream_valid)
+                       r = inflateReset(&(xar->stream));
+               else
+                       r = inflateInit(&(xar->stream));
+               if (r != Z_OK) {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "Couldn't initialize zlib stream.");
+                       return (ARCHIVE_FATAL);
+               }
+               xar->stream_valid = 1;
+               xar->stream.total_in = 0;
+               xar->stream.total_out = 0;
+               break;
+#ifdef HAVE_BZLIB_H
+       case BZIP2:
+               if (xar->bzstream_valid) {
+                       BZ2_bzDecompressEnd(&(xar->bzstream));
+                       xar->bzstream_valid = 0;
+               }
+               r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0);
+               if (r == BZ_MEM_ERROR)
+                       r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1);
+               if (r != BZ_OK) {
+                       const char *detail = NULL;
+                       int err = ARCHIVE_ERRNO_MISC;
+                       switch (r) {
+                       case BZ_PARAM_ERROR:
+                               detail = "invalid setup parameter";
+                               break;
+                       case BZ_MEM_ERROR:
+                               err = ENOMEM;
+                               detail = "out of memory";
+                               break;
+                       case BZ_CONFIG_ERROR:
+                               detail = "mis-compiled library";
+                               break;
+                       }
+                       archive_set_error(&a->archive, err,
+                           "Internal error initializing decompressor%s%s",
+                           detail == NULL ? "" : ": ",
+                           detail);
+                       xar->bzstream_valid = 0;
+                       return (ARCHIVE_FATAL);
+               }
+               xar->bzstream_valid = 1;
+               xar->bzstream.total_in_lo32 = 0;
+               xar->bzstream.total_in_hi32 = 0;
+               xar->bzstream.total_out_lo32 = 0;
+               xar->bzstream.total_out_hi32 = 0;
+               break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+       case XZ:
+       case LZMA:
+               if (xar->lzstream_valid) {
+                       lzma_end(&(xar->lzstream));
+                       xar->lzstream_valid = 0;
+               }
+               if (xar->entry_encoding == XZ)
+                       r = lzma_stream_decoder(&(xar->lzstream),
+                           (1U << 30),/* memlimit */
+                           LZMA_CONCATENATED);
+               else
+                       r = lzma_alone_decoder(&(xar->lzstream),
+                           (1U << 30));/* memlimit */
+               if (r != LZMA_OK) {
+                       switch (r) {
+                       case LZMA_MEM_ERROR:
+                               archive_set_error(&a->archive,
+                                   ENOMEM,
+                                   "Internal error initializing "
+                                   "compression library: "
+                                   "Cannot allocate memory");
+                               break;
+                       case LZMA_OPTIONS_ERROR:
+                               archive_set_error(&a->archive,
+                                   ARCHIVE_ERRNO_MISC,
+                                   "Internal error initializing "
+                                   "compression library: "
+                                   "Invalid or unsupported options");
+                               break;
+                       default:
+                               archive_set_error(&a->archive,
+                                   ARCHIVE_ERRNO_MISC,
+                                   "Internal error initializing "
+                                   "lzma library");
+                               break;
+                       }
+                       return (ARCHIVE_FATAL);
+               }
+               xar->lzstream_valid = 1;
+               xar->lzstream.total_in = 0;
+               xar->lzstream.total_out = 0;
+               break;
+#elif defined(HAVE_LZMADEC_H) && defined(HAVE_LIBLZMADEC)
+       case LZMA:
+               if (xar->lzstream_valid)
+                       lzmadec_end(&(xar->lzstream));
+               r = lzmadec_init(&(xar->lzstram));
+               if (r != LZMADEC_OK) {
+                       switch (r) {
+                       case LZMADEC_HEADER_ERROR:
+                               archive_set_error(&a->archive,
+                                   ARCHIVE_ERRNO_MISC,
+                                   "Internal error initializing "
+                                   "compression library: "
+                                   "invalid header");
+                               break;
+                       case LZMADEC_MEM_ERROR:
+                               archive_set_error(&a->archive,
+                                   ENOMEM,
+                                   "Internal error initializing "
+                                   "compression library: "
+                                   "out of memory");
+                               break;
+                       }
+                       return (ARCHIVE_FATAL);
+               }
+               xar->lzstream_valid = 1;
+               xar->lzstream.total_in = 0;
+               xar->lzstream.total_out = 0;
+               break;
+#endif
+       /*
+        * Unsupported compression.
+        */
+       default:
+#ifndef HAVE_BZLIB_H
+       case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+#if !defined(HAVE_LZMADEC_H) || !defined(HAVE_LIBLZMADEC)
+       case LZMA:
+#endif
+       case XZ:
+#endif
+               switch (xar->entry_encoding) {
+               case BZIP2: detail = "bzip2"; break;
+               case LZMA: detail = "lzma"; break;
+               case XZ: detail = "xz"; break;
+               default: detail = "??"; break;
+               }
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "%s compression not supported on this platform",
+                   detail);
+               xar->bzstream_valid = 0;
+               return (ARCHIVE_FAILED);
+       }
+       return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, const void **buff, size_t *outbytes,
+    const void *b, size_t *used)
+{
+       struct xar *xar;
+       void *outbuff;
+       size_t avail_in, avail_out;
+       int r;
+
+       xar = (struct xar *)(a->format->data);
+       avail_in = *used;
+       outbuff = (void *)*buff;
+       if (outbuff == NULL) {
+               outbuff = xar->buff;
+               *buff = outbuff;
+               avail_out = sizeof(xar->buff);
+       } else
+               avail_out = *outbytes;
+       switch (xar->rd_encoding) {
+       case GZIP:
+               xar->stream.next_in = (Bytef *)(uintptr_t)b;
+               xar->stream.avail_in = avail_in;
+               xar->stream.next_out = (unsigned char *)outbuff;
+               xar->stream.avail_out = avail_out;
+               r = inflate(&(xar->stream), 0);
+               switch (r) {
+               case Z_OK: /* Decompressor made some progress.*/
+               case Z_STREAM_END: /* Found end of stream. */
+                       break;
+               default:
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "File decompression failed (%d)", r);
+                       return (ARCHIVE_FATAL);
+               }
+               *used = avail_in - xar->stream.avail_in;
+               *outbytes = avail_out - xar->stream.avail_out;
+               break;
+#ifdef HAVE_BZLIB_H
+       case BZIP2:
+               xar->bzstream.next_in = (char *)(uintptr_t)b;
+               xar->bzstream.avail_in = avail_in;
+               xar->bzstream.next_out = (char *)outbuff;
+               xar->bzstream.avail_out = avail_out;
+               r = BZ2_bzDecompress(&(xar->bzstream));
+               switch (r) {
+               case BZ_STREAM_END: /* Found end of stream. */
+                       switch (BZ2_bzDecompressEnd(&(xar->bzstream))) {
+                       case BZ_OK:
+                               break;
+                       default:
+                               archive_set_error(&(a->archive),
+                                   ARCHIVE_ERRNO_MISC,
+                                   "Failed to clean up decompressor");
+                               return (ARCHIVE_FATAL);
+                       }
+                       xar->bzstream_valid = 0;
+                       /* FALLTHROUGH */
+               case BZ_OK: /* Decompressor made some progress. */
+                       break;
+               default:
+                       archive_set_error(&(a->archive),
+                           ARCHIVE_ERRNO_MISC,
+                           "bzip decompression failed");
+                       return (ARCHIVE_FATAL);
+               }
+               *used = avail_in - xar->bzstream.avail_in;
+               *outbytes = avail_out - xar->bzstream.avail_out;
+               break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+       case LZMA:
+       case XZ:
+               xar->lzstream.next_in = b;
+               xar->lzstream.avail_in = avail_in;
+               xar->lzstream.next_out = (unsigned char *)outbuff;
+               xar->lzstream.avail_out = avail_out;
+               r = lzma_code(&(xar->lzstream), LZMA_RUN);
+               switch (r) {
+               case LZMA_STREAM_END: /* Found end of stream. */
+                       lzma_end(&(xar->lzstream));
+                       xar->lzstream_valid = 0;
+                       /* FALLTHROUGH */
+               case LZMA_OK: /* Decompressor made some progress. */
+                       break;
+               default:
+                       archive_set_error(&(a->archive),
+                           ARCHIVE_ERRNO_MISC,
+                           "%s decompression failed(%d)",
+                           (xar->entry_encoding == XZ)?"xz":"lzma",
+                           r);
+                       return (ARCHIVE_FATAL);
+               }
+               *used = avail_in - xar->lzstream.avail_in;
+               *outbytes = avail_out - xar->lzstream.avail_out;
+               break;
+#elif defined(HAVE_LZMADEC_H) && defined(HAVE_LIBLZMADEC)
+       case LZMA:
+               xar->lzstream.next_in = b;
+               xar->lzstream.avail_in = avail_in;
+               xar->lzstream.next_out = (unsigned char *)outbuff;
+               xar->lzstream.avail_out = avail_out;
+               r = lzmadec_decode(&(xar->lzstream), 0);
+               switch (r) {
+               case LZMADEC_STREAM_END: /* Found end of stream. */
+                       switch (lzmadec_end(&(xar->lzstream))) {
+                       case LZMADEC_OK:
+                               break;
+                       default:
+                               archive_set_error(&(a->archive),
+                                   ARCHIVE_ERRNO_MISC,
+                                   "Failed to clean up lzmadec decompressor");
+                               return (ARCHIVE_FATAL);
+                       }
+                       xar->lzstream_valid = 0;
+                       /* FALLTHROUGH */
+               case LZMADEC_OK: /* Decompressor made some progress. */
+                       break;
+               default:
+                       archive_set_error(&(a->archive),
+                           ARCHIVE_ERRNO_MISC,
+                           "lzmadec decompression failed(%d)",
+                           r);
+                       return (ARCHIVE_FATAL);
+               }
+               *used = avail_in - xar->lzstream.avail_in;
+               *outbytes = avail_out - xar->lzstream.avail_out;
+               break;
+#endif
+#ifndef HAVE_BZLIB_H
+       case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+#if !defined(HAVE_LZMADEC_H) || !defined(HAVE_LIBLZMADEC)
+       case LZMA:
+#endif
+       case XZ:
+#endif
+       case NONE:
+       default:
+               if (outbuff == xar->buff) {
+                       *buff = b;
+                       *used = avail_in;
+                       *outbytes = avail_in;
+               } else {
+                       if (avail_out > avail_in)
+                               avail_out = avail_in;
+                       memcpy(outbuff, b, avail_out);
+                       *used = avail_out;
+                       *outbytes = avail_out;
+               }
+               break;
+       }
+       return (ARCHIVE_OK);
+}
+
+static int
+decompression_cleanup(struct archive_read *a)
+{
+       struct xar *xar;
+       int r;
+
+       xar = (struct xar *)(a->format->data);
+       r = ARCHIVE_OK;
+       if (xar->stream_valid) {
+               if (inflateEnd(&(xar->stream)) != Z_OK) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "Failed to clean up zlib decompressor");
+                       r = ARCHIVE_FATAL;
+               }
+       }
+#ifdef HAVE_BZLIB_H
+       if (xar->bzstream_valid) {
+               if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "Failed to clean up bzip2 decompressor");
+                       r = ARCHIVE_FATAL;
+               }
+       }
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+       if (xar->lzstream_valid)
+               lzma_end(&(xar->lzstream));
+#elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+       if (xar->lzstream_valid) {
+               if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "Failed to clean up lzmadec decompressor");
+                       r = ARCHIVE_FATAL;
+               }
+       }
+#endif
+       return (r);
+}
+
+static void
+xmlattr_cleanup(struct xmlattr_list *list)
+{
+       struct xmlattr *attr, *next;
+
+       attr = list->first;
+       while (attr != NULL) {
+               next = attr->next;
+               free(attr->name);
+               free(attr->value);
+               free(attr);
+               attr = next;
+       }
+       list->first = NULL;
+       list->last = &(list->first);
+}
+
+static void
+file_new(struct xar *xar, struct xmlattr_list *list)
+{
+       struct xar_file *file;
+       struct xmlattr *attr;
+
+       file = calloc(1, sizeof(*file));
+       if (file == NULL)
+               __archive_errx(1, "Out of memory");
+       file->parent = xar->file;
+       file->mode = 0777 | AE_IFREG;
+       file->atime = time(NULL);
+       file->mtime = time(NULL);
+       xar->file = file;
+       xar->xattr = NULL;
+       for (attr = list->first; attr != NULL; attr = attr->next) {
+               if (strcmp(attr->name, "id") == 0)
+                       file->id = atol10(attr->value, strlen(attr->value));
+       }
+       file->nlink = 1;
+       heap_add_entry(&(xar->file_queue), file);
+}
+
+static void
+file_free(struct xar_file *file)
+{
+       struct xattr *xattr;
+
+       archive_string_free(&(file->pathname));
+       archive_string_free(&(file->symlink));
+       archive_string_free(&(file->uname));
+       archive_string_free(&(file->gname));
+       archive_string_free(&(file->hardlink));
+       xattr = file->xattr_list;
+       while (xattr != NULL) {
+               struct xattr *next;
+
+               next = xattr->next;
+               xattr_free(xattr);
+               xattr = next;
+       }
+
+       free(file);
+}
+
+static void
+xattr_new(struct xar *xar, struct xmlattr_list *list)
+{
+       struct xattr *xattr, **nx;
+       struct xmlattr *attr;
+
+       xattr = calloc(1, sizeof(*xattr));
+       if (xattr == NULL)
+               __archive_errx(1, "Out of memory");
+       xar->xattr = xattr;
+       for (attr = list->first; attr != NULL; attr = attr->next) {
+               if (strcmp(attr->name, "id") == 0)
+                       xattr->id = atol10(attr->value, strlen(attr->value));
+       }
+       /* Chain to xattr list. */
+       for (nx = &(xar->file->xattr_list);
+           *nx != NULL; nx = &((*nx)->next)) {
+               if (xattr->id < (*nx)->id)
+                       break;
+       }
+       xattr->next = *nx;
+       *nx = xattr;
+}
+
+static void
+xattr_free(struct xattr *xattr)
+{
+       archive_string_free(&(xattr->name));
+       free(xattr);
+}
+
+static int
+getencoding(struct xmlattr_list *list)
+{
+       struct xmlattr *attr;
+       enum enctype encoding = NONE;
+
+       for (attr = list->first; attr != NULL; attr = attr->next) {
+               if (strcmp(attr->name, "style") == 0) {
+                       if (strcmp(attr->value, "application/octet-stream") == 0)
+                               encoding = NONE;
+                       else if (strcmp(attr->value, "application/x-gzip") == 0)
+                               encoding = GZIP;
+                       else if (strcmp(attr->value, "application/x-bzip2") == 0)
+                               encoding = BZIP2;
+                       else if (strcmp(attr->value, "application/x-lzma") == 0)
+                               encoding = LZMA;
+                       else if (strcmp(attr->value, "application/x-xz") == 0)
+                               encoding = XZ;
+               }
+       }
+       return (encoding);
+}
+
+static int
+getsumalgorithm(struct xmlattr_list *list)
+{
+       struct xmlattr *attr;
+       int alg = CKSUM_NONE;
+
+       for (attr = list->first; attr != NULL; attr = attr->next) {
+               if (strcmp(attr->name, "style") == 0) {
+                       const char *v = attr->value;
+                       if ((v[0] == 'S' || v[0] == 's') &&
+                           (v[1] == 'H' || v[1] == 'h') &&
+                           (v[2] == 'A' || v[2] == 'a') &&
+                           v[3] == '1' && v[4] == '\0')
+                               alg = CKSUM_SHA1;
+                       if ((v[0] == 'M' || v[0] == 'm') &&
+                           (v[1] == 'D' || v[1] == 'd') &&
+                           v[2] == '5' && v[3] == '\0')
+                               alg = CKSUM_MD5;
+               }
+       }
+       return (alg);
+}
+
+static void
+unknowntag_start(struct xar *xar, const char *name)
+{
+       struct unknown_tag *tag;
+
+#if DEBUG
+       fprintf(stderr, "unknowntag_start:%s\n", name);
+#endif
+       tag = malloc(sizeof(*tag));
+       if (tag == NULL)
+               __archive_errx(1, "Out of memory");
+       tag->next = xar->unknowntags;
+       archive_string_init(&(tag->name));
+       archive_strcpy(&(tag->name), name);
+       if (xar->unknowntags == NULL) {
+               xar->xmlsts_unknown = xar->xmlsts;
+               xar->xmlsts = UNKNOWN;
+       }
+       xar->unknowntags = tag;
+}
+
+static void
+unknowntag_end(struct xar *xar, const char *name)
+{
+       struct unknown_tag *tag;
+
+#if DEBUG
+       fprintf(stderr, "unknowntag_end:%s\n", name);
+#endif
+       tag = xar->unknowntags;
+       if (tag == NULL || name == NULL)
+               return;
+       if (strcmp(tag->name.s, name) == 0) {
+               xar->unknowntags = tag->next;
+               archive_string_free(&(tag->name));
+               free(tag);
+               if (xar->unknowntags == NULL)
+                       xar->xmlsts = xar->xmlsts_unknown;
+       }
+}
+
+static void
+xml_start(void *userData, const char *name, struct xmlattr_list *list)
+{
+       struct archive_read *a;
+       struct xar *xar;
+       struct xmlattr *attr;
+
+       a = (struct archive_read *)userData;
+       xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+       fprintf(stderr, "xml_sta:[%s]\n", name);
+       for (attr = list->first; attr != NULL; attr = attr->next)
+               fprintf(stderr, "    attr:\"%s\"=\"%s\"\n",
+                   attr->name, attr->value);
+#endif
+       xar->base64text = 0;
+       switch (xar->xmlsts) {
+       case INIT:
+               if (strcmp(name, "xar") == 0)
+                       xar->xmlsts = XAR;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case XAR:
+               if (strcmp(name, "toc") == 0)
+                       xar->xmlsts = TOC;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case TOC:
+               if (strcmp(name, "creation-time") == 0)
+                       xar->xmlsts = TOC_CREATION_TIME;
+               else if (strcmp(name, "checksum") == 0)
+                       xar->xmlsts = TOC_CHECKSUM;
+               else if (strcmp(name, "file") == 0) {
+                       file_new(xar, list);
+                       xar->xmlsts = TOC_FILE;
+               }
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case TOC_CHECKSUM:
+               if (strcmp(name, "offset") == 0)
+                       xar->xmlsts = TOC_CHECKSUM_OFFSET;
+               else if (strcmp(name, "size") == 0)
+                       xar->xmlsts = TOC_CHECKSUM_SIZE;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case TOC_FILE:
+               if (strcmp(name, "file") == 0) {
+                       file_new(xar, list);
+               }
+               else if (strcmp(name, "data") == 0)
+                       xar->xmlsts = FILE_DATA;
+               else if (strcmp(name, "ea") == 0) {
+                       xattr_new(xar, list);
+                       xar->xmlsts = FILE_EA;
+               }
+               else if (strcmp(name, "ctime") == 0)
+                       xar->xmlsts = FILE_CTIME;
+               else if (strcmp(name, "mtime") == 0)
+                       xar->xmlsts = FILE_MTIME;
+               else if (strcmp(name, "atime") == 0)
+                       xar->xmlsts = FILE_ATIME;
+               else if (strcmp(name, "group") == 0)
+                       xar->xmlsts = FILE_GROUP;
+               else if (strcmp(name, "gid") == 0)
+                       xar->xmlsts = FILE_GID;
+               else if (strcmp(name, "user") == 0)
+                       xar->xmlsts = FILE_USER;
+               else if (strcmp(name, "uid") == 0)
+                       xar->xmlsts = FILE_UID;
+               else if (strcmp(name, "mode") == 0)
+                       xar->xmlsts = FILE_MODE;
+               else if (strcmp(name, "device") == 0)
+                       xar->xmlsts = FILE_DEVICE;
+               else if (strcmp(name, "deviceno") == 0)
+                       xar->xmlsts = FILE_DEVICENO;
+               else if (strcmp(name, "inode") == 0)
+                       xar->xmlsts = FILE_INODE;
+               else if (strcmp(name, "link") == 0)
+                       xar->xmlsts = FILE_LINK;
+               else if (strcmp(name, "type") == 0) {
+                       xar->xmlsts = FILE_TYPE;
+                       for (attr = list->first; attr != NULL;
+                           attr = attr->next) {
+                               if (strcmp(attr->name, "link") != 0)
+                                       continue;
+                               if (strcmp(attr->value, "original") == 0)
+                                       xar->file->link = xar->file->id * -1;
+                               else {
+                                       xar->file->link = atol10(attr->value,
+                                           strlen(attr->value));
+                                       if (xar->file->link > 0)
+                                               add_link(xar, xar->file);
+                                       else
+                                               xar->file->link = 0;
+                               }
+                       }
+               }
+               else if (strcmp(name, "name") == 0) {
+                       xar->xmlsts = FILE_NAME;
+                       for (attr = list->first; attr != NULL;
+                           attr = attr->next) {
+                               if (strcmp(attr->name, "enctype") == 0 &&
+                                   strcmp(attr->value, "base64") == 0)
+                                       xar->base64text = 1;
+                       }
+               }
+               else if (strcmp(name, "acl") == 0)
+                       xar->xmlsts = FILE_ACL;
+               else if (strcmp(name, "flags") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               else if (strcmp(name, "ext2") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case FILE_DATA:
+               if (strcmp(name, "length") == 0)
+                       xar->xmlsts = FILE_DATA_LENGTH;
+               else if (strcmp(name, "offset") == 0)
+                       xar->xmlsts = FILE_DATA_OFFSET;
+               else if (strcmp(name, "size") == 0)
+                       xar->xmlsts = FILE_DATA_SIZE;
+               else if (strcmp(name, "encoding") == 0) {
+                       xar->xmlsts = FILE_DATA_ENCODING;
+                       xar->file->encoding = getencoding(list);
+               }
+               else if (strcmp(name, "archived-checksum") == 0) {
+                       xar->xmlsts = FILE_DATA_A_CHECKSUM;
+                       xar->file->a_sum.alg = getsumalgorithm(list);
+               }
+               else if (strcmp(name, "extracted-checksum") == 0) {
+                       xar->xmlsts = FILE_DATA_E_CHECKSUM;
+                       xar->file->e_sum.alg = getsumalgorithm(list);
+               }
+               else if (strcmp(name, "content") == 0)
+                       xar->xmlsts = FILE_DATA_CONTENT;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case FILE_DEVICE:
+               if (strcmp(name, "major") == 0)
+                       xar->xmlsts = FILE_DEVICE_MAJOR;
+               else if (strcmp(name, "minor") == 0)
+                       xar->xmlsts = FILE_DEVICE_MINOR;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case FILE_DATA_CONTENT:
+               unknowntag_start(xar, name);
+               break;
+       case FILE_EA:
+               if (strcmp(name, "length") == 0)
+                       xar->xmlsts = FILE_EA_LENGTH;
+               else if (strcmp(name, "offset") == 0)
+                       xar->xmlsts = FILE_EA_OFFSET;
+               else if (strcmp(name, "size") == 0)
+                       xar->xmlsts = FILE_EA_SIZE;
+               else if (strcmp(name, "encoding") == 0) {
+                       xar->xmlsts = FILE_EA_ENCODING;
+                       xar->xattr->encoding = getencoding(list);
+               } else if (strcmp(name, "archived-checksum") == 0)
+                       xar->xmlsts = FILE_EA_A_CHECKSUM;
+               else if (strcmp(name, "extracted-checksum") == 0)
+                       xar->xmlsts = FILE_EA_E_CHECKSUM;
+               else if (strcmp(name, "name") == 0)
+                       xar->xmlsts = FILE_EA_NAME;
+               else if (strcmp(name, "fstype") == 0)
+                       xar->xmlsts = FILE_EA_FSTYPE;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case FILE_ACL:
+               if (strcmp(name, "appleextended") == 0)
+                       xar->xmlsts = FILE_ACL_APPLEEXTENDED;
+               if (strcmp(name, "default") == 0)
+                       xar->xmlsts = FILE_ACL_DEFAULT;
+               else if (strcmp(name, "access") == 0)
+                       xar->xmlsts = FILE_ACL_ACCESS;
+               else
+                       unknowntag_start(xar, name);
+               break;
+       case FILE_FLAGS:
+               if (!xml_parse_file_flags(xar, name))
+                       unknowntag_start(xar, name);
+               break;
+       case FILE_EXT2:
+               if (!xml_parse_file_ext2(xar, name))
+                       unknowntag_start(xar, name);
+               break;
+       case TOC_CREATION_TIME:
+       case TOC_CHECKSUM_OFFSET:
+       case TOC_CHECKSUM_SIZE:
+       case FILE_DATA_LENGTH:
+       case FILE_DATA_OFFSET:
+       case FILE_DATA_SIZE:
+       case FILE_DATA_ENCODING:
+       case FILE_DATA_A_CHECKSUM:
+       case FILE_DATA_E_CHECKSUM:
+       case FILE_EA_LENGTH:
+       case FILE_EA_OFFSET:
+       case FILE_EA_SIZE:
+       case FILE_EA_ENCODING:
+       case FILE_EA_A_CHECKSUM:
+       case FILE_EA_E_CHECKSUM:
+       case FILE_EA_NAME:
+       case FILE_EA_FSTYPE:
+       case FILE_CTIME:
+       case FILE_MTIME:
+       case FILE_ATIME:
+       case FILE_GROUP:
+       case FILE_GID:
+       case FILE_USER:
+       case FILE_UID:
+       case FILE_INODE:
+       case FILE_DEVICE_MAJOR:
+       case FILE_DEVICE_MINOR:
+       case FILE_DEVICENO:
+       case FILE_MODE:
+       case FILE_TYPE:
+       case FILE_LINK:
+       case FILE_NAME:
+       case FILE_ACL_DEFAULT:
+       case FILE_ACL_ACCESS:
+       case FILE_ACL_APPLEEXTENDED:
+       case FILE_FLAGS_USER_NODUMP:
+       case FILE_FLAGS_USER_IMMUTABLE:
+       case FILE_FLAGS_USER_APPEND:
+       case FILE_FLAGS_USER_OPAQUE:
+       case FILE_FLAGS_USER_NOUNLINK:
+       case FILE_FLAGS_SYS_ARCHIVED:
+       case FILE_FLAGS_SYS_IMMUTABLE:
+       case FILE_FLAGS_SYS_APPEND:
+       case FILE_FLAGS_SYS_NOUNLINK:
+       case FILE_FLAGS_SYS_SNAPSHOT:
+       case FILE_EXT2_SecureDeletion:
+       case FILE_EXT2_Undelete:
+       case FILE_EXT2_Compress:
+       case FILE_EXT2_Synchronous:
+       case FILE_EXT2_Immutable:
+       case FILE_EXT2_AppendOnly:
+       case FILE_EXT2_NoDump:
+       case FILE_EXT2_NoAtime:
+       case FILE_EXT2_CompDirty:
+       case FILE_EXT2_CompBlock:
+       case FILE_EXT2_NoCompBlock:
+       case FILE_EXT2_CompError:
+       case FILE_EXT2_BTree:
+       case FILE_EXT2_HashIndexed:
+       case FILE_EXT2_iMagic:
+       case FILE_EXT2_Journaled:
+       case FILE_EXT2_NoTail:
+       case FILE_EXT2_DirSync:
+       case FILE_EXT2_TopDir:
+       case FILE_EXT2_Reserved:
+       case UNKNOWN:
+               unknowntag_start(xar, name);
+               break;
+       }
+}
+
+static void
+xml_end(void *userData, const char *name)
+{
+       struct archive_read *a;
+       struct xar *xar;
+
+       a = (struct archive_read *)userData;
+       xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+       fprintf(stderr, "xml_end:[%s]\n", name);
+#endif
+       switch (xar->xmlsts) {
+       case INIT:
+               break;
+       case XAR:
+               if (strcmp(name, "xar") == 0)
+                       xar->xmlsts = INIT;
+               break;
+       case TOC:
+               if (strcmp(name, "toc") == 0)
+                       xar->xmlsts = XAR;
+               break;
+       case TOC_CREATION_TIME:
+               if (strcmp(name, "creation-time") == 0)
+                       xar->xmlsts = TOC;
+               break;
+       case TOC_CHECKSUM:
+               if (strcmp(name, "checksum") == 0)
+                       xar->xmlsts = TOC;
+               break;
+       case TOC_CHECKSUM_OFFSET:
+               if (strcmp(name, "offset") == 0)
+                       xar->xmlsts = TOC_CHECKSUM;
+               break;
+       case TOC_CHECKSUM_SIZE:
+               if (strcmp(name, "size") == 0)
+                       xar->xmlsts = TOC_CHECKSUM;
+               break;
+       case TOC_FILE:
+               if (strcmp(name, "file") == 0) {
+                       if (xar->file->parent != NULL &&
+                           ((xar->file->mode & AE_IFMT) == AE_IFDIR))
+                               xar->file->parent->subdirs++;
+                       xar->file = xar->file->parent;
+                       if (xar->file == NULL)
+                               xar->xmlsts = TOC;
+               }
+               break;
+       case FILE_DATA:
+               if (strcmp(name, "data") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_DATA_LENGTH:
+               if (strcmp(name, "length") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_DATA_OFFSET:
+               if (strcmp(name, "offset") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_DATA_SIZE:
+               if (strcmp(name, "size") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_DATA_ENCODING:
+               if (strcmp(name, "encoding") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_DATA_A_CHECKSUM:
+               if (strcmp(name, "archived-checksum") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_DATA_E_CHECKSUM:
+               if (strcmp(name, "extracted-checksum") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_DATA_CONTENT:
+               if (strcmp(name, "content") == 0)
+                       xar->xmlsts = FILE_DATA;
+               break;
+       case FILE_EA:
+               if (strcmp(name, "ea") == 0) {
+                       xar->xmlsts = TOC_FILE;
+                       xar->xattr = NULL;
+               }
+               break;
+       case FILE_EA_LENGTH:
+               if (strcmp(name, "length") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_OFFSET:
+               if (strcmp(name, "offset") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_SIZE:
+               if (strcmp(name, "size") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_ENCODING:
+               if (strcmp(name, "encoding") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_A_CHECKSUM:
+               if (strcmp(name, "archived-checksum") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_E_CHECKSUM:
+               if (strcmp(name, "extracted-checksum") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_NAME:
+               if (strcmp(name, "name") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_EA_FSTYPE:
+               if (strcmp(name, "fstype") == 0)
+                       xar->xmlsts = FILE_EA;
+               break;
+       case FILE_CTIME:
+               if (strcmp(name, "ctime") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_MTIME:
+               if (strcmp(name, "mtime") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_ATIME:
+               if (strcmp(name, "atime") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_GROUP:
+               if (strcmp(name, "group") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_GID:
+               if (strcmp(name, "gid") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_USER:
+               if (strcmp(name, "user") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_UID:
+               if (strcmp(name, "uid") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_MODE:
+               if (strcmp(name, "mode") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_DEVICE:
+               if (strcmp(name, "device") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_DEVICE_MAJOR:
+               if (strcmp(name, "major") == 0)
+                       xar->xmlsts = FILE_DEVICE;
+               break;
+       case FILE_DEVICE_MINOR:
+               if (strcmp(name, "minor") == 0)
+                       xar->xmlsts = FILE_DEVICE;
+               break;
+       case FILE_DEVICENO:
+               if (strcmp(name, "deviceno") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_INODE:
+               if (strcmp(name, "inode") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_LINK:
+               if (strcmp(name, "link") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_TYPE:
+               if (strcmp(name, "type") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_NAME:
+               if (strcmp(name, "name") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_ACL:
+               if (strcmp(name, "acl") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_ACL_DEFAULT:
+               if (strcmp(name, "default") == 0)
+                       xar->xmlsts = FILE_ACL;
+               break;
+       case FILE_ACL_ACCESS:
+               if (strcmp(name, "access") == 0)
+                       xar->xmlsts = FILE_ACL;
+               break;
+       case FILE_ACL_APPLEEXTENDED:
+               if (strcmp(name, "appleextended") == 0)
+                       xar->xmlsts = FILE_ACL;
+               break;
+       case FILE_FLAGS:
+               if (strcmp(name, "flags") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_FLAGS_USER_NODUMP:
+               if (strcmp(name, "UserNoDump") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_USER_IMMUTABLE:
+               if (strcmp(name, "UserImmutable") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_USER_APPEND:
+               if (strcmp(name, "UserAppend") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_USER_OPAQUE:
+               if (strcmp(name, "UserOpaque") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_USER_NOUNLINK:
+               if (strcmp(name, "UserNoUnlink") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_SYS_ARCHIVED:
+               if (strcmp(name, "SystemArchived") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_SYS_IMMUTABLE:
+               if (strcmp(name, "SystemImmutable") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_SYS_APPEND:
+               if (strcmp(name, "SystemAppend") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_SYS_NOUNLINK:
+               if (strcmp(name, "SystemNoUnlink") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_FLAGS_SYS_SNAPSHOT:
+               if (strcmp(name, "SystemSnapshot") == 0)
+                       xar->xmlsts = FILE_FLAGS;
+               break;
+       case FILE_EXT2:
+               if (strcmp(name, "ext2") == 0)
+                       xar->xmlsts = TOC_FILE;
+               break;
+       case FILE_EXT2_SecureDeletion:
+               if (strcmp(name, "SecureDeletion") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_Undelete:
+               if (strcmp(name, "Undelete") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_Compress:
+               if (strcmp(name, "Compress") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_Synchronous:
+               if (strcmp(name, "Synchronous") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_Immutable:
+               if (strcmp(name, "Immutable") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_AppendOnly:
+               if (strcmp(name, "AppendOnly") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_NoDump:
+               if (strcmp(name, "NoDump") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_NoAtime:
+               if (strcmp(name, "NoAtime") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_CompDirty:
+               if (strcmp(name, "CompDirty") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_CompBlock:
+               if (strcmp(name, "CompBlock") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_NoCompBlock:
+               if (strcmp(name, "NoCompBlock") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_CompError:
+               if (strcmp(name, "CompError") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_BTree:
+               if (strcmp(name, "BTree") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_HashIndexed:
+               if (strcmp(name, "HashIndexed") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_iMagic:
+               if (strcmp(name, "iMagic") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_Journaled:
+               if (strcmp(name, "Journaled") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_NoTail:
+               if (strcmp(name, "NoTail") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_DirSync:
+               if (strcmp(name, "DirSync") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_TopDir:
+               if (strcmp(name, "TopDir") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case FILE_EXT2_Reserved:
+               if (strcmp(name, "Reserved") == 0)
+                       xar->xmlsts = FILE_EXT2;
+               break;
+       case UNKNOWN:
+               unknowntag_end(xar, name);
+               break;
+       }
+}
+
+static const int base64[256] = {
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* 00 - 0F */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* 10 - 1F */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, 62, -1, -1, -1, 63, /* 20 - 2F */
+       52, 53, 54, 55, 56, 57, 58, 59,
+       60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */
+       -1,  0,  1,  2,  3,  4,  5,  6,
+        7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
+       15, 16, 17, 18, 19, 20, 21, 22,
+       23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */
+       -1, 26, 27, 28, 29, 30, 31, 32,
+       33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+       41, 42, 43, 44, 45, 46, 47, 48,
+       49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */
+};
+
+static void
+strappend_base64(struct archive_string *as, const char *s, size_t l)
+{
+       unsigned char buff[256];
+       unsigned char *out;
+       const unsigned char *b;
+       size_t len;
+
+       len = 0;
+       out = buff;
+       b = (const unsigned char *)s;
+       while (l > 0) {
+               int n = 0;
+
+               if (l > 0) {
+                       if (base64[b[0]] < 0 || base64[b[1]] < 0)
+                               break;
+                       n = base64[*b++] << 18;
+                       n |= base64[*b++] << 12;
+                       *out++ = n >> 16;
+                       len++;
+                       l -= 2;
+               }
+               if (l > 0) {
+                       if (base64[*b] < 0)
+                               break;
+                       n |= base64[*b++] << 6;
+                       *out++ = (n >> 8) & 0xFF;
+                       len++;
+                       --l;
+               }
+               if (l > 0) {
+                       if (base64[*b] < 0)
+                               break;
+                       n |= base64[*b++];
+                       *out++ = n & 0xFF;
+                       len++;
+                       --l;
+               }
+               if (len+3 >= sizeof(buff)) {
+                       archive_strncat(as, (const char *)buff, len);
+                       len = 0;
+                       out = buff;
+               }
+       }
+       if (len > 0)
+               archive_strncat(as, (const char *)buff, len);
+}
+
+static void
+xml_data(void *userData, const char *s, int len)
+{
+       struct archive_read *a;
+       struct xar *xar;
+
+       a = (struct archive_read *)userData;
+       xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+       {
+               char buff[1024];
+               if (len > sizeof(buff)-1)
+                       len = sizeof(buff)-1;
+               memcpy(buff, s, len);
+               buff[len] = 0;
+               fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff);
+       }
+#endif
+       switch (xar->xmlsts) {
+       case TOC_CHECKSUM_OFFSET:
+               xar->toc_chksum_offset = atol10(s, len);
+               break;
+       case TOC_CHECKSUM_SIZE:
+               xar->toc_chksum_size = atol10(s, len);
+               break;
+       default:
+               break;
+       }
+       if (xar->file == NULL)
+               return;
+
+       switch (xar->xmlsts) {
+       case FILE_NAME:
+               if (xar->file->parent != NULL) {
+                       archive_string_concat(&(xar->file->pathname),
+                           &(xar->file->parent->pathname));
+                       archive_strappend_char(&(xar->file->pathname), '/');
+               }
+               xar->file->has |= HAS_PATHNAME;
+               if (xar->base64text)
+                       strappend_base64(&(xar->file->pathname), s, len);
+               else
+                       archive_strncat(&(xar->file->pathname), s, len);
+               break;
+       case FILE_LINK:
+               xar->file->has |= HAS_SYMLINK;
+               archive_strncpy(&(xar->file->symlink), s, len);
+               break;
+       case FILE_TYPE:
+               if (strncmp("file", s, len) == 0 ||
+                   strncmp("hardlink", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFREG;
+               if (strncmp("directory", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFDIR;
+               if (strncmp("symlink", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFLNK;
+               if (strncmp("character special", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFCHR;
+               if (strncmp("block special", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFBLK;
+               if (strncmp("socket", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFSOCK;
+               if (strncmp("fifo", s, len) == 0)
+                       xar->file->mode =
+                           (xar->file->mode & ~AE_IFMT) | AE_IFIFO;
+               xar->file->has |= HAS_TYPE;
+               break;
+       case FILE_INODE:
+               xar->file->has |= HAS_INO;
+               xar->file->ino64 = atol10(s, len);
+               break;
+       case FILE_DEVICE_MAJOR:
+               xar->file->has |= HAS_DEVMAJOR;
+               xar->file->devmajor = (dev_t)atol10(s, len);
+               break;
+       case FILE_DEVICE_MINOR:
+               xar->file->has |= HAS_DEVMINOR;
+               xar->file->devminor = (dev_t)atol10(s, len);
+               break;
+       case FILE_DEVICENO:
+               xar->file->has |= HAS_RDEV;
+               xar->file->rdev = (dev_t)atol10(s, len);
+               break;
+       case FILE_MODE:
+               xar->file->has |= HAS_MODE;
+               xar->file->mode =
+                   (xar->file->mode & AE_IFMT) |
+                   (atol8(s, len) & ~AE_IFMT);
+               break;
+       case FILE_GROUP:
+               xar->file->has |= HAS_GID;
+               archive_strncpy(&(xar->file->gname), s, len);
+               break;
+       case FILE_GID:
+               xar->file->has |= HAS_GID;
+               xar->file->gid = atol10(s, len);
+               break;
+       case FILE_USER:
+               xar->file->has |= HAS_UID;
+               archive_strncpy(&(xar->file->uname), s, len);
+               break;
+       case FILE_UID:
+               xar->file->has |= HAS_UID;
+               xar->file->uid = atol10(s, len);
+               break;
+       case FILE_CTIME:
+               xar->file->has |= HAS_TIME;
+               xar->file->ctime = parse_time(s, len);
+               break;
+       case FILE_MTIME:
+               xar->file->has |= HAS_TIME;
+               xar->file->mtime = parse_time(s, len);
+               break;
+       case FILE_ATIME:
+               xar->file->has |= HAS_TIME;
+               xar->file->atime = parse_time(s, len);
+               break;
+       case FILE_DATA_LENGTH:
+               xar->file->has |= HAS_DATA;
+               xar->file->length = atol10(s, len);
+               break;
+       case FILE_DATA_OFFSET:
+               xar->file->has |= HAS_DATA;
+               xar->file->offset = atol10(s, len);
+               break;
+       case FILE_DATA_SIZE:
+               xar->file->has |= HAS_DATA;
+               xar->file->size = atol10(s, len);
+               break;
+       case FILE_DATA_A_CHECKSUM:
+               xar->file->a_sum.len = atohex(xar->file->a_sum.val,
+                   sizeof(xar->file->a_sum.val), s, len);
+               break;
+       case FILE_DATA_E_CHECKSUM:
+               xar->file->e_sum.len = atohex(xar->file->e_sum.val,
+                   sizeof(xar->file->e_sum.val), s, len);
+               break;
+       case FILE_EA_LENGTH:
+               xar->file->has |= HAS_XATTR;
+               xar->xattr->length = atol10(s, len);
+               break;
+       case FILE_EA_OFFSET:
+               xar->file->has |= HAS_XATTR;
+               xar->xattr->offset = atol10(s, len);
+               break;
+       case FILE_EA_SIZE:
+               xar->file->has |= HAS_XATTR;
+               xar->xattr->size = atol10(s, len);
+               break;
+       case FILE_EA_A_CHECKSUM:
+               xar->file->has |= HAS_XATTR;
+               xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val,
+                   sizeof(xar->xattr->a_sum.val), s, len);
+               break;
+       case FILE_EA_E_CHECKSUM:
+               xar->file->has |= HAS_XATTR;
+               xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val,
+                   sizeof(xar->xattr->e_sum.val), s, len);
+               break;
+       case FILE_EA_NAME:
+               xar->file->has |= HAS_XATTR;
+               archive_strncpy(&(xar->xattr->name), s, len);
+               break;
+       case FILE_EA_FSTYPE:
+               xar->file->has |= HAS_XATTR;
+               archive_strncpy(&(xar->xattr->fstype), s, len);
+               break;
+               break;
+       case FILE_ACL_DEFAULT:
+       case FILE_ACL_ACCESS:
+       case FILE_ACL_APPLEEXTENDED:
+               xar->file->has |= HAS_ACL;
+               /* TODO */
+               break;
+       case INIT:
+       case XAR:
+       case TOC:
+       case TOC_CREATION_TIME:
+       case TOC_CHECKSUM:
+       case TOC_CHECKSUM_OFFSET:
+       case TOC_CHECKSUM_SIZE:
+       case TOC_FILE:
+       case FILE_DATA:
+       case FILE_DATA_ENCODING:
+       case FILE_DATA_CONTENT:
+       case FILE_DEVICE:
+       case FILE_EA:
+       case FILE_EA_ENCODING:
+       case FILE_ACL:
+       case FILE_FLAGS:
+       case FILE_FLAGS_USER_NODUMP:
+       case FILE_FLAGS_USER_IMMUTABLE:
+       case FILE_FLAGS_USER_APPEND:
+       case FILE_FLAGS_USER_OPAQUE:
+       case FILE_FLAGS_USER_NOUNLINK:
+       case FILE_FLAGS_SYS_ARCHIVED:
+       case FILE_FLAGS_SYS_IMMUTABLE:
+       case FILE_FLAGS_SYS_APPEND:
+       case FILE_FLAGS_SYS_NOUNLINK:
+       case FILE_FLAGS_SYS_SNAPSHOT:
+       case FILE_EXT2:
+       case FILE_EXT2_SecureDeletion:
+       case FILE_EXT2_Undelete:
+       case FILE_EXT2_Compress:
+       case FILE_EXT2_Synchronous:
+       case FILE_EXT2_Immutable:
+       case FILE_EXT2_AppendOnly:
+       case FILE_EXT2_NoDump:
+       case FILE_EXT2_NoAtime:
+       case FILE_EXT2_CompDirty:
+       case FILE_EXT2_CompBlock:
+       case FILE_EXT2_NoCompBlock:
+       case FILE_EXT2_CompError:
+       case FILE_EXT2_BTree:
+       case FILE_EXT2_HashIndexed:
+       case FILE_EXT2_iMagic:
+       case FILE_EXT2_Journaled:
+       case FILE_EXT2_NoTail:
+       case FILE_EXT2_DirSync:
+       case FILE_EXT2_TopDir:
+       case FILE_EXT2_Reserved:
+       case UNKNOWN:
+               break;
+       }
+}
+
+/*
+ * BSD file flags.
+ */
+static int
+xml_parse_file_flags(struct xar *xar, const char *name)
+{
+       const char *flag = NULL;
+
+       if (strcmp(name, "UserNoDump") == 0) {
+               xar->xmlsts = FILE_FLAGS_USER_NODUMP;
+               flag = "nodump";
+       }
+       else if (strcmp(name, "UserImmutable") == 0) {
+               xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE;
+               flag = "uimmutable";
+       }
+       else if (strcmp(name, "UserAppend") == 0) {
+               xar->xmlsts = FILE_FLAGS_USER_APPEND;
+               flag = "uappend";
+       }
+       else if (strcmp(name, "UserOpaque") == 0) {
+               xar->xmlsts = FILE_FLAGS_USER_OPAQUE;
+               flag = "opaque";
+       }
+       else if (strcmp(name, "UserNoUnlink") == 0) {
+               xar->xmlsts = FILE_FLAGS_USER_NOUNLINK;
+               flag = "nouunlink";
+       }
+       else if (strcmp(name, "SystemArchived") == 0) {
+               xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED;
+               flag = "archived";
+       }
+       else if (strcmp(name, "SystemImmutable") == 0) {
+               xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE;
+               flag = "simmutable";
+       }
+       else if (strcmp(name, "SystemAppend") == 0) {
+               xar->xmlsts = FILE_FLAGS_SYS_APPEND;
+               flag = "sappend";
+       }
+       else if (strcmp(name, "SystemNoUnlink") == 0) {
+               xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK;
+               flag = "nosunlink";
+       }
+       else if (strcmp(name, "SystemSnapshot") == 0) {
+               xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT;
+               flag = "snapshot";
+       }
+
+       if (flag == NULL)
+               return (0);
+       xar->file->has |= HAS_FFLAGS;
+       if (archive_strlen(&(xar->file->fflags_text)) > 0)
+               archive_strappend_char(&(xar->file->fflags_text), ',');
+       archive_strcat(&(xar->file->fflags_text), flag);
+       return (1);
+}
+
+/*
+ * Linux file flags.
+ */
+static int
+xml_parse_file_ext2(struct xar *xar, const char *name)
+{
+       const char *flag = NULL;
+
+       if (strcmp(name, "SecureDeletion") == 0) {
+               xar->xmlsts = FILE_EXT2_SecureDeletion;
+               flag = "securedeletion";
+       }
+       else if (strcmp(name, "Undelete") == 0) {
+               xar->xmlsts = FILE_EXT2_Undelete;
+               flag = "nouunlink";
+       }
+       else if (strcmp(name, "Compress") == 0) {
+               xar->xmlsts = FILE_EXT2_Compress;
+               flag = "compress";
+       }
+       else if (strcmp(name, "Synchronous") == 0) {
+               xar->xmlsts = FILE_EXT2_Synchronous;
+               flag = "sync";
+       }
+       else if (strcmp(name, "Immutable") == 0) {
+               xar->xmlsts = FILE_EXT2_Immutable;
+               flag = "simmutable";
+       }
+       else if (strcmp(name, "AppendOnly") == 0) {
+               xar->xmlsts = FILE_EXT2_AppendOnly;
+               flag = "sappend";
+       }
+       else if (strcmp(name, "NoDump") == 0) {
+               xar->xmlsts = FILE_EXT2_NoDump;
+               flag = "nodump";
+       }
+       else if (strcmp(name, "NoAtime") == 0) {
+               xar->xmlsts = FILE_EXT2_NoAtime;
+               flag = "noatime";
+       }
+       else if (strcmp(name, "CompDirty") == 0) {
+               xar->xmlsts = FILE_EXT2_CompDirty;
+               flag = "compdirty";
+       }
+       else if (strcmp(name, "CompBlock") == 0) {
+               xar->xmlsts = FILE_EXT2_CompBlock;
+               flag = "comprblk";
+       }
+       else if (strcmp(name, "NoCompBlock") == 0) {
+               xar->xmlsts = FILE_EXT2_NoCompBlock;
+               flag = "nocomprblk";
+       }
+       else if (strcmp(name, "CompError") == 0) {
+               xar->xmlsts = FILE_EXT2_CompError;
+               flag = "comperr";
+       }
+       else if (strcmp(name, "BTree") == 0) {
+               xar->xmlsts = FILE_EXT2_BTree;
+               flag = "btree";
+       }
+       else if (strcmp(name, "HashIndexed") == 0) {
+               xar->xmlsts = FILE_EXT2_HashIndexed;
+               flag = "hashidx";
+       }
+       else if (strcmp(name, "iMagic") == 0) {
+               xar->xmlsts = FILE_EXT2_iMagic;
+               flag = "imagic";
+       }
+       else if (strcmp(name, "Journaled") == 0) {
+               xar->xmlsts = FILE_EXT2_Journaled;
+               flag = "journal";
+       }
+       else if (strcmp(name, "NoTail") == 0) {
+               xar->xmlsts = FILE_EXT2_NoTail;
+               flag = "notail";
+       }
+       else if (strcmp(name, "DirSync") == 0) {
+               xar->xmlsts = FILE_EXT2_DirSync;
+               flag = "dirsync";
+       }
+       else if (strcmp(name, "TopDir") == 0) {
+               xar->xmlsts = FILE_EXT2_TopDir;
+               flag = "topdir";
+       }
+       else if (strcmp(name, "Reserved") == 0) {
+               xar->xmlsts = FILE_EXT2_Reserved;
+               flag = "reserved";
+       }
+
+       if (flag == NULL)
+               return (0);
+       if (archive_strlen(&(xar->file->fflags_text)) > 0)
+               archive_strappend_char(&(xar->file->fflags_text), ',');
+       archive_strcat(&(xar->file->fflags_text), flag);
+       return (1);
+}
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+
+static int
+xml2_xmlattr_setup(struct xmlattr_list *list, xmlTextReaderPtr reader)
+{
+       struct xmlattr *attr;
+       int r;
+
+       list->first = NULL;
+       list->last = &(list->first);
+       r = xmlTextReaderMoveToFirstAttribute(reader);
+       while (r == 1) {
+               attr = malloc(sizeof*(attr));
+               if (attr == NULL)
+                       __archive_errx(1, "Out of memory");
+               attr->name = strdup(
+                   (const char *)xmlTextReaderConstLocalName(reader));
+               if (attr->name == NULL)
+                       __archive_errx(1, "Out of memory");
+               attr->value = strdup(
+                   (const char *)xmlTextReaderConstValue(reader));
+               if (attr->value == NULL)
+                       __archive_errx(1, "Out of memory");
+               attr->next = NULL;
+               *list->last = attr;
+               list->last = &(attr->next);
+               r = xmlTextReaderMoveToNextAttribute(reader);
+       }
+       return (r);
+}
+
+static int
+xml2_read_cb(void *context, char *buffer, int len)
+{
+       struct archive_read *a;
+       struct xar *xar;
+       const void *d;
+       size_t outbytes;
+       size_t used;
+       int r;
+
+       a = (struct archive_read *)context;
+       xar = (struct xar *)(a->format->data);
+
+       if (xar->toc_remaining <= 0)
+               return (0);
+       d = buffer;
+       outbytes = len;
+       r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+       if (r != ARCHIVE_OK)
+               return (r);
+       __archive_read_consume(a, used);
+       xar->toc_remaining -= used;
+       xar->offset += used;
+       xar->toc_total += outbytes;
+       PRINT_TOC(buffer, len);
+
+       return ((int)outbytes);
+}
+
+static int
+xml2_close_cb(void *context)
+{
+
+       (void)context; /* UNUSED */
+       return (0);
+}
+
+static void
+xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,
+    xmlTextReaderLocatorPtr locator)
+{
+       struct archive_read *a;
+
+       a = (struct archive_read *)arg;
+       switch (severity) {
+       case XML_PARSER_SEVERITY_VALIDITY_WARNING:
+       case XML_PARSER_SEVERITY_WARNING:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "XML Parsing error: %s", msg);
+               break;
+       case XML_PARSER_SEVERITY_VALIDITY_ERROR:
+       case XML_PARSER_SEVERITY_ERROR:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "XML Parsing error: %s", msg);
+               break;
+       }
+}
+
+static int
+xml2_read_toc(struct archive_read *a)
+{
+       xmlTextReaderPtr reader;
+       struct xmlattr_list list;
+       int r;
+
+       reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0);
+       if (reader == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Couldn't allocate memory for xml parser");
+               return (ARCHIVE_FATAL);
+       }
+       xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a);
+
+       while ((r = xmlTextReaderRead(reader)) == 1) {
+               const char *name, *value;
+               int type, empty;
+
+               type = xmlTextReaderNodeType(reader);
+               name = (const char *)xmlTextReaderConstLocalName(reader);
+               switch (type) {
+               case XML_READER_TYPE_ELEMENT:
+                       empty = xmlTextReaderIsEmptyElement(reader);
+                       r = xml2_xmlattr_setup(&list, reader);
+                       if (r == 0) {
+                               xml_start(a, name, &list);
+                               xmlattr_cleanup(&list);
+                               if (empty)
+                                       xml_end(a, name);
+                       }
+                       break;
+               case XML_READER_TYPE_END_ELEMENT:
+                       xml_end(a, name);
+                       break;
+               case XML_READER_TYPE_TEXT:
+                       value = (const char *)xmlTextReaderConstValue(reader);
+                       xml_data(a, value, strlen(value));
+                       break;
+               case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+               default:
+                       break;
+               }
+               if (r < 0)
+                       break;
+       }
+       xmlFreeTextReader(reader);
+       xmlCleanupParser();
+
+       return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL);
+}
+
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+
+static void
+expat_xmlattr_setup(struct xmlattr_list *list, const XML_Char **atts)
+{
+       struct xmlattr *attr;
+
+       list->first = NULL;
+       list->last = &(list->first);
+       if (atts == NULL)
+               return;
+       while (atts[0] != NULL && atts[1] != NULL) {
+               attr = malloc(sizeof*(attr));
+               if (attr == NULL)
+                       __archive_errx(1, "Out of memory");
+               attr->name = strdup(atts[0]);
+               if (attr->name == NULL)
+                       __archive_errx(1, "Out of memory");
+               attr->value = strdup(atts[1]);
+               if (attr->value == NULL)
+                       __archive_errx(1, "Out of memory");
+               attr->next = NULL;
+               *list->last = attr;
+               list->last = &(attr->next);
+               atts += 2;
+       }
+}
+
+static void
+expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
+{
+       struct xmlattr_list list;
+
+       expat_xmlattr_setup(&list, atts);
+       xml_start(userData, (const char *)name, &list);
+       xmlattr_cleanup(&list);
+}
+
+static void
+expat_end_cb(void *userData, const XML_Char *name)
+{
+       xml_end(userData, (const char *)name);
+}
+
+static void
+expat_data_cb(void *userData, const XML_Char *s, int len)
+{
+       xml_data(userData, s, len);
+}
+
+static int
+expat_read_toc(struct archive_read *a)
+{
+       struct xar *xar;
+       XML_Parser parser;
+
+       xar = (struct xar *)(a->format->data);
+
+       /* Initialize XML Parser library. */
+       parser = XML_ParserCreate(NULL);
+       if (parser == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Couldn't allocate memory for xml parser");
+               return (ARCHIVE_FATAL);
+       }
+       XML_SetUserData(parser, a);
+       XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);
+       XML_SetCharacterDataHandler(parser, expat_data_cb);
+       xar->xmlsts = INIT;
+
+       while (xar->toc_remaining) {
+               enum XML_Status xr;
+               const void *d;
+               size_t outbytes;
+               size_t used;
+               int r;
+
+               d = NULL;
+               r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+               if (r != ARCHIVE_OK)
+                       return (r);
+               __archive_read_consume(a, used);
+               xar->toc_remaining -= used;
+               xar->offset += used;
+               xar->toc_total += outbytes;
+               PRINT_TOC(d, outbytes);
+
+               xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);
+               if (xr == XML_STATUS_ERROR) {
+                       XML_ParserFree(parser);
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "XML Parsing failed");
+                       return (ARCHIVE_FATAL);
+               }
+       }
+       XML_ParserFree(parser);
+       return (ARCHIVE_OK);
+}
+#endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */
+
+#endif /* Support xar format */
index 86afba358311f543196df1557bde7786128f59e5..cea9e7940292456504d58ef3b6502369738337d4 100644 (file)
@@ -79,6 +79,7 @@ IF(ENABLE_TEST)
     test_read_format_tlz.c
     test_read_format_txz.c
     test_read_format_tz.c
+    test_read_format_xar.c
     test_read_format_zip.c
     test_read_large.c
     test_read_pax_truncated.c
diff --git a/libarchive/test/test_read_format_xar.c b/libarchive/test/test_read_format_xar.c
new file mode 100644 (file)
index 0000000..9b1cf96
--- /dev/null
@@ -0,0 +1,696 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+#define UID    1001
+#define UNAME  "cue"
+#define GID    1001
+#define GNAME  "cue"
+
+/* Verify that a file records with hardlink.
+#How to make
+echo "hellohellohello" > f1
+chown $UNAME:$GNAME f1
+chmod 0644 f1
+ln f1 hardlink
+chown $UNAME:$GNAME hardlink
+chmod 0644 hardlink
+env TZ=utc touch -afm -t 197001020000.01 f1 hardlink
+xar -cf archive1.xar f1 hardlink
+od -t x1 archive1.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive1.xar.txt
+*/
+static unsigned char archive1[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xc6,
+0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x70,0x00,0x00,0x00,0x01,0x78,0xda,0xc4,0x54,
+0xc9,0x6e,0xdb,0x30,0x14,0xbc,0xe7,0x2b,0x08,0xdd,0x55,0xae,0xb6,0x45,0x83,0x56,
+0xd0,0x4b,0xd1,0x7b,0xd3,0x4b,0x6f,0x34,0x17,0x89,0x88,0x36,0x48,0x54,0xe0,0xe4,
+0xeb,0x4b,0x52,0x52,0x0c,0xa7,0x71,0x6f,0x45,0x01,0x01,0x1a,0x0e,0x87,0xa3,0xa7,
+0xf7,0x06,0x14,0x8f,0x97,0xb6,0x01,0x2f,0x66,0x9c,0x5c,0xdf,0x9d,0x32,0xfc,0x05,
+0x65,0xc0,0x74,0xaa,0xd7,0xae,0xab,0x4e,0xd9,0xcf,0xa7,0x6f,0x79,0x91,0x3d,0x96,
+0x0f,0xe2,0x22,0xc7,0xf2,0x01,0x08,0xdf,0xab,0xf0,0x02,0x42,0x8d,0x46,0xfa,0x70,
+0x22,0xf7,0xae,0x35,0x25,0x41,0x88,0xe7,0x98,0xe4,0x88,0x3c,0x61,0x7a,0xa4,0xe8,
+0x48,0xb9,0x80,0xb7,0x92,0x74,0xa8,0x36,0xea,0x79,0x9a,0x5b,0x30,0xf9,0xd7,0xc6,
+0x9c,0xb2,0xa9,0x96,0x38,0x8b,0x3b,0x40,0xf4,0xd6,0x4e,0xc6,0x97,0x48,0xc0,0x15,
+0x25,0x76,0x72,0x6f,0xd1,0x5c,0xc0,0x04,0xa2,0x05,0xdc,0x3c,0xd2,0xca,0xba,0xc6,
+0x00,0xa7,0x4f,0x19,0x59,0x6d,0xd4,0x9d,0x72,0xd8,0xaf,0x70,0x72,0xab,0x03,0x88,
+0x36,0x41,0xcc,0x0f,0x28,0x47,0x38,0xca,0x10,0x3a,0xc6,0x07,0x07,0x59,0x7b,0x95,
+0xc9,0x7b,0x3f,0x17,0x64,0xf2,0x2a,0xab,0xc6,0x7e,0x1e,0x4a,0x35,0x1b,0x01,0x17,
+0xb8,0xb0,0x4e,0x97,0x18,0x21,0x1c,0xc8,0x80,0x12,0x35,0x4f,0x66,0x5c,0x74,0x09,
+0x2d,0xdc,0xbb,0x6c,0xde,0x64,0x6d,0xaf,0x4d,0x89,0xf6,0x8c,0x85,0x62,0x22,0x4c,
+0xa4,0x7f,0x1d,0x0c,0x68,0x5c,0xf7,0x1c,0x66,0x94,0x95,0xb5,0x1c,0x75,0x5c,0x08,
+0x18,0xf9,0x45,0xd1,0xc9,0x50,0xd0,0x75,0x23,0x2d,0x53,0xcb,0x62,0x97,0x6e,0xdb,
+0xb5,0x75,0x5d,0x4b,0x2f,0x13,0x02,0xa2,0x31,0x5d,0xe5,0xeb,0x92,0x50,0x01,0x57,
+0xb8,0xf0,0xeb,0x38,0xc8,0xed,0x64,0xd6,0xd1,0xe0,0xfd,0x75,0x34,0x81,0xdb,0x72,
+0xb3,0xcd,0x57,0x0e,0x43,0xe3,0x54,0x0a,0x01,0xbc,0xe4,0xd5,0x9b,0x1b,0x32,0xb8,
+0x4a,0xe5,0xa8,0x6a,0xf7,0x62,0x74,0xfe,0x31,0x13,0x3f,0xbe,0x7f,0x0d,0xd5,0xd9,
+0x82,0x52,0x4d,0xac,0x56,0x98,0x53,0xc6,0xa9,0x3c,0xb3,0x82,0x4b,0x2d,0x09,0xb5,
+0x85,0x3d,0x70,0x6c,0xf7,0xc4,0x2a,0xba,0xe7,0x45,0x98,0xc3,0x47,0xa3,0xad,0x96,
+0x8b,0x1f,0xa5,0xf2,0x77,0xbf,0xb0,0xd3,0x07,0x76,0x56,0x67,0x75,0xe0,0x9a,0x5a,
+0x7e,0xb6,0x4c,0xda,0xe0,0xcd,0x8a,0xa2,0x40,0x86,0xed,0xc8,0x7e,0xc7,0xac,0x41,
+0x8a,0x87,0x1c,0xff,0xe9,0xb4,0x34,0x0f,0xbe,0x77,0xef,0x9f,0xc4,0xee,0x73,0xd9,
+0x7f,0x8c,0x5d,0x3f,0xba,0xca,0x75,0xb2,0xf9,0x4b,0xfa,0x2c,0xfe,0x24,0x77,0x41,
+0x15,0x2f,0x0d,0x01,0xd3,0x15,0xf2,0x1b,0x00,0x00,0xff,0xff,0x03,0x00,0x88,0x32,
+0x49,0x7b,0x67,0xbf,0xc6,0x01,0x29,0xf2,0x1c,0x40,0x05,0x3c,0x49,0x25,0x9f,0xab,
+0x7c,0x8e,0xc5,0xa5,0x79,0xe0,0x78,0xda,0xca,0x48,0xcd,0xc9,0xc9,0xcf,0x80,0x13,
+0x5c,0x00,0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x37,0xf7,0x06,0x47
+};
+
+static void verify0(struct archive *a, struct archive_entry *ae)
+{
+       const void *p;
+       size_t size;
+       off_t offset;
+
+       assert(archive_entry_filetype(ae) == AE_IFREG);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0644);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "f1");
+       assert(archive_entry_hardlink(ae) == NULL);
+       assert(archive_entry_symlink(ae) == NULL);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+       assertEqualInt(archive_entry_size(ae), 16);
+       assertEqualInt(archive_read_data_block(a, &p, &size, &offset), 0);
+       assertEqualInt((int)size, 16);
+       assertEqualInt((int)offset, 0);
+       assertEqualInt(memcmp(p, "hellohellohello\n", 16), 0);
+}
+
+static void verify1(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       /* A hardlink is not a symlink. */
+       assert(archive_entry_filetype(ae) != AE_IFLNK);
+       /* Nor is it a directory. */
+       assert(archive_entry_filetype(ae) != AE_IFDIR);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0644);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "hardlink");
+       assertEqualString(archive_entry_hardlink(ae), "f1");
+       assert(archive_entry_symlink(ae) == NULL);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+}
+
+/* Verify that symlinks are read correctly.
+#How to make
+echo "hellohellohello" > f1
+chown $UNAME:$GNAME f1
+chmod 0644 f1
+ln -s f1 symlink
+chown $UNAME:$GNAME symlink
+chmod 0644 symlink
+env TZ=utc touch -afm -t 197001020000.01 f1 symlink
+xar -cf archive2.xar f1 symlink
+od -t x1 archive2.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive2.xar.txt
+*/
+static unsigned char archive2[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xe8,
+0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x84,0x00,0x00,0x00,0x01,0x78,0xda,0xb4,0x54,
+0xcb,0x6e,0xa3,0x30,0x14,0xdd,0xf7,0x2b,0x90,0xf7,0x8c,0x1f,0x40,0x82,0x23,0xe3,
+0xaa,0x9b,0x6a,0xf6,0xd3,0xd9,0xcc,0xce,0xf1,0x83,0x58,0xe5,0x25,0x70,0xaa,0xa4,
+0x5f,0x3f,0xb6,0x09,0x4d,0xd3,0x30,0xdd,0x8d,0x84,0xc4,0xf5,0xf1,0xb9,0xc7,0x97,
+0x7b,0x0f,0x66,0x8f,0xa7,0xb6,0x49,0xde,0xf4,0x38,0xd9,0xbe,0xab,0x00,0xfe,0x81,
+0x40,0xa2,0x3b,0xd9,0x2b,0xdb,0xd5,0x15,0xf8,0xfd,0xf2,0x9c,0x96,0xe0,0x91,0x3f,
+0xb0,0x93,0x18,0xf9,0x43,0xc2,0x5c,0x2f,0xfd,0x2b,0x61,0x72,0xd4,0xc2,0xf9,0x8c,
+0xd4,0xd9,0x56,0x73,0x82,0x10,0x4d,0x31,0x49,0x11,0x79,0xc1,0xd9,0x2e,0x2b,0x76,
+0xb8,0x60,0xf0,0x96,0x12,0x93,0x0e,0x5a,0xbe,0x4e,0xc7,0x36,0x99,0xdc,0xb9,0xd1,
+0x15,0x98,0x0e,0x02,0x83,0xb0,0x93,0xb0,0xde,0x98,0x49,0x3b,0x8e,0x18,0xbc,0x44,
+0x11,0x9d,0xec,0x7b,0x10,0x67,0x30,0x06,0x41,0x02,0x2e,0x1a,0x71,0x65,0x6c,0xa3,
+0x13,0xab,0x2a,0x40,0x2e,0x32,0xf2,0xae,0x1c,0xb4,0xcb,0xd1,0x0e,0xd1,0x3f,0x3e,
+0x73,0xa9,0x23,0x61,0xed,0x37,0xb4,0xf6,0x4a,0x13,0xdf,0xd0,0xc4,0x95,0x56,0x8f,
+0xfd,0x71,0xe0,0xf2,0xa8,0x19,0x9c,0xc3,0x19,0xb5,0x8a,0x63,0x84,0xb0,0x07,0x7d,
+0x14,0xa1,0xe3,0xa4,0xc7,0x99,0x17,0xa3,0x19,0xfb,0xa0,0x1d,0x17,0x5a,0xdb,0x2b,
+0xcd,0xd1,0xb6,0xf0,0x3d,0x8c,0x61,0x04,0x1b,0xdb,0xbd,0x26,0xee,0x3c,0xf8,0xb6,
+0x85,0xaf,0x06,0xdc,0xf8,0x94,0x00,0xce,0xdb,0x61,0x87,0x4f,0xe7,0x36,0x20,0x0c,
+0xc6,0x55,0xc4,0x3b,0xd1,0x7e,0xc2,0xe3,0x2a,0xb6,0x31,0x68,0xdc,0xb6,0x70,0x99,
+0x84,0x12,0x4e,0xc4,0xc8,0x9f,0xa9,0xbb,0xda,0x1d,0x38,0xc9,0xfc,0x49,0x73,0x38,
+0xe3,0x97,0x11,0x91,0xdb,0x69,0x5d,0xc6,0x85,0x37,0xd7,0x71,0x79,0x6c,0xf1,0xd2,
+0x32,0x73,0x31,0x0c,0x8d,0x95,0xd1,0x18,0xf0,0x94,0xd6,0xef,0x76,0x00,0xf0,0x42,
+0x15,0xa3,0x3c,0xd8,0x37,0xad,0xd2,0xaf,0x3e,0xf9,0xf5,0xf3,0xc9,0x57,0x67,0xca,
+0x2c,0x53,0xc4,0x28,0x89,0x69,0x96,0xd3,0x4c,0xec,0xf3,0x92,0x0a,0x25,0x48,0x66,
+0x4a,0xb3,0xa5,0xd8,0x6c,0x88,0x91,0xd9,0x86,0x96,0x7e,0x36,0x5f,0x85,0x96,0x5a,
+0x4e,0x6e,0x14,0xd2,0xfd,0xf3,0x84,0x42,0x6d,0xf3,0xbd,0xdc,0xcb,0x2d,0x55,0x99,
+0xa1,0x7b,0x93,0x0b,0xe3,0xb5,0xf3,0xb2,0x2c,0x91,0xce,0x0b,0xb2,0x29,0x72,0xa3,
+0x91,0xa4,0x94,0xc1,0x7b,0xa5,0xb9,0x79,0xf0,0xa3,0x7b,0x2b,0x56,0x9c,0xff,0x0c,
+0xb2,0x66,0x45,0x4c,0xb7,0x28,0x45,0x38,0xd0,0x90,0x37,0x98,0x7f,0xf0,0x9a,0x15,
+0xd7,0x69,0xff,0xdd,0x8a,0x9b,0x3c,0xff,0x6c,0xc5,0xe0,0xae,0x24,0x18,0xaa,0x02,
+0xfd,0x68,0x6b,0xdb,0x89,0x06,0xf0,0x83,0x18,0xd5,0xaa,0xf9,0x82,0x4f,0xef,0x7c,
+0xe7,0x59,0xe1,0x22,0x61,0x30,0x5e,0x2b,0x7f,0x01,0x00,0x00,0xff,0xff,0x03,0x00,
+0x2b,0xab,0x4f,0xf9,0xbb,0xf7,0x90,0xb5,0x34,0x8f,0x7c,0xae,0x72,0xa0,0x80,0xd2,
+0x69,0xc7,0xa2,0xe7,0x44,0x53,0xeb,0x75,0x78,0xda,0xca,0x48,0xcd,0xc9,0xc9,0xcf,
+0x80,0x13,0x5c,0x00,0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x37,0xf7,0x06,0x47
+};
+
+static void verify2(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       assertEqualInt(archive_entry_filetype(ae), AE_IFLNK);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "symlink");
+       assertEqualString(archive_entry_symlink(ae), "f1");
+       assert(archive_entry_hardlink(ae) == NULL);
+}
+
+/* Character device node.
+#How to make
+mknod devchar c 0 30
+chown $UNAME:$GNAME devchar
+chmod 0644 devchar
+env TZ=utc touch -afm -t 197001020000.01 devchar
+xar -cf archive3.xar devchar
+od -t x1 archive3.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive3.xar.txt
+*/
+static unsigned char archive3[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x38,
+0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x3b,0x00,0x00,0x00,0x01,0x78,0xda,0x7c,0x92,
+0x4d,0x6e,0xc3,0x20,0x10,0x85,0xf7,0x39,0x05,0xf2,0xde,0x05,0x9c,0x9f,0x36,0xd6,
+0x84,0xec,0x7a,0x82,0x74,0xd3,0x1d,0xc2,0x93,0x98,0xd4,0x36,0x11,0xe0,0x28,0xe9,
+0xe9,0x0b,0xe3,0xa4,0x69,0xa5,0xaa,0x92,0x25,0x1e,0x8f,0xef,0x8d,0x86,0xc1,0xb0,
+0xbd,0xf4,0x1d,0x3b,0xa3,0x0f,0xd6,0x0d,0x9b,0x42,0x3e,0x89,0x82,0xe1,0x60,0x5c,
+0x63,0x87,0xc3,0xa6,0x78,0xdb,0xbd,0x96,0x2f,0xc5,0x56,0xcd,0xe0,0xa2,0xbd,0x9a,
+0x31,0x88,0xce,0xa4,0x85,0x81,0xf1,0xa8,0x63,0x4a,0x94,0xd1,0xf6,0xa8,0x2a,0x21,
+0xd6,0xa5,0xac,0x4a,0x51,0xed,0xa4,0xa8,0xab,0x79,0x2d,0x57,0xc0,0x7f,0x23,0x14,
+0x6a,0xd1,0x7c,0x84,0xb1,0x67,0x21,0x5e,0x3b,0xdc,0x14,0xa1,0xd5,0xb2,0xc8,0x27,
+0x0c,0xdc,0x7e,0x1f,0x30,0x2a,0x01,0xfc,0xa6,0xc8,0x0d,0xf6,0x33,0x17,0x07,0x4e,
+0x22,0x97,0xe0,0xf7,0x1a,0xb4,0xdb,0xdb,0x0e,0x99,0x6d,0x52,0xdb,0xb7,0x32,0xe6,
+0xaf,0x76,0xaa,0x7a,0xb9,0x7c,0x4f,0xc9,0x7b,0x1f,0x0c,0x7a,0x92,0x72,0xfd,0x2c,
+0x4a,0x21,0x33,0x26,0x44,0x9d,0x3f,0x99,0xb0,0xfe,0x81,0xe9,0x7f,0x30,0xfd,0xc0,
+0x0e,0xde,0x8d,0x27,0x65,0x46,0x04,0x3e,0xc9,0xc9,0xb5,0x8d,0x92,0x42,0xc8,0x64,
+0x26,0x45,0xd6,0x18,0xd0,0x4f,0x1c,0xa9,0xc9,0xfb,0xc6,0xc6,0x3b,0xd6,0xbb,0x06,
+0x95,0x58,0x2d,0x16,0xa9,0x99,0x2c,0xc9,0x6c,0xf0,0x6c,0xcd,0xa4,0x13,0x61,0x07,
+0xe7,0xd5,0x3c,0x0d,0x66,0x52,0x37,0x57,0x1f,0x93,0xce,0x26,0x09,0x8a,0xf1,0x1f,
+0x39,0x88,0xd7,0x13,0x2a,0xd3,0x6a,0xaf,0x4d,0x44,0xcf,0xc2,0x09,0x8d,0xd5,0x1d,
+0x70,0xf2,0x89,0x18,0x74,0xba,0x54,0x8a,0x64,0x08,0x38,0xed,0x68,0xea,0x79,0xd0,
+0xf9,0xf9,0x39,0xbd,0x3f,0x70,0xfa,0x1b,0xbe,0x00,0x00,0x00,0xff,0xff,0x03,0x00,
+0xab,0x43,0xa3,0xac,0x76,0x40,0x1e,0x8b,0x95,0x0d,0x28,0x79,0x79,0x43,0x49,0x4e,
+0x16,0xa1,0x56,0x99,0x1f,0x83,0x77,0x41
+};
+
+static void verify3(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       assertEqualInt(archive_entry_filetype(ae), AE_IFCHR);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0644);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "devchar");
+       assert(archive_entry_symlink(ae) == NULL);
+       assert(archive_entry_hardlink(ae) == NULL);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+}
+
+/* Block device node.
+#How to make
+mknod devblock b 0 30
+chown $UNAME:$GNAME devblock
+chmod 0644 devblock
+env TZ=utc touch -afm -t 197001020000.01 devblock
+xar -cf archive4.xar devblock
+od -t x1 archive4.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive4.xar.txt
+*/
+static unsigned char archive4[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x34,
+0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x38,0x00,0x00,0x00,0x01,0x78,0xda,0x7c,0x92,
+0xc1,0x6e,0xc2,0x30,0x0c,0x86,0xef,0x3c,0x45,0xd4,0x7b,0x17,0x07,0xd0,0x0a,0x95,
+0x09,0xb7,0x3d,0x01,0xbb,0xec,0x96,0xa5,0x06,0x32,0xda,0xa6,0x6a,0x5a,0x04,0x7b,
+0xfa,0x25,0x2e,0x8c,0x4d,0x9a,0x26,0x55,0xea,0x97,0x3f,0x9f,0x2d,0x37,0x29,0x6e,
+0x2f,0x4d,0x2d,0xce,0xd4,0x07,0xe7,0xdb,0x4d,0xa6,0x9e,0x20,0x13,0xd4,0x5a,0x5f,
+0xb9,0xf6,0xb0,0xc9,0x5e,0x77,0x2f,0xf9,0x2a,0xdb,0xea,0x19,0x5e,0x4c,0xaf,0x67,
+0x02,0x07,0x6f,0xe3,0x4b,0xa0,0xed,0xc9,0x0c,0xb1,0x22,0x1f,0x5c,0x43,0x7a,0x0e,
+0xb0,0xce,0xd5,0x3c,0x87,0xf9,0x4e,0x41,0xb9,0x58,0x95,0xaa,0x40,0xf9,0x5b,0xe1,
+0xa2,0x23,0xd9,0x53,0x18,0x1b,0x11,0x86,0x6b,0x4d,0x9b,0x2c,0x1c,0x8d,0xca,0xd2,
+0x8e,0x40,0xbf,0xdf,0x07,0x1a,0x34,0xa0,0xbc,0x11,0xa7,0xc1,0x7d,0xa6,0xe6,0x28,
+0x19,0x52,0x0b,0x79,0xef,0xc1,0xab,0xbd,0xab,0x49,0xb8,0x2a,0x8e,0x7d,0x6b,0x63,
+0xff,0x1e,0x07,0x8a,0xb7,0x58,0x79,0x9f,0x43,0x60,0xc3,0xa8,0xd6,0x05,0xe4,0xa0,
+0x92,0x06,0x50,0xa6,0x47,0x45,0xad,0x79,0x68,0xe6,0x1f,0xcd,0x3c,0xb4,0x43,0xef,
+0xc7,0x4e,0xdb,0x91,0x50,0x4e,0x38,0xa5,0xae,0xd2,0x0a,0x40,0xc5,0x30,0x12,0x47,
+0x63,0xa0,0x7e,0xf2,0x98,0xa6,0xec,0x5b,0x1b,0xef,0x5a,0xe3,0x2b,0xd2,0xf0,0xbc,
+0x5c,0xc6,0x61,0x12,0x72,0x58,0xd1,0xd9,0xd9,0x89,0xa3,0xe1,0x5a,0xdf,0xeb,0x45,
+0x3c,0x98,0x89,0x6e,0xa9,0xf9,0x88,0x9c,0x42,0x06,0x2e,0x93,0x3f,0xea,0x70,0xb8,
+0x76,0xa4,0xdf,0x6b,0x6f,0x4f,0x22,0x74,0x64,0x9d,0xa9,0x51,0x72,0xc6,0xbb,0xad,
+0x89,0x1f,0x14,0x75,0x16,0x50,0xf2,0x92,0x8f,0x3c,0x9d,0x72,0xba,0x7b,0xc9,0x97,
+0x8f,0x92,0x7f,0x85,0x2f,0x00,0x00,0x00,0xff,0xff,0x03,0x00,0xbe,0x66,0xa2,0x82,
+0x3a,0x54,0xd3,0x61,0xaa,0x8e,0x30,0x4c,0xc8,0x36,0x3b,0x7a,0xa4,0xb9,0xef,0xfc,
+0x7a,0x5d,0x21,0xde
+};
+
+static void verify4(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       assertEqualInt(archive_entry_filetype(ae), AE_IFBLK);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0644);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "devblock");
+       assert(archive_entry_symlink(ae) == NULL);
+       assert(archive_entry_hardlink(ae) == NULL);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+}
+
+/* Directory.
+#How to make
+mkdir dir1
+chown $UNAME:$GNAME dir1
+chmod 0755 dir1
+env TZ=utc touch -afm -t 197001020000.01 dir1
+xar -cf archive5.xar dir1
+od -t x1 archive5.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive5.xar.txt
+*/
+static unsigned char archive5[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x16,
+0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xec,0x00,0x00,0x00,0x01,0x78,0xda,0x74,0x91,
+0xc1,0x6e,0xc2,0x30,0x0c,0x86,0xef,0x3c,0x45,0xd4,0x7b,0x17,0xa7,0x83,0x31,0xaa,
+0x34,0xdc,0xf6,0x04,0xec,0xb2,0x5b,0x95,0x1a,0x88,0x68,0x1a,0x94,0xa4,0x13,0xdd,
+0xd3,0x2f,0x71,0xe9,0xd0,0xa4,0x4d,0xaa,0xd4,0x3f,0xbf,0x3f,0xff,0xb2,0x6c,0xb9,
+0xbf,0xd9,0x9e,0x7d,0xa2,0x0f,0xc6,0x0d,0x4d,0x21,0x9e,0xa0,0x60,0x38,0x68,0xd7,
+0x99,0xe1,0xd4,0x14,0xef,0x87,0xb7,0xf2,0xb5,0xd8,0xab,0x95,0xbc,0xb5,0x5e,0xad,
+0x98,0x8c,0x4e,0xa7,0x1f,0x93,0xda,0x63,0x1b,0x53,0x47,0x19,0x8d,0x45,0x55,0x01,
+0xec,0x4a,0x51,0x95,0x50,0x1d,0x04,0xd4,0x6b,0x51,0xaf,0x37,0x92,0xff,0x46,0xa8,
+0xe9,0x8c,0xfa,0x12,0x46,0xcb,0x42,0x9c,0x7a,0x6c,0x8a,0x70,0x6e,0x45,0x91,0x2b,
+0x4c,0xba,0xe3,0x31,0x60,0x54,0x20,0xf9,0x5d,0x91,0x1b,0xcc,0x57,0x0e,0x97,0x9c,
+0x44,0x8e,0xe0,0x4b,0x06,0xbd,0x8e,0xa6,0x47,0x66,0xba,0x34,0xf6,0x3d,0x46,0xff,
+0x3d,0xce,0x33,0x7c,0xa4,0xce,0x65,0x0e,0x26,0x2d,0x49,0xb1,0xdb,0x42,0x09,0x22,
+0x63,0x00,0x75,0xfe,0x44,0xc2,0xec,0x03,0x6b,0xff,0x49,0x7b,0x49,0x58,0xfb,0xc0,
+0x4e,0xde,0x8d,0x57,0xa5,0x47,0x94,0x7c,0x96,0xb3,0x6b,0x3a,0x25,0x00,0x44,0x32,
+0x93,0x22,0x6b,0x0c,0xe8,0x67,0x8e,0xd4,0xec,0xfd,0x60,0xe3,0x82,0x59,0xd7,0xa1,
+0x82,0xed,0x26,0xed,0x90,0x24,0x99,0x71,0xba,0xa2,0xea,0x8c,0x47,0x1d,0x9d,0x9f,
+0x24,0xa7,0x37,0x55,0x86,0xd6,0x52,0x25,0x45,0x90,0xa4,0x35,0xe5,0xcd,0xe4,0x7b,
+0x71,0x3a,0x98,0xe4,0x74,0xbe,0x6f,0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x23,0x7a,
+0x8c,0x2f,0x78,0xe9,0x69,0x28,0x93,0x14,0x72,0x68,0x8d,0xeb,0x42,0x7b,0xf6,0x0f,
+0x70,0x64,0xa3,0xff,0xb9,0x35
+};
+
+static void verify5(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+}
+
+/* fifo
+#How to make
+mkfifo -m 0755 fifo
+chown $UNAME:$GNAME fifo
+env TZ=utc touch -afm -t 197001020000.01 fifo
+xar -cf archive6.xar fifo
+od -t x1 archive6.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive6.xar.txt
+*/
+static unsigned char archive6[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0e,
+0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xe7,0x00,0x00,0x00,0x01,0x78,0xda,0x7c,0x91,
+0xc1,0x6e,0xc3,0x20,0x0c,0x86,0xef,0x7d,0x0a,0xc4,0x3d,0xc3,0x64,0xab,0xda,0x46,
+0x94,0xde,0xf6,0x04,0xdd,0x65,0x37,0x44,0x9c,0x16,0x2d,0x84,0x2a,0x90,0xa9,0xdd,
+0xd3,0x0f,0x9c,0x66,0xd5,0xa4,0x69,0x12,0x52,0xbe,0xfc,0x7c,0xb6,0x2c,0xac,0x0e,
+0x57,0xdf,0xb3,0x4f,0x1c,0xa3,0x0b,0xc3,0x9e,0xcb,0x27,0xe0,0x0c,0x07,0x1b,0x5a,
+0x37,0x9c,0xf6,0xfc,0xed,0xf8,0x5a,0x6d,0xf9,0x41,0xaf,0xd4,0xd5,0x8c,0x7a,0xc5,
+0x54,0x0a,0x36,0x7f,0x98,0xb2,0x23,0x9a,0x94,0x2b,0xaa,0xe4,0x3c,0xea,0x1a,0x60,
+0x57,0xc9,0xba,0x82,0xfa,0x28,0x65,0xf3,0x02,0x4d,0xbd,0x55,0xe2,0xb7,0x42,0x45,
+0x67,0xb4,0x1f,0x71,0xf2,0x2c,0xa6,0x5b,0x8f,0x7b,0x1e,0xcf,0x46,0xf2,0x72,0xc3,
+0x54,0xe8,0xba,0x88,0x49,0x83,0x12,0x77,0xa2,0x34,0xba,0xaf,0xd2,0x5c,0x09,0x82,
+0xd2,0x42,0x2c,0x3d,0xe8,0xaf,0x73,0x3d,0x32,0xd7,0xe6,0xb1,0xef,0x6d,0xec,0xdf,
+0xe3,0xc8,0xe7,0xf7,0x5c,0xb9,0xcc,0xc1,0x94,0x27,0x94,0xbb,0x0d,0x54,0x20,0x8b,
+0x06,0xd0,0x94,0x23,0xb3,0xe6,0x1f,0x9a,0xf9,0x47,0x33,0x0f,0xed,0x34,0x86,0xe9,
+0xa2,0xed,0x84,0x4a,0xcc,0x38,0xa7,0xae,0xd5,0x12,0x40,0xe6,0x30,0x13,0x45,0x53,
+0xc4,0x71,0xf6,0x88,0xe6,0xec,0x47,0x9b,0x16,0xcd,0x87,0x16,0x35,0x6c,0xd6,0xeb,
+0x3c,0x4c,0x41,0x0a,0xd3,0xed,0x82,0xba,0x73,0x5d,0x50,0x82,0x90,0xc2,0xc1,0xf8,
+0x25,0x24,0xa4,0x17,0x2a,0x8f,0x52,0x56,0x25,0x68,0x57,0x4a,0xd0,0xe6,0xbe,0x01,
+0x00,0x00,0xff,0xff,0x03,0x00,0x44,0x19,0x8a,0x2a,0x82,0xbc,0x8c,0xae,0x97,0xa7,
+0x7d,0x65,0xa5,0x82,0xdb,0xaa,0xc2,0xcb,0xbe,0xf0,0x1f,0xd1,0xf9,0x56
+};
+
+static void verify6(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       assertEqualInt(archive_entry_filetype(ae), AE_IFIFO);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "fifo");
+       assert(archive_entry_symlink(ae) == NULL);
+       assert(archive_entry_hardlink(ae) == NULL);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+}
+
+/* Verify that a file records with directory name.
+#How to make
+mkdir dir1
+echo "hellohellohello" > dir1/f1
+chown $UNAME:$GNAME dir1/f1
+chmod 0644 dir1/f1
+env TZ=utc touch -afm -t 197001020000.01 dir1/f1
+xar -cf archive7.xar dir1/f1
+od -t x1 archive7.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive7.xar.txt
+*/
+
+static unsigned char archive7[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xbb,
+0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x8a,0x00,0x00,0x00,0x01,0x78,0xda,0x7c,0x53,
+0xc9,0x6e,0xdb,0x30,0x14,0xbc,0xe7,0x2b,0x04,0xdd,0x55,0x2e,0xa2,0x16,0x1a,0xb4,
+0x82,0x5e,0x8a,0xdc,0x93,0x5e,0x7a,0xa3,0xb9,0xd8,0x44,0xb5,0x41,0xa2,0x02,0x3b,
+0x5f,0x5f,0x92,0xa2,0x1c,0xbb,0x59,0x00,0x01,0x1a,0x3e,0xce,0x1b,0x0d,0x9f,0x86,
+0xec,0xf1,0xdc,0xb5,0xc9,0xab,0x9a,0x66,0x33,0xf4,0xfb,0x14,0xfd,0x80,0x69,0xa2,
+0x7a,0x31,0x48,0xd3,0x1f,0xf7,0xe9,0xef,0x97,0x5f,0x59,0x9d,0x3e,0x36,0x0f,0xec,
+0xcc,0xa7,0xe6,0x21,0x61,0x76,0x10,0xee,0x95,0x30,0x31,0x29,0x6e,0x5d,0x47,0x66,
+0x4d,0xa7,0x1a,0x0c,0x21,0xcd,0x10,0xce,0x20,0x7e,0x41,0x68,0x57,0xe0,0x5d,0x51,
+0x31,0x70,0x4f,0x09,0x4d,0x27,0x25,0xfe,0xce,0x4b,0x97,0xcc,0xf6,0xd2,0xaa,0x7d,
+0x3a,0x9f,0x38,0x4a,0xfd,0x4e,0xc2,0x06,0xad,0x67,0x65,0x1b,0xc8,0x40,0x44,0xa1,
+0x3a,0x9b,0x37,0x2f,0xce,0x40,0x00,0x5e,0x02,0x6c,0x1a,0x61,0xa5,0x4d,0xab,0x12,
+0x23,0x9d,0xed,0x28,0x63,0x2f,0xa3,0x6a,0xa4,0x99,0x94,0xb0,0xc3,0x74,0x61,0x20,
+0xac,0xc3,0x4e,0xcf,0xbb,0xb0,0x83,0x18,0x08,0x30,0x14,0xaf,0xfd,0x78,0xed,0x4f,
+0x98,0xe4,0x96,0xaf,0x30,0x61,0xad,0xea,0x8f,0xf6,0xd4,0xe0,0x9c,0x81,0x08,0xe3,
+0x46,0xb4,0x88,0xef,0xdd,0x6e,0x7e,0x51,0xf9,0xee,0xd7,0x17,0xb7,0x69,0x6e,0xa7,
+0xe6,0xe3,0xd8,0x1a,0x11,0x46,0x03,0xce,0xd9,0xf1,0xcd,0x8c,0x29,0xd8,0xb8,0x7c,
+0x12,0x27,0xf3,0xaa,0x64,0xf6,0xff,0xa8,0x9e,0x9f,0x7e,0xba,0x33,0xea,0x3a,0xcf,
+0x25,0xd6,0x52,0x20,0x9a,0x13,0x9a,0xf3,0x03,0xa9,0x29,0x97,0x1c,0xe7,0xba,0xd6,
+0x15,0x45,0xba,0xc4,0x5a,0xe4,0x25,0xad,0x19,0xf8,0x20,0x74,0x75,0x73,0xb6,0x13,
+0x17,0xf6,0xcb,0x4f,0x14,0xb2,0x22,0x07,0x71,0x10,0x15,0x95,0xb9,0xa6,0x07,0x4d,
+0xb8,0x76,0xe2,0xa4,0xae,0x6b,0xa8,0x48,0x81,0xcb,0x82,0x68,0x05,0x05,0xa5,0x0c,
+0x7c,0x54,0x8a,0x33,0x04,0xef,0x43,0x64,0xe2,0xf3,0x7c,0x90,0xfa,0x8f,0xfb,0x95,
+0x5b,0x30,0x1c,0xaf,0x0b,0x18,0xd1,0x0a,0x66,0x10,0x79,0x1e,0x84,0x3b,0xff,0x20,
+0xc7,0xeb,0x6e,0x78,0xfc,0x1b,0x1e,0xbf,0xe1,0x1d,0xa7,0x61,0x19,0x1b,0xb1,0x28,
+0x06,0x56,0x18,0xcb,0x46,0x36,0x08,0x42,0x17,0x02,0x8f,0xd6,0xda,0x32,0xab,0x69,
+0x65,0x06,0x14,0x8b,0x57,0xe2,0x72,0x25,0x76,0x83,0x54,0x0d,0x2c,0x09,0x71,0x96,
+0x3c,0x5c,0xab,0x21,0x62,0x3e,0x48,0x37,0x69,0x8b,0x71,0xd3,0x77,0x61,0x03,0x9e,
+0xb4,0x86,0x38,0x22,0xd7,0xe1,0xaf,0x13,0x03,0xe1,0x72,0xfd,0x03,0x00,0x00,0xff,
+0xff,0x03,0x00,0x8d,0xb1,0x06,0x76,0xa6,0x7a,0xc3,0xbb,0x13,0x3d,0x45,0xe2,0x2b,
+0x3b,0xd0,0x88,0xc7,0x58,0x7b,0xbd,0x30,0x9d,0x01,0x44,0x78,0xda,0xca,0x48,0xcd,
+0xc9,0xc9,0xcf,0x80,0x13,0x5c,0x00,0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x37,0xf7,
+0x06,0x47
+};
+
+static void verify7(struct archive *a, struct archive_entry *ae)
+{
+       (void)a; /* UNUSED */
+       assert(archive_entry_filetype(ae) == AE_IFREG);
+       assertEqualInt(archive_entry_mode(ae) & 0777, 0644);
+       assertEqualInt(archive_entry_uid(ae), UID);
+       assertEqualInt(archive_entry_gid(ae), GID);
+       assertEqualString(archive_entry_uname(ae), UNAME);
+       assertEqualString(archive_entry_gname(ae), GNAME);
+       assertEqualString(archive_entry_pathname(ae), "dir1/f1");
+       assert(archive_entry_hardlink(ae) == NULL);
+       assert(archive_entry_symlink(ae) == NULL);
+       assertEqualInt(archive_entry_mtime(ae), 86401);
+}
+
+/* Verify that a file records with bzip2 compression
+#How to make
+echo "hellohellohello" > f1
+chown $UNAME:$GNAME f1
+chmod 0644 f1
+env TZ=utc touch -afm -t 197001020000.01 f1
+xar --compression bzip2 -cf archive8.xar f1
+od -t x1 archive8.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive8.xar.txt
+*/
+
+static unsigned char archive8[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xb1,
+0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x42,0x00,0x00,0x00,0x01,0x78,0xda,0x7c,0x53,
+0xcb,0x6e,0xdc,0x20,0x14,0xdd,0xe7,0x2b,0x90,0xf7,0x0e,0x60,0xe3,0x07,0x23,0x86,
+0xa8,0x9b,0xa8,0xfb,0x4e,0x37,0xdd,0x61,0x1e,0x63,0x14,0xbf,0x64,0xe3,0x68,0x92,
+0xaf,0x2f,0x60,0x3b,0xa3,0x34,0x6d,0x25,0x4b,0x3e,0x1c,0x0e,0xe7,0x5e,0xee,0xe5,
+0xb2,0xa7,0x5b,0xdf,0x81,0x57,0x3d,0x2f,0x76,0x1c,0xce,0x09,0x7e,0x44,0x09,0xd0,
+0x83,0x1c,0x95,0x1d,0xae,0xe7,0xe4,0xe7,0xe5,0x39,0xad,0x93,0x27,0xfe,0xc0,0x6e,
+0x62,0xe6,0x0f,0x80,0xb9,0x51,0xfa,0x1f,0x60,0x72,0xd6,0xc2,0xf9,0x13,0xa9,0xb3,
+0xbd,0xe6,0x19,0x42,0x34,0xc5,0x59,0x8a,0xc8,0x05,0xd1,0x13,0xc6,0x27,0x9c,0x33,
+0xf8,0x59,0x12,0x0f,0xb5,0x5a,0xbe,0x2c,0x6b,0x0f,0x16,0xf7,0xd6,0xe9,0x73,0xb2,
+0xb4,0x02,0x27,0x61,0x07,0xb0,0xd1,0x98,0x45,0x3b,0x8e,0x18,0xdc,0x51,0x64,0x17,
+0xfb,0x1e,0xcc,0x19,0x8c,0x20,0x58,0xc0,0xc3,0x23,0xae,0x8c,0xed,0x34,0xb0,0xca,
+0xa7,0xbd,0xdb,0x28,0xe1,0x44,0x44,0x80,0x75,0x7a,0xb8,0xba,0x96,0x13,0xc2,0xe0,
+0x0e,0x37,0x7e,0xf7,0xcf,0x3e,0x87,0xda,0x63,0xe1,0xf2,0x1e,0xcb,0x73,0x47,0x21,
+0x8e,0x84,0xc5,0x34,0x75,0x56,0xc6,0x5b,0xc1,0x5b,0xda,0xbc,0xdb,0x29,0x4b,0xe0,
+0xae,0x15,0xb3,0x6c,0xed,0xab,0x56,0xe9,0x9f,0xb7,0xfc,0xf1,0xfd,0x9b,0x4f,0xcf,
+0xe4,0xa4,0x28,0x4a,0x94,0xcb,0x3a,0xcf,0x9b,0x26,0x93,0xaa,0x92,0xba,0x29,0xa8,
+0x2a,0x89,0x29,0xa8,0x50,0x22,0x97,0x45,0xa1,0x71,0xe5,0xeb,0xf6,0xc5,0xe8,0x48,
+0xe6,0xe6,0x66,0x21,0xdd,0x3f,0x23,0x14,0xaa,0x22,0x8d,0x6c,0x64,0x45,0x55,0x6e,
+0x68,0x63,0x88,0x30,0xa6,0x36,0xa4,0xae,0x6b,0xa4,0x49,0x91,0x95,0x05,0x31,0x1a,
+0x49,0x4a,0x19,0xfc,0xea,0xb4,0x55,0x0f,0x7e,0x94,0x8f,0xc9,0xbf,0xf7,0x15,0xd5,
+0xbf,0x7c,0x0b,0x8e,0x86,0x02,0xd6,0x47,0x88,0x69,0x85,0x52,0x84,0x53,0x94,0x5d,
+0x10,0x3a,0x85,0x0f,0x7b,0x59,0x7f,0x97,0x89,0xff,0xc8,0xc4,0x5d,0x76,0x9d,0xc7,
+0x75,0xe2,0x72,0xd5,0x0c,0x6e,0x70,0x63,0xad,0xe2,0x18,0x21,0xec,0x49,0x8f,0x22,
+0xb5,0x2e,0x7a,0xde,0x74,0x11,0x6d,0xdc,0x87,0x6c,0x3d,0x64,0xfd,0xa8,0x34,0x47,
+0x65,0x78,0x02,0x11,0x46,0xd2,0xbd,0x4d,0x1a,0x74,0x76,0x78,0x39,0x27,0xe3,0x6c,
+0xaf,0x76,0x10,0x5d,0xc2,0x5b,0x31,0xab,0xc0,0x31,0x18,0xb6,0x37,0xe1,0x20,0x7c,
+0x5e,0xc6,0xfb,0x45,0x10,0x1f,0x5f,0x78,0x6f,0x61,0x0a,0x60,0x1c,0x03,0x06,0xe3,
+0x50,0xfc,0x06,0x00,0x00,0xff,0xff,0x03,0x00,0x19,0xcf,0xf5,0xc0,0xf9,0x65,0xe8,
+0x78,0xc3,0xfa,0x5f,0x0a,0xf6,0x09,0x17,0xd8,0xb0,0x54,0xb9,0x02,0x8d,0x91,0x31,
+0x9c,0x42,0x5a,0x68,0x39,0x31,0x41,0x59,0x26,0x53,0x59,0xc1,0x52,0x36,0xf7,0x00,
+0x00,0x03,0x41,0x00,0x00,0x10,0x02,0x44,0xa0,0x00,0x21,0xb4,0x01,0x9a,0x0d,0x46,
+0xa5,0x32,0x38,0xbb,0x92,0x29,0xc2,0x84,0x86,0x0a,0x91,0xb7,0xb8
+};
+
+/* Verify that a file records with no compression
+#How to make
+echo "hellohellohello" > f1
+chown $UNAME:$GNAME f1
+chmod 0644 f1
+env TZ=utc touch -afm -t 197001020000.01 f1
+xar --compression none -cf archive9.xar f1
+od -t x1 archive9.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive9.xar.txt
+*/
+
+static unsigned char archive9[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x98,
+0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x47,0x00,0x00,0x00,0x01,0x78,0xda,0xa4,0x53,
+0x4d,0x6f,0xe3,0x20,0x14,0xbc,0xf7,0x57,0x20,0xee,0x5e,0xc0,0x25,0x89,0x1d,0x11,
+0xaa,0x5e,0xaa,0xbd,0x6f,0xf6,0xb2,0x37,0x02,0x8f,0x18,0xc5,0x1f,0x11,0xc6,0x55,
+0xba,0xbf,0x7e,0x01,0xdb,0xad,0xba,0x55,0x7b,0xa9,0x64,0xc9,0xe3,0x61,0xde,0x78,
+0x78,0xf0,0xc4,0xc3,0xad,0x6b,0xd1,0x33,0xf8,0xd1,0x0d,0xfd,0x01,0xb3,0x1f,0x14,
+0x23,0xe8,0xf5,0x60,0x5c,0x7f,0x3e,0xe0,0xdf,0xc7,0xa7,0xa2,0xc2,0x0f,0xf2,0x4e,
+0xdc,0x94,0x97,0x77,0x48,0x84,0x41,0xc7,0x17,0x12,0xda,0x83,0x0a,0xb1,0xa2,0x08,
+0xae,0x03,0x59,0x52,0x5a,0x17,0xac,0x2c,0x28,0x3f,0xd2,0x7a,0xcf,0xaa,0x3d,0xaf,
+0x05,0x79,0x2f,0xc9,0x45,0x0d,0xe8,0xcb,0x38,0x75,0x68,0x0c,0x2f,0x2d,0x1c,0xf0,
+0xd8,0x28,0x86,0xd3,0x0a,0x12,0x83,0xb5,0x23,0x04,0x49,0x05,0x59,0x50,0x66,0x47,
+0xf7,0x37,0x99,0x0b,0x92,0x41,0xb2,0x20,0xab,0x47,0xfe,0xb2,0xae,0x05,0xe4,0x4c,
+0x8c,0xbd,0xd8,0x18,0x15,0x54,0x46,0x48,0xb4,0xd0,0x9f,0x43,0x23,0xd9,0x56,0x90,
+0x05,0xce,0xfc,0xba,0xb9,0x35,0x84,0xba,0x5e,0x5b,0xa7,0x73,0x52,0x32,0xe8,0x00,
+0xa1,0x18,0x43,0x4c,0xde,0x61,0xb2,0x14,0x2c,0x81,0xca,0xf7,0xd9,0x96,0x70,0xc9,
+0x7e,0x0d,0x17,0x39,0xe5,0x75,0xe3,0x9e,0xc1,0x14,0xff,0x6f,0xf5,0xd7,0xcf,0xc7,
+0x98,0x71,0x63,0x76,0xfc,0xa4,0x4f,0x7a,0x57,0x9b,0x7b,0x5b,0x9f,0x2c,0x57,0xd6,
+0x56,0x96,0x57,0x55,0x45,0x81,0x6f,0xca,0xed,0x86,0x5b,0xa0,0xba,0x8e,0xcd,0xfb,
+0x60,0xb4,0xa6,0xbf,0x05,0xaf,0x62,0xca,0xef,0xff,0xe1,0xa3,0xd3,0xdc,0x42,0xf2,
+0xda,0x43,0xa1,0x3f,0x39,0xdc,0xed,0x9f,0x78,0x0e,0xeb,0xa9,0x22,0xd1,0x65,0xc8,
+0xea,0x1d,0x2d,0x28,0x2b,0x68,0x79,0xa4,0x74,0x9f,0x1e,0x16,0x65,0xdd,0x9b,0x4c,
+0x7d,0x21,0x53,0x6f,0xb2,0xb3,0x1f,0xa6,0xab,0xd4,0x13,0x08,0x32,0xc3,0x99,0x75,
+0x46,0x32,0x4a,0x59,0x24,0x23,0xca,0xd4,0x34,0x82,0x9f,0x75,0x19,0xcd,0xdc,0xab,
+0x6c,0x5a,0x65,0xdd,0x60,0x40,0xd2,0x2d,0xe7,0x31,0x4c,0x82,0x99,0x0c,0x2f,0x57,
+0x40,0xad,0xeb,0x2f,0x07,0x3c,0x78,0x77,0x76,0xbd,0x6a,0xb1,0x6c,0x94,0x37,0x89,
+0x13,0x24,0x2d,0xcf,0xc2,0x5e,0xc5,0x5c,0x36,0xfa,0x65,0x90,0x6f,0x60,0xba,0x74,
+0x69,0x14,0x48,0x9e,0x05,0x41,0xf2,0x64,0xfc,0x03,0x00,0x00,0xff,0xff,0x03,0x00,
+0xee,0x8e,0xf8,0x75,0xa1,0xaf,0x74,0x71,0x3f,0x40,0x08,0xab,0x13,0x7d,0xc0,0x82,
+0x3a,0x56,0xeb,0x4e,0x35,0xf1,0x35,0xb7,0x68,0x65,0x6c,0x6c,0x6f,0x68,0x65,0x6c,
+0x6c,0x6f,0x68,0x65,0x6c,0x6c,0x6f,0x0a
+};
+
+/* Verify that a file records with md5 hashing algorithm
+#How to make
+echo "hellohellohello" > f1
+chown $UNAME:$GNAME f1
+chmod 0644 f1
+env TZ=utc touch -afm -t 197001020000.01 f1
+xar --toc-cksum md5 -cf archive10.xar f1
+od -t x1 archive10.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive10.xar.txt
+*/
+
+static unsigned char archive10[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xaf,
+0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x40,0x00,0x00,0x00,0x02,0x78,0xda,0x7c,0x53,
+0x4d,0x6f,0xdc,0x20,0x10,0xbd,0xe7,0x57,0x20,0xee,0x0e,0x60,0xb3,0xb6,0x59,0xb1,
+0x44,0xbd,0x44,0xbd,0x77,0x7b,0xe9,0x8d,0xe5,0xc3,0x8b,0xe2,0x2f,0x61,0x1c,0x6d,
+0xf2,0xeb,0x8b,0xb1,0x9d,0xb4,0x4d,0x52,0xc9,0x92,0x1f,0x8f,0xc7,0x9b,0x61,0x86,
+0xe1,0x0f,0xb7,0xae,0x05,0xcf,0xc6,0x4f,0x6e,0xe8,0x4f,0x90,0xdc,0x63,0x08,0x4c,
+0xaf,0x06,0xed,0xfa,0xe6,0x04,0x7f,0x9e,0x1f,0xb3,0x1a,0x3e,0x88,0x3b,0x7e,0x93,
+0x5e,0xdc,0x01,0x1e,0x06,0x15,0x7f,0x80,0x2b,0x6f,0x64,0x88,0x27,0xb2,0xe0,0x3a,
+0x23,0x72,0x8c,0x59,0x46,0xf2,0x0c,0xd3,0x33,0x66,0xc7,0x02,0x1f,0x69,0xcd,0xd1,
+0xdf,0x92,0x74,0xe8,0x6a,0xd4,0xd3,0x34,0x77,0x60,0x0a,0x2f,0xad,0x39,0xc1,0x4e,
+0x1f,0xe0,0xb2,0x01,0xf8,0x60,0xed,0x64,0x82,0xc0,0x1c,0x6d,0x28,0xb1,0x93,0x7b,
+0x35,0x82,0x94,0x1c,0x25,0xb0,0x38,0xa0,0xdd,0x22,0xad,0xac,0x6b,0x0d,0x70,0x3a,
+0x66,0xbd,0xd9,0x68,0x19,0x64,0x42,0x80,0xb7,0xa6,0x6f,0xc2,0x55,0xe4,0x05,0x47,
+0x1b,0x5c,0xf9,0xcd,0x7f,0x71,0xfd,0x23,0xd4,0x27,0xb1,0x22,0xb7,0xd7,0x61,0xcf,
+0x57,0x8e,0x63,0xeb,0x54,0xba,0x14,0xba,0x65,0xcd,0xab,0x1b,0x21,0xda,0xa4,0xd2,
+0xab,0xab,0x7b,0x36,0x3a,0xfb,0xf7,0x8e,0x3f,0xbe,0x7f,0x8b,0xd9,0xd9,0xba,0x28,
+0x74,0x6e,0xb5,0x22,0xac,0xa0,0xac,0x90,0x17,0x5a,0x33,0xa9,0x65,0x5e,0xd8,0xda,
+0x56,0x8c,0xd8,0x32,0xb7,0xaa,0x28,0x59,0xac,0xda,0x07,0xa3,0x3d,0x97,0x5b,0xf0,
+0x52,0x85,0x2f,0x23,0x1c,0x74,0x45,0x2f,0xea,0xa2,0x2a,0xa6,0x0b,0xcb,0x2e,0x96,
+0x4a,0x1b,0xbd,0x69,0x5d,0xd7,0xd8,0xd0,0x43,0x5e,0x1e,0xa8,0x35,0x58,0x31,0xc6,
+0xd1,0x47,0xa7,0xb5,0x78,0xe8,0xad,0x7a,0x5c,0x7d,0xd1,0xd5,0xea,0x57,0xec,0xc0,
+0xde,0x4e,0xc0,0xbb,0x04,0x09,0xab,0x70,0x86,0x49,0x86,0xf3,0x33,0xc6,0xc7,0xe5,
+0x23,0x51,0xd6,0xbd,0xcb,0xe4,0x7f,0x64,0xf2,0x5d,0xd6,0xf8,0x61,0x1e,0x85,0x9a,
+0x0d,0x47,0x2b,0x5c,0x59,0xa7,0x05,0xc1,0x98,0x44,0x32,0xa2,0x44,0xcd,0x93,0xf1,
+0xab,0x2e,0xa1,0x95,0x7b,0x93,0xcd,0xbb,0xac,0x1b,0xb4,0x11,0xb8,0xa4,0x34,0x26,
+0xb3,0xc0,0x44,0x86,0x97,0xd1,0x80,0xd6,0xf5,0x4f,0x27,0x38,0x78,0xd7,0xb8,0x5e,
+0xb6,0x50,0x5c,0xa5,0xd7,0x0b,0xc7,0xd1,0xb2,0xbd,0x0a,0x7b,0x19,0xf3,0xb2,0xd1,
+0x2f,0x81,0xf4,0xf6,0x96,0xe7,0xb6,0xcc,0x00,0x4a,0x43,0xc0,0x51,0x1a,0x89,0xdf,
+0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x27,0xf8,0xf5,0x28,0x87,0x01,0xb1,0xb7,0x18,
+0xe8,0x34,0x20,0x06,0x5c,0x66,0x9a,0x43,0x26,0xe7,0x94,0x78,0xda,0xca,0x48,0xcd,
+0xc9,0xc9,0xcf,0x80,0x13,0x5c,0x00,0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x37,0xf7,
+0x06,0x47
+};
+
+/* Verify that a file records with no hashing algorithm
+#How to make
+echo "hellohellohello" > f1
+chown $UNAME:$GNAME f1
+chmod 0644 f1
+env TZ=utc touch -afm -t 197001020000.01 f1
+xar --toc-cksum none -cf archive11.xar f1
+od -t x1 archive11.xar | sed -E -e 's/^0[0-9]+//;s/^  //;s/(  )([0-9a-f]{2})/0x\2,/g;$ D' >  archive11.xar.txt
+*/
+
+static unsigned char archive11[] = {
+0x78,0x61,0x72,0x21,0x00,0x1c,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x98,
+0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xef,0x00,0x00,0x00,0x00,0x78,0xda,0x7c,0x52,
+0xcb,0x6e,0xeb,0x20,0x14,0xdc,0xf7,0x2b,0x10,0x7b,0x17,0xb0,0x89,0x63,0x22,0x42,
+0x75,0x37,0x55,0xf7,0xcd,0xdd,0x74,0x47,0x78,0x38,0xa8,0x7e,0xc9,0xc6,0x55,0xda,
+0xaf,0xbf,0x3c,0xe2,0x56,0x55,0xd5,0x2b,0x21,0x79,0x3c,0xcc,0x39,0x67,0x74,0x18,
+0xfe,0x70,0xed,0x3b,0xf0,0x66,0xe6,0xc5,0x8d,0xc3,0x11,0x92,0x7b,0x0c,0x81,0x19,
+0xd4,0xa8,0xdd,0xd0,0x1e,0xe1,0xdf,0xd3,0x63,0xd1,0xc0,0x07,0x71,0xc7,0xaf,0x72,
+0x16,0x77,0x80,0xfb,0x51,0x85,0x0f,0xe0,0x6a,0x36,0xd2,0x87,0x8a,0xc2,0xbb,0xde,
+0x88,0x12,0x63,0x56,0x90,0xb2,0xc0,0xf4,0x44,0xf0,0x81,0x54,0x07,0x5a,0x73,0xf4,
+0x5d,0x12,0x8b,0xac,0xeb,0x0c,0x70,0x3a,0x4c,0x81,0xf1,0x1f,0x70,0x2d,0xbd,0x4c,
+0x08,0xf0,0xce,0x0c,0xad,0xbf,0x88,0xb2,0xe2,0xe8,0x06,0x33,0x3f,0x5a,0xbb,0x18,
+0x2f,0x30,0x47,0x37,0x94,0xe9,0xc5,0x7d,0x18,0x41,0xc2,0x94,0x04,0x32,0xb7,0xd9,
+0x06,0x8b,0x7f,0xef,0xcc,0x11,0xca,0x69,0xea,0x9c,0x4a,0x1e,0xd0,0xb5,0x68,0x3f,
+0xdc,0x04,0xd1,0x4d,0x2a,0x67,0x75,0x71,0x6f,0x46,0x17,0xea,0x62,0xd4,0xeb,0xb2,
+0xf6,0x5b,0xcd,0xf3,0xd3,0x9f,0x60,0xce,0x36,0x55,0xa5,0x4b,0xab,0x15,0x61,0x15,
+0x65,0x95,0x3c,0xd3,0x86,0x49,0x2d,0xcb,0xca,0x36,0x76,0xcf,0x88,0xad,0x4b,0xab,
+0xaa,0x9a,0x35,0x1c,0xfd,0x68,0xb4,0x79,0xb9,0xfa,0x59,0x2a,0xff,0xeb,0x84,0x9d,
+0xde,0xd3,0xb3,0x3a,0xab,0x3d,0xd3,0x95,0x65,0x67,0x4b,0xa5,0x0d,0xbd,0x69,0xd3,
+0x34,0xd8,0xd0,0x5d,0x59,0xef,0xa8,0x35,0x58,0x31,0xc6,0xd1,0xcf,0x4e,0x79,0x77,
+0xe8,0x73,0x79,0x5c,0xfd,0xf2,0x08,0xe4,0x25,0xbc,0xc2,0xb6,0x7d,0xc0,0xfb,0x04,
+0x09,0xdb,0xe3,0x02,0x93,0x02,0x97,0x27,0x8c,0x0f,0xf1,0x44,0x59,0xff,0x25,0x93,
+0xff,0x91,0xc9,0x2f,0x59,0x3b,0x8f,0xeb,0x24,0xd4,0x6a,0x38,0xca,0x30,0xb3,0x4e,
+0x0b,0x82,0x31,0x09,0x64,0x40,0x89,0x5a,0x17,0x33,0x67,0x5d,0x42,0x99,0xfb,0x94,
+0xad,0x9b,0xac,0x1f,0xb5,0x11,0xb8,0xa6,0x34,0x98,0x89,0x30,0x91,0xfe,0x7d,0x32,
+0xa0,0x73,0xc3,0xeb,0x11,0x8e,0xb3,0x6b,0xdd,0x20,0x3b,0x28,0x2e,0x72,0xd6,0x91,
+0xe3,0x28,0x5e,0x67,0xe1,0x20,0x83,0x2f,0x1b,0xfa,0x25,0x10,0xc3,0x86,0x62,0xda,
+0x62,0x64,0x51,0xca,0x2c,0x47,0x29,0xc1,0xff,0x00,0x00,0x00,0xff,0xff,0x03,0x00,
+0xf1,0x18,0xdc,0x71,0x78,0xda,0xca,0x48,0xcd,0xc9,0xc9,0xcf,0x80,0x13,0x5c,0x00,
+0x00,0x00,0x00,0xff,0xff,0x03,0x00,0x37,0xf7,0x06,0x47
+};
+
+enum enc {
+    GZIP,
+    BZIP2
+};
+
+static void verify(unsigned char *d, size_t s,
+    void (*f1)(struct archive *, struct archive_entry *),
+    void (*f2)(struct archive *, struct archive_entry *),
+    enum enc etype)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       unsigned char *buff;
+       int r;
+
+       assert((a = archive_read_new()) != NULL);
+       switch (etype) {
+       case BZIP2:
+               /* This is only check whether bzip is supported or not.
+                * This filter won't be used this test.  */
+               if (ARCHIVE_OK != archive_read_support_compression_bzip2(a)) {
+                       skipping("Unsupported bzip2");
+                       assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+                       return;
+               }
+               break;
+       case GZIP:
+               /* This gzip must be needed. archive_read_support_format_xar()
+                * will return a warning if gzip is unsupported. */
+               break;
+       }
+       assertA(0 == archive_read_support_compression_all(a));
+       r = archive_read_support_format_xar(a);
+       if (r == ARCHIVE_WARN) {
+               skipping("xar reading not fully supported on this platform");
+               assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+               return;
+       }
+       assert((buff = malloc(100000)) != NULL);
+       if (buff == NULL)
+               return;
+       memcpy(buff, d, s);
+       memset(buff + s, 0, 2048);
+
+       assertA(0 == archive_read_support_format_all(a));
+       assertA(0 == archive_read_open_memory(a, buff, s + 1024));
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_XAR);
+       /* Verify the only entry. */
+       f1(a, ae);
+       if (f2) {
+               assertA(0 == archive_read_next_header(a, &ae));
+               assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
+               assertEqualInt(archive_format(a), ARCHIVE_FORMAT_XAR);
+               /* Verify the only entry. */
+               f2(a, ae);
+       }
+       /* End of archive. */
+       assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       assertA(0 == archive_read_close(a));
+       assertA(0 == archive_read_finish(a));
+       free(buff);
+}
+
+DEFINE_TEST(test_read_format_xar)
+{
+       verify(archive1, sizeof(archive1), verify0, verify1, GZIP);
+       verify(archive2, sizeof(archive2), verify0, verify2, GZIP);
+       verify(archive3, sizeof(archive3), verify3, NULL, GZIP);
+       verify(archive4, sizeof(archive4), verify4, NULL, GZIP);
+       verify(archive5, sizeof(archive5), verify5, NULL, GZIP);
+       verify(archive6, sizeof(archive6), verify6, NULL, GZIP);
+       verify(archive7, sizeof(archive7), verify7, NULL, GZIP);
+       verify(archive8, sizeof(archive8), verify0, NULL, BZIP2);
+       verify(archive9, sizeof(archive9), verify0, NULL, GZIP);
+       verify(archive10, sizeof(archive10), verify0, NULL, GZIP);
+       verify(archive11, sizeof(archive11), verify0, NULL, GZIP);
+}
+