[Midnightbsd-cvs] src: lib/libarchive: Update libarchive, sync with some features from

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Fri Mar 27 15:38:24 EDT 2009


Log Message:
-----------
Update libarchive, sync with some features from google code repo 1.6.2 and freebsd RELENG_7 as of today.

Modified Files:
--------------
    src/lib/libarchive:
        Makefile (r1.5 -> r1.6)
        archive_entry.3 (r1.2 -> r1.3)
        archive_entry.c (r1.2 -> r1.3)
        archive_entry.h (r1.2 -> r1.3)
        archive_entry_private.h (r1.1 -> r1.2)
        archive_platform.h (r1.2 -> r1.3)
        archive_private.h (r1.2 -> r1.3)
        archive_read.3 (r1.2 -> r1.3)
        archive_read.c (r1.2 -> r1.3)
        archive_read_data_into_fd.c (r1.2 -> r1.3)
        archive_read_extract.c (r1.2 -> r1.3)
        archive_read_open_filename.c (r1.2 -> r1.3)
        archive_read_private.h (r1.1 -> r1.2)
        archive_read_support_compression_bzip2.c (r1.2 -> r1.3)
        archive_read_support_compression_gzip.c (r1.2 -> r1.3)
        archive_read_support_compression_none.c (r1.3 -> r1.4)
        archive_read_support_compression_program.c (r1.1 -> r1.2)
        archive_read_support_format_all.c (r1.2 -> r1.3)
        archive_read_support_format_ar.c (r1.1 -> r1.2)
        archive_read_support_format_cpio.c (r1.2 -> r1.3)
        archive_read_support_format_iso9660.c (r1.2 -> r1.3)
        archive_read_support_format_tar.c (r1.4 -> r1.5)
        archive_read_support_format_zip.c (r1.3 -> r1.4)
        archive_string.c (r1.2 -> r1.3)
        archive_string.h (r1.2 -> r1.3)
        archive_string_sprintf.c (r1.2 -> r1.3)
        archive_util.3 (r1.2 -> r1.3)
        archive_util.c (r1.2 -> r1.3)
        archive_write.3 (r1.2 -> r1.3)
        archive_write.c (r1.2 -> r1.3)
        archive_write_disk.3 (r1.1 -> r1.2)
        archive_write_disk.c (r1.1 -> r1.2)
        archive_write_open_filename.c (r1.2 -> r1.3)
        archive_write_private.h (r1.1 -> r1.2)
        archive_write_set_compression_bzip2.c (r1.2 -> r1.3)
        archive_write_set_compression_gzip.c (r1.2 -> r1.3)
        archive_write_set_compression_none.c (r1.2 -> r1.3)
        archive_write_set_compression_program.c (r1.1 -> r1.2)
        archive_write_set_format_ar.c (r1.1 -> r1.2)
        archive_write_set_format_cpio.c (r1.3 -> r1.4)
        archive_write_set_format_cpio_newc.c (r1.2 -> r1.3)
        archive_write_set_format_pax.c (r1.2 -> r1.3)
        archive_write_set_format_shar.c (r1.2 -> r1.3)
        archive_write_set_format_ustar.c (r1.2 -> r1.3)
        config_freebsd.h (r1.2 -> r1.3)
        filter_fork.c (r1.1 -> r1.2)
        libarchive-formats.5 (r1.2 -> r1.3)
        libarchive_internals.3 (r1.1 -> r1.2)
        tar.5 (r1.2 -> r1.3)
    src/lib/libarchive/test:
        Makefile (r1.1 -> r1.2)
        README (r1.1 -> r1.2)
        main.c (r1.1 -> r1.2)
        read_open_memory.c (r1.1 -> r1.2)
        test.h (r1.1 -> r1.2)
        test_acl_pax.c (r1.1 -> r1.2)
        test_archive_api_feature.c (r1.1 -> r1.2)
        test_entry.c (r1.1 -> r1.2)
        test_read_extract.c (r1.1 -> r1.2)
        test_read_format_ar.c (r1.1 -> r1.2)
        test_read_format_cpio_odc.c (r1.1 -> r1.2)
        test_read_format_gtar_sparse.c (r1.1 -> r1.2)
        test_read_format_isorr_bz2.c (r1.1 -> r1.2)
        test_read_format_tar.c (r1.1 -> r1.2)
        test_read_format_zip.c (r1.1 -> r1.2)
        test_read_pax_truncated.c (r1.1 -> r1.2)
        test_tar_filenames.c (r1.1 -> r1.2)
        test_write_disk.c (r1.1 -> r1.2)
        test_write_disk_perms.c (r1.1 -> r1.2)
        test_write_disk_secure.c (r1.1 -> r1.2)
        test_write_format_ar.c (r1.1 -> r1.2)
        test_write_format_cpio.c (r1.1 -> r1.2)

Added Files:
-----------
    src/lib/libarchive:
        archive.h (r1.1)
        archive_endian.h (r1.1)
        archive_entry_link_resolver.c (r1.1)
        archive_entry_strmode.c (r1.1)
        archive_read_support_format_mtree.c (r1.1)
        archive_write_set_compression_compress.c (r1.1)
        cpio.5 (r1.1)
    src/lib/libarchive/test:
        test_acl_freebsd.c (r1.1)
        test_compat_gtar.c (r1.1)
        test_compat_gtar_1.tgz.uu (r1.1)
        test_compat_tar_hardlink.c (r1.1)
        test_compat_tar_hardlink_1.tar.uu (r1.1)
        test_compat_zip.c (r1.1)
        test_compat_zip_1.zip.uu (r1.1)
        test_empty_write.c (r1.1)
        test_entry_strmode.c (r1.1)
        test_link_resolver.c (r1.1)
        test_pax_filename_encoding.c (r1.1)
        test_pax_filename_encoding.tar.gz.uu (r1.1)
        test_read_format_gtar_sparse_1_13.tgz.uu (r1.1)
        test_read_format_gtar_sparse_1_17.tgz.uu (r1.1)
        test_read_format_gtar_sparse_1_17_posix00.tgz.uu (r1.1)
        test_read_format_gtar_sparse_1_17_posix01.tgz.uu (r1.1)
        test_read_format_gtar_sparse_1_17_posix10.tgz.uu (r1.1)
        test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu (r1.1)
        test_read_format_isorr_bz2.iso.bz2.uu (r1.1)
        test_read_format_mtree.c (r1.1)
        test_read_format_tar_empty_filename.c (r1.1)
        test_read_format_tar_empty_filename.tar.uu (r1.1)
        test_read_format_zip.zip.uu (r1.1)
        test_tar_large.c (r1.1)
        test_ustar_filenames.c (r1.1)
        test_write_compress.c (r1.1)
        test_write_disk_hardlink.c (r1.1)
        test_write_disk_sparse.c (r1.1)
        test_write_format_cpio_newc.c (r1.1)
        test_write_format_cpio_odc.c (r1.1)
        test_write_format_tar_ustar.c (r1.1)

-------------- next part --------------
--- /dev/null
+++ lib/libarchive/archive_write_set_compression_compress.c
@@ -0,0 +1,494 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * 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.
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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: src/lib/libarchive/archive_write_set_compression_compress.c,v 1.1.2.1 2008/05/21 04:11:11 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#define	HSIZE		69001	/* 95% occupancy */
+#define	HSHIFT		8	/* 8 - trunc(log2(HSIZE / 65536)) */
+#define	CHECK_GAP 10000		/* Ratio check interval. */
+
+#define	MAXCODE(bits)	((1 << (bits)) - 1)
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define	FIRST	257		/* First free entry. */
+#define	CLEAR	256		/* Table clear output code. */
+
+struct private_data {
+	off_t in_count, out_count, checkpoint;
+
+	int code_len;			/* Number of bits/code. */
+	int cur_maxcode;		/* Maximum code, given n_bits. */
+	int max_maxcode;		/* Should NEVER generate this code. */
+	int hashtab [HSIZE];
+	unsigned short codetab [HSIZE];
+	int first_free;		/* First unused entry. */
+	int compress_ratio;
+
+	int cur_code, cur_fcode;
+
+	int bit_offset;
+	unsigned char bit_buf;
+
+	unsigned char	*compressed;
+	size_t		 compressed_buffer_size;
+	size_t		 compressed_offset;
+};
+
+static int	archive_compressor_compress_finish(struct archive_write *);
+static int	archive_compressor_compress_init(struct archive_write *);
+static int	archive_compressor_compress_write(struct archive_write *,
+		    const void *, size_t);
+
+/*
+ * Allocate, initialize and return a archive object.
+ */
+int
+archive_write_set_compression_compress(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_compression_compress");
+	a->compressor.init = &archive_compressor_compress_init;
+	a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
+	a->archive.compression_name = "compress";
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_compress_init(struct archive_write *a)
+{
+	int ret;
+	struct private_data *state;
+
+	a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
+	a->archive.compression_name = "compress";
+
+	if (a->bytes_per_block < 4) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Can't write Compress header as single block");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (a->client_opener != NULL) {
+		ret = (a->client_opener)(&a->archive, a->client_data);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+
+	state = (struct private_data *)malloc(sizeof(*state));
+	if (state == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data for compression");
+		return (ARCHIVE_FATAL);
+	}
+	memset(state, 0, sizeof(*state));
+
+	state->compressed_buffer_size = a->bytes_per_block;
+	state->compressed = malloc(state->compressed_buffer_size);
+
+	if (state->compressed == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data for compression buffer");
+		free(state);
+		return (ARCHIVE_FATAL);
+	}
+
+	a->compressor.write = archive_compressor_compress_write;
+	a->compressor.finish = archive_compressor_compress_finish;
+
+	state->max_maxcode = 0x10000;	/* Should NEVER generate this code. */
+	state->in_count = 0;		/* Length of input. */
+	state->bit_buf = 0;
+	state->bit_offset = 0;
+	state->out_count = 3;		/* Includes 3-byte header mojo. */
+	state->compress_ratio = 0;
+	state->checkpoint = CHECK_GAP;
+	state->code_len = 9;
+	state->cur_maxcode = MAXCODE(state->code_len);
+	state->first_free = FIRST;
+
+	memset(state->hashtab, 0xff, sizeof(state->hashtab));
+
+	/* Prime output buffer with a gzip header. */
+	state->compressed[0] = 0x1f; /* Compress */
+	state->compressed[1] = 0x9d;
+	state->compressed[2] = 0x90; /* Block mode, 16bit max */
+	state->compressed_offset = 3;
+
+	a->compressor.data = state;
+	return (0);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
+ *		that n_bits =< (long)wordsize - 1.
+ * Outputs:
+ * 	Outputs code to the file.
+ * Assumptions:
+ *	Chars are 8 bits long.
+ * Algorithm:
+ * 	Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly).  Use the VAX insv instruction to insert each
+ * code in turn.  When the buffer fills up empty it and start over.
+ */
+
+static unsigned char rmask[9] =
+	{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output_byte(struct archive_write *a, unsigned char c)
+{
+	struct private_data *state = a->compressor.data;
+	ssize_t bytes_written;
+
+	state->compressed[state->compressed_offset++] = c;
+	++state->out_count;
+
+	if (state->compressed_buffer_size == state->compressed_offset) {
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data,
+		    state->compressed, state->compressed_buffer_size);
+		if (bytes_written <= 0)
+			return ARCHIVE_FATAL;
+		a->archive.raw_position += bytes_written;
+		state->compressed_offset = 0;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int
+output_code(struct archive_write *a, int ocode)
+{
+	struct private_data *state = a->compressor.data;
+	int bits, ret, clear_flg, bit_offset;
+
+	clear_flg = ocode == CLEAR;
+	bits = state->code_len;
+
+	/*
+	 * Since ocode is always >= 8 bits, only need to mask the first
+	 * hunk on the left.
+	 */
+	bit_offset = state->bit_offset % 8;
+	state->bit_buf |= (ocode << bit_offset) & 0xff;
+	output_byte(a, state->bit_buf);
+
+	bits = state->code_len - (8 - bit_offset);
+	ocode >>= 8 - bit_offset;
+	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+	if (bits >= 8) {
+		output_byte(a, ocode & 0xff);
+		ocode >>= 8;
+		bits -= 8;
+	}
+	/* Last bits. */
+	state->bit_offset += state->code_len;
+	state->bit_buf = ocode & rmask[bits];
+	if (state->bit_offset == state->code_len * 8)
+		state->bit_offset = 0;
+
+	/*
+	 * If the next entry is going to be too big for the ocode size,
+	 * then increase it, if possible.
+	 */
+	if (clear_flg || state->first_free > state->cur_maxcode) {
+	       /*
+		* Write the whole buffer, because the input side won't
+		* discover the size increase until after it has read it.
+		*/
+		if (state->bit_offset > 0) {
+			while (state->bit_offset < state->code_len * 8) {
+				ret = output_byte(a, state->bit_buf);
+				if (ret != ARCHIVE_OK)
+					return ret;
+				state->bit_offset += 8;
+				state->bit_buf = 0;
+			}
+		}
+		state->bit_buf = 0;
+		state->bit_offset = 0;
+
+		if (clear_flg) {
+			state->code_len = 9;
+			state->cur_maxcode = MAXCODE(state->code_len);
+		} else {
+			state->code_len++;
+			if (state->code_len == 16)
+				state->cur_maxcode = state->max_maxcode;
+			else
+				state->cur_maxcode = MAXCODE(state->code_len);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+output_flush(struct archive_write *a)
+{
+	struct private_data *state = a->compressor.data;
+	int ret;
+
+	/* At EOF, write the rest of the buffer. */
+	if (state->bit_offset % 8) {
+		state->code_len = (state->bit_offset % 8 + 7) / 8;
+		ret = output_byte(a, state->bit_buf);
+		if (ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_compress_write(struct archive_write *a, const void *buff,
+    size_t length)
+{
+	struct private_data *state;
+	int i;
+	int ratio;
+	int c, disp, ret;
+	const unsigned char *bp;
+
+	state = (struct private_data *)a->compressor.data;
+	if (a->client_writer == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "No write callback is registered?  "
+		    "This is probably an internal programming error.");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (length == 0)
+		return ARCHIVE_OK;
+
+	bp = buff;
+
+	if (state->in_count == 0) {
+		state->cur_code = *bp++;
+		++state->in_count;
+		--length;
+	}
+
+	while (length--) {
+		c = *bp++;
+		state->in_count++;
+		state->cur_fcode = (c << 16) + state->cur_code;
+		i = ((c << HSHIFT) ^ state->cur_code);	/* Xor hashing. */
+
+		if (state->hashtab[i] == state->cur_fcode) {
+			state->cur_code = state->codetab[i];
+			continue;
+		}
+		if (state->hashtab[i] < 0)	/* Empty slot. */
+			goto nomatch;
+		/* Secondary hash (after G. Knott). */
+		if (i == 0)
+			disp = 1;
+		else
+			disp = HSIZE - i;
+ probe:		
+		if ((i -= disp) < 0)
+			i += HSIZE;
+
+		if (state->hashtab[i] == state->cur_fcode) {
+			state->cur_code = state->codetab[i];
+			continue;
+		}
+		if (state->hashtab[i] >= 0)
+			goto probe;
+ nomatch:	
+		ret = output_code(a, state->cur_code);
+		if (ret != ARCHIVE_OK)
+			return ret;
+		state->cur_code = c;
+		if (state->first_free < state->max_maxcode) {
+			state->codetab[i] = state->first_free++;	/* code -> hashtable */
+			state->hashtab[i] = state->cur_fcode;
+			continue;
+		}
+		if (state->in_count < state->checkpoint)
+			continue;
+
+		state->checkpoint = state->in_count + CHECK_GAP;
+
+		if (state->in_count <= 0x007fffff)
+			ratio = state->in_count * 256 / state->out_count;
+		else if ((ratio = state->out_count / 256) == 0)
+			ratio = 0x7fffffff;
+		else
+			ratio = state->in_count / ratio;
+
+		if (ratio > state->compress_ratio)
+			state->compress_ratio = ratio;
+		else {
+			state->compress_ratio = 0;
+			memset(state->hashtab, 0xff, sizeof(state->hashtab));
+			state->first_free = FIRST;
+			ret = output_code(a, CLEAR);
+			if (ret != ARCHIVE_OK)
+				return ret;
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_compress_finish(struct archive_write *a)
+{
+	ssize_t block_length, target_block_length, bytes_written;
+	int ret;
+	struct private_data *state;
+	unsigned tocopy;
+
+	state = (struct private_data *)a->compressor.data;
+	ret = 0;
+	if (a->client_writer == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "No write callback is registered?  "
+		    "This is probably an internal programming error.");
+		ret = ARCHIVE_FATAL;
+		goto cleanup;
+	}
+
+	/* By default, always pad the uncompressed data. */
+	if (a->pad_uncompressed) {
+		while (state->in_count % a->bytes_per_block != 0) {
+			tocopy = a->bytes_per_block -
+			    (state->in_count % a->bytes_per_block);
+			if (tocopy > a->null_length)
+				tocopy = a->null_length;
+			ret = archive_compressor_compress_write(a, a->nulls,
+			    tocopy);
+			if (ret != ARCHIVE_OK)
+				goto cleanup;
+		}
+	}
+
+	ret = output_code(a, state->cur_code);
+	if (ret != ARCHIVE_OK)
+		goto cleanup;
+	ret = output_flush(a);
+	if (ret != ARCHIVE_OK)
+		goto cleanup;
+
+	/* Optionally, pad the final compressed block. */
+	block_length = state->compressed_offset;
+
+	/* Tricky calculation to determine size of last block. */
+	if (a->bytes_in_last_block <= 0)
+		/* Default or Zero: pad to full block */
+		target_block_length = a->bytes_per_block;
+	else
+		/* Round length to next multiple of bytes_in_last_block. */
+		target_block_length = a->bytes_in_last_block *
+		    ( (block_length + a->bytes_in_last_block - 1) /
+			a->bytes_in_last_block);
+	if (target_block_length > a->bytes_per_block)
+		target_block_length = a->bytes_per_block;
+	if (block_length < target_block_length) {
+		memset(state->compressed + state->compressed_offset, 0,
+		    target_block_length - block_length);
+		block_length = target_block_length;
+	}
+
+	/* Write the last block */
+	bytes_written = (a->client_writer)(&a->archive, a->client_data,
+	    state->compressed, block_length);
+	if (bytes_written <= 0)
+		ret = ARCHIVE_FATAL;
+	else
+		a->archive.raw_position += bytes_written;
+
+cleanup:
+	free(state->compressed);
+	free(state);
+	return (ret);
+}
--- /dev/null
+++ lib/libarchive/archive_read_support_format_mtree.c
@@ -0,0 +1,1298 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * 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: src/lib/libarchive/archive_read_support_format_mtree.c,v 1.1.2.5 2008/08/10 04:32:47 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stddef.h>
+/* #include <stdint.h> */ /* See archive_platform.h */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define	O_BINARY 0
+#endif
+
+#define	MTREE_HAS_DEVICE	0x0001
+#define	MTREE_HAS_FFLAGS	0x0002
+#define	MTREE_HAS_GID		0x0004
+#define	MTREE_HAS_GNAME		0x0008
+#define	MTREE_HAS_MTIME		0x0010
+#define	MTREE_HAS_NLINK		0x0020
+#define	MTREE_HAS_PERM		0x0040
+#define	MTREE_HAS_SIZE		0x0080
+#define	MTREE_HAS_TYPE		0x0100
+#define	MTREE_HAS_UID		0x0200
+#define	MTREE_HAS_UNAME		0x0400
+
+#define	MTREE_HAS_OPTIONAL	0x0800
+
+struct mtree_option {
+	struct mtree_option *next;
+	char *value;
+};
+
+struct mtree_entry {
+	struct mtree_entry *next;
+	struct mtree_option *options;
+	char *name;
+	char full;
+	char used;
+};
+
+struct mtree {
+	struct archive_string	 line;
+	size_t			 buffsize;
+	char			*buff;
+	off_t			 offset;
+	int			 fd;
+	int			 filetype;
+	int			 archive_format;
+	const char		*archive_format_name;
+	struct mtree_entry	*entries;
+	struct mtree_entry	*this_entry;
+	struct archive_string	 current_dir;
+	struct archive_string	 contents_name;
+
+	struct archive_entry_linkresolver *resolver;
+
+	off_t			 cur_size, cur_offset;
+};
+
+static int	cleanup(struct archive_read *);
+static int	mtree_bid(struct archive_read *);
+static int	parse_file(struct archive_read *, struct archive_entry *,
+		    struct mtree *, struct mtree_entry *, int *);
+static void	parse_escapes(char *, struct mtree_entry *);
+static int	parse_line(struct archive_read *, struct archive_entry *,
+		    struct mtree *, struct mtree_entry *, int *);
+static int	parse_keyword(struct archive_read *, struct mtree *,
+		    struct archive_entry *, struct mtree_option *, int *);
+static int	read_data(struct archive_read *a,
+		    const void **buff, size_t *size, off_t *offset);
+static ssize_t	readline(struct archive_read *, struct mtree *, char **, ssize_t);
+static int	skip(struct archive_read *a);
+static int	read_header(struct archive_read *,
+		    struct archive_entry *);
+static int64_t	mtree_atol10(char **);
+static int64_t	mtree_atol8(char **);
+static int64_t	mtree_atol(char **);
+
+static void
+free_options(struct mtree_option *head)
+{
+	struct mtree_option *next;
+
+	for (; head != NULL; head = next) {
+		next = head->next;
+		free(head->value);
+		free(head);
+	}
+}
+
+int
+archive_read_support_format_mtree(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct mtree *mtree;
+	int r;
+
+	mtree = (struct mtree *)malloc(sizeof(*mtree));
+	if (mtree == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate mtree data");
+		return (ARCHIVE_FATAL);
+	}
+	memset(mtree, 0, sizeof(*mtree));
+	mtree->fd = -1;
+
+	r = __archive_read_register_format(a, mtree,
+	    mtree_bid, read_header, read_data, skip, cleanup);
+
+	if (r != ARCHIVE_OK)
+		free(mtree);
+	return (ARCHIVE_OK);
+}
+
+static int
+cleanup(struct archive_read *a)
+{
+	struct mtree *mtree;
+	struct mtree_entry *p, *q;
+
+	mtree = (struct mtree *)(a->format->data);
+
+	p = mtree->entries;
+	while (p != NULL) {
+		q = p->next;
+		free(p->name);
+		free_options(p->options);
+		free(p);
+		p = q;
+	}
+	archive_string_free(&mtree->line);
+	archive_string_free(&mtree->current_dir);
+	archive_string_free(&mtree->contents_name);
+	archive_entry_linkresolver_free(mtree->resolver);
+
+	free(mtree->buff);
+	free(mtree);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+
+static int
+mtree_bid(struct archive_read *a)
+{
+	struct mtree *mtree;
+	ssize_t bytes_read;
+	const void *h;
+	const char *signature = "#mtree";
+	const char *p;
+	int bid;
+
+	mtree = (struct mtree *)(a->format->data);
+
+	/* Now let's look at the actual header and see if it matches. */
+	bytes_read = (a->decompressor->read_ahead)(a, &h, strlen(signature));
+
+	if (bytes_read <= 0)
+		return (bytes_read);
+
+	p = h;
+	bid = 0;
+	while (bytes_read > 0 && *signature != '\0') {
+		if (*p != *signature)
+			return (bid = 0);
+		bid += 8;
+		p++;
+		signature++;
+		bytes_read--;
+	}
+	return (bid);
+}
+
+/*
+ * The extended mtree format permits multiple lines specifying
+ * attributes for each file.  For those entries, only the last line
+ * is actually used.  Practically speaking, that means we have
+ * to read the entire mtree file into memory up front.
+ *
+ * The parsing is done in two steps.  First, it is decided if a line
+ * changes the global defaults and if it is, processed accordingly.
+ * Otherwise, the options of the line are merged with the current
+ * global options.
+ */
+static int
+add_option(struct archive_read *a, struct mtree_option **global,
+    const char *value, size_t len)
+{
+	struct mtree_option *option;
+
+	if ((option = malloc(sizeof(*option))) == NULL) {
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	if ((option->value = malloc(len + 1)) == NULL) {
+		free(option);
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	memcpy(option->value, value, len);
+	option->value[len] = '\0';
+	option->next = *global;
+	*global = option;
+	return (ARCHIVE_OK);
+}
+
+static void
+remove_option(struct mtree_option **global, const char *value, size_t len)
+{
+	struct mtree_option *iter, *last;
+
+	last = NULL;
+	for (iter = *global; iter != NULL; last = iter, iter = iter->next) {
+		if (strncmp(iter->value, value, len) == 0 &&
+		    (iter->value[len] == '\0' ||
+		     iter->value[len] == '='))
+			break;
+	}
+	if (iter == NULL)
+		return;
+	if (last == NULL)
+		*global = iter->next;
+	else
+		last->next = iter->next;
+
+	free(iter->value);
+	free(iter);
+}
+
+static int
+process_global_set(struct archive_read *a,
+    struct mtree_option **global, const char *line)
+{
+	const char *next, *eq;
+	size_t len;
+	int r;
+
+	line += 4;
+	for (;;) {
+		next = line + strspn(line, " \t\r\n");
+		if (*next == '\0')
+			return (ARCHIVE_OK);
+		line = next;
+		next = line + strcspn(line, " \t\r\n");
+		eq = strchr(line, '=');
+		if (eq > next)
+			len = next - line;
+		else
+			len = eq - line;
+
+		remove_option(global, line, len);
+		r = add_option(a, global, line, next - line);
+		if (r != ARCHIVE_OK)
+			return (r);
+		line = next;
+	}
+}
+
+static int
+process_global_unset(struct archive_read *a,
+    struct mtree_option **global, const char *line)
+{
+	const char *next;
+	size_t len;
+
+	line += 6;
+	if (strchr(line, '=') != NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "/unset shall not contain `='");
+		return ARCHIVE_FATAL;
+	}
+
+	for (;;) {
+		next = line + strspn(line, " \t\r\n");
+		if (*next == '\0')
+			return (ARCHIVE_OK);
+		line = next;
+		len = strcspn(line, " \t\r\n");
+
+		if (len == 3 && strncmp(line, "all", 3) == 0) {
+			free_options(*global);
+			*global = NULL;
+		} else {
+			remove_option(global, line, len);
+		}
+
+		line += len;
+	}
+}
+
+static int
+process_add_entry(struct archive_read *a, struct mtree *mtree,
+    struct mtree_option **global, const char *line,
+    struct mtree_entry **last_entry)
+{
+	struct mtree_entry *entry;
+	struct mtree_option *iter;
+	const char *next, *eq;
+	size_t len;
+	int r;
+
+	if ((entry = malloc(sizeof(*entry))) == NULL) {
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	entry->next = NULL;
+	entry->options = NULL;
+	entry->name = NULL;
+	entry->used = 0;
+	entry->full = 0;
+
+	/* Add this entry to list. */
+	if (*last_entry == NULL)
+		mtree->entries = entry;
+	else
+		(*last_entry)->next = entry;
+	*last_entry = entry;
+
+	len = strcspn(line, " \t\r\n");
+	if ((entry->name = malloc(len + 1)) == NULL) {
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	memcpy(entry->name, line, len);
+	entry->name[len] = '\0';
+	parse_escapes(entry->name, entry);
+
+	line += len;
+	for (iter = *global; iter != NULL; iter = iter->next) {
+		r = add_option(a, &entry->options, iter->value,
+		    strlen(iter->value));
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	for (;;) {
+		next = line + strspn(line, " \t\r\n");
+		if (*next == '\0')
+			return (ARCHIVE_OK);
+		line = next;
+		next = line + strcspn(line, " \t\r\n");
+		eq = strchr(line, '=');
+		if (eq > next)
+			len = next - line;
+		else
+			len = eq - line;
+
+		remove_option(&entry->options, line, len);
+		r = add_option(a, &entry->options, line, next - line);
+		if (r != ARCHIVE_OK)
+			return (r);
+		line = next;
+	}
+}
+
+static int
+read_mtree(struct archive_read *a, struct mtree *mtree)
+{
+	ssize_t len;
+	uintmax_t counter;
+	char *p;
+	struct mtree_option *global;
+	struct mtree_entry *last_entry;
+	int r;
+
+	mtree->archive_format = ARCHIVE_FORMAT_MTREE_V1;
+	mtree->archive_format_name = "mtree";
+
+	global = NULL;
+	last_entry = NULL;
+	r = ARCHIVE_OK;
+
+	for (counter = 1; ; ++counter) {
+		len = readline(a, mtree, &p, 256);
+		if (len == 0) {
+			mtree->this_entry = mtree->entries;
+			return (ARCHIVE_OK);
+		}
+		if (len < 0)
+			return (len);
+		/* Leading whitespace is never significant, ignore it. */
+		while (*p == ' ' || *p == '\t') {
+			++p;
+			--len;
+		}
+		/* Skip content lines and blank lines. */
+		if (*p == '#')
+			continue;
+		if (*p == '\r' || *p == '\n' || *p == '\0')
+			continue;
+		if (*p != '/') {
+			r = process_add_entry(a, mtree, &global, p,
+			    &last_entry);
+		} else if (strncmp(p, "/set", 4) == 0) {
+			if (p[4] != ' ' && p[4] != '\t')
+				break;
+			r = process_global_set(a, &global, p);
+		} else if (strncmp(p, "/unset", 6) == 0) {
+			if (p[6] != ' ' && p[6] != '\t')
+				break;
+			r = process_global_unset(a, &global, p);
+		} else
+			break;
+
+		if (r != ARCHIVE_OK)
+			return r;
+	}
+
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Can't parse line %ju", counter);
+	return ARCHIVE_FATAL;
+}
+
+/*
+ * Read in the entire mtree file into memory on the first request.
+ * Then use the next unused file to satisfy each header request.
+ */
+static int
+read_header(struct archive_read *a, struct archive_entry *entry)
+{
+	struct mtree *mtree;
+	char *p;
+	int r, use_next;
+
+	mtree = (struct mtree *)(a->format->data);
+
+	if (mtree->fd >= 0) {
+		close(mtree->fd);
+		mtree->fd = -1;
+	}
+
+	if (mtree->entries == NULL) {
+		mtree->resolver = archive_entry_linkresolver_new();
+		if (mtree->resolver == NULL)
+			return ARCHIVE_FATAL;
+		archive_entry_linkresolver_set_strategy(mtree->resolver,
+		    ARCHIVE_FORMAT_MTREE);
+		r = read_mtree(a, mtree);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	a->archive.archive_format = mtree->archive_format;
+	a->archive.archive_format_name = mtree->archive_format_name;
+
+	for (;;) {
+		if (mtree->this_entry == NULL)
+			return (ARCHIVE_EOF);
+		if (strcmp(mtree->this_entry->name, "..") == 0) {
+			mtree->this_entry->used = 1;
+			if (archive_strlen(&mtree->current_dir) > 0) {
+				/* Roll back current path. */
+				p = mtree->current_dir.s
+				    + mtree->current_dir.length - 1;
+				while (p >= mtree->current_dir.s && *p != '/')
+					--p;
+				if (p >= mtree->current_dir.s)
+					--p;
+				mtree->current_dir.length
+				    = p - mtree->current_dir.s + 1;
+			}
+		}
+		if (!mtree->this_entry->used) {
+			use_next = 0;
+			r = parse_file(a, entry, mtree, mtree->this_entry, &use_next);
+			if (use_next == 0)
+				return (r);
+		}
+		mtree->this_entry = mtree->this_entry->next;
+	}
+}
+
+/*
+ * A single file can have multiple lines contribute specifications.
+ * Parse as many lines as necessary, then pull additional information
+ * from a backing file on disk as necessary.
+ */
+static int
+parse_file(struct archive_read *a, struct archive_entry *entry,
+    struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
+{
+	const char *path;
+	struct stat st_storage, *st;
+	struct mtree_entry *mp;
+	struct archive_entry *sparse_entry;
+	int r = ARCHIVE_OK, r1, parsed_kws, mismatched_type;
+
+	mentry->used = 1;
+
+	/* Initialize reasonable defaults. */
+	mtree->filetype = AE_IFREG;
+	archive_entry_set_size(entry, 0);
+
+	/* Parse options from this line. */
+	parsed_kws = 0;
+	r = parse_line(a, entry, mtree, mentry, &parsed_kws);
+
+	if (mentry->full) {
+		archive_entry_copy_pathname(entry, mentry->name);
+		/*
+		 * "Full" entries are allowed to have multiple lines
+		 * and those lines aren't required to be adjacent.  We
+		 * don't support multiple lines for "relative" entries
+		 * nor do we make any attempt to merge data from
+		 * separate "relative" and "full" entries.  (Merging
+		 * "relative" and "full" entries would require dealing
+		 * with pathname canonicalization, which is a very
+		 * tricky subject.)
+		 */
+		for (mp = mentry->next; mp != NULL; mp = mp->next) {
+			if (mp->full && !mp->used
+			    && strcmp(mentry->name, mp->name) == 0) {
+				/* Later lines override earlier ones. */
+				mp->used = 1;
+				r1 = parse_line(a, entry, mtree, mp,
+				    &parsed_kws);
+				if (r1 < r)
+					r = r1;
+			}
+		}
+	} else {
+		/*
+		 * Relative entries require us to construct
+		 * the full path and possibly update the
+		 * current directory.
+		 */
+		size_t n = archive_strlen(&mtree->current_dir);
+		if (n > 0)
+			archive_strcat(&mtree->current_dir, "/");
+		archive_strcat(&mtree->current_dir, mentry->name);
+		archive_entry_copy_pathname(entry, mtree->current_dir.s);
+		if (archive_entry_filetype(entry) != AE_IFDIR)
+			mtree->current_dir.length = n;
+	}
+
+	/*
+	 * Try to open and stat the file to get the real size
+	 * and other file info.  It would be nice to avoid
+	 * this here so that getting a listing of an mtree
+	 * wouldn't require opening every referenced contents
+	 * file.  But then we wouldn't know the actual
+	 * contents size, so I don't see a really viable way
+	 * around this.  (Also, we may want to someday pull
+	 * other unspecified info from the contents file on
+	 * disk.)
+	 */
+	mtree->fd = -1;
+	if (archive_strlen(&mtree->contents_name) > 0)
+		path = mtree->contents_name.s;
+	else
+		path = archive_entry_pathname(entry);
+
+	if (archive_entry_filetype(entry) == AE_IFREG ||
+	    archive_entry_filetype(entry) == AE_IFDIR) {
+		mtree->fd = open(path,
+		    O_RDONLY | O_BINARY);
+		if (mtree->fd == -1 &&
+		    (errno != ENOENT ||
+		     archive_strlen(&mtree->contents_name) > 0)) {
+			archive_set_error(&a->archive, errno,
+			    "Can't open %s", path);
+			r = ARCHIVE_WARN;
+		}
+	}
+
+	st = &st_storage;
+	if (mtree->fd >= 0) {
+		if (fstat(mtree->fd, st) == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Could not fstat %s", path);
+			r = ARCHIVE_WARN;
+			/* If we can't stat it, don't keep it open. */
+			close(mtree->fd);
+			mtree->fd = -1;
+			st = NULL;
+		}
+	} else if (lstat(path, st) == -1) {
+		st = NULL;
+	}
+
+	/*
+	 * If there is a contents file on disk, use that size;
+	 * otherwise leave it as-is (it might have been set from
+	 * the mtree size= keyword).
+	 */
+	if (st != NULL) {
+		mismatched_type = 0;
+		if ((st->st_mode & S_IFMT) == S_IFREG &&
+		    archive_entry_filetype(entry) != AE_IFREG)
+			mismatched_type = 1;
+		if ((st->st_mode & S_IFMT) == S_IFLNK &&
+		    archive_entry_filetype(entry) != AE_IFLNK)
+			mismatched_type = 1;
+		if ((st->st_mode & S_IFSOCK) == S_IFSOCK &&
+		    archive_entry_filetype(entry) != AE_IFSOCK)
+			mismatched_type = 1;
+		if ((st->st_mode & S_IFMT) == S_IFCHR &&
+		    archive_entry_filetype(entry) != AE_IFCHR)
+			mismatched_type = 1;
+		if ((st->st_mode & S_IFMT) == S_IFBLK &&
+		    archive_entry_filetype(entry) != AE_IFBLK)
+			mismatched_type = 1;
+		if ((st->st_mode & S_IFMT) == S_IFDIR &&
+		    archive_entry_filetype(entry) != AE_IFDIR)
+			mismatched_type = 1;
+		if ((st->st_mode & S_IFMT) == S_IFIFO &&
+		    archive_entry_filetype(entry) != AE_IFIFO)
+			mismatched_type = 1;
+
+		if (mismatched_type) {
+			if ((parsed_kws & MTREE_HAS_OPTIONAL) == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "mtree specification has different type for %s",
+				    archive_entry_pathname(entry));
+				r = ARCHIVE_WARN;
+			} else {
+				*use_next = 1;
+			}
+			/* Don't hold a non-regular file open. */
+			if (mtree->fd >= 0)
+				close(mtree->fd);
+			mtree->fd = -1;
+			st = NULL;
+			return r;
+		}
+	}
+
+	if (st != NULL) {
+		if ((parsed_kws & MTREE_HAS_DEVICE) == 0 &&
+		    (archive_entry_filetype(entry) == AE_IFCHR ||
+		     archive_entry_filetype(entry) == AE_IFBLK))
+			archive_entry_set_rdev(entry, st->st_rdev);
+		if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0)
+			archive_entry_set_gid(entry, st->st_gid);
+		if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0)
+			archive_entry_set_uid(entry, st->st_uid);
+		if ((parsed_kws & MTREE_HAS_MTIME) == 0) {
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+			archive_entry_set_mtime(entry, st->st_mtime,
+			    st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+			archive_entry_set_mtime(entry, st->st_mtime,
+			    st->st_mtim.tv_nsec);
+#else
+			archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+		}
+		if ((parsed_kws & MTREE_HAS_NLINK) == 0)
+			archive_entry_set_nlink(entry, st->st_nlink);
+		if ((parsed_kws & MTREE_HAS_PERM) == 0)
+			archive_entry_set_perm(entry, st->st_mode);
+		if ((parsed_kws & MTREE_HAS_SIZE) == 0)
+			archive_entry_set_size(entry, st->st_size);
+		archive_entry_set_ino(entry, st->st_ino);
+		archive_entry_set_dev(entry, st->st_dev);
+
+		archive_entry_linkify(mtree->resolver, &entry, &sparse_entry);
+	} else if (parsed_kws & MTREE_HAS_OPTIONAL) {
+		/*
+		 * Couldn't open the entry, stat it or the on-disk type
+		 * didn't match.  If this entry is optional, just ignore it
+		 * and read the next header entry.
+		 */
+		*use_next = 1;
+		return ARCHIVE_OK;
+	}
+
+	mtree->cur_size = archive_entry_size(entry);
+	mtree->offset = 0;
+
+	return r;
+}
+
+/*
+ * Each line contains a sequence of keywords.
+ */
+static int
+parse_line(struct archive_read *a, struct archive_entry *entry,
+    struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)
+{
+	struct mtree_option *iter;
+	int r = ARCHIVE_OK, r1;
+
+	for (iter = mp->options; iter != NULL; iter = iter->next) {
+		r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);
+		if (r1 < r)
+			r = r1;
+	}
+	if ((*parsed_kws & MTREE_HAS_TYPE) == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Missing type keyword in mtree specification");
+		return (ARCHIVE_WARN);
+	}
+	return (r);
+}
+
+/*
+ * Device entries have one of the following forms:
+ * raw dev_t
+ * format,major,minor[,subdevice]
+ *
+ * Just use major and minor, no translation etc is done
+ * between formats.
+ */
+static int
+parse_device(struct archive *a, struct archive_entry *entry, char *val)
+{
+	char *comma1, *comma2;
+
+	comma1 = strchr(val, ',');
+	if (comma1 == NULL) {
+		archive_entry_set_dev(entry, mtree_atol10(&val));
+		return (ARCHIVE_OK);
+	}
+	++comma1;
+	comma2 = strchr(comma1, ',');
+	if (comma2 == NULL) {
+		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Malformed device attribute");
+		return (ARCHIVE_WARN);
+	}
+	++comma2;
+	archive_entry_set_rdevmajor(entry, mtree_atol(&comma1));
+	archive_entry_set_rdevminor(entry, mtree_atol(&comma2));
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Parse a single keyword and its value.
+ */
+static int
+parse_keyword(struct archive_read *a, struct mtree *mtree,
+    struct archive_entry *entry, struct mtree_option *option, int *parsed_kws)
+{
+	char *val, *key;
+
+	key = option->value;
+
+	if (*key == '\0')
+		return (ARCHIVE_OK);
+
+	if (strcmp(key, "optional") == 0) {
+		*parsed_kws |= MTREE_HAS_OPTIONAL;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "ignore") == 0) {
+		/*
+		 * The mtree processing is not recursive, so
+		 * recursion will only happen for explicitly listed
+		 * entries.
+		 */
+		return (ARCHIVE_OK);
+	}
+
+	val = strchr(key, '=');
+	if (val == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Malformed attribute \"%s\" (%d)", key, key[0]);
+		return (ARCHIVE_WARN);
+	}
+
+	*val = '\0';
+	++val;
+
+	switch (key[0]) {
+	case 'c':
+		if (strcmp(key, "content") == 0
+		    || strcmp(key, "contents") == 0) {
+			parse_escapes(val, NULL);
+			archive_strcpy(&mtree->contents_name, val);
+			break;
+		}
+		if (strcmp(key, "cksum") == 0)
+			break;
+	case 'd':
+		if (strcmp(key, "device") == 0) {
+			*parsed_kws |= MTREE_HAS_DEVICE;
+			return parse_device(&a->archive, entry, val);
+		}
+	case 'f':
+		if (strcmp(key, "flags") == 0) {
+			*parsed_kws |= MTREE_HAS_FFLAGS;
+			archive_entry_copy_fflags_text(entry, val);
+			break;
+		}
+	case 'g':
+		if (strcmp(key, "gid") == 0) {
+			*parsed_kws |= MTREE_HAS_GID;
+			archive_entry_set_gid(entry, mtree_atol10(&val));
+			break;
+		}
+		if (strcmp(key, "gname") == 0) {
+			*parsed_kws |= MTREE_HAS_GNAME;
+			archive_entry_copy_gname(entry, val);
+			break;
+		}
+	case 'l':
+		if (strcmp(key, "link") == 0) {
+			archive_entry_copy_symlink(entry, val);
+			break;
+		}
+	case 'm':
+		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)
+			break;
+		if (strcmp(key, "mode") == 0) {
+			if (val[0] >= '0' && val[0] <= '9') {
+				*parsed_kws |= MTREE_HAS_PERM;
+				archive_entry_set_perm(entry,
+				    mtree_atol8(&val));
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Symbolic mode \"%s\" unsupported", val);
+				return ARCHIVE_WARN;
+			}
+			break;
+		}
+	case 'n':
+		if (strcmp(key, "nlink") == 0) {
+			*parsed_kws |= MTREE_HAS_NLINK;
+			archive_entry_set_nlink(entry, mtree_atol10(&val));
+			break;
+		}
+	case 'r':
+		if (strcmp(key, "rmd160") == 0 ||
+		    strcmp(key, "rmd160digest") == 0)
+			break;
+	case 's':
+		if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0)
+			break;
+		if (strcmp(key, "sha256") == 0 ||
+		    strcmp(key, "sha256digest") == 0)
+			break;
+		if (strcmp(key, "sha384") == 0 ||
+		    strcmp(key, "sha384digest") == 0)
+			break;
+		if (strcmp(key, "sha512") == 0 ||
+		    strcmp(key, "sha512digest") == 0)
+			break;
+		if (strcmp(key, "size") == 0) {
+			archive_entry_set_size(entry, mtree_atol10(&val));
+			break;
+		}
+	case 't':
+		if (strcmp(key, "tags") == 0) {
+			/*
+			 * Comma delimited list of tags.
+			 * Ignore the tags for now, but the interface
+			 * should be extended to allow inclusion/exclusion.
+			 */
+			break;
+		}
+		if (strcmp(key, "time") == 0) {
+			*parsed_kws |= MTREE_HAS_MTIME;
+			archive_entry_set_mtime(entry, mtree_atol10(&val), 0);
+			break;
+		}
+		if (strcmp(key, "type") == 0) {
+			*parsed_kws |= MTREE_HAS_TYPE;
+			switch (val[0]) {
+			case 'b':
+				if (strcmp(val, "block") == 0) {
+					mtree->filetype = AE_IFBLK;
+					break;
+				}
+			case 'c':
+				if (strcmp(val, "char") == 0) {
+					mtree->filetype = AE_IFCHR;
+					break;
+				}
+			case 'd':
+				if (strcmp(val, "dir") == 0) {
+					mtree->filetype = AE_IFDIR;
+					break;
+				}
+			case 'f':
+				if (strcmp(val, "fifo") == 0) {
+					mtree->filetype = AE_IFIFO;
+					break;
+				}
+				if (strcmp(val, "file") == 0) {
+					mtree->filetype = AE_IFREG;
+					break;
+				}
+			case 'l':
+				if (strcmp(val, "link") == 0) {
+					mtree->filetype = AE_IFLNK;
+					break;
+				}
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Unrecognized file type \"%s\"", val);
+				return (ARCHIVE_WARN);
+			}
+			archive_entry_set_filetype(entry, mtree->filetype);
+			break;
+		}
+	case 'u':
+		if (strcmp(key, "uid") == 0) {
+			*parsed_kws |= MTREE_HAS_UID;
+			archive_entry_set_uid(entry, mtree_atol10(&val));
+			break;
+		}
+		if (strcmp(key, "uname") == 0) {
+			*parsed_kws |= MTREE_HAS_UNAME;
+			archive_entry_copy_uname(entry, val);
+			break;
+		}
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unrecognized key %s=%s", key, val);
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset)
+{
+	size_t bytes_to_read;
+	ssize_t bytes_read;
+	struct mtree *mtree;
+
+	mtree = (struct mtree *)(a->format->data);
+	if (mtree->fd < 0) {
+		*buff = NULL;
+		*offset = 0;
+		*size = 0;
+		return (ARCHIVE_EOF);
+	}
+	if (mtree->buff == NULL) {
+		mtree->buffsize = 64 * 1024;
+		mtree->buff = malloc(mtree->buffsize);
+		if (mtree->buff == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+		}
+		return (ARCHIVE_FATAL);
+	}
+
+	*buff = mtree->buff;
+	*offset = mtree->offset;
+	if ((off_t)mtree->buffsize > mtree->cur_size - mtree->offset)
+		bytes_to_read = mtree->cur_size - mtree->offset;
+	else
+		bytes_to_read = mtree->buffsize;
+	bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);
+	if (bytes_read < 0) {
+		archive_set_error(&a->archive, errno, "Can't read");
+		return (ARCHIVE_WARN);
+	}
+	if (bytes_read == 0) {
+		*size = 0;
+		return (ARCHIVE_EOF);
+	}
+	mtree->offset += bytes_read;
+	*size = bytes_read;
+	return (ARCHIVE_OK);
+}
+
+/* Skip does nothing except possibly close the contents file. */
+static int
+skip(struct archive_read *a)
+{
+	struct mtree *mtree;
+
+	mtree = (struct mtree *)(a->format->data);
+	if (mtree->fd >= 0) {
+		close(mtree->fd);
+		mtree->fd = -1;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Since parsing backslash sequences always makes strings shorter,
+ * we can always do this conversion in-place.
+ */
+static void
+parse_escapes(char *src, struct mtree_entry *mentry)
+{
+	char *dest = src;
+	char c;
+
+	/*
+	 * The current directory is somewhat special, it should be archived
+	 * only once as it will confuse extraction otherwise.
+	 */
+	if (strcmp(src, ".") == 0)
+		mentry->full = 1;
+
+	while (*src != '\0') {
+		c = *src++;
+		if (c == '/' && mentry != NULL)
+			mentry->full = 1;
+		if (c == '\\') {
+			switch (src[0]) {
+			case '0':
+				if (src[1] < '0' || src[1] > '7') {
+					c = 0;
+					++src;
+					break;
+				}
+				/* FALLTHROUGH */
+			case '1':
+			case '2':
+			case '3':
+				if (src[1] >= '0' && src[1] <= '7' &&
+				    src[2] >= '0' && src[2] <= '7') {
+					c = (src[0] - '0') << 6;
+					c |= (src[1] - '0') << 3;
+					c |= (src[2] - '0');
+					src += 3;
+				}
+				break;
+			case 'a':
+				c = '\a';
+				++src;
+				break;
+			case 'b':
+				c = '\b';
+				++src;
+				break;
+			case 'f':
+				c = '\f';
+				++src;
+				break;
+			case 'n':
+				c = '\n';
+				++src;
+				break;
+			case 'r':
+				c = '\r';
+				++src;
+				break;
+			case 's':
+				c = ' ';
+				++src;
+				break;
+			case 't':
+				c = '\t';
+				++src;
+				break;
+			case 'v':
+				c = '\v';
+				++src;
+				break;
+			}
+		}
+		*dest++ = c;
+	}
+	*dest = '\0';
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+mtree_atol8(char **p)
+{
+	int64_t	l, limit, last_digit_limit;
+	int digit, base;
+
+	base = 8;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
+
+	l = 0;
+	digit = **p - '0';
+	while (digit >= 0 && digit < base) {
+		if (l>limit || (l == limit && digit > last_digit_limit)) {
+			l = INT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		digit = *++(*p) - '0';
+	}
+	return (l);
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+mtree_atol10(char **p)
+{
+	int64_t l, limit, last_digit_limit;
+	int base, digit, sign;
+
+	base = 10;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
+
+	if (**p == '-') {
+		sign = -1;
+		++(*p);
+	} else
+		sign = 1;
+
+	l = 0;
+	digit = **p - '0';
+	while (digit >= 0 && digit < base) {
+		if (l > limit || (l == limit && digit > last_digit_limit)) {
+			l = UINT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		digit = *++(*p) - '0';
+	}
+	return (sign < 0) ? -l : l;
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+mtree_atol16(char **p)
+{
+	int64_t l, limit, last_digit_limit;
+	int base, digit, sign;
+
+	base = 16;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
+
+	if (**p == '-') {
+		sign = -1;
+		++(*p);
+	} else
+		sign = 1;
+
+	l = 0;
+	if (**p >= '0' && **p <= '9')
+		digit = **p - '0';
+	else if (**p >= 'a' && **p <= 'f')
+		digit = **p - 'a' + 10;
+	else if (**p >= 'A' && **p <= 'F')
+		digit = **p - 'A' + 10;
+	else
+		digit = -1;
+	while (digit >= 0 && digit < base) {
+		if (l > limit || (l == limit && digit > last_digit_limit)) {
+			l = UINT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		if (**p >= '0' && **p <= '9')
+			digit = **p - '0';
+		else if (**p >= 'a' && **p <= 'f')
+			digit = **p - 'a' + 10;
+		else if (**p >= 'A' && **p <= 'F')
+			digit = **p - 'A' + 10;
+		else
+			digit = -1;
+	}
+	return (sign < 0) ? -l : l;
+}
+
+static int64_t
+mtree_atol(char **p)
+{
+	if (**p != '0')
+		return mtree_atol10(p);
+	if ((*p)[1] == 'x' || (*p)[1] == 'X') {
+		*p += 2;
+		return mtree_atol16(p);
+	}
+	return mtree_atol8(p);
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error.  'start' argument is updated to
+ * point to first character of line.
+ */
+static ssize_t
+readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit)
+{
+	ssize_t bytes_read;
+	ssize_t total_size = 0;
+	const void *t;
+	const char *s;
+	void *p;
+	char *u;
+
+	/* Accumulate line in a line buffer. */
+	for (;;) {
+		/* Read some more. */
+		bytes_read = (a->decompressor->read_ahead)(a, &t, 1);
+		if (bytes_read == 0)
+			return (0);
+		if (bytes_read < 0)
+			return (ARCHIVE_FATAL);
+		s = t;  /* Start of line? */
+		p = memchr(t, '\n', bytes_read);
+		/* If we found '\n', trim the read. */
+		if (p != NULL) {
+			bytes_read = 1 + ((const char *)p) - s;
+		}
+		if (total_size + bytes_read + 1 > limit) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Line too long");
+			return (ARCHIVE_FATAL);
+		}
+		if (archive_string_ensure(&mtree->line,
+			total_size + bytes_read + 1) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate working buffer");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(mtree->line.s + total_size, t, bytes_read);
+		(a->decompressor->consume)(a, bytes_read);
+		total_size += bytes_read;
+		/* Null terminate. */
+		mtree->line.s[total_size] = '\0';
+		/* If we found an unescaped '\n', clean up and return. */
+		if (p == NULL)
+			continue;
+		for (u = mtree->line.s; *u; ++u) {
+			if (u[0] == '\n') {
+				*start = mtree->line.s;
+				return total_size;
+			}
+			if (u[0] == '#') {
+				if (p == NULL)
+					break;
+				*start = mtree->line.s;
+				return total_size;
+			}
+			if (u[0] != '\\')
+				continue;
+			if (u[1] == '\\') {
+				++u;
+				continue;
+			}
+			if (u[1] == '\n') {
+				memmove(u, u + 1,
+				    total_size - (u - mtree->line.s) + 1);
+				--total_size;
+				continue;    
+			}
+		}
+	}
+}
Index: archive_read_support_format_cpio.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_cpio.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_support_format_cpio.c -L lib/libarchive/archive_read_support_format_cpio.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_support_format_cpio.c
+++ lib/libarchive/archive_read_support_format_cpio.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.24 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.24.2.1 2008/02/11 00:31:07 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -118,6 +118,8 @@
 static int	archive_read_format_cpio_read_header(struct archive_read *,
 		    struct archive_entry *);
 static int	be4(const unsigned char *);
+static int	find_odc_header(struct archive_read *);
+static int	find_newc_header(struct archive_read *);
 static int	header_bin_be(struct archive_read *, struct cpio *,
 		    struct archive_entry *, size_t *, size_t *);
 static int	header_bin_le(struct archive_read *, struct cpio *,
@@ -126,6 +128,8 @@
 		    struct archive_entry *, size_t *, size_t *);
 static int	header_odc(struct archive_read *, struct cpio *,
 		    struct archive_entry *, size_t *, size_t *);
+static int	is_octal(const char *, size_t);
+static int	is_hex(const char *, size_t);
 static int	le4(const unsigned char *);
 static void	record_hardlink(struct cpio *cpio, struct archive_entry *entry);
 
@@ -161,13 +165,14 @@
 static int
 archive_read_format_cpio_bid(struct archive_read *a)
 {
-	int bid, bytes_read;
+	int bytes_read;
 	const void *h;
 	const unsigned char *p;
 	struct cpio *cpio;
+	int bid;
 
 	cpio = (struct cpio *)(a->format->data);
-	bid = 0;
+
 	bytes_read = (a->decompressor->read_ahead)(a, &h, 6);
 	/* Convert error code into error return. */
 	if (bytes_read < 0)
@@ -176,6 +181,7 @@
 		return (-1);
 
 	p = (const unsigned char *)h;
+	bid = 0;
 	if (memcmp(p, "070707", 6) == 0) {
 		/* ASCII cpio archive (odc, POSIX.1) */
 		cpio->read_header = header_odc;
@@ -231,7 +237,7 @@
 	cpio = (struct cpio *)(a->format->data);
 	r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
 
-	if (r != ARCHIVE_OK)
+	if (r < ARCHIVE_WARN)
 		return (r);
 
 	/* Read name from buffer. */
@@ -266,7 +272,7 @@
 	/* Detect and record hardlinks to previously-extracted entries. */
 	record_hardlink(cpio, entry);
 
-	return (ARCHIVE_OK);
+	return (r);
 }
 
 static int
@@ -306,6 +312,84 @@
 	}
 }
 
+/*
+ * Skip forward to the next cpio newc header by searching for the
+ * 07070[12] string.  This should be generalized and merged with
+ * find_odc_header below.
+ */
+static int
+is_hex(const char *p, size_t len)
+{
+	while (len-- > 0) {
+		if ((*p >= '0' && *p <= '9')
+		    || (*p >= 'a' && *p <= 'f')
+		    || (*p >= 'A' && *p <= 'F'))
+			++p;
+		else
+			return (0);
+	}
+	return (1);
+}
+
+static int
+find_newc_header(struct archive_read *a)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, bytes, skipped = 0;
+
+	for (;;) {
+		bytes = (a->decompressor->read_ahead)(a, &h, 2048);
+		if (bytes < sizeof(struct cpio_newc_header))
+			return (ARCHIVE_FATAL);
+		p = h;
+		q = p + bytes;
+
+		/* Try the typical case first, then go into the slow search.*/
+		if (memcmp("07070", p, 5) == 0
+		    && (p[5] == '1' || p[5] == '2')
+		    && is_hex(p, sizeof(struct cpio_newc_header)))
+			return (ARCHIVE_OK);
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like an odc header.
+		 */
+		while (p + sizeof(struct cpio_newc_header) < q) {
+			switch (p[5]) {
+			case '1':
+			case '2':
+				if (memcmp("07070", p, 5) == 0
+					&& is_hex(p, sizeof(struct cpio_newc_header))) {
+					skip = p - (const char *)h;
+					(a->decompressor->consume)(a, skip);
+					skipped += skip;
+					if (skipped > 0) {
+						archive_set_error(&a->archive,
+						    0,
+						    "Skipped %d bytes before "
+						    "finding valid header",
+						    (int)skipped);
+						return (ARCHIVE_WARN);
+					}
+					return (ARCHIVE_OK);
+				}
+				p += 2;
+				break;
+			case '0':
+				p++;
+				break;
+			default:
+				p += 6;
+				break;
+			}
+		}
+		skip = p - (const char *)h;
+		(a->decompressor->consume)(a, skip);
+		skipped += skip;
+	}
+}
+
 static int
 header_newc(struct archive_read *a, struct cpio *cpio,
     struct archive_entry *entry, size_t *namelength, size_t *name_pad)
@@ -313,6 +397,11 @@
 	const void *h;
 	const struct cpio_newc_header *header;
 	size_t bytes;
+	int r;
+
+	r = find_newc_header(a);
+	if (r < ARCHIVE_WARN)
+		return (r);
 
 	/* Read fixed-size portion of header. */
 	bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header));
@@ -357,7 +446,81 @@
 	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	/* Pad file contents to a multiple of 4. */
 	cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
-	return (ARCHIVE_OK);
+	return (r);
+}
+
+/*
+ * Skip forward to the next cpio odc header by searching for the
+ * 070707 string.  This is a hand-optimized search that could
+ * probably be easily generalized to handle all character-based
+ * cpio variants.
+ */
+static int
+is_octal(const char *p, size_t len)
+{
+	while (len-- > 0) {
+		if (*p < '0' || *p > '7')
+			return (0);
+	        ++p;
+	}
+	return (1);
+}
+
+static int
+find_odc_header(struct archive_read *a)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, bytes, skipped = 0;
+
+	for (;;) {
+		bytes = (a->decompressor->read_ahead)(a, &h, 512);
+		if (bytes < sizeof(struct cpio_odc_header))
+			return (ARCHIVE_FATAL);
+		p = h;
+		q = p + bytes;
+
+		/* Try the typical case first, then go into the slow search.*/
+		if (memcmp("070707", p, 6) == 0
+		    && is_octal(p, sizeof(struct cpio_odc_header)))
+			return (ARCHIVE_OK);
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like an odc header.
+		 */
+		while (p + sizeof(struct cpio_odc_header) < q) {
+			switch (p[5]) {
+			case '7':
+				if (memcmp("070707", p, 6) == 0
+					&& is_octal(p, sizeof(struct cpio_odc_header))) {
+					skip = p - (const char *)h;
+					(a->decompressor->consume)(a, skip);
+					skipped += skip;
+					if (skipped > 0) {
+						archive_set_error(&a->archive,
+						    0,
+						    "Skipped %d bytes before "
+						    "finding valid header",
+						    (int)skipped);
+						return (ARCHIVE_WARN);
+					}
+					return (ARCHIVE_OK);
+				}
+				p += 2;
+				break;
+			case '0':
+				p++;
+				break;
+			default:
+				p += 6;
+				break;
+			}
+		}
+		skip = p - (const char *)h;
+		(a->decompressor->consume)(a, skip);
+		skipped += skip;
+	}
 }
 
 static int
@@ -365,12 +528,18 @@
     struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
+	int r;
 	const struct cpio_odc_header *header;
 	size_t bytes;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
 	a->archive.archive_format_name = "POSIX octet-oriented cpio";
 
+	/* Find the start of the next header. */
+	r = find_odc_header(a);
+	if (r < ARCHIVE_WARN)
+		return (r);
+
 	/* Read fixed-size portion of header. */
 	bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header));
 	if (bytes < sizeof(struct cpio_odc_header))
@@ -400,7 +569,7 @@
 	    atol8(header->c_filesize, sizeof(header->c_filesize));
 	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = 0;
-	return (ARCHIVE_OK);
+	return (r);
 }
 
 static int
@@ -574,7 +743,7 @@
          */
         for (le = cpio->links_head; le; le = le->next) {
                 if (le->dev == dev && le->ino == ino) {
-                        archive_entry_set_hardlink(entry, le->name);
+                        archive_entry_copy_hardlink(entry, le->name);
 
                         if (--le->links <= 0) {
                                 if (le->previous != NULL)
@@ -583,6 +752,7 @@
                                         le->next->previous = le->previous;
                                 if (cpio->links_head == le)
                                         cpio->links_head = le->next;
+				free(le->name);
                                 free(le);
                         }
 
Index: archive_write_set_format_ar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_ar.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_write_set_format_ar.c -L lib/libarchive/archive_write_set_format_ar.c -u -r1.1 -r1.2
--- lib/libarchive/archive_write_set_format_ar.c
+++ lib/libarchive/archive_write_set_format_ar.c
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.3 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.3.4.4 2008/08/10 06:09:25 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -75,8 +75,9 @@
 static ssize_t		 archive_write_ar_data(struct archive_write *,
 			     const void *buff, size_t s);
 static int		 archive_write_ar_destroy(struct archive_write *);
+static int		 archive_write_ar_finish(struct archive_write *);
 static int		 archive_write_ar_finish_entry(struct archive_write *);
-static const char	*basename(const char *path);
+static const char	*ar_basename(const char *path);
 static int		 format_octal(int64_t v, char *p, int s);
 static int		 format_decimal(int64_t v, char *p, int s);
 
@@ -86,8 +87,8 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	int r = archive_write_set_format_ar(a);
 	if (r == ARCHIVE_OK) {
-		a->archive_format = ARCHIVE_FORMAT_AR_BSD;
-		a->archive_format_name = "ar (BSD)";
+		a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+		a->archive.archive_format_name = "ar (BSD)";
 	}
 	return (r);
 }
@@ -98,8 +99,8 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	int r = archive_write_set_format_ar(a);
 	if (r == ARCHIVE_OK) {
-		a->archive_format = ARCHIVE_FORMAT_AR_GNU;
-		a->archive_format_name = "ar (GNU/SVR4)";
+		a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+		a->archive.archive_format_name = "ar (GNU/SVR4)";
 	}
 	return (r);
 }
@@ -126,7 +127,7 @@
 
 	a->format_write_header = archive_write_ar_header;
 	a->format_write_data = archive_write_ar_data;
-	a->format_finish = NULL;
+	a->format_finish = archive_write_ar_finish;
 	a->format_destroy = archive_write_ar_destroy;
 	a->format_finish_entry = archive_write_ar_finish_entry;
 	return (ARCHIVE_OK);
@@ -141,12 +142,15 @@
 	struct ar_w *ar;
 	const char *pathname;
 	const char *filename;
+	int64_t size;
 
 	ret = 0;
 	append_fn = 0;
 	ar = (struct ar_w *)a->format_data;
 	ar->is_strtab = 0;
 	filename = NULL;
+	size = archive_entry_size(entry);
+
 
 	/*
 	 * Reject files with empty name.
@@ -192,18 +196,18 @@
 		goto size;
 	}
 
-	/* 
+	/*
 	 * Otherwise, entry is a normal archive member.
 	 * Strip leading paths from filenames, if any.
 	 */
-	if ((filename = basename(pathname)) == NULL) {
+	if ((filename = ar_basename(pathname)) == NULL) {
 		/* Reject filenames with trailing "/" */
 		archive_set_error(&a->archive, EINVAL,
 		    "Invalid filename");
 		return (ARCHIVE_WARN);
 	}
 
-	if (a->archive_format == ARCHIVE_FORMAT_AR_GNU) {
+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
 		/*
 		 * SVR4/GNU variant use a "/" to mark then end of the filename,
 		 * make it possible to have embedded spaces in the filename.
@@ -260,7 +264,7 @@
 				return (ARCHIVE_WARN);
 			}
 		}
-	} else if (a->archive_format == ARCHIVE_FORMAT_AR_BSD) {
+	} else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) {
 		/*
 		 * BSD variant: for any file name which is more than
 		 * 16 chars or contains one or more embedded space(s), the
@@ -284,8 +288,7 @@
 				return (ARCHIVE_WARN);
 			}
 			append_fn = 1;
-			archive_entry_set_size(entry,
-			    archive_entry_size(entry) + strlen(filename));
+			size += strlen(filename);
 		}
 	}
 
@@ -321,8 +324,7 @@
 	}
 
 size:
-	if (format_decimal(archive_entry_size(entry), buff + AR_size_offset,
-	    AR_size_size)) {
+	if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {
 		archive_set_error(&a->archive, ERANGE,
 		    "File size out of range");
 		return (ARCHIVE_WARN);
@@ -332,7 +334,7 @@
 	if (ret != ARCHIVE_OK)
 		return (ret);
 
-	ar->entry_bytes_remaining = archive_entry_size(entry);
+	ar->entry_bytes_remaining = size;
 	ar->entry_padding = ar->entry_bytes_remaining % 2;
 
 	if (append_fn > 0) {
@@ -387,6 +389,9 @@
 
 	ar = (struct ar_w *)a->format_data;
 
+	if (ar == NULL)
+		return (ARCHIVE_OK);
+
 	if (ar->has_strtab > 0) {
 		free(ar->strtab);
 		ar->strtab = NULL;
@@ -398,6 +403,23 @@
 }
 
 static int
+archive_write_ar_finish(struct archive_write *a)
+{
+	int ret;
+
+	/*
+	 * If we haven't written anything yet, we need to write
+	 * the ar global header now to make it a valid ar archive.
+	 */
+	if (a->archive.file_position == 0) {
+		ret = (a->compressor.write)(a, "!<arch>\n", 8);
+		return (ret);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
 archive_write_ar_finish_entry(struct archive_write *a)
 {
 	struct ar_w *ar;
@@ -507,7 +529,7 @@
 }
 
 static const char *
-basename(const char *path)
+ar_basename(const char *path)
 {
 	const char *endp, *startp;
 
Index: archive_read_support_compression_program.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_program.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_read_support_compression_program.c -L lib/libarchive/archive_read_support_compression_program.c -u -r1.1 -r1.2
--- lib/libarchive/archive_read_support_compression_program.c
+++ lib/libarchive/archive_read_support_compression_program.c
@@ -24,7 +24,26 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.2 2007/07/20 01:28:50 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.2.2.1 2008/08/10 04:32:47 kientzle Exp $");
+
+
+/* This capability is only available on POSIX systems. */
+#if !defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
+    !(defined(HAVE_FORK) || defined(HAVE_VFORK))
+
+/*
+ * On non-Posix systems, allow the program to build, but choke if
+ * this function is actually invoked.
+ */
+int
+archive_read_support_compression_program(struct archive *_a, const char *cmd)
+{
+	archive_set_error(_a, -1,
+	    "External compression programs not supported on this platform");
+	return (ARCHIVE_FATAL);
+}
+
+#else
 
 #ifdef HAVE_SYS_WAIT_H
 #  include <sys/wait.h>
@@ -159,6 +178,12 @@
 		state->child_in_buf_avail = ret;
 	}
 
+	if (state->child_stdin == -1) {
+		fcntl(state->child_stdout, F_SETFL, 0);
+		__archive_check_child(state->child_stdin, state->child_stdout);
+		goto restart_read;
+	}
+
 	do {
 		ret = write(state->child_stdin, state->child_in_buf,
 		    state->child_in_buf_avail);
@@ -173,7 +198,7 @@
 		goto restart_read;
 	} else if (ret == 0 || (ret == -1 && errno == EPIPE)) {
 		close(state->child_stdin);
-		state->child_stdout = -1;
+		state->child_stdin = -1;
 		fcntl(state->child_stdout, F_SETFL, 0);
 		goto restart_read;
 	} else {
@@ -313,3 +338,5 @@
 
 	return (ARCHIVE_OK);
 }
+
+#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */
Index: archive_read_support_compression_bzip2.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_bzip2.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_support_compression_bzip2.c -L lib/libarchive/archive_read_support_compression_bzip2.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_support_compression_bzip2.c
+++ lib/libarchive/archive_read_support_compression_bzip2.c
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.16 2007/05/29 01:00:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.16.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -116,17 +116,29 @@
 	if (buffer[3] < '1' || buffer[3] > '9')
 		return (0);
 	bits_checked += 5;
+	if (len < 5)
+		return (bits_checked);
 
-	/*
-	 * Research Question: Can we do any more to verify that this
-	 * really is BZip2 format??  For 99.9% of the time, the above
-	 * test is sufficient, but it would be nice to do a more
-	 * thorough check.  It's especially troubling that the BZip2
-	 * signature begins with all ASCII characters; a tar archive
-	 * whose first filename begins with 'BZh3' would potentially
-	 * fool this logic.  (It may also be possible to guard against
-	 * such anomalies in archive_read_support_compression_none.)
-	 */
+	/* After BZh[1-9], there must be either a data block
+	 * which begins with 0x314159265359 or an end-of-data
+	 * marker of 0x177245385090. */
+
+	if (buffer[4] == 0x31) {
+		/* Verify the data block signature. */
+		size_t s = len;
+		if (s > 10) s = 10;
+		if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", s - 4) != 0)
+			return (0);
+		bits_checked += 8 * (s - 4);
+	} else if (buffer[4] == 0x17) {
+		/* Verify the end-of-data marker. */
+		size_t s = len;
+		if (s > 10) s = 10;
+		if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", s - 4) != 0)
+			return (0);
+		bits_checked += 8 * (s - 4);
+	} else
+		return (0);
 
 	return (bits_checked);
 }
@@ -145,7 +157,7 @@
 	(void)buff;	/* UNUSED */
 	(void)n;	/* UNUSED */
 
-	archive_set_error(a, -1,
+	archive_set_error(&a->archive, -1,
 	    "This version of libarchive was compiled without bzip2 support");
 	return (ARCHIVE_FATAL);
 }
Index: config_freebsd.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/config_freebsd.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/config_freebsd.h -L lib/libarchive/config_freebsd.h -u -r1.2 -r1.3
--- lib/libarchive/config_freebsd.h
+++ lib/libarchive/config_freebsd.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.5 2007/05/29 01:00:20 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.5.2.3 2008/08/10 04:32:47 kientzle Exp $
  */
 
 /* FreeBSD 5.0 and later have ACL support. */
@@ -37,6 +37,7 @@
 
 #define	HAVE_BZLIB_H 1
 #define	HAVE_CHFLAGS 1
+#define	HAVE_CHOWN 1
 #define	HAVE_DECL_INT64_MAX 1
 #define	HAVE_DECL_INT64_MIN 1
 #define	HAVE_DECL_SIZE_MAX 1
@@ -50,9 +51,13 @@
 #define	HAVE_FCHFLAGS 1
 #define	HAVE_FCHMOD 1
 #define	HAVE_FCHOWN 1
+#define	HAVE_FCNTL 1
 #define	HAVE_FCNTL_H 1
 #define	HAVE_FSEEKO 1
+#define	HAVE_FSTAT 1
 #define	HAVE_FUTIMES 1
+#define	HAVE_GETEUID 1
+#define	HAVE_GETPID 1
 #define	HAVE_GRP_H 1
 #define	HAVE_INTTYPES_H 1
 #define	HAVE_LCHFLAGS 1
@@ -65,10 +70,13 @@
 #define	HAVE_MEMSET 1
 #define	HAVE_MKDIR 1
 #define	HAVE_MKFIFO 1
+#define	HAVE_MKNOD 1
+#define	HAVE_PIPE 1
 #define	HAVE_POLL 1
 #define	HAVE_POLL_H 1
 #define	HAVE_PWD_H 1
 #define	HAVE_SELECT 1
+#define	HAVE_SETENV 1
 #define	HAVE_STDINT_H 1
 #define	HAVE_STDLIB_H 1
 #define	HAVE_STRCHR 1
@@ -79,23 +87,26 @@
 #define	HAVE_STRING_H 1
 #define	HAVE_STRRCHR 1
 #define	HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
-#define	HAVE_STRUCT_STAT_ST_RDEV 1
-#define	HAVE_STRUCT_TM_TM_GMTOFF 1
 #define	HAVE_SYS_ACL_H 1
 #define	HAVE_SYS_IOCTL_H 1
 #define	HAVE_SYS_SELECT_H 1
 #define	HAVE_SYS_STAT_H 1
 #define	HAVE_SYS_TIME_H 1
 #define	HAVE_SYS_TYPES_H 1
+#undef	HAVE_SYS_UTIME_H
 #define	HAVE_SYS_WAIT_H 1
 #define	HAVE_TIMEGM 1
+#define	HAVE_TZSET 1
 #define	HAVE_UNISTD_H 1
+#define	HAVE_UNSETENV 1
 #define	HAVE_UTIME 1
 #define	HAVE_UTIMES 1
 #define	HAVE_UTIME_H 1
+#define	HAVE_VFORK 1
 #define	HAVE_WCHAR_H 1
 #define	HAVE_WCSCPY 1
 #define	HAVE_WCSLEN 1
+#define	HAVE_WCTOMB 1
 #define	HAVE_WMEMCMP 1
 #define	HAVE_WMEMCPY 1
 #define	HAVE_ZLIB_H 1
Index: archive_read_data_into_fd.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_data_into_fd.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_data_into_fd.c -L lib/libarchive/archive_read_data_into_fd.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_data_into_fd.c
+++ lib/libarchive/archive_read_data_into_fd.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.15 2007/04/02 00:21:46 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.15.2.1 2008/08/10 02:19:17 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -64,8 +64,12 @@
 	    ARCHIVE_OK) {
 		const char *p = buff;
 		if (offset > output_offset) {
-			lseek(fd, offset - output_offset, SEEK_CUR);
-			output_offset = offset;
+			output_offset = lseek(fd,
+			    offset - output_offset, SEEK_CUR);
+			if (output_offset != offset) {
+				archive_set_error(a, errno, "Seek error");
+				return (ARCHIVE_FATAL);
+			}
 		}
 		while (size > 0) {
 			bytes_to_write = size;
@@ -74,7 +78,7 @@
 			bytes_written = write(fd, p, bytes_to_write);
 			if (bytes_written < 0) {
 				archive_set_error(a, errno, "Write error");
-				return (-1);
+				return (ARCHIVE_FATAL);
 			}
 			output_offset += bytes_written;
 			total_written += bytes_written;
--- /dev/null
+++ lib/libarchive/cpio.5
@@ -0,0 +1,325 @@
+.\" Copyright (c) 2007 Tim Kientzle
+.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+.\"
+.\" $FreeBSD: src/lib/libarchive/cpio.5,v 1.1.2.2 2008/08/10 04:32:47 kientzle Exp $
+.\"
+.Dd October 5, 2007
+.Dt CPIO 5
+.Os
+.Sh NAME
+.Nm cpio
+.Nd format of cpio archive files
+.Sh DESCRIPTION
+The
+.Nm
+archive format collects any number of files, directories, and other
+file system objects (symbolic links, device nodes, etc.) into a single
+stream of bytes.
+.Ss General Format
+Each file system object in a
+.Nm
+archive comprises a header record with basic numeric metadata
+followed by the full pathname of the entry and the file data.
+The header record stores a series of integer values that generally
+follow the fields in
+.Va struct stat .
+(See
+.Xr stat 2
+for details.)
+The variants differ primarily in how they store those integers
+(binary, octal, or hexadecimal).
+The header is followed by the pathname of the
+entry (the length of the pathname is stored in the header)
+and any file data.
+The end of the archive is indicated by a special record with
+the pathname
+.Dq TRAILER!!! .
+.Ss PWB format
+XXX Any documentation of the original PWB/UNIX 1.0 format? XXX
+.Ss Old Binary Format
+The old binary
+.Nm
+format stores numbers as 2-byte and 4-byte binary values.
+Each entry begins with a header in the following format:
+.Bd -literal -offset indent
+struct header_old_cpio {
+        unsigned short   c_magic;
+        unsigned short   c_dev;
+        unsigned short   c_ino;
+        unsigned short   c_mode;
+        unsigned short   c_uid;
+        unsigned short   c_gid;
+        unsigned short   c_nlink;
+        unsigned short   c_rdev;
+	unsigned short   c_mtime[2];
+        unsigned short   c_namesize;
+	unsigned short   c_filesize[2];
+};
+.Ed
+.Pp
+The
+.Va unsigned short
+fields here are 16-bit integer values; the
+.Va unsigned int
+fields are 32-bit integer values.
+The fields are as follows
+.Bl -tag -width indent
+.It Va magic
+The integer value octal 070707.
+This value can be used to determine whether this archive is
+written with little-endian or big-endian integers.
+.It Va dev , Va ino
+The device and inode numbers from the disk.
+These are used by programs that read
+.Nm
+archives to determine when two entries refer to the same file.
+Programs that synthesize
+.Nm
+archives should be careful to set these to distinct values for each entry.
+.It Va mode
+The mode specifies both the regular permissions and the file type.
+It consists of several bit fields as follows:
+.Bl -tag -width "MMMMMMM" -compact
+.It 0170000
+This masks the file type bits.
+.It 0140000
+File type value for sockets.
+.It 0120000
+File type value for symbolic links.
+For symbolic links, the link body is stored as file data.
+.It 0100000
+File type value for regular files.
+.It 0060000
+File type value for block special devices.
+.It 0040000
+File type value for directories.
+.It 0020000
+File type value for character special devices.
+.It 0010000
+File type value for named pipes or FIFOs.
+.It 0004000
+SUID bit.
+.It 0002000
+SGID bit.
+.It 0001000
+Sticky bit.
+On some systems, this modifies the behavior of executables and/or directories.
+.It 0000777
+The lower 9 bits specify read/write/execute permissions
+for world, group, and user following standard POSIX conventions.
+.El
+.It Va uid , Va gid
+The numeric user id and group id of the owner.
+.It Va nlink
+The number of links to this file.
+Directories always have a value of at least two here.
+Note that hardlinked files include file data with every copy in the archive.
+.It Va rdev
+For block special and character special entries,
+this field contains the associated device number.
+For all other entry types, it should be set to zero by writers
+and ignored by readers.
+.It Va mtime
+Modification time of the file, indicated as the number
+of seconds since the start of the epoch,
+00:00:00 UTC January 1, 1970.
+The four-byte integer is stored with the most-significant 16 bits first
+followed by the least-significant 16 bits.
+Each of the two 16 bit values are stored in machine-native byte order.
+.It Va namesize
+The number of bytes in the pathname that follows the header.
+This count includes the trailing NUL byte.
+.It Va filesize
+The size of the file.
+Note that this archive format is limited to
+four gigabyte file sizes.
+See
+.Va mtime
+above for a description of the storage of four-byte integers.
+.El
+.Pp
+The pathname immediately follows the fixed header.
+If the
+.Cm namesize
+is odd, an additional NUL byte is added after the pathname.
+The file data is then appended, padded with NUL
+bytes to an even length.
+.Pp
+Hardlinked files are not given special treatment;
+the full file contents are included with each copy of the
+file.
+.Ss Portable ASCII Format
+.St -susv2
+standardized an ASCII variant that is portable across all
+platforms.
+It is commonly known as the
+.Dq old character
+format or as the
+.Dq odc
+format.
+It stores the same numeric fields as the old binary format, but
+represents them as 6-character or 11-character octal values.
+.Bd -literal -offset indent
+struct cpio_odc_header {
+        char    c_magic[6];
+        char    c_dev[6];
+        char    c_ino[6];
+        char    c_mode[6];
+        char    c_uid[6];
+        char    c_gid[6];
+        char    c_nlink[6];
+        char    c_rdev[6];
+        char    c_mtime[11];
+        char    c_namesize[6];
+        char    c_filesize[11];
+};
+.Ed
+.Pp
+The fields are identical to those in the old binary format.
+The name and file body follow the fixed header.
+Unlike the old binary format, there is no additional padding
+after the pathname or file contents.
+If the files being archived are themselves entirely ASCII, then
+the resulting archive will be entirely ASCII, except for the
+NUL byte that terminates the name field.
+.Ss New ASCII Format
+The "new" ASCII format uses 8-byte hexadecimal fields for
+all numbers and separates device numbers into separate fields
+for major and minor numbers.
+.Bd -literal -offset indent
+struct cpio_newc_header {
+        char    c_magic[6];
+        char    c_ino[8];
+        char    c_mode[8];
+        char    c_uid[8];
+        char    c_gid[8];
+        char    c_nlink[8];
+        char    c_mtime[8];
+        char    c_filesize[8];
+        char    c_devmajor[8];
+        char    c_devminor[8];
+        char    c_rdevmajor[8];
+        char    c_rdevminor[8];
+        char    c_namesize[8];
+        char    c_check[8];
+};
+.Ed
+.Pp
+Except as specified below, the fields here match those specified
+for the old binary format above.
+.Bl -tag -width indent
+.It Va magic
+The string
+.Dq 070701 .
+.It Va check
+This field is always set to zero by writers and ignored by readers.
+See the next section for more details.
+.El
+.Pp
+The pathname is followed by NUL bytes so that the total size
+of the fixed header plus pathname is a multiple of four.
+Likewise, the file data is padded to a multiple of four bytes.
+Note that this format supports only 4 gigabyte files (unlike the
+older ASCII format, which supports 8 gigabyte files).
+.Pp
+In this format, hardlinked files are handled by setting the
+filesize to zero for each entry except the last one that
+appears in the archive.
+.Ss New CRC Format
+The CRC format is identical to the new ASCII format described
+in the previous section except that the magic field is set
+to
+.Dq 070702
+and the
+.Va check
+field is set to the sum of all bytes in the file data.
+This sum is computed treating all bytes as unsigned values
+and using unsigned arithmetic.
+Only the least-significant 32 bits of the sum are stored.
+.Ss HP variants
+The
+.Nm cpio
+implementation distributed with HPUX used XXXX but stored
+device numbers differently XXX.
+.Ss Other Extensions and Variants
+Sun Solaris uses additional file types to store extended file
+data, including ACLs and extended attributes, as special
+entries in cpio archives.
+.Pp
+XXX Others? XXX
+.Sh BUGS
+The
+.Dq CRC
+format is mis-named, as it uses a simple checksum and
+not a cyclic redundancy check.
+.Pp
+The old binary format is limited to 16 bits for user id,
+group id, device, and inode numbers.
+It is limited to 4 gigabyte file sizes.
+.Pp
+The old ASCII format is limited to 18 bits for
+the user id, group id, device, and inode numbers.
+It is limited to 8 gigabyte file sizes.
+.Pp
+The new ASCII format is limited to 4 gigabyte file sizes.
+.Pp
+None of the cpio formats store user or group names,
+which are essential when moving files between systems with
+dissimilar user or group numbering.
+.Pp
+Especially when writing older cpio variants, it may be necessary
+to map actual device/inode values to synthesized values that
+fit the available fields.
+With very large filesystems, this may be necessary even for
+the newer formats.
+.Sh SEE ALSO
+.Xr cpio 1 ,
+.Xr tar 5
+.Sh STANDARDS
+The
+.Nm cpio
+utility is no longer a part of POSIX or the Single Unix Standard.
+It last appeared in
+.St -susv2 .
+It has been supplanted in subsequent standards by
+.Xr pax 1 .
+The portable ASCII format is currently part of the specification for the
+.Xr pax 1
+utility.
+.Sh HISTORY
+The original cpio utility was written by Dick Haight
+while working in AT&T's Unix Support Group.
+It appeared in 1977 as part of PWB/UNIX 1.0, the
+.Dq Programmer's Work Bench
+derived from
+.At v6
+that was used internally at AT&T.
+Both the old binary and old character formats were in use
+by 1980, according to the System III source released
+by SCO under their
+.Dq Ancient Unix
+license.
+The character format was adopted as part of
+.St -p1003.1-88 .
+XXX when did "newc" appear?  Who invented it?  When did HP come out with their variant?  When did Sun introduce ACLs and extended attributes? XXX
Index: archive_write_set_format_ustar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_ustar.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_format_ustar.c -L lib/libarchive/archive_write_set_format_ustar.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_format_ustar.c
+++ lib/libarchive/archive_write_set_format_ustar.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.24 2007/06/11 05:17:30 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.24.2.3 2008/08/10 04:32:47 kientzle Exp $");
 
 
 #ifdef HAVE_ERRNO_H
@@ -186,8 +186,8 @@
 	a->format_finish = archive_write_ustar_finish;
 	a->format_destroy = archive_write_ustar_destroy;
 	a->format_finish_entry = archive_write_ustar_finish_entry;
-	a->archive_format = ARCHIVE_FORMAT_TAR_USTAR;
-	a->archive_format_name = "POSIX ustar";
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+	a->archive.archive_format_name = "POSIX ustar";
 	return (ARCHIVE_OK);
 }
 
@@ -195,7 +195,7 @@
 archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
 {
 	char buff[512];
-	int ret;
+	int ret, ret2;
 	struct ustar *ustar;
 
 	ustar = (struct ustar *)a->format_data;
@@ -206,7 +206,7 @@
 	    !(archive_entry_filetype(entry) == AE_IFREG))
 		archive_entry_set_size(entry, 0);
 
-	if (AE_IFDIR == archive_entry_mode(entry)) {
+	if (AE_IFDIR == archive_entry_filetype(entry)) {
 		const char *p;
 		char *t;
 		/*
@@ -216,25 +216,30 @@
 		p = archive_entry_pathname(entry);
 		if (p[strlen(p) - 1] != '/') {
 			t = (char *)malloc(strlen(p) + 2);
-			if (t != NULL) {
-				strcpy(t, p);
-				strcat(t, "/");
-				archive_entry_copy_pathname(entry, t);
-				free(t);
+			if (t == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				"Can't allocate ustar data");
+				return(ARCHIVE_FATAL);
 			}
+			strcpy(t, p);
+			strcat(t, "/");
+			archive_entry_copy_pathname(entry, t);
+			free(t);
 		}
 	}
 
 	ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1);
-	if (ret != ARCHIVE_OK)
-		return (ret);
-	ret = (a->compressor.write)(a, buff, 512);
-	if (ret != ARCHIVE_OK)
+	if (ret < ARCHIVE_WARN)
 		return (ret);
+	ret2 = (a->compressor.write)(a, buff, 512);
+	if (ret2 < ARCHIVE_WARN)
+		return (ret2);
+	if (ret2 < ret)
+		ret = ret2;
 
 	ustar->entry_bytes_remaining = archive_entry_size(entry);
 	ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
-	return (ARCHIVE_OK);
+	return (ret);
 }
 
 /*
@@ -279,27 +284,33 @@
 		/* Store in two pieces, splitting at a '/'. */
 		p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/');
 		/*
-		 * If the separator we found is the first '/', find
-		 * the next one.  (This is a pathological case that
-		 * occurs for paths of exactly 101 bytes that start with
-		 * '/'; it occurs because the separating '/' is not
-		 * stored explicitly and the reconstruction assumes that
-		 * an empty prefix means there is no '/' separator.)
+		 * Look for the next '/' if we chose the first character
+		 * as the separator.  (ustar format doesn't permit
+		 * an empty prefix.)
 		 */
 		if (p == pp)
 			p = strchr(p + 1, '/');
-		/*
-		 * If there is no path separator, or the prefix or
-		 * remaining name are too large, return an error.
-		 */
+		/* Fail if the name won't fit. */
 		if (!p) {
+			/* No separator. */
+			archive_set_error(&a->archive, ENAMETOOLONG,
+			    "Pathname too long");
+			ret = ARCHIVE_FAILED;
+		} else if (p[1] == '\0') {
+			/*
+			 * The only feasible separator is a final '/';
+			 * this would result in a non-empty prefix and
+			 * an empty name, which POSIX doesn't
+			 * explicity forbid, but it just feels wrong.
+			 */
 			archive_set_error(&a->archive, ENAMETOOLONG,
 			    "Pathname too long");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 		} else if (p  > pp + USTAR_prefix_size) {
+			/* Prefix is too long. */
 			archive_set_error(&a->archive, ENAMETOOLONG,
 			    "Pathname too long");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 		} else {
 			/* Copy prefix and remainder to appropriate places */
 			memcpy(h + USTAR_prefix_offset, pp, p - pp);
@@ -317,7 +328,7 @@
 		if (copy_length > USTAR_linkname_size) {
 			archive_set_error(&a->archive, ENAMETOOLONG,
 			    "Link contents too long");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 			copy_length = USTAR_linkname_size;
 		}
 		memcpy(h + USTAR_linkname_offset, p, copy_length);
@@ -329,7 +340,7 @@
 		if (copy_length > USTAR_uname_size) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Username too long");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 			copy_length = USTAR_uname_size;
 		}
 		memcpy(h + USTAR_uname_offset, p, copy_length);
@@ -341,7 +352,7 @@
 		if (strlen(p) > USTAR_gname_size) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Group name too long");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 			copy_length = USTAR_gname_size;
 		}
 		memcpy(h + USTAR_gname_offset, p, copy_length);
@@ -349,28 +360,28 @@
 
 	if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
 		archive_set_error(&a->archive, ERANGE, "Numeric mode too large");
-		ret = ARCHIVE_WARN;
+		ret = ARCHIVE_FAILED;
 	}
 
 	if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
 		archive_set_error(&a->archive, ERANGE, "Numeric user ID too large");
-		ret = ARCHIVE_WARN;
+		ret = ARCHIVE_FAILED;
 	}
 
 	if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
 		archive_set_error(&a->archive, ERANGE, "Numeric group ID too large");
-		ret = ARCHIVE_WARN;
+		ret = ARCHIVE_FAILED;
 	}
 
 	if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
 		archive_set_error(&a->archive, ERANGE, "File size out of range");
-		ret = ARCHIVE_WARN;
+		ret = ARCHIVE_FAILED;
 	}
 
 	if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
 		archive_set_error(&a->archive, ERANGE,
 		    "File modification time too large");
-		ret = ARCHIVE_WARN;
+		ret = ARCHIVE_FAILED;
 	}
 
 	if (archive_entry_filetype(entry) == AE_IFBLK
@@ -379,14 +390,14 @@
 			USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) {
 			archive_set_error(&a->archive, ERANGE,
 			    "Major device number too large");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 		}
 
 		if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset,
 			USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) {
 			archive_set_error(&a->archive, ERANGE,
 			    "Minor device number too large");
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 		}
 	}
 
@@ -406,7 +417,7 @@
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 			    "tar format cannot archive this (mode=0%lo)",
 			    (unsigned long)archive_entry_mode(entry));
-			ret = ARCHIVE_WARN;
+			ret = ARCHIVE_FAILED;
 		}
 	}
 
Index: archive_util.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_util.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_util.c -L lib/libarchive/archive_util.c -u -r1.2 -r1.3
--- lib/libarchive/archive_util.c
+++ lib/libarchive/archive_util.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.15 2007/07/06 15:36:38 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.15.2.3 2008/08/10 04:32:47 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -38,29 +38,49 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_string.h"
 
+#if ARCHIVE_VERSION_NUMBER < 3000000
+/* These disappear in libarchive 3.0 */
+/* Deprecated. */
 int
 archive_api_feature(void)
 {
 	return (ARCHIVE_API_FEATURE);
 }
 
+/* Deprecated. */
 int
 archive_api_version(void)
 {
 	return (ARCHIVE_API_VERSION);
 }
 
+/* Deprecated synonym for archive_version_number() */
 int
 archive_version_stamp(void)
 {
-	return (ARCHIVE_VERSION_STAMP);
+	return (archive_version_number());
 }
 
+/* Deprecated synonym for archive_version_string() */
 const char *
 archive_version(void)
 {
-	return (PACKAGE_NAME " " PACKAGE_VERSION);
+	return (archive_version_string());
+}
+#endif
+
+int
+archive_version_number(void)
+{
+	return (ARCHIVE_VERSION_NUMBER);
+}
+
+const char *
+archive_version_string(void)
+{
+	return (ARCHIVE_VERSION_STRING);
 }
 
 int
Index: archive_read.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read.3,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read.3 -L lib/libarchive/archive_read.3 -u -r1.2 -r1.3
--- lib/libarchive/archive_read.3
+++ lib/libarchive/archive_read.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.35 2007/09/19 16:37:45 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.35.2.2 2008/08/10 04:32:47 kientzle Exp $
 .\"
 .Dd August 19, 2006
 .Dt archive_read 3
@@ -56,6 +56,7 @@
 .\" #endif
 .Nm archive_read_data_into_fd ,
 .Nm archive_read_extract ,
+.Nm archive_read_extract2 ,
 .Nm archive_read_extract_set_progress_callback ,
 .Nm archive_read_close ,
 .Nm archive_read_finish
@@ -75,7 +76,10 @@
 .Ft int
 .Fn archive_read_support_compression_none "struct archive *"
 .Ft int
-.Fn archive_read_support_compression_program "struct archive *" "const char *cmd"
+.Fo archive_read_support_compression_program
+.Fa "struct archive *"
+.Fa "const char *cmd"
+.Fc
 .Ft int
 .Fn archive_read_support_format_all "struct archive *"
 .Ft int
@@ -89,15 +93,32 @@
 .Ft int
 .Fn archive_read_support_format_zip "struct archive *"
 .Ft int
-.Fn archive_read_open "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_close_callback *"
-.Ft int
-.Fn archive_read_open2 "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_skip_callback *" "archive_close_callback *"
+.Fo archive_read_open
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "archive_open_callback *"
+.Fa "archive_read_callback *"
+.Fa "archive_close_callback *"
+.Fc
+.Ft int
+.Fo archive_read_open2
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "archive_open_callback *"
+.Fa "archive_read_callback *"
+.Fa "archive_skip_callback *"
+.Fa "archive_close_callback *"
+.Fc
 .Ft int
 .Fn archive_read_open_FILE "struct archive *" "FILE *file"
 .Ft int
 .Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size"
 .Ft int
-.Fn archive_read_open_filename "struct archive *" "const char *filename" "size_t block_size"
+.Fo archive_read_open_filename
+.Fa "struct archive *"
+.Fa "const char *filename"
+.Fa "size_t block_size"
+.Fc
 .Ft int
 .Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size"
 .Ft int
@@ -105,7 +126,12 @@
 .Ft ssize_t
 .Fn archive_read_data "struct archive *" "void *buff" "size_t len"
 .Ft int
-.Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset"
+.Fo archive_read_data_block
+.Fa "struct archive *"
+.Fa "const void **buff"
+.Fa "size_t *len"
+.Fa "off_t *offset"
+.Fc
 .Ft int
 .Fn archive_read_data_skip "struct archive *"
 .\" #if ARCHIVE_API_VERSION < 3
@@ -115,9 +141,23 @@
 .Ft int
 .Fn archive_read_data_into_fd "struct archive *" "int fd"
 .Ft int
-.Fn archive_read_extract "struct archive *" "struct archive_entry *" "int flags"
+.Fo archive_read_extract
+.Fa "struct archive *"
+.Fa "struct archive_entry *"
+.Fa "int flags"
+.Fc
+.Ft int
+.Fo archive_read_extract2
+.Fa "struct archive *src"
+.Fa "struct archive_entry *"
+.Fa "struct archive *dest"
+.Fc
 .Ft void
-.Fn archive_read_extract_set_progress_callback "struct archive *" "void (*func)(void *)" "void *user_data"
+.Fo archive_read_extract_set_progress_callback
+.Fa "struct archive *"
+.Fa "void (*func)(void *)"
+.Fa "void *user_data"
+.Fc
 .Ft int
 .Fn archive_read_close "struct archive *"
 .Ft int
@@ -136,7 +176,13 @@
 Allocates and initializes a
 .Tn struct archive
 object suitable for reading from an archive.
-.It Fn archive_read_support_compression_all , Fn archive_read_support_compression_bzip2 , Fn archive_read_support_compression_compress , Fn archive_read_support_compression_gzip , Fn archive_read_support_compression_none
+.It Xo
+.Fn archive_read_support_compression_all ,
+.Fn archive_read_support_compression_bzip2 ,
+.Fn archive_read_support_compression_compress ,
+.Fn archive_read_support_compression_gzip ,
+.Fn archive_read_support_compression_none
+.Xc
 Enables auto-detection code and decompression support for the
 specified compression.
 Note that
@@ -150,7 +196,14 @@
 Note that this disables automatic detection of the compression format,
 so it makes no sense to specify this in conjunction with any other
 decompression option.
-.It Fn archive_read_support_format_all , Fn archive_read_support_format_cpio , Fn archive_read_support_format_empty , Fn archive_read_support_format_iso9660 , Fn archive_read_support_format_tar, Fn archive_read_support_format_zip
+.It Xo
+.Fn archive_read_support_format_all ,
+.Fn archive_read_support_format_cpio ,
+.Fn archive_read_support_format_empty ,
+.Fn archive_read_support_format_iso9660 ,
+.Fn archive_read_support_format_tar ,
+.Fn archive_read_support_format_zip
+.Xc
 Enables support---including auto-detection code---for the
 specified archive format.
 For example,
@@ -268,6 +321,22 @@
 .Va flags
 argument is passed unmodified to
 .Xr archive_write_disk_set_options 3 .
+.It Fn archive_read_extract2
+This is another version of
+.Fn archive_read_extract
+that allows you to provide your own restore object.
+In particular, this allows you to override the standard lookup functions
+using
+.Xr archive_write_disk_set_group_lookup 3 ,
+and
+.Xr archive_write_disk_set_user_lookup 3 .
+Note that
+.Fn archive_read_extract2
+does not accept a
+.Va flags
+argument; you should use
+.Fn archive_write_disk_set_options
+to set the restore options yourself.
 .It Fn archive_read_extract_set_progress_callback
 Sets a pointer to a user-defined callback that can be used
 for updating progress displays during extraction.
@@ -311,14 +380,26 @@
 .Bl -item -offset indent
 .It
 .Ft typedef ssize_t
-.Fn archive_read_callback "struct archive *" "void *client_data" "const void **buffer"
+.Fo archive_read_callback
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "const void **buffer"
+.Fc
 .It
 .\" #if ARCHIVE_API_VERSION < 2
 .Ft typedef int
-.Fn archive_skip_callback "struct archive *" "void *client_data" "size_t request"
+.Fo archive_skip_callback
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "size_t request"
+.Fc
 .\" #else
 .\" .Ft typedef off_t
-.\" .Fn archive_skip_callback "struct archive *" "void *client_data" "off_t request"
+.\" .Fo archive_skip_callback
+.\" .Fa "struct archive *"
+.\" .Fa "void *client_data"
+.\" .Fa "off_t request"
+.\" .Fc
 .\" #endif
 .It
 .Ft typedef int
Index: Makefile
===================================================================
RCS file: /home/cvs/src/lib/libarchive/Makefile,v
retrieving revision 1.5
retrieving revision 1.6
diff -L lib/libarchive/Makefile -L lib/libarchive/Makefile -u -r1.5 -r1.6
--- lib/libarchive/Makefile
+++ lib/libarchive/Makefile
@@ -1,30 +1,13 @@
-# $FreeBSD: src/lib/libarchive/Makefile,v 1.77 2007/07/15 19:10:34 kientzle Exp $
+# $FreeBSD: src/lib/libarchive/Makefile,v 1.77.2.3 2008/08/10 04:32:47 kientzle Exp $
 
 LIB=	archive
 DPADD=	${LIBBZ2} ${LIBZ}
 LDADD=	-lbz2 -lz
 
-# The libarchive version stamp.
-# Version is three numbers:
-#  Major: Bumped ONLY when API/ABI breakage happens (see SHLIB_MAJOR)
-#  Minor: Bumped when significant new features are added
-#  Revision: Bumped on any notable change
-VERSION= 2.2.4
-
-ARCHIVE_API_MAJOR!=	echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/\..*//'
-ARCHIVE_API_MINOR!=	echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/[0-9]*\.//' -e 's/\..*//'
-ARCHIVE_API_REV!=	echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/.*\.//'
-
-# Can't use /usr/bin/printf to format the version stamp here, because
-# that's not available during installworld.  Fortunately, awk is.
-ARCHIVE_VERSION_STAMP!=	echo ${ARCHIVE_API_MAJOR} ${ARCHIVE_API_MINOR} ${ARCHIVE_API_REV} | awk '{printf("%d%03d%03d",$$1,$$2,$$3)}'
-
 # FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system.
-# It has no real relation to the version number above.
+# It has no real relation to the libarchive version number.
 SHLIB_MAJOR= 4
 
-CFLAGS+=	-DPACKAGE_NAME=\"lib${LIB}\"
-CFLAGS+=	-DPACKAGE_VERSION=\"${VERSION}\"
 CFLAGS+=	-DPLATFORM_CONFIG_H=\"config_freebsd.h\"
 CFLAGS+=	-I${.OBJDIR}
 
@@ -33,27 +16,13 @@
 # Headers to be installed in /usr/include
 INCS=	archive.h archive_entry.h
 
-# Build archive.h from archive.h.in by substituting version information.
-# Note: FreeBSD has inttypes.h, so enable that include in archive.h.in
-archive.h:	archive.h.in Makefile
-	cat ${.CURDIR}/archive.h.in | sed				\
-		-e 's/@ARCHIVE_VERSION@/${VERSION}/g'	 		\
-		-e 's/@SHLIB_MAJOR@/${SHLIB_MAJOR}/g'			\
-		-e 's/@ARCHIVE_API_MAJOR@/${ARCHIVE_API_MAJOR}/g'	\
-		-e 's/@ARCHIVE_API_MINOR@/${ARCHIVE_API_MINOR}/g'	\
-		-e 's/@ARCHIVE_VERSION_STAMP@/${ARCHIVE_VERSION_STAMP}/g' \
-		-e 's|@ARCHIVE_H_INCLUDE_INTTYPES_H@|#include <inttypes.h>  /* For int64_t */|g' \
-		> archive.h
-
-# archive.h needs to be cleaned
-CLEANFILES+=	archive.h
-
 # Sources to be compiled.
-SRCS=	archive.h					\
-	archive_check_magic.c				\
+SRCS=	archive_check_magic.c				\
 	archive_entry.c					\
 	archive_entry_copy_stat.c			\
 	archive_entry_stat.c				\
+	archive_entry_strmode.c				\
+	archive_entry_link_resolver.c			\
 	archive_read.c					\
 	archive_read_data_into_fd.c			\
 	archive_read_extract.c				\
@@ -72,6 +41,7 @@
 	archive_read_support_format_cpio.c		\
 	archive_read_support_format_empty.c		\
 	archive_read_support_format_iso9660.c		\
+	archive_read_support_format_mtree.c		\
 	archive_read_support_format_tar.c		\
 	archive_read_support_format_zip.c		\
 	archive_string.c				\
@@ -86,6 +56,7 @@
 	archive_write_open_filename.c			\
 	archive_write_open_memory.c			\
 	archive_write_set_compression_bzip2.c		\
+	archive_write_set_compression_compress.c	\
 	archive_write_set_compression_gzip.c		\
 	archive_write_set_compression_none.c		\
 	archive_write_set_compression_program.c		\
@@ -105,6 +76,7 @@
 	archive_util.3					\
 	archive_write.3					\
 	archive_write_disk.3				\
+	cpio.5						\
 	libarchive.3					\
 	libarchive-formats.5				\
 	tar.5
@@ -120,10 +92,13 @@
 MLINKS+=	archive_entry.3 archive_entry_acl_text_w.3
 MLINKS+=	archive_entry.3 archive_entry_clear.3
 MLINKS+=	archive_entry.3 archive_entry_clone.3
+MLINKS+=	archive_entry.3 archive_entry_copy_fflags_text.3
 MLINKS+=	archive_entry.3 archive_entry_copy_fflags_text_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_gname.3
 MLINKS+=	archive_entry.3 archive_entry_copy_gname_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_hardlink_w.3
+MLINKS+=	archive_entry.3 archive_entry_copy_link.3
+MLINKS+=	archive_entry.3 archive_entry_copy_link_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_pathname_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_stat.3
 MLINKS+=	archive_entry.3 archive_entry_copy_symlink_w.3
@@ -215,6 +190,7 @@
 MLINKS+=	archive_util.3 archive_format.3
 MLINKS+=	archive_util.3 archive_format_name.3
 MLINKS+=	archive_util.3 archive_set_error.3
+MLINKS+=	archive_write.3 archive_write_close.3
 MLINKS+=	archive_write.3 archive_write_data.3
 MLINKS+=	archive_write.3 archive_write_finish.3
 MLINKS+=	archive_write.3 archive_write_finish_entry.3
@@ -232,6 +208,7 @@
 MLINKS+=	archive_write.3 archive_write_set_bytes_per_block.3
 MLINKS+=	archive_write.3 archive_write_set_callbacks.3
 MLINKS+=	archive_write.3 archive_write_set_compression_bzip2.3
+MLINKS+=	archive_write.3 archive_write_set_compression_compress.3
 MLINKS+=	archive_write.3 archive_write_set_compression_gzip.3
 MLINKS+=	archive_write.3 archive_write_set_compression_none.3
 MLINKS+=	archive_write.3 archive_write_set_compression_program.3
@@ -246,7 +223,8 @@
 MLINKS+=	archive_write_disk.3 archive_write_disk_set_user_lookup.3
 MLINKS+=	libarchive.3 archive.3
 
-test:
+.PHONY: check test
+check test:
 	cd ${.CURDIR}/test && make test
 
 .include <bsd.lib.mk>
Index: archive_entry.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_entry.c -L lib/libarchive/archive_entry.c -u -r1.2 -r1.3
--- lib/libarchive/archive_entry.c
+++ lib/libarchive/archive_entry.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.44 2007/07/15 19:10:34 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.44.2.4 2008/11/28 20:08:47 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -34,9 +34,6 @@
 #endif
 #ifdef MAJOR_IN_MKDEV
 #include <sys/mkdev.h>
-# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
-#  define makedev mkdev
-# endif
 #else
 #ifdef MAJOR_IN_SYSMACROS
 #include <sys/sysmacros.h>
@@ -74,19 +71,37 @@
 #undef max
 #define	max(a, b)	((a)>(b)?(a):(b))
 
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX.  <sigh> */
+#include <sys/netmgr.h>
+#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || defined _WIN32 || defined __WIN32__
+/* Windows. <sigh> */
+#define ae_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#endif
+
 static void	aes_clean(struct aes *);
 static void	aes_copy(struct aes *dest, struct aes *src);
 static const char *	aes_get_mbs(struct aes *);
 static const wchar_t *	aes_get_wcs(struct aes *);
-static void	aes_set_mbs(struct aes *, const char *mbs);
-static void	aes_copy_mbs(struct aes *, const char *mbs);
+static int	aes_set_mbs(struct aes *, const char *mbs);
+static int	aes_copy_mbs(struct aes *, const char *mbs);
 /* static void	aes_set_wcs(struct aes *, const wchar_t *wcs); */
-static void	aes_copy_wcs(struct aes *, const wchar_t *wcs);
-static void	aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
+static int	aes_copy_wcs(struct aes *, const wchar_t *wcs);
+static int	aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
 
 static char *	 ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
 static const wchar_t	*ae_wcstofflags(const wchar_t *stringp,
 		    unsigned long *setp, unsigned long *clrp);
+static const char	*ae_strtofflags(const char *stringp,
+		    unsigned long *setp, unsigned long *clrp);
 static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
 		    const wchar_t *wname, int perm, int id);
 static void	append_id_w(wchar_t **wp, int id);
@@ -131,169 +146,221 @@
 #define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
 #endif
 
-
 static void
 aes_clean(struct aes *aes)
 {
-	if (aes->aes_mbs_alloc) {
-		free(aes->aes_mbs_alloc);
-		aes->aes_mbs_alloc = NULL;
-	}
-	if (aes->aes_wcs_alloc) {
-		free(aes->aes_wcs_alloc);
-		aes->aes_wcs_alloc = NULL;
-	}
-	memset(aes, 0, sizeof(*aes));
+	if (aes->aes_wcs) {
+		free((wchar_t *)(uintptr_t)aes->aes_wcs);
+		aes->aes_wcs = NULL;
+	}
+	archive_string_free(&(aes->aes_mbs));
+	archive_string_free(&(aes->aes_utf8));
+	aes->aes_set = 0;
 }
 
 static void
 aes_copy(struct aes *dest, struct aes *src)
 {
-	*dest = *src;
-	if (src->aes_mbs != NULL) {
-		dest->aes_mbs_alloc = strdup(src->aes_mbs);
-		dest->aes_mbs = dest->aes_mbs_alloc;
-		if (dest->aes_mbs == NULL)
-			__archive_errx(1, "No memory for aes_copy()");
-	}
+	wchar_t *wp;
+
+	dest->aes_set = src->aes_set;
+	archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
+	archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
 
 	if (src->aes_wcs != NULL) {
-		dest->aes_wcs_alloc = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
+		wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
 		    * sizeof(wchar_t));
-		dest->aes_wcs = dest->aes_wcs_alloc;
-		if (dest->aes_wcs == NULL)
+		if (wp == NULL)
 			__archive_errx(1, "No memory for aes_copy()");
-		wcscpy(dest->aes_wcs_alloc, src->aes_wcs);
+		wcscpy(wp, src->aes_wcs);
+		dest->aes_wcs = wp;
 	}
 }
 
 static const char *
-aes_get_mbs(struct aes *aes)
+aes_get_utf8(struct aes *aes)
 {
-	if (aes->aes_mbs == NULL && aes->aes_wcs == NULL)
-		return NULL;
-	if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) {
-		/*
-		 * XXX Need to estimate the number of byte in the
-		 * multi-byte form.  Assume that, on average, wcs
-		 * chars encode to no more than 3 bytes.  There must
-		 * be a better way... XXX
-		 */
-		size_t mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
+	if (aes->aes_set & AES_SET_UTF8)
+		return (aes->aes_utf8.s);
+	if ((aes->aes_set & AES_SET_WCS)
+	    && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) {
+		aes->aes_set |= AES_SET_UTF8;
+		return (aes->aes_utf8.s);
+	}
+	return (NULL);
+}
 
-		aes->aes_mbs_alloc = (char *)malloc(mbs_length);
-		aes->aes_mbs = aes->aes_mbs_alloc;
-		if (aes->aes_mbs == NULL)
-			__archive_errx(1, "No memory for aes_get_mbs()");
-		wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1);
-		aes->aes_mbs_alloc[mbs_length - 1] = 0;
+static const char *
+aes_get_mbs(struct aes *aes)
+{
+	/* If we already have an MBS form, return that immediately. */
+	if (aes->aes_set & AES_SET_MBS)
+		return (aes->aes_mbs.s);
+	/* If there's a WCS form, try converting with the native locale. */
+	if ((aes->aes_set & AES_SET_WCS)
+	    && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) {
+		aes->aes_set |= AES_SET_MBS;
+		return (aes->aes_mbs.s);
 	}
-	return (aes->aes_mbs);
+	/* We'll use UTF-8 for MBS if all else fails. */
+	return (aes_get_utf8(aes));
 }
 
 static const wchar_t *
 aes_get_wcs(struct aes *aes)
 {
-	if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
-		return NULL;
-	if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
+	wchar_t *w;
+	int r;
+
+	/* Return WCS form if we already have it. */
+	if (aes->aes_set & AES_SET_WCS)
+		return (aes->aes_wcs);
+
+	if (aes->aes_set & AES_SET_MBS) {
+		/* Try converting MBS to WCS using native locale. */
 		/*
 		 * No single byte will be more than one wide character,
 		 * so this length estimate will always be big enough.
 		 */
-		size_t wcs_length = strlen(aes->aes_mbs);
+		size_t wcs_length = aes->aes_mbs.length;
 
-		aes->aes_wcs_alloc
-		    = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
-		aes->aes_wcs = aes->aes_wcs_alloc;
-		if (aes->aes_wcs == NULL)
+		w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
+		if (w == NULL)
 			__archive_errx(1, "No memory for aes_get_wcs()");
-		mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
-		aes->aes_wcs_alloc[wcs_length] = 0;
+		r = mbstowcs(w, aes->aes_mbs.s, wcs_length);
+		w[wcs_length] = 0;
+		if (r > 0) {
+			aes->aes_set |= AES_SET_WCS;
+			return (aes->aes_wcs = w);
+		}
+		free(w);
+	}
+
+	if (aes->aes_set & AES_SET_UTF8) {
+		/* Try converting UTF8 to WCS. */
+		aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
+		aes->aes_set |= AES_SET_WCS;
+		return (aes->aes_wcs);
 	}
-	return (aes->aes_wcs);
+	return (NULL);
 }
 
-static void
+static int
 aes_set_mbs(struct aes *aes, const char *mbs)
 {
-	if (aes->aes_mbs_alloc) {
-		free(aes->aes_mbs_alloc);
-		aes->aes_mbs_alloc = NULL;
-	}
-	if (aes->aes_wcs_alloc) {
-		free(aes->aes_wcs_alloc);
-		aes->aes_wcs_alloc = NULL;
-	}
-	aes->aes_mbs = mbs;
-	aes->aes_wcs = NULL;
+	return (aes_copy_mbs(aes, mbs));
 }
 
-static void
+static int
 aes_copy_mbs(struct aes *aes, const char *mbs)
 {
-	if (aes->aes_mbs_alloc) {
-		free(aes->aes_mbs_alloc);
-		aes->aes_mbs_alloc = NULL;
-	}
-	if (aes->aes_wcs_alloc) {
-		free(aes->aes_wcs_alloc);
-		aes->aes_wcs_alloc = NULL;
-	}
-	aes->aes_mbs_alloc = (char *)malloc((strlen(mbs) + 1) * sizeof(char));
-	if (aes->aes_mbs_alloc == NULL)
-		__archive_errx(1, "No memory for aes_copy_mbs()");
-	strcpy(aes->aes_mbs_alloc, mbs);
-	aes->aes_mbs = aes->aes_mbs_alloc;
-	aes->aes_wcs = NULL;
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+	archive_strcpy(&(aes->aes_mbs), mbs);
+	archive_string_empty(&(aes->aes_utf8));
+	if (aes->aes_wcs) {
+		free((wchar_t *)(uintptr_t)aes->aes_wcs);
+		aes->aes_wcs = NULL;
+	}
+	return (0);
 }
 
-#if 0
-static void
-aes_set_wcs(struct aes *aes, const wchar_t *wcs)
+/*
+ * The 'update' form tries to proactively update all forms of
+ * this string (WCS and MBS) and returns an error if any of
+ * them fail.  This is used by the 'pax' handler, for instance,
+ * to detect and report character-conversion failures early while
+ * still allowing clients to get potentially useful values from
+ * the more tolerant lazy conversions.  (get_mbs and get_wcs will
+ * strive to give the user something useful, so you can get hopefully
+ * usable values even if some of the character conversions are failing.)
+ */
+static int
+aes_update_utf8(struct aes *aes, const char *utf8)
 {
-	if (aes->aes_mbs_alloc) {
-		free(aes->aes_mbs_alloc);
-		aes->aes_mbs_alloc = NULL;
-	}
-	if (aes->aes_wcs_alloc) {
-		free(aes->aes_wcs_alloc);
-		aes->aes_wcs_alloc = NULL;
+	if (utf8 == NULL) {
+		aes->aes_set = 0;
+		return (1); /* Succeeded in clearing everything. */
+	}
+
+	/* Save the UTF8 string. */
+	archive_strcpy(&(aes->aes_utf8), utf8);
+
+	/* Empty the mbs and wcs strings. */
+	archive_string_empty(&(aes->aes_mbs));
+	if (aes->aes_wcs) {
+		free((wchar_t *)(uintptr_t)aes->aes_wcs);
+		aes->aes_wcs = NULL;
 	}
-	aes->aes_mbs = NULL;
-	aes->aes_wcs = wcs;
+
+	aes->aes_set = AES_SET_UTF8;	/* Only UTF8 is set now. */
+
+	/* TODO: We should just do a direct UTF-8 to MBS conversion
+	 * here.  That would be faster, use less space, and give the
+	 * same information.  (If a UTF-8 to MBS conversion succeeds,
+	 * then UTF-8->WCS and Unicode->MBS conversions will both
+	 * succeed.) */
+
+	/* Try converting UTF8 to WCS, return false on failure. */
+	aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
+	if (aes->aes_wcs == NULL)
+		return (0);
+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */
+
+	/* Try converting WCS to MBS, return false on failure. */
+	if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL)
+		return (0);
+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
+
+	/* All conversions succeeded. */
+	return (1);
 }
-#endif
 
-static void
+static int
 aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
 {
-	aes_copy_wcs_len(aes, wcs, wcslen(wcs));
+	return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs));
 }
 
-static void
+static int
 aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
 {
-	if (aes->aes_mbs_alloc) {
-		free(aes->aes_mbs_alloc);
-		aes->aes_mbs_alloc = NULL;
-	}
-	if (aes->aes_wcs_alloc) {
-		free(aes->aes_wcs_alloc);
-		aes->aes_wcs_alloc = NULL;
-	}
-	aes->aes_mbs = NULL;
-	aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
-	if (aes->aes_wcs_alloc == NULL)
+	wchar_t *w;
+
+	if (wcs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
+	archive_string_empty(&(aes->aes_mbs));
+	archive_string_empty(&(aes->aes_utf8));
+	if (aes->aes_wcs) {
+		free((wchar_t *)(uintptr_t)aes->aes_wcs);
+		aes->aes_wcs = NULL;
+	}
+	w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
+	if (w == NULL)
 		__archive_errx(1, "No memory for aes_copy_wcs()");
-	wmemcpy(aes->aes_wcs_alloc, wcs, len);
-	aes->aes_wcs_alloc[len] = L'\0';
-	aes->aes_wcs = aes->aes_wcs_alloc;
+	wmemcpy(w, wcs, len);
+	w[len] = L'\0';
+	aes->aes_wcs = w;
+	return (0);
 }
 
+/****************************************************************************
+ *
+ * Public Interface
+ *
+ ****************************************************************************/
+
 struct archive_entry *
 archive_entry_clear(struct archive_entry *entry)
 {
+	if (entry == NULL)
+		return (NULL);
 	aes_clean(&entry->ae_fflags_text);
 	aes_clean(&entry->ae_gname);
 	aes_clean(&entry->ae_hardlink);
@@ -328,6 +395,7 @@
 	aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
 	aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
 	aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
+	entry2->ae_set = entry->ae_set;
 	aes_copy(&entry2->ae_uname, &entry->ae_uname);
 
 	/* Copy ACL data over. */
@@ -386,12 +454,24 @@
 	return (entry->ae_stat.aest_atime_nsec);
 }
 
+int
+archive_entry_atime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_ATIME);
+}
+
 time_t
 archive_entry_ctime(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_ctime);
 }
 
+int
+archive_entry_ctime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_CTIME);
+}
+
 long
 archive_entry_ctime_nsec(struct archive_entry *entry)
 {
@@ -402,7 +482,7 @@
 archive_entry_dev(struct archive_entry *entry)
 {
 	if (entry->ae_stat.aest_dev_is_broken_down)
-		return makedev(entry->ae_stat.aest_devmajor,
+		return ae_makedev(entry->ae_stat.aest_devmajor,
 		    entry->ae_stat.aest_devminor);
 	else
 		return (entry->ae_stat.aest_dev);
@@ -493,13 +573,17 @@
 const char *
 archive_entry_hardlink(struct archive_entry *entry)
 {
-	return (aes_get_mbs(&entry->ae_hardlink));
+	if (entry->ae_set & AE_SET_HARDLINK)
+		return (aes_get_mbs(&entry->ae_hardlink));
+	return (NULL);
 }
 
 const wchar_t *
 archive_entry_hardlink_w(struct archive_entry *entry)
 {
-	return (aes_get_wcs(&entry->ae_hardlink));
+	if (entry->ae_set & AE_SET_HARDLINK)
+		return (aes_get_wcs(&entry->ae_hardlink));
+	return (NULL);
 }
 
 ino_t
@@ -526,6 +610,12 @@
 	return (entry->ae_stat.aest_mtime_nsec);
 }
 
+int
+archive_entry_mtime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_MTIME);
+}
+
 unsigned int
 archive_entry_nlink(struct archive_entry *entry)
 {
@@ -548,7 +638,7 @@
 archive_entry_rdev(struct archive_entry *entry)
 {
 	if (entry->ae_stat.aest_rdev_is_broken_down)
-		return makedev(entry->ae_stat.aest_rdevmajor,
+		return ae_makedev(entry->ae_stat.aest_rdevmajor,
 		    entry->ae_stat.aest_rdevminor);
 	else
 		return (entry->ae_stat.aest_rdev);
@@ -578,16 +668,32 @@
 	return (entry->ae_stat.aest_size);
 }
 
+int
+archive_entry_size_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_SIZE);
+}
+
+const char *
+archive_entry_sourcepath(struct archive_entry *entry)
+{
+	return (aes_get_mbs(&entry->ae_sourcepath));
+}
+
 const char *
 archive_entry_symlink(struct archive_entry *entry)
 {
-	return (aes_get_mbs(&entry->ae_symlink));
+	if (entry->ae_set & AE_SET_SYMLINK)
+		return (aes_get_mbs(&entry->ae_symlink));
+	return (NULL);
 }
 
 const wchar_t *
 archive_entry_symlink_w(struct archive_entry *entry)
 {
-	return (aes_get_wcs(&entry->ae_symlink));
+	if (entry->ae_set & AE_SET_SYMLINK)
+		return (aes_get_wcs(&entry->ae_symlink));
+	return (NULL);
 }
 
 uid_t
@@ -629,6 +735,15 @@
 	entry->ae_fflags_clear = clear;
 }
 
+const char *
+archive_entry_copy_fflags_text(struct archive_entry *entry,
+    const char *flags)
+{
+	aes_copy_mbs(&entry->ae_fflags_text, flags);
+	return (ae_strtofflags(flags,
+		    &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
 const wchar_t *
 archive_entry_copy_fflags_text_w(struct archive_entry *entry,
     const wchar_t *flags)
@@ -663,6 +778,12 @@
 	aes_copy_wcs(&entry->ae_gname, name);
 }
 
+int
+archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
+{
+	return (aes_update_utf8(&entry->ae_gname, name));
+}
+
 void
 archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
 {
@@ -674,37 +795,65 @@
 archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
 {
 	aes_set_mbs(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
 }
 
 void
 archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
 {
 	aes_copy_mbs(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
 }
 
 void
 archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
 {
 	aes_copy_wcs(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
 }
 
 void
 archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_ATIME;
 	entry->ae_stat.aest_atime = t;
 	entry->ae_stat.aest_atime_nsec = ns;
 }
 
 void
+archive_entry_unset_atime(struct archive_entry *entry)
+{
+	archive_entry_set_atime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_ATIME;
+}
+
+void
 archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_CTIME;
 	entry->ae_stat.aest_ctime = t;
 	entry->ae_stat.aest_ctime_nsec = ns;
 }
 
 void
+archive_entry_unset_ctime(struct archive_entry *entry)
+{
+	archive_entry_set_ctime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_CTIME;
+}
+
+void
 archive_entry_set_dev(struct archive_entry *entry, dev_t d)
 {
 	entry->stat_valid = 0;
@@ -732,13 +881,41 @@
 void
 archive_entry_set_link(struct archive_entry *entry, const char *target)
 {
-	if (entry->ae_symlink.aes_mbs != NULL ||
-	    entry->ae_symlink.aes_wcs != NULL)
+	if (entry->ae_set & AE_SET_SYMLINK)
 		aes_set_mbs(&entry->ae_symlink, target);
 	else
 		aes_set_mbs(&entry->ae_hardlink, target);
 }
 
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+	if (entry->ae_set & AE_SET_SYMLINK)
+		aes_copy_mbs(&entry->ae_symlink, target);
+	else
+		aes_copy_mbs(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+	if (entry->ae_set & AE_SET_SYMLINK)
+		aes_copy_wcs(&entry->ae_symlink, target);
+	else
+		aes_copy_wcs(&entry->ae_hardlink, target);
+}
+
+int
+archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
+{
+	if (entry->ae_set & AE_SET_SYMLINK)
+		return (aes_update_utf8(&entry->ae_symlink, target));
+	else
+		return (aes_update_utf8(&entry->ae_hardlink, target));
+}
+
 void
 archive_entry_set_mode(struct archive_entry *entry, mode_t m)
 {
@@ -750,11 +927,19 @@
 archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_MTIME;
 	entry->ae_stat.aest_mtime = m;
 	entry->ae_stat.aest_mtime_nsec = ns;
 }
 
 void
+archive_entry_unset_mtime(struct archive_entry *entry)
+{
+	archive_entry_set_mtime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_MTIME;
+}
+
+void
 archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
 {
 	entry->stat_valid = 0;
@@ -779,6 +964,20 @@
 	aes_copy_wcs(&entry->ae_pathname, name);
 }
 
+int
+archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+	return (aes_update_utf8(&entry->ae_pathname, name));
+}
+
+void
+archive_entry_set_perm(struct archive_entry *entry, mode_t p)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_mode &= AE_IFMT;
+	entry->ae_stat.aest_mode |= ~AE_IFMT & p;
+}
+
 void
 archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
 {
@@ -808,24 +1007,50 @@
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_size = s;
+	entry->ae_set |= AE_SET_SIZE;
+}
+
+void
+archive_entry_unset_size(struct archive_entry *entry)
+{
+	archive_entry_set_size(entry, 0);
+	entry->ae_set &= ~AE_SET_SIZE;
+}
+
+void
+archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
+{
+	aes_set_mbs(&entry->ae_sourcepath, path);
 }
 
 void
 archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
 {
 	aes_set_mbs(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
 }
 
 void
 archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
 {
 	aes_copy_mbs(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
 }
 
 void
 archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
 {
 	aes_copy_wcs(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
 }
 
 void
@@ -853,6 +1078,12 @@
 	aes_copy_wcs(&entry->ae_uname, name);
 }
 
+int
+archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
+{
+	return (aes_update_utf8(&entry->ae_uname, name));
+}
+
 /*
  * ACL management.  The following would, of course, be a lot simpler
  * if: 1) the last draft of POSIX.1e were a really thorough and
@@ -1103,6 +1334,11 @@
 		entry->acl_p = entry->acl_p->next;
 	if (entry->acl_p == NULL) {
 		entry->acl_state = 0;
+		*type = 0;
+		*permset = 0;
+		*tag = 0;
+		*id = -1;
+		*name = NULL;
 		return (ARCHIVE_EOF); /* End of ACL entries. */
 	}
 	*type = entry->acl_p->type;
@@ -1122,7 +1358,7 @@
 archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 {
 	int count;
-	int length;
+	size_t length;
 	const wchar_t *wname;
 	const wchar_t *prefix;
 	wchar_t separator;
@@ -1484,7 +1720,7 @@
 		return (ARCHIVE_OK);
 	} else {
 		*name = NULL;
-		*name = NULL;
+		*value = NULL;
 		*size = (size_t)0;
 		return (ARCHIVE_WARN);
 	}
@@ -1687,7 +1923,7 @@
  *	Convert file flags to a comma-separated string.  If no flags
  *	are set, return the empty string.
  */
-char *
+static char *
 ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
 {
 	char *string, *dp;
@@ -1732,6 +1968,70 @@
 }
 
 /*
+ * strtofflags --
+ *	Take string of arguments and return file flags.  This
+ *	version works a little differently than strtofflags(3).
+ *	In particular, it always tests every token, skipping any
+ *	unrecognized tokens.  It returns a pointer to the first
+ *	unrecognized token, or NULL if every token was recognized.
+ *	This version is also const-correct and does not modify the
+ *	provided string.
+ */
+static const char *
+ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
+{
+	const char *start, *end;
+	struct flag *flag;
+	unsigned long set, clear;
+	const char *failed;
+
+	set = clear = 0;
+	start = s;
+	failed = NULL;
+	/* Find start of first token. */
+	while (*start == '\t'  ||  *start == ' '  ||  *start == ',')
+		start++;
+	while (*start != '\0') {
+		/* Locate end of token. */
+		end = start;
+		while (*end != '\0'  &&  *end != '\t'  &&
+		    *end != ' '  &&  *end != ',')
+			end++;
+		for (flag = flags; flag->name != NULL; flag++) {
+			if (memcmp(start, flag->name, end - start) == 0) {
+				/* Matched "noXXXX", so reverse the sense. */
+				clear |= flag->set;
+				set |= flag->clear;
+				break;
+			} else if (memcmp(start, flag->name + 2, end - start)
+			    == 0) {
+				/* Matched "XXXX", so don't reverse. */
+				set |= flag->set;
+				clear |= flag->clear;
+				break;
+			}
+		}
+		/* Ignore unknown flag names. */
+		if (flag->name == NULL  &&  failed == NULL)
+			failed = start;
+
+		/* Find start of next token. */
+		start = end;
+		while (*start == '\t'  ||  *start == ' '  ||  *start == ',')
+			start++;
+
+	}
+
+	if (setp)
+		*setp = set;
+	if (clrp)
+		*clrp = clear;
+
+	/* Return location of first failure. */
+	return (failed);
+}
+
+/*
  * wcstofflags --
  *	Take string of arguments and return file flags.  This
  *	version works a little differently than strtofflags(3).
@@ -1741,7 +2041,7 @@
  *	This version is also const-correct and does not modify the
  *	provided string.
  */
-const wchar_t *
+static const wchar_t *
 ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
 {
 	const wchar_t *start, *end;
Index: tar.5
===================================================================
RCS file: /home/cvs/src/lib/libarchive/tar.5,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/tar.5 -L lib/libarchive/tar.5 -u -r1.2 -r1.3
--- lib/libarchive/tar.5
+++ lib/libarchive/tar.5
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/tar.5,v 1.17 2007/01/09 08:05:56 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/tar.5,v 1.17.2.1 2008/08/10 04:32:47 kientzle Exp $
 .\"
 .Dd May 20, 2004
 .Dt TAR 5
@@ -221,7 +221,7 @@
 .Bl -tag -width indent -compact
 .It Dq 0
 Regular file.
-NULL should be treated as a synonym, for compatibility purposes.
+NUL should be treated as a synonym, for compatibility purposes.
 .It Dq 1
 Hard link.
 .It Dq 2
@@ -258,7 +258,7 @@
 .It Va magic
 Contains the magic value
 .Dq ustar
-followed by a NULL byte to indicate that this is a POSIX standard archive.
+followed by a NUL byte to indicate that this is a POSIX standard archive.
 Full compliance requires the uname and gname fields be properly set.
 .It Va version
 Version.
@@ -285,7 +285,7 @@
 .El
 .Pp
 Note that all unused bytes must be set to
-.Dv NULL .
+.Dv NUL .
 .Pp
 Field termination is specified slightly differently by POSIX
 than by previous implementations.
@@ -295,14 +295,14 @@
 and
 .Va gname
 fields must have a trailing
-.Dv NULL .
+.Dv NUL .
 The
 .Va pathname ,
 .Va linkname ,
 and
 .Va prefix
 fields must have a trailing
-.Dv NULL
+.Dv NUL
 unless they fill the entire field.
 (In particular, it is possible to store a 256-character pathname if it
 happens to have a
@@ -310,7 +310,7 @@
 as the 156th character.)
 POSIX requires numeric fields to be zero-padded in the front, and allows
 them to be terminated with either space or
-.Dv NULL
+.Dv NUL
 characters.
 .Pp
 Currently, most tar implementations comply with the ustar
Index: archive_read_extract.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_extract.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_extract.c -L lib/libarchive/archive_read_extract.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_extract.c
+++ lib/libarchive/archive_read_extract.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.59 2007/05/29 01:00:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.59.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -67,6 +67,7 @@
 			archive_set_error(&a->archive, ENOMEM, "Can't extract");
 			return (NULL);
 		}
+		memset(a->extract, 0, sizeof(*a->extract));
 		a->extract->ad = archive_write_disk_new();
 		if (a->extract->ad == NULL) {
 			archive_set_error(&a->archive, ENOMEM, "Can't extract");
@@ -81,34 +82,40 @@
 int
 archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
 {
-	struct archive_read *a = (struct archive_read *)_a;
 	struct extract *extract;
-	int r, r2;
 
-	extract = get_extract(a);
+	extract = get_extract((struct archive_read *)_a);
 	if (extract == NULL)
 		return (ARCHIVE_FATAL);
+	archive_write_disk_set_options(extract->ad, flags);
+	return (archive_read_extract2(_a, entry, extract->ad));
+}
+
+int
+archive_read_extract2(struct archive *_a, struct archive_entry *entry,
+    struct archive *ad)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	int r, r2;
 
 	/* Set up for this particular entry. */
-	extract = a->extract;
-	archive_write_disk_set_options(a->extract->ad, flags);
-	archive_write_disk_set_skip_file(a->extract->ad,
+	archive_write_disk_set_skip_file(ad,
 	    a->skip_file_dev, a->skip_file_ino);
-	r = archive_write_header(a->extract->ad, entry);
+	r = archive_write_header(ad, entry);
 	if (r < ARCHIVE_WARN)
 		r = ARCHIVE_WARN;
 	if (r != ARCHIVE_OK)
 		/* If _write_header failed, copy the error. */
- 		archive_copy_error(&a->archive, extract->ad);
+ 		archive_copy_error(&a->archive, ad);
 	else
 		/* Otherwise, pour data into the entry. */
-		r = copy_data(_a, a->extract->ad);
-	r2 = archive_write_finish_entry(a->extract->ad);
+		r = copy_data(_a, ad);
+	r2 = archive_write_finish_entry(ad);
 	if (r2 < ARCHIVE_WARN)
 		r2 = ARCHIVE_WARN;
 	/* Use the first message. */
 	if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
- 		archive_copy_error(&a->archive, extract->ad);
+ 		archive_copy_error(&a->archive, ad);
 	/* Use the worst error return. */
 	if (r2 < r)
 		r = r2;
@@ -130,11 +137,13 @@
 static int
 copy_data(struct archive *ar, struct archive *aw)
 {
-	int r;
+	off_t offset;
 	const void *buff;
+	struct extract *extract;
 	size_t size;
-	off_t offset;
+	int r;
 
+	extract = get_extract((struct archive_read *)ar);
 	for (;;) {
 		r = archive_read_data_block(ar, &buff, &size, &offset);
 		if (r == ARCHIVE_EOF)
@@ -149,6 +158,9 @@
 			    "%s", archive_error_string(aw));
 			return (r);
 		}
+		if (extract->extract_progress)
+			(extract->extract_progress)
+			    (extract->extract_progress_user_data);
 	}
 }
 
Index: archive_util.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_util.3,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_util.3 -L lib/libarchive/archive_util.3 -u -r1.2 -r1.3
--- lib/libarchive/archive_util.3
+++ lib/libarchive/archive_util.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_util.3,v 1.7 2007/05/29 01:00:19 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_util.3,v 1.7.2.1 2008/05/21 04:12:57 kientzle Exp $
 .\"
 .Dd January 8, 2005
 .Dt archive_util 3
@@ -57,7 +57,12 @@
 .Ft const char *
 .Fn archive_format_name "struct archive *"
 .Ft void
-.Fn archive_set_error "struct archive *" "int error_code" "const char *fmt" "..."
+.Fo archive_set_error
+.Fa "struct archive *"
+.Fa "int error_code"
+.Fa "const char *fmt"
+.Fa "..."
+.Fc
 .Sh DESCRIPTION
 These functions provide access to various information about the
 .Tn struct archive
Index: libarchive_internals.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/libarchive_internals.3,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/libarchive_internals.3 -L lib/libarchive/libarchive_internals.3 -u -r1.1 -r1.2
--- lib/libarchive/libarchive_internals.3
+++ lib/libarchive/libarchive_internals.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/libarchive_internals.3,v 1.1 2007/05/29 01:00:20 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/libarchive_internals.3,v 1.1.4.1 2008/02/11 00:31:08 kientzle Exp $
 .\"
 .Dd April 16, 2007
 .Dt LIBARCHIVE 3
@@ -96,12 +96,9 @@
 and offers it to each registered compression handler.
 The one with the highest bid is initialized with the first block.
 Similarly, the format handlers are polled to see which handler
-is the best for each header request.
-(Note that a single file can have entries handled by different
-format handlers;
-this allows a simple handler for a generic version of a format
-with more complex handlers implemented independently for
-extended sub-formats.)
+is the best for each archive.
+(Prior to 2.4.0, the format bidders were invoked for each
+entry, but this design hindered error recovery.)
 .Ss I/O Layer and Client Callbacks
 The read API goes to some lengths to be nice to clients.
 As a result, there are few restrictions on the behavior of
@@ -116,6 +113,8 @@
 The client skip callback returns the number of bytes actually
 skipped, which may be much smaller than the skip requested.
 The only requirement is that the skip not be larger.
+In particular, clients are allowed to return zero for any
+skip that they don't want to handle.
 The skip callback must never be invoked with a negative value.
 .Pp
 Keep in mind that not all clients are reading from disk:
@@ -242,21 +241,12 @@
 method.
 This allows each bidder to look ahead in the input stream.
 Bidders should not look further ahead than necessary, as long
-look aheads put pressure on the compression layer to buffer
+look aheads put pressure on the decompression layer to buffer
 lots of data.
 Most formats only require a few hundred bytes of look ahead;
 look aheads of a few kilobytes are reasonable.
 (The ISO9660 reader sometimes looks ahead by 48k, which
 should be considered an upper limit.)
-Note that the bidder is invoked for every entry.
-For many formats, this is inappropriate; if you can only bid at
-the beginning of the file, store your bid value and check that
-each time your bid function is called.
-For example, the ISO9660 reader initializes a
-.Va bid
-value to -1 at registration time;
-each time the bid function is called, the bid value is returned
-immediately if it is zero or larger.
 .It Read header
 The header read is usually the most complex part of any format.
 There are a few strategies worth mentioning:
Index: archive_read_open_filename.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_open_filename.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_open_filename.c -L lib/libarchive/archive_read_open_filename.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_open_filename.c
+++ lib/libarchive/archive_read_open_filename.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.20 2007/06/26 03:06:48 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.20.2.1 2008/03/12 04:20:29 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -47,6 +47,10 @@
 
 #include "archive.h"
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
 struct read_file_data {
 	int	 fd;
 	size_t	 block_size;
@@ -113,7 +117,7 @@
 		return (ARCHIVE_FATAL);
 	}
 	if (mine->filename[0] != '\0')
-		mine->fd = open(mine->filename, O_RDONLY);
+		mine->fd = open(mine->filename, O_RDONLY | O_BINARY);
 	else
 		mine->fd = 0; /* Fake "open" for stdin. */
 	if (mine->fd < 0) {
Index: archive_read_support_format_zip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_zip.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L lib/libarchive/archive_read_support_format_zip.c -L lib/libarchive/archive_read_support_format_zip.c -u -r1.3 -r1.4
--- lib/libarchive/archive_read_support_format_zip.c
+++ lib/libarchive/archive_read_support_format_zip.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.14.2.2 2007/12/08 07:52:40 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.14.2.7 2008/11/28 20:08:47 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -42,6 +42,7 @@
 #include "archive_entry.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
+#include "archive_endian.h"
 
 struct zip {
 	/* entry_bytes_remaining is the number of bytes we expect. */
@@ -52,6 +53,9 @@
 	int64_t			entry_compressed_bytes_read;
 	int64_t			entry_uncompressed_bytes_read;
 
+	/* Running CRC32 of the decompressed data */
+	unsigned long		entry_crc32;
+
 	unsigned		version;
 	unsigned		system;
 	unsigned		flags;
@@ -69,7 +73,7 @@
 	char			end_of_entry;
 	char			end_of_entry_cleanup;
 
-	long			crc32;
+	unsigned long		crc32;
 	ssize_t			filename_length;
 	ssize_t			extra_length;
 	int64_t			uncompressed_size;
@@ -121,11 +125,6 @@
 static int	archive_read_format_zip_read_data_skip(struct archive_read *a);
 static int	archive_read_format_zip_read_header(struct archive_read *,
 		    struct archive_entry *);
-static int	i2(const char *);
-static int	i4(const char *);
-static unsigned int	u2(const char *);
-static unsigned int	u4(const char *);
-static uint64_t	u8(const char *);
 static int	zip_read_data_deflate(struct archive_read *a, const void **buff,
 		    size_t *size, off_t *offset);
 static int	zip_read_data_none(struct archive_read *a, const void **buff,
@@ -166,41 +165,132 @@
 static int
 archive_read_format_zip_bid(struct archive_read *a)
 {
-	int bytes_read;
-	int bid = 0;
-	const void *h;
 	const char *p;
+	const void *buff;
+	size_t bytes_avail;
 
-	if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP)
-		bid += 1;
-
-	bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
-	if (bytes_read < 4)
-	    return (-1);
-	p = (const char *)h;
+	if ((p = __archive_read_ahead(a, 4)) == NULL)
+		return (-1);
 
+	/*
+	 * Bid of 30 here is: 16 bits for "PK",
+	 * next 16-bit field has four options (-2 bits).
+	 * 16 + 16-2 = 30.
+	 */
 	if (p[0] == 'P' && p[1] == 'K') {
-		bid += 16;
-		if (p[2] == '\001' && p[3] == '\002')
-			bid += 16;
-		else if (p[2] == '\003' && p[3] == '\004')
-			bid += 16;
-		else if (p[2] == '\005' && p[3] == '\006')
-			bid += 16;
-		else if (p[2] == '\007' && p[3] == '\010')
-			bid += 16;
+		if ((p[2] == '\001' && p[3] == '\002')
+		    || (p[2] == '\003' && p[3] == '\004')
+		    || (p[2] == '\005' && p[3] == '\006')
+		    || (p[2] == '\007' && p[3] == '\010')
+		    || (p[2] == '0' && p[3] == '0'))
+			return (30);
+	}
+
+	/*
+	 * Attempt to handle self-extracting archives
+	 * by noting a PE header and searching forward
+	 * up to 64k for a 'PK\003\004' marker.
+	 */
+	if (p[0] == 'M' && p[1] == 'Z') {
+		/*
+		 * TODO: Additional checks that this really is a PE
+		 * file before we invoke the 128k lookahead below.
+		 * No point in allocating a bigger lookahead buffer
+		 * if we don't need to.
+		 */
+		/*
+		 * TODO: Of course, the compression layer lookahead
+		 * buffers aren't dynamically sized yet; they should be.
+		 */
+		bytes_avail = (a->decompressor->read_ahead)(a, &buff, 128*1024);
+		p = (const char *)buff;
+
+		/*
+		 * TODO: Optimize by jumping forward based on values
+		 * in the PE header.  Note that we don't need to be
+		 * exact, but we mustn't skip too far.  The search
+		 * below will compensate if we undershoot.  Skipping
+		 * will also reduce the chance of false positives
+		 * (which is not really all that high to begin with,
+		 * so maybe skipping isn't really necessary).
+		 */
+
+		while (p < bytes_avail + (const char *)buff) {
+			if (p[0] == 'P' && p[1] == 'K' /* "PK" signature */
+			    && p[2] == 3 && p[3] == 4 /* File entry */
+			    && p[8] == 8 /* compression == deflate */
+			    && p[9] == 0 /* High byte of compression */
+				)
+			{
+				return (30);
+			}
+			++p;
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Search forward for a "PK\003\004" file header.  This handles the
+ * case of self-extracting archives, where there is an executable
+ * prepended to the ZIP archive.
+ */
+static int
+skip_sfx(struct archive_read *a)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, bytes;
+
+	/*
+	 * TODO: We should be able to skip forward by a bunch
+	 * by lifting some values from the PE header.  We don't
+	 * need to be exact (we're still going to search forward
+	 * to find the header), but it will speed things up and
+	 * reduce the chance of a false positive.
+	 */
+	for (;;) {
+		bytes = (a->decompressor->read_ahead)(a, &h, 4096);
+		if (bytes < 4)
+			return (ARCHIVE_FATAL);
+		p = h;
+		q = p + bytes;
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like the zip header.
+		 */
+		while (p + 4 < q) {
+			switch (p[3]) {
+			case '\004':
+				/* TODO: Additional verification here. */
+				if (memcmp("PK\003\004", p, 4) == 0) {
+					skip = p - (const char *)h;
+					(a->decompressor->consume)(a, skip);
+					return (ARCHIVE_OK);
+				}
+				p += 4;
+				break;
+			case '\003': p += 1; break;
+			case 'K': p += 2; break;
+			case 'P': p += 3; break;
+			default: p += 4; break;
+			}
+		}
+		skip = p - (const char *)h;
+		(a->decompressor->consume)(a, skip);
 	}
-	return (bid);
 }
 
 static int
 archive_read_format_zip_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	int bytes_read;
 	const void *h;
 	const char *signature;
 	struct zip *zip;
+	int r = ARCHIVE_OK, r1;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
 	if (a->archive.archive_format_name == NULL)
@@ -212,17 +302,44 @@
 	zip->end_of_entry_cleanup = 0;
 	zip->entry_uncompressed_bytes_read = 0;
 	zip->entry_compressed_bytes_read = 0;
-	bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
-	if (bytes_read < 4)
+	zip->entry_crc32 = crc32(0, NULL, 0);
+	if ((h = __archive_read_ahead(a, 4)) == NULL)
 		return (ARCHIVE_FATAL);
 
 	signature = (const char *)h;
+	if (signature[0] == 'M' && signature[1] == 'Z') {
+		/* This is an executable?  Must be self-extracting... */
+		r = skip_sfx(a);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if ((h = __archive_read_ahead(a, 4)) == NULL)
+			return (ARCHIVE_FATAL);
+		signature = (const char *)h;
+	}
+
 	if (signature[0] != 'P' || signature[1] != 'K') {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Bad ZIP file");
 		return (ARCHIVE_FATAL);
 	}
 
+	/*
+	 * "PK00" signature is used for "split" archives that
+	 * only have a single segment.  This means we can just
+	 * skip the PK00; the first real file header should follow.
+	 */
+	if (signature[2] == '0' && signature[3] == '0') {
+		(a->decompressor->consume)(a, 4);
+		if ((h = __archive_read_ahead(a, 4)) == NULL)
+			return (ARCHIVE_FATAL);
+		signature = (const char *)h;
+		if (signature[0] != 'P' || signature[1] != 'K') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Bad ZIP file");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
 	if (signature[2] == '\001' && signature[3] == '\002') {
 		/* Beginning of central directory. */
 		return (ARCHIVE_EOF);
@@ -230,7 +347,10 @@
 
 	if (signature[2] == '\003' && signature[3] == '\004') {
 		/* Regular file entry. */
-		return (zip_read_file_header(a, entry, zip));
+		r1 = zip_read_file_header(a, entry, zip);
+		if (r1 != ARCHIVE_OK)
+			return (r1);
+		return (r);
 	}
 
 	if (signature[2] == '\005' && signature[3] == '\006') {
@@ -260,21 +380,17 @@
 {
 	const struct zip_file_header *p;
 	const void *h;
-	int bytes_read;
 
-	bytes_read =
-	    (a->decompressor->read_ahead)(a, &h, sizeof(struct zip_file_header));
-	if (bytes_read < (int)sizeof(struct zip_file_header)) {
+	if ((p = __archive_read_ahead(a, sizeof *p)) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
-	p = (const struct zip_file_header *)h;
 
 	zip->version = p->version[0];
 	zip->system = p->version[1];
-	zip->flags = i2(p->flags);
-	zip->compression = i2(p->compression);
+	zip->flags = archive_le16dec(p->flags);
+	zip->compression = archive_le16dec(p->compression);
 	if (zip->compression <
 	    sizeof(compression_names)/sizeof(compression_names[0]))
 		zip->compression_name = compression_names[zip->compression];
@@ -286,25 +402,24 @@
 	zip->mode = 0;
 	zip->uid = 0;
 	zip->gid = 0;
-	zip->crc32 = i4(p->crc32);
-	zip->filename_length = i2(p->filename_length);
-	zip->extra_length = i2(p->extra_length);
-	zip->uncompressed_size = u4(p->uncompressed_size);
-	zip->compressed_size = u4(p->compressed_size);
+	zip->crc32 = archive_le32dec(p->crc32);
+	zip->filename_length = archive_le16dec(p->filename_length);
+	zip->extra_length = archive_le16dec(p->extra_length);
+	zip->uncompressed_size = archive_le32dec(p->uncompressed_size);
+	zip->compressed_size = archive_le32dec(p->compressed_size);
 
 	(a->decompressor->consume)(a, sizeof(struct zip_file_header));
 
 
 	/* Read the filename. */
-	bytes_read = (a->decompressor->read_ahead)(a, &h, zip->filename_length);
-	if (bytes_read < zip->filename_length) {
+	if ((h = __archive_read_ahead(a, zip->filename_length)) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
 	if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL)
 		__archive_errx(1, "Out of memory");
-	archive_strncpy(&zip->pathname, (const char *)h, zip->filename_length);
+	archive_strncpy(&zip->pathname, h, zip->filename_length);
 	(a->decompressor->consume)(a, zip->filename_length);
 	archive_entry_set_pathname(entry, zip->pathname.s);
 
@@ -314,8 +429,7 @@
 		zip->mode = AE_IFREG | 0777;
 
 	/* Read the extra data. */
-	bytes_read = (a->decompressor->read_ahead)(a, &h, zip->extra_length);
-	if (bytes_read < zip->extra_length) {
+	if ((h = __archive_read_ahead(a, zip->extra_length)) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
@@ -330,7 +444,9 @@
 	archive_entry_set_mtime(entry, zip->mtime, 0);
 	archive_entry_set_ctime(entry, zip->ctime, 0);
 	archive_entry_set_atime(entry, zip->atime, 0);
-	archive_entry_set_size(entry, zip->uncompressed_size);
+	/* Set the size only if it's meaningful. */
+	if (0 == (zip->flags & ZIP_LENGTH_AT_END))
+		archive_entry_set_size(entry, zip->uncompressed_size);
 
 	zip->entry_bytes_remaining = zip->compressed_size;
 	zip->entry_offset = 0;
@@ -386,21 +502,18 @@
 	if (zip->end_of_entry) {
 		if (!zip->end_of_entry_cleanup) {
 			if (zip->flags & ZIP_LENGTH_AT_END) {
-				const void *h;
 				const char *p;
-				int bytes_read =
-				    (a->decompressor->read_ahead)(a, &h, 16);
-				if (bytes_read < 16) {
+
+				if ((p = __archive_read_ahead(a, 16)) == NULL) {
 					archive_set_error(&a->archive,
 					    ARCHIVE_ERRNO_FILE_FORMAT,
 					    "Truncated ZIP end-of-file record");
 					return (ARCHIVE_FATAL);
 				}
-				p = (const char *)h;
-				zip->crc32 = i4(p + 4);
-				zip->compressed_size = u4(p + 8);
-				zip->uncompressed_size = u4(p + 12);
-				bytes_read = (a->decompressor->consume)(a, 16);
+				zip->crc32 = archive_le32dec(p + 4);
+				zip->compressed_size = archive_le32dec(p + 8);
+				zip->uncompressed_size = archive_le32dec(p + 12);
+				(a->decompressor->consume)(a, 16);
 			}
 
 			/* Check file size, CRC against these values. */
@@ -416,14 +529,13 @@
 				    "ZIP uncompressed data is wrong size");
 				return (ARCHIVE_WARN);
 			}
-/* TODO: Compute CRC. */
-/*
-			if (zip->crc32 != zip->entry_crc32_calculated) {
+			/* Check computed CRC against header */
+			if (zip->crc32 != zip->entry_crc32) {
 				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-				    "ZIP data CRC error");
+				    "ZIP bad CRC: 0x%lx should be 0x%lx",
+				    zip->entry_crc32, zip->crc32);
 				return (ARCHIVE_WARN);
 			}
-*/
 			/* End-of-entry cleanup done. */
 			zip->end_of_entry_cleanup = 1;
 		}
@@ -457,13 +569,22 @@
 			 */
 			r = ARCHIVE_FATAL;
 		} else {
-			/* We know compressed size; just skip it. */
-			archive_read_format_zip_read_data_skip(a);
+			/* We can't decompress this entry, but we will
+			 * be able to skip() it and try the next entry. */
 			r = ARCHIVE_WARN;
 		}
 		break;
 	}
-	return (r);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Update checksum */
+	if (*size)
+		zip->entry_crc32 =
+		    crc32(zip->entry_crc32, *buff, *size);
+	/* Return EOF immediately if this is a non-regular file. */
+	if (AE_IFREG != (zip->mode & AE_IFMT))
+		return (ARCHIVE_EOF);
+	return (ARCHIVE_OK);
 }
 
 /*
@@ -636,10 +757,14 @@
 {
 	struct zip *zip;
 	const void *buff = NULL;
-	ssize_t bytes_avail;
+	off_t bytes_skipped;
 
 	zip = (struct zip *)(a->format->data);
 
+	/* If we've already read to end of data, we're done. */
+	if (zip->end_of_entry_cleanup)
+		return (ARCHIVE_OK);
+
 	/*
 	 * If the length is at the end, we have no choice but
 	 * to decompress all the data to find the end marker.
@@ -659,19 +784,10 @@
 	 * If the length is at the beginning, we can skip the
 	 * compressed data much more quickly.
 	 */
-	while (zip->entry_bytes_remaining > 0) {
-		bytes_avail = (a->decompressor->read_ahead)(a, &buff, 1);
-		if (bytes_avail <= 0) {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Truncated ZIP file body");
-			return (ARCHIVE_FATAL);
-		}
-		if (bytes_avail > zip->entry_bytes_remaining)
-			bytes_avail = zip->entry_bytes_remaining;
-		(a->decompressor->consume)(a, bytes_avail);
-		zip->entry_bytes_remaining -= bytes_avail;
-	}
+	bytes_skipped = (a->decompressor->skip)(a, zip->entry_bytes_remaining);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
 	/* This entry is finished and done. */
 	zip->end_of_entry_cleanup = zip->end_of_entry = 1;
 	return (ARCHIVE_OK);
@@ -695,37 +811,6 @@
 	return (ARCHIVE_OK);
 }
 
-static int
-i2(const char *p)
-{
-	return ((0xff & (int)p[0]) + 256 * (0xff & (int)p[1]));
-}
-
-
-static int
-i4(const char *p)
-{
-	return ((0xffff & i2(p)) + 0x10000 * (0xffff & i2(p+2)));
-}
-
-static unsigned int
-u2(const char *p)
-{
-	return ((0xff & (unsigned int)p[0]) + 256 * (0xff & (unsigned int)p[1]));
-}
-
-static unsigned int
-u4(const char *p)
-{
-	return u2(p) + 0x10000 * u2(p+2);
-}
-
-static uint64_t
-u8(const char *p)
-{
-	return u4(p) + 0x100000000LL * u4(p+4);
-}
-
 /*
  * The extra data is stored as a list of
  *	id1+size1+data1 + id2+size2+data2 ...
@@ -738,8 +823,8 @@
 	const char *p = (const char *)extra;
 	while (offset < zip->extra_length - 4)
 	{
-		unsigned short headerid = u2(p + offset);
-		unsigned short datasize = u2(p + offset + 2);
+		unsigned short headerid = archive_le16dec(p + offset);
+		unsigned short datasize = archive_le16dec(p + offset + 2);
 		offset += 4;
 		if (offset + datasize > zip->extra_length)
 			break;
@@ -751,9 +836,9 @@
 		case 0x0001:
 			/* Zip64 extended information extra field. */
 			if (datasize >= 8)
-				zip->uncompressed_size = u8(p + offset);
+				zip->uncompressed_size = archive_le64dec(p + offset);
 			if (datasize >= 16)
-				zip->compressed_size = u8(p + offset + 8);
+				zip->compressed_size = archive_le64dec(p + offset + 8);
 			break;
 		case 0x5455:
 		{
@@ -765,12 +850,13 @@
 			if (flags & 0x01)
 			{
 #ifdef DEBUG
-				fprintf(stderr, "mtime: %d -> %d\n",
-				    zip->mtime, i4(p + offset));
+				fprintf(stderr, "mtime: %lld -> %d\n",
+				    (long long)zip->mtime,
+				    archive_le32dec(p + offset));
 #endif
 				if (datasize < 4)
 					break;
-				zip->mtime = i4(p + offset);
+				zip->mtime = archive_le32dec(p + offset);
 				offset += 4;
 				datasize -= 4;
 			}
@@ -778,7 +864,7 @@
 			{
 				if (datasize < 4)
 					break;
-				zip->atime = i4(p + offset);
+				zip->atime = archive_le32dec(p + offset);
 				offset += 4;
 				datasize -= 4;
 			}
@@ -786,7 +872,7 @@
 			{
 				if (datasize < 4)
 					break;
-				zip->ctime = i4(p + offset);
+				zip->ctime = archive_le32dec(p + offset);
 				offset += 4;
 				datasize -= 4;
 			}
@@ -796,12 +882,13 @@
 			/* Info-ZIP Unix Extra Field (type 2) "Ux". */
 #ifdef DEBUG
 			fprintf(stderr, "uid %d gid %d\n",
-			    i2(p + offset), i2(p + offset + 2));
+			    archive_le16dec(p + offset),
+			    archive_le16dec(p + offset + 2));
 #endif
 			if (datasize >= 2)
-				zip->uid = i2(p + offset);
+				zip->uid = archive_le16dec(p + offset);
 			if (datasize >= 4)
-				zip->gid = i2(p + offset + 2);
+				zip->gid = archive_le16dec(p + offset + 2);
 			break;
 		default:
 			break;
Index: archive_read.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read.c -L lib/libarchive/archive_read.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read.c
+++ lib/libarchive/archive_read.c
@@ -32,7 +32,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.35 2007/05/29 01:00:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.35.2.2 2008/05/10 07:03:18 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -64,25 +64,12 @@
 archive_read_new(void)
 {
 	struct archive_read *a;
-	unsigned char	*nulls;
 
 	a = (struct archive_read *)malloc(sizeof(*a));
 	if (a == NULL)
 		return (NULL);
 	memset(a, 0, sizeof(*a));
 	a->archive.magic = ARCHIVE_READ_MAGIC;
-	a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
-
-	a->null_length = 1024;
-	nulls = (unsigned char *)malloc(a->null_length);
-	if (nulls == NULL) {
-		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate archive object 'nulls' element");
-		free(a);
-		return (NULL);
-	}
-	memset(nulls, 0, a->null_length);
-	a->nulls = nulls;
 
 	a->archive.state = ARCHIVE_STATE_NEW;
 	a->entry = archive_entry_new();
@@ -307,6 +294,18 @@
 	archive_clear_error(&a->archive);
 
 	/*
+	 * If no format has yet been chosen, choose one.
+	 */
+	if (a->format == NULL) {
+		slot = choose_format(a);
+		if (slot < 0) {
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+		a->format = &(a->formats[slot]);
+	}
+
+	/*
 	 * If client didn't consume entire data, skip any remainder
 	 * (This is especially important for GNU incremental directories.)
 	 */
@@ -324,12 +323,6 @@
 	/* Record start-of-header. */
 	a->header_position = a->archive.file_position;
 
-	slot = choose_format(a);
-	if (slot < 0) {
-		a->archive.state = ARCHIVE_STATE_FATAL;
-		return (ARCHIVE_FATAL);
-	}
-	a->format = &(a->formats[slot]);
 	ret = (a->format->read_header)(a, entry);
 
 	/*
@@ -654,8 +647,6 @@
 			(a->formats[i].cleanup)(a);
 	}
 
-	/* Casting a pointer to int allows us to remove 'const.' */
-	free((void *)(uintptr_t)(const void *)a->nulls);
 	archive_string_free(&a->archive.error_string);
 	if (a->entry)
 		archive_entry_free(a->entry);
@@ -735,3 +726,14 @@
 	__archive_errx(1, "Not enough slots for compression registration");
 	return (NULL); /* Never actually executed. */
 }
+
+/* used internally to simplify read-ahead */
+const void *
+__archive_read_ahead(struct archive_read *a, size_t len)
+{
+	const void *h;
+
+	if ((a->decompressor->read_ahead)(a, &h, len) < (ssize_t)len)
+		return (NULL);
+	return (h);
+}
Index: archive_private.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_private.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_private.h -L lib/libarchive/archive_private.h -u -r1.2 -r1.3
--- lib/libarchive/archive_private.h
+++ lib/libarchive/archive_private.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.29 2007/04/02 00:15:45 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.29.2.1 2008/08/10 05:40:16 kientzle Exp $
  */
 
 #ifndef ARCHIVE_PRIVATE_H_INCLUDED
@@ -92,7 +92,7 @@
 void	__archive_check_magic(struct archive *, unsigned int magic,
 	    unsigned int state, const char *func);
 
-void	__archive_errx(int retvalue, const char *msg);
+void	__archive_errx(int retvalue, const char *msg) __dead2;
 
 #define	err_combine(a,b)	((a) < (b) ? (a) : (b))
 
Index: archive_write_set_compression_program.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_program.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_write_set_compression_program.c -L lib/libarchive/archive_write_set_compression_program.c -u -r1.1 -r1.2
--- lib/libarchive/archive_write_set_compression_program.c
+++ lib/libarchive/archive_write_set_compression_program.c
@@ -25,7 +25,26 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.1 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.1.4.1 2008/08/10 04:32:47 kientzle Exp $");
+
+/* This capability is only available on POSIX systems. */
+#if !defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
+    !(defined(HAVE_FORK) || defined(HAVE_VFORK))
+#include "archive.h"
+
+/*
+ * On non-Posix systems, allow the program to build, but choke if
+ * this function is actually invoked.
+ */
+int
+archive_write_set_compression_program(struct archive *_a, const char *cmd)
+{
+	archive_set_error(_a, -1,
+	    "External compression programs not supported on this platform");
+	return (ARCHIVE_FATAL);
+}
+
+#else
 
 #ifdef HAVE_SYS_WAIT_H
 #  include <sys/wait.h>
@@ -163,6 +182,12 @@
 	if (ret == -1 && errno != EAGAIN)
 		return (-1);
 
+	if (state->child_stdout == -1) {
+		fcntl(state->child_stdin, F_SETFL, 0);
+		__archive_check_child(state->child_stdin, state->child_stdout);
+		goto restart_write;
+	}
+
 	do {
 		ret = read(state->child_stdout,
 		    state->child_buf + state->child_buf_avail,
@@ -320,3 +345,5 @@
 	free(state);
 	return (ret);
 }
+
+#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */
Index: archive_write_disk.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_disk.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_write_disk.c -L lib/libarchive/archive_write_disk.c -u -r1.1 -r1.2
--- lib/libarchive/archive_write_disk.c
+++ lib/libarchive/archive_write_disk.c
@@ -25,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.17 2007/09/21 04:52:42 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.17.2.13 2009/02/09 03:31:53 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -45,6 +45,9 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
 
 #ifdef HAVE_EXT2FS_EXT2_FS_H
 #include <ext2fs/ext2_fs.h>	/* for Linux file flags */
@@ -61,9 +64,6 @@
 #ifdef HAVE_LINUX_FS_H
 #include <linux/fs.h>	/* for Linux file flags */
 #endif
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h>	/* for Linux file flags */
-#endif
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
@@ -89,6 +89,10 @@
 #include "archive_entry.h"
 #include "archive_private.h"
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
 struct fixup_entry {
 	struct fixup_entry	*next;
 	mode_t			 mode;
@@ -136,6 +140,7 @@
 	uid_t			 user_uid;
 	dev_t			 skip_file_dev;
 	ino_t			 skip_file_ino;
+	time_t			 start_time;
 
 	gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid);
 	void  (*cleanup_gid)(void *private);
@@ -171,6 +176,10 @@
 	int			 fd;
 	/* Current offset for writing data to the file. */
 	off_t			 offset;
+	/* Last offset actually written to disk. */
+	off_t			 fd_offset;
+	/* Maximum size of file, -1 if unknown. */
+	off_t			 filesize;
 	/* Dir we were in before this restore; only for deep paths. */
 	int			 restore_pwd;
 	/* Mode we should use for this entry; affected by _PERM and umask. */
@@ -222,7 +231,8 @@
 static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
 static gid_t	trivial_lookup_gid(void *, const char *, gid_t);
 static uid_t	trivial_lookup_uid(void *, const char *, uid_t);
-
+static ssize_t	write_data_block(struct archive_write_disk *,
+		    const char *, size_t);
 
 static struct archive_vtable *archive_write_disk_vtable(void);
 
@@ -233,6 +243,31 @@
 static ssize_t	_archive_write_data(struct archive *, const void *, size_t);
 static ssize_t	_archive_write_data_block(struct archive *, const void *, size_t, off_t);
 
+static int
+_archive_write_disk_lazy_stat(struct archive_write_disk *a)
+{
+	if (a->pst != NULL) {
+		/* Already have stat() data available. */
+		return (ARCHIVE_OK);
+	}
+#ifdef HAVE_FSTAT
+	if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+#endif
+	/*
+	 * XXX At this point, symlinks should not be hit, otherwise
+	 * XXX a race occured.  Do we want to check explicitly for that?
+	 */
+	if (lstat(a->name, &a->st) == 0) {
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+	archive_set_error(&a->archive, errno, "Couldn't stat file");
+	return (ARCHIVE_WARN);
+}
+
 static struct archive_vtable *
 archive_write_disk_vtable(void)
 {
@@ -285,7 +320,7 @@
 	archive_clear_error(&a->archive);
 	if (a->archive.state & ARCHIVE_STATE_DATA) {
 		r = _archive_write_finish_entry(&a->archive);
-		if (r != ARCHIVE_OK)
+		if (r == ARCHIVE_FATAL)
 			return (r);
 	}
 
@@ -299,9 +334,14 @@
 	}
 	a->entry = archive_entry_clone(entry);
 	a->fd = -1;
+	a->fd_offset = 0;
 	a->offset = 0;
 	a->uid = a->user_uid;
 	a->mode = archive_entry_mode(a->entry);
+	if (archive_entry_size_is_set(a->entry))
+		a->filesize = archive_entry_size(a->entry);
+	else
+		a->filesize = -1;
 	archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
 	a->name = a->_name_data.s;
 	archive_clear_error(&a->archive);
@@ -403,13 +443,25 @@
 		fe->mode = a->mode;
 	}
 
-	if (a->deferred & TODO_TIMES) {
+	if ((a->deferred & TODO_TIMES)
+		&& (archive_entry_mtime_is_set(entry)
+		    || archive_entry_atime_is_set(entry))) {
 		fe = current_fixup(a, archive_entry_pathname(entry));
 		fe->fixup |= TODO_TIMES;
-		fe->mtime = archive_entry_mtime(entry);
-		fe->mtime_nanos = archive_entry_mtime_nsec(entry);
-		fe->atime = archive_entry_atime(entry);
-		fe->atime_nanos = archive_entry_atime_nsec(entry);
+		if (archive_entry_mtime_is_set(entry)) {
+			fe->mtime = archive_entry_mtime(entry);
+			fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+		} else {
+			fe->mtime = a->start_time;
+			fe->mtime_nanos = 0;
+		}
+		if (archive_entry_atime_is_set(entry)) {
+			fe->atime = archive_entry_atime(entry);
+			fe->atime_nanos = archive_entry_atime_nsec(entry);
+		} else {
+			fe->atime = a->start_time;
+			fe->atime_nanos = 0;
+		}
 	}
 
 	if (a->deferred & TODO_FFLAGS) {
@@ -421,6 +473,14 @@
 	/* We've created the object and are ready to pour data into it. */
 	if (ret == ARCHIVE_OK)
 		a->archive.state = ARCHIVE_STATE_DATA;
+	/*
+	 * If it's not open, tell our client not to try writing.
+	 * In particular, dirs, links, etc, don't get written to.
+	 */
+	if (a->fd < 0) {
+		archive_entry_set_size(entry, 0);
+		a->filesize = 0;
+	}
 done:
 	/* Restore the user's umask before returning. */
 	umask(a->user_umask);
@@ -440,38 +500,104 @@
 }
 
 static ssize_t
-_archive_write_data_block(struct archive *_a,
-    const void *buff, size_t size, off_t offset)
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
 {
-	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	uint64_t start_size = size;
 	ssize_t bytes_written = 0;
+	ssize_t block_size = 0, bytes_to_write;
+	int r;
 
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
-	    ARCHIVE_STATE_DATA, "archive_write_disk_block");
-	if (a->fd < 0) {
-		archive_set_error(&a->archive, 0, "File not open");
+	if (a->filesize == 0 || a->fd < 0) {
+		archive_set_error(&a->archive, 0,
+		    "Attempt to write to an empty file");
 		return (ARCHIVE_WARN);
 	}
-	archive_clear_error(&a->archive);
 
-	/* Seek if necessary to the specified offset. */
-	if (offset != a->offset) {
-		if (lseek(a->fd, offset, SEEK_SET) < 0) {
-			archive_set_error(&a->archive, errno, "Seek failed");
-			return (ARCHIVE_WARN);
-		}
-		a->offset = offset;
+	if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+		if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
+			return (r);
+		block_size = a->pst->st_blksize;
 	}
 
+	/* If this write would run beyond the file size, truncate it. */
+	if (a->filesize >= 0 && (off_t)(a->offset + size) > a->filesize)
+		start_size = size = (size_t)(a->filesize - a->offset);
+
 	/* Write the data. */
 	while (size > 0) {
-		bytes_written = write(a->fd, buff, size);
+		if (block_size == 0) {
+			bytes_to_write = size;
+		} else {
+			/* We're sparsifying the file. */
+			const char *p, *end;
+			off_t block_end;
+
+			/* Skip leading zero bytes. */
+			for (p = buff, end = buff + size; p < end; ++p) {
+				if (*p != '\0')
+					break;
+			}
+			a->offset += p - buff;
+			size -= p - buff;
+			buff = p;
+			if (size == 0)
+				break;
+
+			/* Calculate next block boundary after offset. */
+			block_end
+			    = (a->offset / block_size + 1) * block_size;
+
+			/* If the adjusted write would cross block boundary,
+			 * truncate it to the block boundary. */
+			bytes_to_write = size;
+			if (a->offset + bytes_to_write > block_end)
+				bytes_to_write = block_end - a->offset;
+		}
+
+		/* Seek if necessary to the specified offset. */
+		if (a->offset != a->fd_offset) {
+			if (lseek(a->fd, a->offset, SEEK_SET) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Seek failed");
+				return (ARCHIVE_FATAL);
+			}
+			a->fd_offset = a->offset;
+			a->archive.file_position = a->offset;
+			a->archive.raw_position = a->offset;
+ 		}
+		bytes_written = write(a->fd, buff, bytes_to_write);
 		if (bytes_written < 0) {
 			archive_set_error(&a->archive, errno, "Write failed");
 			return (ARCHIVE_WARN);
 		}
+		buff += bytes_written;
 		size -= bytes_written;
 		a->offset += bytes_written;
+		a->archive.file_position += bytes_written;
+		a->archive.raw_position += bytes_written;
+		a->fd_offset = a->offset;
+	}
+	return (start_size - size);
+}
+
+static ssize_t
+_archive_write_data_block(struct archive *_a,
+    const void *buff, size_t size, off_t offset)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	ssize_t r;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_disk_block");
+
+	a->offset = offset;
+	r = write_data_block(a, buff, size);
+	if (r < ARCHIVE_OK)
+		return (r);
+	if ((size_t)r < size) {
+		archive_set_error(&a->archive, 0,
+		    "Write request too large");
+		return (ARCHIVE_WARN);
 	}
 	return (ARCHIVE_OK);
 }
@@ -480,17 +606,11 @@
 _archive_write_data(struct archive *_a, const void *buff, size_t size)
 {
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
-	int r;
 
 	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 	    ARCHIVE_STATE_DATA, "archive_write_data");
-	if (a->fd < 0)
-		return (ARCHIVE_OK);
 
-	r = _archive_write_data_block(_a, buff, size, a->offset);
-	if (r < ARCHIVE_OK)
-		return (r);
-	return (size);
+	return (write_data_block(a, buff, size));
 }
 
 static int
@@ -506,11 +626,49 @@
 		return (ARCHIVE_OK);
 	archive_clear_error(&a->archive);
 
+	/* Pad or truncate file to the right size. */
+	if (a->fd < 0) {
+		/* There's no file. */
+	} else if (a->filesize < 0) {
+		/* File size is unknown, so we can't set the size. */
+	} else if (a->fd_offset == a->filesize) {
+		/* Last write ended at exactly the filesize; we're done. */
+		/* Hopefully, this is the common case. */
+	} else {
+		if (ftruncate(a->fd, a->filesize) == -1 &&
+		    a->filesize == 0) {
+			archive_set_error(&a->archive, errno,
+			    "File size could not be restored");
+			return (ARCHIVE_FAILED);
+		}
+		/*
+		 * Explicitly stat the file as some platforms might not
+		 * implement the XSI option to extend files via ftruncate.
+		 */
+		a->pst = NULL;
+		if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
+			return (ret);
+		if (a->st.st_size != a->filesize) {
+			const char nul = '\0';
+			if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Seek failed");
+				return (ARCHIVE_FATAL);
+			}
+			if (write(a->fd, &nul, 1) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Write to restore size failed");
+				return (ARCHIVE_FATAL);
+			}
+			a->pst = NULL;
+		}
+	}
+
 	/* Restore metadata. */
 
 	/*
-	 * Look up the "real" UID only if we're going to need it.  We
-	 * need this for TODO_SGID because chown() requires both.
+	 * Look up the "real" UID only if we're going to need it.
+	 * TODO: the TODO_SGID condition can be dropped here, can't it?
 	 */
 	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
 		a->uid = a->lookup_uid(a->lookup_uid_data,
@@ -518,6 +676,7 @@
 		    archive_entry_uid(a->entry));
 	}
 	/* Look up the "real" GID only if we're going to need it. */
+	/* TODO: the TODO_SUID condition can be dropped here, can't it? */
 	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
 		a->gid = a->lookup_gid(a->lookup_gid_data,
 		    archive_entry_gname(a->entry),
@@ -534,10 +693,6 @@
 		int r2 = set_mode(a, a->mode);
 		if (r2 < ret) ret = r2;
 	}
-	if (a->todo & TODO_TIMES) {
-		int r2 = set_time(a);
-		if (r2 < ret) ret = r2;
-	}
 	if (a->todo & TODO_ACLS) {
 		int r2 = set_acls(a);
 		if (r2 < ret) ret = r2;
@@ -550,6 +705,10 @@
 		int r2 = set_fflags(a);
 		if (r2 < ret) ret = r2;
 	}
+	if (a->todo & TODO_TIMES) {
+		int r2 = set_time(a);
+		if (r2 < ret) ret = r2;
+	}
 
 	/* If there's an fd, we can close it now. */
 	if (a->fd >= 0) {
@@ -616,7 +775,10 @@
 	a->archive.vtable = archive_write_disk_vtable();
 	a->lookup_uid = trivial_lookup_uid;
 	a->lookup_gid = trivial_lookup_gid;
+	a->start_time = time(NULL);
+#ifdef HAVE_GETEUID
 	a->user_uid = geteuid();
+#endif /* HAVE_GETEUID */
 	if (archive_string_ensure(&a->path_safe, 512) == NULL) {
 		free(a);
 		return (NULL);
@@ -647,7 +809,7 @@
 		return;
 
 	/* Try to record our starting dir. */
-	a->restore_pwd = open(".", O_RDONLY);
+	a->restore_pwd = open(".", O_RDONLY | O_BINARY);
 	if (a->restore_pwd < 0)
 		return;
 
@@ -685,12 +847,22 @@
 	int ret = ARCHIVE_OK, en;
 
 	if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+		/*
+		 * TODO: Fix this.  Apparently, there are platforms
+		 * that still allow root to hose the entire filesystem
+		 * by unlinking a dir.  The S_ISDIR() test above
+		 * prevents us from using unlink() here if the new
+		 * object is a dir, but that doesn't mean the old
+		 * object isn't a dir.
+		 */
 		if (unlink(a->name) == 0) {
-			/* We removed it, we're done. */
+			/* We removed it, reset cached stat. */
+			a->pst = NULL;
 		} else if (errno == ENOENT) {
 			/* File didn't exist, that's just as good. */
 		} else if (rmdir(a->name) == 0) {
 			/* It was a dir, but now it's gone. */
+			a->pst = NULL;
 		} else {
 			/* We tried, but couldn't get rid of it. */
 			archive_set_error(&a->archive, errno,
@@ -731,6 +903,7 @@
 			    "Can't remove already-existing dir");
 			return (ARCHIVE_WARN);
 		}
+		a->pst = NULL;
 		/* Try again. */
 		en = create_filesystem_object(a);
 	} else if (en == EEXIST) {
@@ -738,15 +911,31 @@
 		 * We know something is in the way, but we don't know what;
 		 * we need to find out before we go any further.
 		 */
-		if (lstat(a->name, &a->st) != 0) {
+		int r = 0;
+		/*
+		 * The SECURE_SYMLINK logic has already removed a
+		 * symlink to a dir if the client wants that.  So
+		 * follow the symlink if we're creating a dir.
+		 */
+		if (S_ISDIR(a->mode))
+			r = stat(a->name, &a->st);
+		/*
+		 * If it's not a dir (or it's a broken symlink),
+		 * then don't follow it.
+		 */
+		if (r != 0 || !S_ISDIR(a->mode))
+			r = lstat(a->name, &a->st);
+		if (r != 0) {
 			archive_set_error(&a->archive, errno,
 			    "Can't stat existing object");
 			return (ARCHIVE_WARN);
 		}
 
-		/* TODO: if it's a symlink... */
-
-		if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) {
+		/*
+		 * NO_OVERWRITE_NEWER doesn't apply to directories.
+		 */
+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+		    &&  !S_ISDIR(a->st.st_mode)) {
 			if (!older(&(a->st), a->entry)) {
 				archive_set_error(&a->archive, 0,
 				    "File on disk is not older; skipping.");
@@ -770,6 +959,7 @@
 				    "Can't unlink already-existing object");
 				return (ARCHIVE_WARN);
 			}
+			a->pst = NULL;
 			/* Try again. */
 			en = create_filesystem_object(a);
 		} else if (!S_ISDIR(a->mode)) {
@@ -823,8 +1013,30 @@
 	/* We identify hard/symlinks according to the link names. */
 	/* Since link(2) and symlink(2) don't handle modes, we're done here. */
 	linkname = archive_entry_hardlink(a->entry);
-	if (linkname != NULL)
-		return link(linkname, a->name) ? errno : 0;
+	if (linkname != NULL) {
+		r = link(linkname, a->name) ? errno : 0;
+		/*
+		 * New cpio and pax formats allow hardlink entries
+		 * to carry data, so we may have to open the file
+		 * for hardlink entries.
+		 *
+		 * If the hardlink was successfully created and
+		 * the archive doesn't have carry data for it,
+		 * consider it to be non-authoritive for meta data.
+		 * This is consistent with GNU tar and BSD pax.
+		 * If the hardlink does carry data, let the last
+		 * archive entry decide ownership.
+		 */
+		if (r == 0 && a->filesize <= 0) {
+			a->todo = 0;
+			a->deferred = 0;
+		} if (r == 0 && a->filesize > 0) {
+			a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY);
+			if (a->fd < 0)
+				r = errno;
+		}
+		return (r);
+	}
 	linkname = archive_entry_symlink(a->entry);
 	if (linkname != NULL)
 		return symlink(linkname, a->name) ? errno : 0;
@@ -844,24 +1056,38 @@
 	 */
 	mode = final_mode & 0777;
 
-	switch (a->mode & S_IFMT) {
+	switch (a->mode & AE_IFMT) {
 	default:
 		/* POSIX requires that we fall through here. */
 		/* FALLTHROUGH */
-	case S_IFREG:
+	case AE_IFREG:
 		a->fd = open(a->name,
-		    O_WRONLY | O_CREAT | O_EXCL, mode);
+		    O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode);
 		r = (a->fd < 0);
 		break;
-	case S_IFCHR:
+	case AE_IFCHR:
+#ifdef HAVE_MKNOD
+		/* Note: we use AE_IFCHR for the case label, and
+		 * S_IFCHR for the mknod() call.  This is correct.  */
 		r = mknod(a->name, mode | S_IFCHR,
 		    archive_entry_rdev(a->entry));
+#else
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a char device node. */
+		return (EINVAL);
+#endif /* HAVE_MKNOD */
 		break;
-	case S_IFBLK:
+	case AE_IFBLK:
+#ifdef HAVE_MKNOD
 		r = mknod(a->name, mode | S_IFBLK,
 		    archive_entry_rdev(a->entry));
+#else
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a block device node. */
+		return (EINVAL);
+#endif /* HAVE_MKNOD */
 		break;
-	case S_IFDIR:
+	case AE_IFDIR:
 		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
 		r = mkdir(a->name, mode);
 		if (r == 0) {
@@ -869,13 +1095,22 @@
 			a->deferred |= (a->todo & TODO_TIMES);
 			a->todo &= ~TODO_TIMES;
 			/* Never use an immediate chmod(). */
-			if (mode != final_mode)
+			/* We can't avoid the chmod() entirely if EXTRACT_PERM
+			 * because of SysV SGID inheritance. */
+			if ((mode != final_mode)
+			    || (a->flags & ARCHIVE_EXTRACT_PERM))
 				a->deferred |= (a->todo & TODO_MODE);
 			a->todo &= ~TODO_MODE;
 		}
 		break;
-	case S_IFIFO:
+	case AE_IFIFO:
+#ifdef HAVE_MKFIFO
 		r = mkfifo(a->name, mode);
+#else
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a fifo. */
+		return (EINVAL);
+#endif /* HAVE_MKFIFO */
 		break;
 	}
 
@@ -1098,7 +1333,7 @@
 	struct stat st;
 
 	/*
-	 * Gaurd against symlink tricks.  Reject any archive entry whose
+	 * Guard against symlink tricks.  Reject any archive entry whose
 	 * destination would be altered by a symlink.
 	 */
 	/* Whatever we checked last time doesn't need to be re-checked. */
@@ -1134,6 +1369,7 @@
 					pn[0] = c;
 					return (ARCHIVE_WARN);
 				}
+				a->pst = NULL;
 				/*
 				 * Even if we did remove it, a warning
 				 * is in order.  The warning is silly,
@@ -1157,6 +1393,7 @@
 					pn[0] = c;
 					return (ARCHIVE_WARN);
 				}
+				a->pst = NULL;
 			} else {
 				archive_set_error(&a->archive, 0,
 				    "Cannot extract through symlink %s",
@@ -1183,13 +1420,12 @@
 {
 	char *dest, *src;
 	char separator = '\0';
-	int lastdotdot = 0; /* True if last elt copied was '..' */
 
 	dest = src = a->name;
 	if (*src == '\0') {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Invalid empty pathname");
-		return (ARCHIVE_WARN);
+		return (ARCHIVE_FAILED);
 	}
 
 	/* Skip leading '/'. */
@@ -1220,11 +1456,9 @@
 						archive_set_error(&a->archive,
 						    ARCHIVE_ERRNO_MISC,
 						    "Path contains '..'");
-						return (ARCHIVE_WARN);
+						return (ARCHIVE_FAILED);
 					}
-					lastdotdot = 1;
-				} else
-					lastdotdot = 0;
+				}
 				/*
 				 * Note: Under no circumstances do we
 				 * remove '..' elements.  In
@@ -1232,10 +1466,8 @@
 				 * '/foo/../bar/' should create the
 				 * 'foo' dir as a side-effect.
 				 */
-			} else
-				lastdotdot = 0;
-		} else
-			lastdotdot = 0;
+			}
+		}
 
 		/* Copy current element, including leading '/'. */
 		if (separator)
@@ -1254,13 +1486,6 @@
 	 * We've just copied zero or more path elements, not including the
 	 * final '/'.
 	 */
-	if (lastdotdot) {
-		/* Trailing '..' is always wrong. */
-		archive_set_error(&a->archive,
-		    ARCHIVE_ERRNO_MISC,
-		    "Path contains trailing '..'");
-		return (ARCHIVE_WARN);
-	}
 	if (dest == a->name) {
 		/*
 		 * Nothing got copied.  The path must have been something
@@ -1403,8 +1628,8 @@
 /*
  * Note: Although we can skip setting the user id if the desired user
  * id matches the current user, we cannot skip setting the group, as
- * many systems set the gid bit based on the containing directory.  So
- * we have to perform a chown syscall if we want to restore the SGID
+ * many systems set the gid based on the containing directory.  So
+ * we have to perform a chown syscall if we want to set the SGID
  * bit.  (The alternative is to stat() and then possibly chown(); it's
  * more efficient to skip the stat() and just always chown().)  Note
  * that a successful chown() here clears the TODO_SGID_CHECK bit, which
@@ -1421,28 +1646,34 @@
 	}
 
 #ifdef HAVE_FCHOWN
-	if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0)
-		goto success;
+	/* If we have an fd, we can avoid a race. */
+	if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
+		/* We've set owner and know uid/gid are correct. */
+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+		return (ARCHIVE_OK);
+	}
 #endif
 
+	/* We prefer lchown() but will use chown() if that's all we have. */
+	/* Of course, if we have neither, this will always fail. */
 #ifdef HAVE_LCHOWN
-	if (lchown(a->name, a->uid, a->gid) == 0)
-		goto success;
-#else
-	if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0)
-		goto success;
+	if (lchown(a->name, a->uid, a->gid) == 0) {
+		/* We've set owner and know uid/gid are correct. */
+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+		return (ARCHIVE_OK);
+	}
+#elif HAVE_CHOWN
+	if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
+		/* We've set owner and know uid/gid are correct. */
+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+		return (ARCHIVE_OK);
+	}
 #endif
 
 	archive_set_error(&a->archive, errno,
 	    "Can't set user=%d/group=%d for %s", a->uid, a->gid,
 	    a->name);
 	return (ARCHIVE_WARN);
-success:
-	a->todo &= ~TODO_OWNER;
-	/* We know the user/group are correct now. */
-	a->todo &= ~TODO_SGID_CHECK;
-	a->todo &= ~TODO_SUID_CHECK;
-	return (ARCHIVE_OK);
 }
 
 #ifdef HAVE_UTIMES
@@ -1456,11 +1687,31 @@
 {
 	struct timeval times[2];
 
-	times[1].tv_sec = archive_entry_mtime(a->entry);
-	times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
+	/* If no time was provided, we're done. */
+	if (!archive_entry_atime_is_set(a->entry)
+	    && !archive_entry_mtime_is_set(a->entry))
+		return (ARCHIVE_OK);
 
-	times[0].tv_sec = archive_entry_atime(a->entry);
-	times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
+	/* We know at least one is set, so... */
+	if (archive_entry_mtime_is_set(a->entry)) {
+		times[1].tv_sec = archive_entry_mtime(a->entry);
+		times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
+	} else {
+		times[1].tv_sec = a->start_time;
+		times[1].tv_usec = 0;
+	}
+
+	/* If no atime was specified, use start time instead. */
+	/* In theory, it would be marginally more correct to use
+	 * time(NULL) here, but that would cost us an extra syscall
+	 * for little gain. */
+	if (archive_entry_atime_is_set(a->entry)) {
+		times[0].tv_sec = archive_entry_atime(a->entry);
+		times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
+	} else {
+		times[0].tv_sec = a->start_time;
+		times[0].tv_usec = 0;
+	}
 
 #ifdef HAVE_FUTIMES
 	if (a->fd >= 0 && futimes(a->fd, times) == 0) {
@@ -1498,8 +1749,24 @@
 {
 	struct utimbuf times;
 
-	times.modtime = archive_entry_mtime(a->entry);
-	times.actime = archive_entry_atime(a->entry);
+	/* If no time was provided, we're done. */
+	if (!archive_entry_atime_is_set(a->entry)
+	    && !archive_entry_mtime_is_set(a->entry))
+		return (ARCHIVE_OK);
+
+	/* We know at least one is set, so... */
+	/* Set mtime from mtime if set, else start time. */
+	if (archive_entry_mtime_is_set(a->entry))
+		times.modtime = archive_entry_mtime(a->entry);
+	else
+		times.modtime = a->start_time;
+
+	/* Set atime from provided atime, else mtime. */
+	if (archive_entry_atime_is_set(a->entry))
+		times.actime = archive_entry_atime(a->entry);
+	else
+		times.actime = a->start_time;
+
 	if (!S_ISLNK(a->mode) && utime(a->name, &times) != 0) {
 		archive_set_error(&a->archive, errno,
 		    "Can't update time for %s", a->name);
@@ -1533,19 +1800,8 @@
 		 * process, since systems sometimes set GID from
 		 * the enclosing dir or based on ACLs.
 		 */
-		if (a->pst != NULL) {
-			/* Already have stat() data available. */
-#ifdef HAVE_FSTAT
-		} else if (fd >= 0 && fstat(fd, &a->st) == 0) {
-			a->pst = &a->st;
-#endif
-		} else if (stat(a->name, &a->st) == 0) {
-			a->pst = &a->st;
-		} else {
-			archive_set_error(&a->archive, errno,
-			    "Couldn't stat file");
-			return (ARCHIVE_WARN);
-		}
+		if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
+			return (r);
 		if (a->pst->st_gid != a->gid) {
 			mode &= ~ S_ISGID;
 			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
@@ -1708,6 +1964,8 @@
 set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
     mode_t mode, unsigned long set, unsigned long clear)
 {
+	int r;
+
 	(void)mode; /* UNUSED */
 	if (set == 0  && clear == 0)
 		return (ARCHIVE_OK);
@@ -1718,15 +1976,8 @@
 	 * about the correct approach if we're overwriting an existing
 	 * file that already has flags on it. XXX
 	 */
-	if (fd >= 0 && fstat(fd, &a->st) == 0)
-		a->pst = &a->st;
-	else if (lstat(name, &a->st) == 0)
-		a->pst = &a->st;
-	else {
-		archive_set_error(&a->archive, errno,
-		    "Couldn't stat file");
-		return (ARCHIVE_WARN);
-	}
+	if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
+		return (r);
 
 	a->st.st_flags &= ~clear;
 	a->st.st_flags |= set;
@@ -1780,7 +2031,7 @@
 
 	/* If we weren't given an fd, open it ourselves. */
 	if (myfd < 0)
-		myfd = open(name, O_RDONLY|O_NONBLOCK);
+		myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY);
 	if (myfd < 0)
 		return (ARCHIVE_OK);
 
Index: filter_fork.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/filter_fork.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/filter_fork.c -L lib/libarchive/filter_fork.c -u -r1.1 -r1.2
--- lib/libarchive/filter_fork.c
+++ lib/libarchive/filter_fork.c
@@ -25,11 +25,17 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.1 2007/05/29 01:00:20 kientzle Exp $");
+/* This capability is only available on POSIX systems. */
+#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
+    (defined(HAVE_FORK) || defined(HAVE_VFORK))
+
+__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.1.4.2 2008/08/10 04:32:47 kientzle Exp $");
 
 #if defined(HAVE_POLL)
 #  if defined(HAVE_POLL_H)
 #    include <poll.h>
+#  elif defined(HAVE_SYS_POLL_H)
+#    include <sys/poll.h>
 #  endif
 #elif defined(HAVE_SELECT)
 #  if defined(HAVE_SYS_SELECT_H)
@@ -70,7 +76,11 @@
 		stdout_pipe[1] = tmp;
 	}
 
+#if HAVE_VFORK
 	switch ((child = vfork())) {
+#else
+	switch ((child = fork())) {
+#endif
 	case -1:
 		goto stdout_opened;
 	case 0:
@@ -113,25 +123,39 @@
 {
 #if defined(HAVE_POLL)
 	struct pollfd fds[2];
+	int idx;
 
-	fds[0].fd = in;
-	fds[0].events = POLLOUT;
-	fds[1].fd = out;
-	fds[1].events = POLLIN;
+	idx = 0;
+	if (in != -1) {
+		fds[idx].fd = in;
+		fds[idx].events = POLLOUT;
+		++idx;
+	}
+	if (out != -1) {
+		fds[idx].fd = out;
+		fds[idx].events = POLLIN;
+		++idx;
+	}
 
-	poll(fds, 2, -1); /* -1 == INFTIM, wait forever */
+	poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
 #elif defined(HAVE_SELECT)
 	fd_set fds_in, fds_out, fds_error;
 
 	FD_ZERO(&fds_in);
-	FD_SET(out, &fds_in);
 	FD_ZERO(&fds_out);
-	FD_SET(in, &fds_out);
 	FD_ZERO(&fds_error);
-	FD_SET(in, &fds_error);
-	FD_SET(out, &fds_error);
+	if (out != -1) {
+		FD_SET(out, &fds_in);
+		FD_SET(out, &fds_error);
+	}
+	if (in != -1) {
+		FD_SET(in, &fds_out);
+		FD_SET(in, &fds_error);
+	}
 	select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
 #else
 	sleep(1);
 #endif
 }
+
+#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
Index: archive_write_set_format_cpio_newc.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_cpio_newc.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_format_cpio_newc.c -L lib/libarchive/archive_write_set_format_cpio_newc.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_format_cpio_newc.c
+++ lib/libarchive/archive_write_set_format_cpio_newc.c
@@ -25,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio_newc.c,v 1.1.4.1 2007/10/21 22:17:25 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio_newc.c,v 1.1.4.3 2008/05/10 06:57:04 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -102,8 +102,8 @@
 	a->format_finish_entry = archive_write_newc_finish_entry;
 	a->format_finish = archive_write_newc_finish;
 	a->format_destroy = archive_write_newc_destroy;
-	a->archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
-	a->archive_format_name = "SVR4 cpio nocrc";
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+	a->archive.archive_format_name = "SVR4 cpio nocrc";
 	return (ARCHIVE_OK);
 }
 
@@ -176,9 +176,15 @@
 
 	cpio->entry_bytes_remaining = archive_entry_size(entry);
 	cpio->padding = 3 & (-cpio->entry_bytes_remaining);
+
 	/* Write the symlink now. */
-	if (p != NULL  &&  *p != '\0')
+	if (p != NULL  &&  *p != '\0') {
 		ret = (a->compressor.write)(a, p, strlen(p));
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		pad = 0x3 & -strlen(p);
+		ret = (a->compressor.write)(a, "\0\0\0", pad);
+	}
 
 	return (ret);
 }
Index: archive_read_support_format_all.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_all.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_support_format_all.c -L lib/libarchive/archive_read_support_format_all.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_support_format_all.c
+++ lib/libarchive/archive_read_support_format_all.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.9 2007/04/07 05:54:23 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.9.2.1 2008/02/11 00:31:07 kientzle Exp $");
 
 #include "archive.h"
 
@@ -35,6 +35,7 @@
 	archive_read_support_format_cpio(a);
 	archive_read_support_format_empty(a);
 	archive_read_support_format_iso9660(a);
+	archive_read_support_format_mtree(a);
 	archive_read_support_format_tar(a);
 	archive_read_support_format_zip(a);
 	return (ARCHIVE_OK);
Index: archive_write_set_compression_none.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_none.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_compression_none.c -L lib/libarchive/archive_write_set_compression_none.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_compression_none.c
+++ lib/libarchive/archive_write_set_compression_none.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.15 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.15.2.1 2008/02/11 00:31:08 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -151,11 +151,18 @@
 		return (ARCHIVE_OK);
 	}
 
-	while ((remaining > 0) || (state->avail == 0)) {
-		/*
-		 * If we have a full output block, write it and reset the
-		 * output buffer.
-		 */
+	/* If the copy buffer isn't empty, try to fill it. */
+	if (state->avail < state->buffer_size) {
+		/* If buffer is not empty... */
+		/* ... copy data into buffer ... */
+		to_copy = (remaining > state->avail) ?
+		    state->avail : remaining;
+		memcpy(state->next, buff, to_copy);
+		state->next += to_copy;
+		state->avail -= to_copy;
+		buff += to_copy;
+		remaining -= to_copy;
+		/* ... if it's full, write it out. */
 		if (state->avail == 0) {
 			bytes_written = (a->client_writer)(&a->archive,
 			    a->client_data, state->buffer, state->buffer_size);
@@ -166,16 +173,26 @@
 			state->next = state->buffer;
 			state->avail = state->buffer_size;
 		}
+	}
 
-		/* Now we have space in the buffer; copy new data into it. */
-		to_copy = (remaining > state->avail) ?
-		    state->avail : remaining;
-		memcpy(state->next, buff, to_copy);
-		state->next += to_copy;
-		state->avail -= to_copy;
-		buff += to_copy;
-		remaining -= to_copy;
+	while (remaining > state->buffer_size) {
+		/* Write out full blocks directly to client. */
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data, buff, state->buffer_size);
+		if (bytes_written <= 0)
+			return (ARCHIVE_FATAL);
+		a->archive.raw_position += bytes_written;
+		buff += bytes_written;
+		remaining -= bytes_written;
 	}
+
+	if (remaining > 0) {
+		/* Copy last bit into copy buffer. */
+		memcpy(state->next, buff, remaining);
+		state->next += remaining;
+		state->avail -= remaining;
+	}
+
 	a->archive.file_position += length;
 	return (ARCHIVE_OK);
 }
Index: archive_string_sprintf.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_string_sprintf.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_string_sprintf.c -L lib/libarchive/archive_string_sprintf.c -u -r1.2 -r1.3
--- lib/libarchive/archive_string_sprintf.c
+++ lib/libarchive/archive_string_sprintf.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.9 2007/07/15 19:13:59 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.9.2.1 2008/05/10 06:46:09 kientzle Exp $");
 
 /*
  * The use of printf()-family functions can be troublesome
@@ -44,6 +44,16 @@
 #include "archive_string.h"
 #include "archive_private.h"
 
+void
+__archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	archive_string_vsprintf(as, fmt, ap);
+	va_end(ap);
+}
+
 /*
  * Like 'vsprintf', but ensures the target is big enough, resizing if
  * necessary.
Index: archive_string.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_string.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_string.c -L lib/libarchive/archive_string.c -u -r1.2 -r1.3
--- lib/libarchive/archive_string.c
+++ lib/libarchive/archive_string.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.11 2007/07/15 19:13:59 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.11.2.2 2008/12/08 17:18:37 kientzle Exp $");
 
 /*
  * Basic resizable string support, to simplify manipulating arbitrary-sized
@@ -37,6 +37,9 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
 
 #include "archive_private.h"
 #include "archive_string.h"
@@ -55,11 +58,27 @@
 void
 __archive_string_copy(struct archive_string *dest, struct archive_string *src)
 {
-	if (__archive_string_ensure(dest, src->length + 1) == NULL)
-		__archive_errx(1, "Out of memory");
-	memcpy(dest->s, src->s, src->length);
-	dest->length = src->length;
-	dest->s[dest->length] = 0;
+	if (src->length == 0)
+		dest->length = 0;
+	else {
+		if (__archive_string_ensure(dest, src->length + 1) == NULL)
+			__archive_errx(1, "Out of memory");
+		memcpy(dest->s, src->s, src->length);
+		dest->length = src->length;
+		dest->s[dest->length] = 0;
+	}
+}
+
+void
+__archive_string_concat(struct archive_string *dest, struct archive_string *src)
+{
+	if (src->length > 0) {
+		if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL)
+			__archive_errx(1, "Out of memory");
+		memcpy(dest->s + dest->length, src->s, src->length);
+		dest->length += src->length;
+		dest->s[dest->length] = 0;
+	}
 }
 
 void
@@ -67,21 +86,52 @@
 {
 	as->length = 0;
 	as->buffer_length = 0;
-	if (as->s != NULL)
+	if (as->s != NULL) {
 		free(as->s);
+		as->s = NULL;
+	}
 }
 
 /* Returns NULL on any allocation failure. */
 struct archive_string *
 __archive_string_ensure(struct archive_string *as, size_t s)
 {
+	/* If buffer is already big enough, don't reallocate. */
 	if (as->s && (s <= as->buffer_length))
 		return (as);
 
+	/*
+	 * Growing the buffer at least exponentially ensures that
+	 * append operations are always linear in the number of
+	 * characters appended.  Using a smaller growth rate for
+	 * larger buffers reduces memory waste somewhat at the cost of
+	 * a larger constant factor.
+	 */
 	if (as->buffer_length < 32)
+		/* Start with a minimum 32-character buffer. */
 		as->buffer_length = 32;
-	while (as->buffer_length < s)
+	else if (as->buffer_length < 8192)
+		/* Buffers under 8k are doubled for speed. */
 		as->buffer_length *= 2;
+	else {
+		/* Buffers 8k and over grow by at least 25% each time. */
+		size_t old_length = as->buffer_length;
+		as->buffer_length = (as->buffer_length * 5) / 4;
+		/* Be safe: If size wraps, release buffer and return NULL. */
+		if (as->buffer_length < old_length) {
+			free(as->s);
+			as->s = NULL;
+			return (NULL);
+		}
+	}
+	/*
+	 * The computation above is a lower limit to how much we'll
+	 * grow the buffer.  In any case, we have to grow it enough to
+	 * hold the request.
+	 */
+	if (as->buffer_length < s)
+		as->buffer_length = s;
+	/* Now we can reallocate the buffer. */
 	as->s = (char *)realloc(as->s, as->buffer_length);
 	if (as->s == NULL)
 		return (NULL);
@@ -124,3 +174,204 @@
 	__archive_strappend_char(as, digits[d % base]);
 	return (as);
 }
+
+/*
+ * Home-grown wctomb for UTF-8.
+ */
+static int
+my_wctomb_utf8(char *p, wchar_t wc)
+{
+	if (p == NULL)
+		/* UTF-8 doesn't use shift states. */
+		return (0);
+	if (wc <= 0x7f) {
+		p[0] = (char)wc;
+		return (1);
+	}
+	if (wc <= 0x7ff) {
+		p[0] = 0xc0 | ((wc >> 6) & 0x1f);
+		p[1] = 0x80 | (wc & 0x3f);
+		return (2);
+	}
+	if (wc <= 0xffff) {
+		p[0] = 0xe0 | ((wc >> 12) & 0x0f);
+		p[1] = 0x80 | ((wc >> 6) & 0x3f);
+		p[2] = 0x80 | (wc & 0x3f);
+		return (3);
+	}
+	if (wc <= 0x1fffff) {
+		p[0] = 0xf0 | ((wc >> 18) & 0x07);
+		p[1] = 0x80 | ((wc >> 12) & 0x3f);
+		p[2] = 0x80 | ((wc >> 6) & 0x3f);
+		p[3] = 0x80 | (wc & 0x3f);
+		return (4);
+	}
+	/* Unicode has no codes larger than 0x1fffff. */
+	/*
+	 * Awkward point:  UTF-8 <-> wchar_t conversions
+	 * can actually fail.
+	 */
+	return (-1);
+}
+
+static int
+my_wcstombs(struct archive_string *as, const wchar_t *w,
+    int (*func)(char *, wchar_t))
+{
+	int n;
+	char *p;
+	char buff[256];
+
+	/* Clear the shift state before starting. */
+	(*func)(NULL, L'\0');
+
+	/*
+	 * Convert one wide char at a time into 'buff', whenever that
+	 * fills, append it to the string.
+	 */
+	p = buff;
+	while (*w != L'\0') {
+		/* Flush the buffer when we have <=16 bytes free. */
+		/* (No encoding has a single character >16 bytes.) */
+		if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) {
+			*p = '\0';
+			archive_strcat(as, buff);
+			p = buff;
+		}
+		n = (*func)(p, *w++);
+		if (n == -1)
+			return (-1);
+		p += n;
+	}
+	*p = '\0';
+	archive_strcat(as, buff);
+	return (0);
+}
+
+/*
+ * Translates a wide character string into UTF-8 and appends
+ * to the archive_string.  Note: returns NULL if conversion fails.
+ */
+struct archive_string *
+__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w)
+{
+	if (my_wcstombs(as, w, my_wctomb_utf8))
+		return (NULL);
+	return (as);
+}
+
+/*
+ * Translates a wide character string into current locale character set
+ * and appends to the archive_string.  Note: returns NULL if conversion
+ * fails.
+ */
+struct archive_string *
+__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
+{
+#if HAVE_WCTOMB
+	if (my_wcstombs(as, w, wctomb))
+		return (NULL);
+#else
+	/* TODO: Can we do better than this?  Are there platforms
+	 * that have locale support but don't have wctomb()? */
+	if (my_wcstombs(as, w, my_wctomb_utf8))
+		return (NULL);
+#endif
+	return (as);
+}
+
+
+/*
+ * Home-grown mbtowc for UTF-8.  Some systems lack UTF-8
+ * (or even lack mbtowc()) and we need UTF-8 support for pax
+ * format.  So please don't replace this with a call to the
+ * standard mbtowc() function!
+ */
+static int
+my_mbtowc_utf8(wchar_t *pwc, const char *s, size_t n)
+{
+        int ch;
+
+	/* Standard behavior:  a NULL value for 's' just resets shift state. */
+        if (s == NULL)
+                return (0);
+	/* If length argument is zero, don't look at the first character. */
+	if (n <= 0)
+		return (-1);
+
+        /*
+	 * Decode 1-4 bytes depending on the value of the first byte.
+	 */
+        ch = (unsigned char)*s;
+	if (ch == 0) {
+		return (0); /* Standard:  return 0 for end-of-string. */
+	}
+	if ((ch & 0x80) == 0) {
+                *pwc = ch & 0x7f;
+		return (1);
+        }
+	if ((ch & 0xe0) == 0xc0) {
+		if (n < 2)
+			return (-1);
+		if ((s[1] & 0xc0) != 0x80) return (-1);
+                *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
+		return (2);
+        }
+	if ((ch & 0xf0) == 0xe0) {
+		if (n < 3)
+			return (-1);
+		if ((s[1] & 0xc0) != 0x80) return (-1);
+		if ((s[2] & 0xc0) != 0x80) return (-1);
+                *pwc = ((ch & 0x0f) << 12)
+		    | ((s[1] & 0x3f) << 6)
+		    | (s[2] & 0x3f);
+		return (3);
+        }
+	if ((ch & 0xf8) == 0xf0) {
+		if (n < 4)
+			return (-1);
+		if ((s[1] & 0xc0) != 0x80) return (-1);
+		if ((s[2] & 0xc0) != 0x80) return (-1);
+		if ((s[3] & 0xc0) != 0x80) return (-1);
+                *pwc = ((ch & 0x07) << 18)
+		    | ((s[1] & 0x3f) << 12)
+		    | ((s[2] & 0x3f) << 6)
+		    | (s[3] & 0x3f);
+		return (4);
+        }
+	/* Invalid first byte. */
+	return (-1);
+}
+
+/*
+ * Return a wide-character string by converting this archive_string
+ * from UTF-8.
+ */
+wchar_t *
+__archive_string_utf8_w(struct archive_string *as)
+{
+	wchar_t *ws, *dest;
+	const char *src;
+	int n;
+	int err;
+
+	ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t));
+	if (ws == NULL)
+		__archive_errx(1, "Out of memory");
+	err = 0;
+	dest = ws;
+	src = as->s;
+	while (*src != '\0') {
+		n = my_mbtowc_utf8(dest, src, 8);
+		if (n == 0)
+			break;
+		if (n < 0) {
+			free(ws);
+			return (NULL);
+		}
+		dest++;
+		src += n;
+	}
+	*dest++ = L'\0';
+	return (ws);
+}
--- /dev/null
+++ lib/libarchive/archive_entry_strmode.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/archive_entry_strmode.c,v 1.1.2.4 2008/08/10 04:32:47 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const char *
+archive_entry_strmode(struct archive_entry *entry)
+{
+	static const mode_t permbits[] =
+	    { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
+	char *bp = entry->strmode;
+	mode_t mode;
+	int i;
+
+	/* Fill in a default string, then selectively override. */
+	strcpy(bp, "?rwxrwxrwx ");
+
+	mode = archive_entry_mode(entry);
+	switch (archive_entry_filetype(entry)) {
+	case AE_IFREG:  bp[0] = '-'; break;
+	case AE_IFBLK:  bp[0] = 'b'; break;
+	case AE_IFCHR:  bp[0] = 'c'; break;
+	case AE_IFDIR:  bp[0] = 'd'; break;
+	case AE_IFLNK:  bp[0] = 'l'; break;
+	case AE_IFSOCK: bp[0] = 's'; break;
+	case AE_IFIFO:  bp[0] = 'p'; break;
+	default:
+		if (archive_entry_hardlink(entry) != NULL) {
+			bp[0] = 'h';
+			break;
+		}
+	}
+
+	for (i = 0; i < 9; i++)
+		if (!(mode & permbits[i]))
+			bp[i+1] = '-';
+
+	if (mode & S_ISUID) {
+		if (mode & 0100) bp[3] = 's';
+		else bp[3] = 'S';
+	}
+	if (mode & S_ISGID) {
+		if (mode & 0010) bp[6] = 's';
+		else bp[6] = 'S';
+	}
+	if (mode & S_ISVTX) {
+		if (mode & 0001) bp[9] = 't';
+		else bp[9] = 'T';
+	}
+	if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
+		bp[10] = '+';
+
+	return (bp);
+}
Index: archive_write_set_compression_gzip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_gzip.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_compression_gzip.c -L lib/libarchive/archive_write_set_compression_gzip.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_compression_gzip.c
+++ lib/libarchive/archive_write_set_compression_gzip.c
@@ -28,7 +28,7 @@
 /* Don't compile this if we don't have zlib. */
 #if HAVE_ZLIB_H
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.14 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.14.2.2 2008/03/12 04:20:30 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -106,6 +106,21 @@
 			return (ret);
 	}
 
+	/*
+	 * The next check is a temporary workaround until the gzip
+	 * code can be overhauled some.  The code should not require
+	 * that compressed_buffer_size == bytes_per_block.  Removing
+	 * this assumption will allow us to compress larger chunks at
+	 * a time, which should improve overall performance
+	 * marginally.  As a minor side-effect, such a cleanup would
+	 * allow us to support truly arbitrary block sizes.
+	 */
+	if (a->bytes_per_block < 10) {
+		archive_set_error(&a->archive, EINVAL,
+		    "GZip compressor requires a minimum 10 byte block size");
+		return (ARCHIVE_FATAL);
+	}
+
 	state = (struct private_data *)malloc(sizeof(*state));
 	if (state == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -114,6 +129,10 @@
 	}
 	memset(state, 0, sizeof(*state));
 
+	/*
+	 * See comment above.  We should set compressed_buffer_size to
+	 * max(bytes_per_block, 65536), but the code can't handle that yet.
+	 */
 	state->compressed_buffer_size = a->bytes_per_block;
 	state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
 	state->crc = crc32(0L, NULL, 0);
@@ -378,6 +397,10 @@
 			state->stream.avail_out = bytes_written;
 		}
 
+		/* If there's nothing to do, we're done. */
+		if (!finishing && state->stream.avail_in == 0)
+			return (ARCHIVE_OK);
+
 		ret = deflate(&(state->stream),
 		    finishing ? Z_FINISH : Z_NO_FLUSH );
 
@@ -396,7 +419,9 @@
 		default:
 			/* Any other return value indicates an error. */
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "GZip compression failed");
+			    "GZip compression failed:"
+			    " deflate() call returned status %d",
+			    ret);
 			return (ARCHIVE_FATAL);
 		}
 	}
Index: archive_write_private.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_private.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_write_private.h -L lib/libarchive/archive_write_private.h -u -r1.1 -r1.2
--- lib/libarchive/archive_write_private.h
+++ lib/libarchive/archive_write_private.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_write_private.h,v 1.2 2007/05/29 01:00:19 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_write_private.h,v 1.2.4.1 2008/05/21 04:12:29 kientzle Exp $
  */
 
 #ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED
@@ -82,13 +82,6 @@
 	} compressor;
 
 	/*
-	 * Again, write support is considerably simpler because there's
-	 * no need for an auction.
-	 */
-	int		  archive_format;
-	const char	 *archive_format_name;
-
-	/*
 	 * Pointers to format-specific functions for writing.  They're
 	 * initialized by archive_write_set_format_XXX() calls.
 	 */
Index: archive_string.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_string.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_string.h -L lib/libarchive/archive_string.h -u -r1.2 -r1.3
--- lib/libarchive/archive_string.h
+++ lib/libarchive/archive_string.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.9 2007/05/29 01:00:19 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.9.2.3 2008/12/08 17:18:37 kientzle Exp $
  *
  */
 
@@ -30,9 +30,15 @@
 #define	ARCHIVE_STRING_H_INCLUDED
 
 #include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>  /* required for wchar_t on some systems */
+#endif
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
 
 /*
  * Basic resizable/reusable string support a la Java's "StringBuffer."
@@ -60,16 +66,22 @@
 __archive_strappend_char(struct archive_string *, char);
 #define	archive_strappend_char __archive_strappend_char
 
-/* Append a char to an archive_string using UTF8. */
-struct archive_string *
-__archive_strappend_char_UTF8(struct archive_string *, int);
-#define	archive_strappend_char_UTF8 __archive_strappend_char_UTF8
-
 /* Append an integer in the specified base (2 <= base <= 16). */
 struct archive_string *
 __archive_strappend_int(struct archive_string *as, int d, int base);
 #define	archive_strappend_int __archive_strappend_int
 
+/* Convert a wide-char string to UTF-8 and append the result. */
+struct archive_string *
+__archive_strappend_w_utf8(struct archive_string *, const wchar_t *);
+#define	archive_strappend_w_utf8	__archive_strappend_w_utf8
+
+/* Convert a wide-char string to current locale and append the result. */
+/* Returns NULL if conversion fails. */
+struct archive_string *
+__archive_strappend_w_mbs(struct archive_string *, const wchar_t *);
+#define	archive_strappend_w_mbs	__archive_strappend_w_mbs
+
 /* Basic append operation. */
 struct archive_string *
 __archive_string_append(struct archive_string *as, const char *p, size_t s);
@@ -80,6 +92,12 @@
 #define archive_string_copy(dest, src) \
 	__archive_string_copy(dest, src)
 
+/* Concatenate one archive_string to another */
+void
+__archive_string_concat(struct archive_string *dest, struct archive_string *src);
+#define archive_string_concat(dest, src) \
+	__archive_string_concat(dest, src)
+
 /* Ensure that the underlying buffer is at least as large as the request. */
 struct archive_string *
 __archive_string_ensure(struct archive_string *, size_t);
@@ -95,7 +113,7 @@
 
 /* Copy a C string to an archive_string, resizing as necessary. */
 #define	archive_strcpy(as,p) \
-	((as)->length = 0, __archive_string_append((as), (p), strlen(p)))
+	((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p)))
 
 /* Copy a C string to an archive_string with limit, resizing as necessary. */
 #define	archive_strncpy(as,p,l) \
@@ -116,4 +134,12 @@
 	    va_list);
 #define	archive_string_vsprintf	__archive_string_vsprintf
 
+void	__archive_string_sprintf(struct archive_string *, const char *, ...);
+#define	archive_string_sprintf	__archive_string_sprintf
+
+/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.
+ * Returns NULL if conversion failed in any way. */
+wchar_t *__archive_string_utf8_w(struct archive_string *as);
+
+
 #endif
Index: archive_entry_private.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry_private.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_entry_private.h -L lib/libarchive/archive_entry_private.h -u -r1.1 -r1.2
--- lib/libarchive/archive_entry_private.h
+++ lib/libarchive/archive_entry_private.h
@@ -22,23 +22,31 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.1 2007/05/29 01:00:18 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.1.4.4 2008/11/28 20:08:47 kientzle Exp $
  */
 
 #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
 #define	ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
 
+#include "archive_string.h"
+
 /*
  * Handle wide character (i.e., Unicode) and non-wide character
  * strings transparently.
- *
  */
 
 struct aes {
-	const char *aes_mbs;
-	char *aes_mbs_alloc;
+	struct archive_string aes_mbs;
+	struct archive_string aes_utf8;
 	const wchar_t *aes_wcs;
-	wchar_t *aes_wcs_alloc;
+	/* Bitmap of which of the above are valid.  Because we're lazy
+	 * about malloc-ing and reusing the underlying storage, we
+	 * can't rely on NULL pointers to indicate whether a string
+	 * has been set. */
+	int aes_set;
+#define	AES_SET_MBS 1
+#define	AES_SET_UTF8 2
+#define	AES_SET_WCS 4
 };
 
 struct ae_acl {
@@ -128,7 +136,13 @@
 		dev_t		aest_rdevminor;
 	} ae_stat;
 
-
+	int ae_set; /* bitmap of fields that are currently set */
+#define	AE_SET_HARDLINK	1
+#define	AE_SET_SYMLINK	2
+#define	AE_SET_ATIME	4
+#define	AE_SET_CTIME	8
+#define	AE_SET_MTIME	16
+#define	AE_SET_SIZE	64
 
 	/*
 	 * Use aes here so that we get transparent mbs<->wcs conversions.
@@ -142,13 +156,21 @@
 	struct aes ae_symlink;		/* symlink contents */
 	struct aes ae_uname;		/* Name of owner */
 
+	/* Not used within libarchive; useful for some clients. */
+	struct aes ae_sourcepath;	/* Path this entry is sourced from. */
+
+	/* ACL support. */
 	struct ae_acl	*acl_head;
 	struct ae_acl	*acl_p;
 	int		 acl_state;	/* See acl_next for details. */
 	wchar_t		*acl_text_w;
 
+	/* extattr support. */
 	struct ae_xattr *xattr_head;
 	struct ae_xattr *xattr_p;
+
+	/* Miscellaneous. */
+	char		 strmode[12];
 };
 
 
Index: archive_read_support_format_iso9660.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_iso9660.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_support_format_iso9660.c -L lib/libarchive/archive_read_support_format_iso9660.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_support_format_iso9660.c
+++ lib/libarchive/archive_read_support_format_iso9660.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.23 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.23.2.4 2008/12/08 17:18:37 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -138,6 +138,15 @@
 #define PVD_reserved4_size 1
 #define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)
 #define PVD_application_data_size 512
+#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size)
+#define PVD_reserved5_size (2048 - PVD_reserved5_offset)
+
+/* TODO: It would make future maintenance easier to just hardcode the
+ * above values.  In particular, ECMA119 states the offsets as part of
+ * the standard.  That would eliminate the need for the following check.*/
+#if PVD_reserved5_offset != 1395
+#error PVD offset and size definitions are wrong.
+#endif
 
 /* Structure of an on-disk directory record. */
 /* Note:  ISO9660 stores each multi-byte integer twice, once in
@@ -178,23 +187,26 @@
 	uint64_t	 size;	/* File size in bytes. */
 	uint64_t	 ce_offset; /* Offset of CE */
 	uint64_t	 ce_size; /* Size of CE */
+	time_t		 birthtime; /* File created time. */
 	time_t		 mtime;	/* File last modified time. */
 	time_t		 atime;	/* File last accessed time. */
-	time_t		 ctime;	/* File creation time. */
+	time_t		 ctime;	/* File attribute change time. */
+	uint64_t	 rdev; /* Device number */
 	mode_t		 mode;
 	uid_t		 uid;
 	gid_t		 gid;
 	ino_t		 inode;
 	int		 nlinks;
-	char		*name; /* Null-terminated filename. */
+	struct archive_string name; /* Pathname */
+	char		 name_continues; /* Non-zero if name continues */
 	struct archive_string symlink;
+	char		 symlink_continues; /* Non-zero if link continues */
 };
 
 
 struct iso9660 {
 	int	magic;
 #define ISO9660_MAGIC   0x96609660
-	int	bid; /* If non-zero, return this as our bid. */
 	struct archive_string pathname;
 	char	seenRockridge; /* Set true if RR extensions are used. */
 	unsigned char	suspOffset;
@@ -210,6 +222,7 @@
 
 	uint64_t current_position;
 	ssize_t	logical_block_size;
+	uint64_t volume_size; /* Total size of volume in bytes. */
 
 	off_t	entry_sparse_offset;
 	int64_t	entry_bytes_remaining;
@@ -224,7 +237,9 @@
 static int	archive_read_format_iso9660_read_header(struct archive_read *,
 		    struct archive_entry *);
 static const char *build_pathname(struct archive_string *, struct file_info *);
+#if DEBUG
 static void	dump_isodirrec(FILE *, const unsigned char *isodirrec);
+#endif
 static time_t	time_from_tm(struct tm *);
 static time_t	isodate17(const unsigned char *);
 static time_t	isodate7(const unsigned char *);
@@ -238,6 +253,12 @@
 static void	parse_rockridge(struct iso9660 *iso9660,
 		    struct file_info *file, const unsigned char *start,
 		    const unsigned char *end);
+static void	parse_rockridge_NM1(struct file_info *,
+		    const unsigned char *, int);
+static void	parse_rockridge_SL1(struct file_info *,
+		    const unsigned char *, int);
+static void	parse_rockridge_TF1(struct file_info *,
+		    const unsigned char *, int);
 static void	release_file(struct iso9660 *, struct file_info *);
 static unsigned	toi(const void *p, int n);
 
@@ -255,7 +276,6 @@
 	}
 	memset(iso9660, 0, sizeof(*iso9660));
 	iso9660->magic = ISO9660_MAGIC;
-	iso9660->bid = -1; /* We haven't yet bid. */
 
 	r = __archive_read_register_format(a,
 	    iso9660,
@@ -280,12 +300,10 @@
 	ssize_t bytes_read;
 	const void *h;
 	const unsigned char *p;
+	int bid;
 
 	iso9660 = (struct iso9660 *)(a->format->data);
 
-	if (iso9660->bid >= 0)
-		return (iso9660->bid);
-
 	/*
 	 * Skip the first 32k (reserved area) and get the first
 	 * 8 sectors of the volume descriptor table.  Of course,
@@ -293,7 +311,7 @@
 	 */
 	bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048);
 	if (bytes_read < 32768 + 8*2048)
-	    return (iso9660->bid = -1);
+	    return (-1);
 	p = (const unsigned char *)h;
 
 	/* Skip the reserved area. */
@@ -302,29 +320,76 @@
 
 	/* Check each volume descriptor to locate the PVD. */
 	for (; bytes_read > 2048; bytes_read -= 2048, p += 2048) {
-		iso9660->bid = isPVD(iso9660, p);
-		if (iso9660->bid > 0)
-			return (iso9660->bid);
+		bid = isPVD(iso9660, p);
+		if (bid > 0)
+			return (bid);
 		if (*p == '\177') /* End-of-volume-descriptor marker. */
 			break;
 	}
 
 	/* We didn't find a valid PVD; return a bid of zero. */
-	iso9660->bid = 0;
-	return (iso9660->bid);
+	return (0);
 }
 
 static int
 isPVD(struct iso9660 *iso9660, const unsigned char *h)
 {
 	struct file_info *file;
+	int i;
 
-	if (h[0] != 1)
+	/* Type of the Primary Volume Descriptor must be 1. */
+	if (h[PVD_type_offset] != 1)
 		return (0);
-	if (memcmp(h+1, "CD001", 5) != 0)
+
+	/* ID must be "CD001" */
+	if (memcmp(h + PVD_id_offset, "CD001", 5) != 0)
 		return (0);
 
+	/* PVD version must be 1. */
+	if (h[PVD_version_offset] != 1)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (h[PVD_reserved1_offset] != 0)
+		return (0);
+
+	/* Reserved field must be 0. */
+	for (i = 0; i < PVD_reserved2_size; ++i)
+		if (h[PVD_reserved2_offset + i] != 0)
+			return (0);
+
+	/* Reserved field must be 0. */
+	for (i = 0; i < PVD_reserved3_size; ++i)
+		if (h[PVD_reserved3_offset + i] != 0)
+			return (0);
+
+	/* Logical block size must be > 0. */
+	/* I've looked at Ecma 119 and can't find any stronger
+	 * restriction on this field. */
 	iso9660->logical_block_size = toi(h + PVD_logical_block_size_offset, 2);
+	if (iso9660->logical_block_size <= 0)
+		return (0);
+
+	iso9660->volume_size = iso9660->logical_block_size
+	    * (uint64_t)toi(h + PVD_volume_space_size_offset, 4);
+
+	/* File structure version must be 1 for ISO9660/ECMA119. */
+	if (h[PVD_file_structure_version_offset] != 1)
+		return (0);
+
+
+	/* Reserved field must be 0. */
+	for (i = 0; i < PVD_reserved4_size; ++i)
+		if (h[PVD_reserved4_offset + i] != 0)
+			return (0);
+
+	/* Reserved field must be 0. */
+	for (i = 0; i < PVD_reserved5_size; ++i)
+		if (h[PVD_reserved5_offset + i] != 0)
+			return (0);
+
+	/* XXX TODO: Check other values for sanity; reject more
+	 * malformed PVDs. XXX */
 
 	/* Store the root directory in the pending list. */
 	file = parse_file_info(iso9660, NULL, h + PVD_root_directory_record_offset);
@@ -356,15 +421,27 @@
 	iso9660->entry_bytes_remaining = file->size;
 	iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */
 
+	if (file->offset + file->size > iso9660->volume_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "File is beyond end-of-media: %s", file->name);
+		iso9660->entry_bytes_remaining = 0;
+		iso9660->entry_sparse_offset = 0;
+		release_file(iso9660, file);
+		return (ARCHIVE_WARN);
+	}
+
 	/* Set up the entry structure with information about this entry. */
 	archive_entry_set_mode(entry, file->mode);
 	archive_entry_set_uid(entry, file->uid);
 	archive_entry_set_gid(entry, file->gid);
 	archive_entry_set_nlink(entry, file->nlinks);
 	archive_entry_set_ino(entry, file->inode);
+	/* archive_entry_set_birthtime(entry, file->birthtime, 0); */
 	archive_entry_set_mtime(entry, file->mtime, 0);
 	archive_entry_set_ctime(entry, file->ctime, 0);
 	archive_entry_set_atime(entry, file->atime, 0);
+	/* N.B.: Rock Ridge supports 64-bit device numbers. */
+	archive_entry_set_rdev(entry, (dev_t)file->rdev);
 	archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
 	archive_string_empty(&iso9660->pathname);
 	archive_entry_set_pathname(entry,
@@ -537,13 +614,7 @@
 	file->mtime = isodate7(isodirrec + DR_date_offset);
 	file->ctime = file->atime = file->mtime;
 	name_len = (size_t)*(const unsigned char *)(isodirrec + DR_name_len_offset);
-	file->name = (char *)malloc(name_len + 1);
-	if (file->name == NULL) {
-		free(file);
-		return (NULL);
-	}
-	memcpy(file->name, isodirrec + DR_name_offset, name_len);
-	file->name[name_len] = '\0';
+	archive_strncpy(&file->name, isodirrec + DR_name_offset, name_len);
 	flags = *(isodirrec + DR_flags_offset);
 	if (flags & 0x02)
 		file->mode = AE_IFDIR | 0700;
@@ -563,6 +634,7 @@
 		parse_rockridge(iso9660, file, rr_start, rr_end);
 	}
 
+#if DEBUG
 	/* DEBUGGING: Warn about attributes I don't yet fully support. */
 	if ((flags & ~0x02) != 0) {
 		fprintf(stderr, "\n ** Unrecognized flag: ");
@@ -585,7 +657,7 @@
 		dump_isodirrec(stderr, isodirrec);
 		fprintf(stderr, "\n");
 	}
-
+#endif
 	return (file);
 }
 
@@ -597,8 +669,11 @@
 		struct file_info **new_pending_files;
 		int new_size = iso9660->pending_files_allocated * 2;
 
-		if (new_size < 1024)
+		if (iso9660->pending_files_allocated < 1024)
 			new_size = 1024;
+		/* Overflow might keep us from growing the list. */
+		if (new_size <= iso9660->pending_files_allocated)
+			__archive_errx(1, "Out of memory");
 		new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0]));
 		if (new_pending_files == NULL)
 			__archive_errx(1, "Out of memory");
@@ -622,6 +697,7 @@
 	while (p + 4 < end  /* Enough space for another entry. */
 	    && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
 	    && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
+	    && p[2] >= 4 /* Sanity-check length. */
 	    && p + p[2] <= end) { /* Sanity-check length. */
 		const unsigned char *data = p + 4;
 		int data_length = p[2] - 4;
@@ -634,53 +710,54 @@
 		 */
 		switch(p[0]) {
 		case 'C':
-			if (p[0] == 'C' && p[1] == 'E' && version == 1) {
-				/*
-				 * CE extension comprises:
-				 *   8 byte sector containing extension
-				 *   8 byte offset w/in above sector
-				 *   8 byte length of continuation
-				 */
-				file->ce_offset = toi(data, 4)
-				    * iso9660->logical_block_size
-				    + toi(data + 8, 4);
-				file->ce_size = toi(data + 16, 4);
+			if (p[0] == 'C' && p[1] == 'E') {
+				if (version == 1 && data_length == 24) {
+					/*
+					 * CE extension comprises:
+					 *   8 byte sector containing extension
+					 *   8 byte offset w/in above sector
+					 *   8 byte length of continuation
+					 */
+					file->ce_offset = (uint64_t)toi(data, 4)
+					    * iso9660->logical_block_size
+					    + toi(data + 8, 4);
+					file->ce_size = toi(data + 16, 4);
+					/* If the result is rediculous,
+					 * ignore it. */
+					if (file->ce_offset + file->ce_size
+					    > iso9660->volume_size) {
+						file->ce_offset = 0;
+						file->ce_size = 0;
+					}
+				}
 				break;
 			}
 			/* FALLTHROUGH */
 		case 'N':
-			if (p[0] == 'N' && p[1] == 'M' && version == 1
-				&& *data == 0) {
-				/* NM extension with flag byte == 0 */
-				/*
-				 * NM extension comprises:
-				 *   one byte flag
-				 *   rest is long name
-				 */
-				/* TODO: Obey flags. */
-				char *old_name = file->name;
-
-				data++;  /* Skip flag byte. */
-				data_length--;
-				file->name = (char *)malloc(data_length + 1);
-				if (file->name != NULL) {
-					free(old_name);
-					memcpy(file->name, data, data_length);
-					file->name[data_length] = '\0';
-				} else
-					file->name = old_name;
+			if (p[0] == 'N' && p[1] == 'M') {
+				if (version == 1)
+					parse_rockridge_NM1(file,
+					    data, data_length);
 				break;
 			}
 			/* FALLTHROUGH */
 		case 'P':
-			if (p[0] == 'P' && p[1] == 'D' && version == 1) {
+			if (p[0] == 'P' && p[1] == 'D') {
 				/*
 				 * PD extension is padding;
 				 * contents are always ignored.
 				 */
 				break;
 			}
-			if (p[0] == 'P' && p[1] == 'X' && version == 1) {
+			if (p[0] == 'P' && p[1] == 'N') {
+				if (version == 1 && data_length == 16) {
+					file->rdev = toi(data,4);
+					file->rdev <<= 32;
+					file->rdev |= toi(data + 8, 4);
+				}
+				break;
+			}
+			if (p[0] == 'P' && p[1] == 'X') {
 				/*
 				 * PX extension comprises:
 				 *   8 bytes for mode,
@@ -689,12 +766,22 @@
 				 *   8 bytes for gid,
 				 *   8 bytes for inode.
 				 */
-				if (data_length == 32) {
-					file->mode = toi(data, 4);
-					file->nlinks = toi(data + 8, 4);
-					file->uid = toi(data + 16, 4);
-					file->gid = toi(data + 24, 4);
-					file->inode = toi(data + 32, 4);
+				if (version == 1) {
+					if (data_length >= 8)
+						file->mode
+						    = toi(data, 4);
+					if (data_length >= 16)
+						file->nlinks
+						    = toi(data + 8, 4);
+					if (data_length >= 24)
+						file->uid
+						    = toi(data + 16, 4);
+					if (data_length >= 32)
+						file->gid
+						    = toi(data + 24, 4);
+					if (data_length >= 40)
+						file->inode
+						    = toi(data + 32, 4);
 				}
 				break;
 			}
@@ -711,56 +798,14 @@
 			}
 			/* FALLTHROUGH */
 		case 'S':
-			if (p[0] == 'S' && p[1] == 'L' && version == 1
-			    && *data == 0) {
-				int cont = 1;
-				/* SL extension with flags == 0 */
-				/* TODO: handle non-zero flag values. */
-				data++;  /* Skip flag byte. */
-				data_length--;
-				while (data_length > 0) {
-					unsigned char flag = *data++;
-					unsigned char nlen = *data++;
-					data_length -= 2;
-
-					if (cont == 0)
-						archive_strcat(&file->symlink, "/");
-					cont = 0;
-
-					switch(flag) {
-					case 0x01: /* Continue */
-						archive_strncat(&file->symlink,
-						    (const char *)data, nlen);
-						cont = 1;
-						break;
-					case 0x02: /* Current */
-						archive_strcat(&file->symlink, ".");
-						break;
-					case 0x04: /* Parent */
-						archive_strcat(&file->symlink, "..");
-						break;
-					case 0x08: /* Root */
-					case 0x10: /* Volume root */
-						archive_string_empty(&file->symlink);
-						break;
-					case 0x20: /* Hostname */
-						archive_strcat(&file->symlink, "hostname");
-						break;
-					case 0:
-						archive_strncat(&file->symlink,
-						    (const char *)data, nlen);
-						break;
-					default:
-						/* TODO: issue a warning ? */
-						break;
-					}
-					data += nlen;
-					data_length -= nlen;
-				}
+			if (p[0] == 'S' && p[1] == 'L') {
+				if (version == 1)
+					parse_rockridge_SL1(file,
+					    data, data_length);
 				break;
 			}
 			if (p[0] == 'S' && p[1] == 'P'
-			    && version == 1 && data_length == 7
+			    && version == 1 && data_length == 3
 			    && data[0] == (unsigned char)'\xbe'
 			    && data[1] == (unsigned char)'\xef') {
 				/*
@@ -796,66 +841,27 @@
 				return;
 			}
 		case 'T':
-			if (p[0] == 'T' && p[1] == 'F' && version == 1) {
-				char flag = data[0];
-				/*
-				 * TF extension comprises:
-				 *   one byte flag
-				 *   create time (optional)
-				 *   modify time (optional)
-				 *   access time (optional)
-				 *   attribute time (optional)
-				 *  Time format and presence of fields
-				 *  is controlled by flag bits.
-				 */
-				data++;
-				if (flag & 0x80) {
-					/* Use 17-byte time format. */
-					if (flag & 1) /* Create time. */
-						data += 17;
-					if (flag & 2) { /* Modify time. */
-						file->mtime = isodate17(data);
-						data += 17;
-					}
-					if (flag & 4) { /* Access time. */
-						file->atime = isodate17(data);
-						data += 17;
-					}
-					if (flag & 8) { /* Attribute time. */
-						file->ctime = isodate17(data);
-						data += 17;
-					}
-				} else {
-					/* Use 7-byte time format. */
-					if (flag & 1) /* Create time. */
-						data += 7;
-					if (flag & 2) { /* Modify time. */
-						file->mtime = isodate7(data);
-						data += 7;
-					}
-					if (flag & 4) { /* Access time. */
-						file->atime = isodate7(data);
-						data += 7;
-					}
-					if (flag & 8) { /* Attribute time. */
-						file->ctime = isodate7(data);
-						data += 7;
-					}
-				}
+			if (p[0] == 'T' && p[1] == 'F') {
+				if (version == 1)
+					parse_rockridge_TF1(file,
+					    data, data_length);
 				break;
 			}
 			/* FALLTHROUGH */
 		default:
 			/* The FALLTHROUGHs above leave us here for
 			 * any unsupported extension. */
+#if DEBUG
 			{
 				const unsigned char *t;
-				fprintf(stderr, "\nUnsupported RRIP extension for %s\n", file->name);
+				fprintf(stderr, "\nUnsupported RRIP extension for %s\n", file->name.s);
 				fprintf(stderr, " %c%c(%d):", p[0], p[1], data_length);
 				for (t = data; t < data + data_length && t < data + 16; t++)
 					fprintf(stderr, " %02x", *t);
 				fprintf(stderr, "\n");
 			}
+#endif
+			break;
 		}
 
 
@@ -865,14 +871,222 @@
 }
 
 static void
+parse_rockridge_NM1(struct file_info *file, const unsigned char *data,
+    int data_length)
+{
+	if (!file->name_continues)
+		archive_string_empty(&file->name);
+	file->name_continues = 0;
+	if (data_length < 1)
+		return;
+	/*
+	 * NM version 1 extension comprises:
+	 *   1 byte flag, value is one of:
+	 *     = 0: remainder is name
+	 *     = 1: remainder is name, next NM entry continues name
+	 *     = 2: "."
+	 *     = 4: ".."
+	 *     = 32: Implementation specific
+	 *     All other values are reserved.
+	 */
+	switch(data[0]) {
+	case 0:
+		if (data_length < 2)
+			return;
+		archive_strncat(&file->name, data + 1, data_length - 1);
+		break;
+	case 1:
+		if (data_length < 2)
+			return;
+		archive_strncat(&file->name, data + 1, data_length - 1);
+		file->name_continues = 1;
+		break;
+	case 2:
+		archive_strcat(&file->name, ".");
+		break;
+	case 4:
+		archive_strcat(&file->name, "..");
+		break;
+	default:
+		return;
+	}
+
+}
+
+static void
+parse_rockridge_TF1(struct file_info *file, const unsigned char *data,
+    int data_length)
+{
+	char flag;
+	/*
+	 * TF extension comprises:
+	 *   one byte flag
+	 *   create time (optional)
+	 *   modify time (optional)
+	 *   access time (optional)
+	 *   attribute time (optional)
+	 *  Time format and presence of fields
+	 *  is controlled by flag bits.
+	 */
+	if (data_length < 1)
+		return;
+	flag = data[0];
+	++data;
+	--data_length;
+	if (flag & 0x80) {
+		/* Use 17-byte time format. */
+		if ((flag & 1) && data_length >= 17) {
+			/* Create time. */
+			file->birthtime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 2) && data_length >= 17) {
+			/* Modify time. */
+			file->mtime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 4) && data_length >= 17) {
+			/* Access time. */
+			file->atime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 8) && data_length >= 17) {
+			/* Attribute change time. */
+			file->ctime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+	} else {
+		/* Use 7-byte time format. */
+		if ((flag & 1) && data_length >= 7) {
+			/* Create time. */
+			file->birthtime = isodate17(data);
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 2) && data_length >= 7) {
+			/* Modify time. */
+			file->mtime = isodate7(data);
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 4) && data_length >= 7) {
+			/* Access time. */
+			file->atime = isodate7(data);
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 8) && data_length >= 7) {
+			/* Attribute change time. */
+			file->ctime = isodate7(data);
+			data += 7;
+			data_length -= 7;
+		}
+	}
+}
+
+static void
+parse_rockridge_SL1(struct file_info *file, const unsigned char *data,
+    int data_length)
+{
+	int component_continues = 1;
+
+	if (!file->symlink_continues)
+		archive_string_empty(&file->symlink);
+	else
+		archive_strcat(&file->symlink, "/");
+	file->symlink_continues = 0;
+
+	/*
+	 * Defined flag values:
+	 *  0: This is the last SL record for this symbolic link
+	 *  1: this symbolic link field continues in next SL entry
+	 *  All other values are reserved.
+	 */
+	if (data_length < 1)
+		return;
+	switch(*data) {
+	case 0:
+		break;
+	case 1:
+		file->symlink_continues = 1;
+		break;
+	default:
+		return;
+	}
+	++data;  /* Skip flag byte. */
+	--data_length;
+
+	/*
+	 * SL extension body stores "components".
+	 * Basically, this is a complicated way of storing
+	 * a POSIX path.  It also interferes with using
+	 * symlinks for storing non-path data. <sigh>
+	 *
+	 * Each component is 2 bytes (flag and length)
+	 * possibly followed by name data.
+	 */
+	while (data_length >= 2) {
+		unsigned char flag = *data++;
+		unsigned char nlen = *data++;
+		data_length -= 2;
+
+		if (!component_continues)
+			archive_strcat(&file->symlink, "/");
+		component_continues = 0;
+
+		switch(flag) {
+		case 0: /* Usual case, this is text. */
+			if (data_length < nlen)
+				return;
+			archive_strncat(&file->symlink,
+			    (const char *)data, nlen);
+			break;
+		case 0x01: /* Text continues in next component. */
+			if (data_length < nlen)
+				return;
+			archive_strncat(&file->symlink,
+			    (const char *)data, nlen);
+			component_continues = 1;
+			break;
+		case 0x02: /* Current dir. */
+			archive_strcat(&file->symlink, ".");
+			break;
+		case 0x04: /* Parent dir. */
+			archive_strcat(&file->symlink, "..");
+			break;
+		case 0x08: /* Root of filesystem. */
+			archive_string_empty(&file->symlink);
+			archive_strcat(&file->symlink, "/");
+			break;
+		case 0x10: /* Undefined (historically "volume root" */
+			archive_string_empty(&file->symlink);
+			archive_strcat(&file->symlink, "ROOT");
+			break;
+		case 0x20: /* Undefined (historically "hostname") */
+			archive_strcat(&file->symlink, "hostname");
+			break;
+		default:
+			/* TODO: issue a warning ? */
+			return;
+		}
+		data += nlen;
+		data_length -= nlen;
+	}
+}
+
+
+static void
 release_file(struct iso9660 *iso9660, struct file_info *file)
 {
 	struct file_info *parent;
 
 	if (file->refcount == 0) {
 		parent = file->parent;
-		if (file->name)
-			free(file->name);
+		archive_string_free(&file->name);
 		archive_string_free(&file->symlink);
 		free(file);
 		if (parent != NULL) {
@@ -897,11 +1111,18 @@
 
 		/* CE area precedes actual file data? Ignore it. */
 		if (file->ce_offset > file->offset) {
-fprintf(stderr, " *** Discarding CE data.\n");
+#if DEBUG
+			fprintf(stderr, " *** Discarding CE data.\n");
+#endif
 			file->ce_offset = 0;
 			file->ce_size = 0;
 		}
 
+		/* Don't waste time seeking for zero-length bodies. */
+		if (file->size == 0) {
+			file->offset = iso9660->current_position;
+		}
+
 		/* If CE exists, find and read it now. */
 		if (file->ce_offset > 0)
 			offset = file->ce_offset;
@@ -1035,64 +1256,40 @@
 	return (time_from_tm(&tm));
 }
 
-/*
- * timegm() converts a struct tm to a time_t, except it isn't standard,
- * so I provide my own function here that (ideally) is just a wrapper
- * for timegm().
- */
 static time_t
 time_from_tm(struct tm *t)
 {
 #if HAVE_TIMEGM
+	/* Use platform timegm() if available. */
 	return (timegm(t));
-#elif HAVE_STRUCT_TM_TM_GMTOFF
-	/*
-	 * Unfortunately, timegm() isn't standard.  The standard
-	 * mktime() function is a close match, except that it uses
-	 * local timezone instead of GMT.  You can compensate for
-	 * this by adding the timezone and DST offsets back in, at
-	 * the cost of two calls to mktime().
-	 */
-	mktime(t); /* Normalize the time and get the TZ offset. */
-	t->tm_sec += t->tm_gmtoff; /* Try to adjust for the timezone and DST.*/
-	if (t->tm_isdst)
-		t->tm_hour -= 1;
-	return (mktime(t)); /* Re-convert. */
 #else
-	/*
-	 * If you don't have tm_gmtoff, let's try resetting the timezone
-	 * (yecch!).
-	 */
-	time_t ret;
-	char *tz;
-
-	tz = getenv("TZ");
-	setenv("TZ", "UTC 0", 1);
-	tzset();
-	ret = mktime(t);
-	if (tz)
-	    setenv("TZ", tz, 1);
-	else
-	    unsetenv("TZ");
-	tzset();
-	return ret;
+	/* 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 const char *
 build_pathname(struct archive_string *as, struct file_info *file)
 {
-	if (file->parent != NULL && file->parent->name[0] != '\0') {
+	if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {
 		build_pathname(as, file->parent);
 		archive_strcat(as, "/");
 	}
-	if (file->name[0] == '\0')
+	if (archive_strlen(&file->name) == 0)
 		archive_strcat(as, ".");
 	else
-		archive_strcat(as, file->name);
+		archive_string_concat(as, &file->name);
 	return (as->s);
 }
 
+#if DEBUG
 static void
 dump_isodirrec(FILE *out, const unsigned char *isodirrec)
 {
@@ -1117,3 +1314,4 @@
 	fprintf(out, " `%.*s'",
 	    toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset);
 }
+#endif
Index: archive_write_open_filename.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_open_filename.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_open_filename.c -L lib/libarchive/archive_write_open_filename.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_open_filename.c
+++ lib/libarchive/archive_write_open_filename.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19.4.1 2008/03/12 04:20:30 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -47,6 +47,10 @@
 
 #include "archive.h"
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
 struct write_file_data {
 	int		fd;
 	char		filename[1];
@@ -95,7 +99,7 @@
 	struct stat st;
 
 	mine = (struct write_file_data *)client_data;
-	flags = O_WRONLY | O_CREAT | O_TRUNC;
+	flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
 
 	/*
 	 * Open the file.
Index: archive_entry.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_entry.h -L lib/libarchive/archive_entry.h -u -r1.2 -r1.3
--- lib/libarchive/archive_entry.h
+++ lib/libarchive/archive_entry.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2008 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -22,26 +22,80 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.23 2007/07/15 19:10:34 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.23.2.4 2008/11/28 20:08:47 kientzle Exp $
  */
 
 #ifndef ARCHIVE_ENTRY_H_INCLUDED
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
+/*
+ * Note: archive_entry.h is for use outside of libarchive; the
+ * configuration headers (config.h, archive_platform.h, etc.) are
+ * purely internal.  Do NOT use HAVE_XXX configuration macros to
+ * control the behavior of this header!  If you must conditionalize,
+ * use predefined compiler and/or platform macros.
+ */
+
 #include <sys/types.h>
 #include <stddef.h>  /* for wchar_t */
 #include <time.h>
+
+/* Get appropriate definitions of standard POSIX-style types. */
+/* These should match the types used in 'struct stat' */
+#ifdef _WIN32
+#define	__LA_UID_T	unsigned int
+#define	__LA_GID_T	unsigned int
+#define	__LA_DEV_T	unsigned int
+#define	__LA_MODE_T	unsigned short
+#else
 #include <unistd.h>
+#define	__LA_UID_T	uid_t
+#define	__LA_GID_T	gid_t
+#define	__LA_DEV_T	dev_t
+#define	__LA_MODE_T	mode_t
+#endif
+
+/*
+ * XXX Is this defined for all Windows compilers?  If so, in what
+ * header?  It would be nice to remove the __LA_INO_T indirection and
+ * just use plain ino_t everywhere.  Likewise for the other types just
+ * above.
+ */
+#define	__LA_INO_T	ino_t
+
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib.  The default here assumes you're building a DLL.  Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+#  ifdef __GNUC__
+#   define __LA_DECL	__attribute__((dllexport)) extern
+#  else
+#   define __LA_DECL	__declspec(dllexport)
+#  endif
+# else
+#  ifdef __GNUC__
+#   define __LA_DECL	__attribute__((dllimport)) extern
+#  else
+#   define __LA_DECL	__declspec(dllimport)
+#  endif
+# endif
+#else
+/* Static libraries on all platforms and shared libraries on non-Windows. */
+# define __LA_DECL
+#endif
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-
 /*
  * Description of an archive entry.
  *
- * Basically, a "struct stat" with a few text fields added in.
+ * You can think of this as "struct stat" with some text fields added in.
  *
  * TODO: Add "comment", "charset", and possibly other entries that are
  * supported by "pax interchange" format.  However, GNU, ustar, cpio,
@@ -57,11 +111,30 @@
 struct archive_entry;
 
 /*
- * File-type constants.  These are returned from archive_entry_filetype().
+ * File-type constants.  These are returned from archive_entry_filetype()
+ * and passed to archive_entry_set_filetype().
+ *
+ * These values match S_XXX defines on every platform I've checked,
+ * including Windows, AIX, Linux, Solaris, and BSD.  They're
+ * (re)defined here because platforms generally don't define the ones
+ * they don't support.  For example, Windows doesn't define S_IFLNK or
+ * S_IFBLK.  Instead of having a mass of conditional logic and system
+ * checks to define any S_XXX values that aren't supported locally,
+ * I've just defined a new set of such constants so that
+ * libarchive-based applications can manipulate and identify archive
+ * entries properly even if the hosting platform can't store them on
+ * disk.
+ *
+ * These values are also used directly within some portable formats,
+ * such as cpio.  If you find a platform that varies from these, the
+ * correct solution is to leave these alone and translate from these
+ * portable values to platform-native values when entries are read from
+ * or written to disk.
  */
 #define	AE_IFMT		0170000
 #define	AE_IFREG	0100000
 #define	AE_IFLNK	0120000
+#define	AE_IFSOCK	0140000
 #define	AE_IFCHR	0020000
 #define	AE_IFBLK	0060000
 #define	AE_IFDIR	0040000
@@ -71,95 +144,134 @@
  * Basic object manipulation
  */
 
-struct archive_entry	*archive_entry_clear(struct archive_entry *);
+__LA_DECL struct archive_entry	*archive_entry_clear(struct archive_entry *);
 /* The 'clone' function does a deep copy; all of the strings are copied too. */
-struct archive_entry	*archive_entry_clone(struct archive_entry *);
-void			 archive_entry_free(struct archive_entry *);
-struct archive_entry	*archive_entry_new(void);
+__LA_DECL struct archive_entry	*archive_entry_clone(struct archive_entry *);
+__LA_DECL void			 archive_entry_free(struct archive_entry *);
+__LA_DECL struct archive_entry	*archive_entry_new(void);
 
 /*
  * Retrieve fields from an archive_entry.
- */
-
-time_t			 archive_entry_atime(struct archive_entry *);
-long			 archive_entry_atime_nsec(struct archive_entry *);
-time_t			 archive_entry_ctime(struct archive_entry *);
-long			 archive_entry_ctime_nsec(struct archive_entry *);
-dev_t			 archive_entry_dev(struct archive_entry *);
-dev_t			 archive_entry_devmajor(struct archive_entry *);
-dev_t			 archive_entry_devminor(struct archive_entry *);
-mode_t			 archive_entry_filetype(struct archive_entry *);
-void			 archive_entry_fflags(struct archive_entry *,
-			     unsigned long *set, unsigned long *clear);
-const char		*archive_entry_fflags_text(struct archive_entry *);
-gid_t			 archive_entry_gid(struct archive_entry *);
-const char		*archive_entry_gname(struct archive_entry *);
-const wchar_t		*archive_entry_gname_w(struct archive_entry *);
-const char		*archive_entry_hardlink(struct archive_entry *);
-const wchar_t		*archive_entry_hardlink_w(struct archive_entry *);
-ino_t			 archive_entry_ino(struct archive_entry *);
-mode_t			 archive_entry_mode(struct archive_entry *);
-time_t			 archive_entry_mtime(struct archive_entry *);
-long			 archive_entry_mtime_nsec(struct archive_entry *);
-unsigned int		 archive_entry_nlink(struct archive_entry *);
-const char		*archive_entry_pathname(struct archive_entry *);
-const wchar_t		*archive_entry_pathname_w(struct archive_entry *);
-dev_t			 archive_entry_rdev(struct archive_entry *);
-dev_t			 archive_entry_rdevmajor(struct archive_entry *);
-dev_t			 archive_entry_rdevminor(struct archive_entry *);
-int64_t			 archive_entry_size(struct archive_entry *);
-const char		*archive_entry_symlink(struct archive_entry *);
-const wchar_t		*archive_entry_symlink_w(struct archive_entry *);
-uid_t			 archive_entry_uid(struct archive_entry *);
-const char		*archive_entry_uname(struct archive_entry *);
-const wchar_t		*archive_entry_uname_w(struct archive_entry *);
+ *
+ * There are a number of implicit conversions among these fields.  For
+ * example, if a regular string field is set and you read the _w wide
+ * character field, the entry will implicitly convert narrow-to-wide
+ * using the current locale.  Similarly, dev values are automatically
+ * updated when you write devmajor or devminor and vice versa.
+ *
+ * In addition, fields can be "set" or "unset."  Unset string fields
+ * return NULL, non-string fields have _is_set() functions to test
+ * whether they've been set.  You can "unset" a string field by
+ * assigning NULL; non-string fields have _unset() functions to
+ * unset them.
+ *
+ * Note: There is one ambiguity in the above; string fields will
+ * also return NULL when implicit character set conversions fail.
+ * This is usually what you want.
+ */
+__LA_DECL time_t	 archive_entry_atime(struct archive_entry *);
+__LA_DECL long		 archive_entry_atime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_atime_is_set(struct archive_entry *);
+__LA_DECL time_t	 archive_entry_ctime(struct archive_entry *);
+__LA_DECL long		 archive_entry_ctime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_ctime_is_set(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_dev(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_devmajor(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_devminor(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_filetype(struct archive_entry *);
+__LA_DECL void		 archive_entry_fflags(struct archive_entry *,
+			    unsigned long * /* set */,
+			    unsigned long * /* clear */);
+__LA_DECL const char	*archive_entry_fflags_text(struct archive_entry *);
+__LA_DECL __LA_GID_T	 archive_entry_gid(struct archive_entry *);
+__LA_DECL const char	*archive_entry_gname(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_gname_w(struct archive_entry *);
+__LA_DECL const char	*archive_entry_hardlink(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_hardlink_w(struct archive_entry *);
+__LA_DECL __LA_INO_T	 archive_entry_ino(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_mode(struct archive_entry *);
+__LA_DECL time_t	 archive_entry_mtime(struct archive_entry *);
+__LA_DECL long		 archive_entry_mtime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_mtime_is_set(struct archive_entry *);
+__LA_DECL unsigned int	 archive_entry_nlink(struct archive_entry *);
+__LA_DECL const char	*archive_entry_pathname(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_pathname_w(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_rdev(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_rdevmajor(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_rdevminor(struct archive_entry *);
+__LA_DECL const char	*archive_entry_sourcepath(struct archive_entry *);
+__LA_DECL int64_t	 archive_entry_size(struct archive_entry *);
+__LA_DECL int		 archive_entry_size_is_set(struct archive_entry *);
+__LA_DECL const char	*archive_entry_strmode(struct archive_entry *);
+__LA_DECL const char	*archive_entry_symlink(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_symlink_w(struct archive_entry *);
+__LA_DECL __LA_UID_T	 archive_entry_uid(struct archive_entry *);
+__LA_DECL const char	*archive_entry_uname(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_uname_w(struct archive_entry *);
 
 /*
  * Set fields in an archive_entry.
  *
  * Note that string 'set' functions do not copy the string, only the pointer.
  * In contrast, 'copy' functions do copy the object pointed to.
+ *
+ * Note: As of libarchive 2.4, 'set' functions do copy the string and
+ * are therefore exact synonyms for the 'copy' versions.  The 'copy'
+ * names will be retired in libarchive 3.0.
  */
 
-void	archive_entry_set_atime(struct archive_entry *, time_t, long);
-void	archive_entry_set_ctime(struct archive_entry *, time_t, long);
-void	archive_entry_set_dev(struct archive_entry *, dev_t);
-void	archive_entry_set_devmajor(struct archive_entry *, dev_t);
-void	archive_entry_set_devminor(struct archive_entry *, dev_t);
-void	archive_entry_set_filetype(struct archive_entry *, unsigned int);
-void	archive_entry_set_fflags(struct archive_entry *,
-	    unsigned long set, unsigned long clear);
+__LA_DECL void	archive_entry_set_atime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_atime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_ctime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_dev(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_devmajor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_devminor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_filetype(struct archive_entry *, unsigned int);
+__LA_DECL void	archive_entry_set_fflags(struct archive_entry *,
+	    unsigned long /* set */, unsigned long /* clear */);
 /* Returns pointer to start of first invalid token, or NULL if none. */
 /* Note that all recognized tokens are processed, regardless. */
-const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
+	    const char *);
+__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
 	    const wchar_t *);
-void	archive_entry_set_gid(struct archive_entry *, gid_t);
-void	archive_entry_set_gname(struct archive_entry *, const char *);
-void	archive_entry_copy_gname(struct archive_entry *, const char *);
-void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
-void	archive_entry_set_hardlink(struct archive_entry *, const char *);
-void	archive_entry_copy_hardlink(struct archive_entry *, const char *);
-void	archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
-void	archive_entry_set_ino(struct archive_entry *, unsigned long);
-void	archive_entry_set_link(struct archive_entry *, const char *);
-void	archive_entry_set_mode(struct archive_entry *, mode_t);
-void	archive_entry_set_mtime(struct archive_entry *, time_t, long);
-void	archive_entry_set_nlink(struct archive_entry *, unsigned int);
-void	archive_entry_set_pathname(struct archive_entry *, const char *);
-void	archive_entry_copy_pathname(struct archive_entry *, const char *);
-void	archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
-void	archive_entry_set_rdev(struct archive_entry *, dev_t);
-void	archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
-void	archive_entry_set_rdevminor(struct archive_entry *, dev_t);
-void	archive_entry_set_size(struct archive_entry *, int64_t);
-void	archive_entry_set_symlink(struct archive_entry *, const char *);
-void	archive_entry_copy_symlink(struct archive_entry *, const char *);
-void	archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
-void	archive_entry_set_uid(struct archive_entry *, uid_t);
-void	archive_entry_set_uname(struct archive_entry *, const char *);
-void	archive_entry_copy_uname(struct archive_entry *, const char *);
-void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
-
+__LA_DECL void	archive_entry_set_gid(struct archive_entry *, __LA_GID_T);
+__LA_DECL void	archive_entry_set_gname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_gname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+__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 void	archive_entry_set_ino(struct archive_entry *, unsigned long);
+__LA_DECL void	archive_entry_set_link(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_link(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void	archive_entry_set_mtime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_mtime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_nlink(struct archive_entry *, unsigned int);
+__LA_DECL void	archive_entry_set_pathname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_pathname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void	archive_entry_set_rdev(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_rdevminor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_size(struct archive_entry *, int64_t);
+__LA_DECL void	archive_entry_unset_size(struct archive_entry *);
+__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 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 *);
+__LA_DECL void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_uname_utf8(struct archive_entry *, const char *);
 /*
  * Routines to bulk copy fields to/from a platform-native "struct
  * stat."  Libarchive used to just store a struct stat inside of each
@@ -169,8 +281,9 @@
  *
  * TODO: On Linux, provide both stat32 and stat64 versions of these functions.
  */
-const struct stat	*archive_entry_stat(struct archive_entry *);
-void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+__LA_DECL const struct stat	*archive_entry_stat(struct archive_entry *);
+__LA_DECL void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+
 
 /*
  * ACL routines.  This used to simply store and return text-format ACL
@@ -179,6 +292,13 @@
  *   = there are many different ACL text formats
  *   = would like to be able to read/convert archives containing ACLs
  *     on platforms that lack ACL libraries
+ *
+ *  This last point, in particular, forces me to implement a reasonably
+ *  complete set of ACL support routines.
+ *
+ *  TODO: Extend this to support NFSv4/NTFS permissions.  That should
+ *  allow full ACL support on Mac OS, in particular, which uses
+ *  POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions.
  */
 
 /*
@@ -211,23 +331,26 @@
  * POSIX.1e) is useful for handling archive formats that combine
  * default and access information in a single ACL list.
  */
-void	 archive_entry_acl_clear(struct archive_entry *);
-void	 archive_entry_acl_add_entry(struct archive_entry *,
-	     int type, int permset, int tag, int qual, const char *name);
-void	 archive_entry_acl_add_entry_w(struct archive_entry *,
-	     int type, int permset, int tag, int qual, const wchar_t *name);
+__LA_DECL void	 archive_entry_acl_clear(struct archive_entry *);
+__LA_DECL void	 archive_entry_acl_add_entry(struct archive_entry *,
+	    int /* type */, int /* permset */, int /* tag */,
+	    int /* qual */, const char * /* name */);
+__LA_DECL void	 archive_entry_acl_add_entry_w(struct archive_entry *,
+	    int /* type */, int /* permset */, int /* tag */,
+	    int /* qual */, const wchar_t * /* name */);
 
 /*
  * To retrieve the ACL, first "reset", then repeatedly ask for the
  * "next" entry.  The want_type parameter allows you to request only
  * access entries or only default entries.
  */
-int	 archive_entry_acl_reset(struct archive_entry *, int want_type);
-int	 archive_entry_acl_next(struct archive_entry *, int want_type,
-	     int *type, int *permset, int *tag, int *qual, const char **name);
-int	 archive_entry_acl_next_w(struct archive_entry *, int want_type,
-	     int *type, int *permset, int *tag, int *qual,
-	     const wchar_t **name);
+__LA_DECL int	 archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
+__LA_DECL int	 archive_entry_acl_next(struct archive_entry *, int /* want_type */,
+	    int * /* type */, int * /* permset */, int * /* tag */,
+	    int * /* qual */, const char ** /* name */);
+__LA_DECL int	 archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
+	    int * /* type */, int * /* permset */, int * /* tag */,
+	    int * /* qual */, const wchar_t ** /* name */);
 
 /*
  * Construct a text-format ACL.  The flags argument is a bitmask that
@@ -242,10 +365,11 @@
  */
 #define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	1024
 #define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
-const wchar_t	*archive_entry_acl_text_w(struct archive_entry *, int flags);
+__LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,
+		    int /* flags */);
 
 /* Return a count of entries matching 'want_type' */
-int	 archive_entry_acl_count(struct archive_entry *, int want_type);
+__LA_DECL int	 archive_entry_acl_count(struct archive_entry *, int /* want_type */);
 
 /*
  * Private ACL parser.  This is private because it handles some
@@ -256,32 +380,114 @@
  * this interface are likely to be surprised when it changes.
  *
  * You were warned!
+ *
+ * TODO: Move this declaration out of the public header and into
+ * a private header.  Warnings above are silly.
  */
-int		 __archive_entry_acl_parse_w(struct archive_entry *,
-		     const wchar_t *, int type);
-
-
-#ifdef __cplusplus
-}
-#endif
+__LA_DECL int		 __archive_entry_acl_parse_w(struct archive_entry *,
+		    const wchar_t *, int /* type */);
 
 /*
  * extended attributes
  */
 
-void	 archive_entry_xattr_clear(struct archive_entry *);
-void	 archive_entry_xattr_add_entry(struct archive_entry *,
-	     const char *name, const void *value, size_t size);
+__LA_DECL void	 archive_entry_xattr_clear(struct archive_entry *);
+__LA_DECL void	 archive_entry_xattr_add_entry(struct archive_entry *,
+	    const char * /* name */, const void * /* value */,
+	    size_t /* size */);
 
 /*
  * To retrieve the xattr list, first "reset", then repeatedly ask for the
  * "next" entry.
  */
 
-int	archive_entry_xattr_count(struct archive_entry *);
-int	archive_entry_xattr_reset(struct archive_entry *);
-int	archive_entry_xattr_next(struct archive_entry *,
-	     const char **name, const void **value, size_t *);
+__LA_DECL int	archive_entry_xattr_count(struct archive_entry *);
+__LA_DECL int	archive_entry_xattr_reset(struct archive_entry *);
+__LA_DECL int	archive_entry_xattr_next(struct archive_entry *,
+	    const char ** /* name */, const void ** /* value */, size_t *);
+
+/*
+ * Utility to match up hardlinks.
+ *
+ * The 'struct archive_entry_linkresolver' is a cache of archive entries
+ * for files with multiple links.  Here's how to use it:
+ *   1. Create a lookup object with archive_entry_linkresolver_new()
+ *   2. Tell it the archive format you're using.
+ *   3. Hand each archive_entry to archive_entry_linkify().
+ *      That function will return 0, 1, or 2 entries that should
+ *      be written.
+ *   4. Call archive_entry_linkify(resolver, NULL) until
+ *      no more entries are returned.
+ *   5. Call archive_entry_link_resolver_free(resolver) to free resources.
+ *
+ * The entries returned have their hardlink and size fields updated
+ * appropriately.  If an entry is passed in that does not refer to
+ * a file with multiple links, it is returned unchanged.  The intention
+ * is that you should be able to simply filter all entries through
+ * this machine.
+ *
+ * To make things more efficient, be sure that each entry has a valid
+ * nlinks value.  The hardlink cache uses this to track when all links
+ * have been found.  If the nlinks value is zero, it will keep every
+ * name in the cache indefinitely, which can use a lot of memory.
+ *
+ * Note that archive_entry_size() is reset to zero if the file
+ * body should not be written to the archive.  Pay attention!
+ */
+__LA_DECL struct archive_entry_linkresolver;
+
+/*
+ * There are three different strategies for marking hardlinks.
+ * The descriptions below name them after the best-known
+ * formats that rely on each strategy:
+ *
+ * "Old cpio" is the simplest, it always returns any entry unmodified.
+ *    As far as I know, only cpio formats use this.  Old cpio archives
+ *    store every link with the full body; the onus is on the dearchiver
+ *    to detect and properly link the files as they are restored.
+ * "tar" is also pretty simple; it caches a copy the first time it sees
+ *    any link.  Subsequent appearances are modified to be hardlink
+ *    references to the first one without any body.  Used by all tar
+ *    formats, although the newest tar formats permit the "old cpio" strategy
+ *    as well.  This strategy is very simple for the dearchiver,
+ *    and reasonably straightforward for the archiver.
+ * "new cpio" is trickier.  It stores the body only with the last
+ *    occurrence.  The complication is that we might not
+ *    see every link to a particular file in a single session, so
+ *    there's no easy way to know when we've seen the last occurrence.
+ *    The solution here is to queue one link until we see the next.
+ *    At the end of the session, you can enumerate any remaining
+ *    entries by calling archive_entry_linkify(NULL) and store those
+ *    bodies.  If you have a file with three links l1, l2, and l3,
+ *    you'll get the following behavior if you see all three links:
+ *           linkify(l1) => NULL   (the resolver stores l1 internally)
+ *           linkify(l2) => l1     (resolver stores l2, you write l1)
+ *           linkify(l3) => l2, l3 (all links seen, you can write both).
+ *    If you only see l1 and l2, you'll get this behavior:
+ *           linkify(l1) => NULL
+ *           linkify(l2) => l1
+ *           linkify(NULL) => l2   (at end, you retrieve remaining links)
+ *    As the name suggests, this strategy is used by newer cpio variants.
+ *    It's noticably more complex for the archiver, slightly more complex
+ *    for the dearchiver than the tar strategy, but makes it straightforward
+ *    to restore a file using any link by simply continuing to scan until
+ *    you see a link that is stored with a body.  In contrast, the tar
+ *    strategy requires you to rescan the archive from the beginning to
+ *    correctly extract an arbitrary link.
+ */
+
+__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
+__LA_DECL void archive_entry_linkresolver_set_strategy(
+	struct archive_entry_linkresolver *, int /* format_code */);
+__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
+__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
+    struct archive_entry **, struct archive_entry **);
+
+#ifdef __cplusplus
+}
+#endif
 
+/* This is meaningless outside of this header. */
+#undef __LA_DECL
 
 #endif /* !ARCHIVE_ENTRY_H_INCLUDED */
Index: archive_write.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write.c -L lib/libarchive/archive_write.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write.c
+++ lib/libarchive/archive_write.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write.c,v 1.26 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write.c,v 1.26.2.1 2008/05/10 06:49:13 kientzle Exp $");
 
 /*
  * This file contains the "essential" portions of the write API, that
@@ -97,7 +97,12 @@
 	a->archive.magic = ARCHIVE_WRITE_MAGIC;
 	a->archive.state = ARCHIVE_STATE_NEW;
 	a->archive.vtable = archive_write_vtable();
-	a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
+	/*
+	 * The value 10240 here matches the traditional tar default,
+	 * but is otherwise arbitrary.
+	 * TODO: Set the default block size from the format selected.
+	 */
+	a->bytes_per_block = 10240;
 	a->bytes_in_last_block = -1;	/* Default */
 
 	/* Initialize a block of nulls for padding purposes. */
Index: archive_entry.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry.3,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_entry.3 -L lib/libarchive/archive_entry.3 -u -r1.2 -r1.3
--- lib/libarchive/archive_entry.3
+++ lib/libarchive/archive_entry.3
@@ -22,9 +22,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_entry.3,v 1.15 2007/07/15 19:10:34 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_entry.3,v 1.15.2.2 2008/08/10 04:32:47 kientzle Exp $
 .\"
-.Dd December 15, 2003
+.Dd May 12, 2008
 .Dt archive_entry 3
 .Os
 .Sh NAME
@@ -40,12 +40,16 @@
 .Nm archive_entry_atime_nsec ,
 .Nm archive_entry_clear ,
 .Nm archive_entry_clone ,
+.Nm archive_entry_copy_fflags_text ,
 .Nm archive_entry_copy_fflags_text_w ,
 .Nm archive_entry_copy_gname ,
 .Nm archive_entry_copy_gname_w ,
 .Nm archive_entry_copy_hardlink ,
 .Nm archive_entry_copy_hardlink_w ,
+.Nm archive_entry_copy_link ,
+.Nm archive_entry_copy_link_w ,
 .Nm archive_entry_copy_pathname_w ,
+.Nm archive_entry_copy_sourcepath ,
 .Nm archive_entry_copy_stat ,
 .Nm archive_entry_copy_symlink ,
 .Nm archive_entry_copy_symlink_w ,
@@ -93,6 +97,7 @@
 .Nm archive_entry_set_uid ,
 .Nm archive_entry_set_uname ,
 .Nm archive_entry_size ,
+.Nm archive_entry_sourcepath ,
 .Nm archive_entry_stat ,
 .Nm archive_entry_symlink ,
 .Nm archive_entry_uid ,
@@ -101,17 +106,47 @@
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft void
-.Fn archive_entry_acl_add_entry "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const char *name"
-.Ft void
-.Fn archive_entry_acl_add_entry_w "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const wchar_t *name"
+.Fo archive_entry_acl_add_entry
+.Fa "struct archive_entry *"
+.Fa "int type"
+.Fa "int permset"
+.Fa "int tag"
+.Fa "int qual"
+.Fa "const char *name"
+.Fc
+.Ft void
+.Fo archive_entry_acl_add_entry_w
+.Fa "struct archive_entry *"
+.Fa "int type"
+.Fa "int permset"
+.Fa "int tag"
+.Fa "int qual"
+.Fa "const wchar_t *name"
+.Fc
 .Ft void
 .Fn archive_entry_acl_clear "struct archive_entry *"
 .Ft int
 .Fn archive_entry_acl_count "struct archive_entry *" "int type"
 .Ft int
-.Fn archive_entry_acl_next "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const char **name"
+.Fo archive_entry_acl_next
+.Fa "struct archive_entry *"
+.Fa "int want_type"
+.Fa "int *type"
+.Fa "int *permset"
+.Fa "int *tag"
+.Fa "int *qual"
+.Fa "const char **name"
+.Fc
 .Ft int
-.Fn archive_entry_acl_next_w "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const wchar_t **name"
+.Fo archive_entry_acl_next_w
+.Fa "struct archive_entry *"
+.Fa "int want_type"
+.Fa "int *type"
+.Fa "int *permset"
+.Fa "int *tag"
+.Fa "int *qual"
+.Fa "const wchar_t **name"
+.Fc
 .Ft int
 .Fn archive_entry_acl_reset "struct archive_entry *" "int want_type"
 .Ft const wchar_t *
@@ -124,6 +159,8 @@
 .Fn archive_entry_clear "struct archive_entry *"
 .Ft struct archive_entry *
 .Fn archive_entry_clone "struct archive_entry *"
+.Ft const char * *
+.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const char *"
 .Ft const wchar_t *
 .Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
 .Ft void
@@ -135,6 +172,8 @@
 .Ft void
 .Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *"
 .Ft void
+.Fn archive_entry_copy_sourcepath "struct archive_entry *" "const char *"
+.Ft void
 .Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *"
 .Ft void
 .Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
@@ -155,7 +194,11 @@
 .Ft mode_t
 .Fn archive_entry_filetype "struct archive_entry *"
 .Ft void
-.Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear"
+.Fo archive_entry_fflags
+.Fa "struct archive_entry *"
+.Fa "unsigned long *set"
+.Fa "unsigned long *clear"
+.Fc
 .Ft const char *
 .Fn archive_entry_fflags_text "struct archive_entry *"
 .Ft void
@@ -195,7 +238,11 @@
 .Ft void
 .Fn archive_entry_set_filetype "struct archive_entry *" "unsigned int"
 .Ft void
-.Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear"
+.Fo archive_entry_set_fflags
+.Fa "struct archive_entry *"
+.Fa "unsigned long set"
+.Fa "unsigned long clear"
+.Fc
 .Ft void
 .Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
 .Ft void
@@ -230,6 +277,8 @@
 .Fn archive_entry_set_uname "struct archive_entry *" "const char *"
 .Ft int64_t
 .Fn archive_entry_size "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_sourcepath "struct archive_entry *"
 .Ft const struct stat *
 .Fn archive_entry_stat "struct archive_entry *"
 .Ft const char *
@@ -336,8 +385,10 @@
 .Pp
 The canonical text format is a comma-separated list of flag names.
 The
+.Fn archive_entry_copy_fflags_text
+and
 .Fn archive_entry_copy_fflags_text_w
-function parses the provided text and sets the internal bitmap values.
+functions parse the provided text and sets the internal bitmap values.
 This is a platform-specific operation; names that are not meaningful
 on the current platform will be ignored.
 The function returns a pointer to the start of the first name that was not
Index: libarchive-formats.5
===================================================================
RCS file: /home/cvs/src/lib/libarchive/libarchive-formats.5,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/libarchive-formats.5 -L lib/libarchive/libarchive-formats.5 -u -r1.2 -r1.3
--- lib/libarchive/libarchive-formats.5
+++ lib/libarchive/libarchive-formats.5
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.14 2007/04/05 05:07:53 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.14.2.2 2008/08/10 04:32:47 kientzle Exp $
 .\"
 .Dd April 27, 2004
 .Dt libarchive-formats 3
@@ -74,6 +74,8 @@
 modern long filename and linkname support, as well as atime and ctime data.
 The libarchive library does not support multi-volume
 archives, nor the old GNU long filename format.
+It can read GNU sparse file entries, including the new POSIX-based
+formats, but cannot write GNU sparse file entries.
 .It Cm pax
 The
 .Xr libarchive 3
@@ -93,7 +95,7 @@
 .Dq star
 archiver.
 The libarchive library can read most of the SCHILY keys.
-It ignores any keywords that it does not understand.
+It silently ignores any keywords that it does not understand.
 .It Cm restricted pax
 The libarchive library can also write pax archives in which it
 attempts to suppress the extended attributes entry whenever
@@ -139,19 +141,18 @@
 This essentially removes the limitations on file size, modification time,
 and device numbers.
 .Pp
-The first tar program appeared in Sixth Edition Unix (circa 1976).
-This makes the tar format one of the oldest and most widely-supported
-archive formats.
+The first tar program appeared in Seventh Edition Unix in 1979.
 The first official standard for the tar file format was the
 .Dq ustar
 (Unix Standard Tar) format defined by POSIX in 1988.
 POSIX.1-2001 extended the ustar format to create the
 .Dq pax interchange
 format.
-There have also been many custom variations.
 .Ss Cpio Formats
 The libarchive library can read a number of common cpio variants and can write
 .Dq odc
+and
+.Dq newc
 format archives.
 A cpio archive stores each entry as a fixed-size header followed
 by a variable-length filename and variable-length data.
@@ -184,15 +185,26 @@
 contents, although libarchive does not currently verify this CRC.
 .El
 .Pp
-Cpio is an old format that was widely used because of its simplicity
-and its support for very long filenames.
-Unfortunately, it has many limitations that make it unsuitable
+Cpio first appeared in PWB/UNIX 1.0, which was released within
+AT&T in 1977.
+PWB/UNIX 1.0 formed the basis of System III Unix, released outside
+of AT&T in 1981.
+This makes cpio older than tar, although cpio was not included
+in Version 7 AT&T Unix.
+As a result, the tar command became much better known in universities
+and research groups that used Version 7.
+The combination of the
+.Nm find
+and
+.Nm cpio
+utilities provided very precise control over file selection.
+Unfortunately, the format has many limitations that make it unsuitable
 for widespread use.
 Only the POSIX format permits files over 4GB, and its 18-bit
 limit for most other fields makes it unsuitable for modern systems.
 In addition, cpio formats only store numeric UID/GID values (not
 usernames and group names), which can make it very difficult to correctly
-transfer archives across systems.
+transfer archives across systems with dissimilar user numbering.
 .Ss Shar Formats
 A
 .Dq shell archive
@@ -247,6 +259,14 @@
 the GNU format derived from SVR4,
 and the BSD format, which first appeared in 4.4BSD.
 Libarchive provides read and write support for both variants.
+.Ss mtree
+Libarchive can read files in
+.Xr mtree 5
+format. This format is not a true archive format, but rather a description
+of a file hierarchy. When requested, libarchive obtains the contents of
+the files described by the
+.Xr mtree 5
+format from files on disk instead.
 .Sh SEE ALSO
 .Xr ar 1 ,
 .Xr cpio 1 ,
@@ -255,4 +275,6 @@
 .Xr tar 1 ,
 .Xr zip 1 ,
 .Xr zlib 3 ,
+.Xr cpio 5 ,
+.Xr mtree 5 ,
 .Xr tar 5
Index: archive_read_support_format_ar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_ar.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_read_support_format_ar.c -L lib/libarchive/archive_read_support_format_ar.c -u -r1.1 -r1.2
--- lib/libarchive/archive_read_support_format_ar.c
+++ lib/libarchive/archive_read_support_format_ar.c
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.6 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.6.4.4 2008/08/10 04:32:47 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -47,7 +47,6 @@
 #include "archive_read_private.h"
 
 struct ar {
-	int	 bid;
 	off_t	 entry_bytes_remaining;
 	off_t	 entry_offset;
 	off_t	 entry_padding;
@@ -84,8 +83,7 @@
 		    struct archive_entry *e);
 static uint64_t	ar_atol8(const char *p, unsigned char_cnt);
 static uint64_t	ar_atol10(const char *p, unsigned char_cnt);
-static int	ar_parse_gnu_filename_table(struct archive_read *, struct ar *,
-		    const void *, size_t);
+static int	ar_parse_gnu_filename_table(struct archive_read *a);
 static int	ar_parse_common_header(struct ar *ar, struct archive_entry *,
 		    const char *h);
 
@@ -103,7 +101,6 @@
 		return (ARCHIVE_FATAL);
 	}
 	memset(ar, 0, sizeof(*ar));
-	ar->bid = -1;
 	ar->strtab = NULL;
 
 	r = __archive_read_register_format(a,
@@ -148,9 +145,6 @@
 
 	ar = (struct ar *)(a->format->data);
 
-	if (ar->bid > 0)
-		return (ar->bid);
-
 	/*
 	 * Verify the 8-byte file signature.
 	 * TODO: Do we need to check more than this?
@@ -159,8 +153,7 @@
 	if (bytes_read < 8)
 		return (-1);
 	if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
-		ar->bid = 64;
-		return (ar->bid);
+		return (64);
 	}
 	return (-1);
 }
@@ -173,8 +166,8 @@
 	struct ar *ar;
 	uint64_t number; /* Used to hold parsed numbers before validation. */
 	ssize_t bytes_read;
-	size_t bsd_name_length, entry_size;
-	char *p;
+	size_t bsd_name_length, entry_size, s;
+	char *p, *st;
 	const void *b;
 	const char *h;
 	int r;
@@ -274,8 +267,7 @@
 		/* This must come before any call to _read_ahead. */
 		ar_parse_common_header(ar, entry, h);
 		archive_entry_copy_pathname(entry, filename);
-		archive_entry_set_mode(entry,
-		    S_IFREG | (archive_entry_mode(entry) & 0777));
+		archive_entry_set_filetype(entry, AE_IFREG);
 		/* Get the size of the filename table. */
 		number = ar_atol10(h + AR_size_offset, AR_size_size);
 		if (number > SIZE_MAX) {
@@ -284,22 +276,42 @@
 			return (ARCHIVE_FATAL);
 		}
 		entry_size = (size_t)number;
+		if (entry_size == 0) {
+			archive_set_error(&a->archive, EINVAL,
+			    "Invalid string table");
+			return (ARCHIVE_WARN);
+		}
+		if (ar->strtab != NULL) {
+			archive_set_error(&a->archive, EINVAL,
+			    "More than one string tables exist");
+			return (ARCHIVE_WARN);
+		}
+
 		/* Read the filename table into memory. */
-		bytes_read = (a->decompressor->read_ahead)(a, &b, entry_size);
-		if (bytes_read <= 0)
-			return (ARCHIVE_FATAL);
-		if ((size_t)bytes_read < entry_size) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "Truncated input file");
+		st = malloc(entry_size);
+		if (st == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate filename table buffer");
 			return (ARCHIVE_FATAL);
 		}
-		/*
-		 * Don't consume the contents, so the client will
-		 * also get a shot at reading it.
-		 */
+		ar->strtab = st;
+		ar->strtab_size = entry_size;
+		for (s = entry_size; s > 0; s -= bytes_read) {
+			bytes_read = (a->decompressor->read_ahead)(a, &b, s);
+			if (bytes_read <= 0)
+				return (ARCHIVE_FATAL);
+			if (bytes_read > (ssize_t)s)
+				bytes_read = s;
+			memcpy(st, b, bytes_read);
+			st += bytes_read;
+			(a->decompressor->consume)(a, bytes_read);
+		}
+		/* All contents are consumed. */
+		ar->entry_bytes_remaining = 0;
+		archive_entry_set_size(entry, ar->entry_bytes_remaining);
 
 		/* Parse the filename table. */
-		return (ar_parse_gnu_filename_table(a, ar, b, entry_size));
+		return (ar_parse_gnu_filename_table(a));
 	}
 
 	/*
@@ -338,12 +350,16 @@
 
 		/* Parse the size of the name, adjust the file size. */
 		number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
-		if ((off_t)number > ar->entry_bytes_remaining) {
+		bsd_name_length = (size_t)number;
+		/* Guard against the filename + trailing NUL
+		 * overflowing a size_t and against the filename size
+		 * being larger than the entire entry. */
+		if (number > (uint64_t)(bsd_name_length + 1)
+		    || (off_t)bsd_name_length > ar->entry_bytes_remaining) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Bad input file size");
 			return (ARCHIVE_FATAL);
 		}
-		bsd_name_length = (size_t)number;
 		ar->entry_bytes_remaining -= bsd_name_length;
 		/* Adjust file size reported to client. */
 		archive_entry_set_size(entry, ar->entry_bytes_remaining);
@@ -381,8 +397,7 @@
 		/* Parse the time, owner, mode, size fields. */
 		r = ar_parse_common_header(ar, entry, h);
 		/* Force the file type to a regular file. */
-		archive_entry_set_mode(entry,
-		    S_IFREG | (archive_entry_mode(entry) & 0777));
+		archive_entry_set_filetype(entry, AE_IFREG);
 		return (r);
 	}
 
@@ -500,31 +515,15 @@
 }
 
 static int
-ar_parse_gnu_filename_table(struct archive_read *a, struct ar *ar,
-    const void *h, size_t size)
+ar_parse_gnu_filename_table(struct archive_read *a)
 {
+	struct ar *ar;
 	char *p;
+	size_t size;
 
-	if (ar->strtab != NULL) {
-		archive_set_error(&a->archive, EINVAL,
-		    "More than one string tables exist");
-		return (ARCHIVE_WARN);
-	}
-
-	if (size == 0) {
-		archive_set_error(&a->archive, EINVAL, "Invalid string table");
-		return (ARCHIVE_WARN);
-	}
-
-	ar->strtab_size = size;
-	ar->strtab = malloc(size);
-	if (ar->strtab == NULL) {
-		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate string table buffer");
-		return (ARCHIVE_FATAL);
-	}
+	ar = (struct ar*)(a->format->data);
+	size = ar->strtab_size;
 
-	(void)memcpy(ar->strtab, h, size);
 	for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
 		if (*p == '/') {
 			*p++ = '\0';
Index: archive_write_set_compression_bzip2.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_bzip2.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_compression_bzip2.c -L lib/libarchive/archive_write_set_compression_bzip2.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_compression_bzip2.c
+++ lib/libarchive/archive_write_set_compression_bzip2.c
@@ -28,7 +28,7 @@
 /* Don't compile this if we don't have bzlib. */
 #if HAVE_BZLIB_H
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.12 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.12.2.1 2008/02/11 00:31:08 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -320,6 +320,10 @@
 			state->stream.avail_out = bytes_written;
 		}
 
+		/* If there's nothing to do, we're done. */
+		if (!finishing && state->stream.avail_in == 0)
+			return (ARCHIVE_OK);
+
 		ret = BZ2_bzCompress(&(state->stream),
 		    finishing ? BZ_FINISH : BZ_RUN);
 
@@ -339,7 +343,9 @@
 			/* Any other return value indicates an error */
 			archive_set_error(&a->archive,
 			    ARCHIVE_ERRNO_PROGRAMMER,
-			    "Bzip2 compression failed");
+			    "Bzip2 compression failed;"
+			    " BZ2_bzCompress() returned %d",
+			    ret);
 			return (ARCHIVE_FATAL);
 		}
 	}
Index: archive_read_support_compression_gzip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_gzip.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_read_support_compression_gzip.c -L lib/libarchive/archive_read_support_compression_gzip.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_support_compression_gzip.c
+++ lib/libarchive/archive_read_support_compression_gzip.c
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.15 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.15.2.1 2008/03/12 04:20:30 kientzle Exp $");
 
 
 #ifdef HAVE_ERRNO_H
@@ -146,7 +146,7 @@
 	(void)buff;	/* UNUSED */
 	(void)n;	/* UNUSED */
 
-	archive_set_error(a, -1,
+	archive_set_error(&a->archive, -1,
 	    "This version of libarchive was compiled without gzip support");
 	return (ARCHIVE_FATAL);
 }
Index: archive_write_set_format_shar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_shar.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_format_shar.c -L lib/libarchive/archive_write_set_format_shar.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_format_shar.c
+++ lib/libarchive/archive_write_set_format_shar.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.18 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.18.2.1 2008/05/10 06:57:04 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -113,8 +113,8 @@
 	a->format_destroy = archive_write_shar_destroy;
 	a->format_write_data = archive_write_shar_data_sed;
 	a->format_finish_entry = archive_write_shar_finish_entry;
-	a->archive_format = ARCHIVE_FORMAT_SHAR_BASE;
-	a->archive_format_name = "shar";
+	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
+	a->archive.archive_format_name = "shar";
 	return (ARCHIVE_OK);
 }
 
@@ -134,8 +134,8 @@
 	shar = (struct shar *)a->format_data;
 	shar->dump = 1;
 	a->format_write_data = archive_write_shar_data_uuencode;
-	a->archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
-	a->archive_format_name = "shar dump";
+	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
+	a->archive.archive_format_name = "shar dump";
 	return (ARCHIVE_OK);
 }
 
Index: archive_platform.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_platform.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_platform.h -L lib/libarchive/archive_platform.h -u -r1.2 -r1.3
--- lib/libarchive/archive_platform.h
+++ lib/libarchive/archive_platform.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.27 2007/05/29 01:00:18 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.27.2.3 2008/08/10 04:32:47 kientzle Exp $
  */
 
 /*
@@ -36,7 +36,13 @@
 #ifndef ARCHIVE_PLATFORM_H_INCLUDED
 #define	ARCHIVE_PLATFORM_H_INCLUDED
 
-#if defined(PLATFORM_CONFIG_H)
+/* archive.h and archive_entry.h require this. */
+#define	__LIBARCHIVE_BUILD 1
+
+#ifdef _WIN32
+#include "config_windows.h"
+#include "archive_windows.h"
+#elif defined(PLATFORM_CONFIG_H)
 /* Use hand-built config.h in environments that need it. */
 #include PLATFORM_CONFIG_H
 #elif defined(HAVE_CONFIG_H)
@@ -57,7 +63,8 @@
 #ifdef __FreeBSD__
 #include <sys/cdefs.h>  /* For __FBSDID */
 #else
-#define	__FBSDID(a)     /* null */
+/* Just leaving this macro replacement empty leads to a dangling semicolon. */
+#define	__FBSDID(a)     struct _undefined_hack
 #endif
 
 /* Try to get standard C99-style integer type definitions. */
@@ -122,4 +129,4 @@
 #define	ARCHIVE_ERRNO_MISC (-1)
 #endif
 
-#endif /* !ARCHIVE_H_INCLUDED */
+#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
Index: archive_read_private.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_private.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_read_private.h -L lib/libarchive/archive_read_private.h -u -r1.1 -r1.2
--- lib/libarchive/archive_read_private.h
+++ lib/libarchive/archive_read_private.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.3 2007/05/29 01:00:18 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.3.4.2 2008/05/10 07:05:14 kientzle Exp $
  */
 
 #ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
@@ -41,10 +41,6 @@
 	dev_t		  skip_file_dev;
 	ino_t		  skip_file_ino;
 
-	/* Utility:  Pointer to a block of nulls. */
-	const unsigned char	*nulls;
-	size_t			 null_length;
-
 	/*
 	 * Used by archive_read_data() to track blocks and copy
 	 * data to client buffers, filling gaps with zero bytes.
@@ -58,30 +54,9 @@
 	archive_open_callback	*client_opener;
 	archive_read_callback	*client_reader;
 	archive_skip_callback	*client_skipper;
-	archive_write_callback	*client_writer;
 	archive_close_callback	*client_closer;
 	void			*client_data;
 
-	/*
-	 * Blocking information.  Note that bytes_in_last_block is
-	 * misleadingly named; I should find a better name.  These
-	 * control the final output from all compressors, including
-	 * compression_none.
-	 */
-	int		  bytes_per_block;
-	int		  bytes_in_last_block;
-
-	/*
-	 * These control whether data within a gzip/bzip2 compressed
-	 * stream gets padded or not.  If pad_uncompressed is set,
-	 * the data will be padded to a full block before being
-	 * compressed.  The pad_uncompressed_byte determines the value
-	 * that will be used for padding.  Note that these have no
-	 * effect on compression "none."
-	 */
-	int		  pad_uncompressed;
-	int		  pad_uncompressed_byte; /* TODO: Support this. */
-
 	/* File offset of beginning of most recently-read header. */
 	off_t		  header_position;
 
@@ -118,17 +93,10 @@
 
 	/*
 	 * Format detection is mostly the same as compression
-	 * detection, with two significant differences: The bidders
+	 * detection, with one significant difference: The bidders
 	 * use the read_ahead calls above to examine the stream rather
 	 * than having the supervisor hand them a block of data to
-	 * examine, and the auction is repeated for every header.
-	 * Winning bidders should set the archive_format and
-	 * archive_format_name appropriately.  Bid routines should
-	 * check archive_format and decline to bid if the format of
-	 * the last header was incompatible.
-	 *
-	 * Again, write support is considerably simpler because there's
-	 * no need for an auction.
+	 * examine.
 	 */
 
 	struct archive_format_descriptor {
@@ -142,18 +110,6 @@
 	struct archive_format_descriptor	*format; /* Active format. */
 
 	/*
-	 * Pointers to format-specific functions for writing.  They're
-	 * initialized by archive_write_set_format_XXX() calls.
-	 */
-	int	(*format_init)(struct archive *); /* Only used on write. */
-	int	(*format_finish)(struct archive *);
-	int	(*format_finish_entry)(struct archive *);
-	int 	(*format_write_header)(struct archive *,
-		    struct archive_entry *);
-	ssize_t	(*format_write_data)(struct archive *,
-		    const void *buff, size_t);
-
-	/*
 	 * Various information needed by archive_extract.
 	 */
 	struct extract		 *extract;
@@ -173,4 +129,7 @@
 	    int (*bid)(const void *, size_t),
 	    int (*init)(struct archive_read *, const void *, size_t));
 
+const void
+	*__archive_read_ahead(struct archive_read *, size_t);
+
 #endif
Index: archive_read_support_format_tar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_tar.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -L lib/libarchive/archive_read_support_format_tar.c -L lib/libarchive/archive_read_support_format_tar.c -u -r1.4 -r1.5
--- lib/libarchive/archive_read_support_format_tar.c
+++ lib/libarchive/archive_read_support_format_tar.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.61.2.1 2007/10/29 03:28:28 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.61.2.5 2008/08/10 05:39:25 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -128,8 +128,8 @@
 	char	isextended[1];
 	char	realsize[12];
 	/*
-	 * GNU doesn't use POSIX 'prefix' field; they use the 'L' (longname)
-	 * entry instead.
+	 * Old GNU format doesn't use POSIX 'prefix' field; they use
+	 * the 'L' (longname) entry instead.
 	 */
 };
 
@@ -144,8 +144,10 @@
 
 struct tar {
 	struct archive_string	 acl_text;
-	struct archive_string	 entry_name;
-	struct archive_string	 entry_linkname;
+	struct archive_string	 entry_pathname;
+	/* For "GNU.sparse.name" and other similar path extensions. */
+	struct archive_string	 entry_pathname_override;
+	struct archive_string	 entry_linkpath;
 	struct archive_string	 entry_uname;
 	struct archive_string	 entry_gname;
 	struct archive_string	 longlink;
@@ -153,6 +155,7 @@
 	struct archive_string	 pax_header;
 	struct archive_string	 pax_global;
 	struct archive_string	 line;
+	int			 pax_hdrcharset_binary;
 	wchar_t 		*pax_entry;
 	size_t			 pax_entry_length;
 	int			 header_recursion_depth;
@@ -164,15 +167,14 @@
 	struct sparse_block	*sparse_last;
 	int64_t			 sparse_offset;
 	int64_t			 sparse_numbytes;
-	int64_t			 sparse_realsize;
 	int			 sparse_gnu_major;
 	int			 sparse_gnu_minor;
 	char			 sparse_gnu_pending;
 };
 
-static size_t	UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
+static ssize_t	UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
 static int	archive_block_is_null(const unsigned char *p);
-static char	*base64_decode(const wchar_t *, size_t, size_t *);
+static char	*base64_decode(const char *, size_t, size_t *);
 static void	 gnu_add_sparse_entry(struct tar *,
 		    off_t offset, off_t remaining);
 static void	gnu_clear_sparse_list(struct tar *);
@@ -180,7 +182,7 @@
 		    const struct archive_entry_header_gnutar *header);
 static void	gnu_sparse_old_parse(struct tar *,
 		    const struct gnu_sparse *sparse, int length);
-static int	gnu_sparse_01_parse(struct tar *, const wchar_t *);
+static int	gnu_sparse_01_parse(struct tar *, const char *);
 static ssize_t	gnu_sparse_10_read(struct archive_read *, struct tar *);
 static int	header_Solaris_ACL(struct archive_read *,  struct tar *,
 		    struct archive_entry *, const void *);
@@ -211,24 +213,23 @@
 		    struct archive_entry *);
 static int	checksum(struct archive_read *, const void *);
 static int 	pax_attribute(struct tar *, struct archive_entry *,
-		    wchar_t *key, wchar_t *value);
+		    char *key, char *value);
 static int 	pax_header(struct archive_read *, struct tar *,
 		    struct archive_entry *, char *attr);
-static void	pax_time(const wchar_t *, int64_t *sec, long *nanos);
+static void	pax_time(const char *, int64_t *sec, long *nanos);
 static ssize_t	readline(struct archive_read *, struct tar *, const char **,
 		    ssize_t limit);
 static int	read_body_to_string(struct archive_read *, struct tar *,
 		    struct archive_string *, const void *h);
 static int64_t	tar_atol(const char *, unsigned);
-static int64_t	tar_atol10(const wchar_t *, unsigned);
+static int64_t	tar_atol10(const char *, unsigned);
 static int64_t	tar_atol256(const char *, unsigned);
 static int64_t	tar_atol8(const char *, unsigned);
 static int	tar_read_header(struct archive_read *, struct tar *,
 		    struct archive_entry *);
 static int	tohex(int c);
 static char	*url_decode(const char *);
-static int	utf8_decode(wchar_t *, const char *, size_t length);
-static char	*wide_to_narrow(const wchar_t *wval);
+static wchar_t	*utf8_decode(struct tar *, const char *, size_t length);
 
 int
 archive_read_support_format_gnutar(struct archive *a)
@@ -272,13 +273,16 @@
 	tar = (struct tar *)(a->format->data);
 	gnu_clear_sparse_list(tar);
 	archive_string_free(&tar->acl_text);
-	archive_string_free(&tar->entry_name);
-	archive_string_free(&tar->entry_linkname);
+	archive_string_free(&tar->entry_pathname);
+	archive_string_free(&tar->entry_pathname_override);
+	archive_string_free(&tar->entry_linkpath);
 	archive_string_free(&tar->entry_uname);
 	archive_string_free(&tar->entry_gname);
 	archive_string_free(&tar->line);
 	archive_string_free(&tar->pax_global);
 	archive_string_free(&tar->pax_header);
+	archive_string_free(&tar->longname);
+	archive_string_free(&tar->longlink);
 	free(tar->pax_entry);
 	free(tar);
 	(a->format->data) = NULL;
@@ -294,24 +298,8 @@
 	const void *h;
 	const struct archive_entry_header_ustar *header;
 
-	/*
-	 * If we're already reading a non-tar file, don't
-	 * bother to bid.
-	 */
-	if (a->archive.archive_format != 0 &&
-	    (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
-	    ARCHIVE_FORMAT_TAR)
-		return (0);
 	bid = 0;
 
-	/*
-	 * If we're already reading a tar format, start the bid at 1 as
-	 * a failsafe.
-	 */
-	if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) ==
-	    ARCHIVE_FORMAT_TAR)
-		bid++;
-
 	/* Now let's look at the actual header and see if it matches. */
 	if (a->decompressor->read_ahead != NULL)
 		bytes_read = (a->decompressor->read_ahead)(a, &h, 512);
@@ -319,32 +307,18 @@
 		bytes_read = 0; /* Empty file. */
 	if (bytes_read < 0)
 		return (ARCHIVE_FATAL);
-	if (bytes_read == 0  &&  bid > 0) {
-		/* An archive without a proper end-of-archive marker. */
-		/* Hold our nose and bid 1 anyway. */
-		return (1);
-	}
-	if (bytes_read < 512) {
-		/* If it's a new archive, then just return a zero bid. */
-		if (bid == 0)
-			return (0);
-		/*
-		 * If we already know this is a tar archive,
-		 * then we have a problem.
-		 */
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Truncated tar archive");
-		return (ARCHIVE_FATAL);
-	}
+	if (bytes_read < 512)
+		return (0);
 
 	/* If it's an end-of-archive mark, we can handle it. */
-	if ((*(const char *)h) == 0 && archive_block_is_null((const unsigned char *)h)) {
-		/* If it's a known tar file, end-of-archive is definite. */
-		if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) ==
-		    ARCHIVE_FORMAT_TAR)
-			return (512);
-		/* Empty archive? */
-		return (1);
+	if ((*(const char *)h) == 0
+	    && archive_block_is_null((const unsigned char *)h)) {
+		/*
+		 * Usually, I bid the number of bits verified, but
+		 * in this case, 4096 seems excessive so I picked 10 as
+		 * an arbitrary but reasonable-seeming value.
+		 */
+		return (10);
 	}
 
 	/* If it's not an end-of-archive mark, it must have a valid checksum.*/
@@ -441,7 +415,7 @@
 		free(sp);
 	}
 	tar->sparse_last = NULL;
-	tar->sparse_realsize = -1; /* Mark this as "unset" */
+	tar->realsize = -1; /* Mark this as "unset" */
 
 	r = tar_read_header(a, tar, entry);
 
@@ -452,8 +426,6 @@
 	if (tar->sparse_list == NULL)
 		gnu_add_sparse_entry(tar, 0, tar->entry_bytes_remaining);
 
-	tar->realsize = archive_entry_size(entry);
-
 	if (r == ARCHIVE_OK) {
 		/*
 		 * "Regular" entry with trailing '/' is really
@@ -581,16 +553,24 @@
 
 	/* Read 512-byte header record */
 	bytes = (a->decompressor->read_ahead)(a, &h, 512);
-	if (bytes < 512) {
+	if (bytes < 0)
+		return (bytes);
+	if (bytes == 0) {
 		/*
-		 * If we're here, it's becase the _bid function accepted
-		 * this file.  So just call a short read end-of-archive
-		 * and be done with it.
+		 * An archive that just ends without a proper
+		 * end-of-archive marker.  Yes, there are tar programs
+		 * that do this; hold our nose and accept it.
 		 */
 		return (ARCHIVE_EOF);
 	}
+	if (bytes < 512) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated tar archive");
+		return (ARCHIVE_FATAL);
+	}
 	(a->decompressor->consume)(a, 512);
 
+
 	/* Check for end-of-archive mark. */
 	if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) {
 		/* Try to consume a second all-null record, as well. */
@@ -598,6 +578,10 @@
 		if (bytes > 0)
 			(a->decompressor->consume)(a, bytes);
 		archive_set_error(&a->archive, 0, NULL);
+		if (a->archive.archive_format_name == NULL) {
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+			a->archive.archive_format_name = "tar";
+		}
 		return (ARCHIVE_EOF);
 	}
 
@@ -733,7 +717,7 @@
 {
 	unsigned i;
 
-	for (i = 0; i < ARCHIVE_BYTES_PER_RECORD / sizeof(*p); i++)
+	for (i = 0; i < 512; i++)
 		if (*p++)
 			return (0);
 	return (1);
@@ -785,16 +769,9 @@
 	while (*p != '\0' && p < acl + size)
 		p++;
 
-	wp = (wchar_t *)malloc((p - acl + 1) * sizeof(wchar_t));
-	if (wp == NULL) {
-		archive_set_error(&a->archive, ENOMEM,
-		    "Can't allocate work buffer for ACL parsing");
-		return (ARCHIVE_FATAL);
-	}
-	utf8_decode(wp, acl, p - acl);
+	wp = utf8_decode(tar, acl, p - acl);
 	err = __archive_entry_acl_parse_w(entry, wp,
 	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-	free(wp);
 	return (err);
 }
 
@@ -814,7 +791,7 @@
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 	/* Set symlink if symlink already set, else hardlink. */
-	archive_entry_set_link(entry, tar->longlink.s);
+	archive_entry_copy_link(entry, tar->longlink.s);
 	return (ARCHIVE_OK);
 }
 
@@ -834,7 +811,7 @@
 	err = tar_read_header(a, tar, entry);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
-	archive_entry_set_pathname(entry, tar->longname.s);
+	archive_entry_copy_pathname(entry, tar->longname.s);
 	return (ARCHIVE_OK);
 }
 
@@ -926,16 +903,17 @@
 
 	header = (const struct archive_entry_header_ustar *)h;
 	if (header->linkname[0])
-		archive_strncpy(&(tar->entry_linkname), header->linkname,
+		archive_strncpy(&(tar->entry_linkpath), header->linkname,
 		    sizeof(header->linkname));
 	else
-		archive_string_empty(&(tar->entry_linkname));
+		archive_string_empty(&(tar->entry_linkpath));
 
 	/* Parse out the numeric fields (all are octal) */
 	archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode)));
 	archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
 	archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
 	tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
+	tar->realsize = tar->entry_bytes_remaining;
 	archive_entry_set_size(entry, tar->entry_bytes_remaining);
 	archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
 
@@ -944,7 +922,7 @@
 
 	switch (tartype) {
 	case '1': /* Hard link */
-		archive_entry_set_hardlink(entry, tar->entry_linkname.s);
+		archive_entry_copy_hardlink(entry, tar->entry_linkpath.s);
 		/*
 		 * The following may seem odd, but: Technically, tar
 		 * does not store the file type for a "hard link"
@@ -961,36 +939,52 @@
 		 * A tricky point: Traditionally, tar readers have
 		 * ignored the size field when reading hardlink
 		 * entries, and some writers put non-zero sizes even
-		 * though the body is empty.  POSIX.1-2001 broke with
-		 * this tradition by permitting hardlink entries to
-		 * store valid bodies in pax interchange format, but
-		 * not in ustar format.  Since there is no hard and
-		 * fast way to distinguish pax interchange from
-		 * earlier archives (the 'x' and 'g' entries are
-		 * optional, after all), we need a heuristic.  Here, I
-		 * use the bid function to test whether or not there's
-		 * a valid header following.  Of course, if we know
-		 * this is pax interchange format, then we must obey
-		 * the size.
-		 *
-		 * This heuristic will only fail for a pax interchange
-		 * archive that is storing hardlink bodies, no pax
-		 * extended attribute entries have yet occurred, and
-		 * we encounter a hardlink entry for a file that is
-		 * itself an uncompressed tar archive.
+		 * though the body is empty.  POSIX blessed this
+		 * convention in the 1988 standard, but broke with
+		 * this tradition in 2001 by permitting hardlink
+		 * entries to store valid bodies in pax interchange
+		 * format, but not in ustar format.  Since there is no
+		 * hard and fast way to distinguish pax interchange
+		 * from earlier archives (the 'x' and 'g' entries are
+		 * optional, after all), we need a heuristic.
 		 */
-		if (archive_entry_size(entry) > 0  &&
-		    a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE  &&
-		    archive_read_format_tar_bid(a) > 50) {
+		if (archive_entry_size(entry) == 0) {
+			/* If the size is already zero, we're done. */
+		}  else if (a->archive.archive_format
+		    == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+			/* Definitely pax extended; must obey hardlink size. */
+		} else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR
+		    || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR)
+		{
+			/* Old-style or GNU tar: we must ignore the size. */
+			archive_entry_set_size(entry, 0);
+			tar->entry_bytes_remaining = 0;
+		} else if (archive_read_format_tar_bid(a) > 50) {
+			/*
+			 * We don't know if it's pax: If the bid
+			 * function sees a valid ustar header
+			 * immediately following, then let's ignore
+			 * the hardlink size.
+			 */
 			archive_entry_set_size(entry, 0);
 			tar->entry_bytes_remaining = 0;
 		}
+		/*
+		 * TODO: There are still two cases I'd like to handle:
+		 *   = a ustar non-pax archive with a hardlink entry at
+		 *     end-of-archive.  (Look for block of nulls following?)
+		 *   = a pax archive that has not seen any pax headers
+		 *     and has an entry which is a hardlink entry storing
+		 *     a body containing an uncompressed tar archive.
+		 * The first is worth addressing; I don't see any reliable
+		 * way to deal with the second possibility.
+		 */
 		break;
 	case '2': /* Symlink */
 		archive_entry_set_filetype(entry, AE_IFLNK);
 		archive_entry_set_size(entry, 0);
 		tar->entry_bytes_remaining = 0;
-		archive_entry_set_symlink(entry, tar->entry_linkname.s);
+		archive_entry_copy_symlink(entry, tar->entry_linkpath.s);
 		break;
 	case '3': /* Character device */
 		archive_entry_set_filetype(entry, AE_IFCHR);
@@ -1062,8 +1056,8 @@
 
 	/* Copy filename over (to ensure null termination). */
 	header = (const struct archive_entry_header_ustar *)h;
-	archive_strncpy(&(tar->entry_name), header->name, sizeof(header->name));
-	archive_entry_set_pathname(entry, tar->entry_name.s);
+	archive_strncpy(&(tar->entry_pathname), header->name, sizeof(header->name));
+	archive_entry_copy_pathname(entry, tar->entry_pathname.s);
 
 	/* Grab rest of common fields */
 	header_common(a, tar, entry, h);
@@ -1134,7 +1128,7 @@
 	header = (const struct archive_entry_header_ustar *)h;
 
 	/* Copy name into an internal buffer to ensure null-termination. */
-	as = &(tar->entry_name);
+	as = &(tar->entry_pathname);
 	if (header->prefix[0]) {
 		archive_strncpy(as, header->prefix, sizeof(header->prefix));
 		if (as->s[archive_strlen(as) - 1] != '/')
@@ -1143,7 +1137,7 @@
 	} else
 		archive_strncpy(as, header->name, sizeof(header->name));
 
-	archive_entry_set_pathname(entry, as->s);
+	archive_entry_copy_pathname(entry, as->s);
 
 	/* Handle rest of common fields. */
 	header_common(a, tar, entry, h);
@@ -1151,11 +1145,11 @@
 	/* Handle POSIX ustar fields. */
 	archive_strncpy(&(tar->entry_uname), header->uname,
 	    sizeof(header->uname));
-	archive_entry_set_uname(entry, tar->entry_uname.s);
+	archive_entry_copy_uname(entry, tar->entry_uname.s);
 
 	archive_strncpy(&(tar->entry_gname), header->gname,
 	    sizeof(header->gname));
-	archive_entry_set_gname(entry, tar->entry_gname.s);
+	archive_entry_copy_gname(entry, tar->entry_gname.s);
 
 	/* Parse out device numbers only for char and block specials. */
 	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
@@ -1182,10 +1176,16 @@
 {
 	size_t attr_length, l, line_length;
 	char *line, *p;
-	wchar_t *key, *wp, *value;
+	char *key, *value;
 	int err, err2;
 
 	attr_length = strlen(attr);
+	tar->pax_hdrcharset_binary = 0;
+	archive_string_empty(&(tar->entry_gname));
+	archive_string_empty(&(tar->entry_linkpath));
+	archive_string_empty(&(tar->entry_pathname));
+	archive_string_empty(&(tar->entry_pathname_override));
+	archive_string_empty(&(tar->entry_uname));
 	err = ARCHIVE_OK;
 	while (attr_length > 0) {
 		/* Parse decimal length field at start of line. */
@@ -1228,49 +1228,24 @@
 			return (ARCHIVE_WARN);
 		}
 
-		/* Ensure pax_entry buffer is big enough. */
-		if (tar->pax_entry_length <= line_length) {
-			wchar_t *old_entry = tar->pax_entry;
-
-			if (tar->pax_entry_length <= 0)
-				tar->pax_entry_length = 1024;
-			while (tar->pax_entry_length <= line_length + 1)
-				tar->pax_entry_length *= 2;
-
-			old_entry = tar->pax_entry;
-			tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
-			    tar->pax_entry_length * sizeof(wchar_t));
-			if (tar->pax_entry == NULL) {
-				free(old_entry);
-				archive_set_error(&a->archive, ENOMEM,
-					"No memory");
-				return (ARCHIVE_FATAL);
-			}
-		}
-
-		/* Decode UTF-8 to wchar_t, null-terminate result. */
-		if (utf8_decode(tar->pax_entry, p,
-			line_length - (p - attr) - 1)) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			   "Invalid UTF8 character in pax extended attribute");
-			err = err_combine(err, ARCHIVE_WARN);
-		}
+		/* Null-terminate the line. */
+		attr[line_length - 1] = '\0';
 
-		/* Null-terminate 'key' value. */
-		wp = key = tar->pax_entry;
-		if (key[0] == L'=')
+		/* Find end of key and null terminate it. */
+		key = p;
+		if (key[0] == '=')
 			return (-1);
-		while (*wp && *wp != L'=')
-			++wp;
-		if (*wp == L'\0') {
+		while (*p && *p != '=')
+			++p;
+		if (*p == '\0') {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Invalid pax extended attributes");
 			return (ARCHIVE_WARN);
 		}
-		*wp = 0;
+		*p = '\0';
 
 		/* Identify null-terminated 'value' portion. */
-		value = wp + 1;
+		value = p + 1;
 
 		/* Identify this attribute and set it in the entry. */
 		err2 = pax_attribute(tar, entry, key, value);
@@ -1280,33 +1255,98 @@
 		attr += line_length;
 		attr_length -= line_length;
 	}
+	if (archive_strlen(&(tar->entry_gname)) > 0) {
+		value = tar->entry_gname.s;
+		if (tar->pax_hdrcharset_binary)
+			archive_entry_copy_gname(entry, value);
+		else {
+			if (!archive_entry_update_gname_utf8(entry, value)) {
+				err = ARCHIVE_WARN;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Gname in pax header can't "
+				    "be converted to current locale.");
+			}
+		}
+	}
+	if (archive_strlen(&(tar->entry_linkpath)) > 0) {
+		value = tar->entry_linkpath.s;
+		if (tar->pax_hdrcharset_binary)
+			archive_entry_copy_link(entry, value);
+		else {
+			if (!archive_entry_update_link_utf8(entry, value)) {
+				err = ARCHIVE_WARN;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Linkname in pax header can't "
+				    "be converted to current locale.");
+			}
+		}
+	}
+	/*
+	 * Some extensions (such as the GNU sparse file extensions)
+	 * deliberately store a synthetic name under the regular 'path'
+	 * attribute and the real file name under a different attribute.
+	 * Since we're supposed to not care about the order, we
+	 * have no choice but to store all of the various filenames
+	 * we find and figure it all out afterwards.  This is the
+	 * figuring out part.
+	 */
+	value = NULL;
+	if (archive_strlen(&(tar->entry_pathname_override)) > 0)
+		value = tar->entry_pathname_override.s;
+	else if (archive_strlen(&(tar->entry_pathname)) > 0)
+		value = tar->entry_pathname.s;
+	if (value != NULL) {
+		if (tar->pax_hdrcharset_binary)
+			archive_entry_copy_pathname(entry, value);
+		else {
+			if (!archive_entry_update_pathname_utf8(entry, value)) {
+				err = ARCHIVE_WARN;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Pathname in pax header can't be "
+				    "converted to current locale.");
+			}
+		}
+	}
+	if (archive_strlen(&(tar->entry_uname)) > 0) {
+		value = tar->entry_uname.s;
+		if (tar->pax_hdrcharset_binary)
+			archive_entry_copy_uname(entry, value);
+		else {
+			if (!archive_entry_update_uname_utf8(entry, value)) {
+				err = ARCHIVE_WARN;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Uname in pax header can't "
+				    "be converted to current locale.");
+			}
+		}
+	}
 	return (err);
 }
 
 static int
 pax_attribute_xattr(struct archive_entry *entry,
-	wchar_t *name, wchar_t *value)
+	char *name, char *value)
 {
-	char *name_decoded, *name_narrow;
+	char *name_decoded;
 	void *value_decoded;
 	size_t value_len;
 
-	if (wcslen(name) < 18 || (wcsncmp(name, L"LIBARCHIVE.xattr.", 17)) != 0)
+	if (strlen(name) < 18 || (strncmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
 		return 3;
 
 	name += 17;
 
 	/* URL-decode name */
-	name_narrow = wide_to_narrow(name);
-	if (name_narrow == NULL)
-		return 2;
-	name_decoded = url_decode(name_narrow);
-	free(name_narrow);
+	name_decoded = url_decode(name);
 	if (name_decoded == NULL)
 		return 2;
 
 	/* Base-64 decode value */
-	value_decoded = base64_decode(value, wcslen(value), &value_len);
+	value_decoded = base64_decode(value, strlen(value), &value_len);
 	if (value_decoded == NULL) {
 		free(name_decoded);
 		return 1;
@@ -1335,22 +1375,23 @@
  */
 static int
 pax_attribute(struct tar *tar, struct archive_entry *entry,
-    wchar_t *key, wchar_t *value)
+    char *key, char *value)
 {
 	int64_t s;
 	long n;
+	wchar_t *wp;
 
 	switch (key[0]) {
 	case 'G':
 		/* GNU "0.0" sparse pax format. */
-		if (wcscmp(key, L"GNU.sparse.numblocks") == 0) {
+		if (strcmp(key, "GNU.sparse.numblocks") == 0) {
 			tar->sparse_offset = -1;
 			tar->sparse_numbytes = -1;
 			tar->sparse_gnu_major = 0;
 			tar->sparse_gnu_minor = 0;
 		}
-		if (wcscmp(key, L"GNU.sparse.offset") == 0) {
-			tar->sparse_offset = tar_atol10(value, wcslen(value));
+		if (strcmp(key, "GNU.sparse.offset") == 0) {
+			tar->sparse_offset = tar_atol10(value, strlen(value));
 			if (tar->sparse_numbytes != -1) {
 				gnu_add_sparse_entry(tar,
 				    tar->sparse_offset, tar->sparse_numbytes);
@@ -1358,8 +1399,8 @@
 				tar->sparse_numbytes = -1;
 			}
 		}
-		if (wcscmp(key, L"GNU.sparse.numbytes") == 0) {
-			tar->sparse_numbytes = tar_atol10(value, wcslen(value));
+		if (strcmp(key, "GNU.sparse.numbytes") == 0) {
+			tar->sparse_numbytes = tar_atol10(value, strlen(value));
 			if (tar->sparse_numbytes != -1) {
 				gnu_add_sparse_entry(tar,
 				    tar->sparse_offset, tar->sparse_numbytes);
@@ -1367,12 +1408,13 @@
 				tar->sparse_numbytes = -1;
 			}
 		}
-		if (wcscmp(key, L"GNU.sparse.size") == 0)
-			archive_entry_set_size(entry,
-			    tar_atol10(value, wcslen(value)));
+		if (strcmp(key, "GNU.sparse.size") == 0) {
+			tar->realsize = tar_atol10(value, strlen(value));
+			archive_entry_set_size(entry, tar->realsize);
+		}
 
 		/* GNU "0.1" sparse pax format. */
-		if (wcscmp(key, L"GNU.sparse.map") == 0) {
+		if (strcmp(key, "GNU.sparse.map") == 0) {
 			tar->sparse_gnu_major = 0;
 			tar->sparse_gnu_minor = 1;
 			if (gnu_sparse_01_parse(tar, value) != ARCHIVE_OK)
@@ -1380,19 +1422,26 @@
 		}
 
 		/* GNU "1.0" sparse pax format */
-		if (wcscmp(key, L"GNU.sparse.major") == 0) {
-			tar->sparse_gnu_major = tar_atol10(value, wcslen(value));
+		if (strcmp(key, "GNU.sparse.major") == 0) {
+			tar->sparse_gnu_major = tar_atol10(value, strlen(value));
 			tar->sparse_gnu_pending = 1;
 		}
-		if (wcscmp(key, L"GNU.sparse.minor") == 0) {
-			tar->sparse_gnu_minor = tar_atol10(value, wcslen(value));
+		if (strcmp(key, "GNU.sparse.minor") == 0) {
+			tar->sparse_gnu_minor = tar_atol10(value, strlen(value));
 			tar->sparse_gnu_pending = 1;
 		}
-		if (wcscmp(key, L"GNU.sparse.name") == 0)
-			archive_entry_copy_pathname_w(entry, value);
-		if (wcscmp(key, L"GNU.sparse.realsize") == 0) {
-			tar->sparse_realsize = tar_atol10(value, wcslen(value));
-			archive_entry_set_size(entry, tar->sparse_realsize);
+		if (strcmp(key, "GNU.sparse.name") == 0) {
+			/*
+			 * The real filename; when storing sparse
+			 * files, GNU tar puts a synthesized name into
+			 * the regular 'path' attribute in an attempt
+			 * to limit confusion. ;-)
+			 */
+			archive_strcpy(&(tar->entry_pathname_override), value);
+		}
+		if (strcmp(key, "GNU.sparse.realsize") == 0) {
+			tar->realsize = tar_atol10(value, strlen(value));
+			archive_entry_set_size(entry, tar->realsize);
 		}
 		break;
 	case 'L':
@@ -1402,100 +1451,128 @@
 		if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0)
 			archive_entry_set_xxxxxx(entry, value);
 */
-		if (wcsncmp(key, L"LIBARCHIVE.xattr.", 17)==0)
+		if (strncmp(key, "LIBARCHIVE.xattr.", 17)==0)
 			pax_attribute_xattr(entry, key, value);
 		break;
 	case 'S':
 		/* We support some keys used by the "star" archiver */
-		if (wcscmp(key, L"SCHILY.acl.access")==0)
-			__archive_entry_acl_parse_w(entry, value,
+		if (strcmp(key, "SCHILY.acl.access")==0) {
+			wp = utf8_decode(tar, value, strlen(value));
+			/* TODO: if (wp == NULL) */
+			__archive_entry_acl_parse_w(entry, wp,
 			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-		else if (wcscmp(key, L"SCHILY.acl.default")==0)
-			__archive_entry_acl_parse_w(entry, value,
+		} else if (strcmp(key, "SCHILY.acl.default")==0) {
+			wp = utf8_decode(tar, value, strlen(value));
+			/* TODO: if (wp == NULL) */
+			__archive_entry_acl_parse_w(entry, wp,
 			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
-		else if (wcscmp(key, L"SCHILY.devmajor")==0)
-			archive_entry_set_rdevmajor(entry, tar_atol10(value, wcslen(value)));
-		else if (wcscmp(key, L"SCHILY.devminor")==0)
-			archive_entry_set_rdevminor(entry, tar_atol10(value, wcslen(value)));
-		else if (wcscmp(key, L"SCHILY.fflags")==0)
-			archive_entry_copy_fflags_text_w(entry, value);
-		else if (wcscmp(key, L"SCHILY.dev")==0)
-			archive_entry_set_dev(entry, tar_atol10(value, wcslen(value)));
-		else if (wcscmp(key, L"SCHILY.ino")==0)
-			archive_entry_set_ino(entry, tar_atol10(value, wcslen(value)));
-		else if (wcscmp(key, L"SCHILY.nlink")==0)
-			archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value)));
+		} else if (strcmp(key, "SCHILY.devmajor")==0) {
+			archive_entry_set_rdevmajor(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.devminor")==0) {
+			archive_entry_set_rdevminor(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.fflags")==0) {
+			archive_entry_copy_fflags_text(entry, value);
+		} else if (strcmp(key, "SCHILY.dev")==0) {
+			archive_entry_set_dev(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.ino")==0) {
+			archive_entry_set_ino(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.nlink")==0) {
+			archive_entry_set_nlink(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.realsize")==0) {
+			tar->realsize = tar_atol10(value, strlen(value));
+			archive_entry_set_size(entry, tar->realsize);
+		}
 		break;
 	case 'a':
-		if (wcscmp(key, L"atime")==0) {
+		if (strcmp(key, "atime")==0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_atime(entry, s, n);
 		}
 		break;
 	case 'c':
-		if (wcscmp(key, L"ctime")==0) {
+		if (strcmp(key, "ctime")==0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_ctime(entry, s, n);
-		} else if (wcscmp(key, L"charset")==0) {
+		} else if (strcmp(key, "charset")==0) {
 			/* TODO: Publish charset information in entry. */
-		} else if (wcscmp(key, L"comment")==0) {
+		} else if (strcmp(key, "comment")==0) {
 			/* TODO: Publish comment in entry. */
 		}
 		break;
 	case 'g':
-		if (wcscmp(key, L"gid")==0)
-			archive_entry_set_gid(entry, tar_atol10(value, wcslen(value)));
-		else if (wcscmp(key, L"gname")==0)
-			archive_entry_copy_gname_w(entry, value);
+		if (strcmp(key, "gid")==0) {
+			archive_entry_set_gid(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "gname")==0) {
+			archive_strcpy(&(tar->entry_gname), value);
+		}
+		break;
+	case 'h':
+		if (strcmp(key, "hdrcharset") == 0) {
+			if (strcmp(value, "BINARY") == 0)
+				tar->pax_hdrcharset_binary = 1;
+			else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
+				tar->pax_hdrcharset_binary = 0;
+			else {
+				/* TODO: Warn about unsupported hdrcharset */
+			}
+		}
 		break;
 	case 'l':
 		/* pax interchange doesn't distinguish hardlink vs. symlink. */
-		if (wcscmp(key, L"linkpath")==0) {
-			if (archive_entry_hardlink(entry))
-				archive_entry_copy_hardlink_w(entry, value);
-			else
-				archive_entry_copy_symlink_w(entry, value);
+		if (strcmp(key, "linkpath")==0) {
+			archive_strcpy(&(tar->entry_linkpath), value);
 		}
 		break;
 	case 'm':
-		if (wcscmp(key, L"mtime")==0) {
+		if (strcmp(key, "mtime")==0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_mtime(entry, s, n);
 		}
 		break;
 	case 'p':
-		if (wcscmp(key, L"path")==0)
-			archive_entry_copy_pathname_w(entry, value);
+		if (strcmp(key, "path")==0) {
+			archive_strcpy(&(tar->entry_pathname), value);
+		}
 		break;
 	case 'r':
 		/* POSIX has reserved 'realtime.*' */
 		break;
 	case 's':
 		/* POSIX has reserved 'security.*' */
-		/* Someday: if (wcscmp(key, L"security.acl")==0) { ... } */
-		if (wcscmp(key, L"size")==0) {
+		/* Someday: if (strcmp(key, "security.acl")==0) { ... } */
+		if (strcmp(key, "size")==0) {
 			/* "size" is the size of the data in the entry. */
 			tar->entry_bytes_remaining
-			    = tar_atol10(value, wcslen(value));
+			    = tar_atol10(value, strlen(value));
 			/*
 			 * But, "size" is not necessarily the size of
 			 * the file on disk; if this is a sparse file,
 			 * the disk size may have already been set from
-			 * GNU.sparse.realsize.
+			 * GNU.sparse.realsize or GNU.sparse.size or
+			 * an old GNU header field or SCHILY.realsize
+			 * or ....
 			 */
-			if (tar->sparse_realsize < 0) {
+			if (tar->realsize < 0) {
 				archive_entry_set_size(entry,
 				    tar->entry_bytes_remaining);
-				tar->sparse_realsize
+				tar->realsize
 				    = tar->entry_bytes_remaining;
 			}
 		}
 		break;
 	case 'u':
-		if (wcscmp(key, L"uid")==0)
-			archive_entry_set_uid(entry, tar_atol10(value, wcslen(value)));
-		else if (wcscmp(key, L"uname")==0)
-			archive_entry_copy_uname_w(entry, value);
+		if (strcmp(key, "uid")==0) {
+			archive_entry_set_uid(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "uname")==0) {
+			archive_strcpy(&(tar->entry_uname), value);
+		}
 		break;
 	}
 	return (0);
@@ -1507,7 +1584,7 @@
  * parse a decimal time value, which may include a fractional portion
  */
 static void
-pax_time(const wchar_t *p, int64_t *ps, long *pn)
+pax_time(const char *p, int64_t *ps, long *pn)
 {
 	char digit;
 	int64_t	s;
@@ -1575,9 +1652,9 @@
 
 	/* Copy filename over (to ensure null termination). */
 	header = (const struct archive_entry_header_gnutar *)h;
-	archive_strncpy(&(tar->entry_name), header->name,
+	archive_strncpy(&(tar->entry_pathname), header->name,
 	    sizeof(header->name));
-	archive_entry_set_pathname(entry, tar->entry_name.s);
+	archive_entry_copy_pathname(entry, tar->entry_pathname.s);
 
 	/* Fields common to ustar and GNU */
 	/* XXX Can the following be factored out since it's common
@@ -1585,11 +1662,11 @@
 	 * header_common, perhaps?  */
 	archive_strncpy(&(tar->entry_uname),
 	    header->uname, sizeof(header->uname));
-	archive_entry_set_uname(entry, tar->entry_uname.s);
+	archive_entry_copy_uname(entry, tar->entry_uname.s);
 
 	archive_strncpy(&(tar->entry_gname),
 	    header->gname, sizeof(header->gname));
-	archive_entry_set_gname(entry, tar->entry_gname.s);
+	archive_entry_copy_gname(entry, tar->entry_gname.s);
 
 	/* Parse out device numbers only for char and block specials */
 	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
@@ -1608,8 +1685,9 @@
 	archive_entry_set_ctime(entry,
 	    tar_atol(header->ctime, sizeof(header->ctime)), 0);
 	if (header->realsize[0] != 0) {
-		archive_entry_set_size(entry,
-		    tar_atol(header->realsize, sizeof(header->realsize)));
+		tar->realsize
+		    = tar_atol(header->realsize, sizeof(header->realsize));
+		archive_entry_set_size(entry, tar->realsize);
 	}
 
 	if (header->sparse[0].offset[0] != 0) {
@@ -1742,9 +1820,9 @@
  */
 
 static int
-gnu_sparse_01_parse(struct tar *tar, const wchar_t *p)
+gnu_sparse_01_parse(struct tar *tar, const char *p)
 {
-	const wchar_t *e;
+	const char *e;
 	off_t offset = -1, size = -1;
 
 	for (;;) {
@@ -1779,12 +1857,11 @@
  * don't support this format will extract the block map along with the
  * data and a separate post-process can restore the sparseness.
  *
- * Unfortunately, GNU tar 1.16 adds bogus padding to the end of the
- * entry that depends on the size of the map; this means we have to
- * parse the sparse map when we read the header (otherwise, entry_skip
- * will fail).  This is why sparse_10_read is called from read_header
- * above, instead of at the beginning of read_data, where it "should"
- * go.
+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary
+ * padding to the body of the file when using this format.  GNU tar
+ * 1.17 corrected this bug without bumping the version number, so
+ * it's not possible to support both variants.  This code supports
+ * the later variant at the expense of not supporting the former.
  *
  * This variant also replaced GNU.sparse.size with GNU.sparse.realsize
  * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
@@ -1948,7 +2025,7 @@
  * it does obey locale.
  */
 static int64_t
-tar_atol10(const wchar_t *p, unsigned char_cnt)
+tar_atol10(const char *p, unsigned char_cnt)
 {
 	int64_t l, limit, last_digit_limit;
 	int base, digit, sign;
@@ -1981,10 +2058,7 @@
 /*
  * Parse a base-256 integer.  This is just a straight signed binary
  * value in big-endian order, except that the high-order bit is
- * ignored.  Remember that "int64_t" may or may not be exactly 64
- * bits; the implementation here tries to avoid making any assumptions
- * about the actual size of an int64_t.  It does assume we're using
- * twos-complement arithmetic, though.
+ * ignored.
  */
 static int64_t
 tar_atol256(const char *_p, unsigned char_cnt)
@@ -2082,15 +2156,36 @@
 	}
 }
 
-static int
-utf8_decode(wchar_t *dest, const char *src, size_t length)
+static wchar_t *
+utf8_decode(struct tar *tar, const char *src, size_t length)
 {
-	size_t n;
-	int err;
+	wchar_t *dest;
+	ssize_t n;
+
+	/* Ensure pax_entry buffer is big enough. */
+	if (tar->pax_entry_length <= length) {
+		wchar_t *old_entry = tar->pax_entry;
+
+		if (tar->pax_entry_length <= 0)
+			tar->pax_entry_length = 1024;
+		while (tar->pax_entry_length <= length + 1)
+			tar->pax_entry_length *= 2;
+
+		old_entry = tar->pax_entry;
+		tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
+		    tar->pax_entry_length * sizeof(wchar_t));
+		if (tar->pax_entry == NULL) {
+			free(old_entry);
+			/* TODO: Handle this error. */
+			return (NULL);
+		}
+	}
 
-	err = 0;
+	dest = tar->pax_entry;
 	while (length > 0) {
 		n = UTF8_mbrtowc(dest, src, length);
+		if (n < 0)
+			return (NULL);
 		if (n == 0)
 			break;
 		dest++;
@@ -2098,13 +2193,13 @@
 		length -= n;
 	}
 	*dest++ = L'\0';
-	return (err);
+	return (tar->pax_entry);
 }
 
 /*
  * Copied and simplified from FreeBSD libc/locale.
  */
-static size_t
+static ssize_t
 UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
 {
         int ch, i, len, mask;
@@ -2131,22 +2226,14 @@
         } else if ((ch & 0xf8) == 0xf0) {
                 mask = 0x07;
                 len = 4;
-        } else if ((ch & 0xfc) == 0xf8) {
-                mask = 0x03;
-                len = 5;
-        } else if ((ch & 0xfe) == 0xfc) {
-                mask = 0x01;
-                len = 6;
         } else {
-		/* Invalid first byte; convert to '?' */
-		*pwc = '?';
-		return (1);
+		/* Invalid first byte. */
+		return (-1);
         }
 
         if (n < (size_t)len) {
-		/* Invalid first byte; convert to '?' */
-		*pwc = '?';
-                return (1);
+		/* Valid first byte but truncated. */
+                return (-2);
 	}
 
         /*
@@ -2192,7 +2279,7 @@
  * omits line breaks; RFC1341 used for MIME requires both.)
  */
 static char *
-base64_decode(const wchar_t *src, size_t len, size_t *out_len)
+base64_decode(const char *s, size_t len, size_t *out_len)
 {
 	static const unsigned char digits[64] = {
 		'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
@@ -2202,6 +2289,7 @@
 		'4','5','6','7','8','9','+','/' };
 	static unsigned char decode_table[128];
 	char *out, *d;
+	const unsigned char *src = (const unsigned char *)s;
 
 	/* If the decode table is not yet initialized, prepare it. */
 	if (decode_table[digits[1]] != 1) {
@@ -2213,7 +2301,7 @@
 
 	/* Allocate enough space to hold the entire output. */
 	/* Note that we may not use all of this... */
-	out = (char *)malloc((len * 3 + 3) / 4);
+	out = (char *)malloc(len - len / 4 + 1);
 	if (out == NULL) {
 		*out_len = 0;
 		return (NULL);
@@ -2262,43 +2350,6 @@
 	return (out);
 }
 
-/*
- * This is a little tricky because the C99 standard wcstombs()
- * function returns the number of bytes that were converted,
- * not the number that should be converted.  As a result,
- * we can never accurately size the output buffer (without
- * doing a tedious output size calculation in advance).
- * This approach (try a conversion, then try again if it fails)
- * will almost always succeed on the first try, and is thus
- * much faster, at the cost of sometimes requiring multiple
- * passes while we expand the buffer.
- */
-static char *
-wide_to_narrow(const wchar_t *wval)
-{
-	int converted_length;
-	/* Guess an output buffer size and try the conversion. */
-	int alloc_length = wcslen(wval) * 3;
-	char *mbs_val = (char *)malloc(alloc_length + 1);
-	if (mbs_val == NULL)
-		return (NULL);
-	converted_length = wcstombs(mbs_val, wval, alloc_length);
-
-	/* If we exhausted the buffer, resize and try again. */
-	while (converted_length >= alloc_length) {
-		free(mbs_val);
-		alloc_length *= 2;
-		mbs_val = (char *)malloc(alloc_length + 1);
-		if (mbs_val == NULL)
-			return (NULL);
-		converted_length = wcstombs(mbs_val, wval, alloc_length);
-	}
-
-	/* Ensure a trailing null and return the final string. */
-	mbs_val[alloc_length] = '\0';
-	return (mbs_val);
-}
-
 static char *
 url_decode(const char *in)
 {
@@ -2309,7 +2360,7 @@
 	if (out == NULL)
 		return (NULL);
 	for (s = in, d = out; *s != '\0'; ) {
-		if (*s == '%') {
+		if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') {
 			/* Try to convert % escape */
 			int digit1 = tohex(s[1]);
 			int digit2 = tohex(s[2]);
--- /dev/null
+++ lib/libarchive/archive_endian.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2002 Thomas Moestl <tmm at FreeBSD.org>
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_endian.h,v 1.1.2.3 2008/08/10 04:32:47 kientzle Exp $
+ *
+ * Borrowed from FreeBSD's <sys/endian.h>
+ */
+
+/* Note:  This is a purely internal header! */
+/* Do not use this outside of libarchive internal code! */
+
+#ifndef ARCHIVE_ENDIAN_H_INCLUDED
+#define ARCHIVE_ENDIAN_H_INCLUDED
+
+
+/* Watcom C++ doesn't support 'inline' in C code.  (For any version?) */
+#if defined( __WATCOMC__ )
+	#define	inline
+#endif
+
+/* Visual C++ 6.0 doesn't support 'inline' in C code.  (Does VC7? VC8?) */
+#if defined(_MSC_VER)
+	#define	inline
+#endif
+
+/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
+
+static inline uint16_t
+archive_be16dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return ((p[0] << 8) | p[1]);
+}
+
+static inline uint32_t
+archive_be32dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+}
+
+static inline uint64_t
+archive_be64dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
+}
+
+static inline uint16_t
+archive_le16dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return ((p[1] << 8) | p[0]);
+}
+
+static inline uint32_t
+archive_le32dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+static inline uint64_t
+archive_le64dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
+}
+
+static inline void
+archive_be16enc(void *pp, uint16_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = (u >> 8) & 0xff;
+	p[1] = u & 0xff;
+}
+
+static inline void
+archive_be32enc(void *pp, uint32_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = (u >> 24) & 0xff;
+	p[1] = (u >> 16) & 0xff;
+	p[2] = (u >> 8) & 0xff;
+	p[3] = u & 0xff;
+}
+
+static inline void
+archive_be64enc(void *pp, uint64_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	archive_be32enc(p, u >> 32);
+	archive_be32enc(p + 4, u & 0xffffffff);
+}
+
+static inline void
+archive_le16enc(void *pp, uint16_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = u & 0xff;
+	p[1] = (u >> 8) & 0xff;
+}
+
+static inline void
+archive_le32enc(void *pp, uint32_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = u & 0xff;
+	p[1] = (u >> 8) & 0xff;
+	p[2] = (u >> 16) & 0xff;
+	p[3] = (u >> 24) & 0xff;
+}
+
+static inline void
+archive_le64enc(void *pp, uint64_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	archive_le32enc(p, u & 0xffffffff);
+	archive_le32enc(p + 4, u >> 32);
+}
+
+#endif
Index: archive_read_support_compression_none.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_none.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L lib/libarchive/archive_read_support_compression_none.c -L lib/libarchive/archive_read_support_compression_none.c -u -r1.3 -r1.4
--- lib/libarchive/archive_read_support_compression_none.c
+++ lib/libarchive/archive_read_support_compression_none.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.17.2.1 2007/11/02 08:42:14 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.17.2.2 2008/02/11 00:31:07 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -170,56 +170,45 @@
 		min = state->buffer_size;
 
 	/*
-	 * Try to satisfy the request directly from the client
-	 * buffer.  We can do this if all of the data in the copy
-	 * buffer was copied from the current client buffer.  This
-	 * also covers the case where the copy buffer is empty and
-	 * the client buffer has all the data we need.
+	 * Keep pulling more data until we can satisfy the request.
 	 */
-	if (state->client_total >= state->client_avail + state->avail
-	    && state->client_avail + state->avail >= min) {
-		state->client_avail += state->avail;
-		state->client_next -= state->avail;
-		state->avail = 0;
-		state->next = state->buffer;
-		*buff = state->client_next;
-		return (state->client_avail);
-	}
+	for (;;) {
 
-	/*
-	 * If we can't use client buffer, we'll have to use copy buffer.
-	 */
+		/*
+		 * If we can satisfy from the copy buffer, we're done.
+		 */
+		if (state->avail >= min) {
+			*buff = state->next;
+			return (state->avail);
+		}
 
-	/* Move data forward in copy buffer if necessary. */
-	if (state->next > state->buffer &&
-	    state->next + min > state->buffer + state->buffer_size) {
-		if (state->avail > 0)
-			memmove(state->buffer, state->next, state->avail);
-		state->next = state->buffer;
-	}
+		/*
+		 * We can satisfy directly from client buffer if everything
+		 * currently in the copy buffer is still in the client buffer.
+		 */
+		if (state->client_total >= state->client_avail + state->avail
+		    && state->client_avail + state->avail >= min) {
+			/* "Roll back" to client buffer. */
+			state->client_avail += state->avail;
+			state->client_next -= state->avail;
+			/* Copy buffer is now empty. */
+			state->avail = 0;
+			state->next = state->buffer;
+			/* Return data from client buffer. */
+			*buff = state->client_next;
+			return (state->client_avail);
+		}
 
-	/* Collect data in copy buffer to fulfill request. */
-	while (state->avail < min) {
-		/* Copy data from client buffer to our copy buffer. */
-		if (state->client_avail > 0) {
-			/* First estimate: copy to fill rest of buffer. */
-			size_t tocopy = (state->buffer + state->buffer_size)
-			    - (state->next + state->avail);
-			/* Don't copy more than is available. */
-			if (tocopy > state->client_avail)
-				tocopy = state->client_avail;
-			memcpy(state->next + state->avail, state->client_next,
-			    tocopy);
-			state->client_next += tocopy;
-			state->client_avail -= tocopy;
-			state->avail += tocopy;
-		} else {
-			/* There is no more client data: fetch more. */
-			/*
-			 * It seems to me that const void ** and const
-			 * char ** should be compatible, but they
-			 * aren't, hence the cast.
-			 */
+		/* Move data forward in copy buffer if necessary. */
+		if (state->next > state->buffer &&
+		    state->next + min > state->buffer + state->buffer_size) {
+			if (state->avail > 0)
+				memmove(state->buffer, state->next, state->avail);
+			state->next = state->buffer;
+		}
+
+		/* If we've used up the client data, get more. */
+		if (state->client_avail <= 0) {
 			bytes_read = (a->client_reader)(&a->archive,
 			    a->client_data, &state->client_buff);
 			if (bytes_read < 0) {		/* Read error. */
@@ -232,17 +221,33 @@
 				state->client_total = state->client_avail = 0;
 				state->client_next = state->client_buff = NULL;
 				state->end_of_file = 1;
-				break;
+				/* Return whatever we do have. */
+				*buff = state->next;
+				return (state->avail);
 			}
 			a->archive.raw_position += bytes_read;
 			state->client_total = bytes_read;
 			state->client_avail = state->client_total;
 			state->client_next = state->client_buff;
 		}
+		else
+		{
+			/* We can add client data to copy buffer. */
+			/* First estimate: copy to fill rest of buffer. */
+			size_t tocopy = (state->buffer + state->buffer_size)
+			    - (state->next + state->avail);
+			/* Don't copy more than is available. */
+			if (tocopy > state->client_avail)
+				tocopy = state->client_avail;
+			memcpy(state->next + state->avail, state->client_next,
+			    tocopy);
+			/* Remove this data from client buffer. */
+			state->client_next += tocopy;
+			state->client_avail -= tocopy;
+			/* add it to copy buffer. */
+			state->avail += tocopy;
+		}
 	}
-
-	*buff = state->next;
-	return (state->avail);
 }
 
 /*
--- /dev/null
+++ lib/libarchive/archive_entry_link_resolver.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/archive_entry_link_resolver.c,v 1.1.2.2 2008/08/10 04:32:47 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+/*
+ * This is mostly a pretty straightforward hash table implementation.
+ * The only interesting bit is the different strategies used to
+ * match up links.  These strategies match those used by various
+ * archiving formats:
+ *   tar - content stored with first link, remainder refer back to it.
+ *       This requires us to match each subsequent link up with the
+ *       first appearance.
+ *   cpio - Old cpio just stored body with each link, match-ups were
+ *       implicit.  This is trivial.
+ *   new cpio - New cpio only stores body with last link, match-ups
+ *       are implicit.  This is actually quite tricky; see the notes
+ *       below.
+ */
+
+/* Users pass us a format code, we translate that into a strategy here. */
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR	0
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
+
+/* Initial size of link cache. */
+#define	links_cache_initial_size 1024
+
+struct links_entry {
+	struct links_entry	*next;
+	struct links_entry	*previous;
+	int			 links; /* # links not yet seen */
+	int			 hash;
+	struct archive_entry	*canonical;
+	struct archive_entry	*entry;
+};
+
+struct archive_entry_linkresolver {
+	struct links_entry	**buckets;
+	struct links_entry	 *spare;
+	unsigned long		  number_entries;
+	size_t			  number_buckets;
+	int			  strategy;
+};
+
+static struct links_entry *find_entry(struct archive_entry_linkresolver *,
+		    struct archive_entry *);
+static void grow_hash(struct archive_entry_linkresolver *);
+static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
+		    struct archive_entry *);
+static struct links_entry *next_entry(struct archive_entry_linkresolver *);
+
+struct archive_entry_linkresolver *
+archive_entry_linkresolver_new(void)
+{
+	struct archive_entry_linkresolver *res;
+	size_t i;
+
+	res = malloc(sizeof(struct archive_entry_linkresolver));
+	if (res == NULL)
+		return (NULL);
+	memset(res, 0, sizeof(struct archive_entry_linkresolver));
+	res->number_buckets = links_cache_initial_size;
+	res->buckets = malloc(res->number_buckets *
+	    sizeof(res->buckets[0]));
+	if (res->buckets == NULL) {
+		free(res);
+		return (NULL);
+	}
+	for (i = 0; i < res->number_buckets; i++)
+		res->buckets[i] = NULL;
+	return (res);
+}
+
+void
+archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
+    int fmt)
+{
+	int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
+
+	switch (fmtbase) {
+	case ARCHIVE_FORMAT_CPIO:
+		switch (fmt) {
+		case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
+		case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
+			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
+			break;
+		default:
+			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+			break;
+		}
+		break;
+	case ARCHIVE_FORMAT_MTREE:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
+		break;
+	case ARCHIVE_FORMAT_TAR:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
+		break;
+	default:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
+		break;
+	}
+}
+
+void
+archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
+{
+	struct links_entry *le;
+
+	if (res == NULL)
+		return;
+
+	if (res->buckets != NULL) {
+		while ((le = next_entry(res)) != NULL)
+			archive_entry_free(le->entry);
+		free(res->buckets);
+		res->buckets = NULL;
+	}
+	free(res);
+}
+
+void
+archive_entry_linkify(struct archive_entry_linkresolver *res,
+    struct archive_entry **e, struct archive_entry **f)
+{
+	struct links_entry *le;
+	struct archive_entry *t;
+
+	*f = NULL; /* Default: Don't return a second entry. */
+
+	if (*e == NULL) {
+		le = next_entry(res);
+		if (le != NULL) {
+			*e = le->entry;
+			le->entry = NULL;
+		}
+		return;
+	}
+
+	/* If it has only one link, then we're done. */
+	if (archive_entry_nlink(*e) == 1)
+		return;
+	/* Directories never have hardlinks. */
+	if (archive_entry_filetype(*e) == AE_IFDIR)
+		return;
+
+	switch (res->strategy) {
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+			archive_entry_set_size(*e, 0);
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+		} else
+			insert_entry(res, *e);
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+		} else
+			insert_entry(res, *e);
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
+		/* This one is trivial. */
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+			/*
+			 * Put the new entry in le, return the
+			 * old entry from le.
+			 */
+			t = *e;
+			*e = le->entry;
+			le->entry = t;
+			/* Make the old entry into a hardlink. */
+			archive_entry_set_size(*e, 0);
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+			/* If we ran out of links, return the
+			 * final entry as well. */
+			if (le->links == 0) {
+				*f = le->entry;
+				le->entry = NULL;
+			}
+		} else {
+			/*
+			 * If we haven't seen it, tuck it away
+			 * for future use.
+			 */
+			le = insert_entry(res, *e);
+			le->entry = *e;
+			*e = NULL;
+		}
+		return;
+	default:
+		break;
+	}
+	return;
+}
+
+static struct links_entry *
+find_entry(struct archive_entry_linkresolver *res,
+    struct archive_entry *entry)
+{
+	struct links_entry	*le;
+	int			 hash, bucket;
+	dev_t			 dev;
+	ino_t			 ino;
+
+	/* Free a held entry. */
+	if (res->spare != NULL) {
+		archive_entry_free(res->spare->canonical);
+		archive_entry_free(res->spare->entry);
+		free(res->spare);
+		res->spare = NULL;
+	}
+
+	/* If the links cache overflowed and got flushed, don't bother. */
+	if (res->buckets == NULL)
+		return (NULL);
+
+	dev = archive_entry_dev(entry);
+	ino = archive_entry_ino(entry);
+	hash = dev ^ ino;
+
+	/* Try to locate this entry in the links cache. */
+	bucket = hash % res->number_buckets;
+	for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+		if (le->hash == hash
+		    && dev == archive_entry_dev(le->canonical)
+		    && ino == archive_entry_ino(le->canonical)) {
+			/*
+			 * Decrement link count each time and release
+			 * the entry if it hits zero.  This saves
+			 * memory and is necessary for detecting
+			 * missed links.
+			 */
+			--le->links;
+			if (le->links > 0)
+				return (le);
+			/* Remove it from this hash bucket. */
+			if (le->previous != NULL)
+				le->previous->next = le->next;
+			if (le->next != NULL)
+				le->next->previous = le->previous;
+			if (res->buckets[bucket] == le)
+				res->buckets[bucket] = le->next;
+			res->number_entries--;
+			/* Defer freeing this entry. */
+			res->spare = le;
+			return (le);
+		}
+	}
+	return (NULL);
+}
+
+static struct links_entry *
+next_entry(struct archive_entry_linkresolver *res)
+{
+	struct links_entry	*le;
+	size_t			 bucket;
+
+	/* Free a held entry. */
+	if (res->spare != NULL) {
+		archive_entry_free(res->spare->canonical);
+		free(res->spare);
+		res->spare = NULL;
+	}
+
+	/* If the links cache overflowed and got flushed, don't bother. */
+	if (res->buckets == NULL)
+		return (NULL);
+
+	/* Look for next non-empty bucket in the links cache. */
+	for (bucket = 0; bucket < res->number_buckets; bucket++) {
+		le = res->buckets[bucket];
+		if (le != NULL) {
+			/* Remove it from this hash bucket. */
+			if (le->next != NULL)
+				le->next->previous = le->previous;
+			res->buckets[bucket] = le->next;
+			res->number_entries--;
+			/* Defer freeing this entry. */
+			res->spare = le;
+			return (le);
+		}
+	}
+	return (NULL);
+}
+
+static struct links_entry *
+insert_entry(struct archive_entry_linkresolver *res,
+    struct archive_entry *entry)
+{
+	struct links_entry *le;
+	int			 hash, bucket;
+
+	/* Add this entry to the links cache. */
+	le = malloc(sizeof(struct links_entry));
+	if (le == NULL)
+		return (NULL);
+	memset(le, 0, sizeof(*le));
+	le->canonical = archive_entry_clone(entry);
+
+	/* If the links cache is getting too full, enlarge the hash table. */
+	if (res->number_entries > res->number_buckets * 2)
+		grow_hash(res);
+
+	hash = archive_entry_dev(entry) ^ archive_entry_ino(entry);
+	bucket = hash % res->number_buckets;
+
+	/* If we could allocate the entry, record it. */
+	if (res->buckets[bucket] != NULL)
+		res->buckets[bucket]->previous = le;
+	res->number_entries++;
+	le->next = res->buckets[bucket];
+	le->previous = NULL;
+	res->buckets[bucket] = le;
+	le->hash = hash;
+	le->links = archive_entry_nlink(entry) - 1;
+	return (le);
+}
+
+static void
+grow_hash(struct archive_entry_linkresolver *res)
+{
+	struct links_entry *le, **new_buckets;
+	size_t new_size;
+	size_t i, bucket;
+
+	/* Try to enlarge the bucket list. */
+	new_size = res->number_buckets * 2;
+	new_buckets = malloc(new_size * sizeof(struct links_entry *));
+
+	if (new_buckets != NULL) {
+		memset(new_buckets, 0,
+		    new_size * sizeof(struct links_entry *));
+		for (i = 0; i < res->number_buckets; i++) {
+			while (res->buckets[i] != NULL) {
+				/* Remove entry from old bucket. */
+				le = res->buckets[i];
+				res->buckets[i] = le->next;
+
+				/* Add entry to new bucket. */
+				bucket = le->hash % new_size;
+
+				if (new_buckets[bucket] != NULL)
+					new_buckets[bucket]->previous =
+					    le;
+				le->next = new_buckets[bucket];
+				le->previous = NULL;
+				new_buckets[bucket] = le;
+			}
+		}
+		free(res->buckets);
+		res->buckets = new_buckets;
+		res->number_buckets = new_size;
+	}
+}
Index: archive_write_set_format_pax.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_pax.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write_set_format_pax.c -L lib/libarchive/archive_write_set_format_pax.c -u -r1.2 -r1.3
--- lib/libarchive/archive_write_set_format_pax.c
+++ lib/libarchive/archive_write_set_format_pax.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.41 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.41.2.4 2008/08/10 06:09:25 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -85,8 +85,8 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	int r;
 	r = archive_write_set_format_pax(&a->archive);
-	a->archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
-	a->archive_format_name = "restricted POSIX pax interchange";
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
+	a->archive.archive_format_name = "restricted POSIX pax interchange";
 	return (r);
 }
 
@@ -116,8 +116,8 @@
 	a->format_finish = archive_write_pax_finish;
 	a->format_destroy = archive_write_pax_destroy;
 	a->format_finish_entry = archive_write_pax_finish_entry;
-	a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
-	a->archive_format_name = "POSIX pax interchange";
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+	a->archive.archive_format_name = "POSIX pax interchange";
 	return (ARCHIVE_OK);
 }
 
@@ -383,19 +383,25 @@
     struct archive_entry *entry_original)
 {
 	struct archive_entry *entry_main;
-	const char *linkname, *p;
+	const char *p;
 	char *t;
-	const char *hardlink;
 	const wchar_t *wp;
-	const char *suffix_start;
+	const char *suffix;
 	int need_extension, r, ret;
 	struct pax *pax;
+	const char *hdrcharset = NULL;
+	const char *hardlink;
+	const char *path = NULL, *linkpath = NULL;
+	const char *uname = NULL, *gname = NULL;
+	const wchar_t *path_w = NULL, *linkpath_w = NULL;
+	const wchar_t *uname_w = NULL, *gname_w = NULL;
 
 	char paxbuff[512];
 	char ustarbuff[512];
 	char ustar_entry_name[256];
 	char pax_entry_name[256];
 
+	ret = ARCHIVE_OK;
 	need_extension = 0;
 	pax = (struct pax *)a->format_data;
 
@@ -418,12 +424,15 @@
 			p = archive_entry_pathname(entry_original);
 			if (p[strlen(p) - 1] != '/') {
 				t = (char *)malloc(strlen(p) + 2);
-				if (t != NULL) {
-					strcpy(t, p);
-					strcat(t, "/");
-					archive_entry_copy_pathname(entry_original, t);
-					free(t);
+				if (t == NULL) {
+					archive_set_error(&a->archive, ENOMEM,
+					"Can't allocate pax data");
+					return(ARCHIVE_FATAL);
 				}
+				strcpy(t, p);
+				strcat(t, "/");
+				archive_entry_copy_pathname(entry_original, t);
+				free(t);
 			}
 			break;
 		default:
@@ -439,53 +448,148 @@
 	archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
 
 	/*
-	 * Determining whether or not the name is too big is ugly
-	 * because of the rules for dividing names between 'name' and
-	 * 'prefix' fields.  Here, I pick out the longest possible
-	 * suffix, then test whether the remaining prefix is too long.
+	 * First, check the name fields and see if any of them
+	 * require binary coding.  If any of them does, then all of
+	 * them do.
 	 */
-	wp = archive_entry_pathname_w(entry_main);
-	p = archive_entry_pathname(entry_main);
-	if (strlen(p) <= 100)	/* Short enough for just 'name' field */
-		suffix_start = p;	/* Record a zero-length prefix */
-	else
-		/* Find the largest suffix that fits in 'name' field. */
-		suffix_start = strchr(p + strlen(p) - 100 - 1, '/');
+	hdrcharset = NULL;
+	path = archive_entry_pathname(entry_main);
+	path_w = archive_entry_pathname_w(entry_main);
+	if (path != NULL && path_w == NULL) {
+		archive_set_error(&a->archive, EILSEQ,
+		    "Can't translate pathname '%s' to UTF-8", path);
+		ret = ARCHIVE_WARN;
+		hdrcharset = "BINARY";
+	}
+	uname = archive_entry_uname(entry_main);
+	uname_w = archive_entry_uname_w(entry_main);
+	if (uname != NULL && uname_w == NULL) {
+		archive_set_error(&a->archive, EILSEQ,
+		    "Can't translate uname '%s' to UTF-8", uname);
+		ret = ARCHIVE_WARN;
+		hdrcharset = "BINARY";
+	}
+	gname = archive_entry_gname(entry_main);
+	gname_w = archive_entry_gname_w(entry_main);
+	if (gname != NULL && gname_w == NULL) {
+		archive_set_error(&a->archive, EILSEQ,
+		    "Can't translate gname '%s' to UTF-8", gname);
+		ret = ARCHIVE_WARN;
+		hdrcharset = "BINARY";
+	}
+	linkpath = hardlink;
+	if (linkpath != NULL) {
+		linkpath_w = archive_entry_hardlink_w(entry_main);
+	} else {
+		linkpath = archive_entry_symlink(entry_main);
+		if (linkpath != NULL)
+			linkpath_w = archive_entry_symlink_w(entry_main);
+	}
+	if (linkpath != NULL && linkpath_w == NULL) {
+		archive_set_error(&a->archive, EILSEQ,
+		    "Can't translate linkpath '%s' to UTF-8", linkpath);
+		ret = ARCHIVE_WARN;
+		hdrcharset = "BINARY";
+	}
+
+	/* Store the header encoding first, to be nice to readers. */
+	if (hdrcharset != NULL)
+		add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset);
+
 
 	/*
 	 * If name is too long, or has non-ASCII characters, add
-	 * 'path' to pax extended attrs.
+	 * 'path' to pax extended attrs.  (Note that an unconvertible
+	 * name must have non-ASCII characters.)
 	 */
-	if (suffix_start == NULL || suffix_start - p > 155 || has_non_ASCII(wp)) {
-		add_pax_attr_w(&(pax->pax_header), "path", wp);
+	if (path == NULL) {
+		/* We don't have a narrow version, so we have to store
+		 * the wide version. */
+		add_pax_attr_w(&(pax->pax_header), "path", path_w);
+		archive_entry_set_pathname(entry_main, "@WidePath");
+		need_extension = 1;
+	} else if (has_non_ASCII(path_w)) {
+		/* We have non-ASCII characters. */
+		if (path_w == NULL || hdrcharset != NULL) {
+			/* Can't do UTF-8, so store it raw. */
+			add_pax_attr(&(pax->pax_header), "path", path);
+		} else {
+			/* Store UTF-8 */
+			add_pax_attr_w(&(pax->pax_header),
+			    "path", path_w);
+		}
 		archive_entry_set_pathname(entry_main,
-		    build_ustar_entry_name(ustar_entry_name, p, strlen(p), NULL));
+		    build_ustar_entry_name(ustar_entry_name,
+			path, strlen(path), NULL));
 		need_extension = 1;
+	} else {
+		/* We have an all-ASCII path; we'd like to just store
+		 * it in the ustar header if it will fit.  Yes, this
+		 * duplicates some of the logic in
+		 * write_set_format_ustar.c
+		 */
+		if (strlen(path) <= 100) {
+			/* Fits in the old 100-char tar name field. */
+		} else {
+			/* Find largest suffix that will fit. */
+			/* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */
+			suffix = strchr(path + strlen(path) - 100 - 1, '/');
+			/* Don't attempt an empty prefix. */
+			if (suffix == path)
+				suffix = strchr(suffix + 1, '/');
+			/* We can put it in the ustar header if it's
+			 * all ASCII and it's either <= 100 characters
+			 * or can be split at a '/' into a prefix <=
+			 * 155 chars and a suffix <= 100 chars.  (Note
+			 * the strchr() above will return NULL exactly
+			 * when the path can't be split.)
+			 */
+			if (suffix == NULL       /* Suffix > 100 chars. */
+			    || suffix[1] == '\0'    /* empty suffix */
+			    || suffix - path > 155)  /* Prefix > 155 chars */
+			{
+				if (path_w == NULL || hdrcharset != NULL) {
+					/* Can't do UTF-8, so store it raw. */
+					add_pax_attr(&(pax->pax_header),
+					    "path", path);
+				} else {
+					/* Store UTF-8 */
+					add_pax_attr_w(&(pax->pax_header),
+					    "path", path_w);
+				}
+				archive_entry_set_pathname(entry_main,
+				    build_ustar_entry_name(ustar_entry_name,
+					path, strlen(path), NULL));
+				need_extension = 1;
+			}
+		}
 	}
 
-	/* If link name is too long or has non-ASCII characters, add
-	 * 'linkpath' to pax extended attrs. */
-	linkname = hardlink;
-	if (linkname == NULL)
-		linkname = archive_entry_symlink(entry_main);
-
-	if (linkname != NULL) {
-		/* There is a link name, get the wide version as well. */
-		if (hardlink != NULL)
-			wp = archive_entry_hardlink_w(entry_main);
-		else
-			wp = archive_entry_symlink_w(entry_main);
-
-		/* If the link is long or has a non-ASCII character,
-		 * store it as a pax extended attribute. */
-		if (strlen(linkname) > 100 || has_non_ASCII(wp)) {
-			add_pax_attr_w(&(pax->pax_header), "linkpath", wp);
-			if (hardlink != NULL)
-				archive_entry_set_hardlink(entry_main,
-				    "././@LongHardLink");
+	if (linkpath != NULL) {
+		/* If link name is too long or has non-ASCII characters, add
+		 * 'linkpath' to pax extended attrs. */
+		if (strlen(linkpath) > 100 || linkpath_w == NULL
+		    || linkpath_w == NULL || has_non_ASCII(linkpath_w)) {
+			if (linkpath_w == NULL || hdrcharset != NULL)
+				/* If the linkpath is not convertible
+				 * to wide, or we're encoding in
+				 * binary anyway, store it raw. */
+				add_pax_attr(&(pax->pax_header),
+				    "linkpath", linkpath);
 			else
-				archive_entry_set_symlink(entry_main,
-				    "././@LongSymLink");
+				/* If the link is long or has a
+				 * non-ASCII character, store it as a
+				 * pax extended attribute. */
+				add_pax_attr_w(&(pax->pax_header),
+				    "linkpath", linkpath_w);
+			if (strlen(linkpath) > 100) {
+				if (hardlink != NULL)
+					archive_entry_set_hardlink(entry_main,
+					    "././@LongHardLink");
+				else
+					archive_entry_set_symlink(entry_main,
+					    "././@LongSymLink");
+			}
 			need_extension = 1;
 		}
 	}
@@ -506,12 +610,20 @@
 
 	/* If group name is too large or has non-ASCII characters, add
 	 * 'gname' to pax extended attrs. */
-	p = archive_entry_gname(entry_main);
-	wp = archive_entry_gname_w(entry_main);
-	if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) {
-		add_pax_attr_w(&(pax->pax_header), "gname", wp);
-		archive_entry_set_gname(entry_main, NULL);
-		need_extension = 1;
+	if (gname != NULL) {
+		if (strlen(gname) > 31
+		    || gname_w == NULL
+		    || has_non_ASCII(gname_w))
+		{
+			if (gname_w == NULL || hdrcharset != NULL) {
+				add_pax_attr(&(pax->pax_header),
+				    "gname", gname);
+			} else  {
+				add_pax_attr_w(&(pax->pax_header),
+				    "gname", gname_w);
+			}
+			need_extension = 1;
+		}
 	}
 
 	/* If numeric UID is too large, add 'uid' to pax extended attrs. */
@@ -521,14 +633,21 @@
 		need_extension = 1;
 	}
 
-	/* If user name is too large, add 'uname' to pax extended attrs. */
-	/* TODO: If uname has non-ASCII characters, use pax attribute. */
-	p = archive_entry_uname(entry_main);
-	wp = archive_entry_uname_w(entry_main);
-	if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) {
-		add_pax_attr_w(&(pax->pax_header), "uname", wp);
-		archive_entry_set_uname(entry_main, NULL);
-		need_extension = 1;
+	/* Add 'uname' to pax extended attrs if necessary. */
+	if (uname != NULL) {
+		if (strlen(uname) > 31
+		    || uname_w == NULL
+		    || has_non_ASCII(uname_w))
+		{
+			if (uname_w == NULL || hdrcharset != NULL) {
+				add_pax_attr(&(pax->pax_header),
+				    "uname", uname);
+			} else {
+				add_pax_attr_w(&(pax->pax_header),
+				    "uname", uname_w);
+			}
+			need_extension = 1;
+		}
 	}
 
 	/*
@@ -621,7 +740,7 @@
 	 * already set (we're already generating an extended header, so
 	 * may as well include these).
 	 */
-	if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
 	    need_extension) {
 
 		if (archive_entry_mtime(entry_main) < 0  ||
@@ -684,7 +803,7 @@
 	 * Pax-restricted does not store data for hardlinks, in order
 	 * to improve compatibility with ustar.
 	 */
-	if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
 	    hardlink != NULL)
 		archive_entry_set_size(entry_main, 0);
 
@@ -730,7 +849,6 @@
 	__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0);
 
 	/* If we built any extended attributes, write that entry first. */
-	ret = ARCHIVE_OK;
 	if (archive_strlen(&(pax->pax_header)) > 0) {
 		struct archive_entry *pax_attr_entry;
 		time_t s;
@@ -790,13 +908,13 @@
 		/* Standard ustar doesn't support ctime. */
 		archive_entry_set_ctime(pax_attr_entry, 0, 0);
 
-		ret = __archive_write_format_header_ustar(a, paxbuff,
+		r = __archive_write_format_header_ustar(a, paxbuff,
 		    pax_attr_entry, 'x', 1);
 
 		archive_entry_free(pax_attr_entry);
 
 		/* Note that the 'x' header shouldn't ever fail to format */
-		if (ret != 0) {
+		if (r != 0) {
 			const char *msg = "archive_write_pax_header: "
 			    "'x' header failed?!  This can't happen.\n";
 			write(2, msg, strlen(msg));
@@ -981,23 +1099,31 @@
 
 /*
  * The ustar header for the pax extended attributes must have a
- * reasonable name:  SUSv3 suggests 'dirname'/PaxHeader/'filename'
+ * reasonable name:  SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename'
+ * where 'pid' is the PID of the archiving process.  Unfortunately,
+ * that makes testing a pain since the output varies for each run,
+ * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename'
+ * for now.  (Someday, I'll make this settable.  Then I can use the
+ * SUS recommendation as default and test harnesses can override it
+ * to get predictable results.)
  *
- * Joerg Schiling has argued that this is unnecessary because, in practice,
- * if the pax extended attributes get extracted as regular files, noone is
- * going to bother reading those attributes to manually restore them.
- * Based on this, 'star' uses /tmp/PaxHeader/'basename' as the ustar header
- * name.  This is a tempting argument, but I'm not entirely convinced.
- * I'm also uncomfortable with the fact that "/tmp" is a Unix-ism.
+ * Joerg Schilling has argued that this is unnecessary because, in
+ * practice, if the pax extended attributes get extracted as regular
+ * files, noone is going to bother reading those attributes to
+ * manually restore them.  Based on this, 'star' uses
+ * /tmp/PaxHeader/'basename' as the ustar header name.  This is a
+ * tempting argument, in part because it's simpler than the SUSv3
+ * recommendation, but I'm not entirely convinced.  I'm also
+ * uncomfortable with the fact that "/tmp" is a Unix-ism.
  *
- * The following routine implements the SUSv3 recommendation, and is
- * much simpler because build_ustar_entry_name() above already does
- * most of the work (we just need to give it an extra path element to
- * insert and handle a few pathological cases).
+ * The following routine leverages build_ustar_entry_name() above and
+ * so is simpler than you might think.  It just needs to provide the
+ * additional path element and handle a few pathological cases).
  */
 static char *
 build_pax_attribute_name(char *dest, const char *src)
 {
+	char buff[64];
 	const char *p;
 
 	/* Handle the null filename case. */
@@ -1036,8 +1162,19 @@
 		return (dest);
 	}
 
+	/*
+	 * TODO: Push this string into the 'pax' structure to avoid
+	 * recomputing it every time.  That will also open the door
+	 * to having clients override it.
+	 */
+#if HAVE_GETPID && 0  /* Disable this for now; see above comment. */
+	sprintf(buff, "PaxHeader.%d", getpid());
+#else
+	/* If the platform can't fetch the pid, don't include it. */
+	strcpy(buff, "PaxHeader");
+#endif
 	/* General case: build a ustar-compatible name adding "/PaxHeader/". */
-	build_ustar_entry_name(dest, src, p - src, "PaxHeader");
+	build_ustar_entry_name(dest, src, p - src, buff);
 
 	return (dest);
 }
@@ -1063,6 +1200,9 @@
 	struct pax *pax;
 
 	pax = (struct pax *)a->format_data;
+	if (pax == NULL)
+		return (ARCHIVE_OK);
+
 	archive_string_free(&pax->pax_header);
 	free(pax);
 	a->format_data = NULL;
@@ -1117,6 +1257,8 @@
 static int
 has_non_ASCII(const wchar_t *wp)
 {
+	if (wp == NULL)
+		return (1);
 	while (*wp != L'\0' && *wp < 128)
 		wp++;
 	return (*wp != L'\0');
--- /dev/null
+++ lib/libarchive/archive.h
@@ -0,0 +1,592 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive.h,v 1.18.2.1 2008/08/10 04:32:47 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_H_INCLUDED
+#define	ARCHIVE_H_INCLUDED
+
+/*
+ * Note: archive.h is for use outside of libarchive; the configuration
+ * headers (config.h, archive_platform.h, etc.) are purely internal.
+ * Do NOT use HAVE_XXX configuration macros to control the behavior of
+ * this header!  If you must conditionalize, use predefined compiler and/or
+ * platform macros.
+ */
+
+#include <sys/types.h>  /* Linux requires this for off_t */
+#if !defined(__WATCOMC__) && !defined(_MSC_VER)
+/* Header unavailable on Watcom C or MS Visual C++. */
+#include <inttypes.h> /* int64_t, etc. */
+#endif
+#include <stdio.h> /* For FILE * */
+
+/* Get appropriate definitions of standard POSIX-style types. */
+/* These should match the types used in 'struct stat' */
+#ifdef _WIN32
+#define	__LA_SSIZE_T	long
+#define	__LA_UID_T	unsigned int
+#define	__LA_GID_T	unsigned int
+#else
+#include <unistd.h>  /* ssize_t, uid_t, and gid_t */
+#define	__LA_SSIZE_T	ssize_t
+#define	__LA_UID_T	uid_t
+#define	__LA_GID_T	gid_t
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib.  The default here assumes you're building a DLL.  Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+#  ifdef __GNUC__
+#   define __LA_DECL	__attribute__((dllexport)) extern
+#  else
+#   define __LA_DECL	__declspec(dllexport)
+#  endif
+# else
+#  ifdef __GNUC__
+#   define __LA_DECL	__attribute__((dllimport)) extern
+#  else
+#   define __LA_DECL	__declspec(dllimport)
+#  endif
+# endif
+#else
+/* Static libraries or non-Windows needs no special declaration. */
+# define __LA_DECL
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The version number is provided as both a macro and a function.
+ * The macro identifies the installed header; the function identifies
+ * the library version (which may not be the same if you're using a
+ * dynamically-linked version of the library).  Of course, if the
+ * header and library are very different, you should expect some
+ * strangeness.  Don't do that.
+ */
+
+/*
+ * The version number is expressed as a single integer that makes it
+ * easy to compare versions at build time: for version a.b.c, the
+ * version number is printf("%d%03d%03d",a,b,c).  For example, if you
+ * know your application requires version 2.12.108 or later, you can
+ * assert that ARCHIVE_VERSION >= 2012108.
+ *
+ * This single-number format was introduced with libarchive 1.9.0 in
+ * the libarchive 1.x family and libarchive 2.2.4 in the libarchive
+ * 2.x family.  The following may be useful if you really want to do
+ * feature detection for earlier libarchive versions (which defined
+ * ARCHIVE_API_VERSION and ARCHIVE_API_FEATURE instead):
+ *
+ * #ifndef ARCHIVE_VERSION_NUMBER
+ * #define ARCHIVE_VERSION_NUMBER	\
+ *             (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
+ * #endif
+ */
+#define	ARCHIVE_VERSION_NUMBER 2005005
+__LA_DECL int		archive_version_number(void);
+
+/*
+ * Textual name/version of the library, useful for version displays.
+ */
+#define	ARCHIVE_VERSION_STRING "libarchive 2.5.5"
+__LA_DECL const char *	archive_version_string(void);
+
+#if ARCHIVE_VERSION_NUMBER < 3000000
+/*
+ * Deprecated; these are older names that will be removed in favor of
+ * the simpler definitions above.
+ */
+#define	ARCHIVE_VERSION_STAMP	ARCHIVE_VERSION_NUMBER
+__LA_DECL int		archive_version_stamp(void);
+#define	ARCHIVE_LIBRARY_VERSION	ARCHIVE_VERSION_STRING
+__LA_DECL const char *	archive_version(void);
+#define	ARCHIVE_API_VERSION	(ARCHIVE_VERSION_NUMBER / 1000000)
+__LA_DECL int		archive_api_version(void);
+#define	ARCHIVE_API_FEATURE	((ARCHIVE_VERSION_NUMBER / 1000) % 1000)
+__LA_DECL int		archive_api_feature(void);
+#endif
+
+#if ARCHIVE_VERSION_NUMBER < 3000000
+/* This should never have been here in the first place. */
+/* Legacy of old tar assumptions, will be removed in libarchive 3.0. */
+#define	ARCHIVE_BYTES_PER_RECORD	  512
+#define	ARCHIVE_DEFAULT_BYTES_PER_BLOCK	10240
+#endif
+
+/* Declare our basic types. */
+struct archive;
+struct archive_entry;
+
+/*
+ * Error codes: Use archive_errno() and archive_error_string()
+ * to retrieve details.  Unless specified otherwise, all functions
+ * that return 'int' use these codes.
+ */
+#define	ARCHIVE_EOF	  1	/* Found end of archive. */
+#define	ARCHIVE_OK	  0	/* Operation was successful. */
+#define	ARCHIVE_RETRY	(-10)	/* Retry might succeed. */
+#define	ARCHIVE_WARN	(-20)	/* Partial success. */
+/* For example, if write_header "fails", then you can't push data. */
+#define	ARCHIVE_FAILED	(-25)	/* Current operation cannot complete. */
+/* But if write_header is "fatal," then this archive is dead and useless. */
+#define	ARCHIVE_FATAL	(-30)	/* No more operations are possible. */
+
+/*
+ * As far as possible, archive_errno returns standard platform errno codes.
+ * Of course, the details vary by platform, so the actual definitions
+ * here are stored in "archive_platform.h".  The symbols are listed here
+ * for reference; as a rule, clients should not need to know the exact
+ * platform-dependent error code.
+ */
+/* Unrecognized or invalid file format. */
+/* #define	ARCHIVE_ERRNO_FILE_FORMAT */
+/* Illegal usage of the library. */
+/* #define	ARCHIVE_ERRNO_PROGRAMMER_ERROR */
+/* Unknown or unclassified error. */
+/* #define	ARCHIVE_ERRNO_MISC */
+
+/*
+ * Callbacks are invoked to automatically read/skip/write/open/close the
+ * archive. You can provide your own for complex tasks (like breaking
+ * archives across multiple tapes) or use standard ones built into the
+ * library.
+ */
+
+/* Returns pointer and size of next block of data from archive. */
+typedef __LA_SSIZE_T	archive_read_callback(struct archive *, void *_client_data,
+		    const void **_buffer);
+/* Skips at most request bytes from archive and returns the skipped amount */
+#if ARCHIVE_VERSION_NUMBER < 2000000
+typedef __LA_SSIZE_T	archive_skip_callback(struct archive *, void *_client_data,
+		    size_t request);
+#else
+typedef off_t	archive_skip_callback(struct archive *, void *_client_data,
+		    off_t request);
+#endif
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef __LA_SSIZE_T	archive_write_callback(struct archive *, void *_client_data,
+		    const void *_buffer, size_t _length);
+typedef int	archive_open_callback(struct archive *, void *_client_data);
+typedef int	archive_close_callback(struct archive *, void *_client_data);
+
+/*
+ * Codes for archive_compression.
+ */
+#define	ARCHIVE_COMPRESSION_NONE	0
+#define	ARCHIVE_COMPRESSION_GZIP	1
+#define	ARCHIVE_COMPRESSION_BZIP2	2
+#define	ARCHIVE_COMPRESSION_COMPRESS	3
+#define	ARCHIVE_COMPRESSION_PROGRAM	4
+
+/*
+ * Codes returned by archive_format.
+ *
+ * Top 16 bits identifies the format family (e.g., "tar"); lower
+ * 16 bits indicate the variant.  This is updated by read_next_header.
+ * Note that the lower 16 bits will often vary from entry to entry.
+ * In some cases, this variation occurs as libarchive learns more about
+ * the archive (for example, later entries might utilize extensions that
+ * weren't necessary earlier in the archive; in this case, libarchive
+ * will change the format code to indicate the extended format that
+ * was used).  In other cases, it's because different tools have
+ * modified the archive and so different parts of the archive
+ * actually have slightly different formts.  (Both tar and cpio store
+ * format codes in each entry, so it is quite possible for each
+ * entry to be in a different format.)
+ */
+#define	ARCHIVE_FORMAT_BASE_MASK		0xff0000
+#define	ARCHIVE_FORMAT_CPIO			0x10000
+#define	ARCHIVE_FORMAT_CPIO_POSIX		(ARCHIVE_FORMAT_CPIO | 1)
+#define	ARCHIVE_FORMAT_CPIO_BIN_LE		(ARCHIVE_FORMAT_CPIO | 2)
+#define	ARCHIVE_FORMAT_CPIO_BIN_BE		(ARCHIVE_FORMAT_CPIO | 3)
+#define	ARCHIVE_FORMAT_CPIO_SVR4_NOCRC		(ARCHIVE_FORMAT_CPIO | 4)
+#define	ARCHIVE_FORMAT_CPIO_SVR4_CRC		(ARCHIVE_FORMAT_CPIO | 5)
+#define	ARCHIVE_FORMAT_SHAR			0x20000
+#define	ARCHIVE_FORMAT_SHAR_BASE		(ARCHIVE_FORMAT_SHAR | 1)
+#define	ARCHIVE_FORMAT_SHAR_DUMP		(ARCHIVE_FORMAT_SHAR | 2)
+#define	ARCHIVE_FORMAT_TAR			0x30000
+#define	ARCHIVE_FORMAT_TAR_USTAR		(ARCHIVE_FORMAT_TAR | 1)
+#define	ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE	(ARCHIVE_FORMAT_TAR | 2)
+#define	ARCHIVE_FORMAT_TAR_PAX_RESTRICTED	(ARCHIVE_FORMAT_TAR | 3)
+#define	ARCHIVE_FORMAT_TAR_GNUTAR		(ARCHIVE_FORMAT_TAR | 4)
+#define	ARCHIVE_FORMAT_ISO9660			0x40000
+#define	ARCHIVE_FORMAT_ISO9660_ROCKRIDGE	(ARCHIVE_FORMAT_ISO9660 | 1)
+#define	ARCHIVE_FORMAT_ZIP			0x50000
+#define	ARCHIVE_FORMAT_EMPTY			0x60000
+#define	ARCHIVE_FORMAT_AR			0x70000
+#define	ARCHIVE_FORMAT_AR_GNU			(ARCHIVE_FORMAT_AR | 1)
+#define	ARCHIVE_FORMAT_AR_BSD			(ARCHIVE_FORMAT_AR | 2)
+#define	ARCHIVE_FORMAT_MTREE			0x80000
+#define	ARCHIVE_FORMAT_MTREE_V1			(ARCHIVE_FORMAT_MTREE | 1)
+#define	ARCHIVE_FORMAT_MTREE_V2			(ARCHIVE_FORMAT_MTREE | 2)
+
+/*-
+ * Basic outline for reading an archive:
+ *   1) Ask archive_read_new for an archive reader object.
+ *   2) Update any global properties as appropriate.
+ *      In particular, you'll certainly want to call appropriate
+ *      archive_read_support_XXX functions.
+ *   3) Call archive_read_open_XXX to open the archive
+ *   4) Repeatedly call archive_read_next_header to get information about
+ *      successive archive entries.  Call archive_read_data to extract
+ *      data for entries of interest.
+ *   5) Call archive_read_finish to end processing.
+ */
+__LA_DECL struct archive	*archive_read_new(void);
+
+/*
+ * The archive_read_support_XXX calls enable auto-detect for this
+ * archive handle.  They also link in the necessary support code.
+ * For example, if you don't want bzlib linked in, don't invoke
+ * support_compression_bzip2().  The "all" functions provide the
+ * obvious shorthand.
+ */
+__LA_DECL int		 archive_read_support_compression_all(struct archive *);
+__LA_DECL int		 archive_read_support_compression_bzip2(struct archive *);
+__LA_DECL int		 archive_read_support_compression_compress(struct archive *);
+__LA_DECL int		 archive_read_support_compression_gzip(struct archive *);
+__LA_DECL int		 archive_read_support_compression_none(struct archive *);
+__LA_DECL int		 archive_read_support_compression_program(struct archive *,
+		     const char *command);
+
+__LA_DECL int		 archive_read_support_format_all(struct archive *);
+__LA_DECL int		 archive_read_support_format_ar(struct archive *);
+__LA_DECL int		 archive_read_support_format_cpio(struct archive *);
+__LA_DECL int		 archive_read_support_format_empty(struct archive *);
+__LA_DECL int		 archive_read_support_format_gnutar(struct archive *);
+__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_tar(struct archive *);
+__LA_DECL int		 archive_read_support_format_zip(struct archive *);
+
+
+/* Open the archive using callbacks for archive I/O. */
+__LA_DECL int		 archive_read_open(struct archive *, void *_client_data,
+		     archive_open_callback *, archive_read_callback *,
+		     archive_close_callback *);
+__LA_DECL int		 archive_read_open2(struct archive *, void *_client_data,
+		     archive_open_callback *, archive_read_callback *,
+		     archive_skip_callback *, archive_close_callback *);
+
+/*
+ * A variety of shortcuts that invoke archive_read_open() with
+ * canned callbacks suitable for common situations.  The ones that
+ * accept a block size handle tape blocking correctly.
+ */
+/* Use this if you know the filename.  Note: NULL indicates stdin. */
+__LA_DECL int		 archive_read_open_filename(struct archive *,
+		     const char *_filename, size_t _block_size);
+/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
+__LA_DECL int		 archive_read_open_file(struct archive *,
+		     const char *_filename, size_t _block_size);
+/* Read an archive that's stored in memory. */
+__LA_DECL int		 archive_read_open_memory(struct archive *,
+		     void * buff, size_t size);
+/* A more involved version that is only used for internal testing. */
+__LA_DECL int		archive_read_open_memory2(struct archive *a, void *buff,
+		     size_t size, size_t read_size);
+/* Read an archive that's already open, using the file descriptor. */
+__LA_DECL int		 archive_read_open_fd(struct archive *, int _fd,
+		     size_t _block_size);
+/* Read an archive that's already open, using a FILE *. */
+/* Note: DO NOT use this with tape drives. */
+__LA_DECL int		 archive_read_open_FILE(struct archive *, FILE *_file);
+
+/* Parses and returns next entry header. */
+__LA_DECL int		 archive_read_next_header(struct archive *,
+		     struct archive_entry **);
+
+/*
+ * Retrieve the byte offset in UNCOMPRESSED data where last-read
+ * header started.
+ */
+__LA_DECL int64_t		 archive_read_header_position(struct archive *);
+
+/* Read data from the body of an entry.  Similar to read(2). */
+__LA_DECL __LA_SSIZE_T		 archive_read_data(struct archive *, void *, size_t);
+/*
+ * A zero-copy version of archive_read_data that also exposes the file offset
+ * of each returned block.  Note that the client has no way to specify
+ * the desired size of the block.  The API does guarantee that offsets will
+ * be strictly increasing and that returned blocks will not overlap.
+ */
+__LA_DECL int		 archive_read_data_block(struct archive *a,
+		    const void **buff, size_t *size, off_t *offset);
+
+/*-
+ * Some convenience functions that are built on archive_read_data:
+ *  'skip': skips entire entry
+ *  'into_buffer': writes data into memory buffer that you provide
+ *  'into_fd': writes data to specified filedes
+ */
+__LA_DECL int		 archive_read_data_skip(struct archive *);
+__LA_DECL int		 archive_read_data_into_buffer(struct archive *, void *buffer,
+		     __LA_SSIZE_T len);
+__LA_DECL int		 archive_read_data_into_fd(struct archive *, int fd);
+
+/*-
+ * Convenience function to recreate the current entry (whose header
+ * has just been read) on disk.
+ *
+ * This does quite a bit more than just copy data to disk. It also:
+ *  - Creates intermediate directories as required.
+ *  - Manages directory permissions:  non-writable directories will
+ *    be initially created with write permission enabled; when the
+ *    archive is closed, dir permissions are edited to the values specified
+ *    in the archive.
+ *  - Checks hardlinks:  hardlinks will not be extracted unless the
+ *    linked-to file was also extracted within the same session. (TODO)
+ */
+
+/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
+
+/* Default: Do not try to set owner/group. */
+#define	ARCHIVE_EXTRACT_OWNER			(0x0001)
+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
+#define	ARCHIVE_EXTRACT_PERM			(0x0002)
+/* Default: Do not restore mtime/atime. */
+#define	ARCHIVE_EXTRACT_TIME			(0x0004)
+/* Default: Replace existing files. */
+#define	ARCHIVE_EXTRACT_NO_OVERWRITE 		(0x0008)
+/* Default: Try create first, unlink only if create fails with EEXIST. */
+#define	ARCHIVE_EXTRACT_UNLINK			(0x0010)
+/* Default: Do not restore ACLs. */
+#define	ARCHIVE_EXTRACT_ACL			(0x0020)
+/* Default: Do not restore fflags. */
+#define	ARCHIVE_EXTRACT_FFLAGS			(0x0040)
+/* Default: Do not restore xattrs. */
+#define	ARCHIVE_EXTRACT_XATTR 			(0x0080)
+/* Default: Do not try to guard against extracts redirected by symlinks. */
+/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
+#define	ARCHIVE_EXTRACT_SECURE_SYMLINKS		(0x0100)
+/* Default: Do not reject entries with '..' as path elements. */
+#define	ARCHIVE_EXTRACT_SECURE_NODOTDOT		(0x0200)
+/* Default: Create parent directories as needed. */
+#define	ARCHIVE_EXTRACT_NO_AUTODIR		(0x0400)
+/* Default: Overwrite files, even if one on disk is newer. */
+#define	ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER	(0x0800)
+/* Detect blocks of 0 and write holes instead. */
+#define	ARCHIVE_EXTRACT_SPARSE			(0x1000)
+
+__LA_DECL int	 archive_read_extract(struct archive *, struct archive_entry *,
+		     int flags);
+__LA_DECL int	 archive_read_extract2(struct archive *, struct archive_entry *,
+		     struct archive * /* dest */);
+__LA_DECL void	 archive_read_extract_set_progress_callback(struct archive *,
+		     void (*_progress_func)(void *), void *_user_data);
+
+/* Record the dev/ino of a file that will not be written.  This is
+ * generally set to the dev/ino of the archive being read. */
+__LA_DECL void		archive_read_extract_set_skip_file(struct archive *,
+		     dev_t, ino_t);
+
+/* Close the file and release most resources. */
+__LA_DECL int		 archive_read_close(struct archive *);
+/* Release all resources and destroy the object. */
+/* Note that archive_read_finish will call archive_read_close for you. */
+#if ARCHIVE_VERSION_NUMBER >= 2000000
+__LA_DECL int		 archive_read_finish(struct archive *);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* Erroneously declared to return void in libarchive 1.x */
+__LA_DECL void		 archive_read_finish(struct archive *);
+#endif
+
+/*-
+ * To create an archive:
+ *   1) Ask archive_write_new for a archive writer object.
+ *   2) Set any global properties.  In particular, you should set
+ *      the compression and format to use.
+ *   3) Call archive_write_open to open the file (most people
+ *       will use archive_write_open_file or archive_write_open_fd,
+ *       which provide convenient canned I/O callbacks for you).
+ *   4) For each entry:
+ *      - construct an appropriate struct archive_entry structure
+ *      - archive_write_header to write the header
+ *      - archive_write_data to write the entry data
+ *   5) archive_write_close to close the output
+ *   6) archive_write_finish to cleanup the writer and release resources
+ */
+__LA_DECL struct archive	*archive_write_new(void);
+__LA_DECL int		 archive_write_set_bytes_per_block(struct archive *,
+		     int bytes_per_block);
+__LA_DECL int		 archive_write_get_bytes_per_block(struct archive *);
+/* XXX This is badly misnamed; suggestions appreciated. XXX */
+__LA_DECL int		 archive_write_set_bytes_in_last_block(struct archive *,
+		     int bytes_in_last_block);
+__LA_DECL int		 archive_write_get_bytes_in_last_block(struct archive *);
+
+/* The dev/ino of a file that won't be archived.  This is used
+ * to avoid recursively adding an archive to itself. */
+__LA_DECL int		 archive_write_set_skip_file(struct archive *, dev_t, ino_t);
+
+__LA_DECL int		 archive_write_set_compression_bzip2(struct archive *);
+__LA_DECL int		 archive_write_set_compression_compress(struct archive *);
+__LA_DECL int		 archive_write_set_compression_gzip(struct archive *);
+__LA_DECL int		 archive_write_set_compression_none(struct archive *);
+__LA_DECL int		 archive_write_set_compression_program(struct archive *,
+		     const char *cmd);
+/* A convenience function to set the format based on the code or name. */
+__LA_DECL int		 archive_write_set_format(struct archive *, int format_code);
+__LA_DECL int		 archive_write_set_format_by_name(struct archive *,
+		     const char *name);
+/* To minimize link pollution, use one or more of the following. */
+__LA_DECL int		 archive_write_set_format_ar_bsd(struct archive *);
+__LA_DECL int		 archive_write_set_format_ar_svr4(struct archive *);
+__LA_DECL int		 archive_write_set_format_cpio(struct archive *);
+__LA_DECL int		 archive_write_set_format_cpio_newc(struct archive *);
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+__LA_DECL int		 archive_write_set_format_pax(struct archive *);
+__LA_DECL int		 archive_write_set_format_pax_restricted(struct archive *);
+__LA_DECL int		 archive_write_set_format_shar(struct archive *);
+__LA_DECL int		 archive_write_set_format_shar_dump(struct archive *);
+__LA_DECL int		 archive_write_set_format_ustar(struct archive *);
+__LA_DECL int		 archive_write_open(struct archive *, void *,
+		     archive_open_callback *, archive_write_callback *,
+		     archive_close_callback *);
+__LA_DECL int		 archive_write_open_fd(struct archive *, int _fd);
+__LA_DECL int		 archive_write_open_filename(struct archive *, const char *_file);
+/* A deprecated synonym for archive_write_open_filename() */
+__LA_DECL int		 archive_write_open_file(struct archive *, const char *_file);
+__LA_DECL int		 archive_write_open_FILE(struct archive *, FILE *);
+/* _buffSize is the size of the buffer, _used refers to a variable that
+ * will be updated after each write into the buffer. */
+__LA_DECL int		 archive_write_open_memory(struct archive *,
+			void *_buffer, size_t _buffSize, size_t *_used);
+
+/*
+ * Note that the library will truncate writes beyond the size provided
+ * to archive_write_header or pad if the provided data is short.
+ */
+__LA_DECL int		 archive_write_header(struct archive *,
+		     struct archive_entry *);
+#if ARCHIVE_VERSION_NUMBER >= 2000000
+__LA_DECL __LA_SSIZE_T		 archive_write_data(struct archive *, const void *, size_t);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* This was erroneously declared to return "int" in libarchive 1.x. */
+__LA_DECL int		 archive_write_data(struct archive *, const void *, size_t);
+#endif
+__LA_DECL __LA_SSIZE_T		 archive_write_data_block(struct archive *, const void *, size_t, off_t);
+__LA_DECL int		 archive_write_finish_entry(struct archive *);
+__LA_DECL int		 archive_write_close(struct archive *);
+#if ARCHIVE_VERSION_NUMBER >= 2000000
+__LA_DECL int		 archive_write_finish(struct archive *);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* Return value was incorrect in libarchive 1.x. */
+__LA_DECL void		 archive_write_finish(struct archive *);
+#endif
+
+/*-
+ * To create objects on disk:
+ *   1) Ask archive_write_disk_new for a new archive_write_disk object.
+ *   2) Set any global properties.  In particular, you should set
+ *      the compression and format to use.
+ *   3) For each entry:
+ *      - construct an appropriate struct archive_entry structure
+ *      - archive_write_header to create the file/dir/etc on disk
+ *      - archive_write_data to write the entry data
+ *   4) archive_write_finish to cleanup the writer and release resources
+ *
+ * In particular, you can use this in conjunction with archive_read()
+ * to pull entries out of an archive and create them on disk.
+ */
+__LA_DECL struct archive	*archive_write_disk_new(void);
+/* This file will not be overwritten. */
+__LA_DECL int		 archive_write_disk_set_skip_file(struct archive *,
+		     dev_t, ino_t);
+/* Set flags to control how the next item gets created. */
+__LA_DECL int		 archive_write_disk_set_options(struct archive *,
+		     int flags);
+/*
+ * The lookup functions are given uname/uid (or gname/gid) pairs and
+ * return a uid (gid) suitable for this system.  These are used for
+ * restoring ownership and for setting ACLs.  The default functions
+ * are naive, they just return the uid/gid.  These are small, so reasonable
+ * for applications that don't need to preserve ownership; they
+ * are probably also appropriate for applications that are doing
+ * same-system backup and restore.
+ */
+/*
+ * The "standard" lookup functions use common system calls to lookup
+ * the uname/gname, falling back to the uid/gid if the names can't be
+ * found.  They cache lookups and are reasonably fast, but can be very
+ * large, so they are not used unless you ask for them.  In
+ * particular, these match the specifications of POSIX "pax" and old
+ * POSIX "tar".
+ */
+__LA_DECL int	 archive_write_disk_set_standard_lookup(struct archive *);
+/*
+ * If neither the default (naive) nor the standard (big) functions suit
+ * your needs, you can write your own and register them.  Be sure to
+ * include a cleanup function if you have allocated private data.
+ */
+__LA_DECL int	 archive_write_disk_set_group_lookup(struct archive *,
+			    void * /* private_data */,
+			    __LA_GID_T (*)(void *, const char *, __LA_GID_T),
+			    void (* /* cleanup */)(void *));
+__LA_DECL int	 archive_write_disk_set_user_lookup(struct archive *,
+			    void * /* private_data */,
+			    __LA_UID_T (*)(void *, const char *, __LA_UID_T),
+			    void (* /* cleanup */)(void *));
+
+/*
+ * Accessor functions to read/set various information in
+ * the struct archive object:
+ */
+/* Bytes written after compression or read before decompression. */
+__LA_DECL int64_t	 archive_position_compressed(struct archive *);
+/* Bytes written to compressor or read from decompressor. */
+__LA_DECL int64_t	 archive_position_uncompressed(struct archive *);
+
+__LA_DECL const char	*archive_compression_name(struct archive *);
+__LA_DECL int		 archive_compression(struct archive *);
+__LA_DECL int		 archive_errno(struct archive *);
+__LA_DECL const char	*archive_error_string(struct archive *);
+__LA_DECL const char	*archive_format_name(struct archive *);
+__LA_DECL int		 archive_format(struct archive *);
+__LA_DECL void		 archive_clear_error(struct archive *);
+__LA_DECL void		 archive_set_error(struct archive *, int _err,
+			    const char *fmt, ...);
+__LA_DECL void		 archive_copy_error(struct archive *dest,
+			    struct archive *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* This is meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_H_INCLUDED */
Index: archive_write_set_format_cpio.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_cpio.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L lib/libarchive/archive_write_set_format_cpio.c -L lib/libarchive/archive_write_set_format_cpio.c -u -r1.3 -r1.4
--- lib/libarchive/archive_write_set_format_cpio.c
+++ lib/libarchive/archive_write_set_format_cpio.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.11.2.1 2007/10/21 22:17:25 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.11.2.3 2008/05/10 06:57:04 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -97,8 +97,8 @@
 	a->format_finish_entry = archive_write_cpio_finish_entry;
 	a->format_finish = archive_write_cpio_finish;
 	a->format_destroy = archive_write_cpio_destroy;
-	a->archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
-	a->archive_format_name = "POSIX cpio";
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+	a->archive.archive_format_name = "POSIX cpio";
 	return (ARCHIVE_OK);
 }
 
@@ -228,6 +228,7 @@
 
 	cpio = (struct cpio *)a->format_data;
 	trailer = archive_entry_new();
+	/* nlink = 1 here for GNU cpio compat. */
 	archive_entry_set_nlink(trailer, 1);
 	archive_entry_set_pathname(trailer, "TRAILER!!!");
 	er = archive_write_cpio_header(a, trailer);
Index: archive_write_disk.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_disk.3,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/archive_write_disk.3 -L lib/libarchive/archive_write_disk.3 -u -r1.1 -r1.2
--- lib/libarchive/archive_write_disk.3
+++ lib/libarchive/archive_write_disk.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_write_disk.3,v 1.1 2007/03/03 07:37:36 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_write_disk.3,v 1.1.4.2 2008/08/10 04:32:47 kientzle Exp $
 .\"
 .Dd March 2, 2007
 .Dt archive_write_disk 3
@@ -49,11 +49,21 @@
 .Ft int
 .Fn archive_write_disk_set_skip_file "struct archive *" "dev_t" "ino_t"
 .Ft int
-.Fn archive_write_disk_set_group_lookup "struct archive *" "void *" "gid_t (*)(void *, const char *gname, gid_t gid)" "void (*cleanup)(void *)"
+.Fo archive_write_disk_set_group_lookup
+.Fa "struct archive *"
+.Fa "void *"
+.Fa "gid_t (*)(void *, const char *gname, gid_t gid)"
+.Fa "void (*cleanup)(void *)"
+.Fc
 .Ft int
 .Fn archive_write_disk_set_standard_lookup "struct archive *"
 .Ft int
-.Fn archive_write_disk_set_user_lookup "struct archive *" "void *" "uid_t (*)(void *, const char *uname, uid_t uid)" "void (*cleanup)(void *)"
+.Fo archive_write_disk_set_user_lookup
+.Fa "struct archive *"
+.Fa "void *"
+.Fa "uid_t (*)(void *, const char *uname, uid_t uid)"
+.Fa "void (*cleanup)(void *)"
+.Fc
 .Ft int
 .Fn archive_write_header "struct archive *" "struct archive_entry *"
 .Ft ssize_t
@@ -160,7 +170,14 @@
 .Pa ..
 always cause an error, regardless of this flag.
 .El
-.It Fn archive_write_disk_set_group_lookup , Fn archive_write_disk_set_user_lookup
+.It Cm ARCHIVE_EXTRACT_SPARSE
+Scan data for blocks of NUL bytes and try to recreate them with holes.
+This results in sparse files, independent of whether the archive format
+supports or uses them.
+.It Xo
+.Fn archive_write_disk_set_group_lookup ,
+.Fn archive_write_disk_set_user_lookup
+.Xc
 The
 .Tn struct archive_entry
 objects contain both names and ids that can be used to identify users
Index: archive_write.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write.3,v
retrieving revision 1.2
retrieving revision 1.3
diff -L lib/libarchive/archive_write.3 -L lib/libarchive/archive_write.3 -u -r1.2 -r1.3
--- lib/libarchive/archive_write.3
+++ lib/libarchive/archive_write.3
@@ -22,9 +22,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.22 2007/05/29 01:00:19 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.22.2.2 2008/08/10 04:32:47 kientzle Exp $
 .\"
-.Dd August 19, 2006
+.Dd May 11, 2008
 .Dt archive_write 3
 .Os
 .Sh NAME
@@ -39,6 +39,7 @@
 .Nm archive_write_set_bytes_per_block ,
 .Nm archive_write_set_bytes_in_last_block ,
 .Nm archive_write_set_compression_bzip2 ,
+.Nm archive_write_set_compression_compress ,
 .Nm archive_write_set_compression_gzip ,
 .Nm archive_write_set_compression_none ,
 .Nm archive_write_set_compression_program ,
@@ -66,11 +67,16 @@
 .Ft int
 .Fn archive_write_set_compression_bzip2 "struct archive *"
 .Ft int
+.Fn archive_write_set_compression_compress "struct archive *"
+.Ft int
 .Fn archive_write_set_compression_gzip "struct archive *"
 .Ft int
 .Fn archive_write_set_compression_none "struct archive *"
 .Ft int
-.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
+.Fo archive_write_set_compression_program
+.Fa "struct archive *"
+.Fa "const char * cmd"
+.Fc
 .Ft int
 .Fn archive_write_set_format_cpio "struct archive *"
 .Ft int
@@ -84,7 +90,13 @@
 .Ft int
 .Fn archive_write_set_format_ustar "struct archive *"
 .Ft int
-.Fn archive_write_open "struct archive *" "void *client_data" "archive_open_callback *" "archive_write_callback *" "archive_close_callback *"
+.Fo archive_write_open
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "archive_open_callback *"
+.Fa "archive_write_callback *"
+.Fa "archive_close_callback *"
+.Fc
 .Ft int
 .Fn archive_write_open_fd "struct archive *" "int fd"
 .Ft int
@@ -92,7 +104,12 @@
 .Ft int
 .Fn archive_write_open_filename "struct archive *" "const char *filename"
 .Ft int
-.Fn archive_write_open_memory "struct archive *" "void *buffer" "size_t bufferSize" "size_t *outUsed"
+.Fo archive_write_open_memory
+.Fa "struct archive *"
+.Fa "void *buffer"
+.Fa "size_t bufferSize"
+.Fa "size_t *outUsed"
+.Fc
 .Ft int
 .Fn archive_write_header "struct archive *" "struct archive_entry *"
 .Ft ssize_t
@@ -150,7 +167,14 @@
 .It Fn archive_write_get_bytes_in_last_block
 Retrieve the currently-set value for last block size.
 A value of -1 here indicates that the library should use default values.
-.It Fn archive_write_set_format_cpio , Fn archive_write_set_format_pax , Fn archive_write_set_format_pax_restricted , Fn archive_write_set_format_shar , Fn archive_write_set_format_shar_binary , Fn archive_write_set_format_ustar
+.It Xo
+.Fn archive_write_set_format_cpio ,
+.Fn archive_write_set_format_pax ,
+.Fn archive_write_set_format_pax_restricted ,
+.Fn archive_write_set_format_shar ,
+.Fn archive_write_set_format_shar_binary ,
+.Fn archive_write_set_format_ustar
+.Xc
 Sets the format that will be used for the archive.
 The library can write
 POSIX octet-oriented cpio format archives,
@@ -174,7 +198,12 @@
 is the library default; this is the same as pax format, but suppresses
 the pax extended header for most normal files.
 In most cases, this will result in ordinary ustar archives.
-.It Fn archive_write_set_compression_bzip2 , Fn archive_write_set_compression_gzip , Fn archive_write_set_compression_none
+.It Xo
+.Fn archive_write_set_compression_bzip2 ,
+.Fn archive_write_set_compression_compress ,
+.Fn archive_write_set_compression_gzip ,
+.Fn archive_write_set_compression_none
+.Xc
 The resulting archive will be compressed as specified.
 Note that the compressed output is always properly blocked.
 .It Fn archive_write_set_compression_program
@@ -310,7 +339,12 @@
 .Bl -item -offset indent
 .It
 .Ft typedef ssize_t
-.Fn archive_write_callback "struct archive *" "void *client_data" "void *buffer" "size_t length"
+.Fo archive_write_callback
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "void *buffer"
+.Fa "size_t length"
+.Fc
 .El
 .Pp
 The write callback is invoked whenever the library
--- /dev/null
+++ lib/libarchive/test/test_write_compress.c
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 2007 Tim Kientzle
+ * 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
+ *    in this position and unchanged.
+ * 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: src/lib/libarchive/test/test_write_compress.c,v 1.2.2.1 2008/05/21 04:14:39 kientzle Exp $");
+
+/*
+ * A basic exercise of compress reading and writing.
+ *
+ * TODO: Add a reference file and make sure we can decompress that.
+ */
+
+DEFINE_TEST(test_write_compress)
+{
+	struct archive_entry *ae;
+	struct archive* a;
+	char *buff, *data;
+	size_t buffsize, datasize;
+	char path[16];
+	size_t used;
+	int i;
+
+	buffsize = 1000000;
+	assert(NULL != (buff = (char *)malloc(buffsize)));
+
+	datasize = 10000;
+	assert(NULL != (data = (char *)malloc(datasize)));
+	memset(data, 0, datasize);
+
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_compress(a));
+	assertA(0 == archive_write_open_memory(a, buff, buffsize, &used));
+
+	for (i = 0; i < 100; i++) {
+		sprintf(path, "file%03d", i);
+		assert((ae = archive_entry_new()) != NULL);
+		archive_entry_copy_pathname(ae, path);
+		archive_entry_set_size(ae, datasize);
+		archive_entry_set_filetype(ae, AE_IFREG);
+		assertA(0 == archive_write_header(a, ae));
+		assertA(datasize == (size_t)archive_write_data(a, data, datasize));
+		archive_entry_free(ae);
+	}
+
+
+	archive_write_close(a);
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Now, read the data back.
+	 */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+
+	for (i = 0; i < 100; i++) {
+		sprintf(path, "file%03d", i);
+		assertEqualInt(0, archive_read_next_header(a, &ae));
+		assertEqualString(path, archive_entry_pathname(ae));
+		assertEqualInt(datasize, archive_entry_size(ae));
+	}
+
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	free(data);
+	free(buff);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tgz.uu
@@ -0,0 +1,28 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tgz.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_read_format_gtar_sparse_1_17_posix10.tgz
+M'XL(`&.";$<``^W7RV[;5A1&88WU%'J!RN=^&7B:9E0$*/H`1,*!B]@))`<P
+M\O2A)#O];=@RBJV8$+*^"1G*T=9E'8%G??%AN'L_#I_&S78=6RGQ8OMUV&S'
+MQ>FX24EI=_0U.SWN!9\7WM48O?,^Q87S,96R6-V=\#6\Z-OV=MA,+\7Z//?O
+MY>?Q3(2P^O.O?]:'[WQ]/?S[97/IET^N7MU,5]TR%+UZ,UR/EX?S9?3ZR&8<
+M/F^OOH^7T:=<0UL&MQINKZ8_][ZWT&-Q87?MX^-K;CGWA_$;6E],7]S?^^_M
+MW=7G\9?\!+RR_N-NN?RW_J?K/ON<%ZLW640/ZW]J\>C?O?;XF:[_N.R36);9
+MAZ6?3EL-^_.'M<NJQ!'#W"\`9XU^YK5^Z?X_G&[&Z_?_]>G]?\Z!^_^W\+_N
+M_^L+]__3W4)X=@/0^_[#\,_L`.(S.P#/O<9;.W;_?ZJ?@*/K?[K-C,[+^O>[
+M^_^0(_?_;Z'WES8`87?NVF$S,)W7E/;G:3HOS>W/\^[<'_YOF<ZGG^W]>3M\
+M"(=S>?XNS^^=#/!>)O@@(WR4&3[)$%]DBB_Z-JK.:3JGRYS at 9$[P,B<$F1.2
+MS`E)YH2LGU>1.:'JG*9SNLR)3N;$('-BD#DQZL8LZ1>394XL,B=6G=-DSOW;
+M<??_D#E)O_^D`:1'!6@"21M(&D'2"I)FD+2#K!UD[2!K!UD[R-I!U at ZR=I"U
+M at ZP=9.V@:`=%.RC:0=$.BG90M(.B'13MH&@'13NHVD'5#JIV4+6#JAU4[:!J
+M!U4[J-I!TPZ:=M"T at Z8=-.V@:0=-.VB/?@P>_1IH!TT[Z-I!UPZZ=M"U at ZX=
+M=.V@:P==.^@/'?@>N=^P8O\&"_J!!?W`@GY at 03^PH!]86/NA/UC0#RSH!Q;T
+M`POZ at 07]P()^8,'^'W.B'UC0#RSH!Q;T`POZ at 07]P(+]/^9$/["@'UC0#RSH
+M!Q;T`POZ at 07[?\R)?F!!/["@'UC0#RSH!Q;T`POV_Y at 3_<""?F!!/["@'UC0
+M#RSH!Q;L_S$G^H$%_<""?F!!/["@'UC0#RS8_V-.]`,+^H$%_<""?F!!/["@
+M'UBP_\><Z`<6]`,+^H$%_<""?F!!/[!@_X\YT0\LZ`<6]`,+^H$%_<""?F#!
+M_A]SHA]8T`\LZ`<6]`,+^H$%_<""_3_F1#^PH!]8T`\LZ`<6]`,+^H$%^W_,
+MB7Y at 03^PH!^;]<6'X>[].'P:-]MU;*7$BYLO-W]LOPZ;[7BB&6Y24MH=?<U.
+MCP?3N7<U1N^\3W'A?`HY+E9W)YI_U+?M[;"97HKU>>[?RL_CF0AN-=Q>78^7
+MWO<6>BS.+Z=K'Y]>F_MUXM<X^6)_QJOKW^GZ]]/Z#]G5Q>I-%M'#^I^"/_IW
+5KSU^INL?````P._C!\JB`&$`\```
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix00.tgz.uu
@@ -0,0 +1,30 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix00.tgz.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_read_format_gtar_sparse_1_17_posix00.tgz
+M'XL(`&*";$<``^W9S6[;1A2&8:UU%;Z!RO/#^5MHW:R*;'H!K,,`06L[$&7`
+M[=5W%#FN'(Q-Z1S5K-#W642!E&/3/M\$'\'5]<?^\</0?QHVX\KG&,KU^+7?
+MC,/B?$P5NV[W:E,PAZ_?=,8MK$G>6V-MYQ;&^BZ9Q=7C&:_A50_CMM_42]%^
+MG:>?Y?GU0KAT]?,OOZ[V.U^-7_X:UMYV(;F\=/'PH[N'V]_^N+_Y?5S[I<N'
+MG]Q__CP.VW6I?%R^_(*[J3^WP[@.UBU=:8S9.I:3.WGN^2I#<\XLG;GJMU]N
+MA[6U);MZ:<;NWKMY^9Y9SKV!>9W]L#=,G'^W.R[_G/_ZOK7)U?/_+H?H^_FO
+ML7CSWTU]?J'G7ZN?^P)PT<C/O%:O]3]WON_Q]O__UEN??NQ_(7KZWWMX6>6^
+M];]2]GMI5+)]`2SE/]$`W6[.Y-.;8YU+77?R7%?G8C8GSX7=G#W]]Q+K7`BG
+M_U[R/H&GSPGW5Z;WYTUK[V9Z@>U!.[W!]J";7F%[T$_OL#W832^Q/1BGM_C*
+MX/0:VX-)NL<LW6,1[M$9X1Z=%>[1.>$>72?<H^N$>W1!N$<7A7MT2;K'+-UC
+M$>[1&^$>O1/NT3OA'KT7[M%WPCWZ(-RCC\(]^B3=8Q;N\>DXGGZIG1'NL3NB
+MW[0'CR at X[<$C&DY[\(B*TQX\HN.T!X\H.>W!(UK.*X/2/4I[3I#VG"#M.4':
+M<X*TYP1ISPG2GA.D/2=(>TZ0]IP@[3E1VG.BM.=$:<^)TIX3I3TG2GM.E/:<
+M*.TY4=ISHK3G)&G/2=*>DZ0])TE[3I+VG"3M.4G:<Y*TYR1IS\G2GI.E/2=+
+M>TZ6]IPL[3E9VG.RM.=D:<_)TIZ3I3TG2WM.D?:<(NTY1=ISBK3G%&G/*=*>
+M4Z0]ITA[3IGH.;;XQL-UUWBX;O_G#]<OP'ZY9WS8T_#F\Q_;.6_LP?,?NS#6
+MU;=Y_G\)>'X+#?(##?(##?(##?(##?(##6U^R!\TR`\TR`\TR`\TR`\TR`\T
+MR`\TN/_'G,@/-,@/-,@/-,@/-,@/-,@/-+C_QYS(#S3(#S3(#S3(#S3(#S3(
+M#S2X_\><R`\TR`\TR`\TR`\TR`\TR`\TN/_'G,@/-,@/-,@/-,@/-,@/-,@/
+M-+C_QYS(#S3(#S3(#S3(#S3(#S3(#S2X_\><R`\TR`\TR`\TR`\TR`\TR`\T
+MN/_'G,@/-,@/-,@/-,@/-,@/-,@/-+C_QYS(#S3(#S3(#S3(#S3(#S3(#S2X
+M_\><R`\TR`\TR`\TR`\TR`\TR`\TN/_'G,@/-,@/-,@/-,@/-,@/-,@/-+C_
+MQYS(#S3(#S3(C\[J^F/_^&'H/PV;<>5S#.7Z[O[NI_%KOQF',WT/4\6NV[W:
+M%,SAZU[]NS7)>VNL[=S"U#]"6EP]GNG[O^EAW/:;>BG:K_/THSR_7 at AGKOKM
+ME]MA;6W)KOAH[+*^=_/C>W-?)_X=9S_L#9/GWQR>?UO/OPNFGO]W.43?SW\-
+B_)O_;NKS"SW_`````````````````(#+]#?B%\M:`!@!````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_pax_filename_encoding.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_pax_filename_encoding.c,v 1.1.2.3 2008/08/25 01:55:40 kientzle Exp $");
+
+#include <locale.h>
+
+/*
+ * Pax interchange is supposed to encode filenames into
+ * UTF-8.  Of course, that's not always possible.  This
+ * test is intended to verify that filenames always get
+ * stored and restored correctly, regardless of the encodings.
+ */
+
+/*
+ * Read a manually-created archive that has filenames that are
+ * stored in binary instead of UTF-8 and verify that we get
+ * the right filename returned and that we get a warning only
+ * if the header isn't marked as binary.
+ */
+DEFINE_TEST(test_pax_filename_encoding_1)
+{
+	static const char testname[] = "test_pax_filename_encoding.tar.gz";
+	/*
+	 * \314\214 is a valid 2-byte UTF-8 sequence.
+	 * \374 is invalid in UTF-8.
+	 */
+	char filename[] = "abc\314\214mno\374xyz";
+	struct archive *a;
+	struct archive_entry *entry;
+
+	/*
+	 * Read an archive that has non-UTF8 pax filenames in it.
+	 */
+	extract_reference_file(testname);
+	a = archive_read_new();
+	assertEqualInt(ARCHIVE_OK, archive_read_support_format_tar(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_support_compression_gzip(a));
+	assertEqualInt(ARCHIVE_OK,
+	    archive_read_open_filename(a, testname, 10240));
+	/*
+	 * First entry in this test archive has an invalid UTF-8 sequence
+	 * in it, but the header is not marked as hdrcharset=BINARY, so that
+	 * requires a warning.
+	 */
+	failure("Invalid UTF8 in a pax archive pathname should cause a warning");
+	assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
+	assertEqualString(filename, archive_entry_pathname(entry));
+	/*
+	 * Second entry is identical except that it does have
+	 * hdrcharset=BINARY, so no warning should be generated.
+	 */
+	failure("A pathname with hdrcharset=BINARY can have invalid UTF8\n"
+	    " characters in it without generating a warning");
+	assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &entry));
+	assertEqualString(filename, archive_entry_pathname(entry));
+	archive_read_finish(a);
+}
+
+/*
+ * Set the locale and write a pathname containing invalid characters.
+ * This should work; the underlying implementation should automatically
+ * fall back to storing the pathname in binary.
+ */
+DEFINE_TEST(test_pax_filename_encoding_2)
+{
+	char filename[] = "abc\314\214mno\374xyz";
+	struct archive *a;
+	struct archive_entry *entry;
+	char buff[65536];
+	char longname[] = "abc\314\214mno\374xyz"
+	    "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
+	    "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
+	    "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
+	    "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
+	    "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
+	    "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz"
+	    ;
+	size_t used;
+
+	/*
+	 * We need a starting locale which has invalid sequences.
+	 * de_DE.UTF-8 seems to be commonly supported.
+	 */
+	/* If it doesn't exist, just warn and return. */
+	if (NULL == setlocale(LC_ALL, "de_DE.UTF-8")) {
+		skipping("invalid encoding tests require a suitable locale;"
+		    " de_DE.UTF-8 not available on this system");
+		return;
+	}
+
+	assert((a = archive_write_new()) != NULL);
+	assertEqualIntA(a, 0, archive_write_set_format_pax(a));
+	assertEqualIntA(a, 0, archive_write_set_compression_none(a));
+	assertEqualIntA(a, 0, archive_write_set_bytes_per_block(a, 0));
+	assertEqualInt(0,
+	    archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	assert((entry = archive_entry_new()) != NULL);
+	/* Set pathname, gname, uname, hardlink to nonconvertible values. */
+	archive_entry_copy_pathname(entry, filename);
+	archive_entry_copy_gname(entry, filename);
+	archive_entry_copy_uname(entry, filename);
+	archive_entry_copy_hardlink(entry, filename);
+	archive_entry_set_filetype(entry, AE_IFREG);
+	failure("This should generate a warning for nonconvertible names.");
+	assertEqualInt(ARCHIVE_WARN, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assert((entry = archive_entry_new()) != NULL);
+	/* Set path, gname, uname, and symlink to nonconvertible values. */
+	archive_entry_copy_pathname(entry, filename);
+	archive_entry_copy_gname(entry, filename);
+	archive_entry_copy_uname(entry, filename);
+	archive_entry_copy_symlink(entry, filename);
+	archive_entry_set_filetype(entry, AE_IFLNK);
+	failure("This should generate a warning for nonconvertible names.");
+	assertEqualInt(ARCHIVE_WARN, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assert((entry = archive_entry_new()) != NULL);
+	/* Set pathname to a very long nonconvertible value. */
+	archive_entry_copy_pathname(entry, longname);
+	archive_entry_set_filetype(entry, AE_IFREG);
+	failure("This should generate a warning for nonconvertible names.");
+	assertEqualInt(ARCHIVE_WARN, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assertEqualInt(0, archive_write_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(a);
+#else
+	assertEqualInt(0, archive_write_finish(a));
+#endif
+
+	/*
+	 * Now read the entries back.
+	 */
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualInt(0, archive_read_support_format_tar(a));
+	assertEqualInt(0, archive_read_open_memory(a, buff, used));
+
+	assertEqualInt(0, archive_read_next_header(a, &entry));
+	assertEqualString(filename, archive_entry_pathname(entry));
+	assertEqualString(filename, archive_entry_gname(entry));
+	assertEqualString(filename, archive_entry_uname(entry));
+	assertEqualString(filename, archive_entry_hardlink(entry));
+
+	assertEqualInt(0, archive_read_next_header(a, &entry));
+	assertEqualString(filename, archive_entry_pathname(entry));
+	assertEqualString(filename, archive_entry_gname(entry));
+	assertEqualString(filename, archive_entry_uname(entry));
+	assertEqualString(filename, archive_entry_symlink(entry));
+
+	assertEqualInt(0, archive_read_next_header(a, &entry));
+	assertEqualString(longname, archive_entry_pathname(entry));
+
+	assertEqualInt(0, archive_read_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_read_finish(a);
+#else
+	assertEqualInt(0, archive_read_finish(a));
+#endif
+}
+
+/*
+ * Create an entry starting from a wide-character Unicode pathname,
+ * read it back into "C" locale, which doesn't support the name.
+ * TODO: Figure out the "right" behavior here.
+ */
+DEFINE_TEST(test_pax_filename_encoding_3)
+{
+	wchar_t badname[] = L"xxxAyyyBzzz";
+	const char badname_utf8[] = "xxx\xE1\x88\xB4yyy\xE5\x99\xB8zzz";
+	struct archive *a;
+	struct archive_entry *entry;
+	char buff[65536];
+	size_t used;
+
+	badname[3] = 0x1234;
+	badname[7] = 0x5678;
+
+	/* If it doesn't exist, just warn and return. */
+	if (NULL == setlocale(LC_ALL, "C")) {
+		skipping("Can't set \"C\" locale, so can't exercise "
+		    "certain character-conversion failures");
+		return;
+	}
+
+	assert((a = archive_write_new()) != NULL);
+	assertEqualIntA(a, 0, archive_write_set_format_pax(a));
+	assertEqualIntA(a, 0, archive_write_set_compression_none(a));
+	assertEqualIntA(a, 0, archive_write_set_bytes_per_block(a, 0));
+	assertEqualInt(0,
+	    archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	assert((entry = archive_entry_new()) != NULL);
+	/* Set pathname to non-convertible wide value. */
+	archive_entry_copy_pathname_w(entry, badname);
+	archive_entry_set_filetype(entry, AE_IFREG);
+	assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname_w(entry, L"abc");
+	/* Set gname to non-convertible wide value. */
+	archive_entry_copy_gname_w(entry, badname);
+	archive_entry_set_filetype(entry, AE_IFREG);
+	assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname_w(entry, L"abc");
+	/* Set uname to non-convertible wide value. */
+	archive_entry_copy_uname_w(entry, badname);
+	archive_entry_set_filetype(entry, AE_IFREG);
+	assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname_w(entry, L"abc");
+	/* Set hardlink to non-convertible wide value. */
+	archive_entry_copy_hardlink_w(entry, badname);
+	archive_entry_set_filetype(entry, AE_IFREG);
+	assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname_w(entry, L"abc");
+	/* Set symlink to non-convertible wide value. */
+	archive_entry_copy_symlink_w(entry, badname);
+	archive_entry_set_filetype(entry, AE_IFLNK);
+	assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	assertEqualInt(0, archive_write_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(a);
+#else
+	assertEqualInt(0, archive_write_finish(a));
+#endif
+
+	/*
+	 * Now read the entries back.
+	 */
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualInt(0, archive_read_support_format_tar(a));
+	assertEqualInt(0, archive_read_open_memory(a, buff, used));
+
+	failure("A non-convertible pathname should cause a warning.");
+	assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
+	assertEqualWString(badname, archive_entry_pathname_w(entry));
+	failure("If native locale can't convert, we should get UTF-8 back.");
+	assertEqualString(badname_utf8, archive_entry_pathname(entry));
+
+	failure("A non-convertible gname should cause a warning.");
+	assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
+	assertEqualWString(badname, archive_entry_gname_w(entry));
+	failure("If native locale can't convert, we should get UTF-8 back.");
+	assertEqualString(badname_utf8, archive_entry_gname(entry));
+
+	failure("A non-convertible uname should cause a warning.");
+	assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
+	assertEqualWString(badname, archive_entry_uname_w(entry));
+	failure("If native locale can't convert, we should get UTF-8 back.");
+	assertEqualString(badname_utf8, archive_entry_uname(entry));
+
+	failure("A non-convertible hardlink should cause a warning.");
+	assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
+	assertEqualWString(badname, archive_entry_hardlink_w(entry));
+	failure("If native locale can't convert, we should get UTF-8 back.");
+	assertEqualString(badname_utf8, archive_entry_hardlink(entry));
+
+	failure("A non-convertible symlink should cause a warning.");
+	assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry));
+	assertEqualWString(badname, archive_entry_symlink_w(entry));
+	assertEqualWString(NULL, archive_entry_hardlink_w(entry));
+	failure("If native locale can't convert, we should get UTF-8 back.");
+	assertEqualString(badname_utf8, archive_entry_symlink(entry));
+
+	assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &entry));
+
+	assertEqualInt(0, archive_read_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_read_finish(a);
+#else
+	assertEqualInt(0, archive_read_finish(a));
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_acl_freebsd.c
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 2003-2008 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_acl_freebsd.c,v 1.1.2.1 2008/10/23 04:48:48 kientzle Exp $");
+
+#if defined(__FreeBSD__) && __FreeBSD__ > 4
+#include <sys/acl.h>
+
+struct myacl_t {
+	int type;  /* Type of ACL: "access" or "default" */
+	int permset; /* Permissions for this class of users. */
+	int tag; /* Owner, User, Owning group, group, other, etc. */
+	int qual; /* GID or UID of user/group, depending on tag. */
+	const char *name; /* Name of user/group, depending on tag. */
+};
+
+static struct myacl_t acls2[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	  ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	  ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, "" },
+	{ 0, 0, 0, 0, NULL }
+};
+
+static void
+set_acls(struct archive_entry *ae, struct myacl_t *acls)
+{
+	int i;
+
+	archive_entry_acl_clear(ae);
+	for (i = 0; acls[i].name != NULL; i++) {
+		archive_entry_acl_add_entry(ae,
+		    acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
+		    acls[i].name);
+	}
+}
+
+static int
+acl_match(acl_entry_t aclent, struct myacl_t *myacl)
+{
+	acl_tag_t tag_type;
+	acl_permset_t opaque_ps;
+	int permset = 0;
+
+	acl_get_tag_type(aclent, &tag_type);
+
+	/* translate the silly opaque permset to a bitmap */
+	acl_get_permset(aclent, &opaque_ps);
+	if (acl_get_perm_np(opaque_ps, ACL_EXECUTE))
+		permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+	if (acl_get_perm_np(opaque_ps, ACL_WRITE))
+		permset |= ARCHIVE_ENTRY_ACL_WRITE;
+	if (acl_get_perm_np(opaque_ps, ACL_READ))
+		permset |= ARCHIVE_ENTRY_ACL_READ;
+
+	if (permset != myacl->permset)
+		return (0);
+
+	switch (tag_type) {
+	case ACL_USER_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
+		break;
+	case ACL_USER:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+			return (0);
+		if ((uid_t)myacl->qual != *(uid_t *)acl_get_qualifier(aclent))
+			return (0);
+		break;
+	case ACL_GROUP_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
+		break;
+	case ACL_GROUP:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+			return (0);
+		if ((gid_t)myacl->qual != *(gid_t *)acl_get_qualifier(aclent))
+			return (0);
+		break;
+	case ACL_MASK:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
+		break;
+	case ACL_OTHER:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0);
+		break;
+	}
+	return (1);
+}
+
+static void
+compare_acls(acl_t acl, struct myacl_t *myacls)
+{
+	int *marker;
+	int entry_id = ACL_FIRST_ENTRY;
+	int matched;
+	int i, n;
+	acl_entry_t acl_entry;
+
+	/* Count ACL entries in myacls array and allocate an indirect array. */
+	for (n = 0; myacls[n].name != NULL; ++n)
+		continue;
+	marker = malloc(sizeof(marker[0]) * n);
+	for (i = 0; i < n; i++)
+		marker[i] = i;
+
+	/*
+	 * Iterate over acls in system acl object, try to match each
+	 * one with an item in the myacls array.
+	 */
+	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
+		/* After the first time... */
+		entry_id = ACL_NEXT_ENTRY;
+
+		/* Search for a matching entry (tag and qualifier) */
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(acl_entry, &myacls[marker[i]])) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+
+		/* TODO: Print out more details in this case. */
+		failure("ACL entry on file that shouldn't be there");
+		assert(matched == 1);
+	}
+
+	/* Dump entries in the myacls array that weren't in the system acl. */
+	for (i = 0; i < n; ++i) {
+		failure(" ACL entry missing from file: "
+		    "type=%d,permset=%d,tag=%d,qual=%d,name=``%s''\n",
+		    myacls[marker[i]].type, myacls[marker[i]].permset,
+		    myacls[marker[i]].tag, myacls[marker[i]].qual,
+		    myacls[marker[i]].name);
+		assert(0); /* Record this as a failure. */
+	}
+	free(marker);
+}
+
+#endif
+
+
+/*
+ * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
+ */
+
+DEFINE_TEST(test_acl_freebsd)
+{
+#if !defined(__FreeBSD__)
+	skipping("FreeBSD-specific ACL restore test");
+#elif __FreeBSD__ < 5
+	skipping("ACL restore supported only on FreeBSD 5.0 and later");
+#else
+	struct stat st;
+	struct archive *a;
+	struct archive_entry *ae;
+	int n, fd;
+	acl_t acl;
+
+	/*
+	 * First, do a quick manual set/read of ACL data to
+	 * verify that the local filesystem does support ACLs.
+	 * If it doesn't, we'll simply skip the remaining tests.
+	 */
+	acl = acl_from_text("u::rwx,u:1:rw,g::rwx,g:15:rx,o::rwx,m::rwx");
+	assert((void *)acl != NULL);
+	/* Create a test file and try to set an ACL on it. */
+	fd = open("pretest", O_WRONLY | O_CREAT | O_EXCL, 0777);
+	failure("Could not create test file?!");
+	n = -1;
+	if (assert(fd >= 0)) {
+		n = acl_set_fd(fd, acl);
+		failure("acl_set_fd(): errno = %d (%s)",
+		    errno, strerror(errno));
+		assertEqualInt(0, n);
+		close(fd);
+	}
+
+	if (fd < 0 || n != 0) {
+		skipping("ACL tests require that ACL support be enabled on the filesystem");
+		return;
+	}
+
+	/* Create a write-to-disk object. */
+	assert(NULL != (a = archive_write_disk_new()));
+	archive_write_disk_set_options(a,
+	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
+
+	/* Populate an archive entry with some metadata, including ACL info */
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_pathname(ae, "test0");
+	archive_entry_set_mtime(ae, 123456, 7890);
+	archive_entry_set_size(ae, 0);
+	set_acls(ae, acls2);
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* Close the archive. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_write_finish(a));
+
+	/* Verify the data on disk. */
+	assertEqualInt(0, stat("test0", &st));
+	assertEqualInt(st.st_mtime, 123456);
+	acl = acl_get_file("test0", ACL_TYPE_ACCESS);
+	assert(acl != (acl_t)NULL);
+	compare_acls(acl, acls2);
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_write_disk_hardlink.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_write_disk_hardlink.c,v 1.1.2.4 2008/11/28 20:08:47 kientzle Exp $");
+
+#define UMASK 022
+
+/*
+ * Exercise hardlink recreation.
+ *
+ * File permissions are chosen so that the authoritive entry
+ * has the correct permission and the non-authoritive versions
+ * are just writeable files.
+ */
+DEFINE_TEST(test_write_disk_hardlink)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("archive_write_disk_hardlink tests");
+#else
+	static const char data[]="abcdefghijklmnopqrstuvwxyz";
+	struct archive *ad;
+	struct archive_entry *ae;
+	struct stat st, st2;
+
+	/* Force the umask to something predictable. */
+	umask(UMASK);
+
+	/* Write entries to disk. */
+	assert((ad = archive_write_disk_new()) != NULL);
+
+	/*
+	 * First, use a tar-like approach; a regular file, then
+	 * a separate "hardlink" entry.
+	 */
+
+	/* Regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link1a");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, sizeof(data));
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(sizeof(data),
+	    archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/* Link.  Size of zero means this doesn't carry data. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link1b");
+	archive_entry_set_mode(ae, S_IFREG | 0642);
+	archive_entry_set_size(ae, 0);
+	archive_entry_copy_hardlink(ae, "link1a");
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(ARCHIVE_WARN,
+	    archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/*
+	 * Repeat tar approach test, but use unset to mark the
+	 * hardlink as having no data.
+	 */
+
+	/* Regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link2a");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, sizeof(data));
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(sizeof(data),
+	    archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/* Link.  Unset size means this doesn't carry data. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link2b");
+	archive_entry_set_mode(ae, S_IFREG | 0642);
+	archive_entry_unset_size(ae);
+	archive_entry_copy_hardlink(ae, "link2a");
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(ARCHIVE_WARN,
+	    archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/*
+	 * Second, try an old-cpio-like approach; a regular file, then
+	 * another identical one (which has been marked hardlink).
+	 */
+
+	/* Regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link3a");
+	archive_entry_set_mode(ae, S_IFREG | 0600);
+	archive_entry_set_size(ae, sizeof(data));
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/* Link. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link3b");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, sizeof(data));
+	archive_entry_copy_hardlink(ae, "link3a");
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/*
+	 * Finally, try a new-cpio-like approach, where the initial
+	 * regular file is empty and the hardlink has the data.
+	 */
+
+	/* Regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link4a");
+	archive_entry_set_mode(ae, S_IFREG | 0600);
+	archive_entry_set_size(ae, 0);
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+#if ARCHIVE_VERSION_NUMBER < 3000000
+	assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1));
+#else
+	assertEqualInt(-1, archive_write_data(ad, data, 1));
+#endif
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/* Link. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link4b");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, sizeof(data));
+	archive_entry_copy_hardlink(ae, "link4a");
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(ad);
+#else
+	assertEqualInt(0, archive_write_finish(ad));
+#endif
+
+	/* Test the entries on disk. */
+
+	/* Test #1 */
+	assert(0 == stat("link1a", &st));
+	/* If the hardlink was successfully created and the archive
+	 * doesn't carry data for it, we consider it to be
+	 * non-authoritive for meta data as well.  This is consistent
+	 * with GNU tar and BSD pax.  */
+	assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK);
+	assertEqualInt(st.st_size, sizeof(data));
+	assertEqualInt(st.st_nlink, 2);
+
+	assert(0 == stat("link1b", &st2));
+	assertEqualInt(st.st_mode, st2.st_mode);
+	assertEqualInt(st.st_size, st2.st_size);
+	assertEqualInt(st.st_nlink, st2.st_nlink);
+	assertEqualInt(st.st_ino, st2.st_ino);
+	assertEqualInt(st.st_dev, st2.st_dev);
+
+	/* Test #2: Should produce identical results to test #1 */
+	/* Note that marking a hardlink with size = 0 is treated the
+	 * same as having an unset size.  This is partly for backwards
+	 * compatibility (we used to not have unset tracking, so
+	 * relied on size == 0) and partly to match the model used by
+	 * common file formats that store a size of zero for
+	 * hardlinks. */
+	assert(0 == stat("link2a", &st));
+	assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK);
+	assertEqualInt(st.st_size, sizeof(data));
+	assertEqualInt(st.st_nlink, 2);
+
+	assert(0 == stat("link2b", &st2));
+	assertEqualInt(st.st_mode, st2.st_mode);
+	assertEqualInt(st.st_size, st2.st_size);
+	assertEqualInt(st.st_nlink, st2.st_nlink);
+	assertEqualInt(st.st_ino, st2.st_ino);
+	assertEqualInt(st.st_dev, st2.st_dev);
+
+	/* Test #3 */
+	assert(0 == stat("link3a", &st));
+	assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK);
+	assertEqualInt(st.st_size, sizeof(data));
+	assertEqualInt(st.st_nlink, 2);
+
+	assert(0 == stat("link3b", &st2));
+	assertEqualInt(st2.st_mode, (S_IFREG | 0755) & ~UMASK);
+	assertEqualInt(st2.st_size, sizeof(data));
+	assertEqualInt(st2.st_nlink, 2);
+	assertEqualInt(st.st_ino, st2.st_ino);
+	assertEqualInt(st.st_dev, st2.st_dev);
+
+	/* Test #4 */
+	assert(0 == stat("link4a", &st));
+	assertEqualInt(st.st_mode, (S_IFREG | 0755) & ~UMASK);
+	assertEqualInt(st.st_size, sizeof(data));
+	assertEqualInt(st.st_nlink, 2);
+
+	assert(0 == stat("link4b", &st2));
+	assertEqualInt(st2.st_mode, (S_IFREG | 0755) & ~UMASK);
+	assertEqualInt(st2.st_size, sizeof(data));
+	assertEqualInt(st2.st_nlink, 2);
+	assertEqualInt(st.st_ino, st2.st_ino);
+	assertEqualInt(st.st_dev, st2.st_dev);
+#endif
+}
Index: read_open_memory.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/read_open_memory.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/read_open_memory.c -L lib/libarchive/test/read_open_memory.c -u -r1.1 -r1.2
--- lib/libarchive/test/read_open_memory.c
+++ lib/libarchive/test/read_open_memory.c
@@ -24,7 +24,7 @@
  */
 
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/read_open_memory.c,v 1.1 2007/07/13 15:12:52 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/read_open_memory.c,v 1.1.2.1 2008/02/11 00:31:08 kientzle Exp $");
 
 #include <errno.h>
 #include <stdlib.h>
@@ -96,7 +96,7 @@
 memory_read(struct archive *a, void *client_data, const void **buff)
 {
 	struct read_memory_data *mine = (struct read_memory_data *)client_data;
-	ssize_t size;
+	size_t size;
 
 	(void)a; /* UNUSED */
 	size = mine->end - mine->buffer;
--- /dev/null
+++ lib/libarchive/test/test_read_format_isorr_bz2.iso.bz2.uu
@@ -0,0 +1,24 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_isorr_bz2.iso.bz2.uu,v 1.1.2.1 2008/08/10 04:32:47 kientzle Exp $
+
+begin 644 test_read_format_isorr_bz2.iso.bz2
+M0EIH.3%!629361M#:2D``,?_W?__6_Y58_GX/__?X*?OWB8AZB0`,`$@`$0"
+M at 0C``QP`U:!,&J>FJ>2:CR)^I'ZB#3U/4-`!H:#(:``]0P@`!H9!D!II-$,A
+M&BGFJ>34]0T!IHT&AHT:#0:``!ZC$`#0T.`!H&@&AH``!IB&C30```!H9``!
+MA(D0DTQ3T3$TVA-!IIZAA-'J:!H!H#0:#30:#30T;1*PHGAZ"/F;E""L"I6"
+M8W&#'./D%S=_T4T96&+ at 94X&AL;:`Y+0C?:%=B#:8`:PP`2WF"20!EXL)6=]
+M8=A)!0Q)($C&$U#8AI(&QL2!"10P4^8D$"0,8$I-.!3R8YWZ]Q1./IDR^VYN
+MRJ&76*,$3PG?U(,=C;I20`D<&9/%5ILJIGI0(SWP3KRID6=#1MV*A>)(*B0$
+M$E:><!0D%K$G(WM("\:="00+`OCJ`Y0K(4B%(:1`#1-.%*`S0+)N`I4'MID+
+M$8MAN[,\.QJ`I=ZDA(5<O`KS[(+3L4-$,@&#*7!<`@AO7Y8*E9S#7L>B944(
+MO-&.8&:1K;>[K$?O7R-FWA;%5+E]WBV<T&*%[O-6_,_/]:YC;<A-%^UFF09U
+MP`*D@;&4KUHN&1:F_D1:YM:J-EG8L%<+BF4W%"6TU:I68MIJ6"FVH>T9PR7J
+MNU2C2G2>5**"XH4HD`PF+(*DTT&47'A+)B";NS-UH>(]7G^\/G_343KU\17<
+M<*""-SM"%>BVIJL8SF]7L-1.-LSRP2%=KX&C56*FC&#C$XNMGL)]3X&^$V4Z
+MY`()G`%`KUR!HU8Z'"HWNE&P6MI:KZ<F%H/X3DN/F&%#`.%8#!HTK295C.#[
+M+^4C&90I^(::@`Y$=<OX=S3?,A#ZU'\'^+PYHV2PK?Q-,&8/Q$A3@$*X*=$A
+MK;I2)&A^MSZ`*](@"[>^Q"H0L7.OV8ZJW409[QO=`&&D%=5&@RP`MO%R/J#Q
+M-KJ*6D;EH7:DK0.48 at 8HF*IP(>*YMR$>!+A,)X+;`$94@@?U]B/=2T0CY-2=
+M*_1FPF<-G\\@Z-_,Q>06='5:(B#3`W$8Y!:C-CE22SM9*S$00,XXJTIZ!GA(
+LGTMN:F\J-,D9>?.38*!I7T>--*B_=T44HJ?#@``"1;7#_Q=R13A0D!M#:2D`
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_pax_filename_encoding.tar.gz.uu
@@ -0,0 +1,11 @@
+$FreeBSD: src/lib/libarchive/test/test_pax_filename_encoding.tar.gz.uu,v 1.1.2.2 2008/08/10 05:38:07 kientzle Exp $
+begin 644 test_pax_filename_encoding.tar.gz
+M'XL(`)4;VT<``^V6STK#0!#&<\Y3[!/HS/Z-ASVHEQ1$BE[L<4T6$DP32:-$
+MG\%'\Y%Z,*7$UEJLE"91NK_+P.P>OF'X^&9LZM":V):GYCYZ?YOFQ5W]\NH=
+M%`"0G).FHA*P7I>@E`1!22E!`9,$4#"0'JD/*V,[3[/*E(V4*IW^^&_7^W(4
+M\EG_"13)HZD2W6Y_WFS?IT"B9EZKD8(0+)"!6/3,EQZ5/BIR>QF.KB8GL7W6
+M0>!3VC;2O-"<H>#\S,>@[>99FC]H](>>VM'2G>M7[/(_ at -CP/V-4>`2Z$K3.
+MD?L_M%E6#"W",1CC;_D_[SW_*;+-_!><N?SO at R;_D[B,$E/.;*4O1M?G-Q/_
+L%T<!1\7V/@IP\<T=!7^![ER_8H_\%PI=_O>!RW^'P^$X3CX`98.>C@`4````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_write_format_cpio_odc.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_write_format_cpio_odc.c,v 1.1.2.2 2008/08/09 23:14:37 kientzle Exp $");
+
+
+static int
+is_octal(const char *p, size_t l)
+{
+	while (l > 0) {
+		if (*p < '0' || *p > '7')
+			return (0);
+		--l;
+		++p;
+	}
+	return (1);
+}
+
+/*
+ * Detailed verification that cpio 'odc' archives are written with
+ * the correct format.
+ */
+DEFINE_TEST(test_write_format_cpio_odc)
+{
+	struct archive *a;
+	struct archive_entry *entry;
+	char *buff, *e;
+	size_t buffsize = 100000;
+	size_t used;
+
+	buff = malloc(buffsize);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertEqualIntA(a, 0, archive_write_set_format_cpio(a));
+	assertEqualIntA(a, 0, archive_write_set_compression_none(a));
+	assertEqualIntA(a, 0, archive_write_open_memory(a, buff, buffsize, &used));
+
+	/*
+	 * Add various files to it.
+	 * TODO: Extend this to cover more filetypes.
+	 */
+
+	/* "file" with 10 bytes of content */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, "file");
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_uid(entry, 80);
+	archive_entry_set_gid(entry, 90);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 89);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	assertEqualIntA(a, 10, archive_write_data(a, "1234567890", 10));
+
+	/* Hardlink to "file" with 10 bytes of content */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, "linkfile");
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_uid(entry, 80);
+	archive_entry_set_gid(entry, 90);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 89);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	assertEqualIntA(a, 10, archive_write_data(a, "1234567890", 10));
+
+	/* "dir" */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 2, 20);
+	archive_entry_set_pathname(entry, "dir");
+	archive_entry_set_mode(entry, S_IFDIR | 0775);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	/* Write of data to dir should fail == zero bytes get written. */
+	assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
+
+	/* "symlink" pointing to "file" */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 3, 30);
+	archive_entry_set_pathname(entry, "symlink");
+	archive_entry_set_mode(entry, S_IFLNK | 0664);
+	archive_entry_set_symlink(entry,"file");
+	archive_entry_set_size(entry, 0);
+	archive_entry_set_uid(entry, 88);
+	archive_entry_set_gid(entry, 98);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 90);
+	archive_entry_set_nlink(entry, 1);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	/* Write of data to symlink should fail == zero bytes get written. */
+	assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
+
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Verify the archive format.
+	 */
+	e = buff;
+
+	/* "file" */
+	assert(is_octal(e, 76)); /* Entire header is octal digits. */
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assertEqualMem(e + 6, "000014", 6); /* dev */
+	assertEqualMem(e + 12, "000131", 6); /* ino */
+	assertEqualMem(e + 18, "100664", 6); /* Mode */
+	assertEqualMem(e + 24, "000120", 6); /* uid */
+	assertEqualMem(e + 30, "000132", 6); /* gid */
+	assertEqualMem(e + 36, "000002", 6); /* nlink */
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	assertEqualMem(e + 48, "00000000001", 11); /* mtime */
+	assertEqualMem(e + 59, "000005", 6); /* Name size */
+	assertEqualMem(e + 65, "00000000012", 11); /* File size */
+	assertEqualMem(e + 76, "file\0", 5); /* Name contents */
+	assertEqualMem(e + 81, "1234567890", 10); /* File contents */
+	e += 91;
+
+	/* hardlink to "file" */
+	assert(is_octal(e, 76)); /* Entire header is octal digits. */
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assertEqualMem(e + 6, "000014", 6); /* dev */
+	assertEqualMem(e + 12, "000131", 6); /* ino */
+	assertEqualMem(e + 18, "100664", 6); /* Mode */
+	assertEqualMem(e + 24, "000120", 6); /* uid */
+	assertEqualMem(e + 30, "000132", 6); /* gid */
+	assertEqualMem(e + 36, "000002", 6); /* nlink */
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	assertEqualMem(e + 48, "00000000001", 11); /* mtime */
+	assertEqualMem(e + 59, "000011", 6); /* Name size */
+	assertEqualMem(e + 65, "00000000012", 11); /* File size */
+	assertEqualMem(e + 76, "linkfile\0", 9); /* Name contents */
+	assertEqualMem(e + 85, "1234567890", 10); /* File contents */
+	e += 95;
+
+	/* "dir" */
+	assert(is_octal(e, 76));
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assertEqualMem(e + 6, "000000", 6); /* dev */
+	assertEqualMem(e + 12, "000000", 6); /* ino */
+	assertEqualMem(e + 18, "040775", 6); /* Mode */
+	assertEqualMem(e + 24, "000000", 6); /* uid */
+	assertEqualMem(e + 30, "000000", 6); /* gid */
+	assertEqualMem(e + 36, "000002", 6); /* Nlink */
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	assertEqualMem(e + 48, "00000000002", 11); /* mtime */
+	assertEqualMem(e + 59, "000004", 6); /* Name size */
+	assertEqualMem(e + 65, "00000000000", 11); /* File size */
+	assertEqualMem(e + 76, "dir\0", 4); /* name */
+	e += 80;
+
+	/* "symlink" pointing to "file" */
+	assert(is_octal(e, 76)); /* Entire header is octal digits. */
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assertEqualMem(e + 6, "000014", 6); /* dev */
+	assertEqualMem(e + 12, "000132", 6); /* ino */
+	assertEqualMem(e + 18, "120664", 6); /* Mode */
+	assertEqualMem(e + 24, "000130", 6); /* uid */
+	assertEqualMem(e + 30, "000142", 6); /* gid */
+	assertEqualMem(e + 36, "000001", 6); /* nlink */
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	assertEqualMem(e + 48, "00000000003", 11); /* mtime */
+	assertEqualMem(e + 59, "000010", 6); /* Name size */
+	assertEqualMem(e + 65, "00000000004", 11); /* File size */
+	assertEqualMem(e + 76, "symlink\0", 8); /* Name contents */
+	assertEqualMem(e + 84, "file", 4); /* File contents == link target */
+	e += 88;
+
+	/* TODO: Verify other types of entries. */
+
+	/* Last entry is end-of-archive marker. */
+	assert(is_octal(e, 76));
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assertEqualMem(e + 6, "000000", 6); /* dev */
+	assertEqualMem(e + 12, "000000", 6); /* ino */
+	assertEqualMem(e + 18, "000000", 6); /* Mode */
+	assertEqualMem(e + 24, "000000", 6); /* uid */
+	assertEqualMem(e + 30, "000000", 6); /* gid */
+	assertEqualMem(e + 36, "000001", 6); /* Nlink */
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	assertEqualMem(e + 48, "00000000000", 11); /* mtime */
+	assertEqualMem(e + 59, "000013", 6); /* Name size */
+	assertEqualMem(e + 65, "00000000000", 11); /* File size */
+	assertEqualMem(e + 76, "TRAILER!!!\0", 11); /* Name */
+	e += 87;
+
+	assertEqualInt(used, e - buff);
+
+	free(buff);
+}
--- /dev/null
+++ lib/libarchive/test/test_write_format_cpio_newc.c
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_write_format_cpio_newc.c,v 1.2.2.2 2008/08/09 23:14:37 kientzle Exp $");
+
+
+static int
+is_hex(const char *p, size_t l)
+{
+	while (l > 0) {
+		if (*p >= 0 && *p <= '9') {
+			/* Ascii digit */
+		} else if (*p >= 'a' && *p <= 'f') {
+			/* lowercase letter a-f */
+		} else {
+			/* Not hex. */
+			return (0);
+		}
+		--l;
+		++p;
+	}
+	return (1);
+}
+
+/*
+ * Detailed verification that cpio 'newc' archives are written with
+ * the correct format.
+ */
+DEFINE_TEST(test_write_format_cpio_newc)
+{
+	struct archive *a;
+	struct archive_entry *entry;
+	char *buff, *e;
+	size_t buffsize = 100000;
+	size_t used;
+
+	buff = malloc(buffsize);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertEqualIntA(a, 0, archive_write_set_format_cpio_newc(a));
+	assertEqualIntA(a, 0, archive_write_set_compression_none(a));
+	assertEqualIntA(a, 0, archive_write_open_memory(a, buff, buffsize, &used));
+
+	/*
+	 * Add various files to it.
+	 * TODO: Extend this to cover more filetypes.
+	 */
+
+	/* Regular file */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, "file");
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_uid(entry, 80);
+	archive_entry_set_gid(entry, 90);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 89);
+	archive_entry_set_nlink(entry, 1);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	assertEqualIntA(a, 10, archive_write_data(a, "1234567890", 10));
+
+	/* Directory */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 2, 20);
+	archive_entry_set_pathname(entry, "dir");
+	archive_entry_set_mode(entry, S_IFDIR | 0775);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
+
+	/* Symlink */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 3, 30);
+	archive_entry_set_pathname(entry, "lnk");
+	archive_entry_set_mode(entry, S_IFLNK | 0664);
+	archive_entry_set_size(entry, 0);
+	archive_entry_set_uid(entry, 83);
+	archive_entry_set_gid(entry, 93);
+	archive_entry_set_dev(entry, 13);
+	archive_entry_set_ino(entry, 88);
+	archive_entry_set_nlink(entry, 1);
+	archive_entry_set_symlink(entry,"a");
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Verify the archive format.
+	 */
+	e = buff;
+
+	/* First entry is "file" */
+	assert(is_hex(e, 110)); /* Entire header is hex digits. */
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assertEqualMem(e + 6, "00000059", 8); /* ino */
+	assertEqualMem(e + 14, "000081b4", 8); /* Mode */
+	assertEqualMem(e + 22, "00000050", 8); /* uid */
+	assertEqualMem(e + 30, "0000005a", 8); /* gid */
+	assertEqualMem(e + 38, "00000001", 8); /* nlink */
+	assertEqualMem(e + 46, "00000001", 8); /* mtime */
+	assertEqualMem(e + 54, "0000000a", 8); /* File size */
+	assertEqualMem(e + 62, "00000000", 8); /* devmajor */
+	assertEqualMem(e + 70, "0000000c", 8); /* devminor */
+	assertEqualMem(e + 78, "00000000", 8); /* rdevmajor */
+	assertEqualMem(e + 86, "00000000", 8); /* rdevminor */
+	assertEqualMem(e + 94, "00000005", 8); /* Name size */
+	assertEqualMem(e + 102, "00000000", 8); /* CRC */
+	assertEqualMem(e + 110, "file\0\0", 6); /* Name contents */
+	assertEqualMem(e + 116, "1234567890", 10); /* File body */
+	assertEqualMem(e + 126, "\0\0", 2); /* Pad to multiple of 4 */
+	e += 128; /* Must be multiple of four here! */
+
+	/* Second entry is "dir" */
+	assert(is_hex(e, 110));
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assertEqualMem(e + 6, "00000000", 8); /* ino */
+	assertEqualMem(e + 14, "000041fd", 8); /* Mode */
+	assertEqualMem(e + 22, "00000000", 8); /* uid */
+	assertEqualMem(e + 30, "00000000", 8); /* gid */
+	assertEqualMem(e + 38, "00000002", 8); /* nlink */
+	assertEqualMem(e + 46, "00000002", 8); /* mtime */
+	assertEqualMem(e + 54, "00000000", 8); /* File size */
+	assertEqualMem(e + 62, "00000000", 8); /* devmajor */
+	assertEqualMem(e + 70, "00000000", 8); /* devminor */
+	assertEqualMem(e + 78, "00000000", 8); /* rdevmajor */
+	assertEqualMem(e + 86, "00000000", 8); /* rdevminor */
+	assertEqualMem(e + 94, "00000004", 8); /* Name size */
+	assertEqualMem(e + 102, "00000000", 8); /* CRC */
+	assertEqualMem(e + 110, "dir\0", 4); /* name */
+	assertEqualMem(e + 114, "\0\0", 2); /* Pad to multiple of 4 */
+	e += 116; /* Must be multiple of four here! */
+
+	/* Third entry is "lnk" */
+	assert(is_hex(e, 110)); /* Entire header is hex digits. */
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assertEqualMem(e + 6, "00000058", 8); /* ino */
+	assertEqualMem(e + 14, "0000a1b4", 8); /* Mode */
+	assertEqualMem(e + 22, "00000053", 8); /* uid */
+	assertEqualMem(e + 30, "0000005d", 8); /* gid */
+	assertEqualMem(e + 38, "00000001", 8); /* nlink */
+	assertEqualMem(e + 46, "00000003", 8); /* mtime */
+	assertEqualMem(e + 54, "00000001", 8); /* File size */
+	assertEqualMem(e + 62, "00000000", 8); /* devmajor */
+	assertEqualMem(e + 70, "0000000d", 8); /* devminor */
+	assertEqualMem(e + 78, "00000000", 8); /* rdevmajor */
+	assertEqualMem(e + 86, "00000000", 8); /* rdevminor */
+	assertEqualMem(e + 94, "00000004", 8); /* Name size */
+	assertEqualMem(e + 102, "00000000", 8); /* CRC */
+	assertEqualMem(e + 110, "lnk\0\0\0", 6); /* Name contents */
+	assertEqualMem(e + 116, "a\0\0\0", 4); /* File body + pad */
+	e += 120; /* Must be multiple of four here! */
+
+	/* TODO: Verify other types of entries. */
+
+	/* Last entry is end-of-archive marker. */
+	assert(is_hex(e, 76));
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assertEqualMem(e + 6, "00000000", 8); /* ino */
+	assertEqualMem(e + 14, "00000000", 8); /* Mode */
+	assertEqualMem(e + 22, "00000000", 8); /* uid */
+	assertEqualMem(e + 30, "00000000", 8); /* gid */
+	assertEqualMem(e + 38, "00000001", 8); /* nlink */
+	assertEqualMem(e + 46, "00000000", 8); /* mtime */
+	assertEqualMem(e + 54, "00000000", 8); /* File size */
+	assertEqualMem(e + 62, "00000000", 8); /* devmajor */
+	assertEqualMem(e + 70, "00000000", 8); /* devminor */
+	assertEqualMem(e + 78, "00000000", 8); /* rdevmajor */
+	assertEqualMem(e + 86, "00000000", 8); /* rdevminor */
+	assertEqualMem(e + 94, "0000000b", 8); /* Name size */
+	assertEqualMem(e + 102, "00000000", 8); /* CRC */
+	assertEqualMem(e + 110, "TRAILER!!!\0", 11); /* Name */
+	assertEqualMem(e + 121, "\0\0\0", 3); /* Pad to multiple of 4 bytes */
+	e += 124; /* Must be multiple of four here! */
+
+	assertEqualInt(used, e - buff);
+
+	free(buff);
+}
Index: test_read_format_ar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_format_ar.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_format_ar.c -L lib/libarchive/test/test_read_format_ar.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_format_ar.c
+++ lib/libarchive/test/test_read_format_ar.c
@@ -26,7 +26,7 @@
  */
 
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_ar.c,v 1.4 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_ar.c,v 1.4.2.1 2008/05/21 04:07:35 kientzle Exp $");
 
 #if ARCHIVE_VERSION_STAMP >= 1009000
 /*
@@ -75,9 +75,7 @@
 	assertEqualInt(0, archive_entry_mtime(ae));
 	assertEqualInt(0, archive_entry_uid(ae));
 	assertEqualInt(0, archive_entry_gid(ae));
-	assertEqualInt(40, archive_entry_size(ae));
-	assertEqualIntA(a, 40, archive_read_data(a, buff, 50));
-	assert(0 == memcmp(buff, "yyytttsssaaafff.o/\nhhhhjjjjkkkkllll.o/\n\n", 40));
+	assertEqualInt(0, archive_entry_size(ae));
 
 	/* First Entry */
 	assertA(0 == archive_read_next_header(a, &ae));
--- /dev/null
+++ lib/libarchive/test/test_read_format_zip.zip.uu
@@ -0,0 +1,14 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_zip.zip.uu,v 1.2.2.3 2008/11/28 20:08:47 kientzle Exp $
+begin 644 test_read_format_zip.zip
+M4$L#!`H`"````%EFLS8````````````````$`!4`9&ER+U54"0`#&55/1M19
+M_4A5>`0`Z`/H`U!+!P@```````````````!02P,$%`````@`;V:S-CHW9CT*
+M````$@````4`%0!F:6QE,554"0`#055/1L!9_4A5>`0`Z`/H`\M(S<G)Y\I`
+M(@%02P,$%``(``@`6FJS-@``````````$@````4`%0!F:6QE,E54"0`#K%M/
+M1L!9_4A5>`0`Z`/H`\M(S<G)Y\I`(@%02P<(.C=F$@H````2````4$L!`A<#
+M"@`(````66:S-@````````````````0`#0`````````0`.U!`````&1I<B]5
+M5`4``QE53T95>```4$L!`A<#%``(``@`;V:S-CHW9CT*````$@````4`#0``
+M`````0```.V!1P```&9I;&4Q550%``-!54]&57@``%!+`0(7`Q0`"``(`%IJ
+MLS8Z-V8]"@```!(````%``T```````$```#M at 8D```!F:6QE,E54!0`#K%M/
+;1E5X``!02P4&``````,``P"_````VP``````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_read_format_mtree.c
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_read_format_mtree.c,v 1.1.2.2 2008/08/10 04:32:47 kientzle Exp $");
+
+/* Single entry with a hardlink. */
+static unsigned char archive[] = {
+	"#mtree\n"
+	"file type=file uid=18 mode=0123\n"
+	"dir type=dir\n"
+	" file\\040with\\040space type=file uid=18\n"
+	" ..\n"
+	"file\\04with\\040space type=file\n"
+	"dir2 type=dir\n"
+	" dir3a type=dir\n"
+	"  indir3a type=file\n"
+	"dir2/fullindir2 type=file mode=0777\n"
+	"  ..\n"
+	" indir2 type=file\n"
+	" dir3b type=dir\n"
+	"  indir3b type=file\n"
+	"  ..\n"
+	" ..\n"
+	"notindir type=file\n"
+	"dir2/fullindir2 mode=0644\n"
+};
+
+DEFINE_TEST(test_read_format_mtree)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK,
+	    archive_read_support_compression_all(a));
+	assertEqualIntA(a, ARCHIVE_OK,
+	    archive_read_support_format_all(a));
+	assertEqualIntA(a, ARCHIVE_OK,
+	    archive_read_open_memory(a, archive, sizeof(archive)));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE_V1);
+	assertEqualString(archive_entry_pathname(ae), "file");
+	assertEqualInt(archive_entry_uid(ae), 18);
+	assert(S_ISREG(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123);
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir");
+	assert(S_ISDIR(archive_entry_mode(ae)));
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir/file with space");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "file\\04with space");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2/dir3a");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2/dir3a/indir3a");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2/fullindir2");
+	assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2/indir2");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2/dir3b");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "dir2/dir3b/indir3b");
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(archive_entry_pathname(ae), "notindir");
+
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
Index: main.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/main.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/main.c -L lib/libarchive/test/main.c -u -r1.1 -r1.2
--- lib/libarchive/test/main.c
+++ lib/libarchive/test/main.c
@@ -27,12 +27,35 @@
  * Various utility routines useful for test programs.
  * Each test program is linked against this file.
  */
+#include "test.h"
+
 #include <errno.h>
+#include <locale.h>
 #include <stdarg.h>
 #include <time.h>
 
-#include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/main.c,v 1.8 2007/07/31 05:03:27 kientzle Exp $");
+/*
+ * This same file is used pretty much verbatim for all test harnesses.
+ *
+ * The next few lines are the only differences.
+ */
+#undef	PROGRAM              /* Testing a library, not a program. */
+#define	ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */
+#define	EXTRA_DUMP(x)	archive_error_string((struct archive *)(x))
+#define	EXTRA_VERSION	archive_version()
+__FBSDID("$FreeBSD: src/lib/libarchive/test/main.c,v 1.8.2.4 2008/08/10 04:32:47 kientzle Exp $");
+
+/*
+ * "list.h" is simply created by "grep DEFINE_TEST"; it has
+ * a line like
+ *      DEFINE_TEST(test_function)
+ * for each test.
+ * Include it here with a suitable DEFINE_TEST to declare all of the
+ * test functions.
+ */
+#undef DEFINE_TEST
+#define	DEFINE_TEST(name) void name(void);
+#include "list.h"
 
 /* Interix doesn't define these in a standard header. */
 #if __INTERIX__
@@ -40,14 +63,23 @@
 extern int optind;
 #endif
 
-/* Default is to crash and try to force a core dump on failure. */
-static int dump_on_failure = 1;
+/* Enable core dump on failure. */
+static int dump_on_failure = 0;
+/* Default is to remove temp dirs for successful tests. */
+static int keep_temp_files = 0;
 /* Default is to print some basic information about each test. */
 static int quiet_flag = 0;
+/* Default is to summarize repeated failures. */
+static int verbose = 0;
 /* Cumulative count of component failures. */
 static int failures = 0;
 /* Cumulative count of skipped component tests. */
 static int skips = 0;
+/* Cumulative count of assertions. */
+static int assertions = 0;
+
+/* Directory where uuencoded reference files can be found. */
+static char *refdir;
 
 /*
  * My own implementation of the standard assert() macro emits the
@@ -57,12 +89,10 @@
  * Emacs.  ;-)
  *
  * It also supports a few special features specifically to simplify
- * libarchive test harnesses:
+ * test harnesses:
  *    failure(fmt, args) -- Stores a text string that gets
  *          printed if the following assertion fails, good for
  *          explaining subtle tests.
- *    assertA(a, cond) -- If the test fails, also prints out any error
- *          message stored in archive object 'a'.
  */
 static char msg[4096];
 
@@ -70,18 +100,19 @@
  * For each test source file, we remember how many times each
  * failure was reported.
  */
-static const char *failed_filename;
+static const char *failed_filename = NULL;
 static struct line {
 	int line;
 	int count;
 }  failed_lines[1000];
 
-
-/* Count this failure; return the number of previous failures. */
+/*
+ * Count this failure; return the number of previous failures.
+ */
 static int
 previous_failures(const char *filename, int line)
 {
-	int i;
+	unsigned int i;
 	int count;
 
 	if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
@@ -100,24 +131,30 @@
 			return (0);
 		}
 	}
+	return (0);
 }
 
-/* Inform user that we're skipping a test. */
-static const char *skipped_filename;
-static int skipped_line;
-void skipping_setup(const char *filename, int line)
+/*
+ * Copy arguments into file-local variables.
+ */
+static const char *test_filename;
+static int test_line;
+static void *test_extra;
+void test_setup(const char *filename, int line)
 {
-	skipped_filename = filename;
-	skipped_line = line;
+	test_filename = filename;
+	test_line = line;
 }
+
+/*
+ * Inform user that we're skipping a test.
+ */
 void
 test_skipping(const char *fmt, ...)
 {
-	int i;
-	int line = skipped_line;
 	va_list ap;
 
-	if (previous_failures(skipped_filename, skipped_line))
+	if (previous_failures(test_filename, test_line))
 		return;
 
 	va_start(ap, fmt);
@@ -130,29 +167,34 @@
 
 /* Common handling of failed tests. */
 static void
-test_failed(struct archive *a, int line)
+report_failure(void *extra)
 {
-	int i;
-
-	failures ++;
-
 	if (msg[0] != '\0') {
 		fprintf(stderr, "   Description: %s\n", msg);
 		msg[0] = '\0';
 	}
-	if (a != NULL) {
-		fprintf(stderr, "   archive error: %s\n", archive_error_string(a));
-	}
+
+#ifdef EXTRA_DUMP
+	if (extra != NULL)
+		fprintf(stderr, "   detail: %s\n", EXTRA_DUMP(extra));
+#else
+	(void)extra; /* UNUSED */
+#endif
 
 	if (dump_on_failure) {
-		fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n");
+		fprintf(stderr,
+		    " *** forcing core dump so failure can be debugged ***\n");
 		*(char *)(NULL) = 0;
 		exit(1);
 	}
 }
 
-/* Summarize repeated failures in the just-completed test file. */
-int
+/*
+ * Summarize repeated failures in the just-completed test file.
+ * The reports above suppress multiple failures from the same source
+ * line; this reports on any tests that did fail multiple times.
+ */
+static int
 summarize_comparator(const void *a0, const void *b0)
 {
 	const struct line *a = a0, *b = b0;
@@ -165,10 +207,10 @@
 	return (a->line - b->line);
 }
 
-void
-summarize(const char *filename)
+static void
+summarize(void)
 {
-	int i;
+	unsigned int i;
 
 	qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
 	    sizeof(failed_lines[0]), summarize_comparator);
@@ -195,101 +237,485 @@
 }
 
 /* Generic assert() just displays the failed condition. */
-void
-test_assert(const char *file, int line, int value, const char *condition, struct archive *a)
+int
+test_assert(const char *file, int line, int value, const char *condition, void *extra)
 {
+	++assertions;
 	if (value) {
 		msg[0] = '\0';
-		return;
+		return (value);
 	}
-	if (previous_failures(file, line))
-		return;
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (value);
 	fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
 	fprintf(stderr, "   Condition: %s\n", condition);
-	test_failed(a, line);
+	report_failure(extra);
+	return (value);
 }
 
 /* assertEqualInt() displays the values of the two integers. */
-void
+int
 test_assert_equal_int(const char *file, int line,
-    int v1, const char *e1, int v2, const char *e2, struct archive *a)
+    int v1, const char *e1, int v2, const char *e2, void *extra)
 {
+	++assertions;
 	if (v1 == v2) {
 		msg[0] = '\0';
-		return;
+		return (1);
 	}
-	if (previous_failures(file, line))
-		return;
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (0);
 	fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
 	    file, line);
 	fprintf(stderr, "      %s=%d\n", e1, v1);
 	fprintf(stderr, "      %s=%d\n", e2, v2);
-	test_failed(a, line);
+	report_failure(extra);
+	return (0);
+}
+
+static void strdump(const char *p)
+{
+	if (p == NULL) {
+		fprintf(stderr, "(null)");
+		return;
+	}
+	fprintf(stderr, "\"");
+	while (*p != '\0') {
+		unsigned int c = 0xff & *p++;
+		switch (c) {
+		case '\a': fprintf(stderr, "\a"); break;
+		case '\b': fprintf(stderr, "\b"); break;
+		case '\n': fprintf(stderr, "\n"); break;
+		case '\r': fprintf(stderr, "\r"); break;
+		default:
+			if (c >= 32 && c < 127)
+				fprintf(stderr, "%c", c);
+			else
+				fprintf(stderr, "\\x%02X", c);
+		}
+	}
+	fprintf(stderr, "\"");
 }
 
 /* assertEqualString() displays the values of the two strings. */
-void
+int
 test_assert_equal_string(const char *file, int line,
     const char *v1, const char *e1,
     const char *v2, const char *e2,
-    struct archive *a)
+    void *extra)
 {
+	++assertions;
 	if (v1 == NULL || v2 == NULL) {
 		if (v1 == v2) {
 			msg[0] = '\0';
-			return;
+			return (1);
 		}
 	} else if (strcmp(v1, v2) == 0) {
 		msg[0] = '\0';
-		return;
+		return (1);
 	}
-	if (previous_failures(file, line))
-		return;
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (0);
 	fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
 	    file, line);
-	fprintf(stderr, "      %s = \"%s\"\n", e1, v1);
-	fprintf(stderr, "      %s = \"%s\"\n", e2, v2);
-	test_failed(a, line);
+	fprintf(stderr, "      %s = ", e1);
+	strdump(v1);
+	fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1));
+	fprintf(stderr, "      %s = ", e2);
+	strdump(v2);
+	fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2));
+	report_failure(extra);
+	return (0);
+}
+
+static void wcsdump(const wchar_t *w)
+{
+	if (w == NULL) {
+		fprintf(stderr, "(null)");
+		return;
+	}
+	fprintf(stderr, "\"");
+	while (*w != L'\0') {
+		unsigned int c = *w++;
+		if (c >= 32 && c < 127)
+			fprintf(stderr, "%c", c);
+		else if (c < 256)
+			fprintf(stderr, "\\x%02X", c);
+		else if (c < 0x10000)
+			fprintf(stderr, "\\u%04X", c);
+		else
+			fprintf(stderr, "\\U%08X", c);
+	}
+	fprintf(stderr, "\"");
 }
 
 /* assertEqualWString() displays the values of the two strings. */
-void
+int
 test_assert_equal_wstring(const char *file, int line,
     const wchar_t *v1, const char *e1,
     const wchar_t *v2, const char *e2,
-    struct archive *a)
+    void *extra)
 {
-	if (wcscmp(v1, v2) == 0) {
+	++assertions;
+	if (v1 == NULL) {
+		if (v2 == NULL) {
+			msg[0] = '\0';
+			return (1);
+		}
+	} else if (v2 == NULL) {
+		if (v1 == NULL) {
+			msg[0] = '\0';
+			return (1);
+		}
+	} else if (wcscmp(v1, v2) == 0) {
 		msg[0] = '\0';
-		return;
+		return (1);
 	}
-	if (previous_failures(file, line))
-		return;
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (0);
 	fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
 	    file, line);
-	fwprintf(stderr, L"      %s = \"%ls\"\n", e1, v1);
-	fwprintf(stderr, L"      %s = \"%ls\"\n", e2, v2);
-	test_failed(a, line);
+	fprintf(stderr, "      %s = ", e1);
+	wcsdump(v1);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "      %s = ", e2);
+	wcsdump(v2);
+	fprintf(stderr, "\n");
+	report_failure(extra);
+	return (0);
+}
+
+/*
+ * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
+ * any bytes in p that differ from ref will be highlighted with '_'
+ * before and after the hex value.
+ */
+static void
+hexdump(const char *p, const char *ref, size_t l, size_t offset)
+{
+	size_t i, j;
+	char sep;
+
+	for(i=0; i < l; i+=16) {
+		fprintf(stderr, "%04x", i + offset);
+		sep = ' ';
+		for (j = 0; j < 16 && i + j < l; j++) {
+			if (ref != NULL && p[i + j] != ref[i + j])
+				sep = '_';
+			fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]);
+			if (ref != NULL && p[i + j] == ref[i + j])
+				sep = ' ';
+		}
+		for (; j < 16; j++) {
+			fprintf(stderr, "%c  ", sep);
+			sep = ' ';
+		}
+		fprintf(stderr, "%c", sep);
+		for (j=0; j < 16 && i + j < l; j++) {
+			int c = p[i + j];
+			if (c >= ' ' && c <= 126)
+				fprintf(stderr, "%c", c);
+			else
+				fprintf(stderr, ".");
+		}
+		fprintf(stderr, "\n");
+	}
+}
+
+/* assertEqualMem() displays the values of the two memory blocks. */
+/* TODO: For long blocks, hexdump the first bytes that actually differ. */
+int
+test_assert_equal_mem(const char *file, int line,
+    const char *v1, const char *e1,
+    const char *v2, const char *e2,
+    size_t l, const char *ld, void *extra)
+{
+	++assertions;
+	if (v1 == NULL || v2 == NULL) {
+		if (v1 == v2) {
+			msg[0] = '\0';
+			return (1);
+		}
+	} else if (memcmp(v1, v2, l) == 0) {
+		msg[0] = '\0';
+		return (1);
+	}
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (0);
+	fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n",
+	    file, line);
+	fprintf(stderr, "      size %s = %d\n", ld, (int)l);
+	fprintf(stderr, "      Dump of %s\n", e1);
+	hexdump(v1, v2, l < 32 ? l : 32, 0);
+	fprintf(stderr, "      Dump of %s\n", e2);
+	hexdump(v2, v1, l < 32 ? l : 32, 0);
+	fprintf(stderr, "\n");
+	report_failure(extra);
+	return (0);
+}
+
+int
+test_assert_empty_file(const char *f1fmt, ...)
+{
+	char buff[1024];
+	char f1[1024];
+	struct stat st;
+	va_list ap;
+	ssize_t s;
+	int fd;
+
+
+	va_start(ap, f1fmt);
+	vsprintf(f1, f1fmt, ap);
+	va_end(ap);
+
+	if (stat(f1, &st) != 0) {
+		fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1);
+		report_failure(NULL);
+		return (0);
+	}
+	if (st.st_size == 0)
+		return (1);
+
+	failures ++;
+	if (!verbose && previous_failures(test_filename, test_line))
+		return (0);
+
+	fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1);
+	fprintf(stderr, "    File size: %d\n", (int)st.st_size);
+	fprintf(stderr, "    Contents:\n");
+	fd = open(f1, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "    Unable to open %s\n", f1);
+	} else {
+		s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size;
+		s = read(fd, buff, s);
+		hexdump(buff, NULL, s, 0);
+	}
+	report_failure(NULL);
+	return (0);
+}
+
+/* assertEqualFile() asserts that two files have the same contents. */
+/* TODO: hexdump the first bytes that actually differ. */
+int
+test_assert_equal_file(const char *f1, const char *f2pattern, ...)
+{
+	char f2[1024];
+	va_list ap;
+	char buff1[1024];
+	char buff2[1024];
+	int fd1, fd2;
+	int n1, n2;
+
+	va_start(ap, f2pattern);
+	vsprintf(f2, f2pattern, ap);
+	va_end(ap);
+
+	fd1 = open(f1, O_RDONLY);
+	fd2 = open(f2, O_RDONLY);
+	for (;;) {
+		n1 = read(fd1, buff1, sizeof(buff1));
+		n2 = read(fd2, buff2, sizeof(buff2));
+		if (n1 != n2)
+			break;
+		if (n1 == 0 && n2 == 0)
+			return (1);
+		if (memcmp(buff1, buff2, n1) != 0)
+			break;
+	}
+	failures ++;
+	if (!verbose && previous_failures(test_filename, test_line))
+		return (0);
+	fprintf(stderr, "%s:%d: Files are not identical\n",
+	    test_filename, test_line);
+	fprintf(stderr, "  file1=\"%s\"\n", f1);
+	fprintf(stderr, "  file2=\"%s\"\n", f2);
+	report_failure(test_extra);
+	return (0);
+}
+
+int
+test_assert_file_exists(const char *fpattern, ...)
+{
+	char f[1024];
+	va_list ap;
+
+	va_start(ap, fpattern);
+	vsprintf(f, fpattern, ap);
+	va_end(ap);
+
+	if (!access(f, F_OK))
+		return (1);
+	if (!previous_failures(test_filename, test_line)) {
+		fprintf(stderr, "%s:%d: File doesn't exist\n",
+		    test_filename, test_line);
+		fprintf(stderr, "  file=\"%s\"\n", f);
+		report_failure(test_extra);
+	}
+	return (0);
+}
+
+int
+test_assert_file_not_exists(const char *fpattern, ...)
+{
+	char f[1024];
+	va_list ap;
+
+	va_start(ap, fpattern);
+	vsprintf(f, fpattern, ap);
+	va_end(ap);
+
+	if (access(f, F_OK))
+		return (1);
+	if (!previous_failures(test_filename, test_line)) {
+		fprintf(stderr, "%s:%d: File exists and shouldn't\n",
+		    test_filename, test_line);
+		fprintf(stderr, "  file=\"%s\"\n", f);
+		report_failure(test_extra);
+	}
+	return (0);
+}
+
+/* assertFileContents() asserts the contents of a file. */
+int
+test_assert_file_contents(const void *buff, int s, const char *fpattern, ...)
+{
+	char f[1024];
+	va_list ap;
+	char *contents;
+	int fd;
+	int n;
+
+	va_start(ap, fpattern);
+	vsprintf(f, fpattern, ap);
+	va_end(ap);
+
+	fd = open(f, O_RDONLY);
+	contents = malloc(s * 2);
+	n = read(fd, contents, s * 2);
+	if (n == s && memcmp(buff, contents, s) == 0) {
+		free(contents);
+		return (1);
+	}
+	failures ++;
+	if (!previous_failures(test_filename, test_line)) {
+		fprintf(stderr, "%s:%d: File contents don't match\n",
+		    test_filename, test_line);
+		fprintf(stderr, "  file=\"%s\"\n", f);
+		if (n > 0)
+			hexdump(contents, buff, n, 0);
+		else {
+			fprintf(stderr, "  File empty, contents should be:\n");
+			hexdump(buff, NULL, s, 0);
+		}
+		report_failure(test_extra);
+	}
+	free(contents);
+	return (0);
+}
+
+/*
+ * Call standard system() call, but build up the command line using
+ * sprintf() conventions.
+ */
+int
+systemf(const char *fmt, ...)
+{
+	char buff[8192];
+	va_list ap;
+	int r;
+
+	va_start(ap, fmt);
+	vsprintf(buff, fmt, ap);
+	r = system(buff);
+	va_end(ap);
+	return (r);
+}
+
+/*
+ * Slurp a file into memory for ease of comparison and testing.
+ * Returns size of file in 'sizep' if non-NULL, null-terminates
+ * data in memory for ease of use.
+ */
+char *
+slurpfile(size_t * sizep, const char *fmt, ...)
+{
+	char filename[8192];
+	struct stat st;
+	va_list ap;
+	char *p;
+	ssize_t bytes_read;
+	int fd;
+	int r;
+
+	va_start(ap, fmt);
+	vsprintf(filename, fmt, ap);
+	va_end(ap);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		/* Note: No error; non-existent file is okay here. */
+		return (NULL);
+	}
+	r = fstat(fd, &st);
+	if (r != 0) {
+		fprintf(stderr, "Can't stat file %s\n", filename);
+		close(fd);
+		return (NULL);
+	}
+	p = malloc(st.st_size + 1);
+	if (p == NULL) {
+		fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename);
+		close(fd);
+		return (NULL);
+	}
+	bytes_read = read(fd, p, st.st_size);
+	if (bytes_read < st.st_size) {
+		fprintf(stderr, "Can't read file %s\n", filename);
+		close(fd);
+		free(p);
+		return (NULL);
+	}
+	p[st.st_size] = '\0';
+	if (sizep != NULL)
+		*sizep = (size_t)st.st_size;
+	close(fd);
+	return (p);
 }
 
 /*
  * "list.h" is automatically generated; it just has a lot of lines like:
  * 	DEFINE_TEST(function_name)
- * The common "test.h" includes it to declare all of the test functions.
- * We reuse it here to define a list of all tests to run.
+ * It's used above to declare all of the test functions.
+ * We reuse it here to define a list of all tests (functions and names).
  */
 #undef DEFINE_TEST
-#define DEFINE_TEST(n) { n, #n },
+#define	DEFINE_TEST(n) { n, #n },
 struct { void (*func)(void); const char *name; } tests[] = {
 	#include "list.h"
 };
 
+/*
+ * Each test is run in a private work dir.  Those work dirs
+ * do have consistent and predictable names, in case a group
+ * of tests need to collaborate.  However, there is no provision
+ * for requiring that tests run in a certain order.
+ */
 static int test_run(int i, const char *tmpdir)
 {
 	int failures_before = failures;
 
-	if (!quiet_flag)
+	if (!quiet_flag) {
 		printf("%d: %s\n", i, tests[i].name);
+		fflush(stdout);
+	}
+
 	/*
 	 * Always explicitly chdir() in case the last test moved us to
 	 * a strange place.
@@ -307,69 +733,202 @@
 		    tests[i].name);
 		exit(1);
 	}
+	/* Chdir() to that work directory. */
 	if (chdir(tests[i].name)) {
 		fprintf(stderr,
 		    "ERROR: Couldn't chdir to temp dir ``%s''\n",
 		    tests[i].name);
 		exit(1);
 	}
+	/* Explicitly reset the locale before each test. */
+	setlocale(LC_ALL, "C");
+	/* Run the actual test. */
 	(*tests[i].func)();
-	summarize(tests[i].name);
+	/* Summarize the results of this test. */
+	summarize();
+	/* If there were no failures, we can remove the work dir. */
+	if (failures == failures_before) {
+		if (!keep_temp_files && chdir(tmpdir) == 0) {
+			systemf("rm -rf %s", tests[i].name);
+		}
+	}
+	/* Return appropriate status. */
 	return (failures == failures_before ? 0 : 1);
 }
 
-static void usage(void)
+static void usage(const char *program)
 {
 	static const int limit = sizeof(tests) / sizeof(tests[0]);
 	int i;
 
-	printf("Usage: libarchive_test [options] <test> <test> ...\n");
+	printf("Usage: %s [options] <test> <test> ...\n", program);
 	printf("Default is to run all tests.\n");
 	printf("Otherwise, specify the numbers of the tests you wish to run.\n");
 	printf("Options:\n");
-	printf("  -k  Keep running after failures.\n");
-	printf("      Default: Core dump after any failure.\n");
+	printf("  -d  Dump core after any failure, for debugging.\n");
+	printf("  -k  Keep all temp files.\n");
+	printf("      Default: temp files for successful tests deleted.\n");
+#ifdef PROGRAM
+	printf("  -p <path>  Path to executable to be tested.\n");
+	printf("      Default: path taken from " ENVBASE " environment variable.\n");
+#endif
 	printf("  -q  Quiet.\n");
+	printf("  -r <dir>   Path to dir containing reference files.\n");
+	printf("      Default: Current directory.\n");
+	printf("  -v  Verbose.\n");
 	printf("Available tests:\n");
 	for (i = 0; i < limit; i++)
 		printf("  %d: %s\n", i, tests[i].name);
 	exit(1);
 }
 
+#define	UUDECODE(c) (((c) - 0x20) & 0x3f)
+
+void
+extract_reference_file(const char *name)
+{
+	char buff[1024];
+	FILE *in, *out;
+
+	sprintf(buff, "%s/%s.uu", refdir, name);
+	in = fopen(buff, "r");
+	failure("Couldn't open reference file %s", buff);
+	assert(in != NULL);
+	if (in == NULL)
+		return;
+	/* Read up to and including the 'begin' line. */
+	for (;;) {
+		if (fgets(buff, sizeof(buff), in) == NULL) {
+			/* TODO: This is a failure. */
+			return;
+		}
+		if (memcmp(buff, "begin ", 6) == 0)
+			break;
+	}
+	/* Now, decode the rest and write it. */
+	/* Not a lot of error checking here; the input better be right. */
+	out = fopen(name, "w");
+	while (fgets(buff, sizeof(buff), in) != NULL) {
+		char *p = buff;
+		int bytes;
+
+		if (memcmp(buff, "end", 3) == 0)
+			break;
+
+		bytes = UUDECODE(*p++);
+		while (bytes > 0) {
+			int n = 0;
+			/* Write out 1-3 bytes from that. */
+			if (bytes > 0) {
+				n = UUDECODE(*p++) << 18;
+				n |= UUDECODE(*p++) << 12;
+				fputc(n >> 16, out);
+				--bytes;
+			}
+			if (bytes > 0) {
+				n |= UUDECODE(*p++) << 6;
+				fputc((n >> 8) & 0xFF, out);
+				--bytes;
+			}
+			if (bytes > 0) {
+				n |= UUDECODE(*p++);
+				fputc(n & 0xFF, out);
+				--bytes;
+			}
+		}
+	}
+	fclose(out);
+	fclose(in);
+}
+
+
 int main(int argc, char **argv)
 {
 	static const int limit = sizeof(tests) / sizeof(tests[0]);
 	int i, tests_run = 0, tests_failed = 0, opt;
 	time_t now;
+	char *refdir_alloc = NULL;
+	char *progname, *p;
 	char tmpdir[256];
+	char tmpdir_timestamp[256];
+
+	/*
+	 * Name of this program, used to build root of our temp directory
+	 * tree.
+	 */
+	progname = p = argv[0];
+	while (*p != '\0') {
+		if (*p == '/')
+			progname = p + 1;
+		++p;
+	}
 
-	while ((opt = getopt(argc, argv, "kq")) != -1) {
+#ifdef PROGRAM
+	/* Get the target program from environment, if available. */
+	testprog = getenv(ENVBASE);
+#endif
+
+	/* Allow -d to be controlled through the environment. */
+	if (getenv(ENVBASE "_DEBUG") != NULL)
+		dump_on_failure = 1;
+
+	/* Get the directory holding test files from environment. */
+	refdir = getenv(ENVBASE "_TEST_FILES");
+
+	/*
+	 * Parse options.
+	 */
+	while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) {
 		switch (opt) {
+		case 'd':
+			dump_on_failure = 1;
+			break;
 		case 'k':
-			dump_on_failure = 0;
+			keep_temp_files = 1;
+			break;
+		case 'p':
+#ifdef PROGRAM
+			testprog = optarg;
+#else
+			usage(progname);
+#endif
 			break;
 		case 'q':
-			quiet_flag = 1;
+			quiet_flag++;
+			break;
+		case 'r':
+			refdir = optarg;
+			break;
+		case 'v':
+			verbose = 1;
 			break;
 		case '?':
 		default:
-			usage();
+			usage(progname);
 		}
 	}
 	argc -= optind;
 	argv += optind;
 
 	/*
+	 * Sanity-check that our options make sense.
+	 */
+#ifdef PROGRAM
+	if (testprog == NULL)
+		usage(progname);
+#endif
+
+	/*
 	 * Create a temp directory for the following tests.
 	 * Include the time the tests started as part of the name,
 	 * to make it easier to track the results of multiple tests.
 	 */
 	now = time(NULL);
 	for (i = 0; i < 1000; i++) {
-		strftime(tmpdir, sizeof(tmpdir),
-		    "/tmp/libarchive_test.%Y-%m-%dT%H.%M.%S",
+		strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
+		    "%Y-%m-%dT%H.%M.%S",
 		    localtime(&now));
-		sprintf(tmpdir + strlen(tmpdir), "-%03d", i);
+		sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i);
 		if (mkdir(tmpdir,0755) == 0)
 			break;
 		if (errno == EEXIST)
@@ -379,11 +938,38 @@
 		exit(1);
 	}
 
+	/*
+	 * If the user didn't specify a directory for locating
+	 * reference files, use the current directory for that.
+	 */
+	if (refdir == NULL) {
+		systemf("/bin/pwd > %s/refdir", tmpdir);
+		refdir = refdir_alloc = slurpfile(NULL, "%s/refdir", tmpdir);
+		p = refdir + strlen(refdir);
+		while (p[-1] == '\n') {
+			--p;
+			*p = '\0';
+		}
+		systemf("rm %s/refdir", tmpdir);
+	}
+
+	/*
+	 * Banner with basic information.
+	 */
 	if (!quiet_flag) {
-		printf("Running libarchive tests in: %s\n", tmpdir);
-		printf("Exercising %s\n", archive_version());
+		printf("Running tests in: %s\n", tmpdir);
+		printf("Reference files will be read from: %s\n", refdir);
+#ifdef PROGRAM
+		printf("Running tests on: %s\n", testprog);
+#endif
+		printf("Exercising: ");
+		fflush(stdout);
+		printf("%s\n", EXTRA_VERSION);
 	}
 
+	/*
+	 * Run some or all of the individual tests.
+	 */
 	if (argc == 0) {
 		/* Default: Run all tests. */
 		for (i = 0; i < limit; i++) {
@@ -396,7 +982,7 @@
 			i = atoi(*argv);
 			if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
 				printf("*** INVALID Test %s\n", *argv);
-				usage();
+				usage(progname);
 			} else {
 				if (test_run(i, tmpdir))
 					tests_failed++;
@@ -405,10 +991,24 @@
 			argv++;
 		}
 	}
-	printf("\n");
-	printf("%d of %d test groups reported failures\n",
-	    tests_failed, tests_run);
-	printf(" Total of %d individual tests failed.\n", failures);
-	printf(" Total of %d individual tests were skipped.\n", skips);
+
+	/*
+	 * Report summary statistics.
+	 */
+	if (!quiet_flag) {
+		printf("\n");
+		printf("%d of %d tests reported failures\n",
+		    tests_failed, tests_run);
+		printf(" Total of %d assertions checked.\n", assertions);
+		printf(" Total of %d assertions failed.\n", failures);
+		printf(" Total of %d assertions skipped.\n", skips);
+	}
+
+	free(refdir_alloc);
+
+	/* If the final tmpdir is empty, we can remove it. */
+	/* This should be the usual case when all tests succeed. */
+	rmdir(tmpdir);
+
 	return (tests_failed);
 }
Index: test_write_disk_perms.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_write_disk_perms.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_write_disk_perms.c -L lib/libarchive/test/test_write_disk_perms.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_write_disk_perms.c
+++ lib/libarchive/test/test_write_disk_perms.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_perms.c,v 1.7 2007/08/12 17:35:05 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_perms.c,v 1.7.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 #if ARCHIVE_VERSION_STAMP >= 1009000
 
@@ -186,7 +186,7 @@
 	/* Check original perms. */
 	assert(0 == stat("dir_overwrite_0744", &st));
 	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
-	assert((st.st_mode & 07777) == 0744);
+	assert((st.st_mode & 0777) == 0744);
 	/* Overwrite shouldn't edit perms. */
 	assert((ae = archive_entry_new()) != NULL);
 	archive_entry_copy_pathname(ae, "dir_overwrite_0744");
@@ -197,7 +197,7 @@
 	/* Make sure they're unchanged. */
 	assert(0 == stat("dir_overwrite_0744", &st));
 	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
-	assert((st.st_mode & 07777) == 0744);
+	assert((st.st_mode & 0777) == 0744);
 
 	/* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */
 	assert((ae = archive_entry_new()) != NULL);
@@ -244,16 +244,18 @@
 	failure("Opportunistic SUID failure shouldn't return error.");
 	assertEqualInt(0, archive_write_finish_entry(a));
 
-	assert(archive_entry_clear(ae) != NULL);
-	archive_entry_copy_pathname(ae, "file_bad_suid2");
-	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
-	archive_entry_set_uid(ae, getuid() + 1);
-	archive_write_disk_set_options(a,
-	    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
-	assertA(0 == archive_write_header(a, ae));
-	/* Owner change should fail here. */
-	failure("Non-opportunistic SUID failure should return error.");
-	assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a));
+        if (getuid() != 0) {
+		assert(archive_entry_clear(ae) != NULL);
+		archive_entry_copy_pathname(ae, "file_bad_suid2");
+		archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
+		archive_entry_set_uid(ae, getuid() + 1);
+		archive_write_disk_set_options(a,
+		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
+		assertA(0 == archive_write_header(a, ae));
+		/* Owner change should fail here. */
+		failure("Non-opportunistic SUID failure should return error.");
+		assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a));
+	}
 
 	/* Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit */
 	assert(archive_entry_clear(ae) != NULL);
@@ -383,7 +385,7 @@
 
 	assert(0 == stat("dir_overwrite_0744", &st));
 	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
-	assert((st.st_mode & 07777) == 0744);
+	assert((st.st_mode & 0777) == 0744);
 
 	assert(0 == stat("file_no_suid", &st));
 	failure("file_0755: st.st_mode=%o", st.st_mode);
@@ -403,10 +405,13 @@
 	failure("file_bad_suid: st.st_mode=%o", st.st_mode);
 	assert((st.st_mode & 07777) == (0742));
 
-	/* SUID bit should NOT have been set here. */
-	assert(0 == stat("file_bad_suid2", &st));
-	failure("file_bad_suid2: st.st_mode=%o", st.st_mode);
-	assert((st.st_mode & 07777) == (0742));
+	/* Some things don't fail if you're root, so suppress this. */
+	if (getuid() != 0) {
+		/* SUID bit should NOT have been set here. */
+		assert(0 == stat("file_bad_suid2", &st));
+		failure("file_bad_suid2: st.st_mode=%o", st.st_mode);
+		assert((st.st_mode & 07777) == (0742));
+	}
 
 	/* SGID should be set here. */
 	assert(0 == stat("file_perm_sgid", &st));
Index: test_read_format_isorr_bz2.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_format_isorr_bz2.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_format_isorr_bz2.c -L lib/libarchive/test/test_read_format_isorr_bz2.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_format_isorr_bz2.c
+++ lib/libarchive/test/test_read_format_isorr_bz2.c
@@ -23,11 +23,11 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_isorr_bz2.c,v 1.2 2007/05/29 01:00:21 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_isorr_bz2.c,v 1.2.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 /*
 Execute the following to rebuild the data for this program:
-   tail -n +5 test-read_format-isorr_bz2.c | /bin/sh
+   tail -n +32 test_read_format_isorr_bz2.c | /bin/sh
 
 rm -rf /tmp/iso
 mkdir /tmp/iso
@@ -35,143 +35,104 @@
 echo "hello" >/tmp/iso/file
 ln /tmp/iso/file /tmp/iso/hardlink
 (cd /tmp/iso; ln -s file symlink)
-TZ=utc touch -afhm -t 197001010000.01 /tmp/iso /tmp/iso/file /tmp/iso/dir
-TZ=utc touch -afhm -t 196912312359.58 /tmp/iso/symlink
-mkhybrid -R -uid 1 -gid 2 /tmp/iso | bzip2 > data.iso.bz2
-cat data.iso.bz2 | ./maketest.pl > data.c
+TZ=utc touch -afhm -t 197001020000.01 /tmp/iso /tmp/iso/file /tmp/iso/dir
+TZ=utc touch -afhm -t 197001030000.02 /tmp/iso/symlink
+mkhybrid -R -uid 1 -gid 2 /tmp/iso | bzip2 > test_read_format_isorr_bz2.iso.bz2
+F=test_read_format_isorr_bz2.iso.bz2
+uuencode $F $F > $F.uu
 exit 1
  */
 
-static unsigned char archive[] = {
-'B','Z','h','9','1','A','Y','&','S','Y','G',11,4,'c',0,0,199,255,221,255,
-255,203,252,221,'c',251,248,'?',255,223,224,167,255,222,'&','!',234,'$',0,
-'0',1,' ',0,'D',2,129,8,192,3,14,'2','3','$',19,184,'J',' ','F',168,244,201,
-149,'6','Q',226,155,'S',212,209,160,'h','4','i',160,26,13,0,244,134,212,0,
-218,'O',212,153,1,144,244,128,148,' ',147,13,' ',213,'=','1','\'',169,166,
-128,'=','!',233,0,208,0,26,0,0,30,160,'h',0,'4','z',130,180,163,'@',0,0,4,
-211,0,0,0,2,'b','`',0,0,0,0,0,8,146,133,'F',154,'y','A',163,'A',161,163,'@',
-'z',134,'C','C','F',131,'F','@',0,0,0,0,6,154,26,'Q',24,234,180,'P',172,251,
-'=',2,'P','H','&','Y','o',130,28,'"',229,210,247,227,248,200,'?','6',161,
-'?',170,'H',172,'"','H','I',16,'2','"','&',148,'G',133,'T','z',224,1,215,
-' ',0,191,184,10,160,24,248,180,183,244,156,'K',202,133,208,'U',5,'6','C',
-26,144,'H',168,'H','H','(','"',151,'@','m',223,'(','P',169,'e',145,148,'6',
-237,235,7,227,204,']','k','{',241,187,227,244,251,':','a','L',138,'#','R',
-'"',221,'_',239,')',140,'*','*',172,'Q',16,1,16,207,166,251,233,'Z',169,'4',
-'_',195,'a',14,18,231,'}',14,139,137,'e',213,185,'T',194,'D','`',25,'$',187,
-208,'%','c',162,'~',181,'@',204,'2',238,'P',161,213,127,'I',169,3,' ','o',
-6,161,16,128,'F',214,'S','m',6,244,11,229,'Z','y','.',176,'q',' ',248,167,
-204,26,193,'q',211,241,214,133,221,212,'I','`',28,244,'N','N','f','H','9',
-'w',245,209,'*',20,26,208,'h','(',194,156,192,'l',';',192,'X','T',151,177,
-209,'0',156,16,'=',20,'k',184,144,'z',26,'j',133,194,'9',227,'<','[','^',
-17,'w','p',225,220,248,'>',205,'>','[',19,'5',155,17,175,28,28,168,175,'n',
-'\'','c','w',27,222,204,'k','n','x','I',23,237,'c',145,11,184,'A','(',1,169,
-'0',180,189,134,'\\','Y','x',187,'C',151,'d','k','y','-','L',218,138,'s',
-'*','(',12,'h',242,'*',17,'E','L',202,146,138,'l','0',217,160,'9','.','S',
-214,198,143,'3','&',237,'=','t','P',168,214,210,'`','p','J',181,'H',138,149,
-'1','B',206,22,164,'[','O','A',172,134,224,179,219,166,184,'X',185,'W',154,
-219,19,161,'Y',184,220,237,147,'9',191,237,'&','i','_',226,146,205,160,'@',
-'b',182,';',3,'!',183,'J','t',161,160,178,173,'S',235,':','2',159,':',245,
-'{','U',174,'P',142,'G','(',')',9,168,185,'A','U',231,193,'g',213,'e',12,
-'X',223,22,249,')',152,237,'G',150,156,3,201,245,212,'2',218,209,177,196,
-235,'_','~',137,24,31,196,232,'B',172,'w',159,24,'n',156,150,225,'1','y',
-22,'#',138,193,227,232,169,170,166,179,1,11,182,'i',')',160,180,198,175,128,
-249,167,5,194,142,183,'f',134,206,180,'&','E','!','[',31,195,':',192,'s',
-232,187,'N',131,'Y',137,243,15,'y',12,'J',163,'-',242,'5',197,151,130,163,
-240,220,'T',161,'L',159,141,159,152,'4',18,128,'.','^',250,168,200,163,'P',
-231,'Y','w','F','U',186,'x',190,16,'0',228,22,'9','F','t',168,157,'i',190,
-'+',246,141,142,18,' ','M',174,197,'O',165,'m',224,27,'b',150,'|','W','H',
-196,'.','*','Q','$',225,'I','-',148,169,'F',7,197,'m','-',130,153,0,158,21,
-'(',221,221,226,206,'g',13,159,163,'y',176,'~',158,'k','4','q','d','s',177,
-'7',14,217,'1',173,206,228,'t',250,200,170,162,'d','2','Z','$','e',168,224,
-223,129,174,229,165,187,252,203,'-',28,'`',207,183,'-','/',127,196,230,131,
-'B',30,237,' ',8,26,194,'O',132,'L','K','\\',144,'L','c',1,10,176,192,'c',
-0,244,2,168,3,0,'+',233,186,16,17,'P',17,129,252,'2',0,2,154,247,255,166,
-'.',228,138,'p',161,' ',142,22,8,198};
-
 DEFINE_TEST(test_read_format_isorr_bz2)
 {
+	const char *refname = "test_read_format_isorr_bz2.iso.bz2";
 	struct archive_entry *ae;
 	struct archive *a;
 	const void *p;
 	size_t size;
 	off_t offset;
+
+	extract_reference_file(refname);
 	assert((a = archive_read_new()) != NULL);
-	assert(0 == archive_read_support_compression_all(a));
-	assert(0 == archive_read_support_format_all(a));
-	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertEqualInt(0, archive_read_support_compression_all(a));
+	assertEqualInt(0, archive_read_support_format_all(a));
+	assertEqualInt(0, archive_read_open_filename(a, refname, 10240));
 
 	/* First entry is '.' root directory. */
-	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(0, archive_read_next_header(a, &ae));
 	assertEqualString(".", archive_entry_pathname(ae));
 	assert(S_ISDIR(archive_entry_stat(ae)->st_mode));
 	assertEqualInt(2048, archive_entry_size(ae));
-	assertEqualInt(1, archive_entry_mtime(ae));
+	assertEqualInt(86401, archive_entry_mtime(ae));
 	assertEqualInt(0, archive_entry_mtime_nsec(ae));
-	assertEqualInt(1, archive_entry_ctime(ae));
+	assertEqualInt(86401, archive_entry_ctime(ae));
 	assertEqualInt(0, archive_entry_stat(ae)->st_nlink);
 	assertEqualInt(0, archive_entry_uid(ae));
+	assertEqualIntA(a, ARCHIVE_EOF,
+	    archive_read_data_block(a, &p, &size, &offset));
+	assertEqualInt(size, 0);
 
 	/* A directory. */
-	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(0, archive_read_next_header(a, &ae));
 	assertEqualString("dir", archive_entry_pathname(ae));
 	assert(S_ISDIR(archive_entry_stat(ae)->st_mode));
-	assert(2048 == archive_entry_size(ae));
-	assert(1 == archive_entry_mtime(ae));
-	assert(1 == archive_entry_atime(ae));
-	assert(2 == archive_entry_stat(ae)->st_nlink);
-	assert(1 == archive_entry_uid(ae));
-	assert(2 == archive_entry_gid(ae));
+	assertEqualInt(2048, archive_entry_size(ae));
+	assertEqualInt(86401, archive_entry_mtime(ae));
+	assertEqualInt(86401, archive_entry_atime(ae));
+	assertEqualInt(2, archive_entry_stat(ae)->st_nlink);
+	assertEqualInt(1, archive_entry_uid(ae));
+	assertEqualInt(2, archive_entry_gid(ae));
 
 	/* A regular file. */
-	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(0, archive_read_next_header(a, &ae));
 	assertEqualString("file", archive_entry_pathname(ae));
 	assert(S_ISREG(archive_entry_stat(ae)->st_mode));
-	assert(6 == archive_entry_size(ae));
-	assert(0 == archive_read_data_block(a, &p, &size, &offset));
-	assert(6 == size);
-	assert(0 == offset);
-	assert(0 == memcmp(p, "hello\n", 6));
-	assert(1 == archive_entry_mtime(ae));
-	assert(1 == archive_entry_atime(ae));
-	assert(2 == archive_entry_stat(ae)->st_nlink);
-	assert(1 == archive_entry_uid(ae));
-	assert(2 == archive_entry_gid(ae));
+	assertEqualInt(6, archive_entry_size(ae));
+	assertEqualInt(0, archive_read_data_block(a, &p, &size, &offset));
+	assertEqualInt(6, size);
+	assertEqualInt(0, offset);
+	assertEqualInt(0, memcmp(p, "hello\n", 6));
+	assertEqualInt(86401, archive_entry_mtime(ae));
+	assertEqualInt(86401, archive_entry_atime(ae));
+	assertEqualInt(2, archive_entry_stat(ae)->st_nlink);
+	assertEqualInt(1, archive_entry_uid(ae));
+	assertEqualInt(2, archive_entry_gid(ae));
 
 	/* A hardlink to the regular file. */
-	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(0, archive_read_next_header(a, &ae));
 	assertEqualString("hardlink", archive_entry_pathname(ae));
 	assert(S_ISREG(archive_entry_stat(ae)->st_mode));
 	assertEqualString("file", archive_entry_hardlink(ae));
-	assert(6 == archive_entry_size(ae));
-	assert(1 == archive_entry_mtime(ae));
-	assert(1 == archive_entry_atime(ae));
-	assert(2 == archive_entry_stat(ae)->st_nlink);
-	assert(1 == archive_entry_uid(ae));
-	assert(2 == archive_entry_gid(ae));
+	assertEqualInt(6, archive_entry_size(ae));
+	assertEqualInt(86401, archive_entry_mtime(ae));
+	assertEqualInt(86401, archive_entry_atime(ae));
+	assertEqualInt(2, archive_entry_stat(ae)->st_nlink);
+	assertEqualInt(1, archive_entry_uid(ae));
+	assertEqualInt(2, archive_entry_gid(ae));
 
 	/* A symlink to the regular file. */
-	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(0, archive_read_next_header(a, &ae));
 	assertEqualString("symlink", archive_entry_pathname(ae));
 	assert(S_ISLNK(archive_entry_stat(ae)->st_mode));
 	assertEqualString("file", archive_entry_symlink(ae));
-	assert(0 == archive_entry_size(ae));
-	assert(-2 == archive_entry_mtime(ae));
-	assert(-2 == archive_entry_atime(ae));
-	assert(1 == archive_entry_stat(ae)->st_nlink);
-	assert(1 == archive_entry_uid(ae));
-	assert(2 == archive_entry_gid(ae));
+	assertEqualInt(0, archive_entry_size(ae));
+	assertEqualInt(172802, archive_entry_mtime(ae));
+	assertEqualInt(172802, archive_entry_atime(ae));
+	assertEqualInt(1, archive_entry_stat(ae)->st_nlink);
+	assertEqualInt(1, archive_entry_uid(ae));
+	assertEqualInt(2, archive_entry_gid(ae));
 
 	/* End of archive. */
-	assert(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+	assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae));
 
 	/* Verify archive format. */
-	assert(archive_compression(a) == ARCHIVE_COMPRESSION_BZIP2);
-	assert(archive_format(a) == ARCHIVE_FORMAT_ISO9660_ROCKRIDGE);
+	assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_BZIP2);
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660_ROCKRIDGE);
 
 	/* Close the archive. */
-	assert(0 == archive_read_close(a));
+	assertEqualInt(0, archive_read_close(a));
 #if ARCHIVE_API_VERSION > 1
-	assert(0 == archive_read_finish(a));
+	assertEqualInt(0, archive_read_finish(a));
 #else
 	archive_read_finish(a);
 #endif
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse_1_13.tgz.uu
@@ -0,0 +1,27 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse_1_13.tgz.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_read_format_gtar_sparse_1_13.tgz
+M'XL(`&&";$<``^W72VX;1Q2%X<Y.N($`=>NYD*Q``P\\L&.(\OYSNP/)LGE`
+M!SDF.D#^SP,G94&\7?VS']<O3\_7#]M#E2AE]KZ54F*-\O[O7<W_W:*LUJ)$
+M]%R/M>;:+G\\=JR_?;V^/#U?+MO+QT]W?^YG__ZO[5O09L\]>MN1M__.7:IB
+M/=HZO*[O2W<_('?U\.NG?_KUOQ+_(_0#G.=ZW/_K0S_C[OT_>FW?W?]C*[5&
+M:]S_[]S_6V]J?=72?US_K92Q8L1JZB%A?_YJI0_QV^I<98KU5D=?:CT/0AU%
+MKW6HH^@S0AU%CCINCF)?'S/4G#/Z4'/.T4+-N2)F4>NYV6+./=XIYHS<M2KF
+MS#UH4\R9:=<JYHS\S"GFC-I6$W-&77D^Q?H>A9JSK;INJW@[_]%;-#5OG_D8
+M+M9'G4W-.XZCOEV?M:FZ8\ZJZCXB5ON:AUW$G#4_N8LY:\F*Q)PUH at XQ9\WO
+MB.HWFUBJWUHS8+'?Q]=)S9FG)]2</0]`S9F;4-6<?<VIYAQMJ'[K6.VVWV_G
+M/\]F51T?7W\U[]K_J/5Y',:/ZRTG;F+>5F;^(K$>^]5?K8]UV_%^.8JI^CTN
+M4V+.O,VTKN;,[2MJSHRWJSE[7 at 75G"/#4W/NJVK.F1LD]OGU<GJ[OM90<^9I
+M#C5G[MJX<_[[OB-BWOSRA.JX9_2JXYY?:-7Q<?D7\_8,;XIY>^98Q;[VW*BI
+MYNQYQ5!SYK=0]=M'7HC5G&-4U>]QFU)SSLQ+S9E?FZ;F7!F,F'/LZ8DY1^ZH
+MZG<_]ZK?D1=ZU>]Q.[US_D=>F&Z?&O;U5=5SP=B?T]2\&:9Z+AAYHE7'>='N
+MJN,\#4UU?-S^U9QSOP.H]?V/6#\>AMZM%_E at A'^$]W<XZ`<.^H&#?N"@'SCH
+M!PZW'_J#@W[@H!\XZ`<.^H&#?N"@'SAX_\>9Z`<.^H&#?N"@'SCH!P[Z at 8/W
+M?YR)?N"@'SCH!P[Z at 8-^X*`?.'C_QYGH!P[Z at 8-^X*`?..@'#OJ!@_=_G(E^
+MX*`?..@'#OJ!@W[@H!\X>/_'F>@'#OJ!@W[@H!\XZ`<.^H&#]W^<B7[@H!\X
+MZ`<.^H&#?N"@'SAX_\>9Z`<.^H&#?N"@'SCH!P[Z at 8/W?YR)?N"@'SCH!P[Z
+M at 8-^X*`?.'C_QYGH!P[Z at 8-^X*`?..@'#OJ!@_=_G(E^X*`?..@'#OJ!@W[@
+MH!\X>/_'F>@'#OJ!@WX\G__\_/OUR]/S]</C/J-$*;/WK902:Y3W?[_:HJS6
+MHD3TR)^/&F6[E,>-],W7Z\O3\^6RO7S\=/?G?O;O`````````/`?\Q>.)E`.
+$`/``````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_tar_large.c
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_tar_large.c,v 1.1.2.3 2008/08/25 01:55:40 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This is a somewhat tricky test that verifies the ability to
+ * write and read very large entries to tar archives.  It
+ * writes entries from 2GB up to 1TB to an archive in memory.
+ * The memory storage here carefully avoids actually storing
+ * any part of the file bodies, so it runs very quickly and requires
+ * very little memory.  If you're willing to wait a few minutes,
+ * you should be able to exercise petabyte entries with this code.
+ */
+
+/*
+ * Each file is built up by duplicating the following block.
+ */
+static size_t filedatasize;
+static void *filedata;
+
+/*
+ * We store the archive as blocks of data generated by libarchive,
+ * each possibly followed by bytes of file data.
+ */
+struct memblock {
+	struct memblock *next;
+	size_t	size;
+	void *buff;
+	off_t filebytes;
+};
+
+/*
+ * The total memory store is just a list of memblocks plus
+ * some accounting overhead.
+ */
+struct memdata {
+	off_t filebytes;
+	void *buff;
+	struct memblock *first;
+	struct memblock *last;
+};
+
+/* The following size definitions simplify things below. */
+#define KB ((off_t)1024)
+#define MB ((off_t)1024 * KB)
+#define GB ((off_t)1024 * MB)
+#define TB ((off_t)1024 * GB)
+
+#if ARCHIVE_API_VERSION < 2
+static ssize_t	memory_read_skip(struct archive *, void *, size_t request);
+#else
+static off_t	memory_read_skip(struct archive *, void *, off_t request);
+#endif
+static ssize_t	memory_read(struct archive *, void *, const void **buff);
+static ssize_t	memory_write(struct archive *, void *, const void *, size_t);
+
+
+static ssize_t
+memory_write(struct archive *a, void *_private, const void *buff, size_t size)
+{
+	struct memdata *private = _private;
+	struct memblock *block;
+
+	(void)a;
+
+	/*
+	 * Since libarchive tries to behave in a zero-copy manner, if
+	 * you give a pointer to filedata to the library, a pointer
+	 * into that data will (usually) pop out here.  This way, we
+	 * can tell the difference between filedata and library header
+	 * and metadata.
+	 */
+	if ((const char *)filedata <= (const char *)buff
+	    && (const char *)buff < (const char *)filedata + filedatasize) {
+		/* We don't need to store a block of file data. */
+		private->last->filebytes += size;
+	} else {
+		/* Yes, we're assuming the very first write is metadata. */
+		/* It's header or metadata, copy and save it. */
+		block = (struct memblock *)malloc(sizeof(*block));
+		memset(block, 0, sizeof(*block));
+		block->size = size;
+		block->buff = malloc(size);
+		memcpy(block->buff, buff, size);
+		if (private->last == NULL) {
+			private->first = private->last = block;
+		} else {
+			private->last->next = block;
+			private->last = block;
+		}
+		block->next = NULL;
+	}
+	return (size);
+}
+
+static ssize_t
+memory_read(struct archive *a, void *_private, const void **buff)
+{
+	struct memdata *private = _private;
+	struct memblock *block;
+	ssize_t size;
+
+	(void)a;
+
+	free(private->buff);
+	private->buff = NULL;
+	if (private->first == NULL) {
+		private->last = NULL;
+		return (ARCHIVE_EOF);
+	}
+	if (private->filebytes > 0) {
+		/*
+		 * We're returning file bytes, simulate it by
+		 * passing blocks from the template data.
+		 */
+		if (private->filebytes > (off_t)filedatasize)
+			size = filedatasize;
+		else
+			size = (ssize_t)private->filebytes;
+		private->filebytes -= size;
+		*buff = filedata;
+	} else {
+		/*
+		 * We need to get some real data to return.
+		 */
+		block = private->first;
+		private->first = block->next;
+		size = block->size;
+		if (block->buff != NULL) {
+			private->buff = block->buff;
+			*buff = block->buff;
+		} else {
+			private->buff = NULL;
+			*buff = filedata;
+		}
+		private->filebytes = block->filebytes;
+		free(block);
+	}
+	return (size);
+}
+
+
+#if ARCHIVE_API_VERSION < 2
+static ssize_t
+memory_read_skip(struct archive *a, void *private, size_t skip)
+{
+	(void)a;  /* UNUSED */
+	(void)private; /* UNUSED */
+	(void)skip; /* UNUSED */
+	return (0);
+}
+#else
+static off_t
+memory_read_skip(struct archive *a, void *_private, off_t skip)
+{
+	struct memdata *private = _private;
+
+	(void)a;
+
+	if (private->first == NULL) {
+		private->last = NULL;
+		return (0);
+	}
+	if (private->filebytes > 0) {
+		if (private->filebytes < skip)
+			skip = private->filebytes;
+		private->filebytes -= skip;
+	} else {
+		skip = 0;
+	}
+	return (skip);
+}
+#endif
+
+DEFINE_TEST(test_tar_large)
+{
+	/* The sizes of the entries we're going to generate. */
+	static off_t tests[] = {
+		/* Test for 32-bit signed overflow. */
+		2 * GB - 1, 2 * GB, 2 * GB + 1,
+		/* Test for 32-bit unsigned overflow. */
+		4 * GB - 1, 4 * GB, 4 * GB + 1,
+		/* 8GB is the "official" max for ustar. */
+		8 * GB - 1, 8 * GB, 8 * GB + 1,
+		/* Bend ustar a tad and you can get 64GB (12 octal digits). */
+		64 * GB - 1, 64 * GB,
+		/* And larger entries that require non-ustar extensions. */
+		256 * GB, 1 * TB, 0 };
+	int i;
+	char namebuff[64];
+	struct memdata memdata;
+	struct archive_entry *ae;
+	struct archive *a;
+	off_t  filesize, writesize;
+
+	filedatasize = 1 * MB;
+	filedata = malloc(filedatasize);
+	memset(filedata, 0xAA, filedatasize);
+	memset(&memdata, 0, sizeof(memdata));
+
+	/*
+	 * Open an archive for writing.
+	 */
+	a = archive_write_new();
+	archive_write_set_format_pax_restricted(a);
+	archive_write_set_bytes_per_block(a, 0); /* No buffering. */
+	archive_write_open(a, &memdata, NULL, memory_write, NULL);
+
+	/*
+	 * Write a series of large files to it.
+	 */
+	for (i = 0; tests[i] > 0; i++) {
+		assert((ae = archive_entry_new()) != NULL);
+		sprintf(namebuff, "file_%d", i);
+		archive_entry_copy_pathname(ae, namebuff);
+		archive_entry_set_mode(ae, S_IFREG | 0755);
+		filesize = tests[i];
+
+		if (filesize < 0) {
+			skipping("32-bit off_t doesn't permit testing of very large files.");
+			return;
+		}
+		archive_entry_set_size(ae, filesize);
+
+		assertA(0 == archive_write_header(a, ae));
+		archive_entry_free(ae);
+
+		/*
+		 * Write the actual data to the archive.
+		 */
+		while (filesize > 0) {
+			writesize = filedatasize;
+			if (writesize > filesize)
+				writesize = filesize;
+			assertA(writesize == archive_write_data(a, filedata, writesize));
+			filesize -= writesize;
+		}
+	}
+
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "lastfile");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Open the same archive for reading.
+	 */
+	a = archive_read_new();
+	archive_read_support_format_tar(a);
+	archive_read_open2(a, &memdata, NULL,
+	    memory_read, memory_read_skip, NULL);
+
+	/*
+	 * Read entries back.
+	 */
+	for (i = 0; tests[i] > 0; i++) {
+		assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+		sprintf(namebuff, "file_%d", i);
+		assertEqualString(namebuff, archive_entry_pathname(ae));
+		assert(tests[i] == archive_entry_size(ae));
+	}
+	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+	assertEqualString("lastfile", archive_entry_pathname(ae));
+
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+	/* Close out the archive. */
+	assertA(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	free(memdata.buff);
+	free(filedata);
+}
Index: Makefile
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/Makefile,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/Makefile -L lib/libarchive/test/Makefile -u -r1.1 -r1.2
--- lib/libarchive/test/Makefile
+++ lib/libarchive/test/Makefile
@@ -1,11 +1,26 @@
-# $FreeBSD: src/lib/libarchive/test/Makefile,v 1.11 2007/07/13 15:14:35 kientzle Exp $
+# $FreeBSD: src/lib/libarchive/test/Makefile,v 1.11.2.7 2009/02/09 03:31:53 kientzle Exp $
+
+# Where to find the libarchive sources
+LA_SRCDIR=${.CURDIR}/..
+.PATH: ${LA_SRCDIR}
+
+# Get a list of all libarchive source files
+LA_SRCS!=make -f ${LA_SRCDIR}/Makefile -V SRCS
 
 TESTS= \
 	test_acl_basic.c			\
+	test_acl_freebsd.c			\
 	test_acl_pax.c				\
 	test_archive_api_feature.c		\
 	test_bad_fd.c				\
+	test_compat_gtar.c			\
+	test_compat_tar_hardlink.c		\
+	test_compat_zip.c			\
+	test_empty_write.c			\
 	test_entry.c				\
+	test_entry_strmode.c			\
+	test_link_resolver.c			\
+	test_pax_filename_encoding.c		\
 	test_read_compress_program.c		\
 	test_read_data_large.c			\
 	test_read_extract.c			\
@@ -22,8 +37,10 @@
 	test_read_format_gtar_sparse.c		\
 	test_read_format_iso_gz.c		\
 	test_read_format_isorr_bz2.c		\
+	test_read_format_mtree.c		\
 	test_read_format_pax_bz2.c		\
 	test_read_format_tar.c			\
+	test_read_format_tar_empty_filename.c	\
 	test_read_format_tbz.c			\
 	test_read_format_tgz.c			\
 	test_read_format_tz.c			\
@@ -33,50 +50,60 @@
 	test_read_position.c			\
 	test_read_truncated.c			\
 	test_tar_filenames.c			\
+	test_tar_large.c			\
+	test_ustar_filenames.c			\
+	test_write_compress.c			\
 	test_write_compress_program.c		\
 	test_write_disk.c			\
+	test_write_disk_hardlink.c		\
 	test_write_disk_perms.c			\
 	test_write_disk_secure.c		\
+	test_write_disk_sparse.c		\
 	test_write_format_ar.c			\
 	test_write_format_cpio.c		\
 	test_write_format_cpio_empty.c		\
+	test_write_format_cpio_newc.c		\
+	test_write_format_cpio_odc.c		\
 	test_write_format_shar_empty.c		\
 	test_write_format_tar.c			\
 	test_write_format_tar_empty.c		\
+	test_write_format_tar_ustar.c		\
 	test_write_open_memory.c
 
-SRCS= ${TESTS}					\
+
+# Build the test program using all libarchive sources + the test sources.
+SRCS= ${LA_SRCS}				\
+	${TESTS}				\
 	list.h					\
 	main.c					\
 	read_open_memory.c
 
-CLEANFILES+= list.h
-
 NO_MAN=yes
 
 PROG=libarchive_test
-DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
-LDADD= -larchive -lz -lbz2
+INTERNALPROG=yes  # Don't install this; it's just for testing
+DPADD=${LIBBZ2} ${LIBZ}
+CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
+LDADD= -lz -lbz2
 CFLAGS+= -static -g
-CFLAGS+= -I${.OBJDIR}
+CFLAGS+= -I${LA_SRCDIR} -I.
 
 # Uncomment to link against dmalloc
 #LDADD+= -L/usr/local/lib -ldmalloc
 #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
 #WARNS=6
 
-test:	libarchive_test
-	./libarchive_test -k
+# Build libarchive_test and run it.
+check test: libarchive_test
+	./libarchive_test -v -r ${.CURDIR}
 
+# list.h is just a list of all tests, as indicated by DEFINE_TEST macro lines
 list.h: ${TESTS} Makefile
 	(cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h
 
-clean:
-	rm -f *.out
-	rm -f *.o
-	rm -f *.core
-	rm -f *~
-	rm -f list.h
+CLEANFILES += *.out *.o *.core *~ list.h
+
+cleantest:
 	-chmod -R +w /tmp/libarchive_test.*
 	rm -rf /tmp/libarchive_test.*
 
Index: test_tar_filenames.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_tar_filenames.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_tar_filenames.c -L lib/libarchive/test/test_tar_filenames.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_tar_filenames.c
+++ lib/libarchive/test/test_tar_filenames.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_tar_filenames.c,v 1.7 2007/07/14 17:52:01 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_tar_filenames.c,v 1.7.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 /*
  * Exercise various lengths of filenames in tar archives,
@@ -40,19 +40,22 @@
 	struct archive_entry *ae;
 	struct archive *a;
 	size_t used;
-	size_t prefix_length = 0;
-	int i = 0;
+	char *p;
+	int i;
 
+	p = filename;
 	if (prefix) {
 		strcpy(filename, prefix);
-		i = prefix_length = strlen(prefix);
+		p += strlen(p);
 	}
-	for (; i < prefix_length + dlen; i++)
-		filename[i] = 'a';
-	filename[i++] = '/';
-	for (; i < prefix_length + dlen + flen + 1; i++)
-		filename[i] = 'b';
-	filename[i++] = '\0';
+	if (dlen > 0) {
+		for (i = 0; i < dlen; i++)
+			*p++ = 'a';
+		*p++ = '/';
+	}
+	for (i = 0; i < flen; i++)
+		*p++ = 'b';
+	*p = '\0';
 
 	strcpy(dirname, filename);
 
@@ -160,15 +163,22 @@
 	int dlen, flen;
 
 	/* Repeat the following for a variety of dir/file lengths. */
-	for (dlen = 40; dlen < 60; dlen++) {
-		for (flen = 40; flen < 60; flen++) {
+	for (dlen = 45; dlen < 55; dlen++) {
+		for (flen = 45; flen < 55; flen++) {
+			test_filename(NULL, dlen, flen);
+			test_filename("/", dlen, flen);
+		}
+	}
+
+	for (dlen = 0; dlen < 140; dlen += 10) {
+		for (flen = 98; flen < 102; flen++) {
 			test_filename(NULL, dlen, flen);
 			test_filename("/", dlen, flen);
 		}
 	}
 
 	for (dlen = 140; dlen < 160; dlen++) {
-		for (flen = 90; flen < 110; flen++) {
+		for (flen = 95; flen < 105; flen++) {
 			test_filename(NULL, dlen, flen);
 			test_filename("/", dlen, flen);
 		}
--- /dev/null
+++ lib/libarchive/test/test_entry_strmode.c
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_entry_strmode.c,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $");
+
+DEFINE_TEST(test_entry_strmode)
+{
+	struct archive_entry *entry;
+
+	assert((entry = archive_entry_new()) != NULL);
+
+	archive_entry_set_mode(entry, S_IFREG | 0642);
+	assertEqualString(archive_entry_strmode(entry), "-rw-r---w- ");
+
+	archive_entry_set_mode(entry, S_IFBLK | 03642);
+	assertEqualString(archive_entry_strmode(entry), "brw-r-S-wT ");
+
+	archive_entry_set_mode(entry, S_IFCHR | 05777);
+	assertEqualString(archive_entry_strmode(entry), "crwsrwxrwt ");
+
+	archive_entry_set_mode(entry, S_IFLNK | 04000);
+	assertEqualString(archive_entry_strmode(entry), "l--S------ ");
+
+	/* Release the experimental entry. */
+	archive_entry_free(entry);
+}
--- /dev/null
+++ lib/libarchive/test/test_write_disk_sparse.c
@@ -0,0 +1,278 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_write_disk_sparse.c,v 1.1.2.1 2009/02/09 03:31:53 kientzle Exp $");
+
+/*
+ * Write a file using archive_write_data call, read the file
+ * back and verify the contents.  The data written includes large
+ * blocks of nulls, so it should exercise the sparsification logic
+ * if ARCHIVE_EXTRACT_SPARSE is enabled.
+ */
+static void
+verify_write_data(struct archive *a, int sparse)
+{
+	static const char data[]="abcdefghijklmnopqrstuvwxyz";
+	struct stat st;
+	struct archive_entry *ae;
+	size_t buff_size = 64 * 1024;
+	char *buff, *p;
+	const char *msg = sparse ? "sparse" : "non-sparse";
+	int fd;
+
+	buff = malloc(buff_size);
+	assert(buff != NULL);
+
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_size(ae, 8 * buff_size);
+	archive_entry_set_pathname(ae, "test_write_data");
+	archive_entry_set_mode(ae, AE_IFREG | 0755);
+	assertEqualIntA(a, 0, archive_write_header(a, ae));
+
+	/* Use archive_write_data() to write three relatively sparse blocks. */
+
+	/* First has non-null data at beginning. */
+	memset(buff, 0, buff_size);
+	memcpy(buff, data, sizeof(data));
+	failure("%s", msg);
+	assertEqualInt(buff_size, archive_write_data(a, buff, buff_size));
+
+	/* Second has non-null data in the middle. */
+	memset(buff, 0, buff_size);
+	memcpy(buff + buff_size / 2 - 3, data, sizeof(data));
+	failure("%s", msg);
+	assertEqualInt(buff_size, archive_write_data(a, buff, buff_size));
+
+	/* Third has non-null data at the end. */
+	memset(buff, 0, buff_size);
+	memcpy(buff + buff_size - sizeof(data), data, sizeof(data));
+	failure("%s", msg);
+	assertEqualInt(buff_size, archive_write_data(a, buff, buff_size));
+
+	failure("%s", msg);
+	assertEqualIntA(a, 0, archive_write_finish_entry(a));
+
+	/* Test the entry on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+        assertEqualInt(st.st_size, 8 * buff_size);
+	fd = open(archive_entry_pathname(ae), O_RDONLY);
+	if (!assert(fd >= 0))
+		return;
+
+	/* Check first block. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	failure("%s", msg);
+	assertEqualMem(buff, data, sizeof(data));
+	for (p = buff + sizeof(data); p < buff + buff_size; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check second block. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	for (p = buff; p < buff + buff_size; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (p == buff + buff_size / 2 - 3) {
+			assertEqualMem(p, data, sizeof(data));
+			p += sizeof(data);
+		} else if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check third block. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	for (p = buff; p < buff + buff_size - sizeof(data); ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+	failure("%s", msg);
+	assertEqualMem(buff + buff_size - sizeof(data), data, sizeof(data));
+
+	/* XXX more XXX */
+
+	assertEqualInt(0, close(fd));
+	free(buff);
+}
+
+/*
+ * As above, but using the archive_write_data_block() call.
+ */
+static void
+verify_write_data_block(struct archive *a, int sparse)
+{
+	static const char data[]="abcdefghijklmnopqrstuvwxyz";
+	struct stat st;
+	struct archive_entry *ae;
+	size_t buff_size = 64 * 1024;
+	char *buff, *p;
+	const char *msg = sparse ? "sparse" : "non-sparse";
+	int fd;
+
+	buff = malloc(buff_size);
+	assert(buff != NULL);
+
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_size(ae, 8 * buff_size);
+	archive_entry_set_pathname(ae, "test_write_data_block");
+	archive_entry_set_mode(ae, AE_IFREG | 0755);
+	assertEqualIntA(a, 0, archive_write_header(a, ae));
+
+	/* Use archive_write_data_block() to write three
+	   relatively sparse blocks. */
+
+	/* First has non-null data at beginning. */
+	memset(buff, 0, buff_size);
+	memcpy(buff, data, sizeof(data));
+	failure("%s", msg);
+	assertEqualInt(ARCHIVE_OK,
+	    archive_write_data_block(a, buff, buff_size, 100));
+
+	/* Second has non-null data in the middle. */
+	memset(buff, 0, buff_size);
+	memcpy(buff + buff_size / 2 - 3, data, sizeof(data));
+	failure("%s", msg);
+	assertEqualInt(ARCHIVE_OK,
+	    archive_write_data_block(a, buff, buff_size, buff_size + 200));
+
+	/* Third has non-null data at the end. */
+	memset(buff, 0, buff_size);
+	memcpy(buff + buff_size - sizeof(data), data, sizeof(data));
+	failure("%s", msg);
+	assertEqualInt(ARCHIVE_OK,
+	    archive_write_data_block(a, buff, buff_size, buff_size * 2 + 300));
+
+	failure("%s", msg);
+	assertEqualIntA(a, 0, archive_write_finish_entry(a));
+
+	/* Test the entry on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+        assertEqualInt(st.st_size, 8 * buff_size);
+	fd = open(archive_entry_pathname(ae), O_RDONLY);
+	if (!assert(fd >= 0))
+		return;
+
+	/* Check 100-byte gap at beginning */
+	assertEqualInt(100, read(fd, buff, 100));
+	failure("%s", msg);
+	for (p = buff; p < buff + 100; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check first block. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	failure("%s", msg);
+	assertEqualMem(buff, data, sizeof(data));
+	for (p = buff + sizeof(data); p < buff + buff_size; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check 100-byte gap */
+	assertEqualInt(100, read(fd, buff, 100));
+	failure("%s", msg);
+	for (p = buff; p < buff + 100; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check second block. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	for (p = buff; p < buff + buff_size; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (p == buff + buff_size / 2 - 3) {
+			assertEqualMem(p, data, sizeof(data));
+			p += sizeof(data);
+		} else if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check 100-byte gap */
+	assertEqualInt(100, read(fd, buff, 100));
+	failure("%s", msg);
+	for (p = buff; p < buff + 100; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+
+	/* Check third block. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	for (p = buff; p < buff + buff_size - sizeof(data); ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+	failure("%s", msg);
+	assertEqualMem(buff + buff_size - sizeof(data), data, sizeof(data));
+
+	/* Check another block size beyond last we wrote. */
+	assertEqualInt(buff_size, read(fd, buff, buff_size));
+	failure("%s", msg);
+	for (p = buff; p < buff + buff_size; ++p) {
+		failure("offset: %d, %s", (int)(p - buff), msg);
+		if (!assertEqualInt(0, *p))
+			break;
+	}
+
+
+	/* XXX more XXX */
+
+	assertEqualInt(0, close(fd));
+	free(buff);
+}
+
+DEFINE_TEST(test_write_disk_sparse)
+{
+	struct archive *ad;
+
+
+	/*
+	 * The return values, etc, of the write data functions
+	 * shouldn't change regardless of whether we've requested
+	 * sparsification.  (The performance and pattern of actual
+	 * write calls to the disk should vary, of course, but the
+	 * client program shouldn't see any difference.)
+	 */
+	assert((ad = archive_write_disk_new()) != NULL);
+        archive_write_disk_set_options(ad, 0);
+	verify_write_data(ad, 0);
+	verify_write_data_block(ad, 0);
+	assertEqualInt(0, archive_write_finish(ad));
+
+	assert((ad = archive_write_disk_new()) != NULL);
+        archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_SPARSE);
+	verify_write_data(ad, 1);
+	verify_write_data_block(ad, 1);
+	assertEqualInt(0, archive_write_finish(ad));
+
+}
Index: test_read_format_cpio_odc.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_format_cpio_odc.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_format_cpio_odc.c -L lib/libarchive/test/test_read_format_cpio_odc.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_format_cpio_odc.c
+++ lib/libarchive/test/test_read_format_cpio_odc.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_odc.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_odc.c,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $");
 
 static unsigned char archive[] = {
 '0','7','0','7','0','7','0','0','2','0','2','5','0','7','4','6','6','1','0',
@@ -54,7 +54,7 @@
 	assertA(0 == archive_read_support_compression_all(a));
 	assertA(0 == archive_read_support_format_all(a));
 	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
-	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
 	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
 	assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_POSIX);
 	assert(0 == archive_read_close(a));
--- /dev/null
+++ lib/libarchive/test/test_read_format_tar_empty_filename.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_read_format_tar_empty_filename.c,v 1.1.2.1 2008/08/10 04:32:47 kientzle Exp $");
+
+/*
+ * Tar entries with empty filenames are unusual, but shouldn't crash us.
+ */
+DEFINE_TEST(test_read_format_tar_empty_filename)
+{
+	char name[] = "test_read_format_tar_empty_filename.tar";
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+	extract_reference_file(name);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+	/* Read first entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("", archive_entry_pathname(ae));
+	assertEqualInt(1208628157, archive_entry_mtime(ae));
+	assertEqualInt(1000, archive_entry_uid(ae));
+	assertEqualString("tim", archive_entry_uname(ae));
+	assertEqualInt(0, archive_entry_gid(ae));
+	assertEqualString("wheel", archive_entry_gname(ae));
+	assertEqualInt(040775, archive_entry_mode(ae));
+
+	/* Verify the end-of-archive. */
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+	/* Verify that the format detection worked. */
+	assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_compat_zip_1.zip.uu
@@ -0,0 +1,15 @@
+$FreeBSD: src/lib/libarchive/test/test_compat_zip_1.zip.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_compat_zip_1.zip
+M4$L#!!0`"``(``B$@S<````````````````4````345402U)3D8O34%.249%
+M4U0N34;S3<S+3$LM+M$-2RTJSLS/LU(PU#/@Y7+,0Q)Q+$A,SDA5`(H!)<U!
+MTLY%J8DEJ2FZ3I56"BF9B4DY^;J&>J9Z!O$&YKI)!H8*&L&E>0J^F<E%^<65
+MQ26IN<4*GGG)>IJ\7+Q<`%!+!PAHTY\490```'$```!02P,$%``(``@`"(2#
+M-P````````````````D```!T;7`N8VQA<W,[]6_7/@8&!D,&+G8&#G8&3BX&
+M1 at 86'@8V!E9&!F8-S3!&!C:;S+S,$CN at L'-^2BHC at T!68EFB?DYB7KJ^?U)6
+M:G()4&%);@&#(@,34"\(,`(AT``@R0[D"8+Y#`RL6ML9F#>"%3```%!+!P at +
+M(*8V:````'8```!02P$"%``4``@`"``(A(,W:-.?%&4```!Q````%```````
+M````````````````345402U)3D8O34%.249%4U0N34902P$"%``4``@`"``(
+MA(,W"R"F-F@```!V````"0````````````````"G````=&UP+F-L87-S4$L%
+J!@`````"``(`>0```$8!```7`%!R;T=U87)D+"!V97)S:6]N(#0N,"XQ
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_read_format_tar_empty_filename.tar.uu
@@ -0,0 +1,39 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_tar_empty_filename.tar.uu,v 1.2.2.2 2008/08/10 05:38:07 kientzle Exp $
+begin 644 test_compat_tar_1.tar
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#<W-2``,#`Q-S4P(``P,#`P,#`@`#`P,#`P,#`P,#`P
+M(#$Q,#`R-#,Q-C<U(#`Q,3`P,0`@-0``````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,'1I;0``
+M````````````````````````````````````=VAE96P`````````````````
+M```````````````````P,#`P,#`@`#`P,#`P,"``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+&````````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_compat_gtar_1.tgz.uu
@@ -0,0 +1,10 @@
+$FreeBSD: src/lib/libarchive/test/test_compat_gtar_1.tgz.uu,v 1.1.2.1 2008/02/11 00:31:08 kientzle Exp $
+begin 644 test_compat_gtar_1.tgz
+M'XL(`,N`6T<``^W62PZ",!`&X!YE3@`SI:6Z<R^7\(&*+Q+%>'W+PJB)43=4
+MJO^W:1.Z:#KYATG2)!T5]7Y95/N-Z@:UF)ZO7B9"-TPD[%@4%1W=Y\'IV$P.
+M1.I0U\VK<^=566Y#7"@LT9FQN1L,.>[=M]\Q5@%JHX0Y-Z;-NSC+]^LM\S[R
+M.G?,XC(B+:Q949"B7O/?5+N7Y]Y]CU32U_[OZS_NZ#X/T/][T\/1_\/K;?XQ
+M_P4QF<[FY6*YJM9Q[[[]CK$*4!O_CV%G[6?SGS9^_C/&:I]_'6(X_?/Y#P``
+4````````````?L\%KFMT6@`H````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_link_resolver.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_link_resolver.c,v 1.2.2.1 2008/08/10 04:32:47 kientzle Exp $");
+
+static void test_linkify_tar(void)
+{
+	struct archive_entry *entry, *e2;
+	struct archive_entry_linkresolver *resolver;
+
+	/* Initialize the resolver. */
+	assert(NULL != (resolver = archive_entry_linkresolver_new()));
+	archive_entry_linkresolver_set_strategy(resolver,
+	    ARCHIVE_FORMAT_TAR_USTAR);
+
+	/* Create an entry with only 1 link and try to linkify it. */
+	assert(NULL != (entry = archive_entry_new()));
+	archive_entry_set_pathname(entry, "test1");
+	archive_entry_set_ino(entry, 1);
+	archive_entry_set_dev(entry, 2);
+	archive_entry_set_nlink(entry, 1);
+	archive_entry_set_size(entry, 10);
+	archive_entry_linkify(resolver, &entry, &e2);
+
+	/* Shouldn't have been changed. */
+	assert(e2 == NULL);
+	assertEqualInt(10, archive_entry_size(entry));
+	assertEqualString("test1", archive_entry_pathname(entry));
+
+	/* Now, try again with an entry that has 2 links. */
+	archive_entry_set_pathname(entry, "test2");
+	archive_entry_set_nlink(entry, 2);
+	archive_entry_set_ino(entry, 2);
+	archive_entry_linkify(resolver, &entry, &e2);
+	/* Shouldn't be altered, since it wasn't seen before. */
+	assert(e2 == NULL);
+	assertEqualString("test2", archive_entry_pathname(entry));
+	assertEqualString(NULL, archive_entry_hardlink(entry));
+	assertEqualInt(10, archive_entry_size(entry));
+
+	/* Match again and make sure it does get altered. */
+	archive_entry_linkify(resolver, &entry, &e2);
+	assert(e2 == NULL);
+	assertEqualString("test2", archive_entry_pathname(entry));
+	assertEqualString("test2", archive_entry_hardlink(entry));
+	assertEqualInt(0, archive_entry_size(entry));
+
+
+	/* Dirs should never be matched as hardlinks, regardless. */
+	archive_entry_set_pathname(entry, "test3");
+	archive_entry_set_nlink(entry, 2);
+	archive_entry_set_filetype(entry, AE_IFDIR);
+	archive_entry_set_ino(entry, 3);
+	archive_entry_set_hardlink(entry, NULL);
+	archive_entry_linkify(resolver, &entry, &e2);
+	/* Shouldn't be altered, since it wasn't seen before. */
+	assert(e2 == NULL);
+	assertEqualString("test3", archive_entry_pathname(entry));
+	assertEqualString(NULL, archive_entry_hardlink(entry));
+
+	/* Dir, so it shouldn't get matched. */
+	archive_entry_linkify(resolver, &entry, &e2);
+	assert(e2 == NULL);
+	assertEqualString("test3", archive_entry_pathname(entry));
+	assertEqualString(NULL, archive_entry_hardlink(entry));
+
+	archive_entry_free(entry);
+	archive_entry_linkresolver_free(resolver);
+}
+
+static void test_linkify_old_cpio(void)
+{
+	struct archive_entry *entry, *e2;
+	struct archive_entry_linkresolver *resolver;
+
+	/* Initialize the resolver. */
+	assert(NULL != (resolver = archive_entry_linkresolver_new()));
+	archive_entry_linkresolver_set_strategy(resolver,
+	    ARCHIVE_FORMAT_CPIO_POSIX);
+
+	/* Create an entry with 2 link and try to linkify it. */
+	assert(NULL != (entry = archive_entry_new()));
+	archive_entry_set_pathname(entry, "test1");
+	archive_entry_set_ino(entry, 1);
+	archive_entry_set_dev(entry, 2);
+	archive_entry_set_nlink(entry, 2);
+	archive_entry_set_size(entry, 10);
+	archive_entry_linkify(resolver, &entry, &e2);
+
+	/* Shouldn't have been changed. */
+	assert(e2 == NULL);
+	assertEqualInt(10, archive_entry_size(entry));
+	assertEqualString("test1", archive_entry_pathname(entry));
+
+	/* Still shouldn't be matched. */
+	archive_entry_linkify(resolver, &entry, &e2);
+	assert(e2 == NULL);
+	assertEqualString("test1", archive_entry_pathname(entry));
+	assertEqualString(NULL, archive_entry_hardlink(entry));
+	assertEqualInt(10, archive_entry_size(entry));
+
+	archive_entry_free(entry);
+	archive_entry_linkresolver_free(resolver);
+}
+
+static void test_linkify_new_cpio(void)
+{
+	struct archive_entry *entry, *e2;
+	struct archive_entry_linkresolver *resolver;
+
+	/* Initialize the resolver. */
+	assert(NULL != (resolver = archive_entry_linkresolver_new()));
+	archive_entry_linkresolver_set_strategy(resolver,
+	    ARCHIVE_FORMAT_CPIO_SVR4_NOCRC);
+
+	/* Create an entry with only 1 link and try to linkify it. */
+	assert(NULL != (entry = archive_entry_new()));
+	archive_entry_set_pathname(entry, "test1");
+	archive_entry_set_ino(entry, 1);
+	archive_entry_set_dev(entry, 2);
+	archive_entry_set_nlink(entry, 1);
+	archive_entry_set_size(entry, 10);
+	archive_entry_linkify(resolver, &entry, &e2);
+
+	/* Shouldn't have been changed. */
+	assert(e2 == NULL);
+	assertEqualInt(10, archive_entry_size(entry));
+	assertEqualString("test1", archive_entry_pathname(entry));
+
+	/* Now, try again with an entry that has 3 links. */
+	archive_entry_set_pathname(entry, "test2");
+	archive_entry_set_nlink(entry, 3);
+	archive_entry_set_ino(entry, 2);
+	archive_entry_linkify(resolver, &entry, &e2);
+
+	/* First time, it just gets swallowed. */
+	assert(entry == NULL);
+	assert(e2 == NULL);
+
+	/* Match again. */
+	assert(NULL != (entry = archive_entry_new()));
+	archive_entry_set_pathname(entry, "test3");
+	archive_entry_set_ino(entry, 2);
+	archive_entry_set_dev(entry, 2);
+	archive_entry_set_nlink(entry, 2);
+	archive_entry_set_size(entry, 10);
+	archive_entry_linkify(resolver, &entry, &e2);
+
+	/* Should get back "test2" and nothing else. */
+	assertEqualString("test2", archive_entry_pathname(entry));
+	assertEqualInt(0, archive_entry_size(entry));
+	archive_entry_free(entry);
+	assert(NULL == e2);
+	archive_entry_free(e2); /* This should be a no-op. */
+
+	/* Match a third time. */
+	assert(NULL != (entry = archive_entry_new()));
+	archive_entry_set_pathname(entry, "test4");
+	archive_entry_set_ino(entry, 2);
+	archive_entry_set_dev(entry, 2);
+	archive_entry_set_nlink(entry, 3);
+	archive_entry_set_size(entry, 10);
+	archive_entry_linkify(resolver, &entry, &e2);
+
+	/* Should get back "test3". */
+	assertEqualString("test3", archive_entry_pathname(entry));
+	assertEqualInt(0, archive_entry_size(entry));
+
+	/* Since "test4" was the last link, should get it back also. */
+	assertEqualString("test4", archive_entry_pathname(e2));
+	assertEqualInt(10, archive_entry_size(e2));
+
+	archive_entry_free(entry);
+	archive_entry_free(e2);
+	archive_entry_linkresolver_free(resolver);
+}
+
+DEFINE_TEST(test_link_resolver)
+{
+	test_linkify_tar();
+	test_linkify_old_cpio();
+	test_linkify_new_cpio();
+}
Index: test_write_disk.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_write_disk.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_write_disk.c -L lib/libarchive/test/test_write_disk.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_write_disk.c
+++ lib/libarchive/test/test_write_disk.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk.c,v 1.6 2007/09/21 04:52:43 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk.c,v 1.6.2.5 2008/11/28 20:08:47 kientzle Exp $");
 
 #if ARCHIVE_VERSION_STAMP >= 1009000
 
@@ -48,7 +48,11 @@
 	assert(0 == stat(archive_entry_pathname(ae), &st));
 	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
 	    st.st_mode, archive_entry_mode(ae));
-	assert(st.st_mode == (archive_entry_mode(ae) & ~UMASK));
+	/* When verifying a dir, ignore the S_ISGID bit, as some systems set
+	 * that automatically. */
+	if (archive_entry_filetype(ae) == AE_IFDIR)
+		st.st_mode &= ~S_ISGID;
+	assertEqualInt(st.st_mode, archive_entry_mode(ae) & ~UMASK);
 }
 
 static void create_reg_file(struct archive_entry *ae, const char *msg)
@@ -56,10 +60,31 @@
 	static const char data[]="abcdefghijklmnopqrstuvwxyz";
 	struct archive *ad;
 	struct stat st;
+	time_t now;
 
 	/* Write the entry to disk. */
 	assert((ad = archive_write_disk_new()) != NULL);
+        archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME);
 	failure("%s", msg);
+	/*
+	 * A touchy API design issue: archive_write_data() does (as of
+	 * 2.4.12) enforce the entry size as a limit on the data
+	 * written to the file.  This was not enforced prior to
+	 * 2.4.12.  The change was prompted by the refined
+	 * hardlink-restore semantics introduced at that time.  In
+	 * short, libarchive needs to know whether a "hardlink entry"
+	 * is going to overwrite the contents so that it can know
+	 * whether or not to open the file for writing.  This implies
+	 * that there is a fundamental semantic difference between an
+	 * entry with a zero size and one with a non-zero size in the
+	 * case of hardlinks and treating the hardlink case
+	 * differently from the regular file case is just asking for
+	 * trouble.  So, a zero size must always mean that no data
+	 * will be accepted, which is consistent with the file size in
+	 * the entry being a maximum size.
+	 */
+	archive_entry_set_size(ae, sizeof(data));
+	archive_entry_set_mtime(ae, 123456789, 0);
 	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
 	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
 	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
@@ -73,7 +98,11 @@
 	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
 	    st.st_mode, archive_entry_mode(ae));
 	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
-	assertEqualInt(st.st_size, sizeof(data));
+        assertEqualInt(st.st_size, sizeof(data));
+        assertEqualInt(st.st_mtime, 123456789);
+        failure("No atime was specified, so atime should get set to current time");
+	now = time(NULL);
+        assert(st.st_atime <= now && st.st_atime > now - 5);
 }
 
 static void create_reg_file2(struct archive_entry *ae, const char *msg)
@@ -92,6 +121,11 @@
 	/* Write the entry to disk. */
 	assert((ad = archive_write_disk_new()) != NULL);
 	failure("%s", msg);
+	/*
+	 * See above for an explanation why this next call
+	 * is necessary.
+	 */
+	archive_entry_set_size(ae, datasize);
 	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
 	for (i = 0; i < datasize - 999; i += 1000) {
 		assertEqualIntA(ad, ARCHIVE_OK,
@@ -118,6 +152,61 @@
 	free(compare);
 	free(data);
 }
+
+static void create_reg_file3(struct archive_entry *ae, const char *msg)
+{
+	static const char data[]="abcdefghijklmnopqrstuvwxyz";
+	struct archive *ad;
+	struct stat st;
+
+	/* Write the entry to disk. */
+	assert((ad = archive_write_disk_new()) != NULL);
+	failure("%s", msg);
+	/* Set the size smaller than the data and verify the truncation. */
+	archive_entry_set_size(ae, 5);
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(5, archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(ad);
+#else
+	assertEqualInt(0, archive_write_finish(ad));
+#endif
+	/* Test the entry on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+	    st.st_mode, archive_entry_mode(ae));
+	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
+	assertEqualInt(st.st_size, 5);
+}
+
+
+static void create_reg_file4(struct archive_entry *ae, const char *msg)
+{
+	static const char data[]="abcdefghijklmnopqrstuvwxyz";
+	struct archive *ad;
+	struct stat st;
+
+	/* Write the entry to disk. */
+	assert((ad = archive_write_disk_new()) != NULL);
+	/* Leave the size unset.  The data should not be truncated. */
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(ARCHIVE_OK,
+	    archive_write_data_block(ad, data, sizeof(data), 0));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(ad);
+#else
+	assertEqualInt(0, archive_write_finish(ad));
+#endif
+	/* Test the entry on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+	    st.st_mode, archive_entry_mode(ae));
+	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
+	failure(msg);
+	assertEqualInt(st.st_size, sizeof(data));
+}
 #endif
 
 DEFINE_TEST(test_write_disk)
@@ -144,6 +233,20 @@
 	create_reg_file2(ae, "Test creating another regular file");
 	archive_entry_free(ae);
 
+	/* A regular file with a size restriction */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file3");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	create_reg_file3(ae, "Regular file with size restriction");
+	archive_entry_free(ae);
+
+	/* A regular file with an unspecified size */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file3");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	create_reg_file4(ae, "Regular file with unspecified size");
+	archive_entry_free(ae);
+
 	/* A regular file over an existing file */
 	assert((ae = archive_entry_new()) != NULL);
 	archive_entry_copy_pathname(ae, "file");
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu
@@ -0,0 +1,1370 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_read_format_gtar_sparse_1_17_posix10_modified.tar
+M+B]087A(96%D97)S+C,X-C8S+W-P87)S90``````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V-#0`,#`P,3<U,``P,#`Q-S4P`#`P,#`P,#`P,C$U
+M`#$P-S,S,3`Q,30S`#`Q,S0V-@`@>```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,```````
+M````````````````````````````````````````````````````````````
+M```````````````````P,#`P,#`P`#`P,#`P,#``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````R,B!'3E4N<W!A<G-E+FUA:F]R/3$*,C(@1TY5
+M+G-P87)S92YM:6YO<CTP"C(V($=.52YS<&%R<V4N;F%M93US<&%R<V4*,S$@
+M1TY5+G-P87)S92YR96%L<VEZ93TS,30U-S(X"C(P(&%T:6UE/3$Q.3 at R.3,V
+M,#(*,C`@8W1I;64],3$Y.#(Y,S8P,`H`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````"XO1TY54W!A<G-E
+M1FEL92XS.#8V,R]S<&%R<V4`````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C0T`#`P,#$W-3``,#`P,3<U,``P,#`P,#`P,S`P,``Q,#<S,S$P,3$T
+M,``P,34Q-34`(#``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!T:6T`````````````````
+M`````````````````````'1I;0``````````````````````````````````
+M````,#`P,#`P,``P,#`P,#`P````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````(R%G;G4M<W!A<G-E+69O<FUA=`HC9F]R;6%T.C$N,`HS"CDY.3DS
+M- at HU,3(*,3DY.3 at W, at HU,3(*,S$T-3<R.`HP"@``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````80``
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````80``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````+B]087A(96%D97)S+C,X-C8S+W-P87)S93(`````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````#`P,#`V-#0`,#`P,3<U,``P,#`Q-S4P
+M`#`P,#`P,#`P,C$W`#$P-S,S,3`Q,30S`#`Q,S4U,@`@>```````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````!U
+M<W1A<@`P,```````````````````````````````````````````````````
+M```````````````````````````````````P,#`P,#`P`#`P,#`P,#``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````R,B!'3E4N<W!A<G-E+FUA
+M:F]R/3$*,C(@1TY5+G-P87)S92YM:6YO<CTP"C(W($=.52YS<&%R<V4N;F%M
+M93US<&%R<V4R"C,R($=.52YS<&%R<V4N<F5A;'-I>F4].3DP,#`P,#$*,C`@
+M871I;64],3$Y.#(Y,S8P,PHR,"!C=&EM93TQ,3DX,CDS-C`Q"@``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`"XO1TY54W!A<G-E1FEL92XS.#8V,R]S<&%R<V4R````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`P-C0T`#`P,#$W-3``,#`P,3<U,``P,#`P,#$T-3,P
+M,0`Q,#<S,S$P,3$T,0`P,34R-3,`(#``````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````=7-T87(`,#!T:6T`
+M`````````````````````````````````````'1I;0``````````````````
+M````````````````````,#`P,#`P,``P,#`P,#`P````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````.3D*.3DY.3,V"C4Q, at HQ.3DY.#<R"C4Q, at HR
+M.3DY.#`X"C4Q, at HS.3DY-S0T"C4Q, at HT.3DY-C@P"C4Q, at HU.3DY-C$V"C4Q
+M, at HV.3DY-34R"C4Q, at HX,#`P,#`P"C4Q, at HX.3DY.3,V"C4Q, at HY.3DY.#<R
+M"C4Q, at HQ,#DY.3 at P.`HU,3(*,3$Y.3DW-#0*-3$R"C$R.3DY-C at P"C4Q, at HQ
+M,SDY.38Q- at HU,3(*,30Y.3DU-3(*-3$R"C$V,#`P,#`P"C4Q, at HQ-CDY.3DS
+M- at HU,3(*,3<Y.3DX-S(*-3$R"C$X.3DY.#`X"C4Q, at HQ.3DY.3<T-`HU,3(*
+M,C`Y.3DV.#`*-3$R"C(Q.3DY-C$V"C4Q, at HR,CDY.34U, at HU,3(*,C0P,#`P
+M,#`*-3$R"C(T.3DY.3,V"C4Q, at HR-3DY.3@W, at HU,3(*,C8Y.3DX,#@*-3$R
+M"C(W.3DY-S0T"C4Q, at HR.#DY.38X,`HU,3(*,CDY.3DV,38*-3$R"C,P.3DY
+M-34R"C4Q, at HS,C`P,#`P,`HU,3(*,S(Y.3DY,S8*-3$R"C,S.3DY.#<R"C4Q
+M, at HS-#DY.3 at P.`HU,3(*,S4Y.3DW-#0*-3$R"C,V.3DY-C at P"C4Q, at HS-SDY
+M.38Q- at HU,3(*,S at Y.3DU-3(*-3$R"C0P,#`P,#`P"C4Q, at HT,#DY.3DS- at HU
+M,3(*-#$Y.3DX-S(*-3$R"C0R.3DY.#`X"C4Q, at HT,SDY.3<T-`HU,3(*-#0Y
+M.3DV.#`*-3$R"C0U.3DY-C$V"C4Q, at HT-CDY.34U, at HU,3(*-#@P,#`P,#`*
+M-3$R"C0X.3DY.3,V"C4Q, at HT.3DY.3@W, at HU,3(*-3`Y.3DX,#@*-3$R"C4Q
+M.3DY-S0T"C4Q, at HU,CDY.38X,`HU,3(*-3,Y.3DV,38*-3$R"C4T.3DY-34R
+M"C4Q, at HU-C`P,#`P,`HU,3(*-38Y.3DY,S8*-3$R"C4W.3DY.#<R"C4Q, at HU
+M.#DY.3 at P.`HU,3(*-3DY.3DW-#0*-3$R"C8P.3DY-C at P"C4Q, at HV,3DY.38Q
+M- at HU,3(*-C(Y.3DU-3(*-3$R"C8T,#`P,#`P"C4Q, at HV-#DY.3DS- at HU,3(*
+M-C4Y.3DX-S(*-3$R"C8V.3DY.#`X"C4Q, at HV-SDY.3<T-`HU,3(*-C at Y.3DV
+M.#`*-3$R"C8Y.3DY-C$V"C4Q, at HW,#DY.34U, at HU,3(*-S(P,#`P,#`*-3$R
+M"C<R.3DY.3,V"C4Q, at HW,SDY.3 at W, at HU,3(*-S0Y.3DX,#@*-3$R"C<U.3DY
+M-S0T"C4Q, at HW-CDY.38X,`HU,3(*-S<Y.3DV,38*-3$R"C<X.3DY-34R"C4Q
+M, at HX,#`P,#`P,`HU,3(*.#`Y.3DY,S8*-3$R"C at Q.3DY.#<R"C4Q, at HX,CDY
+M.3 at P.`HU,3(*.#,Y.3DW-#0*-3$R"C at T.3DY-C@P"C4Q, at HX-3DY.38Q-@HU
+M,3(*.#8Y.3DU-3(*-3$R"C at X,#`P,#`P"C4Q, at HX.#DY.3DS- at HU,3(*.#DY
+M.3DX-S(*-3$R"CDP.3DY.#`X"C4Q, at HY,3DY.3<T-`HU,3(*.3(Y.3DV.#`*
+M-3$R"CDS.3DY-C$V"C4Q, at HY-#DY.34U, at HU,3(*.38P,#`P,#`*-3$R"CDV
+M.3DY.3,V"C4Q, at HY-SDY.3@W, at HU,3(*.3 at Y.3DX,#@*,3DS"@``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````````&$`
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&$`````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````&$`````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````&$`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````&$`````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````````&$`
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&$`````````````
+M````````````````````````````````````````````````````````````
+M``````````!A````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````!A
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````!A````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````!A````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````!A````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````!A````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````!A
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````!A````````````
+M````````````````````````````````````````````````````````````
+M````````````80``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M80``````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````80``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````80``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````80``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````80``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M80``````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````80``````````
+M````````````````````````````````````````````````````````````
+M`````````````&$`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`&$`````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````&$`````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````&$`````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````&$`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````&$`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`&$`````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````&$`````````
+M````````````````````````````````````````````````````````````
+M``````````````!A````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``!A````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````!A````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````!A````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````!A````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````!A````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``!A````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````!A````````
+M````````````````````````````````````````````````````````````
+M````````````````80``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````80``````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````80``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````80``````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````80``````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````80``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````80``````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````80``````
+M````````````````````````````````````````````````````````````
+M`````````````````&$`````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````&$`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````&$`````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````&$`````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````&$`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````&$`````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````&$`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````&$`````
+M````````````````````````````````````````````````````````````
+M``````````````````!A````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````!A````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````!A````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!A````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````!A````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````!A````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````!A````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````!A````
+M````````````````````````````````````````````````````````````
+M````````````````````80``````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````80``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````80``
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````80``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````80``````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````80``````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````80``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````80``
+M````````````````````````````````````````````````````````````
+M`````````````````````&$`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````&$`````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````````&$`
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&$`````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````&$`````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````&$`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````&$`````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````````&$`
+M````````````````````````````````````````````````````````````
+M``````````````````````!A````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````!A````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````!A
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````!A````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````!A````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````!A````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````!A````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````!A
+M````````````````````````````````````````````````````````````
+M````````````````````````80``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````80``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M80``````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````80``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````80``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````80``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````80``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M80``````````````````````````````````````````````````````````
+M`````````````````````````&$`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````&$`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`&$`````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````&$`````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````+B]0
+M87A(96%D97)S+C,X-C8S+VYO;BUS<&%R<V4`````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````#`P,#`V-#0`,#`P,3<U,``P,#`Q-S4P`#`P,#`P,#`P,#4P`#$P
+M-S,S,3`Q,30S`#`Q-#(U,P`@>```````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````!U<W1A<@`P,```````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`P,#`P`#`P,#`P,#``````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````R,"!A=&EM93TQ,3DX,CDS-C`Q"C(P(&-T:6UE/3$Q
+M.3 at R.3,V,#$*````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````&YO;BUS<&%R<V4`````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````P,#`P
+M-C0T`#`P,#$W-3``,#`P,3<U,``P,#`P,#`P,#`P,``Q,#<S,S$P,3$T,0`P
+M,3(U,#<`(#``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````=7-T87(`,#!T:6T`````````````````````
+M`````````````````'1I;0``````````````````````````````````````
+M,#`P,#`P,``P,#`P,#`P````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+/````````````````````
+`
+end
Index: test_acl_pax.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_acl_pax.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_acl_pax.c -L lib/libarchive/test/test_acl_pax.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_acl_pax.c
+++ lib/libarchive/test/test_acl_pax.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_acl_pax.c,v 1.4 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_acl_pax.c,v 1.4.2.1 2008/08/10 04:32:47 kientzle Exp $");
 
 /*
  * Exercise the system-independent portion of the ACL support.
@@ -332,14 +332,10 @@
 		return (1);
 	if (qual != acl->qual)
 		return (0);
-	if (name == NULL) {
-		if (acl->name == NULL || acl->name[0] == '\0')
-			return (1);
-	}
-	if (acl->name == NULL) {
-		if (name[0] == '\0')
-			return (1);
-	}
+	if (name == NULL)
+		return (acl->name == NULL || acl->name[0] == '\0');
+	if (acl->name == NULL)
+		return (name == NULL || name[0] == '\0');
 	return (0 == strcmp(name, acl->name));
 }
 
--- /dev/null
+++ lib/libarchive/test/test_compat_tar_hardlink.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_compat_tar_hardlink.c,v 1.1.2.3 2008/08/25 01:55:40 kientzle Exp $");
+
+/*
+ * Background:  There are two written standards for the tar file format.
+ * The first is the POSIX 1988 "ustar" format, the second is the 2001
+ * "pax extended" format that builds on the "ustar" format by adding
+ * support for generic additional attributes.  Buried in the details
+ * is one frustrating incompatibility:  The 1988 standard says that
+ * tar readers MUST ignore the size field on hardlink entries; the
+ * 2001 standard says that tar readers MUST obey the size field on
+ * hardlink entries.  libarchive tries to navigate this particular
+ * minefield by using auto-detect logic to guess whether it should
+ * or should not obey the size field.
+ *
+ * This test tries to probe the boundaries of such handling; the test
+ * archives here were adapted from real archives created by real
+ * tar implementations that are (as of early 2008) apparently still
+ * in use.
+ */
+
+static void
+test_compat_tar_hardlink_1(void)
+{
+	char name[] = "test_compat_tar_hardlink_1.tar";
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+	extract_reference_file(name);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+	/* Read first entry, which is a regular file. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("xmcd-3.3.2/docs_d/READMf",
+		archive_entry_pathname(ae));
+	assertEqualString(NULL, archive_entry_hardlink(ae));
+	assertEqualInt(321, archive_entry_size(ae));
+	assertEqualInt(1082575645, archive_entry_mtime(ae));
+	assertEqualInt(1851, archive_entry_uid(ae));
+	assertEqualInt(3, archive_entry_gid(ae));
+	assertEqualInt(0100444, archive_entry_mode(ae));
+
+	/* Read second entry, which is a hard link at the end of archive. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("xmcd-3.3.2/README",
+		archive_entry_pathname(ae));
+	assertEqualString(
+		"xmcd-3.3.2/docs_d/READMf",
+		archive_entry_hardlink(ae));
+	assertEqualInt(0, archive_entry_size(ae));
+	assertEqualInt(1082575645, archive_entry_mtime(ae));
+	assertEqualInt(1851, archive_entry_uid(ae));
+	assertEqualInt(3, archive_entry_gid(ae));
+	assertEqualInt(0100444, archive_entry_mode(ae));
+
+	/* Verify the end-of-archive. */
+	/*
+	 * This failed in libarchive 2.4.12 because the tar reader
+	 * tried to obey the size field for the hard link and ended
+	 * up running past the end of the file.
+	 */
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+	/* Verify that the format detection worked. */
+	assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR);
+
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_read_finish(a);
+#else
+	assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+#endif
+}
+
+DEFINE_TEST(test_compat_tar_hardlink)
+{
+	test_compat_tar_hardlink_1();
+}
+
+
Index: test_write_format_cpio.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_write_format_cpio.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_write_format_cpio.c -L lib/libarchive/test/test_write_format_cpio.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_write_format_cpio.c
+++ lib/libarchive/test/test_write_format_cpio.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_cpio.c,v 1.2 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_cpio.c,v 1.2.2.1 2008/02/11 00:31:09 kientzle Exp $");
 
 /* The version stamp macro was introduced after cpio write support. */
 #if ARCHIVE_VERSION_STAMP >= 1009000
@@ -37,6 +37,7 @@
 	size_t used;
 	size_t buffsize = 1000000;
 	char *buff;
+	int damaged = 0;
 
 	buff = malloc(buffsize);
 
@@ -66,6 +67,41 @@
 	archive_entry_free(ae);
 	assertA(8 == archive_write_data(a, "12345678", 9));
 
+	/*
+	 * Write another file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(ae, 1, 10);
+	assert(1 == archive_entry_mtime(ae));
+	assert(10 == archive_entry_mtime_nsec(ae));
+	p = strdup("file2");
+	archive_entry_copy_pathname(ae, p);
+	strcpy(p, "XXXX");
+	free(p);
+	assertEqualString("file2", archive_entry_pathname(ae));
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	assert((S_IFREG | 0755) == archive_entry_mode(ae));
+	archive_entry_set_size(ae, 4);
+
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(4 == archive_write_data(a, "1234", 5));
+
+	/*
+	 * Write a directory to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(ae, 11, 110);
+	archive_entry_copy_pathname(ae, "dir");
+	archive_entry_set_mode(ae, S_IFDIR | 0755);
+	archive_entry_set_size(ae, 512);
+
+	assertA(0 == archive_write_header(a, ae));
+	assertEqualInt(0, archive_entry_size(ae));
+	archive_entry_free(ae);
+	assertEqualIntA(a, 0, archive_write_data(a, "12345678", 9));
+
+
 	/* Close out the archive. */
 	assertA(0 == archive_write_close(a));
 #if ARCHIVE_API_VERSION > 1
@@ -75,6 +111,22 @@
 #endif
 
 	/*
+	 * Damage the second entry to test the search-ahead recovery.
+	 */
+	{
+		int i;
+		for (i = 80; i < 150; i++) {
+			if (memcmp(buff + i, "07070", 5) == 0) {
+				damaged = 1;
+				buff[i] = 'X';
+				break;
+			}
+		}
+	}
+	failure("Unable to locate the second header for damage-recovery test.");
+	assert(damaged = 1);
+
+	/*
 	 * Now, read the data back.
 	 */
 	assert((a = archive_read_new()) != NULL);
@@ -82,21 +134,53 @@
 	assertA(0 == archive_read_support_compression_all(a));
 	assertA(0 == archive_read_open_memory(a, buff, used));
 
-	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
 
-	assert(1 == archive_entry_mtime(ae));
+	assertEqualInt(1, archive_entry_mtime(ae));
 	/* Not the same as above: cpio doesn't store hi-res times. */
 	assert(0 == archive_entry_mtime_nsec(ae));
 	assert(0 == archive_entry_atime(ae));
 	assert(0 == archive_entry_ctime(ae));
 	assertEqualString("file", archive_entry_pathname(ae));
-	assert((S_IFREG | 0755) == archive_entry_mode(ae));
-	assert(8 == archive_entry_size(ae));
+	assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
+	assertEqualInt(8, archive_entry_size(ae));
 	assertA(8 == archive_read_data(a, filedata, 10));
 	assert(0 == memcmp(filedata, "12345678", 8));
 
+	/*
+	 * Read the second file back.
+	 */
+	if (!damaged) {
+		assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+		assertEqualInt(1, archive_entry_mtime(ae));
+		/* Not the same as above: cpio doesn't store hi-res times. */
+		assert(0 == archive_entry_mtime_nsec(ae));
+		assert(0 == archive_entry_atime(ae));
+		assert(0 == archive_entry_ctime(ae));
+		assertEqualString("file2", archive_entry_pathname(ae));
+		assert((S_IFREG | 0755) == archive_entry_mode(ae));
+		assertEqualInt(4, archive_entry_size(ae));
+		assertEqualIntA(a, 4, archive_read_data(a, filedata, 10));
+		assert(0 == memcmp(filedata, "1234", 4));
+	}
+
+	/*
+	 * Read the dir entry back.
+	 */
+	assertEqualIntA(a,
+	    damaged ? ARCHIVE_WARN : ARCHIVE_OK,
+	    archive_read_next_header(a, &ae));
+	assertEqualInt(11, archive_entry_mtime(ae));
+	assert(0 == archive_entry_mtime_nsec(ae));
+	assert(0 == archive_entry_atime(ae));
+	assert(0 == archive_entry_ctime(ae));
+	assertEqualString("dir", archive_entry_pathname(ae));
+	assertEqualInt((S_IFDIR | 0755), archive_entry_mode(ae));
+	assertEqualInt(0, archive_entry_size(ae));
+	assertEqualIntA(a, 0, archive_read_data(a, filedata, 10));
+
 	/* Verify the end of the archive. */
-	assert(1 == archive_read_next_header(a, &ae));
+	assertEqualIntA(a, 1, archive_read_next_header(a, &ae));
 	assert(0 == archive_read_close(a));
 #if ARCHIVE_API_VERSION > 1
 	assert(0 == archive_read_finish(a));
Index: test.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test.h -L lib/libarchive/test/test.h -u -r1.1 -r1.2
--- lib/libarchive/test/test.h
+++ lib/libarchive/test/test.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/test/test.h,v 1.6 2007/07/14 17:52:01 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/test/test.h,v 1.6.2.4 2008/08/10 04:32:47 kientzle Exp $
  */
 
 /* Every test program should #include "test.h" as the first thing. */
@@ -31,22 +31,6 @@
  * The goal of this file (and the matching test.c) is to
  * simplify the very repetitive test-*.c test programs.
  */
-
-#define _FILE_OFFSET_BITS 64
-
-#include <archive.h>
-#include <archive_entry.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <wchar.h>
-
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#endif
-
 #if defined(HAVE_CONFIG_H)
 /* Most POSIX platforms use the 'configure' script to build config.h */
 #include "../../config.h"
@@ -61,6 +45,22 @@
 #error Oops: No config.h and no pre-built configuration in test.h.
 #endif
 
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <wchar.h>
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
 /* No non-FreeBSD platform will have __FBSDID, so just define it here. */
 #ifdef __FreeBSD__
 #include <sys/cdefs.h>  /* For __FBSDID */
@@ -69,52 +69,44 @@
 #endif
 
 /*
- * ARCHIVE_VERSION_STAMP first appeared in 1.9 and libarchive 2.2.4.
- * We can approximate it for earlier versions, though.
- * This is used to disable tests of features not present in the current
- * version.
- */
-#ifndef ARCHIVE_VERSION_STAMP
-#define ARCHIVE_VERSION_STAMP	\
-		(ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
-#endif
-
-
-/*
- * "list.h" is simply created by "grep DEFINE_TEST"; it has
- * a line like
- *      DEFINE_TEST(test_function)
- * for each test.
- * Include it here with a suitable DEFINE_TEST to declare all of the
- * test functions.
- */
-#define DEFINE_TEST(name) void name(void);
-#include "list.h"
-/*
  * Redefine DEFINE_TEST for use in defining the test functions.
  */
 #undef DEFINE_TEST
-#define DEFINE_TEST(name) void name(void)
+#define DEFINE_TEST(name) void name(void); void name(void)
 
 /* An implementation of the standard assert() macro */
 #define assert(e)   test_assert(__FILE__, __LINE__, (e), #e, NULL)
-/* As above, but reports any archive_error found in variable 'a' */
-#define assertA(e)   test_assert(__FILE__, __LINE__, (e), #e, (a))
 
-/* Asserts that two integers are the same.  Reports value of each one if not. */
-#define assertEqualIntA(a,v1,v2)   \
-  test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
+/* Assert two integers are the same.  Reports value of each one if not. */
 #define assertEqualInt(v1,v2)   \
   test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
 
-/* Asserts that two strings are the same.  Reports value of each one if not. */
-#define assertEqualStringA(a,v1,v2)   \
-  test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
+/* Assert two strings are the same.  Reports value of each one if not. */
 #define assertEqualString(v1,v2)   \
   test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
 /* As above, but v1 and v2 are wchar_t * */
 #define assertEqualWString(v1,v2)   \
   test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+/* As above, but raw blocks of bytes. */
+#define assertEqualMem(v1, v2, l)	\
+  test_assert_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
+/* Assert two files are the same; allow printf-style expansion of second name.
+ * See below for comments about variable arguments here...
+ */
+#define assertEqualFile		\
+  test_setup(__FILE__, __LINE__);test_assert_equal_file
+/* Assert that a file is empty; supports printf-style arguments. */
+#define assertEmptyFile		\
+  test_setup(__FILE__, __LINE__);test_assert_empty_file
+/* Assert that a file exists; supports printf-style arguments. */
+#define assertFileExists		\
+  test_setup(__FILE__, __LINE__);test_assert_file_exists
+/* Assert that a file exists; supports printf-style arguments. */
+#define assertFileNotExists		\
+  test_setup(__FILE__, __LINE__);test_assert_file_not_exists
+/* Assert that file contents match a string; supports printf-style arguments. */
+#define assertFileContents             \
+  test_setup(__FILE__, __LINE__);test_assert_file_contents
 
 /*
  * This would be simple with C99 variadic macros, but I don't want to
@@ -123,17 +115,57 @@
  * but effective.
  */
 #define skipping	\
-  skipping_setup(__FILE__, __LINE__);test_skipping
+  test_setup(__FILE__, __LINE__);test_skipping
 
 /* Function declarations.  These are defined in test_utility.c. */
 void failure(const char *fmt, ...);
-void skipping_setup(const char *, int);
+void test_setup(const char *, int);
 void test_skipping(const char *fmt, ...);
-void test_assert(const char *, int, int, const char *, struct archive *);
-void test_assert_equal_int(const char *, int, int, const char *, int, const char *, struct archive *);
-void test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, struct archive *);
-void test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, struct archive *);
+int test_assert(const char *, int, int, const char *, void *);
+int test_assert_empty_file(const char *, ...);
+int test_assert_equal_file(const char *, const char *, ...);
+int test_assert_equal_int(const char *, int, int, const char *, int, const char *, void *);
+int test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *);
+int test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
+int test_assert_equal_mem(const char *, int, const char *, const char *, const char *, const char *, size_t, const char *, void *);
+int test_assert_file_contents(const void *, int, const char *, ...);
+int test_assert_file_exists(const char *, ...);
+int test_assert_file_not_exists(const char *, ...);
+
+/* Like sprintf, then system() */
+int systemf(const char * fmt, ...);
+
+/* Suck file into string allocated via malloc(). Call free() when done. */
+/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
+char *slurpfile(size_t *, const char *fmt, ...);
+
+/* Extracts named reference file to the current directory. */
+void extract_reference_file(const char *);
+
+/*
+ * Special interfaces for libarchive test harness.
+ */
+
+#include "archive.h"
+#include "archive_entry.h"
 
 /* Special customized read-from-memory interface. */
 int read_open_memory(struct archive *, void *, size_t, size_t);
-int	read_open_memory(struct archive *, void *, size_t, size_t);
+
+/*
+ * ARCHIVE_VERSION_STAMP first appeared in 1.9 and libarchive 2.2.4.
+ * We can approximate it for earlier versions, though.
+ * This is used to disable tests of features not present in the current
+ * version.
+ */
+#ifndef ARCHIVE_VERSION_STAMP
+#define ARCHIVE_VERSION_STAMP	\
+		(ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
+#endif
+
+/* Versions of above that accept an archive argument for additional info. */
+#define assertA(e)   test_assert(__FILE__, __LINE__, (e), #e, (a))
+#define assertEqualIntA(a,v1,v2)   \
+  test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
+#define assertEqualStringA(a,v1,v2)   \
+  test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tgz.uu
@@ -0,0 +1,28 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tgz.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_read_format_gtar_sparse_1_17_posix01.tgz
+M'XL(`&*";$<``^W7WVX:1QB&<8ZY"B[`Q?/-OYT]X+3-416IZ at 5L'0ZLQHX%
+MMF3UZKN`';]V&SO21[PB>GXG;,#F`_,,F5F>?QSN/ZR'3^O-=IE:K7:^O1DV
+MV_7L>,*HYKR[M:X$O=V+)<XL="E9,,MQ%BSEFF>+^R.^AF^ZV]X.F_&E>)_G
+MX;U\O3T1L5O\]ON?R\-GOMQ>_K->)<NEBVT>JSYT?7?UU^<O%W]O5^GE(\/5
+M>G6XGA?31ZZ&FU4_2O6L6#RS\;)U<7_],.0LS&-8#+>7XU.8]2V./QOB[KZ+
+MY_>%^=1_J9_3\GS\O/[8?UR_7GY>_Y"O@#?6?]PMEZ?U/]YOQ<:OA,6[+*+'
+M]3_F]NK/O?7XB:Y_KV'J%X"31C_36GYK_Q>/-^.M[_]JNO]+N_U?*<;^[SW$
+M]I_]7]_OWX'-G^\-GS:`??_RH:<=8)Q;[.MW;@+C[CJTPX9PO.YRWE_G\;JV
+ML+\NNVL[_&X=KTLY_&X[_)T/U_+\O3R_!1E@)A,LR at A+,L.R#+$J4ZSJV^AT
+M3M,YO<R)0>9$DSDQRIR894[,,B<6_7M5F1,[G=-T3B]S4I`Y*<J<%&5.2KHY
+MS_K!%)F3JLQ)G<YI,N?A[82'?\B<K)]_U@#RLP(T@:P-9(T@:P59,\C:0=$.
+MBG90M(.B'13MH&@'13LHVD'1#HIV4+6#JAU4[:!J!U4[J-I!U0ZJ=E"U at ZH=
+M=-I!IQUTVD&G'73:0:<==-I!IQUTVD'3#IIVT+2#IATT[:!I!TT[:,^^#)Y]
+M&V@'33OHM8->.^BU at UX[Z+6#7COHM8->.^@?.[`^?>?YUB8ZW[YV_CO6%N#5
+M__\MQQ1,_O^WW?DOYLKY[Q2P?X<'_<"#?N!!/_"@'WC0#SR\_=`?/.@''O0#
+M#_J!!_W`@W[@03_PX/R/*=$//.@''O0##_J!!_W`@W[@P?D?4Z(?>-`//.@'
+M'O0##_J!!_W`@_,_ID0_\*`?>-`//.@''O0##_J!!^=_3(E^X$$_\*`?>-`/
+M/.@''O0##\[_F!+]P(-^X$$_\*`?>-`//.@''IS_,27Z at 0?]P(-^X$$_\*`?
+M>-`//#C_8TKT`P_Z at 0?]P(-^X$$_\*`?>'#^QY3H!Q[T`P_Z at 0?]P(-^X$$_
+M\.#\CRG1#SSH!Q[T`P_Z at 0?]P(-^X,'Y'U.B'WC0#SSH!Q[T`P_Z at 0?]P(/S
+M/Z9$/_"@'WC0C\_R_.-P_V$]?%IOMLO4:K7SZR_7OVQOALUV?:09851SWMU:
+M5X+>'HS7%KJ4+)CE-`N68['9XOY(\U]UM[T=-N-+\3[/PUOY>GLB8E@,MY=7
+MZY59WV*?:K#Y>-_%R_NF?IWX,8Z^V/_'F^L_Z/JW<?W'$KK9XET6T>/Z'X-_
+9]>?>>OQ$US\``````/CY_0O4#S!&`/``````
+`
+end
Index: test_read_pax_truncated.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_pax_truncated.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_pax_truncated.c -L lib/libarchive/test/test_read_pax_truncated.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_pax_truncated.c
+++ lib/libarchive/test/test_read_pax_truncated.c
@@ -23,15 +23,15 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_pax_truncated.c,v 1.1 2007/07/13 15:14:35 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_pax_truncated.c,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $");
 
 DEFINE_TEST(test_read_pax_truncated)
 {
 	struct archive_entry *ae;
 	struct archive *a;
-	size_t used, i;
+	ssize_t used, i;
 	size_t buff_size = 1000000;
-	size_t filedata_size = 100000;
+	ssize_t filedata_size = 100000;
 	char *buff = malloc(buff_size);
 	char *buff2 = malloc(buff_size);
 	char *filedata = malloc(filedata_size);
Index: test_write_disk_secure.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_write_disk_secure.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_write_disk_secure.c -L lib/libarchive/test/test_write_disk_secure.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_write_disk_secure.c
+++ lib/libarchive/test/test_write_disk_secure.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_secure.c,v 1.3 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_secure.c,v 1.3.2.2 2008/09/12 04:34:56 kientzle Exp $");
 
 #define UMASK 022
 
@@ -105,17 +105,89 @@
 	archive_entry_free(ae);
 	assert(0 == archive_write_finish_entry(a));
 
+	/*
+	 * Without security checks, extracting a dir over a link to a
+	 * dir should follow the link.
+	 */
+	/* Create a symlink to a dir. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir3");
+	archive_entry_set_mode(ae, S_IFLNK | 0777);
+	archive_entry_set_symlink(ae, "dir");
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Extract a dir whose name matches the symlink. */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir3");
+	archive_entry_set_mode(ae, S_IFDIR | 0777);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Verify link was followed. */
+	assertEqualInt(0, lstat("link_to_dir3", &st));
+	assert(S_ISLNK(st.st_mode));
+	archive_entry_free(ae);
 
-#if ARCHIVE_API_VERSION > 1
-	assert(0 == archive_write_finish(a));
-#else
+	/*
+	 * As above, but a broken link, so the link should get replaced.
+	 */
+	/* Create a symlink to a dir. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir4");
+	archive_entry_set_mode(ae, S_IFLNK | 0777);
+	archive_entry_set_symlink(ae, "nonexistent_dir");
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Extract a dir whose name matches the symlink. */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir4");
+	archive_entry_set_mode(ae, S_IFDIR | 0777);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Verify link was replaced. */
+	assertEqualInt(0, lstat("link_to_dir4", &st));
+	assert(S_ISDIR(st.st_mode));
+	archive_entry_free(ae);
+
+	/*
+	 * As above, but a link to a non-dir, so the link should get replaced.
+	 */
+	/* Create a regular file and a symlink to it */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "non_dir");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Create symlink to the file. */
+	archive_entry_copy_pathname(ae, "link_to_dir5");
+	archive_entry_set_mode(ae, S_IFLNK | 0777);
+	archive_entry_set_symlink(ae, "non_dir");
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Extract a dir whose name matches the symlink. */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir5");
+	archive_entry_set_mode(ae, S_IFDIR | 0777);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	/* Verify link was replaced. */
+	assertEqualInt(0, lstat("link_to_dir5", &st));
+	assert(S_ISDIR(st.st_mode));
+	archive_entry_free(ae);
+
+#if ARCHIVE_VERSION_NUMBER < 2000000
 	archive_write_finish(a);
+#else
+	assert(0 == archive_write_finish(a));
 #endif
 
 	/* Test the entries on disk. */
 	assert(0 == lstat("dir", &st));
 	failure("dir: st.st_mode=%o", st.st_mode);
-	assert((st.st_mode & 07777) == 0755);
+	assert((st.st_mode & 0777) == 0755);
 
 	assert(0 == lstat("link_to_dir", &st));
 	failure("link_to_dir: st.st_mode=%o", st.st_mode);
@@ -137,7 +209,7 @@
 	failure("link_to_dir2 should have been re-created as a true dir");
 	assert(S_ISDIR(st.st_mode));
 	failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode);
-	assert((st.st_mode & 07777) == 0755);
+	assert((st.st_mode & 0777) == 0755);
 
 	assert(0 == lstat("link_to_dir2/filec", &st));
 	assert(S_ISREG(st.st_mode));
Index: test_read_format_zip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_format_zip.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_format_zip.c -L lib/libarchive/test/test_read_format_zip.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_format_zip.c
+++ lib/libarchive/test/test_read_format_zip.c
@@ -23,55 +23,55 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_zip.c,v 1.2 2007/05/29 01:00:21 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_zip.c,v 1.2.2.4 2008/11/28 20:08:47 kientzle Exp $");
 
-static unsigned char archive[] = {
-'P','K',3,4,10,0,0,0,0,0,'Y','f',179,'6',0,0,0,0,0,0,0,0,0,0,0,0,4,0,21,0,
-'d','i','r','/','U','T',9,0,3,25,'U','O','F',25,'U','O','F','U','x',4,0,232,
-3,232,3,'P','K',3,4,20,0,0,0,8,0,'o','f',179,'6',':','7','f','=',10,0,0,0,
-18,0,0,0,5,0,21,0,'f','i','l','e','1','U','T',9,0,3,'A','U','O','F',172,'[',
-'O','F','U','x',4,0,232,3,232,3,203,'H',205,201,201,231,202,'@','"',1,'P',
-'K',3,4,20,0,0,0,8,0,'Z','j',179,'6',':','7','f','=',10,0,0,0,18,0,0,0,5,
-0,21,0,'f','i','l','e','2','U','T',9,0,3,172,'[','O','F',172,'[','O','F',
-'U','x',4,0,232,3,232,3,203,'H',205,201,201,231,202,'@','"',1,'P','K',1,2,
-23,3,10,0,0,0,0,0,'Y','f',179,'6',0,0,0,0,0,0,0,0,0,0,0,0,4,0,13,0,0,0,0,
-0,0,0,16,0,237,'A',0,0,0,0,'d','i','r','/','U','T',5,0,3,25,'U','O','F','U',
-'x',0,0,'P','K',1,2,23,3,20,0,0,0,8,0,'o','f',179,'6',':','7','f','=',10,
-0,0,0,18,0,0,0,5,0,13,0,0,0,0,0,1,0,0,0,164,129,'7',0,0,0,'f','i','l','e',
-'1','U','T',5,0,3,'A','U','O','F','U','x',0,0,'P','K',1,2,23,3,20,0,0,0,8,
-0,'Z','j',179,'6',':','7','f','=',10,0,0,0,18,0,0,0,5,0,13,0,0,0,0,0,1,0,
-0,0,164,129,'y',0,0,0,'f','i','l','e','2','U','T',5,0,3,172,'[','O','F','U',
-'x',0,0,'P','K',5,6,0,0,0,0,3,0,3,0,191,0,0,0,187,0,0,0,0,0};
+/*
+ * The reference file for this has been manually tweaked so that:
+ *   * file2 has length-at-end but file1 does not
+ *   * file2 has an invalid CRC
+ */
 
 DEFINE_TEST(test_read_format_zip)
 {
+	const char *refname = "test_read_format_zip.zip";
 	struct archive_entry *ae;
 	struct archive *a;
 	char *buff[128];
+	const void *pv;
+	size_t s;
+	off_t o;
 
+	extract_reference_file(refname);
 	assert((a = archive_read_new()) != NULL);
 	assertA(0 == archive_read_support_compression_all(a));
 	assertA(0 == archive_read_support_format_all(a));
-	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_open_filename(a, refname, 10240));
 	assertA(0 == archive_read_next_header(a, &ae));
 	assertEqualString("dir/", archive_entry_pathname(ae));
 	assertEqualInt(1179604249, archive_entry_mtime(ae));
 	assertEqualInt(0, archive_entry_size(ae));
+	assertEqualIntA(a, ARCHIVE_EOF,
+	    archive_read_data_block(a, &pv, &s, &o));
+	assertEqualInt(s, 0);
 	assertA(0 == archive_read_next_header(a, &ae));
 	assertEqualString("file1", archive_entry_pathname(ae));
 	assertEqualInt(1179604289, archive_entry_mtime(ae));
 	assertEqualInt(18, archive_entry_size(ae));
-	assertEqualInt(18, archive_read_data(a, buff, 18));
+	failure("archive_read_data() returns number of bytes read");
+	assertEqualInt(18, archive_read_data(a, buff, 19));
 	assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
 	assertA(0 == archive_read_next_header(a, &ae));
 	assertEqualString("file2", archive_entry_pathname(ae));
 	assertEqualInt(1179605932, archive_entry_mtime(ae));
-	assertEqualInt(18, archive_entry_size(ae));
-	assertEqualInt(18, archive_read_data(a, buff, 18));
+	failure("file2 has length-at-end, so we shouldn't see a valid size");
+	assertEqualInt(0, archive_entry_size_is_set(ae));
+	failure("file2 has a bad CRC, so reading to end should fail");
+	assertEqualInt(ARCHIVE_WARN, archive_read_data(a, buff, 19));
 	assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
 	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
 	assertA(archive_format(a) == ARCHIVE_FORMAT_ZIP);
 	assert(0 == archive_read_close(a));
+
 #if ARCHIVE_API_VERSION > 1
 	assert(0 == archive_read_finish(a));
 #else
Index: test_write_format_ar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_write_format_ar.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_write_format_ar.c -L lib/libarchive/test/test_write_format_ar.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_write_format_ar.c
+++ lib/libarchive/test/test_write_format_ar.c
@@ -26,11 +26,11 @@
  */
 
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_ar.c,v 1.5 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_ar.c,v 1.5.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 char buff[4096];
 char buff2[64];
-static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n";
+static char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n";
 
 DEFINE_TEST(test_write_format_ar)
 {
@@ -119,9 +119,7 @@
 	assertA(0 == archive_read_next_header(a, &ae));
 	assertEqualInt(0, archive_entry_mtime(ae));
 	assertEqualString("//", archive_entry_pathname(ae));
-	assertEqualInt(strlen(strtab), archive_entry_size(ae));
-	assertEqualIntA(a, strlen(strtab), archive_read_data(a, buff2, 100));
-	assert(0 == memcmp(buff2, strtab, strlen(strtab)));
+	assertEqualInt(0, archive_entry_size(ae));
 
 	assertA(0 == archive_read_next_header(a, &ae));
 	assert(1 == archive_entry_mtime(ae));
@@ -164,6 +162,7 @@
 	archive_entry_set_filetype(ae, AE_IFREG);
 	archive_entry_set_size(ae, 5);
 	assertA(0 == archive_write_header(a, ae));
+	assertA(5 == archive_entry_size(ae));
 	assertA(5 == archive_write_data(a, "12345", 7));
 	archive_entry_free(ae);
 
--- /dev/null
+++ lib/libarchive/test/test_compat_zip.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_compat_zip.c,v 1.1.2.2 2008/05/21 04:14:39 kientzle Exp $");
+
+/* Copy this function for each test file and adjust it accordingly. */
+static void
+test_compat_zip_1(void)
+{
+	char name[] = "test_compat_zip_1.zip";
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+	extract_reference_file(name);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+	/* Read first entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("META-INF/MANIFEST.MF", archive_entry_pathname(ae));
+
+	/* Read second entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("tmp.class", archive_entry_pathname(ae));
+
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+	assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ZIP);
+
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
+DEFINE_TEST(test_compat_zip)
+{
+	test_compat_zip_1();
+}
+
+
Index: test_read_extract.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_extract.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_extract.c -L lib/libarchive/test/test_read_extract.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_extract.c
+++ lib/libarchive/test/test_read_extract.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_extract.c,v 1.3 2007/05/29 01:00:20 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_extract.c,v 1.3.2.1 2008/08/10 04:32:47 kientzle Exp $");
 
 #define BUFF_SIZE 1000000
 #define FILE_BUFF_SIZE 100000
@@ -135,12 +135,18 @@
 #endif
 
 	/* Test the entries on disk. */
+	/* This first entry was extracted with ARCHIVE_EXTRACT_PERM,
+	 * so the permissions should have been restored exactly,
+	 * including resetting the gid bit on those platforms
+	 * where gid is inherited by subdirs. */
 	assert(0 == stat("dir_0775", &st));
 	failure("This was 0775 in archive, and should be 0775 on disk");
-	assert(st.st_mode == (S_IFDIR | 0775));
+	assertEqualInt(st.st_mode, S_IFDIR | 0775);
+	/* Everything else was extracted without ARCHIVE_EXTRACT_PERM,
+	 * so there may be some sloppiness about gid bits on directories. */
 	assert(0 == stat("file", &st));
 	failure("st.st_mode=%o should be %o", st.st_mode, S_IFREG | 0755);
-	assert(st.st_mode == (S_IFREG | 0755));
+	assertEqualInt(st.st_mode, S_IFREG | 0755);
 	failure("The file extracted to disk is the wrong size.");
 	assert(st.st_size == FILE_BUFF_SIZE);
 	fd = open("file", O_RDONLY);
@@ -153,23 +159,26 @@
 	assert(memcmp(buff, file_buff, FILE_BUFF_SIZE) == 0);
 	assert(0 == stat("dir", &st));
 	failure("This was 0777 in archive, but umask should make it 0755");
-	assert(st.st_mode == (S_IFDIR | 0755));
+	/* If EXTRACT_PERM wasn't used, be careful to ignore sgid bit
+	 * when checking dir modes, as some systems inherit sgid bit
+	 * from the parent dir. */
+	assertEqualInt(0755, st.st_mode & 0777);
 	assert(0 == stat("dir/file", &st));
 	assert(st.st_mode == (S_IFREG | 0700));
 	assert(0 == stat("dir2", &st));
-	assert(st.st_mode == (S_IFDIR | 0755));
+	assertEqualInt(0755, st.st_mode & 0777);
 	assert(0 == stat("dir2/file", &st));
 	assert(st.st_mode == (S_IFREG | 0000));
 	assert(0 == stat("dir3", &st));
-	assert(st.st_mode == (S_IFDIR | 0710));
+	assertEqualInt(0710, st.st_mode & 0777);
 	assert(0 == stat("dir4", &st));
-	assert(st.st_mode == (S_IFDIR | 0755));
+	assertEqualInt(0755, st.st_mode & 0777);
 	assert(0 == stat("dir4/a", &st));
-	assert(st.st_mode == (S_IFDIR | 0755));
+	assertEqualInt(0755, st.st_mode & 0777);
 	assert(0 == stat("dir4/b", &st));
-	assert(st.st_mode == (S_IFDIR | 0755));
+	assertEqualInt(0755, st.st_mode & 0777);
 	assert(0 == stat("dir4/c", &st));
-	assert(st.st_mode == (S_IFDIR | 0711));
+	assertEqualInt(0711, st.st_mode & 0777);
 	assert(0 == lstat("symlink", &st));
 	assert(S_ISLNK(st.st_mode));
 #if HAVE_LCHMOD
--- /dev/null
+++ lib/libarchive/test/test_empty_write.c
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_empty_write.c,v 1.1.2.2 2008/05/21 04:14:39 kientzle Exp $");
+
+DEFINE_TEST(test_empty_write)
+{
+	char buff[32768];
+	struct archive_entry *ae;
+	struct archive *a;
+	size_t used;
+
+	/*
+	 * Exercise a zero-byte write to a gzip-compressed archive.
+	 */
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_gzip(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+	/* Write a file to it. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, 0);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* THE TEST: write zero bytes to this entry. */
+	/* This used to crash. */
+	assertEqualIntA(a, 0, archive_write_data(a, "", 0));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+
+	/*
+	 * Again, with bzip2 compression.
+	 */
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_bzip2(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+	/* Write a file to it. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, 0);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* THE TEST: write zero bytes to this entry. */
+	assertEqualIntA(a, 0, archive_write_data(a, "", 0));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+
+	/*
+	 * For good measure, one more time with no compression.
+	 */
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+	/* Write a file to it. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, 0);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* THE TEST: write zero bytes to this entry. */
+	assertEqualIntA(a, 0, archive_write_data(a, "", 0));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+}
Index: test_read_format_gtar_sparse.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_format_gtar_sparse.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_format_gtar_sparse.c -L lib/libarchive/test/test_read_format_gtar_sparse.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_format_gtar_sparse.c
+++ lib/libarchive/test/test_read_format_gtar_sparse.c
@@ -23,13 +23,13 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse.c,v 1.6 2007/08/18 21:53:25 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse.c,v 1.6.2.2 2008/05/21 04:14:39 kientzle Exp $");
 
 
 struct contents {
 	off_t	o;
 	size_t	s;
-	char *d;
+	const char *d;
 };
 
 struct contents archive_contents_sparse[] = {
@@ -171,623 +171,6 @@
 	{ NULL, NULL }
 };
 
-
-/* Old GNU tar sparse format, as created by gtar 1.13 */
-static unsigned char archive_old_gtar_1_13[] = {
-31,139,8,0,30,'%',193,'F',0,3,237,215,'K','n',219,'H',20,133,'a',246,'N',
-180,129,6,170,'n',189,22,210,'+',208,' ',131,12,146,14,',','g',255,'}',201,
-192,142,17,29,'(','A',159,24,'l',160,255,207,3,219,'e',193,186,'$',127,241,
-'q',251,'r','}',186,'}',216,222,'U',169,165,204,222,183,'R','J',']',163,188,
-253,190,139,252,'u',171,'e',206,18,17,189,205,'m','_',')',177,']',254,'z',
-223,177,190,249,'z','{',190,'>',']','.',219,243,199,'O',15,'_',247,179,191,
-255,'k',251,'.','h',179,231,'>','z',221,'#',175,'?',231,'^',10,177,'^',219,
-':',188,172,239,'K',15,223,160,246,'o',175,250,253,211,'_',127,255,191,196,
-255,8,253,0,231,185,29,215,255,'x',215,247,'x','x',253,175,'=',218,221,245,
-'?','j',31,'\\',255,31,'\\',255,'[','o','j','}','E',233,'?',174,255,'Q',202,
-'X','u',212,213,212,'M',194,'~',167,213,'J',31,226,191,197,'\\','e',138,245,
-22,163,'/',181,158,27,161,182,162,'G',12,181,21,'}',214,170,182,'"','G',29,
-'w','[',177,175,143,'Y',213,156,'3','c','Q','s',206,209,170,154,'s',213,':',
-139,'Z',207,157,'-',230,220,227,157,'b',206,154,'{','-',196,156,185,15,218,
-20,'s',214,',','=',196,156,'5',223,'s',138,'9','k',180,213,196,156,'5','V',
-30,'O',177,190,'G',161,230,'l','+',214,'}',21,175,199,191,246,'V',155,154,
-183,207,181,212,188,'#','f','S',243,142,'c',171,239,215,'g','4','U','w',157,
-'3','T',221,'G',196,'j',191,230,'f',23,'1','g',228,';','w','1','g',148,172,
-'H',204,25,181,198,16,'s','F','~','F','T',191,217,196,'R',253,230,185,'j',
-170,'~',143,143,147,154,'3',15,'O','U','s',246,220,0,'5','g',238,132,'P',
-'s',246,'5',167,154,'s',180,161,250,141,177,218,'}',191,223,143,127,30,205,
-'P',29,31,31,127,'5',239,218,191,212,250,'<','6',227,199,245,150,19,'7','1',
-'o','+','3',255,145,'X',175,'Q','U',199,'-',247,210,'}',199,251,233,168,'N',
-213,239,'q',154,18,'s',182,204,189,171,'9','s',247,21,'5','g',198,219,213,
-156,'=',207,130,'j',206,145,225,169,'9',247,'U','5','g','^',247,'T',191,'/',
-167,211,251,245,181,134,154,'3',15,'s','U','s',230,'^',27,15,142,127,223,
-247,136,152,'7','?','<','U','u',220,'3','z',213,'q',207,15,180,234,248,'8',
-253,139,'y','{',134,'7',197,188,'=','s',12,177,'_',243,206,' ',239,'"',196,
-'z',207,'3',134,154,'3','?',133,170,223,'>',242,'D',172,230,28,'#','T',191,
-199,'e','J',205,'9','3','/','5','g','~','l',154,154,'s','e','0','b',206,177,
-167,'\'',230,28,185,'G','U',191,251,177,'W',253,142,'<',209,171,'~',143,203,
-233,131,227,'?',242,196,'t',127,215,176,175,175,'P',247,5,'#','s','Q',247,
-5,'#',195,'T',247,5,'#',15,180,234,'8','O',218,']','u',156,135,161,169,142,
-143,203,191,154,'s',238,'W',0,181,190,127,137,245,227,'f',232,205,'z',145,
-'7','F',248,'%','<',191,195,'A','?','p',208,15,28,244,3,7,253,192,'A','?',
-'p',184,253,208,31,28,244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
-193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
-28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
-193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
-28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
-193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
-28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
-193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
-28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
-193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
-28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
-193,243,'?',206,'D','?','p',208,15,28,244,227,249,252,247,231,'?','o','_',
-174,'O',183,15,239,247,30,165,150,'2','{',223,'J',')','u',141,242,246,251,
-139,173,150,'9','K','D',244,'6',243,245,'5',127,218,'.',229,253,'F',250,238,
-235,237,249,250,'t',185,'l',207,31,'?','=','|',221,207,254,14,0,0,0,0,0,0,
-0,255,'1',255,0,178,'s',140,'2',0,240,0,0};
-
-/* Old GNU tar sparse format, as created by gtar 1.17 */
-static unsigned char archive_old_gtar_1_17[] = {
-31,139,8,0,30,'%',193,'F',0,3,237,215,']','r',19,'G',20,134,'a','e','\'',
-218,'@',170,186,'O',255,'-','$','+',208,5,23,'\\','@','(',203,236,'?','g',
-134,216,'8',232,139,160,248,'P','M',170,242,'>',20,'%',211,'6',214,153,158,
-'W','#',205,245,211,229,233,250,238,244,'P','%',205,222,183,199,186,'F','y',
-251,184,137,252,'{',170,'e',206,18,17,189,205,'S','~','w',197,'<',157,255,
-'x',236,'X','_','|',190,'>','_',158,206,231,211,243,251,15,'w',127,238,'{',
-223,255,'i',219,22,180,217,235,182,11,127,239,200,235,215,185,'K','!',214,
-'k',255,242,239,151,245,253,235,'{','O',240,250,31,'~',185,203,175,255,149,
-248,31,161,159,'c',']',247,235,127,'<',244,'9',238,'^',255,'k',143,'V',234,
-'?',175,255,17,'5',127,156,235,255,191,'^',255,'[',235,'M',173,175,'(',253,
-219,245,223,'J',25,171,142,186,182,'m','V',207,158,251,223,135,248,'m','1',
-'W',153,'b',189,197,232,'K',173,231,'A',168,163,232,17,'C',29,'E',159,181,
-170,163,200,'Q',199,205,'Q','l',235,'c','V','5',231,172,'}',168,'9',231,'h',
-'U',205,185,'j',157,'E',173,231,'f',139,'9',243,'a','N','1','g',205,']',11,
-'1','g',238,'A',155,'b',206,154,165,135,152,179,230,'s','N','1','g',141,182,
-154,152,179,198,202,243,')',214,183,'(',212,156,'m',197,186,173,226,245,252,
-215,222,'j','S',243,246,185,150,154,'w',196,'l','j',222,177,31,245,237,250,
-140,166,234,174,'s',134,170,'{',143,'X',237,'k',30,'v',17,'s','F','>','s',
-23,'s','F',201,138,196,156,'Q','k',12,'1','g',228,'k','D',245,155,'M',',',
-213,'o','^',171,166,234,'w',127,'9',169,'9',243,244,'T','5','g',207,3,'P',
-'s',230,'&',132,154,179,175,'9',213,156,163,13,213,'o',140,213,'n',251,253,
-'z',254,243,'l',134,234,'x',127,249,171,'y',215,246,'G',173,207,253,'0',190,
-']','o','9','q',19,243,182,'2',243,23,137,245,26,'U','u',220,'r',151,'n',
-';',222,'.','G','u',170,'~',247,203,148,152,179,'e',238,']',205,153,219,'W',
-212,156,25,'o','W','s',246,188,10,170,'9','G',134,167,230,220,'V',213,156,
-249,190,167,250,'}',185,156,222,174,175,'5',212,156,'y',154,171,154,'3','w',
-'m',220,'9',255,'}',219,17,'1','o',190,'x',170,234,184,'g',244,170,227,158,
-'/','h',213,241,'~',249,23,243,246,12,'o',138,'y','{',230,24,'b','_',243,
-147,'A','~',138,16,235,'=',175,24,'j',206,'|',21,170,'~',251,200,11,177,154,
-'s',140,'P',253,238,'o','S','j',206,153,'y',169,'9',243,'e',211,212,156,'+',
-131,17,'s',142,'-','=','1',231,200,29,'U',253,'n',231,'^',245,';',242,'B',
-175,250,221,223,'N',239,156,255,145,23,166,219,'O',13,219,250,10,245,185,
-'`','d','.',234,'s',193,200,'0',213,231,130,145,'\'','Z','u',156,23,237,174,
-':',206,211,208,'T',199,251,219,191,154,'s','n',239,0,'j','}',251,'#',214,
-247,15,'C','o',214,139,252,'`',132,31,194,253,27,28,244,3,7,253,192,'A','?',
-'p',208,15,28,244,3,135,219,15,253,193,'A','?','p',208,15,28,244,3,7,253,
-192,'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,
-15,28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
-'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
-28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
-'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
-28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
-'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
-28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
-'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
-28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
-'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?',158,143,127,
-'~',252,253,250,233,242,'t','}',247,184,231,'(','i',246,190,'=',214,'5',202,
-219,199,23,167,'Z',230,',',17,209,219,'<',149,'Z','#',234,233,'\\',30,'7',
-210,'W',159,175,207,151,167,243,249,244,252,254,195,221,159,251,222,247,1,
-0,0,0,0,0,0,0,248,15,249,11,162,'$',218,227,0,240,0,0};
-
-#if ARCHIVE_VERSION_STAMP >= 1009000
-/* libarchive < 1.9 does not support this. */
-/* GNU tar "posix" sparse format 0.0, as created by gtar 1.17 */
-static unsigned char archive_0_0_gtar_1_17[] = {
-31,139,8,0,31,'%',193,'F',0,3,237,217,207,'n',218,'X',20,199,'q',214,'<',
-5,'/','0',228,222,'s','}',255,'x',193,'z',186,26,'u',211,7,240,164,174,20,
-205,'$',169,'0',145,'2',243,244,'5','%',205,144,200,193,'p',14,141,203,232,
-251,217,'P','A',14,'8','9',191,'[',253,',',150,'W',31,155,199,15,'m',243,
-185,']','w',203,232,156,148,171,238,'k',179,238,218,217,249,184,'^',170,170,
-237,163,207,209,237,'?','~','W','9',153,'y',151,146,19,145,'*',228,153,243,
-161,'J','2','[','<',158,241,26,222,244,208,'m',154,'u',127,')',214,247,'y',
-250,']',158,31,'/',132,228,197,239,127,'|','Z',238,'v',190,236,'n',254,'m',
-'W',193,'W','1','K',153,'K',218,127,233,238,225,246,207,191,239,175,255,234,
-'V','a','.','e',255,149,251,'/','_',186,'v',179,170,'{','!',205,'_',190,225,
-'v',234,159,'M',219,173,162,151,185,212,3,'c',190,31,'+','Y','N',158,'{',
-190,202,'8','8',231,230,226,22,205,230,230,182,']','y','_',178,'K',193,'e',
-191,'}',238,250,229,'s','n','>',245,6,166,'u',246,195,'>','`',228,252,203,
-246,184,252,'w',254,'S',127,254,'}',14,'a',182,'x',151,'C',244,227,252,247,
-177,'8',248,'s','c',175,'_',232,249,183,'j',166,190,0,'\\','4',242,'3',173,
-229,'[',253,'O',206,247,25,135,255,255,247,193,247,157,239,'U',255,139,'1',
-210,255,222,195,203,'*',247,189,255,213,245,'n','/',3,149,'l','W',0,235,250,
-151,'h',128,178,157,'s',229,244,230,216,207,229,170,':','y',174,234,231,'R',
-'q','\'',207,197,237,156,'?',253,239,146,250,185,24,'O',255,187,148,']',2,
-'O',159,'S',238,175,30,223,'_','p','C','{','w',227,11,28,30,244,227,27,28,
-30,148,241,21,14,15,134,241,29,14,15,'V',227,'K',28,30,'L',227,'[','|','c',
-'p','|',141,195,131,'Y',187,199,162,221,'c',173,220,163,'8',229,30,197,'+',
-247,'(',162,220,163,'T',202,'=','J',165,220,163,'D',229,30,'%',')',247,'(',
-'Y',187,199,162,221,'c',173,220,'c','p',202,'=',6,'Q',238,'1',136,'r',143,
-'!','(',247,24,'*',229,30,'C','T',238,'1','$',229,30,'C',214,238,177,'(',
-247,248,'t',28,'O',191,212,202,')',247,'X',29,209,'o',134,7,143,'(','8',195,
-131,'G','4',156,225,193,'#','*',206,240,224,17,29,'g','x',240,136,146,'3',
-'<','x','D',203,'y','c','P',187,'G','m',207,137,218,158,19,181,'=','\'','j',
-'{','N',212,246,156,168,237,'9','Q',219,'s',162,182,231,'D','m',207,137,218,
-158,19,181,'=','\'','i','{','N',210,246,156,164,237,'9','I',219,'s',146,182,
-231,'$','m',207,'I',218,158,147,180,'=','\'','i','{','N',210,246,156,172,
-237,'9','Y',219,'s',178,182,231,'d','m',207,201,218,158,147,181,'=','\'',
-'k','{','N',214,246,156,172,237,'9','E',219,'s',138,182,231,20,'m',207,')',
-218,158,'S',180,'=',167,'h','{','N',209,246,156,162,237,'9','E',219,'s',138,
-182,231,20,'m',207,169,181,'=',167,214,246,156,'Z',219,'s','j','m',207,169,
-181,'=',167,214,246,156,'Z',219,'s','j','m',207,169,'G','z',142,175,3,'_',
-174,255,'_',236,150,'{',198,'/','{',6,28,252,254,199,'W',18,156,127,245,253,
-191,'8','I','|',255,127,9,248,254,22,22,228,7,22,228,7,22,228,7,22,228,7,
-22,228,7,22,214,252,144,'?','X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',
-144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,
-31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,
-'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',
-144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,
-31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,
-'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',
-145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,
-143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',
-'p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,
-31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,
-'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',
-144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,155,229,
-213,199,230,241,'C',219,'|','n',215,221,'2',':','\'',229,234,238,254,238,
-183,238,'k',179,238,218,'3','}',134,235,165,170,218,'>',250,28,221,254,227,
-'N',255,'o',239,'R','r','"','R',133,'<','s',190,146,232,'g',139,199,'3','}',
-254,'A',15,221,166,'Y',247,151,'b','}',159,167,'_',229,249,241,'B',136,'[',
-'4',155,155,219,'v',229,'}',201,'.',5,151,221,188,127,238,250,245,'s','S',
-'_','\'','~',142,179,31,246,1,163,231,223,237,159,255,212,159,127,137,210,
-159,255,'w','9','D','?',206,127,31,248,131,'?','7',246,250,133,158,127,0,
-0,0,0,0,0,0,0,0,0,0,0,0,192,'e',250,6,'X',180,13,'8',0,24,1,0};
-#endif
-
-#if ARCHIVE_VERSION_STAMP >= 1009000
-/* libarchive < 1.9 does not support this. */
-/* GNU tar "posix" sparse format 0.1, as created by gtar 1.17 */
-static unsigned char archive_0_1_gtar_1_17[] = {
-31,139,8,0,31,'%',193,'F',0,3,237,215,205,'n',26,'W',24,135,'q',214,'\\',
-5,23,224,194,249,'>','3',11,182,'m','V','U',164,170,23,'0','u','f','a','%',
-'v',',',198,150,172,'^','}',135,15,'\'',127,187,9,'T','z',137,167,'D',207,
-'o',195,4,'l','^','0',207,'!',231,',','W',239,187,167,'w','}',247,161,223,
-12,203,236,'\\',244,171,225,190,219,12,253,236,'|',220,168,164,180,189,245,
-'5',';',189,221,9,'9',204,188,'+',197,133,16,'R',172,'3',231,'c',202,'u',
-182,'x',':',227,'k',248,174,199,225,161,219,140,'/',197,250,'<',135,247,242,
-229,246,'B',132,186,248,237,247,'?',151,251,207,'|','9',220,252,221,175,163,
-31,255,250,161,153,135,162,15,221,'=',222,254,245,233,243,245,199,'a',29,
-'_','?',210,221,246,235,253,245,'<','{','}',228,182,187,'_',183,163,'X',174,
-178,15,'W','~',188,'l','j',216,']',31,134,'\\',185,'y','p',139,238,225,'f',
-'|',10,239,155,234,'J','t',213,'o',239,187,'~','y',159,155,'O',253,151,250,
-'9','-','W',227,231,245,199,238,227,250,245,230,'S',255,'C',190,2,'N',172,
-255,176,']','.','_',215,127,25,215,127,246,169,204,22,'o',178,136,158,215,
-255,152,219,209,159,';',245,248,133,174,127,171,'n',234,23,128,139,'F','?',
-211,'Z','~','o',255,23,206,'7',227,212,247,127,241,178,255,'K','n',187,255,
-203,'1',179,255,'{',11,161,249,215,254,175,'m','w',239,'`',220,133,213,'o',
-'o',0,219,246,245,'C','_','w',128,'a',238,'C','[',254,227,'&','0','l',175,
-']',179,223,16,142,215,'5',165,221,'u',26,175,'K',227,'v',215,'y','{',237,
-247,191,'[',198,235,156,247,191,219,236,255,206,251,'k','y',254,'V',158,223,
-';',25,224,189,'L',240,'A','F',248,'(','3','|',146,'!',190,200,20,'_',244,
-'m','T',157,211,232,156,'V',230,4,'\'','s',130,151,'9','!',200,156,144,'d',
-'N','H','2','\'','d',253,'{',21,153,19,170,206,'i','t','N','+','s',162,147,
-'9','1',200,156,24,'d','N',140,186,'9','O',250,193,'d',153,19,139,204,137,
-'U',231,'4','2',231,240,'v',220,225,31,'2','\'',233,231,159,'4',128,244,162,
-0,'M',' ','i',3,'I','#','H','Z','A',210,12,146,'v',144,181,131,172,29,'d',
-237,' ','k',7,'Y',';',200,218,'A',214,14,178,'v',144,181,131,172,29,20,237,
-160,'h',7,'E',';','(',218,'A',209,14,138,'v','P',180,131,162,29,20,237,160,
-'h',7,'U',';',168,218,'A',213,14,170,'v','P',181,131,170,29,'T',237,160,'j',
-7,'U',';','h',180,131,'F',';','h',180,131,'F',';','h',180,131,'F',';','h',
-180,131,230,197,151,193,139,'o',3,237,160,209,14,'Z',237,160,213,14,'Z',237,
-160,213,14,'Z',237,160,213,14,'Z',237,160,213,14,218,231,14,'|',27,255,231,
-231,219,'c',231,191,'s','m',1,142,254,255,239,'S',136,206,191,'>',255,133,
-228,'9',255,']',2,246,239,176,160,31,'X',208,15,',',232,7,22,244,3,11,250,
-129,133,181,31,250,131,5,253,192,130,'~','`','A','?',176,160,31,'X',208,15,
-',',232,7,22,156,255,'1','%',250,129,5,253,192,130,'~','`','A','?',176,160,
-31,'X',208,15,',','8',255,'c','J',244,3,11,250,129,5,253,192,130,'~','`',
-'A','?',176,160,31,'X','p',254,199,148,232,7,22,244,3,11,250,129,5,253,192,
-130,'~','`','A','?',176,224,252,143,')',209,15,',',232,7,22,244,3,11,250,
-129,5,253,192,130,'~','`',193,249,31,'S',162,31,'X',208,15,',',232,7,22,244,
-3,11,250,129,5,253,192,130,243,'?',166,'D','?',176,160,31,'X',208,15,',',
-232,7,22,244,3,11,250,129,5,231,127,'L',137,'~','`','A','?',176,160,31,'X',
-208,15,',',232,7,22,244,3,11,206,255,152,18,253,192,130,'~','`','A','?',176,
-160,31,'X',208,15,',',232,7,22,156,255,'1','%',250,129,5,253,192,130,'~',
-'`','A','?',176,160,31,'X',208,15,',','8',255,'c','J',244,3,11,250,129,5,
-253,192,130,'~','`','A','?',176,160,31,'X','p',254,199,148,232,7,22,244,3,
-11,250,177,'Y',174,222,'w','O',239,250,238,'C',191,25,150,217,185,232,'W',
-'w',159,239,'~',25,238,187,205,208,159,'i',134,27,149,148,182,183,190,'f',
-167,183,'{',227,181,'w',165,184,16,'B','J',227,253,'>',133,152,'g',139,167,
-'3',205,'?',234,'q','x',232,'6',227,'K',177,'>',207,225,173,'|',185,189,16,
-193,'-',186,135,155,219,'~',237,'}','S',']',137,174,186,249,'x',223,245,235,
-251,166,'~',157,248,'1',206,190,216,191,225,228,250,'w',178,254,'c',25,215,
-127,200,193,207,22,'o',178,136,158,215,255,24,252,209,159,';',245,248,133,
-174,127,0,0,0,0,0,240,243,251,7,233,'Q','N','O',0,240,0,0};
-#endif
-
-#if ARCHIVE_VERSION_STAMP >= 1009000
-/* libarchive < 1.9 does not support this. */
-/* GNU tar "posix" sparse format 1.0, as created by gtar 1.17 */
-static unsigned char archive_1_0_gtar_1_17[] = {
-31,139,8,0,' ','%',193,'F',0,3,237,215,205,'n',26,'I',20,134,'a',214,'\\',
-5,'7',16,168,255,234,'^','x',155,'d',21,'E',138,230,2,'Z','I','/',24,197,
-'N',4,142,'d',205,213,'O',1,182,245,217,178,'A',163,'C',220,131,242,'>',155,
-'n',183,'m',14,'?','o',161,174,229,234,243,'p',247,'q',28,190,141,155,237,
-'2',';',23,211,'j',251,'s',216,'l',199,217,249,184,166,164,180,';',250,154,
-157,30,247,130,207,'3',239,'J','q','!',132,148,218,'u',31,'S',142,179,197,
-221,25,159,195,171,'~','m','o',135,'M','{','*',214,199,185,127,'-',143,199,
-11,17,194,226,195,167,191,150,135,207,'|','y','=',252,253,'c','s',229,231,
-207,174,174,'o',218,'U','7',15,'E',175,222,12,215,227,213,225,'|',30,189,
-254,'f','3',14,223,183,235,127,198,171,232,'S',174,161,155,7,183,24,'n',215,
-237,207,189,239,170,'+',209,'U',191,187,246,245,233,'5','7',159,250,205,248,
-3,'-','W',237,131,251,178,255,220,222,175,191,143,191,229,'+',224,196,250,
-143,187,229,242,184,254,'c','i',235,'?',251,28,'f',139,'7','Y','D',15,235,
-191,181,'x',244,239,'N',253,254,'B',215,127,156,247,'M',',',243,236,195,220,
-183,211,174,134,253,249,195,218,'e','U',226,136,'a',234,'\'',128,139,'F',
-'?',211,'Z',190,'v',255,31,206,'7',227,244,253,127,'}','~',255,159,'c',229,
-254,255,'-',252,167,251,255,250,202,253,127,187,'[',8,'/','n',0,250,'~',255,
-'f',248,23,'v',0,129,29,192,255,193,177,251,255,'s','}',5,28,']',255,237,
-'6','3',':',255,252,254,'?',164,202,253,255,'[',232,251,215,'6',0,'a','w',
-238,186,195,'f',160,157,215,148,246,231,169,157,151,206,237,207,243,238,220,
-31,254,183,180,243,156,15,255,219,29,222,132,195,185,'<','~','/',143,239,
-157,12,240,'^','&',248,' ','#','|',148,25,'>',201,16,'_','d',138,'/',250,
-'2',170,206,233,'t','N','/','s',130,147,'9',193,203,156,16,'d','N','H','2',
-'\'','$',153,19,178,190,'_','E',230,132,170,'s',':',157,211,203,156,232,'d',
-'N',12,'2','\'',6,153,19,163,'n',204,146,'~','0','Y',230,196,'"','s','b',
-213,'9',157,204,185,127,'9',238,254,7,153,147,244,243,'O',26,'@','z','R',
-128,'&',144,180,129,164,17,'$',173,' ','i',6,'I',';',200,218,'A',214,14,178,
-'v',144,181,131,172,29,'d',237,' ','k',7,'Y',';',200,218,'A',214,14,138,'v',
-'P',180,131,162,29,20,237,160,'h',7,'E',';','(',218,'A',209,14,138,'v','P',
-180,131,170,29,'T',237,160,'j',7,'U',';',168,218,'A',213,14,170,'v','P',181,
-131,170,29,'t',218,'A',167,29,'t',218,'A',167,29,'t',218,'A',167,29,'t',218,
-'A',247,228,203,224,201,183,129,'v',208,'i',7,189,'v',208,'k',7,189,'v',208,
-'k',7,189,'v',208,'k',7,189,'v',208,'k',7,253,'C',7,190,143,220,'o','X',177,
-127,131,5,253,192,130,'~','`','A','?',176,160,31,'X',208,15,',',172,253,208,
-31,',',232,7,22,244,3,11,250,129,5,253,192,130,'~','`','A','?',176,'`',255,
-143,')',209,15,',',232,7,22,244,3,11,250,129,5,253,192,130,'~','`',193,254,
-31,'S',162,31,'X',208,15,',',232,7,22,244,3,11,250,129,5,253,192,130,253,
-'?',166,'D','?',176,160,31,'X',208,15,',',232,7,22,244,3,11,250,129,5,251,
-127,'L',137,'~','`','A','?',176,160,31,'X',208,15,',',232,7,22,244,3,11,246,
-255,152,18,253,192,130,'~','`','A','?',176,160,31,'X',208,15,',',232,7,22,
-236,255,'1','%',250,129,5,253,192,130,'~','`','A','?',176,160,31,'X',208,
-15,',',216,255,'c','J',244,3,11,250,129,5,253,192,130,'~','`','A','?',176,
-160,31,'X',176,255,199,148,232,7,22,244,3,11,250,129,5,253,192,130,'~','`',
-'A','?',176,'`',255,143,')',209,15,',',232,7,22,244,3,11,250,129,5,253,192,
-130,'~','`',193,254,31,'S',162,31,'X',208,15,',',232,7,22,244,3,11,250,129,
-5,253,192,130,253,'?',166,'D','?',176,160,31,'X',208,143,205,'r',245,'y',
-184,251,'8',14,223,198,205,'v',153,157,139,'i','u',243,227,230,221,246,231,
-176,217,142,'g',154,225,154,146,210,238,232,'k','v','z','<','h',231,222,149,
-226,'B',8,')',181,235,'>',133,'v','X',220,157,'i',254,'Q',191,182,183,195,
-166,'=',21,235,227,220,191,148,199,227,133,8,'n','1',220,174,175,199,'+',
-239,187,234,'J','t',213,205,219,181,175,207,175,'M',253,'<',241,'{',156,'}',
-177,191,224,228,250,'w',178,254,'c','i',235,'?',228,224,'g',139,'7','Y','D',
-15,235,191,5,127,244,239,'N',253,254,'B',215,'?',0,0,0,128,'?',199,191,200,
-'e','(',171,0,240,0,0};
-#endif
-
-
-/*
- * The following test archive is a little odd.  First, it's uncompressed,
- * because that exercises some of the block reassembly code a little harder.
- * Second, it includes some leading comments prior to the sparse block
- * description.  GNU tar doesn't do this, but I think it should, so I
- * want to ensure that libarchive correctly ignores such comments.
- */
-#if ARCHIVE_VERSION_STAMP >= 1009000
-
-/* Because it's uncompressed, I've made this archive a bit simpler. */
-struct archive_contents files_1_0b[] = {
-	{ "sparse", archive_contents_sparse },
-	{ "non-sparse", archive_contents_nonsparse },
-	{ NULL, NULL }
-};
-
-static unsigned char archive_1_0b[] = {
-'.','/','P','a','x','H','e','a','d','e','r','s','.','7','5','4','7','/','s',
-'p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7',
-'5','0',0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','0','2',
-'1','5',0,'1','0','6','5','7','4','5','4','6','1','3',0,'0','1','3','4','2',
-'5',0,' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s',
-'t','a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,'2','2',' ','G','N','U','.','s','p','a','r','s',
-'e','.','m','a','j','o','r','=','1',10,'2','2',' ','G','N','U','.','s','p',
-'a','r','s','e','.','m','i','n','o','r','=','0',10,'2','6',' ','G','N','U',
-'.','s','p','a','r','s','e','.','n','a','m','e','=','s','p','a','r','s','e',
-10,'3','1',' ','G','N','U','.','s','p','a','r','s','e','.','r','e','a','l',
-'s','i','z','e','=','3','1','4','5','7','2','8',10,'2','0',' ','a','t','i',
-'m','e','=','1','1','8','6','8','7','9','7','9','9',10,'2','0',' ','c','t',
-'i','m','e','=','1','1','8','6','8','7','9','5','2','8',10,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'.','/','G',
-'N','U','S','p','a','r','s','e','F','i','l','e','.','7','5','4','7','/','s',
-'p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7','5','0',
-0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','3','0','0','0',
-0,'1','0','6','5','7','4','5','4','0','5','0',0,'0','1','5','1','1','2',0,
-' ','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a',
-'r',0,'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-
-/* 32 added bytes containing extra comments at beginning of sparse block */
-'#','!','g','n','u','-','s','p','a','r','s','e','-','f','o','r','m','a',
-'t',10,'#','f','o','r','m','a','t',':','1','.','0',10,
-
-'3',10,'9','9','9','9','3','6',10,'5','1','2',
-10,'1','9','9','9','8','7','2',10,'5','1','2',10,'3','1','4','5','7','2',
-'8',10,'0',10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-
-/* 32 removed bytes to preserve alignment. */
-/* 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, */
-0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'a',0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'a',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'.','/','P','a',
-'x','H','e','a','d','e','r','s','.','7','5','4','7','/','n','o','n','-','s',
-'p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7','5','0',
-0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','0','0','5','0',
-0,'1','0','6','5','7','4','5','4','6','1','3',0,'0','1','4','2','1','2',0,
-' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a',
-'r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
-'0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,'2','0',' ','a','t','i','m','e','=','1','1','8','6','8','7',
-'9','7','9','9',10,'2','0',' ','c','t','i','m','e','=','1','1','8','6','8',
-'7','9','5','4','4',10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,'n','o','n','-','s','p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7','5','0',0,'0','0','0',
-'1','7','5','0',0,'0','0','0','0','0','0','0','0','0','0','1',0,'1','0','6',
-'5','7','4','5','4','0','7','0',0,'0','1','2','5','3','1',0,' ','0',0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,'0','0',
-'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'t',
-'i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
-'0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,'a',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
-#endif
-
 /*
  * A tricky piece of code that verifies the contents of a sparse
  * archive entry against a description as defined at the top of this
@@ -795,29 +178,24 @@
  */
 #define min(a,b) ((a) < (b) ? (a) : (b))
 
-/*
- * A convenience wrapper that adds the size of the buffer and the
- * name of the buffer to any call.
- */
-#define verify_archive(buffer, contents)	\
-	_verify_archive(buffer, sizeof(buffer), #buffer, contents)
-
 static void
-_verify_archive(void *buffer, size_t length, const char *name,
-    struct archive_contents *ac)
+verify_archive_file(const char *name, struct archive_contents *ac)
 {
 	struct archive_entry *ae;
-	struct archive *a;
 	int err;
 	/* data, size, offset of next expected block. */
 	struct contents expect;
 	/* data, size, offset of block read from archive. */
 	struct contents actual;
+	struct archive *a;
+
+	extract_reference_file(name);
 
 	assert((a = archive_read_new()) != NULL);
 	assert(0 == archive_read_support_compression_all(a));
 	assert(0 == archive_read_support_format_tar(a));
-	assert(0 == read_open_memory(a, buffer, length, 3));
+	failure("Can't open %s", name);
+	assert(0 == archive_read_open_filename(a, name, 3));
 
 	while (ac->filename != NULL) {
 		struct contents *cts = ac->contents;
@@ -831,7 +209,7 @@
 				 (const void **)&actual.d,
 				 &actual.s, &actual.o))) {
 			while (actual.s > 0) {
-				char c = *(char *)actual.d;
+				char c = *(const char *)actual.d;
 				if(actual.o < expect.o) {
 					/*
 					 * Any byte before the expected
@@ -892,35 +270,51 @@
 #endif
 }
 
+
 DEFINE_TEST(test_read_format_gtar_sparse)
 {
-	/*
-	   FILE *t = fopen("archive_1_0.tgz", "w");
-	   fwrite(archive_1_0, sizeof(archive_1_0), 1, t);
-	   fclose(t);
-	*/
-
-	verify_archive(archive_old_gtar_1_13, files);
+	/* Two archives that use the "GNU tar sparse format". */
+	verify_archive_file("test_read_format_gtar_sparse_1_13.tgz", files);
+	verify_archive_file("test_read_format_gtar_sparse_1_17.tgz", files);
 
 	/*
-	 * libarchive < 1.9 doesn't support the newer sparse formats
-	 * from GNU tar 1.15 and 1.16.
+	 * libarchive < 1.9 doesn't support the newer --posix sparse formats
+	 * from GNU tar 1.15 and later.
 	 */
 #if ARCHIVE_VERSION_STAMP < 1009000
-	skipping("read support for GNUtar sparse format 0.0");
+	skipping("read support for GNUtar --posix sparse formats");
 #else
-	verify_archive(archive_0_0_gtar_1_17, files);
-#endif
-#if ARCHIVE_VERSION_STAMP < 1009000
-	skipping("read support for GNUtar sparse format 0.1");
-#else
-	verify_archive(archive_0_1_gtar_1_17, files);
-#endif
-#if ARCHIVE_VERSION_STAMP < 1009000
-	skipping("read support for GNUtar sparse format 1.0");
-#else
-	verify_archive(archive_1_0_gtar_1_17, files);
-	verify_archive(archive_1_0b, files_1_0b);
+	/*
+	 * An archive created by GNU tar 1.17 using --posix --sparse-format=0.1
+	 */
+	verify_archive_file(
+		"test_read_format_gtar_sparse_1_17_posix00.tgz",
+		files);
+	/*
+	 * An archive created by GNU tar 1.17 using --posix --sparse-format=0.1
+	 */
+	verify_archive_file(
+		"test_read_format_gtar_sparse_1_17_posix01.tgz",
+		files);
+	/*
+	 * An archive created by GNU tar 1.17 using --posix --sparse-format=1.0
+	 */
+	verify_archive_file(
+		"test_read_format_gtar_sparse_1_17_posix10.tgz",
+		files);
+	/*
+	 * The last test archive here is a little odd.  First, it's
+	 * uncompressed, because that exercises some of the block
+	 * reassembly code a little harder.  Second, it includes some
+	 * leading comments prior to the sparse block description.
+	 * GNU tar doesn't do this, but I think it should, so I want
+	 * to ensure that libarchive correctly ignores such comments.
+	 * Dump the file, looking for "#!gnu-sparse-format" starting
+	 * at byte 0x600.
+	 */
+	verify_archive_file(
+		"test_read_format_gtar_sparse_1_17_posix10_modified.tar",
+		files);
 #endif
 }
 
--- /dev/null
+++ lib/libarchive/test/test_write_format_tar_ustar.c
@@ -0,0 +1,346 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_write_format_tar_ustar.c,v 1.1.2.2 2008/08/25 01:55:40 kientzle Exp $");
+
+static int
+is_null(const char *p, size_t l)
+{
+	while (l > 0) {
+		if (*p != '\0')
+			return (0);
+		--l;
+		++p;
+	}
+	return (1);
+}
+
+/* Verify the contents, then erase them to NUL bytes. */
+/* Tar requires all "unused" bytes be set to NUL; this allows us
+ * to easily verify that by invoking is_null() over the entire header
+ * after verifying each field. */
+#define myAssertEqualMem(a,b,s) assertEqualMem(a, b, s); memset(a, 0, s)
+
+/*
+ * Detailed verification that 'ustar' archives are written with
+ * the correct format.
+ */
+DEFINE_TEST(test_write_format_tar_ustar)
+{
+	struct archive *a;
+	struct archive_entry *entry;
+	char *buff, *e;
+	size_t buffsize = 100000;
+	size_t used;
+	int i;
+	char f99[100];
+	char f100[101];
+	char f256[257];
+
+	for (i = 0; i < 99; ++i)
+		f99[i] = 'a' + i % 26;
+	f99[99] = '\0';
+
+	for (i = 0; i < 100; ++i)
+		f100[i] = 'A' + i % 26;
+	f100[100] = '\0';
+
+	for (i = 0; i < 256; ++i)
+		f256[i] = 'A' + i % 26;
+	f256[155] = '/';
+	f256[256] = '\0';
+
+	buff = malloc(buffsize);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertEqualIntA(a, 0, archive_write_set_format_ustar(a));
+	assertEqualIntA(a, 0, archive_write_set_compression_none(a));
+	assertEqualIntA(a, 0, archive_write_open_memory(a, buff, buffsize, &used));
+
+	/*
+	 * Add various files to it.
+	 * TODO: Extend this to cover more filetypes.
+	 */
+
+	/* "file" with 10 bytes of content */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, "file");
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_uid(entry, 80);
+	archive_entry_set_gid(entry, 90);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 89);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	assertEqualIntA(a, 10, archive_write_data(a, "1234567890", 10));
+
+	/* Hardlink to "file" with 10 bytes of content */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, "linkfile");
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	/* TODO: Put this back and fix the bug. */
+	/* archive_entry_set_size(entry, 10); */
+	archive_entry_set_uid(entry, 80);
+	archive_entry_set_gid(entry, 90);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 89);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	/* Write of data to dir should fail == zero bytes get written. */
+	assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
+
+	/* "dir" */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 2, 20);
+	archive_entry_set_pathname(entry, "dir");
+	archive_entry_set_mode(entry, S_IFDIR | 0775);
+	archive_entry_set_size(entry, 10);
+	archive_entry_set_nlink(entry, 2);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	/* Write of data to dir should fail == zero bytes get written. */
+	assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
+
+	/* "symlink" pointing to "file" */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 3, 30);
+	archive_entry_set_pathname(entry, "symlink");
+	archive_entry_set_mode(entry, S_IFLNK | 0664);
+	archive_entry_set_symlink(entry,"file");
+	archive_entry_set_size(entry, 0);
+	archive_entry_set_uid(entry, 88);
+	archive_entry_set_gid(entry, 98);
+	archive_entry_set_dev(entry, 12);
+	archive_entry_set_ino(entry, 90);
+	archive_entry_set_nlink(entry, 1);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+	/* Write of data to symlink should fail == zero bytes get written. */
+	assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10));
+
+	/* file with 99-char filename. */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, f99);
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 0);
+	archive_entry_set_uid(entry, 82);
+	archive_entry_set_gid(entry, 93);
+	archive_entry_set_dev(entry, 102);
+	archive_entry_set_ino(entry, 7);
+	archive_entry_set_nlink(entry, 1);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	/* file with 100-char filename. */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, f100);
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 0);
+	archive_entry_set_uid(entry, 82);
+	archive_entry_set_gid(entry, 93);
+	archive_entry_set_dev(entry, 102);
+	archive_entry_set_ino(entry, 7);
+	archive_entry_set_nlink(entry, 1);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+	/* file with 256-char filename. */
+	assert((entry = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(entry, 1, 10);
+	archive_entry_set_pathname(entry, f256);
+	archive_entry_set_mode(entry, S_IFREG | 0664);
+	archive_entry_set_size(entry, 0);
+	archive_entry_set_uid(entry, 82);
+	archive_entry_set_gid(entry, 93);
+	archive_entry_set_dev(entry, 102);
+	archive_entry_set_ino(entry, 7);
+	archive_entry_set_nlink(entry, 1);
+	assertEqualIntA(a, 0, archive_write_header(a, entry));
+	archive_entry_free(entry);
+
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(a);
+#else
+	assert(0 == archive_write_finish(a));
+#endif
+
+	/*
+	 * Verify the archive format.
+	 */
+	e = buff;
+
+	/* "file" */
+	myAssertEqualMem(e + 0, "file", 5); /* Filename */
+	myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000120 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000132 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000012 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "010034\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "0", 1); /* linkflag */
+	myAssertEqualMem(e + 157, "", 1); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, "", 1); /* prefix */
+	assert(is_null(e + 0, 512));
+	myAssertEqualMem(e + 512, "1234567890", 10);
+	assert(is_null(e + 512, 512));
+	e += 1024;
+
+	/* hardlink to "file" */
+	myAssertEqualMem(e + 0, "linkfile", 9); /* Filename */
+	myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000120 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000132 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "010707\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "0", 1); /* linkflag */
+	myAssertEqualMem(e + 157, "", 1); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, "", 1); /* prefix */
+	assert(is_null(e + 0, 512));
+	e += 512;
+
+	/* "dir" */
+	myAssertEqualMem(e + 0, "dir/", 4); /* Filename */
+	myAssertEqualMem(e + 100, "000775 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000000 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000000 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000002 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "007747\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "5", 1); /* typeflag */
+	myAssertEqualMem(e + 157, "", 1); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, "", 1); /* prefix */
+	assert(is_null(e + 0, 512));
+	e += 512;
+
+	/* "symlink" pointing to "file" */
+	myAssertEqualMem(e + 0, "symlink", 8); /* Filename */
+	myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000130 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000142 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000003 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "011446\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "2", 1); /* linkflag */
+	myAssertEqualMem(e + 157, "file", 5); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, "", 1); /* prefix */
+	assert(is_null(e + 0, 512));
+	e += 512;
+
+	/* File with 99-char filename */
+	myAssertEqualMem(e + 0, f99, 100); /* Filename */
+	myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000122 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000135 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "034242\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "0", 1); /* linkflag */
+	myAssertEqualMem(e + 157, "", 1); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, "", 1); /* prefix */
+	assert(is_null(e + 0, 512));
+	e += 512;
+
+	/* File with 100-char filename */
+	myAssertEqualMem(e + 0, f100, 100); /* Filename */
+	myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000122 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000135 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "026230\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "0", 1); /* linkflag */
+	myAssertEqualMem(e + 157, "", 1); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, "", 1); /* prefix */
+	assert(is_null(e + 0, 512));
+	e += 512;
+
+	/* File with 256-char filename */
+	myAssertEqualMem(e + 0, f256 + 156, 100); /* Filename */
+	myAssertEqualMem(e + 100, "000664 ", 8); /* mode */
+	myAssertEqualMem(e + 108, "000122 ", 8); /* uid */
+	myAssertEqualMem(e + 116, "000135 ", 8); /* gid */
+	myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */
+	myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */
+	myAssertEqualMem(e + 148, "055570\0 ", 8); /* checksum */
+	myAssertEqualMem(e + 156, "0", 1); /* linkflag */
+	myAssertEqualMem(e + 157, "", 1); /* linkname */
+	myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+	myAssertEqualMem(e + 265, "", 1); /* uname */
+	myAssertEqualMem(e + 297, "", 1); /* gname */
+	myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
+	myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */
+	myAssertEqualMem(e + 345, f256, 155); /* prefix */
+	assert(is_null(e + 0, 512));
+	e += 512;
+
+	/* TODO: Verify other types of entries. */
+
+	/* Last entry is end-of-archive marker. */
+	assert(is_null(e, 1024));
+	e += 1024;
+
+	assertEqualInt(used, e - buff);
+
+	free(buff);
+}
--- /dev/null
+++ lib/libarchive/test/test_compat_tar_hardlink_1.tar.uu
@@ -0,0 +1,39 @@
+$FreeBSD: src/lib/libarchive/test/test_compat_tar_hardlink_1.tar.uu,v 1.1.2.1 2008/02/11 00:31:08 kientzle Exp $
+begin 644 test_compat_tar_hardlink_1.tar
+M>&UC9"TS+C,N,B]D;V-S7V0O4D5!1$UF````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````"`@(#0T-"``("`S-#<S(``@("`@(#,@`"`@("`@("`@-3`Q
+M(#$P,#0Q-30U-#,U("`@-S8U-``@````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````!X>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'@`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````'AM8V0M,RXS+C(O
+M4D5!1$U%````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````@
+M("`T-#0@`"`@,S0W,R``("`@("`S(``@("`@("`@(#4P,2`Q,#`T,34T-30S
+M-2`@,3(R,#<`(#%X;6-D+3,N,RXR+V1O8W-?9"]214%$368`````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+&````````
+`
+end
--- /dev/null
+++ lib/libarchive/test/test_ustar_filenames.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_ustar_filenames.c,v 1.1.2.2 2008/08/25 01:55:40 kientzle Exp $");
+
+/*
+ * Exercise various lengths of filenames in ustar archives.
+ */
+
+static void
+test_filename(const char *prefix, int dlen, int flen)
+{
+	char buff[8192];
+	char filename[400];
+	char dirname[400];
+	struct archive_entry *ae;
+	struct archive *a;
+	size_t used;
+	int separator = 0;
+	int i = 0;
+
+	if (prefix != NULL) {
+		strcpy(filename, prefix);
+		i = strlen(prefix);
+	}
+	if (dlen > 0) {
+		for (; i < dlen; i++)
+			filename[i] = 'a';
+		filename[i++] = '/';
+		separator = 1;
+	}
+	for (; i < dlen + flen + separator; i++)
+		filename[i] = 'b';
+	filename[i++] = '\0';
+
+	strcpy(dirname, filename);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_set_bytes_per_block(a,0));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/*
+	 * Write a file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, filename);
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	failure("dlen=%d, flen=%d", dlen, flen);
+	if (flen > 100) {
+		assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
+	} else {
+		assertEqualIntA(a, 0, archive_write_header(a, ae));
+	}
+	archive_entry_free(ae);
+
+	/*
+	 * Write a dir to it (without trailing '/').
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, dirname);
+	archive_entry_set_mode(ae, S_IFDIR | 0755);
+	failure("dlen=%d, flen=%d", dlen, flen);
+	if (flen >= 100) {
+		assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
+	} else {
+		assertEqualIntA(a, 0, archive_write_header(a, ae));
+	}
+	archive_entry_free(ae);
+
+	/* Tar adds a '/' to directory names. */
+	strcat(dirname, "/");
+
+	/*
+	 * Write a dir to it (with trailing '/').
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, dirname);
+	archive_entry_set_mode(ae, S_IFDIR | 0755);
+	failure("dlen=%d, flen=%d", dlen, flen);
+	if (flen >= 100) {
+		assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
+	} else {
+		assertEqualIntA(a, 0, archive_write_header(a, ae));
+	}
+	archive_entry_free(ae);
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_write_finish(a);
+#else
+	assertEqualInt(0, archive_write_finish(a));
+#endif
+
+	/*
+	 * Now, read the data back.
+	 */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	if (flen <= 100) {
+		/* Read the file and check the filename. */
+		assertA(0 == archive_read_next_header(a, &ae));
+		failure("dlen=%d, flen=%d", dlen, flen);
+		assertEqualString(filename, archive_entry_pathname(ae));
+		assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
+	}
+
+	/*
+	 * Read the two dirs and check the names.
+	 *
+	 * Both dirs should read back with the same name, since
+	 * tar should add a trailing '/' to any dir that doesn't
+	 * already have one.
+	 */
+	if (flen <= 99) {
+		assertA(0 == archive_read_next_header(a, &ae));
+		assert((S_IFDIR | 0755) == archive_entry_mode(ae));
+		failure("dlen=%d, flen=%d", dlen, flen);
+		assertEqualString(dirname, archive_entry_pathname(ae));
+	}
+
+	if (flen <= 99) {
+		assertA(0 == archive_read_next_header(a, &ae));
+		assert((S_IFDIR | 0755) == archive_entry_mode(ae));
+		assertEqualString(dirname, archive_entry_pathname(ae));
+	}
+
+	/* Verify the end of the archive. */
+	failure("This fails if entries were written that should not have been written.  dlen=%d, flen=%d", dlen, flen);
+	assertEqualInt(1, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+	archive_read_finish(a);
+#else
+	assertEqualInt(0, archive_read_finish(a));
+#endif
+}
+
+DEFINE_TEST(test_ustar_filenames)
+{
+	int dlen, flen;
+
+	/* Try a bunch of different file/dir lengths that add up
+	 * to just a little less or a little more than 100 bytes.
+	 * This exercises the code that splits paths between ustar
+	 * filename and prefix fields.
+	 */
+	for (dlen = 5; dlen < 70; dlen += 5) {
+		for (flen = 100 - dlen - 5; flen < 100 - dlen + 5; flen++) {
+			test_filename(NULL, dlen, flen);
+			test_filename("/", dlen, flen);
+		}
+	}
+
+	/* Probe the 100-char limit for paths with no '/'. */
+	for (flen = 90; flen < 110; flen++) {
+		test_filename(NULL, 0, flen);
+		test_filename("/", dlen, flen);
+	}
+
+	/* XXXX TODO Probe the 100-char limit with a dir prefix. */
+	/* XXXX TODO Probe the 255-char total limit. */
+}
Index: test_entry.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_entry.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_entry.c -L lib/libarchive/test/test_entry.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_entry.c
+++ lib/libarchive/test/test_entry.c
@@ -23,7 +23,9 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_entry.c,v 1.2 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_entry.c,v 1.2.2.3 2008/11/28 20:08:47 kientzle Exp $");
+
+#include <locale.h>
 
 /*
  * Most of these tests are system-independent, though a few depend on
@@ -50,6 +52,8 @@
 	const void *xval; /* For xattr tests. */
 	size_t xsize; /* For xattr tests. */
 	int c;
+	wchar_t wc;
+	long l;
 
 	assert((e = archive_entry_new()) != NULL);
 
@@ -65,14 +69,25 @@
 	 * The following tests are ordered alphabetically by the
 	 * name of the field.
 	 */
+
 	/* atime */
 	archive_entry_set_atime(e, 13579, 24680);
 	assertEqualInt(archive_entry_atime(e), 13579);
 	assertEqualInt(archive_entry_atime_nsec(e), 24680);
+	archive_entry_unset_atime(e);
+	assertEqualInt(archive_entry_atime(e), 0);
+	assertEqualInt(archive_entry_atime_nsec(e), 0);
+	assert(!archive_entry_atime_is_set(e));
+
 	/* ctime */
 	archive_entry_set_ctime(e, 13580, 24681);
 	assertEqualInt(archive_entry_ctime(e), 13580);
 	assertEqualInt(archive_entry_ctime_nsec(e), 24681);
+	archive_entry_unset_ctime(e);
+	assertEqualInt(archive_entry_ctime(e), 0);
+	assertEqualInt(archive_entry_ctime_nsec(e), 0);
+	assert(!archive_entry_ctime_is_set(e));
+
 #if ARCHIVE_VERSION_STAMP >= 1009000
 	/* dev */
 	archive_entry_set_dev(e, 235);
@@ -81,6 +96,7 @@
 	skipping("archive_entry_dev()");
 #endif
 	/* devmajor/devminor are tested specially below. */
+
 #if ARCHIVE_VERSION_STAMP >= 1009000
 	/* filetype */
 	archive_entry_set_filetype(e, AE_IFREG);
@@ -88,10 +104,13 @@
 #else
 	skipping("archive_entry_filetype()");
 #endif
+
 	/* fflags are tested specially below */
+
 	/* gid */
 	archive_entry_set_gid(e, 204);
 	assertEqualInt(archive_entry_gid(e), 204);
+
 	/* gname */
 	archive_entry_set_gname(e, "group");
 	assertEqualString(archive_entry_gname(e), "group");
@@ -100,6 +119,7 @@
 	assertEqualWString(archive_entry_gname_w(e), L"wgroup");
 	memset(wbuff, 0, sizeof(wbuff));
 	assertEqualWString(archive_entry_gname_w(e), L"wgroup");
+
 	/* hardlink */
 	archive_entry_set_hardlink(e, "hardlinkname");
 	assertEqualString(archive_entry_hardlink(e), "hardlinkname");
@@ -120,15 +140,50 @@
 #else
 	skipping("archive_entry_ino()");
 #endif
+
 	/* link */
-	/* TODO: implement these tests. */
+	archive_entry_set_hardlink(e, "hardlinkname");
+	archive_entry_set_symlink(e, NULL);
+	archive_entry_set_link(e, "link");
+	assertEqualString(archive_entry_hardlink(e), "link");
+	assertEqualString(archive_entry_symlink(e), NULL);
+	archive_entry_copy_link(e, "link2");
+	assertEqualString(archive_entry_hardlink(e), "link2");
+	assertEqualString(archive_entry_symlink(e), NULL);
+	archive_entry_copy_link_w(e, L"link3");
+	assertEqualString(archive_entry_hardlink(e), "link3");
+	assertEqualString(archive_entry_symlink(e), NULL);
+	archive_entry_set_hardlink(e, NULL);
+	archive_entry_set_symlink(e, "symlink");
+	archive_entry_set_link(e, "link");
+	assertEqualString(archive_entry_hardlink(e), NULL);
+	assertEqualString(archive_entry_symlink(e), "link");
+	archive_entry_copy_link(e, "link2");
+	assertEqualString(archive_entry_hardlink(e), NULL);
+	assertEqualString(archive_entry_symlink(e), "link2");
+	archive_entry_copy_link_w(e, L"link3");
+	assertEqualString(archive_entry_hardlink(e), NULL);
+	assertEqualString(archive_entry_symlink(e), "link3");
+	/* Arbitrarily override symlink if both hardlink and symlink set. */
+	archive_entry_set_hardlink(e, "hardlink");
+	archive_entry_set_symlink(e, "symlink");
+	archive_entry_set_link(e, "link");
+	assertEqualString(archive_entry_hardlink(e), "hardlink");
+	assertEqualString(archive_entry_symlink(e), "link");
+
 	/* mode */
 	archive_entry_set_mode(e, 0123456);
 	assertEqualInt(archive_entry_mode(e), 0123456);
+
 	/* mtime */
 	archive_entry_set_mtime(e, 13581, 24682);
 	assertEqualInt(archive_entry_mtime(e), 13581);
 	assertEqualInt(archive_entry_mtime_nsec(e), 24682);
+	archive_entry_unset_mtime(e);
+	assertEqualInt(archive_entry_mtime(e), 0);
+	assertEqualInt(archive_entry_mtime_nsec(e), 0);
+	assert(!archive_entry_mtime_is_set(e));
+
 #if ARCHIVE_VERSION_STAMP >= 1009000
 	/* nlink */
 	archive_entry_set_nlink(e, 736);
@@ -136,6 +191,7 @@
 #else
 	skipping("archive_entry_nlink()");
 #endif
+
 	/* pathname */
 	archive_entry_set_pathname(e, "path");
 	assertEqualString(archive_entry_pathname(e), "path");
@@ -151,6 +207,7 @@
 	assertEqualWString(archive_entry_pathname_w(e), L"wpath");
 	memset(wbuff, 0, sizeof(wbuff));
 	assertEqualWString(archive_entry_pathname_w(e), L"wpath");
+
 #if ARCHIVE_VERSION_STAMP >= 1009000
 	/* rdev */
 	archive_entry_set_rdev(e, 532);
@@ -159,9 +216,14 @@
 	skipping("archive_entry_rdev()");
 #endif
 	/* rdevmajor/rdevminor are tested specially below. */
+
 	/* size */
 	archive_entry_set_size(e, 987654321);
 	assertEqualInt(archive_entry_size(e), 987654321);
+	archive_entry_unset_size(e);
+	assertEqualInt(archive_entry_size(e), 0);
+	assert(!archive_entry_size_is_set(e));
+
 	/* symlink */
 	archive_entry_set_symlink(e, "symlinkname");
 	assertEqualString(archive_entry_symlink(e), "symlinkname");
@@ -174,9 +236,11 @@
 #endif
 	archive_entry_copy_symlink_w(e, L"wsymlink");
 	assertEqualWString(archive_entry_symlink_w(e), L"wsymlink");
+
 	/* uid */
 	archive_entry_set_uid(e, 83);
 	assertEqualInt(archive_entry_uid(e), 83);
+
 	/* uname */
 	archive_entry_set_uname(e, "user");
 	assertEqualString(archive_entry_uname(e), "user");
@@ -199,6 +263,11 @@
 	assertEqualString(archive_entry_fflags_text(e),
 	    "uappnd,nouchg,nodump,noopaque,uunlnk");
 	/* TODO: Test archive_entry_copy_fflags_text_w() */
+	/* Test archive_entry_copy_fflags_text() */
+	archive_entry_copy_fflags_text(e, "nouappnd, nouchg, dump,uunlnk");
+	archive_entry_fflags(e, &set, &clear);
+	assertEqualInt(16, set);
+	assertEqualInt(7, clear);
 #endif
 
 	/* See test_acl_basic.c for tests of ACL set/get consistency. */
@@ -213,10 +282,16 @@
 	assertEqualInt(1, archive_entry_xattr_count(e));
 	assertEqualInt(ARCHIVE_WARN,
 	    archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	assertEqualString(xname, NULL);
+	assertEqualString(xval, NULL);
+	assertEqualInt(xsize, 0);
 	archive_entry_xattr_clear(e);
 	assertEqualInt(0, archive_entry_xattr_reset(e));
 	assertEqualInt(ARCHIVE_WARN,
 	    archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	assertEqualString(xname, NULL);
+	assertEqualString(xval, NULL);
+	assertEqualInt(xsize, 0);
 	archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12);
 	assertEqualInt(1, archive_entry_xattr_reset(e));
 	archive_entry_xattr_add_entry(e, "xattr2", "xattrvalue2", 12);
@@ -225,6 +300,9 @@
 	assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
 	assertEqualInt(ARCHIVE_WARN,
 	    archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	assertEqualString(xname, NULL);
+	assertEqualString(xval, NULL);
+	assertEqualInt(xsize, 0);
 
 
 	/*
@@ -348,6 +426,11 @@
 	assertEqualString(xname, "xattr1");
 	assertEqualString(xval, "xattrvalue");
 	assertEqualInt(xsize, 11);
+	assertEqualInt(ARCHIVE_WARN,
+	    archive_entry_xattr_next(e2, &xname, &xval, &xsize));
+	assertEqualString(xname, NULL);
+	assertEqualString(xval, NULL);
+	assertEqualInt(xsize, 0);
 #endif
 
 	/* Change the original */
@@ -453,6 +536,14 @@
 	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER);
 	assertEqualInt(qual, 77);
 	assertEqualString(name, "user77");
+	assertEqualInt(1, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, 0);
+	assertEqualInt(permset, 0);
+	assertEqualInt(tag, 0);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
 #endif
 #if ARCHIVE_VERSION_STAMP < 1009000
 	skipping("xattr preserved in archive_entry copy");
@@ -670,6 +761,61 @@
 #endif
 #endif
 
+	/*
+	 * Exercise the character-conversion logic, if we can.
+	 */
+	if (NULL == setlocale(LC_ALL, "de_DE.UTF-8")) {
+		skipping("Can't exercise charset-conversion logic without"
+			" a suitable locale.");
+	} else {
+		/* A filename that cannot be converted to wide characters. */
+		archive_entry_copy_pathname(e, "abc\314\214mno\374xyz");
+		failure("Converting invalid chars to Unicode should fail.");
+		assert(NULL == archive_entry_pathname_w(e));
+		//failure("Converting invalid chars to UTF-8 should fail.");
+		//assert(NULL == archive_entry_pathname_utf8(e));
+
+		/* A group name that cannot be converted. */
+		archive_entry_copy_gname(e, "abc\314\214mno\374xyz");
+		failure("Converting invalid chars to Unicode should fail.");
+		assert(NULL == archive_entry_gname_w(e));
+
+		/* A user name that cannot be converted. */
+		archive_entry_copy_uname(e, "abc\314\214mno\374xyz");
+		failure("Converting invalid chars to Unicode should fail.");
+		assert(NULL == archive_entry_uname_w(e));
+
+		/* A hardlink target that cannot be converted. */
+		archive_entry_copy_hardlink(e, "abc\314\214mno\374xyz");
+		failure("Converting invalid chars to Unicode should fail.");
+		assert(NULL == archive_entry_hardlink_w(e));
+
+		/* A symlink target that cannot be converted. */
+		archive_entry_copy_symlink(e, "abc\314\214mno\374xyz");
+		failure("Converting invalid chars to Unicode should fail.");
+		assert(NULL == archive_entry_symlink_w(e));
+	}
+
+	l = 0x12345678L;
+	wc = (wchar_t)l; /* Wide character too big for UTF-8. */
+	if (NULL == setlocale(LC_ALL, "C") || (long)wc != l) {
+		skipping("Testing charset conversion failure requires 32-bit wchar_t and support for \"C\" locale.");
+	} else {
+		/*
+		 * Build the string L"xxx\U12345678yyy\u5678zzz" without
+		 * using C99 \u#### syntax, which isn't uniformly
+		 * supported.  (GCC 3.4.6, for instance, defaults to
+		 * "c89 plus GNU extensions.")
+		 */
+		wcscpy(wbuff, L"xxxAyyyBzzz");
+		wbuff[3] = 0x12345678;
+		wbuff[7] = 0x5678;
+		/* A wide filename that cannot be converted to narrow. */
+		archive_entry_copy_pathname_w(e, wbuff);
+		failure("Converting wide characters from Unicode should fail.");
+		assertEqualString(NULL, archive_entry_pathname(e));
+	}
+
 	/* Release the experimental entry. */
 	archive_entry_free(e);
 }
--- /dev/null
+++ lib/libarchive/test/test_compat_gtar.c
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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: src/lib/libarchive/test/test_compat_gtar.c,v 1.1.2.2 2008/05/21 04:14:39 kientzle Exp $");
+
+/*
+ * Verify our ability to read sample files created by GNU tar.
+ * It should be easy to add any new sample files sent in by users
+ * to this collection of tests.
+ */
+
+/* Copy this function for each test file and adjust it accordingly. */
+
+/*
+ * test_compat_gtar_1.tgz exercises reading long filenames and
+ * symlink targets stored in the GNU tar format.
+ */
+static void
+test_compat_gtar_1(void)
+{
+	char name[] = "test_compat_gtar_1.tgz";
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+	extract_reference_file(name);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+	/* Read first entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(
+		"12345678901234567890123456789012345678901234567890"
+		"12345678901234567890123456789012345678901234567890"
+		"12345678901234567890123456789012345678901234567890"
+		"12345678901234567890123456789012345678901234567890",
+		archive_entry_pathname(ae));
+	assertEqualInt(1197179003, archive_entry_mtime(ae));
+	assertEqualInt(1000, archive_entry_uid(ae));
+	assertEqualString("tim", archive_entry_uname(ae));
+	assertEqualInt(1000, archive_entry_gid(ae));
+	assertEqualString("tim", archive_entry_gname(ae));
+	assertEqualInt(0100644, archive_entry_mode(ae));
+
+	/* Read second entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString(
+		"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"
+		"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"
+		"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"
+		"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij",
+		archive_entry_pathname(ae));
+	assertEqualString(
+		"12345678901234567890123456789012345678901234567890"
+		"12345678901234567890123456789012345678901234567890"
+		"12345678901234567890123456789012345678901234567890"
+		"12345678901234567890123456789012345678901234567890",
+		archive_entry_symlink(ae));
+	assertEqualInt(1197179043, archive_entry_mtime(ae));
+	assertEqualInt(1000, archive_entry_uid(ae));
+	assertEqualString("tim", archive_entry_uname(ae));
+	assertEqualInt(1000, archive_entry_gid(ae));
+	assertEqualString("tim", archive_entry_gname(ae));
+	assertEqualInt(0120755, archive_entry_mode(ae));
+
+	/* Verify the end-of-archive. */
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+	/* Verify that the format detection worked. */
+	assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_GZIP);
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR);
+
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
+DEFINE_TEST(test_compat_gtar)
+{
+	test_compat_gtar_1();
+}
+
+
Index: test_archive_api_feature.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_archive_api_feature.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_archive_api_feature.c -L lib/libarchive/test/test_archive_api_feature.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_archive_api_feature.c
+++ lib/libarchive/test/test_archive_api_feature.c
@@ -23,10 +23,34 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_archive_api_feature.c,v 1.3 2007/07/06 15:43:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_archive_api_feature.c,v 1.3.2.2 2008/08/10 04:32:47 kientzle Exp $");
 
 DEFINE_TEST(test_archive_api_feature)
 {
+	char buff[128];
+	const char *p;
+
+	/* This is the (hopefully) final versioning API. */
+	assertEqualInt(ARCHIVE_VERSION_NUMBER, archive_version_number());
+	sprintf(buff, "libarchive %d.%d.%d",
+	    archive_version_number() / 1000000,
+	    (archive_version_number() / 1000) % 1000,
+	    archive_version_number() % 1000);
+	failure("Version string is: %s, computed is: %s",
+	    archive_version_string(), buff);
+	assert(memcmp(buff, archive_version_string(), strlen(buff)) == 0);
+	if (strlen(buff) < strlen(archive_version_string())) {
+		p = archive_version_string() + strlen(buff);
+		failure("Version string is: %s", archive_version_string());
+		assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd');
+		++p;
+		failure("Version string is: %s", archive_version_string());
+		assert(*p == '\0');
+	}
+
+/* This is all scheduled to disappear in libarchive 3.0 */
+#if ARCHIVE_VERSION_NUMBER < 3000000
+	assertEqualInt(ARCHIVE_VERSION_STAMP, ARCHIVE_VERSION_NUMBER);
 	assertEqualInt(ARCHIVE_API_FEATURE, archive_api_feature());
 	assertEqualInt(ARCHIVE_API_VERSION, archive_api_version());
 	/*
@@ -48,4 +72,5 @@
 	skipping("archive_version_stamp()");
 #endif
 	assertEqualString(ARCHIVE_LIBRARY_VERSION, archive_version());
+#endif
 }
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse_1_17.tgz.uu
@@ -0,0 +1,27 @@
+$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse_1_17.tgz.uu,v 1.1.2.1 2008/02/11 00:31:09 kientzle Exp $
+begin 644 test_read_format_gtar_sparse_1_17.tgz
+M'XL(`&&";$<``^W776X3611%83,33Z"E>^[O0'H$?N"!!V at 4A_GWJ8*$".\V
+MK=Y8U1+KB\#H)L2G;BV77=?/EZ?K^]-#E31[WQYCC?+V<5/SSRG*:BU*1,_U
+M6"O:Z?SG8\?ZZLOU^?)T/I^>/WR\^W,_^_Y_MFU!FSVV7?BV(Z__SEVJ8CTW
+M:?>ROO_[WA.\_H=?[O+K?R5^(_1SK.M^_:\/?8Z[U__HM95X<_V/4ZDU6N7Z
+M_\_7_]9Z4^NKEO[C^KM2QHH1:]MF]>RY_WV(WU;G*E.LMSKZ4NMY$.HH>JU#
+M'46?$>HH<M1Q<Q3;^IBAYIS1AYISCA9JSA4QBUK/S19SYL.<8L[(7:MBSMR#
+M-L6<F7:M8L[(YYQBSJAM-3%GU)7G4ZQO4:@YVZKKMHK7\Q^]15/S]KF6FG?4
+MV=2\8S_JV_59FZH[YJRJ[CUBM:]YV$7,6?.9NYBSEJQ(S%DCZA!SUGR-J'ZS
+MB:7ZK34#%ON]OYS4G'EZ0LW9\P#4G+D)5<W9UYQJSM&&ZK>.U6[[_7[^\VQ6
+MU?'^\E?SKNU+K<_],'Y<;SEQ$_.V,O,7B?7(J[_8UY:[=-OQ=CF*J?K=+U-B
+MSGR;:5W-F=M7U)P9;U=S]KP*JCE'AJ?FW%;5G#,W2.SSR^7T=GVMH>;,TQQJ
+MSMRU<>?\]VU'Q+SYX at G5<<_H5<<]7]"JX_WR+^;M&=X4\_;,L8I][;E14\W9
+M\XJAYLQ7H>JWC[P0JSG'J*K?_6U*S3DS+S5GOFR:FG-E,&+.L:4GYARYHZK?
+M[=RK?D=>Z%6_^]OIG?,_\L)T^ZEA6U]5?2X8V^<T-6^&J3X7C#S1JN.\:'?5
+M<9Z&ICK>W_[5G'-[!U#KVY=8WS\,O5DO\H,1_A7NW^"@'SCH!P[Z at 8-^X*`?
+M.-Q^Z`\.^H&#?N"@'SCH!P[Z at 8-^X.#^'T>B'SCH!P[Z at 8-^X*`?..@'#N[_
+M<23Z at 8-^X*`?..@'#OJ!@W[@X/X?1Z(?..@'#OJ!@W[@H!\XZ`<.[O]Q)/J!
+M at W[@H!\XZ`<.^H&#?N#@_A]'HA\XZ`<.^H&#?N"@'SCH!P[N_W$D^H&#?N"@
+M'SCH!P[Z at 8-^X.#^'T>B'SCH!P[Z at 8-^X*`?..@'#N[_<23Z at 8-^X*`?..@'
+M#OJ!@W[@X/X?1Z(?..@'#OJ!@W[@H!\XZ`<.[O]Q)/J!@W[@H!\XZ`<.^H&#
+M?N#@_A]'HA\XZ`<.^O%\^NO3']?/EZ?K^\<]1TFS]^TQUBAO'U^<HJS6HD3T
+M..7?M:S3N3QNI.^^7)\O3^?SZ?G#Q[L_][/O`P````````#P/_(W91GI)`#P
+"````
+`
+end
Index: test_read_format_tar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/test_read_format_tar.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/test_read_format_tar.c -L lib/libarchive/test/test_read_format_tar.c -u -r1.1 -r1.2
--- lib/libarchive/test/test_read_format_tar.c
+++ lib/libarchive/test/test_read_format_tar.c
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_tar.c,v 1.2 2007/07/14 05:35:17 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_tar.c,v 1.2.2.1 2008/02/11 00:31:09 kientzle Exp $");
 
 /*
  * Each of these archives is a short archive with a single entry.  The
@@ -36,6 +36,52 @@
  * '2' is a symlink, '5' is a dir, etc.
  */
 
+/* Empty archive. */
+static unsigned char archiveEmpty[] = {
+	/* 512 zero bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
+};
+
+static void verifyEmpty(void)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archiveEmpty, 512));
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+	assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE);
+	failure("512 zero bytes should be recognized as a tar archive.");
+	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR);
+
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
 /* Single entry with a hardlink. */
 static unsigned char archive1[] = {
 'h','a','r','d','l','i','n','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -411,6 +457,7 @@
 
 DEFINE_TEST(test_read_format_tar)
 {
+	verifyEmpty();
 	verify(archive1, sizeof(archive1), verify1,
 	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
 	verify(archive2, sizeof(archive2), verify2,
Index: README
===================================================================
RCS file: /home/cvs/src/lib/libarchive/test/README,v
retrieving revision 1.1
retrieving revision 1.2
diff -L lib/libarchive/test/README -L lib/libarchive/test/README -u -r1.1 -r1.2
--- lib/libarchive/test/README
+++ lib/libarchive/test/README
@@ -1,4 +1,4 @@
-$FreeBSD: src/lib/libarchive/test/README,v 1.2 2007/05/29 01:00:20 kientzle Exp $
+$FreeBSD: src/lib/libarchive/test/README,v 1.2.2.1 2008/02/11 00:31:08 kientzle Exp $
 
 This is the test harness for libarchive.
 
@@ -32,7 +32,11 @@
 
   * The test function should use assert(), assertA() and similar macros
     defined in test.h.  If you need to add new macros of this form, feel
-    free to do so.
+    free to do so.  The current macro set includes assertEqualInt() and
+    assertEqualString() that print out additional detail about their
+    arguments if the assertion does fail.  'A' versions also accept
+    a struct archive * and display any error message from there on
+    failure.
 
   * You are encouraged to document each assertion with a failure() call
     just before the assert.  The failure() function is a printf-like
@@ -43,7 +47,10 @@
        assert(strcmp(buff1, buff2) == 0);
 
   * Tests are encouraged to be economical with their memory and disk usage,
-    though this is not essential.
+    though this is not essential.  The test is occasionally run under
+    a memory debugger to try to locate memory leaks in the library;
+    as a result, tests should be careful to release any memory they
+    allocate.
 
   * Disable tests on specific platforms as necessary.  Please don't
     use config.h to adjust feature requirements, as I want the tests
@@ -53,3 +60,4 @@
 #if !defined(__PLATFORM) && !defined(__Platform2__)
     assert(xxxx)
 #endif
+


More information about the Midnightbsd-cvs mailing list