[Midnightbsd-cvs] src: usr.bin/cpio: add bsdcpio

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Fri Mar 27 16:22:29 EDT 2009


Log Message:
-----------
add bsdcpio

Added Files:
-----------
    src/usr.bin/cpio:
        Makefile (r1.1)
        bsdcpio.1 (r1.1)
        cmdline.c (r1.1)
        config_midnightbsd.h (r1.1)
        cpio.c (r1.1)
        cpio.h (r1.1)
        cpio_platform.h (r1.1)
        err.c (r1.1)
        matching.c (r1.1)
        matching.h (r1.1)
        pathmatch.c (r1.1)
        pathmatch.h (r1.1)
    src/usr.bin/cpio/test:
        Makefile (r1.1)
        main.c (r1.1)
        test.h (r1.1)
        test_0.c (r1.1)
        test_basic.c (r1.1)
        test_format_newc.c (r1.1)
        test_gcpio_compat.c (r1.1)
        test_gcpio_compat_ref.bin.uu (r1.1)
        test_gcpio_compat_ref.crc.uu (r1.1)
        test_gcpio_compat_ref.newc.uu (r1.1)
        test_gcpio_compat_ref.ustar.uu (r1.1)
        test_option_B.c (r1.1)
        test_option_L.c (r1.1)
        test_option_a.c (r1.1)
        test_option_c.c (r1.1)
        test_option_d.c (r1.1)
        test_option_ell.c (r1.1)
        test_option_f.c (r1.1)
        test_option_f.cpio.uu (r1.1)
        test_option_help.c (r1.1)
        test_option_m.c (r1.1)
        test_option_m.cpio.uu (r1.1)
        test_option_t.c (r1.1)
        test_option_t.cpio.uu (r1.1)
        test_option_t.stdout.uu (r1.1)
        test_option_tv.stdout.uu (r1.1)
        test_option_u.c (r1.1)
        test_option_version.c (r1.1)
        test_option_y.c (r1.1)
        test_option_z.c (r1.1)
        test_owner_parse.c (r1.1)
        test_passthrough_dotdot.c (r1.1)
        test_passthrough_reverse.c (r1.1)
        test_pathmatch.c (r1.1)

-------------- next part --------------
--- /dev/null
+++ usr.bin/cpio/bsdcpio.1
@@ -0,0 +1,367 @@
+.\" 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 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/usr.bin/cpio/bsdcpio.1,v 1.1 2008/05/26 17:15:34 kientzle Exp $
+.\"
+.Dd December 21, 2007
+.Dt BSDCPIO 1
+.Os
+.Sh NAME
+.Nm cpio
+.Nd copy files to and from archives
+.Sh SYNOPSIS
+.Nm
+.Brq Fl i
+.Op Ar options
+.Op Ar pattern ...
+.Op Ar < archive
+.Nm
+.Brq Fl o
+.Op Ar options
+.Ar < name-list
+.Op Ar > archive
+.Nm
+.Brq Fl p
+.Op Ar options
+.Ar dest-dir
+.Ar < name-list
+.Sh DESCRIPTION
+.Nm
+copies files between archives and directories.
+This implementation can extract from tar, pax, cpio, zip, jar, ar,
+and ISO 9660 cdrom images and can create tar, pax, cpio, ar,
+and shar archives.
+.Pp
+The first option to
+.Nm
+is a mode indicator from the following list:
+.Bl -tag -compact -width indent
+.It Fl i
+Input.
+Read an archive from standard input (unless overriden) and extract the
+contents to disk or (if the
+.Fl t
+option is specified)
+list the contents to standard output.
+If one or more file patterns are specified, only files matching
+one of the patterns will be extracted.
+.It Fl o
+Output.
+Read a list of filenames from standard input and produce a new archive
+on standard output (unless overriden) containing the specified items.
+.It Fl p
+Pass-through.
+Read a list of filenames from standard input and copy the files to the
+specified directory.
+.El
+.Pp
+.Sh OPTIONS
+Unless specifically stated otherwise, options are applicable in
+all operating modes.
+.Bl -tag -width indent
+.It Fl A
+(o mode only)
+Append to the specified archive.
+(Not yet implemented.)
+.It Fl a
+(o and p modes)
+Reset access times on files after they are read.
+.It Fl B
+(o mode only)
+Block output to records of 5120 bytes.
+.It Fl C Ar size
+(o mode only)
+Block output to records of
+.Ar size
+bytes.
+.It Fl c
+(o mode only)
+Use the old POSIX portable character format.
+Equivalent to
+.Fl -format Ar odc .
+.It Fl d
+(i and p modes)
+Create directories as necessary.
+.It Fl E Ar file
+(i mode only)
+Read list of file name patterns from
+.Ar file
+to list and extract.
+.It Fl F Ar file
+Read archive from or write archive to
+.Ar file .
+.It Fl f Ar pattern
+(i mode only)
+Ignore files that match
+.Ar pattern .
+.It Fl -format Ar format
+(o mode only)
+Produce the output archive in the specified format.
+Supported formats include:
+.Pp
+.Bl -tag -width "iso9660" -compact
+.It Ar cpio
+Synonym for
+.Ar odc .
+.It Ar newc
+The SVR4 portable cpio format.
+.It Ar odc
+The old POSIX.1 portable octet-oriented cpio format.
+.It Ar pax
+The POSIX.1 pax format, an extension of the ustar format.
+.It Ar ustar
+The POSIX.1 tar format.
+.El
+.Pp
+The default format is
+.Ar odc .
+See
+.Xr libarchive_formats 5
+for more complete information about the
+formats currently supported by the underlying
+.Xr libarchive 3
+library.
+.It Fl I Ar file
+Read archive from
+.Ar file .
+.It Fl i
+Input mode.
+See above for description.
+.It Fl -insecure
+(i and p mode only)
+Disable security checks during extraction or copying.
+This allows extraction via symbolic links and path names containing
+.Sq ..
+in the name.
+.It Fl L
+(o and p modes)
+All symbolic links will be followed.
+Normally, symbolic links are archived and copied as symbolic links.
+With this option, the target of the link will be archived or copied instead.
+.It Fl l
+(p mode only)
+Create links from the target directory to the original files,
+instead of copying.
+.It Fl m
+(i and p modes)
+Set file modification time on created files to match
+those in the source.
+.It Fl O Ar file
+Write archive to
+.Ar file .
+.It Fl o
+Output mode.
+See above for description.
+.It Fl p
+Pass-through mode.
+See above for description.
+.It Fl -quiet
+Suppress unnecessary messages.
+.It Fl R Oo user Oc Ns Oo : Oc Ns Oo group Oc
+Set the owner and/or group on files in the output.
+If group is specified with no user
+(for example,
+.Fl R Ar :wheel )
+then the group will be set but not the user.
+If the user is specified with a trailing colon and no group
+(for example,
+.Fl R Ar root: )
+then the group will be set to the user's default group.
+If the user is specified with no trailing colon, then
+the user will be set but not the group.
+In
+.Fl i
+and
+.Fl p
+modes, this option can only be used by the super-user.
+(For compatibility, a period can be used in place of the colon.)
+.It Fl r
+(All modes.)
+Rename files interactively.
+For each file, a prompt is written to
+.Pa /dev/tty
+containing the name of the file and a line is read from
+.Pa /dev/tty .
+If the line read is blank, the file is skipped.
+If the line contains a single period, the file is processed normally.
+Otherwise, the line is taken to be the new name of the file.
+.It Fl t
+(i mode only)
+List the contents of the archive to stdout;
+do not restore the contents to disk.
+.It Fl u
+(i and p modes)
+Unconditionally overwrite existing files.
+Ordinarily, an older file will not overwrite a newer file on disk.
+.It Fl v
+Print the name of each file to stderr as it is processed.
+With
+.Fl t ,
+provide a detailed listing of each file.
+.It Fl -version
+Print the program version information and exit.
+.It Fl y
+(o mode only)
+Compress the archive with bzip2-compatible compression before writing it.
+In input mode, this option is ignored;
+bzip2 compression is recognized automatically on input.
+.It Fl Z
+(o mode only)
+Compress the archive with compress-compatible compression before writing it.
+In input mode, this option is ignored;
+compression is recognized automatically on input.
+.It Fl z
+(o mode only)
+Compress the archive with gzip-compatible compression before writing it.
+In input mode, this option is ignored;
+gzip compression is recognized automatically on input.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width ".Ev BLOCKSIZE"
+.It Ev LANG
+The locale to use.
+See
+.Xr environ 7
+for more information.
+.It Ev TZ
+The timezone to use when displaying dates.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The
+.Nm
+command is traditionally used to copy file heirarchies in conjunction
+with the
+.Xr find 1
+command.
+The first example here simply copies all files from
+.Pa src
+to
+.Pa dest :
+.Dl Nm find Pa src | Nm Fl pmud Pa dest
+.Pp
+By carefully selecting options to the
+.Xr find 1
+command and combining it with other standard utilities,
+it is possible to exercise very fine control over which files are copied.
+This next example copies files from
+.Pa src
+to
+.Pa dest
+that are more than 2 days old and whose names match a particular pattern:
+.Dl Nm find Pa src Fl mtime Ar +2 | Nm grep foo[bar] | Nm Fl pdmu Pa dest
+.Pp
+This example copies files from
+.Pa src
+to
+.Pa dest
+that are more than 2 days old and which contain the word
+.Do foobar Dc :
+.Dl Nm find Pa src Fl mtime Ar +2 | Nm xargs Nm grep -l foobar | Nm Fl pdmu Pa dest
+.Sh COMPATIBILITY
+The mode options i, o, and p and the options
+a, B, c, d, f, l, m, r, t, u, and v comply with SUSv2.
+.Pp
+The old POSIX.1 standard specified that only
+.Fl i ,
+.Fl o ,
+and
+.Fl p
+were interpreted as command-line options.
+Each took a single argument of a list of modifier
+characters.
+For example, the standard syntax allows
+.Fl imu
+but does not support
+.Fl miu
+or
+.Fl i Fl m Fl u ,
+since
+.Ar m
+and
+.Ar u
+are only modifiers to
+.Fl i ,
+they are not command-line options in their own right.
+The syntax supported by this implementation is backwards-compatible
+with the standard.
+For best compatibility, scripts should limit themselves to the
+standard syntax.
+.Sh SEE ALSO
+.Xr bzip2 1 ,
+.Xr tar 1 ,
+.Xr gzip 1 ,
+.Xr mt 1 ,
+.Xr pax 1 ,
+.Xr libarchive 3 ,
+.Xr cpio 5 ,
+.Xr libarchive-formats 5 ,
+.Xr tar 5
+.Sh STANDARDS
+There is no current POSIX standard for the cpio command; it appeared
+in
+.St -p1003.1-96
+but was dropped from
+.St -p1003.1-2001 .
+.Pp
+The cpio, ustar, and pax interchange file formats are defined by
+.St -p1003.1-2001
+for the pax command.
+.Sh HISTORY
+The original
+.Nm cpio
+and
+.Nm find
+utilities were written by Dick Haight
+while working in AT&T's Unix Support Group.
+They first appeared in 1977 in PWB/UNIX 1.0, the
+.Dq Programmer's Work Bench
+system developed for use within AT&T.
+They were first released outside of AT&T as part of System III Unix in 1981.
+As a result,
+.Nm cpio
+actually predates
+.Nm tar ,
+even though it was not well-known outside of AT&T until some time later.
+.Pp
+This is a complete re-implementation based on the
+.Xr libarchive 3
+library.
+.Sh BUGS
+The cpio archive format has several basic limitations:
+It does not store user and group names, only numbers.
+As a result, it cannot be reliably used to transfer
+files between systems with dissimilar user and group numbering.
+Older cpio formats limit the user and group numbers to
+16 or 18 bits, which is insufficient for modern systems.
+The cpio archive formats cannot support files over 4 gigabytes,
+except for the
+.Dq odc
+variant, which can support files up to 8 gigabytes.
--- /dev/null
+++ usr.bin/cpio/pathmatch.h
@@ -0,0 +1,37 @@
+/*-
+ * 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
+ *    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.
+ *
+ * $FreeBSD: src/usr.bin/cpio/pathmatch.h,v 1.1 2008/05/26 17:15:34 kientzle Exp $
+ */
+
+#ifndef PATHMATCH_H
+#define PATHMATCH_H
+
+#define PATHMATCH_NO_ANCHOR_START	1
+#define PATHMATCH_NO_ANCHOR_END	2
+
+int pathmatch(const char *p, const char *s, int flags);
+
+#endif
--- /dev/null
+++ usr.bin/cpio/cpio.c
@@ -0,0 +1,1242 @@
+/*-
+ * 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
+ *    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 "cpio_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.15 2008/12/06 07:30:40 kientzle Exp $");
+
+#include <sys/types.h>
+#include <archive.h>
+#include <archive_entry.h>
+
+#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
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "cpio.h"
+#include "matching.h"
+
+/* Fixed size of uname/gname caches. */
+#define	name_cache_size 101
+
+struct name_cache {
+	int	probes;
+	int	hits;
+	size_t	size;
+	struct {
+		id_t id;
+		char *name;
+	} cache[name_cache_size];
+};
+
+static int	copy_data(struct archive *, struct archive *);
+static const char *cpio_rename(const char *name);
+static int	entry_to_archive(struct cpio *, struct archive_entry *);
+static int	file_to_archive(struct cpio *, const char *);
+static void	free_cache(struct name_cache *cache);
+static void	list_item_verbose(struct cpio *, struct archive_entry *);
+static void	long_help(void);
+static const char *lookup_gname(struct cpio *, gid_t gid);
+static int	lookup_gname_helper(struct cpio *,
+		    const char **name, id_t gid);
+static const char *lookup_uname(struct cpio *, uid_t uid);
+static int	lookup_uname_helper(struct cpio *,
+		    const char **name, id_t uid);
+static void	mode_in(struct cpio *);
+static void	mode_list(struct cpio *);
+static void	mode_out(struct cpio *);
+static void	mode_pass(struct cpio *, const char *);
+static void	restore_time(struct cpio *, struct archive_entry *,
+		    const char *, int fd);
+static void	usage(void);
+static void	version(void);
+
+int
+main(int argc, char *argv[])
+{
+	static char buff[16384];
+	struct cpio _cpio; /* Allocated on stack. */
+	struct cpio *cpio;
+	int uid, gid;
+	int opt;
+
+	cpio = &_cpio;
+	memset(cpio, 0, sizeof(*cpio));
+	cpio->buff = buff;
+	cpio->buff_size = sizeof(buff);
+
+	/* Need cpio_progname before calling cpio_warnc. */
+	if (*argv == NULL)
+		cpio_progname = "bsdcpio";
+	else {
+		cpio_progname = strrchr(*argv, '/');
+		if (cpio_progname != NULL)
+			cpio_progname++;
+		else
+			cpio_progname = *argv;
+	}
+
+	cpio->uid_override = -1;
+	cpio->gid_override = -1;
+	cpio->argv = argv;
+	cpio->argc = argc;
+	cpio->line_separator = '\n';
+	cpio->mode = '\0';
+	cpio->verbose = 0;
+	cpio->compress = '\0';
+	/* TODO: Implement old binary format in libarchive, use that here. */
+	cpio->format = "odc"; /* Default format */
+	cpio->extract_flags = ARCHIVE_EXTRACT_NO_AUTODIR;
+	cpio->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
+	cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS;
+	cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;
+	cpio->extract_flags |= ARCHIVE_EXTRACT_PERM;
+	cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
+	cpio->extract_flags |= ARCHIVE_EXTRACT_ACL;
+	if (geteuid() == 0)
+		cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER;
+	cpio->bytes_per_block = 512;
+	cpio->filename = NULL;
+
+	while ((opt = cpio_getopt(cpio)) != -1) {
+		switch (opt) {
+		case '0': /* GNU convention: --null, -0 */
+			cpio->line_separator = '\0';
+			break;
+		case 'A': /* NetBSD/OpenBSD */
+			cpio->option_append = 1;
+			break;
+		case 'a': /* POSIX 1997 */
+			cpio->option_atime_restore = 1;
+			break;
+		case 'B': /* POSIX 1997 */
+			cpio->bytes_per_block = 5120;
+			break;
+		case 'C': /* NetBSD/OpenBSD */
+			cpio->bytes_per_block = atoi(cpio->optarg);
+			if (cpio->bytes_per_block <= 0)
+				cpio_errc(1, 0, "Invalid blocksize %s", cpio->optarg);
+			break;
+		case 'c': /* POSIX 1997 */
+			cpio->format = "odc";
+			break;
+		case 'd': /* POSIX 1997 */
+			cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR;
+			break;
+		case 'E': /* NetBSD/OpenBSD */
+			include_from_file(cpio, cpio->optarg);
+			break;
+		case 'F': /* NetBSD/OpenBSD/GNU cpio */
+			cpio->filename = cpio->optarg;
+			break;
+		case 'f': /* POSIX 1997 */
+			exclude(cpio, cpio->optarg);
+			break;
+		case 'H': /* GNU cpio (also --format) */
+			cpio->format = cpio->optarg;
+			break;
+		case 'h':
+			long_help();
+			break;
+		case 'I': /* NetBSD/OpenBSD */
+			cpio->filename = cpio->optarg;
+			break;
+		case 'i': /* POSIX 1997 */
+			cpio->mode = opt;
+			break;
+		case OPTION_INSECURE:
+			cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS;
+			cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT;
+			break;
+		case 'L': /* GNU cpio */
+			cpio->option_follow_links = 1;
+			break;
+		case 'l': /* POSIX 1997 */
+			cpio->option_link = 1;
+			break;
+		case 'm': /* POSIX 1997 */
+			cpio->extract_flags |= ARCHIVE_EXTRACT_TIME;
+			break;
+		case OPTION_NO_PRESERVE_OWNER: /* GNU cpio */
+			cpio->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
+			break;
+		case 'O': /* GNU cpio */
+			cpio->filename = cpio->optarg;
+			break;
+		case 'o': /* POSIX 1997 */
+			cpio->mode = opt;
+			break;
+		case 'p': /* POSIX 1997 */
+			cpio->mode = opt;
+			cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT;
+			break;
+		case OPTION_QUIET: /* GNU cpio */
+			cpio->quiet = 1;
+			break;
+		case 'R': /* GNU cpio, also --owner */
+			if (owner_parse(cpio->optarg, &uid, &gid))
+				usage();
+			if (uid != -1)
+				cpio->uid_override = uid;
+			if (gid != -1)
+				cpio->gid_override = gid;
+			break;
+		case 'r': /* POSIX 1997 */
+			cpio->option_rename = 1;
+			break;
+		case 't': /* POSIX 1997 */
+			cpio->option_list = 1;
+			break;
+		case 'u': /* POSIX 1997 */
+			cpio->extract_flags
+			    &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
+			break;
+		case 'v': /* POSIX 1997 */
+			cpio->verbose++;
+			break;
+		case OPTION_VERSION: /* GNU convention */
+			version();
+			break;
+#if 0
+	        /*
+		 * cpio_getopt() handles -W specially, so it's not
+		 * available here.
+		 */
+		case 'W': /* Obscure, but useful GNU convention. */
+			break;
+#endif
+		case 'y': /* tar convention */
+			cpio->compress = opt;
+			break;
+		case 'Z': /* tar convention */
+			cpio->compress = opt;
+			break;
+		case 'z': /* tar convention */
+			cpio->compress = opt;
+			break;
+		default:
+			usage();
+		}
+	}
+
+	/* TODO: Sanity-check args, error out on nonsensical combinations. */
+
+	switch (cpio->mode) {
+	case 'o':
+		mode_out(cpio);
+		break;
+	case 'i':
+		while (*cpio->argv != NULL) {
+			include(cpio, *cpio->argv);
+			--cpio->argc;
+			++cpio->argv;
+		}
+		if (cpio->option_list)
+			mode_list(cpio);
+		else
+			mode_in(cpio);
+		break;
+	case 'p':
+		if (*cpio->argv == NULL || **cpio->argv == '\0')
+			cpio_errc(1, 0,
+			    "-p mode requires a target directory");
+		mode_pass(cpio, *cpio->argv);
+		break;
+	default:
+		cpio_errc(1, 0,
+		    "Must specify at least one of -i, -o, or -p");
+	}
+
+	free_cache(cpio->gname_cache);
+	free_cache(cpio->uname_cache);
+	return (0);
+}
+
+void
+usage(void)
+{
+	const char	*p;
+
+	p = cpio_progname;
+
+	fprintf(stderr, "Brief Usage:\n");
+	fprintf(stderr, "  List:    %s -it < archive\n", p);
+	fprintf(stderr, "  Extract: %s -i < archive\n", p);
+	fprintf(stderr, "  Create:  %s -o < filenames > archive\n", p);
+	fprintf(stderr, "  Help:    %s --help\n", p);
+	exit(1);
+}
+
+static const char *long_help_msg =
+	"First option must be a mode specifier:\n"
+	"  -i Input  -o Output  -p Pass\n"
+	"Common Options:\n"
+	"  -v    Verbose\n"
+	"Create: %p -o [options]  < [list of files] > [archive]\n"
+	"  -z, -y  Compress archive with gzip/bzip2\n"
+	"  --format {odc|newc|ustar}  Select archive format\n"
+	"List: %p -it < [archive]\n"
+	"Extract: %p -i [options] < [archive]\n";
+
+
+/*
+ * Note that the word 'bsdcpio' will always appear in the first line
+ * of output.
+ *
+ * In particular, /bin/sh scripts that need to test for the presence
+ * of bsdcpio can use the following template:
+ *
+ * if (cpio --help 2>&1 | grep bsdcpio >/dev/null 2>&1 ) then \
+ *          echo bsdcpio; else echo not bsdcpio; fi
+ */
+static void
+long_help(void)
+{
+	const char	*prog;
+	const char	*p;
+
+	prog = cpio_progname;
+
+	fflush(stderr);
+
+	p = (strcmp(prog,"bsdcpio") != 0) ? "(bsdcpio)" : "";
+	printf("%s%s: manipulate archive files\n", prog, p);
+
+	for (p = long_help_msg; *p != '\0'; p++) {
+		if (*p == '%') {
+			if (p[1] == 'p') {
+				fputs(prog, stdout);
+				p++;
+			} else
+				putchar('%');
+		} else
+			putchar(*p);
+	}
+	version();
+}
+
+static void
+version(void)
+{
+	fprintf(stdout,"bsdcpio %s -- %s\n",
+	    BSDCPIO_VERSION_STRING,
+	    archive_version());
+	exit(0);
+}
+
+static void
+mode_out(struct cpio *cpio)
+{
+	unsigned long blocks;
+	struct archive_entry *entry, *spare;
+	struct line_reader *lr;
+	const char *p;
+	int r;
+
+	if (cpio->option_append)
+		cpio_errc(1, 0, "Append mode not yet supported.");
+	cpio->archive = archive_write_new();
+	if (cpio->archive == NULL)
+		cpio_errc(1, 0, "Failed to allocate archive object");
+	switch (cpio->compress) {
+	case 'j': case 'y':
+		archive_write_set_compression_bzip2(cpio->archive);
+		break;
+	case 'z':
+		archive_write_set_compression_gzip(cpio->archive);
+		break;
+	case 'Z':
+		archive_write_set_compression_compress(cpio->archive);
+		break;
+	default:
+		archive_write_set_compression_none(cpio->archive);
+		break;
+	}
+	r = archive_write_set_format_by_name(cpio->archive, cpio->format);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(cpio->archive));
+	archive_write_set_bytes_per_block(cpio->archive, cpio->bytes_per_block);
+	cpio->linkresolver = archive_entry_linkresolver_new();
+	archive_entry_linkresolver_set_strategy(cpio->linkresolver,
+	    archive_format(cpio->archive));
+
+	r = archive_write_open_file(cpio->archive, cpio->filename);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(cpio->archive));
+	lr = process_lines_init("-", cpio->line_separator);
+	while ((p = process_lines_next(lr)) != NULL)
+		file_to_archive(cpio, p);
+	process_lines_free(lr);
+
+	/*
+	 * The hardlink detection may have queued up a couple of entries
+	 * that can now be flushed.
+	 */
+	entry = NULL;
+	archive_entry_linkify(cpio->linkresolver, &entry, &spare);
+	while (entry != NULL) {
+		entry_to_archive(cpio, entry);
+		archive_entry_free(entry);
+		entry = NULL;
+		archive_entry_linkify(cpio->linkresolver, &entry, &spare);
+	}
+
+	r = archive_write_close(cpio->archive);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(cpio->archive));
+
+	if (!cpio->quiet) {
+		blocks = (archive_position_uncompressed(cpio->archive) + 511)
+			      / 512;
+		fprintf(stderr, "%lu %s\n", blocks,
+		    blocks == 1 ? "block" : "blocks");
+	}
+	archive_write_finish(cpio->archive);
+}
+
+/*
+ * This is used by both out mode (to copy objects from disk into
+ * an archive) and pass mode (to copy objects from disk to
+ * an archive_write_disk "archive").
+ */
+static int
+file_to_archive(struct cpio *cpio, const char *srcpath)
+{
+	struct stat st;
+	const char *destpath;
+	struct archive_entry *entry, *spare;
+	size_t len;
+	const char *p;
+	int lnklen;
+	int r;
+
+	/*
+	 * Create an archive_entry describing the source file.
+	 */
+	entry = archive_entry_new();
+	if (entry == NULL)
+		cpio_errc(1, 0, "Couldn't allocate entry");
+	archive_entry_copy_sourcepath(entry, srcpath);
+
+	/* Get stat information. */
+	if (cpio->option_follow_links)
+		r = stat(srcpath, &st);
+	else
+		r = lstat(srcpath, &st);
+	if (r != 0) {
+		cpio_warnc(errno, "Couldn't stat \"%s\"", srcpath);
+		archive_entry_free(entry);
+		return (0);
+	}
+
+	if (cpio->uid_override >= 0)
+		st.st_uid = cpio->uid_override;
+	if (cpio->gid_override >= 0)
+		st.st_gid = cpio->uid_override;
+	archive_entry_copy_stat(entry, &st);
+
+	/* If its a symlink, pull the target. */
+	if (S_ISLNK(st.st_mode)) {
+		lnklen = readlink(srcpath, cpio->buff, cpio->buff_size);
+		if (lnklen < 0) {
+			cpio_warnc(errno,
+			    "%s: Couldn't read symbolic link", srcpath);
+			archive_entry_free(entry);
+			return (0);
+		}
+		cpio->buff[lnklen] = 0;
+		archive_entry_set_symlink(entry, cpio->buff);
+	}
+
+	/*
+	 * Generate a destination path for this entry.
+	 * "destination path" is the name to which it will be copied in
+	 * pass mode or the name that will go into the archive in
+	 * output mode.
+	 */
+	destpath = srcpath;
+	if (cpio->destdir) {
+		len = strlen(cpio->destdir) + strlen(srcpath) + 8;
+		if (len >= cpio->pass_destpath_alloc) {
+			while (len >= cpio->pass_destpath_alloc) {
+				cpio->pass_destpath_alloc += 512;
+				cpio->pass_destpath_alloc *= 2;
+			}
+			free(cpio->pass_destpath);
+			cpio->pass_destpath = malloc(cpio->pass_destpath_alloc);
+			if (cpio->pass_destpath == NULL)
+				cpio_errc(1, ENOMEM,
+				    "Can't allocate path buffer");
+		}
+		strcpy(cpio->pass_destpath, cpio->destdir);
+		p = srcpath;
+		while (p[0] == '/')
+			++p;
+		strcat(cpio->pass_destpath, p);
+		destpath = cpio->pass_destpath;
+	}
+	if (cpio->option_rename)
+		destpath = cpio_rename(destpath);
+	if (destpath == NULL)
+		return (0);
+	archive_entry_copy_pathname(entry, destpath);
+
+	/*
+	 * If we're trying to preserve hardlinks, match them here.
+	 */
+	spare = NULL;
+	if (cpio->linkresolver != NULL
+	    && !S_ISDIR(st.st_mode)) {
+		archive_entry_linkify(cpio->linkresolver, &entry, &spare);
+	}
+
+	if (entry != NULL) {
+		r = entry_to_archive(cpio, entry);
+		archive_entry_free(entry);
+	}
+	if (spare != NULL) {
+		if (r == 0)
+			r = entry_to_archive(cpio, spare);
+		archive_entry_free(spare);
+	}
+	return (r);
+}
+
+static int
+entry_to_archive(struct cpio *cpio, struct archive_entry *entry)
+{
+	const char *destpath = archive_entry_pathname(entry);
+	const char *srcpath = archive_entry_sourcepath(entry);
+	int fd = -1;
+	ssize_t bytes_read;
+	int r;
+
+	/* Print out the destination name to the user. */
+	if (cpio->verbose)
+		fprintf(stderr,"%s", destpath);
+
+	/*
+	 * Option_link only makes sense in pass mode and for
+	 * regular files.  Also note: if a link operation fails
+	 * because of cross-device restrictions, we'll fall back
+	 * to copy mode for that entry.
+	 *
+	 * TODO: Test other cpio implementations to see if they
+	 * hard-link anything other than regular files here.
+	 */
+	if (cpio->option_link
+	    && archive_entry_filetype(entry) == AE_IFREG)
+	{
+		struct archive_entry *t;
+		/* Save the original entry in case we need it later. */
+		t = archive_entry_clone(entry);
+		if (t == NULL)
+			cpio_errc(1, ENOMEM, "Can't create link");
+		/* Note: link(2) doesn't create parent directories,
+		 * so we use archive_write_header() instead as a
+		 * convenience. */
+		archive_entry_set_hardlink(t, srcpath);
+		/* This is a straight link that carries no data. */
+		archive_entry_set_size(t, 0);
+		r = archive_write_header(cpio->archive, t);
+		archive_entry_free(t);
+		if (r != ARCHIVE_OK)
+			cpio_warnc(archive_errno(cpio->archive),
+			    archive_error_string(cpio->archive));
+		if (r == ARCHIVE_FATAL)
+			exit(1);
+#ifdef EXDEV
+		if (r != ARCHIVE_OK && archive_errno(cpio->archive) == EXDEV) {
+			/* Cross-device link:  Just fall through and use
+			 * the original entry to copy the file over. */
+			cpio_warnc(0, "Copying file instead");
+		} else
+#endif
+		return (0);
+	}
+
+	/*
+	 * Make sure we can open the file (if necessary) before
+	 * trying to write the header.
+	 */
+	if (archive_entry_filetype(entry) == AE_IFREG) {
+		if (archive_entry_size(entry) > 0) {
+			fd = open(srcpath, O_RDONLY);
+			if (fd < 0) {
+				cpio_warnc(errno,
+				    "%s: could not open file", srcpath);
+				goto cleanup;
+			}
+		}
+	} else {
+		archive_entry_set_size(entry, 0);
+	}
+
+	r = archive_write_header(cpio->archive, entry);
+
+	if (r != ARCHIVE_OK)
+		cpio_warnc(archive_errno(cpio->archive),
+		    "%s: %s",
+		    destpath,
+		    archive_error_string(cpio->archive));
+
+	if (r == ARCHIVE_FATAL)
+		exit(1);
+
+	if (r >= ARCHIVE_WARN && fd >= 0) {
+		bytes_read = read(fd, cpio->buff, cpio->buff_size);
+		while (bytes_read > 0) {
+			r = archive_write_data(cpio->archive,
+			    cpio->buff, bytes_read);
+			if (r < 0)
+				cpio_errc(1, archive_errno(cpio->archive),
+				    archive_error_string(cpio->archive));
+			if (r < bytes_read) {
+				cpio_warnc(0,
+				    "Truncated write; file may have grown while being archived.");
+			}
+			bytes_read = read(fd, cpio->buff, cpio->buff_size);
+		}
+	}
+
+	restore_time(cpio, entry, srcpath, fd);
+
+cleanup:
+	if (cpio->verbose)
+		fprintf(stderr,"\n");
+	if (fd >= 0)
+		close(fd);
+	return (0);
+}
+
+static void
+restore_time(struct cpio *cpio, struct archive_entry *entry,
+    const char *name, int fd)
+{
+#ifndef HAVE_UTIMES
+	static int warned = 0;
+
+	(void)cpio; /* UNUSED */
+	(void)entry; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)fd; /* UNUSED */
+
+	if (!warned)
+		cpio_warnc(0, "Can't restore access times on this platform");
+	warned = 1;
+	return;
+#else
+	struct timeval times[2];
+
+	if (!cpio->option_atime_restore)
+		return;
+
+        times[1].tv_sec = archive_entry_mtime(entry);
+        times[1].tv_usec = archive_entry_mtime_nsec(entry) / 1000;
+
+        times[0].tv_sec = archive_entry_atime(entry);
+        times[0].tv_usec = archive_entry_atime_nsec(entry) / 1000;
+
+#ifdef HAVE_FUTIMES
+        if (fd >= 0 && futimes(fd, times) == 0)
+		return;
+#endif
+
+#ifdef HAVE_LUTIMES
+        if (lutimes(name, times) != 0)
+#else
+        if (!S_ISLNK(archive_entry_mode(entry)) && utimes(name, times) != 0)
+#endif
+                cpio_warnc(errno, "Can't update time for %s", name);
+#endif
+}
+
+
+static void
+mode_in(struct cpio *cpio)
+{
+	struct archive *a;
+	struct archive_entry *entry;
+	struct archive *ext;
+	const char *destpath;
+	unsigned long blocks;
+	int r;
+
+	ext = archive_write_disk_new();
+	if (ext == NULL)
+		cpio_errc(1, 0, "Couldn't allocate restore object");
+	r = archive_write_disk_set_options(ext, cpio->extract_flags);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(ext));
+	a = archive_read_new();
+	if (a == NULL)
+		cpio_errc(1, 0, "Couldn't allocate archive object");
+	archive_read_support_compression_all(a);
+	archive_read_support_format_all(a);
+
+	if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block))
+		cpio_errc(1, archive_errno(a),
+		    archive_error_string(a));
+	for (;;) {
+		r = archive_read_next_header(a, &entry);
+		if (r == ARCHIVE_EOF)
+			break;
+		if (r != ARCHIVE_OK) {
+			cpio_errc(1, archive_errno(a),
+			    archive_error_string(a));
+		}
+		if (excluded(cpio, archive_entry_pathname(entry)))
+			continue;
+		if (cpio->option_rename) {
+			destpath = cpio_rename(archive_entry_pathname(entry));
+			archive_entry_set_pathname(entry, destpath);
+		} else
+			destpath = archive_entry_pathname(entry);
+		if (destpath == NULL)
+			continue;
+		if (cpio->verbose)
+			fprintf(stdout, "%s\n", destpath);
+		if (cpio->uid_override >= 0)
+			archive_entry_set_uid(entry, cpio->uid_override);
+		if (cpio->gid_override >= 0)
+			archive_entry_set_gid(entry, cpio->gid_override);
+		r = archive_write_header(ext, entry);
+		if (r != ARCHIVE_OK) {
+			fprintf(stderr, "%s: %s\n",
+			    archive_entry_pathname(entry),
+			    archive_error_string(ext));
+		} else if (archive_entry_size(entry) > 0) {
+			r = copy_data(a, ext);
+		}
+	}
+	r = archive_read_close(a);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(a));
+	r = archive_write_close(ext);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(ext));
+	if (!cpio->quiet) {
+		blocks = (archive_position_uncompressed(a) + 511)
+			      / 512;
+		fprintf(stderr, "%lu %s\n", blocks,
+		    blocks == 1 ? "block" : "blocks");
+	}
+	archive_read_finish(a);
+	archive_write_finish(ext);
+	exit(0);
+}
+
+static int
+copy_data(struct archive *ar, struct archive *aw)
+{
+	int r;
+	size_t size;
+	const void *block;
+	off_t offset;
+
+	for (;;) {
+		r = archive_read_data_block(ar, &block, &size, &offset);
+		if (r == ARCHIVE_EOF)
+			return (ARCHIVE_OK);
+		if (r != ARCHIVE_OK) {
+			cpio_warnc(archive_errno(ar),
+			    "%s", archive_error_string(ar));
+			return (r);
+		}
+		r = archive_write_data_block(aw, block, size, offset);
+		if (r != ARCHIVE_OK) {
+			cpio_warnc(archive_errno(aw),
+			    archive_error_string(aw));
+			return (r);
+		}
+	}
+}
+
+static void
+mode_list(struct cpio *cpio)
+{
+	struct archive *a;
+	struct archive_entry *entry;
+	unsigned long blocks;
+	int r;
+
+	a = archive_read_new();
+	if (a == NULL)
+		cpio_errc(1, 0, "Couldn't allocate archive object");
+	archive_read_support_compression_all(a);
+	archive_read_support_format_all(a);
+
+	if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block))
+		cpio_errc(1, archive_errno(a),
+		    archive_error_string(a));
+	for (;;) {
+		r = archive_read_next_header(a, &entry);
+		if (r == ARCHIVE_EOF)
+			break;
+		if (r != ARCHIVE_OK) {
+			cpio_errc(1, archive_errno(a),
+			    archive_error_string(a));
+		}
+		if (excluded(cpio, archive_entry_pathname(entry)))
+			continue;
+		if (cpio->verbose)
+			list_item_verbose(cpio, entry);
+		else
+			fprintf(stdout, "%s\n", archive_entry_pathname(entry));
+	}
+	r = archive_read_close(a);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(a));
+	if (!cpio->quiet) {
+		blocks = (archive_position_uncompressed(a) + 511)
+			      / 512;
+		fprintf(stderr, "%lu %s\n", blocks,
+		    blocks == 1 ? "block" : "blocks");
+	}
+	archive_read_finish(a);
+	exit(0);
+}
+
+/*
+ * Display information about the current file.
+ *
+ * The format here roughly duplicates the output of 'ls -l'.
+ * This is based on SUSv2, where 'tar tv' is documented as
+ * listing additional information in an "unspecified format,"
+ * and 'pax -l' is documented as using the same format as 'ls -l'.
+ */
+static void
+list_item_verbose(struct cpio *cpio, struct archive_entry *entry)
+{
+	char			 size[32];
+	char			 date[32];
+	const char 		*uname, *gname;
+	FILE			*out = stdout;
+	const struct stat	*st;
+	const char		*fmt;
+	time_t			 tim;
+	static time_t		 now;
+
+	st = archive_entry_stat(entry);
+
+	if (!now)
+		time(&now);
+
+	/* Use uname if it's present, else uid. */
+	uname = archive_entry_uname(entry);
+	if (uname == NULL)
+		uname = lookup_uname(cpio, archive_entry_uid(entry));
+
+	/* Use gname if it's present, else gid. */
+	gname = archive_entry_gname(entry);
+	if (gname == NULL)
+		gname = lookup_gname(cpio, archive_entry_gid(entry));
+
+	/* Print device number or file size. */
+	if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+		snprintf(size, sizeof(size), "%lu,%lu",
+		    (unsigned long)major(st->st_rdev),
+		    (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */
+	} else {
+		snprintf(size, sizeof(size), CPIO_FILESIZE_PRINTF,
+		    (CPIO_FILESIZE_TYPE)st->st_size);
+	}
+
+	/* Format the time using 'ls -l' conventions. */
+	tim = (time_t)st->st_mtime;
+	if (abs(tim - now) > (365/2)*86400)
+		fmt = cpio->day_first ? "%e %b  %Y" : "%b %e  %Y";
+	else
+		fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M";
+	strftime(date, sizeof(date), fmt, localtime(&tim));
+
+	fprintf(out, "%s%3d %-8s %-8s %8s %12s %s",
+	    archive_entry_strmode(entry),
+	    archive_entry_nlink(entry),
+	    uname, gname, size, date,
+	    archive_entry_pathname(entry));
+
+	/* Extra information for links. */
+	if (archive_entry_hardlink(entry)) /* Hard link */
+		fprintf(out, " link to %s", archive_entry_hardlink(entry));
+	else if (archive_entry_symlink(entry)) /* Symbolic link */
+		fprintf(out, " -> %s", archive_entry_symlink(entry));
+	fprintf(out, "\n");
+}
+
+static void
+mode_pass(struct cpio *cpio, const char *destdir)
+{
+	unsigned long blocks;
+	struct line_reader *lr;
+	const char *p;
+	int r;
+
+	/* Ensure target dir has a trailing '/' to simplify path surgery. */
+	cpio->destdir = malloc(strlen(destdir) + 8);
+	strcpy(cpio->destdir, destdir);
+	if (destdir[strlen(destdir) - 1] != '/')
+		strcat(cpio->destdir, "/");
+
+	cpio->archive = archive_write_disk_new();
+	if (cpio->archive == NULL)
+		cpio_errc(1, 0, "Failed to allocate archive object");
+	r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(cpio->archive));
+	cpio->linkresolver = archive_entry_linkresolver_new();
+	archive_write_disk_set_standard_lookup(cpio->archive);
+	lr = process_lines_init("-", cpio->line_separator);
+	while ((p = process_lines_next(lr)) != NULL)
+		file_to_archive(cpio, p);
+	process_lines_free(lr);
+
+	archive_entry_linkresolver_free(cpio->linkresolver);
+	r = archive_write_close(cpio->archive);
+	if (r != ARCHIVE_OK)
+		cpio_errc(1, 0, archive_error_string(cpio->archive));
+
+	if (!cpio->quiet) {
+		blocks = (archive_position_uncompressed(cpio->archive) + 511)
+			      / 512;
+		fprintf(stderr, "%lu %s\n", blocks,
+		    blocks == 1 ? "block" : "blocks");
+	}
+
+	archive_write_finish(cpio->archive);
+}
+
+/*
+ * Prompt for a new name for this entry.  Returns a pointer to the
+ * new name or NULL if the entry should not be copied.  This
+ * implements the semantics defined in POSIX.1-1996, which specifies
+ * that an input of '.' means the name should be unchanged.  GNU cpio
+ * treats '.' as a literal new name.
+ */
+static const char *
+cpio_rename(const char *name)
+{
+	static char buff[1024];
+	FILE *t;
+	char *p, *ret;
+
+	t = fopen("/dev/tty", "r+");
+	if (t == NULL)
+		return (name);
+	fprintf(t, "%s (Enter/./(new name))? ", name);
+	fflush(t);
+
+	p = fgets(buff, sizeof(buff), t);
+	fclose(t);
+	if (p == NULL)
+		/* End-of-file is a blank line. */
+		return (NULL);
+
+	while (*p == ' ' || *p == '\t')
+		++p;
+	if (*p == '\n' || *p == '\0')
+		/* Empty line. */
+		return (NULL);
+	if (*p == '.' && p[1] == '\n')
+		/* Single period preserves original name. */
+		return (name);
+	ret = p;
+	/* Trim the final newline. */
+	while (*p != '\0' && *p != '\n')
+		++p;
+	/* Overwrite the final \n with a null character. */
+	*p = '\0';
+	return (ret);
+}
+
+
+/*
+ * Read lines from file and do something with each one.  If option_null
+ * is set, lines are terminated with zero bytes; otherwise, they're
+ * terminated with newlines.
+ *
+ * This uses a self-sizing buffer to handle arbitrarily-long lines.
+ */
+struct line_reader {
+	FILE *f;
+	char *buff, *buff_end, *line_start, *line_end, *p;
+	char *pathname;
+	size_t buff_length;
+	int separator;
+	int ret;
+};
+
+struct line_reader *
+process_lines_init(const char *pathname, char separator)
+{
+	struct line_reader *lr;
+
+	lr = calloc(1, sizeof(*lr));
+	if (lr == NULL)
+		cpio_errc(1, ENOMEM, "Can't open %s", pathname);
+
+	lr->separator = separator;
+	lr->pathname = strdup(pathname);
+
+	if (strcmp(pathname, "-") == 0)
+		lr->f = stdin;
+	else
+		lr->f = fopen(pathname, "r");
+	if (lr->f == NULL)
+		cpio_errc(1, errno, "Couldn't open %s", pathname);
+	lr->buff_length = 8192;
+	lr->buff = malloc(lr->buff_length);
+	if (lr->buff == NULL)
+		cpio_errc(1, ENOMEM, "Can't read %s", pathname);
+	lr->line_start = lr->line_end = lr->buff_end = lr->buff;
+
+	return (lr);
+}
+
+const char *
+process_lines_next(struct line_reader *lr)
+{
+	size_t bytes_wanted, bytes_read, new_buff_size;
+	char *line_start, *p;
+
+	for (;;) {
+		/* If there's a line in the buffer, return it immediately. */
+		while (lr->line_end < lr->buff_end) {
+			if (*lr->line_end == lr->separator) {
+				*lr->line_end = '\0';
+				line_start = lr->line_start;
+				lr->line_start = lr->line_end + 1;
+				lr->line_end = lr->line_start;
+				return (line_start);
+			} else
+				lr->line_end++;
+		}
+
+		/* If we're at end-of-file, process the final data. */
+		if (lr->f == NULL) {
+			/* If there's more text, return one last line. */
+			if (lr->line_end > lr->line_start) {
+				*lr->line_end = '\0';
+				line_start = lr->line_start;
+				lr->line_start = lr->line_end + 1;
+				lr->line_end = lr->line_start;
+				return (line_start);
+			}
+			/* Otherwise, we're done. */
+			return (NULL);
+		}
+
+		/* Buffer only has part of a line. */
+		if (lr->line_start > lr->buff) {
+			/* Move a leftover fractional line to the beginning. */
+			memmove(lr->buff, lr->line_start,
+			    lr->buff_end - lr->line_start);
+			lr->buff_end -= lr->line_start - lr->buff;
+			lr->line_end -= lr->line_start - lr->buff;
+			lr->line_start = lr->buff;
+		} else {
+			/* Line is too big; enlarge the buffer. */
+			new_buff_size = lr->buff_length * 2;
+			if (new_buff_size <= lr->buff_length)
+				cpio_errc(1, ENOMEM,
+				    "Line too long in %s", lr->pathname);
+			lr->buff_length = new_buff_size;
+			p = realloc(lr->buff, new_buff_size);
+			if (p == NULL)
+				cpio_errc(1, ENOMEM,
+				    "Line too long in %s", lr->pathname);
+			lr->buff_end = p + (lr->buff_end - lr->buff);
+			lr->line_end = p + (lr->line_end - lr->buff);
+			lr->line_start = lr->buff = p;
+		}
+
+		/* Get some more data into the buffer. */
+		bytes_wanted = lr->buff + lr->buff_length - lr->buff_end;
+		bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f);
+		lr->buff_end += bytes_read;
+
+		if (ferror(lr->f))
+			cpio_errc(1, errno, "Can't read %s", lr->pathname);
+		if (feof(lr->f)) {
+			if (lr->f != stdin)
+				fclose(lr->f);
+			lr->f = NULL;
+		}
+	}
+}
+
+void
+process_lines_free(struct line_reader *lr)
+{
+	free(lr->buff);
+	free(lr->pathname);
+	free(lr);
+}
+
+static void
+free_cache(struct name_cache *cache)
+{
+	size_t i;
+
+	if (cache != NULL) {
+		for (i = 0; i < cache->size; i++)
+			free(cache->cache[i].name);
+		free(cache);
+	}
+}
+
+/*
+ * Lookup uname/gname from uid/gid, return NULL if no match.
+ */
+static const char *
+lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable,
+    int (*lookup_fn)(struct cpio *, const char **, id_t), id_t id)
+{
+	char asnum[16];
+	struct name_cache	*cache;
+	const char *name;
+	int slot;
+
+
+	if (*name_cache_variable == NULL) {
+		*name_cache_variable = malloc(sizeof(struct name_cache));
+		if (*name_cache_variable == NULL)
+			cpio_errc(1, ENOMEM, "No more memory");
+		memset(*name_cache_variable, 0, sizeof(struct name_cache));
+		(*name_cache_variable)->size = name_cache_size;
+	}
+
+	cache = *name_cache_variable;
+	cache->probes++;
+
+	slot = id % cache->size;
+	if (cache->cache[slot].name != NULL) {
+		if (cache->cache[slot].id == id) {
+			cache->hits++;
+			return (cache->cache[slot].name);
+		}
+		free(cache->cache[slot].name);
+		cache->cache[slot].name = NULL;
+	}
+
+	if (lookup_fn(cpio, &name, id) == 0) {
+		if (name == NULL || name[0] == '\0') {
+			/* If lookup failed, format it as a number. */
+			snprintf(asnum, sizeof(asnum), "%u", (unsigned)id);
+			name = asnum;
+		}
+		cache->cache[slot].name = strdup(name);
+		if (cache->cache[slot].name != NULL) {
+			cache->cache[slot].id = id;
+			return (cache->cache[slot].name);
+		}
+		/*
+		 * Conveniently, NULL marks an empty slot, so
+		 * if the strdup() fails, we've just failed to
+		 * cache it.  No recovery necessary.
+		 */
+	}
+	return (NULL);
+}
+
+static const char *
+lookup_uname(struct cpio *cpio, uid_t uid)
+{
+	return (lookup_name(cpio, &cpio->uname_cache,
+		    &lookup_uname_helper, (id_t)uid));
+}
+
+static int
+lookup_uname_helper(struct cpio *cpio, const char **name, id_t id)
+{
+	struct passwd	*pwent;
+
+	(void)cpio; /* UNUSED */
+
+	errno = 0;
+	pwent = getpwuid((uid_t)id);
+	if (pwent == NULL) {
+		*name = NULL;
+		if (errno != 0)
+			cpio_warnc(errno, "getpwuid(%d) failed", id);
+		return (errno);
+	}
+
+	*name = pwent->pw_name;
+	return (0);
+}
+
+static const char *
+lookup_gname(struct cpio *cpio, gid_t gid)
+{
+	return (lookup_name(cpio, &cpio->gname_cache,
+		    &lookup_gname_helper, (id_t)gid));
+}
+
+static int
+lookup_gname_helper(struct cpio *cpio, const char **name, id_t id)
+{
+	struct group	*grent;
+
+	(void)cpio; /* UNUSED */
+
+	errno = 0;
+	grent = getgrgid((gid_t)id);
+	if (grent == NULL) {
+		*name = NULL;
+		if (errno != 0)
+			cpio_warnc(errno, "getgrgid(%d) failed", id);
+		return (errno);
+	}
+
+	*name = grent->gr_name;
+	return (0);
+}
--- /dev/null
+++ usr.bin/cpio/matching.c
@@ -0,0 +1,259 @@
+/*-
+ * 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 "cpio_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/cpio/matching.c,v 1.2 2008/06/21 02:20:20 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 "matching.h"
+#include "pathmatch.h"
+
+struct match {
+	struct match	 *next;
+	int		  matches;
+	char		  pattern[1];
+};
+
+struct matching {
+	struct match	 *exclusions;
+	int		  exclusions_count;
+	struct match	 *inclusions;
+	int		  inclusions_count;
+	int		  inclusions_unmatched_count;
+};
+
+static void	add_pattern(struct match **list, const char *pattern);
+static void	initialize_matching(struct cpio *);
+static int	match_exclusion(struct match *, const char *pathname);
+static int	match_inclusion(struct match *, const char *pathname);
+
+/*
+ * The matching logic here needs to be re-thought.  I started out to
+ * try to mimic gtar's matching logic, but it's not entirely
+ * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns
+ * on the command line as anchored, but --exclude doesn't.
+ */
+
+/*
+ * Utility functions to manage exclusion/inclusion patterns
+ */
+
+int
+exclude(struct cpio *cpio, const char *pattern)
+{
+	struct matching *matching;
+
+	if (cpio->matching == NULL)
+		initialize_matching(cpio);
+	matching = cpio->matching;
+	add_pattern(&(matching->exclusions), pattern);
+	matching->exclusions_count++;
+	return (0);
+}
+
+#if 0
+int
+exclude_from_file(struct cpio *cpio, const char *pathname)
+{
+	return (process_lines(cpio, pathname, &exclude));
+}
+#endif
+
+int
+include(struct cpio *cpio, const char *pattern)
+{
+	struct matching *matching;
+
+	if (cpio->matching == NULL)
+		initialize_matching(cpio);
+	matching = cpio->matching;
+	add_pattern(&(matching->inclusions), pattern);
+	matching->inclusions_count++;
+	matching->inclusions_unmatched_count++;
+	return (0);
+}
+
+int
+include_from_file(struct cpio *cpio, const char *pathname)
+{
+	struct line_reader *lr;
+	const char *p;
+	int ret = 0;
+
+	lr = process_lines_init(pathname, '\n');
+	while ((p = process_lines_next(lr)) != NULL)
+		if (include(cpio, p) != 0)
+			ret = -1;
+	process_lines_free(lr);
+	return (ret);
+}
+
+static void
+add_pattern(struct match **list, const char *pattern)
+{
+	struct match *match;
+
+	match = malloc(sizeof(*match) + strlen(pattern) + 1);
+	if (match == NULL)
+		cpio_errc(1, errno, "Out of memory");
+	if (pattern[0] == '/')
+		pattern++;
+	strcpy(match->pattern, pattern);
+	/* Both "foo/" and "foo" should match "foo/bar". */
+	if (match->pattern[strlen(match->pattern)-1] == '/')
+		match->pattern[strlen(match->pattern)-1] = '\0';
+	match->next = *list;
+	*list = match;
+	match->matches = 0;
+}
+
+
+int
+excluded(struct cpio *cpio, const char *pathname)
+{
+	struct matching *matching;
+	struct match *match;
+	struct match *matched;
+
+	matching = cpio->matching;
+	if (matching == NULL)
+		return (0);
+
+	/* Exclusions take priority */
+	for (match = matching->exclusions; match != NULL; match = match->next){
+		if (match_exclusion(match, pathname))
+			return (1);
+	}
+
+	/* Then check for inclusions */
+	matched = NULL;
+	for (match = matching->inclusions; match != NULL; match = match->next){
+		if (match_inclusion(match, pathname)) {
+			/*
+			 * If this pattern has never been matched,
+			 * then we're done.
+			 */
+			if (match->matches == 0) {
+				match->matches++;
+				matching->inclusions_unmatched_count++;
+				return (0);
+			}
+			/*
+			 * Otherwise, remember the match but keep checking
+			 * in case we can tick off an unmatched pattern.
+			 */
+			matched = match;
+		}
+	}
+	/*
+	 * We didn't find a pattern that had never been matched, but
+	 * we did find a match, so count it and exit.
+	 */
+	if (matched != NULL) {
+		matched->matches++;
+		return (0);
+	}
+
+	/* If there were inclusions, default is to exclude. */
+	if (matching->inclusions != NULL)
+	    return (1);
+
+	/* No explicit inclusions, default is to match. */
+	return (0);
+}
+
+/*
+ * This is a little odd, but it matches the default behavior of
+ * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
+ *
+ */
+int
+match_exclusion(struct match *match, const char *pathname)
+{
+	return (pathmatch(match->pattern,
+		    pathname,
+		    PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+}
+
+/*
+ * Again, mimic gtar:  inclusions are always anchored (have to match
+ * the beginning of the path) even though exclusions are not anchored.
+ */
+int
+match_inclusion(struct match *match, const char *pathname)
+{
+	return (pathmatch(match->pattern, pathname, 0));
+}
+
+void
+cleanup_exclusions(struct cpio *cpio)
+{
+	struct match *p, *q;
+
+	if (cpio->matching) {
+		p = cpio->matching->inclusions;
+		while (p != NULL) {
+			q = p;
+			p = p->next;
+			free(q);
+		}
+		p = cpio->matching->exclusions;
+		while (p != NULL) {
+			q = p;
+			p = p->next;
+			free(q);
+		}
+		free(cpio->matching);
+	}
+}
+
+static void
+initialize_matching(struct cpio *cpio)
+{
+	cpio->matching = malloc(sizeof(*cpio->matching));
+	if (cpio->matching == NULL)
+		cpio_errc(1, errno, "No memory");
+	memset(cpio->matching, 0, sizeof(*cpio->matching));
+}
+
+int
+unmatched_inclusions(struct cpio *cpio)
+{
+	struct matching *matching;
+
+	matching = cpio->matching;
+	if (matching == NULL)
+		return (0);
+	return (matching->inclusions_unmatched_count);
+}
--- /dev/null
+++ usr.bin/cpio/config_midnightbsd.h
@@ -0,0 +1,104 @@
+/*-
+ * 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.
+ *
+ * $MidnightBSD: src/usr.bin/cpio/config_midnightbsd.h,v 1.1 2009/03/27 20:22:26 laffer1 Exp $
+ * $FreeBSD: src/usr.bin/cpio/config_freebsd.h,v 1.3 2008/12/06 07:30:40 kientzle Exp $
+ */
+
+/* A default configuration for FreeBSD, used if there is no config.h. */
+
+#include <sys/param.h>  /* __FreeBSD_version */
+
+#define	HAVE_ACL_GET_PERM 0
+#define	HAVE_ACL_GET_PERM_NP 1
+#define	HAVE_ACL_PERMSET_T 1
+#define	HAVE_ACL_USER 1
+#undef	HAVE_ATTR_XATTR_H
+#define	HAVE_BZLIB_H 1
+#define	HAVE_CHFLAGS 1
+#define	HAVE_DECL_OPTARG 1
+#define	HAVE_DECL_OPTIND 1
+#define	HAVE_DIRENT_D_NAMLEN 1
+#define	HAVE_DIRENT_H 1
+#define	HAVE_D_MD_ORDER 1
+#define	HAVE_ERRNO_H 1
+#undef	HAVE_EXT2FS_EXT2_FS_H
+#define	HAVE_FCHDIR 1
+#define	HAVE_FCNTL_H 1
+#define	HAVE_FNMATCH 1
+#define	HAVE_FNMATCH_H 1
+#define	HAVE_FNM_LEADING_DIR 1
+#define	HAVE_FTRUNCATE 1
+#define	HAVE_FUTIMES 1
+#undef	HAVE_GETXATTR
+#define	HAVE_GRP_H 1
+#define	HAVE_INTTYPES_H 1
+#define	HAVE_LANGINFO_H 1
+#undef	HAVE_LGETXATTR
+#undef	HAVE_LIBACL
+#define	HAVE_LIBARCHIVE 1
+#define	HAVE_LIBBZ2 1
+#define	HAVE_LIBZ 1
+#define	HAVE_LIMITS_H 1
+#undef	HAVE_LINUX_EXT2_FS_H
+#undef	HAVE_LINUX_FS_H
+#undef	HAVE_LISTXATTR
+#undef	HAVE_LLISTXATTR
+#define	HAVE_LOCALE_H 1
+#define	HAVE_LUTIMES 1
+#define	HAVE_MALLOC 1
+#define	HAVE_MEMMOVE 1
+#define	HAVE_MEMORY_H 1
+#define	HAVE_MEMSET 1
+#define	HAVE_NL_LANGINFO 1
+#define	HAVE_PATHS_H 1
+#define	HAVE_PWD_H 1
+#define	HAVE_SETLOCALE 1
+#define	HAVE_STDARG_H 1
+#define	HAVE_STDINT_H 1
+#define	HAVE_STDLIB_H 1
+#define	HAVE_STRCHR 1
+#define	HAVE_STRDUP 1
+#define	HAVE_STRERROR 1
+#define	HAVE_STRFTIME 1
+#define	HAVE_STRINGS_H 1
+#define	HAVE_STRING_H 1
+#define	HAVE_STRRCHR 1
+#undef	HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#define	HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define	HAVE_SYS_ACL_H 1
+#define	HAVE_SYS_IOCTL_H 1
+#define	HAVE_SYS_PARAM_H 1
+#define	HAVE_SYS_STAT_H 1
+#define	HAVE_TIME_H 1
+#define	HAVE_SYS_TYPES_H 1
+#define	HAVE_UINTMAX_T 1
+#define	HAVE_UNISTD_H 1
+#define	HAVE_UNSIGNED_LONG_LONG 1
+#define	HAVE_UTIMES 1
+#define	HAVE_VPRINTF 1
+#define	HAVE_ZLIB_H 1
+#undef	MAJOR_IN_MKDEV
+#define	STDC_HEADERS 1
+
--- /dev/null
+++ usr.bin/cpio/cpio_platform.h
@@ -0,0 +1,92 @@
+/*-
+ * 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/usr.bin/cpio/cpio_platform.h,v 1.2 2008/12/06 07:15:42 kientzle Exp $
+ */
+
+/*
+ * This header is the first thing included in any of the cpio
+ * source files.  As far as possible, platform-specific issues should
+ * be dealt with here and not within individual source files.
+ */
+
+#ifndef CPIO_PLATFORM_H_INCLUDED
+#define	CPIO_PLATFORM_H_INCLUDED
+
+#if defined(PLATFORM_CONFIG_H)
+/* Use hand-built config.h in environments that need it. */
+#include PLATFORM_CONFIG_H
+#elif defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "../config.h"
+#else
+/* Warn if cpio hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no built-in configuration in cpio_platform.h.
+#endif /* !HAVE_CONFIG_H */
+
+/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>  /* For __FBSDID */
+#else
+/* Just leaving this macro replacement empty leads to a dangling semicolon. */
+#define	__FBSDID(a)     struct _undefined_hack
+#endif
+
+#ifdef HAVE_LIBARCHIVE
+/* If we're using the platform libarchive, include system headers. */
+#include <archive.h>
+#include <archive_entry.h>
+#else
+/* Otherwise, include user headers. */
+#include "archive.h"
+#include "archive_entry.h"
+#endif
+
+/*
+ * We need to be able to display a filesize using printf().  The type
+ * and format string here must be compatible with one another and
+ * large enough for any file.
+ */
+#if HAVE_UINTMAX_T
+#define	CPIO_FILESIZE_TYPE	uintmax_t
+#define	CPIO_FILESIZE_PRINTF	"%ju"
+#else
+#if HAVE_UNSIGNED_LONG_LONG
+#define	CPIO_FILESIZE_TYPE	unsigned long long
+#define	CPIO_FILESIZE_PRINTF	"%llu"
+#else
+#define	CPIO_FILESIZE_TYPE	unsigned long
+#define	CPIO_FILESIZE_PRINTF	"%lu"
+#endif
+#endif
+
+/* How to mark functions that don't return. */
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+                          (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
+#define __LA_DEAD       __attribute__((__noreturn__))
+#else
+#define __LA_DEAD
+#endif
+
+#endif /* !CPIO_PLATFORM_H_INCLUDED */
--- /dev/null
+++ usr.bin/cpio/cmdline.c
@@ -0,0 +1,341 @@
+/*-
+ * 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
+ *    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 "cpio_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/cpio/cmdline.c,v 1.5 2008/12/06 07:30:40 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "cpio.h"
+
+/*
+ * Short options for cpio.  Please keep this sorted.
+ */
+static const char *short_options = "0AaBC:F:O:cdE:f:H:hijLlmopR:rtuvW:yZz";
+
+/*
+ * Long options for cpio.  Please keep this sorted.
+ */
+static const struct option {
+	const char *name;
+	int required;	/* 1 if this option requires an argument */
+	int equivalent;	/* Equivalent short option. */
+} cpio_longopts[] = {
+	{ "create",			0, 'o' },
+	{ "extract",			0, 'i' },
+	{ "file",			1, 'F' },
+	{ "format",             	1, 'H' },
+	{ "help",			0, 'h' },
+	{ "insecure",			0, OPTION_INSECURE },
+	{ "link",			0, 'l' },
+	{ "list",			0, 't' },
+	{ "make-directories",		0, 'd' },
+	{ "no-preserve-owner",		0, OPTION_NO_PRESERVE_OWNER },
+	{ "null",			0, '0' },
+	{ "owner",			1, 'R' },
+	{ "pass-through",		0, 'p' },
+	{ "preserve-modification-time", 0, 'm' },
+	{ "quiet",			0, OPTION_QUIET },
+	{ "unconditional",		0, 'u' },
+	{ "verbose",			0, 'v' },
+	{ "version",			0, OPTION_VERSION },
+	{ NULL, 0, 0 }
+};
+
+/*
+ * I used to try to select platform-provided getopt() or
+ * getopt_long(), but that caused a lot of headaches.  In particular,
+ * I couldn't consistently use long options in the test harness
+ * because not all platforms have getopt_long().  That in turn led to
+ * overuse of the -W hack in the test harness, which made it rough to
+ * run the test harness against GNU cpio.  (I periodically run the
+ * test harness here against GNU cpio as a sanity-check.  Yes,
+ * I've found a couple of bugs in GNU cpio that way.)
+ */
+int
+cpio_getopt(struct cpio *cpio)
+{
+	enum { state_start = 0, state_next_word, state_short, state_long };
+	static int state = state_start;
+	static char *opt_word;
+
+	const struct option *popt, *match = NULL, *match2 = NULL;
+	const char *p, *long_prefix = "--";
+	size_t optlength;
+	int opt = '?';
+	int required = 0;
+
+	cpio->optarg = NULL;
+
+	/* First time through, initialize everything. */
+	if (state == state_start) {
+		/* Skip program name. */
+		++cpio->argv;
+		--cpio->argc;
+		state = state_next_word;
+	}
+
+	/*
+	 * We're ready to look at the next word in argv.
+	 */
+	if (state == state_next_word) {
+		/* No more arguments, so no more options. */
+		if (cpio->argv[0] == NULL)
+			return (-1);
+		/* Doesn't start with '-', so no more options. */
+		if (cpio->argv[0][0] != '-')
+			return (-1);
+		/* "--" marks end of options; consume it and return. */
+		if (strcmp(cpio->argv[0], "--") == 0) {
+			++cpio->argv;
+			--cpio->argc;
+			return (-1);
+		}
+		/* Get next word for parsing. */
+		opt_word = *cpio->argv++;
+		--cpio->argc;
+		if (opt_word[1] == '-') {
+			/* Set up long option parser. */
+			state = state_long;
+			opt_word += 2; /* Skip leading '--' */
+		} else {
+			/* Set up short option parser. */
+			state = state_short;
+			++opt_word;  /* Skip leading '-' */
+		}
+	}
+
+	/*
+	 * We're parsing a group of POSIX-style single-character options.
+	 */
+	if (state == state_short) {
+		/* Peel next option off of a group of short options. */
+		opt = *opt_word++;
+		if (opt == '\0') {
+			/* End of this group; recurse to get next option. */
+			state = state_next_word;
+			return cpio_getopt(cpio);
+		}
+
+		/* Does this option take an argument? */
+		p = strchr(short_options, opt);
+		if (p == NULL)
+			return ('?');
+		if (p[1] == ':')
+			required = 1;
+
+		/* If it takes an argument, parse that. */
+		if (required) {
+			/* If arg is run-in, opt_word already points to it. */
+			if (opt_word[0] == '\0') {
+				/* Otherwise, pick up the next word. */
+				opt_word = *cpio->argv;
+				if (opt_word == NULL) {
+					cpio_warnc(0,
+					    "Option -%c requires an argument",
+					    opt);
+					return ('?');
+				}
+				++cpio->argv;
+				--cpio->argc;
+			}
+			if (opt == 'W') {
+				state = state_long;
+				long_prefix = "-W "; /* For clearer errors. */
+			} else {
+				state = state_next_word;
+				cpio->optarg = opt_word;
+			}
+		}
+	}
+
+	/* We're reading a long option, including -W long=arg convention. */
+	if (state == state_long) {
+		/* After this long option, we'll be starting a new word. */
+		state = state_next_word;
+
+		/* Option name ends at '=' if there is one. */
+		p = strchr(opt_word, '=');
+		if (p != NULL) {
+			optlength = (size_t)(p - opt_word);
+			cpio->optarg = (char *)(uintptr_t)(p + 1);
+		} else {
+			optlength = strlen(opt_word);
+		}
+
+		/* Search the table for an unambiguous match. */
+		for (popt = cpio_longopts; popt->name != NULL; popt++) {
+			/* Short-circuit if first chars don't match. */
+			if (popt->name[0] != opt_word[0])
+				continue;
+			/* If option is a prefix of name in table, record it.*/
+			if (strncmp(opt_word, popt->name, optlength) == 0) {
+				match2 = match; /* Record up to two matches. */
+				match = popt;
+				/* If it's an exact match, we're done. */
+				if (strlen(popt->name) == optlength) {
+					match2 = NULL; /* Forget the others. */
+					break;
+				}
+			}
+		}
+
+		/* Fail if there wasn't a unique match. */
+		if (match == NULL) {
+			cpio_warnc(0,
+			    "Option %s%s is not supported",
+			    long_prefix, opt_word);
+			return ('?');
+		}
+		if (match2 != NULL) {
+			cpio_warnc(0,
+			    "Ambiguous option %s%s (matches --%s and --%s)",
+			    long_prefix, opt_word, match->name, match2->name);
+			return ('?');
+		}
+
+		/* We've found a unique match; does it need an argument? */
+		if (match->required) {
+			/* Argument required: get next word if necessary. */
+			if (cpio->optarg == NULL) {
+				cpio->optarg = *cpio->argv;
+				if (cpio->optarg == NULL) {
+					cpio_warnc(0,
+					    "Option %s%s requires an argument",
+					    long_prefix, match->name);
+					return ('?');
+				}
+				++cpio->argv;
+				--cpio->argc;
+			}
+		} else {
+			/* Argument forbidden: fail if there is one. */
+			if (cpio->optarg != NULL) {
+				cpio_warnc(0,
+				    "Option %s%s does not allow an argument",
+				    long_prefix, match->name);
+				return ('?');
+			}
+		}
+		return (match->equivalent);
+	}
+
+	return (opt);
+}
+
+
+/*
+ * Parse the argument to the -R or --owner flag.
+ *
+ * The format is one of the following:
+ *   <user>    - Override user but not group
+ *   <user>:   - Override both, group is user's default group
+ *   <user>:<group> - Override both
+ *   :<group>  - Override group but not user
+ *
+ * A period can be used instead of the colon.
+ *
+ * Sets uid/gid as appropriate, -1 indicates uid/gid not specified.
+ *
+ */
+int
+owner_parse(const char *spec, int *uid, int *gid)
+{
+	const char *u, *ue, *g;
+
+	*uid = -1;
+	*gid = -1;
+
+	/*
+	 * Split spec into [user][:.][group]
+	 *  u -> first char of username, NULL if no username
+	 *  ue -> first char after username (colon, period, or \0)
+	 *  g -> first char of group name
+	 */
+	if (*spec == ':' || *spec == '.') {
+		/* If spec starts with ':' or '.', then just group. */
+		ue = u = NULL;
+		g = spec + 1;
+	} else {
+		/* Otherwise, [user] or [user][:] or [user][:][group] */
+		ue = u = spec;
+		while (*ue != ':' && *ue != '.' && *ue != '\0')
+			++ue;
+		g = ue;
+		if (*g != '\0') /* Skip : or . to find first char of group. */
+			++g;
+	}
+
+	if (u != NULL) {
+		/* Look up user: ue is first char after end of user. */
+		char *user;
+		struct passwd *pwent;
+
+		user = (char *)malloc(ue - u + 1);
+		if (user == NULL) {
+			cpio_warnc(errno, "Couldn't allocate memory");
+			return (1);
+		}
+		memcpy(user, u, ue - u);
+		user[ue - u] = '\0';
+		pwent = getpwnam(user);
+		if (pwent == NULL) {
+			cpio_warnc(errno, "Couldn't lookup user ``%s''", user);
+			return (1);
+		}
+		free(user);
+		*uid = pwent->pw_uid;
+		if (*ue != '\0' && *g == '\0')
+			*gid = pwent->pw_gid;
+	}
+	if (*g != '\0') {
+		struct group *grp;
+		grp = getgrnam(g);
+		if (grp != NULL)
+			*gid = grp->gr_gid;
+		else {
+			cpio_warnc(errno, "Couldn't look up group ``%s''", g);
+			return (1);
+		}
+	}
+	return (0);
+}
--- /dev/null
+++ usr.bin/cpio/err.c
@@ -0,0 +1,73 @@
+/*-
+ * 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
+ *    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 "cpio_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/cpio/err.c,v 1.1 2008/05/26 17:15:34 kientzle Exp $");
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "cpio.h"
+
+static void
+cpio_vwarnc(int code, const char *fmt, va_list ap)
+{
+	fprintf(stderr, "%s: ", cpio_progname);
+	vfprintf(stderr, fmt, ap);
+	if (code != 0)
+		fprintf(stderr, ": %s", strerror(code));
+	fprintf(stderr, "\n");
+}
+
+void
+cpio_warnc(int code, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	cpio_vwarnc(code, fmt, ap);
+	va_end(ap);
+}
+
+void
+cpio_errc(int eval, int code, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	cpio_vwarnc(code, fmt, ap);
+	va_end(ap);
+	exit(eval);
+}
--- /dev/null
+++ usr.bin/cpio/matching.h
@@ -0,0 +1,40 @@
+/*-
+ * 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
+ *    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.
+ *
+ * $FreeBSD: src/usr.bin/cpio/matching.h,v 1.1 2008/05/26 17:15:34 kientzle Exp $
+ */
+
+#ifndef MATCHING_H
+#define MATCHING_H
+
+#include "cpio.h"
+
+int	exclude(struct cpio *, const char *pattern);
+int	include(struct cpio *, const char *pattern);
+int	excluded(struct cpio *cpio, const char *pathname);
+void	cleanup_exclusions(struct cpio *cpio);
+int	unmatched_inclusions(struct cpio *cpio);
+
+#endif
--- /dev/null
+++ usr.bin/cpio/Makefile
@@ -0,0 +1,23 @@
+# $MidnightBSD: src/usr.bin/cpio/Makefile,v 1.1 2009/03/27 20:22:26 laffer1 Exp $
+# $FreeBSD: src/usr.bin/cpio/Makefile,v 1.6 2008/12/06 07:30:40 kientzle Exp $
+
+.include <bsd.own.mk>
+
+PROG=	bsdcpio
+BSDCPIO_VERSION_STRING=1.1.0
+SRCS=	cpio.c cmdline.c err.c matching.c pathmatch.c
+WARNS?=	6
+DPADD=	${LIBARCHIVE} ${LIBZ} ${LIBBZ2}
+CFLAGS+= -DBSDCPIO_VERSION_STRING=\"${BSDCPIO_VERSION_STRING}\"
+CFLAGS+= -DPLATFORM_CONFIG_H=\"config_midnightbsd.h\"
+LDADD+=	-larchive -lz -lbz2
+
+SYMLINKS=bsdcpio ${BINDIR}/cpio
+MLINKS= bsdcpio.1 cpio.1
+
+.PHONY: check test
+
+check test: $(PROG) bsdcpio.1.gz
+	cd ${.CURDIR}/test && make clean test
+
+.include <bsd.prog.mk>
--- /dev/null
+++ usr.bin/cpio/cpio.h
@@ -0,0 +1,118 @@
+/*-
+ * 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/usr.bin/cpio/cpio.h,v 1.7 2008/12/06 07:30:40 kientzle Exp $
+ */
+
+#ifndef CPIO_H_INCLUDED
+#define CPIO_H_INCLUDED
+
+#include "cpio_platform.h"
+#include <stdio.h>
+
+#define	DEFAULT_BYTES_PER_BLOCK	(20*512)
+
+/*
+ * The internal state for the "cpio" program.
+ *
+ * Keeping all of the state in a structure like this simplifies memory
+ * leak testing (at exit, anything left on the heap is suspect).  A
+ * pointer to this structure is passed to most cpio internal
+ * functions.
+ */
+struct cpio {
+	/* Option parsing */
+	const char	 *optarg;
+
+	/* Options */
+	const char	 *filename;
+	char		  mode; /* -i -o -p */
+	char		  compress; /* -j, -y, or -z */
+	const char	 *format; /* -H format */
+	int		  bytes_per_block; /* -b block_size */
+	int		  verbose;   /* -v */
+	int		  quiet;   /* --quiet */
+	int		  extract_flags; /* Flags for extract operation */
+	char		  symlink_mode; /* H or L, per BSD conventions */
+	const char	 *compress_program;
+	char		  line_separator; /* --null ? '\0' : '\n' */
+	int		  option_append; /* -A, only relevant for -o */
+	int		  option_atime_restore; /* -a */
+	int		  option_follow_links; /* -L */
+	int		  option_link; /* -l */
+	int		  option_list; /* -t */
+	int		  option_rename; /* -r */
+	char		 *destdir;
+	size_t		  pass_destpath_alloc;
+	char		 *pass_destpath;
+	int		  uid_override;
+	int		  gid_override;
+	int		  day_first; /* true if locale prefers day/mon */
+
+	/* If >= 0, then close this when done. */
+	int		  fd;
+
+	/* Miscellaneous state information */
+	struct archive	 *archive;
+	int		  argc;
+	char		**argv;
+	int		  return_value; /* Value returned by main() */
+	struct archive_entry_linkresolver *linkresolver;
+
+	struct name_cache *uname_cache;
+	struct name_cache *gname_cache;
+
+	/* Work data. */
+	struct matching  *matching;
+	char		 *buff;
+	size_t		  buff_size;
+};
+
+/* Name of this program; used in error reporting, initialized in main(). */
+const char *cpio_progname;
+
+void	cpio_errc(int _eval, int _code, const char *fmt, ...) __LA_DEAD;
+void	cpio_warnc(int _code, const char *fmt, ...);
+
+int	owner_parse(const char *, int *, int *);
+
+
+/* Fake short equivalents for long options that otherwise lack them. */
+enum {
+	OPTION_INSECURE = 1,
+	OPTION_NO_PRESERVE_OWNER,
+	OPTION_QUIET,
+	OPTION_VERSION
+};
+
+struct line_reader;
+
+struct line_reader *process_lines_init(const char *, char separator);
+const char *process_lines_next(struct line_reader *);
+void	process_lines_free(struct line_reader *);
+
+int	cpio_getopt(struct cpio *cpio);
+int	include_from_file(struct cpio *, const char *);
+
+#endif
--- /dev/null
+++ usr.bin/cpio/pathmatch.c
@@ -0,0 +1,250 @@
+/*-
+ * 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
+ *    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 "cpio_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/cpio/pathmatch.c,v 1.1 2008/05/26 17:15:34 kientzle Exp $");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "pathmatch.h"
+
+/*
+ * Check whether a character 'c' is matched by a list specification [...]:
+ *    * Leading '!' negates the class.
+ *    * <char>-<char> is a range of characters
+ *    * \<char> removes any special meaning for <char>
+ *
+ * Some interesting boundary cases:
+ *   a-d-e is one range (a-d) followed by two single characters - and e.
+ *   \a-\d is same as a-d
+ *   a\-d is three single characters: a, d, -
+ *   Trailing - is not special (so [a-] is two characters a and -).
+ *   Initial - is not special ([a-] is same as [-a] is same as [\\-a])
+ *   This function never sees a trailing \.
+ *   [] always fails
+ *   [!] always succeeds
+ */
+static int
+pm_list(const char *start, const char *end, const char c, int flags)
+{
+	const char *p = start;
+	char rangeStart = '\0', nextRangeStart;
+	int match = 1, nomatch = 0;
+
+	/* This will be used soon... */
+	(void)flags; /* UNUSED */
+
+	/* If this is a negated class, return success for nomatch. */
+	if (*p == '!' && p < end) {
+		match = 0;
+		nomatch = 1;
+		++p;
+	}
+
+	while (p < end) {
+		nextRangeStart = '\0';
+		switch (*p) {
+		case '-':
+			/* Trailing or initial '-' is not special. */
+			if ((rangeStart == '\0') || (p == end - 1)) {
+				if (*p == c)
+					return (match);
+			} else {
+				char rangeEnd = *++p;
+				if (rangeEnd == '\\')
+					rangeEnd = *++p;
+				if ((rangeStart <= c) && (c <= rangeEnd))
+					return (match);
+			}
+			break;
+		case '\\':
+			++p;
+			/* Fall through */
+		default:
+			if (*p == c)
+				return (match);
+			nextRangeStart = *p; /* Possible start of range. */
+		}
+		rangeStart = nextRangeStart;
+		++p;
+	}
+	return (nomatch);
+}
+
+/*
+ * If s is pointing to "./", ".//", "./././" or the like, skip it.
+ */
+static const char *
+pm_slashskip(const char *s) {
+	while (*s == '.' || *s == '/') {
+		if (s[0] != '/' && s[1] != '/')
+			break;
+		++s;
+	}
+	return (s);
+}
+
+static int
+pm(const char *p, const char *s, int flags)
+{
+	const char *end;
+
+	/*
+	 * Ignore leading './', './/', '././', etc.
+	 */
+	if (s[0] == '.' && s[1] == '/')
+		s = pm_slashskip(s + 1);
+	if (p[0] == '.' && p[1] == '/')
+		p = pm_slashskip(p + 1);
+
+	for (;;) {
+		switch (*p) {
+		case '\0':
+			if (s[0] == '/') {
+				if (flags & PATHMATCH_NO_ANCHOR_END)
+					return (1);
+				/* "dir" == "dir/" == "dir/." */
+				s = pm_slashskip(s);
+				if (s[0] == '.' && s[1] == '\0')
+					return (1);
+			}
+			return (*s == '\0');
+			break;
+		case '?':
+			/* ? always succeds, unless we hit end of 's' */
+			if (*s == '\0')
+				return (0);
+			break;
+		case '*':
+			/* "*" == "**" == "***" ... */
+			while (*p == '*')
+				++p;
+			/* Trailing '*' always succeeds. */
+			if (*p == '\0')
+				return (1);
+			while (*s) {
+				if (pathmatch(p, s, flags))
+					return (1);
+				++s;
+			}
+			return (0);
+			break;
+		case '[':
+			/*
+			 * Find the end of the [...] character class,
+			 * ignoring \] that might occur within the class.
+			 */
+			end = p + 1;
+			while (*end != '\0' && *end != ']') {
+				if (*end == '\\' && end[1] != '\0')
+					++end;
+				++end;
+			}
+			if (*end == ']') {
+				/* We found [...], try to match it. */
+				if (!pm_list(p + 1, end, *s, flags))
+					return (0);
+				p = end; /* Jump to trailing ']' char. */
+				break;
+			} else
+				/* No final ']', so just match '['. */
+				if (*p != *s)
+					return (0);
+			break;
+		default:
+			if (*p == *s)
+				break;
+			if ((*s == '\0') && (*p == '/')) {
+				p = pm_slashskip(p);
+				if (*p == '\0')
+					return (1);
+				if (p[0] == '.' && p[1] == '\0')
+					return (1);
+				return (0);
+			}
+			return (0);
+			break;
+		case '\\':
+			/* Trailing '\\' matches itself. */
+			if (p[1] == '\0') {
+				if (*s != '\\')
+					return (0);
+			} else {
+				++p;
+				if (*p != *s)
+					return (0);
+			}
+			break;
+		}
+		/*
+		 * TODO: pattern of "\/\.\/" should not match plain "/",
+		 * it should only match explicit "/./".
+		 */
+		if (*p == '/')
+			p = pm_slashskip(p);
+		else
+			++p;
+		if (*s == '/')
+			s = pm_slashskip(s);
+		else
+			++s;
+	}
+}
+
+/* Main entry point. */
+int
+pathmatch(const char *p, const char *s, int flags)
+{
+	/* Empty pattern only matches the empty string. */
+	if (p == NULL || *p == '\0')
+		return (s == NULL || *s == '\0');
+
+	/* Leading '^' anchors the start of the pattern. */
+	if (*p == '^') {
+		++p;
+		flags &= ~PATHMATCH_NO_ANCHOR_START;
+	}
+
+	/* Certain patterns anchor implicitly. */
+	if (*p == '*' || *p == '/')
+		return (pm(p, s, flags));
+
+	/* If start is unanchored, try to match start of each path element. */
+	if (flags & PATHMATCH_NO_ANCHOR_START) {
+		for ( ; p != NULL; p = strchr(p, '/')) {
+			if (*p == '/')
+				p++;
+			if (pm(p, s, flags))
+				return (1);
+		}
+		return (0);
+	}
+
+	/* Default: Match from beginning. */
+	return (pm(p, s, flags));
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_f.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/usr.bin/cpio/test/test_option_f.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+/*
+ * Unpack the archive in a new dir.
+ */
+static void
+unpack(const char *dirname, const char *option)
+{
+	int r;
+
+	assertEqualInt(0, mkdir(dirname, 0755));
+	assertEqualInt(0, chdir(dirname));
+	extract_reference_file("test_option_f.cpio");
+	r = systemf("%s -i %s < test_option_f.cpio > copy-no-a.out 2>copy-no-a.err", testprog, option);
+	assertEqualInt(0, r);
+	assertEqualInt(0, chdir(".."));
+}
+
+DEFINE_TEST(test_option_f)
+{
+	/* Calibrate:  No -f option, so everything should be extracted. */
+	unpack("t0", "");
+	assertEqualInt(0, access("t0/a123", F_OK));
+	assertEqualInt(0, access("t0/a234", F_OK));
+	assertEqualInt(0, access("t0/b123", F_OK));
+	assertEqualInt(0, access("t0/b234", F_OK));
+
+	/* Don't extract 'a*' files. */
+	unpack("t1", "-f 'a*'");
+	assert(0 != access("t1/a123", F_OK));
+	assert(0 != access("t1/a234", F_OK));
+	assertEqualInt(0, access("t1/b123", F_OK));
+	assertEqualInt(0, access("t1/b234", F_OK));
+
+	/* Don't extract 'b*' files. */
+	unpack("t2", "-f 'b*'");
+	assertEqualInt(0, access("t2/a123", F_OK));
+	assertEqualInt(0, access("t2/a234", F_OK));
+	assert(0 != access("t2/b123", F_OK));
+	assert(0 != access("t2/b234", F_OK));
+}
--- /dev/null
+++ usr.bin/cpio/test/main.c
@@ -0,0 +1,1015 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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>
+
+/*
+ * This same file is used pretty much verbatim for all test harnesses.
+ *
+ * The next few lines are the only differences.
+ */
+#define	PROGRAM "bsdcpio"    /* Name of program being tested. */
+#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */
+#undef	EXTRA_DUMP	     /* How to dump extra data */
+/* How to generate extra version info. */
+#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
+__FBSDID("$FreeBSD: src/usr.bin/cpio/test/main.c,v 1.3 2008/08/24 04:58:22 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__
+extern char *optarg;
+extern int optind;
+#endif
+
+/* 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
+ * message in the same format as GCC (file:line: message).
+ * It also includes some additional useful information.
+ * This makes it a lot easier to skim through test failures in
+ * Emacs.  ;-)
+ *
+ * It also supports a few special features specifically to simplify
+ * test harnesses:
+ *    failure(fmt, args) -- Stores a text string that gets
+ *          printed if the following assertion fails, good for
+ *          explaining subtle tests.
+ */
+static char msg[4096];
+
+/*
+ * For each test source file, we remember how many times each
+ * failure was reported.
+ */
+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.
+ */
+static int
+previous_failures(const char *filename, int line)
+{
+	unsigned int i;
+	int count;
+
+	if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
+		memset(failed_lines, 0, sizeof(failed_lines));
+	failed_filename = filename;
+
+	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
+		if (failed_lines[i].line == line) {
+			count = failed_lines[i].count;
+			failed_lines[i].count++;
+			return (count);
+		}
+		if (failed_lines[i].line == 0) {
+			failed_lines[i].line = line;
+			failed_lines[i].count = 1;
+			return (0);
+		}
+	}
+	return (0);
+}
+
+/*
+ * 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)
+{
+	test_filename = filename;
+	test_line = line;
+}
+
+/*
+ * Inform user that we're skipping a test.
+ */
+void
+test_skipping(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (previous_failures(test_filename, test_line))
+		return;
+
+	va_start(ap, fmt);
+	fprintf(stderr, " *** SKIPPING: ");
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+	++skips;
+}
+
+/* Common handling of failed tests. */
+static void
+report_failure(void *extra)
+{
+	if (msg[0] != '\0') {
+		fprintf(stderr, "   Description: %s\n", msg);
+		msg[0] = '\0';
+	}
+
+#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");
+		*(char *)(NULL) = 0;
+		exit(1);
+	}
+}
+
+/*
+ * 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;
+	if (a->line == 0 && b->line == 0)
+		return (0);
+	if (a->line == 0)
+		return (1);
+	if (b->line == 0)
+		return (-1);
+	return (a->line - b->line);
+}
+
+static void
+summarize(void)
+{
+	unsigned int i;
+
+	qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
+	    sizeof(failed_lines[0]), summarize_comparator);
+	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
+		if (failed_lines[i].line == 0)
+			break;
+		if (failed_lines[i].count > 1)
+			fprintf(stderr, "%s:%d: Failed %d times\n",
+			    failed_filename, failed_lines[i].line,
+			    failed_lines[i].count);
+	}
+	/* Clear the failure history for the next file. */
+	memset(failed_lines, 0, sizeof(failed_lines));
+}
+
+/* Set up a message to display only after a test fails. */
+void
+failure(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vsprintf(msg, fmt, ap);
+	va_end(ap);
+}
+
+/* Generic assert() just displays the failed condition. */
+int
+test_assert(const char *file, int line, int value, const char *condition, void *extra)
+{
+	++assertions;
+	if (value) {
+		msg[0] = '\0';
+		return (value);
+	}
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (value);
+	fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
+	fprintf(stderr, "   Condition: %s\n", condition);
+	report_failure(extra);
+	return (value);
+}
+
+/* assertEqualInt() displays the values of the two integers. */
+int
+test_assert_equal_int(const char *file, int line,
+    int v1, const char *e1, int v2, const char *e2, void *extra)
+{
+	++assertions;
+	if (v1 == v2) {
+		msg[0] = '\0';
+		return (1);
+	}
+	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);
+	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. */
+int
+test_assert_equal_string(const char *file, int line,
+    const char *v1, const char *e1,
+    const char *v2, const char *e2,
+    void *extra)
+{
+	++assertions;
+	if (v1 == NULL || v2 == NULL) {
+		if (v1 == v2) {
+			msg[0] = '\0';
+			return (1);
+		}
+	} else if (strcmp(v1, v2) == 0) {
+		msg[0] = '\0';
+		return (1);
+	}
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (0);
+	fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
+	    file, 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. */
+int
+test_assert_equal_wstring(const char *file, int line,
+    const wchar_t *v1, const char *e1,
+    const wchar_t *v2, const char *e2,
+    void *extra)
+{
+	++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 (1);
+	}
+	failures ++;
+	if (!verbose && previous_failures(file, line))
+		return (0);
+	fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
+	    file, 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 + 128);
+	n = read(fd, contents, s * 2 + 128);
+	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)
+ * 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 },
+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) {
+		printf("%d: %s\n", i, tests[i].name);
+		fflush(stdout);
+	}
+
+	/*
+	 * Always explicitly chdir() in case the last test moved us to
+	 * a strange place.
+	 */
+	if (chdir(tmpdir)) {
+		fprintf(stderr,
+		    "ERROR: Couldn't chdir to temp dir %s\n",
+		    tmpdir);
+		exit(1);
+	}
+	/* Create a temp directory for this specific test. */
+	if (mkdir(tests[i].name, 0755)) {
+		fprintf(stderr,
+		    "ERROR: Couldn't create temp dir ``%s''\n",
+		    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 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(const char *program)
+{
+	static const int limit = sizeof(tests) / sizeof(tests[0]);
+	int i;
+
+	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("  -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;
+	}
+
+#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':
+			keep_temp_files = 1;
+			break;
+		case 'p':
+#ifdef PROGRAM
+			testprog = optarg;
+#else
+			usage(progname);
+#endif
+			break;
+		case 'q':
+			quiet_flag++;
+			break;
+		case 'r':
+			refdir = optarg;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case '?':
+		default:
+			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_timestamp, sizeof(tmpdir_timestamp),
+		    "%Y-%m-%dT%H.%M.%S",
+		    localtime(&now));
+		sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i);
+		if (mkdir(tmpdir,0755) == 0)
+			break;
+		if (errno == EEXIST)
+			continue;
+		fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
+		    tmpdir);
+		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 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++) {
+			if (test_run(i, tmpdir))
+				tests_failed++;
+			tests_run++;
+		}
+	} else {
+		while (*(argv) != NULL) {
+			i = atoi(*argv);
+			if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
+				printf("*** INVALID Test %s\n", *argv);
+				usage(progname);
+			} else {
+				if (test_run(i, tmpdir))
+					tests_failed++;
+				tests_run++;
+			}
+			argv++;
+		}
+	}
+
+	/*
+	 * 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);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_f.cpio.uu
@@ -0,0 +1,16 @@
+$FreeBSD: src/usr.bin/cpio/test/test_option_f.cpio.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_option_f.cpio
+M,#<P-S`W,#`P,3,Q-C(Q-38Q,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P
+M,#`P,3`W,S4Q,3(U,C8P,#`P,#4P,#`P,#`P,#`P,&$Q,C,`,#<P-S`W,#`P
+M,3,Q-C(Q-38S,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P,#`P,3`W,S4Q
+M,3(U-#`P,#`P,#4P,#`P,#`P,#`P,&$R,S0`,#<P-S`W,#`P,3,Q-C(Q-38R
+M,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P,#`P,3`W,S4Q,3(U,S0P,#`P
+M,#4P,#`P,#`P,#`P,&(Q,C,`,#<P-S`W,#`P,3,Q-C(Q-38T,3`P-C0T,#`Q
+M-S4P,#`Q-S4P,#`P,#`Q,#`P,#`P,3`W,S4Q,3(U-#,P,#`P,#4P,#`P,#`P
+M,#`P,&(R,S0`,#<P-S`W,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`Q,#`P,#`P,#`P,#`P,#`P,#`P,#`P,3,P,#`P,#`P,#`P,%1204E,
+M15(A(2$`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+1````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_basic.c
@@ -0,0 +1,227 @@
+/*-
+ * 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/usr.bin/cpio/test/test_basic.c,v 1.4 2008/08/25 06:39:29 kientzle Exp $");
+
+static void
+verify_files(const char *target)
+{
+	struct stat st, st2;
+	char buff[128];
+	int r;
+
+	/*
+	 * Verify unpacked files.
+	 */
+
+	/* Regular file with 2 links. */
+	r = lstat("file", &st);
+	failure("Failed to stat file %s/file, errno=%d", target, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st.st_mode));
+		assertEqualInt(0644, st.st_mode & 0777);
+		assertEqualInt(10, st.st_size);
+		failure("file %s/file should have 2 links", target);
+		assertEqualInt(2, st.st_nlink);
+	}
+
+	/* Another name for the same file. */
+	r = lstat("linkfile", &st2);
+	failure("Failed to stat file %s/linkfile, errno=%d", target, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st2.st_mode));
+		assertEqualInt(0644, st2.st_mode & 0777);
+		assertEqualInt(10, st2.st_size);
+		failure("file %s/linkfile should have 2 links", target);
+		assertEqualInt(2, st2.st_nlink);
+		/* Verify that the two are really hardlinked. */
+		assertEqualInt(st.st_dev, st2.st_dev);
+		failure("%s/linkfile and %s/file should be hardlinked",
+		    target, target);
+		assertEqualInt(st.st_ino, st2.st_ino);
+	}
+
+	/* Symlink */
+	r = lstat("symlink", &st);
+	failure("Failed to stat file %s/symlink, errno=%d", target, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		failure("symlink should be a symlink; actual mode is %o",
+		    st.st_mode);
+		assert(S_ISLNK(st.st_mode));
+		if (S_ISLNK(st.st_mode)) {
+			r = readlink("symlink", buff, sizeof(buff));
+			assertEqualInt(r, 4);
+			buff[r] = '\0';
+			assertEqualString(buff, "file");
+		}
+	}
+
+	/* Another file with 1 link and different permissions. */
+	r = lstat("file2", &st);
+	failure("Failed to stat file %s/file2, errno=%d", target, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st.st_mode));
+		failure("%s/file2: st.st_mode = %o", target, st.st_mode);
+		assertEqualInt(0777, st.st_mode & 0777);
+		assertEqualInt(10, st.st_size);
+		failure("file %s/file2 should have 1 link", target);
+		assertEqualInt(1, st.st_nlink);
+	}
+
+	/* dir */
+	r = lstat("dir", &st);
+	if (r == 0) {
+		assertEqualInt(r, 0);
+		assert(S_ISDIR(st.st_mode));
+		failure("%s/dir: st.st_mode = %o", target, st.st_mode);
+		assertEqualInt(0775, st.st_mode & 0777);
+	}
+}
+
+static void
+basic_cpio(const char *target,
+    const char *pack_options,
+    const char *unpack_options,
+    const char *se)
+{
+	int r;
+
+	if (!assertEqualInt(0, mkdir(target, 0775)))
+	    return;
+
+	/* Use the cpio program to create an archive. */
+	r = systemf("%s -o %s < filelist >%s/archive 2>%s/pack.err",
+	    testprog, pack_options, target, target);
+	failure("Error invoking %s -o %s", testprog, pack_options);
+	assertEqualInt(r, 0);
+
+	chdir(target);
+
+	/* Verify stderr. */
+	failure("Expected: %s, options=%s", se, pack_options);
+	assertFileContents(se, strlen(se), "pack.err");
+
+	/*
+	 * Use cpio to unpack the archive into another directory.
+	 */
+	r = systemf("%s -i %s< archive >unpack.out 2>unpack.err",
+	    testprog, unpack_options);
+	failure("Error invoking %s -i %s", testprog, unpack_options);
+	assertEqualInt(r, 0);
+
+	/* Verify stderr. */
+	failure("Error invoking %s -i %s in dir %s", testprog, unpack_options, target);
+	assertFileContents(se, strlen(se), "unpack.err");
+
+	verify_files(target);
+
+	chdir("..");
+}
+
+static void
+passthrough(const char *target)
+{
+	int r;
+
+	if (!assertEqualInt(0, mkdir(target, 0775)))
+		return;
+
+	/*
+	 * Use cpio passthrough mode to copy files to another directory.
+	 */
+	r = systemf("%s -p %s <filelist >%s/stdout 2>%s/stderr",
+	    testprog, target, target, target);
+	failure("Error invoking %s -p", testprog);
+	assertEqualInt(r, 0);
+
+	chdir(target);
+
+	/* Verify stderr. */
+	failure("Error invoking %s -p in dir %s",
+	    testprog, target);
+	assertFileContents("1 block\n", 8, "stderr");
+
+	verify_files(target);
+	chdir("..");
+}
+
+DEFINE_TEST(test_basic)
+{
+	int fd;
+	int filelist;
+	int oldumask;
+
+	oldumask = umask(0);
+
+	/*
+	 * Create an assortment of files on disk.
+	 */
+	filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+
+	/* File with 10 bytes content. */
+	fd = open("file", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	write(filelist, "file\n", 5);
+
+	/* hardlink to above file. */
+	assertEqualInt(0, link("file", "linkfile"));
+	write(filelist, "linkfile\n", 9);
+
+	/* Symlink to above file. */
+	assertEqualInt(0, symlink("file", "symlink"));
+	write(filelist, "symlink\n", 8);
+
+	/* Another file with different permissions. */
+	fd = open("file2", O_CREAT | O_WRONLY, 0777);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	write(filelist, "file2\n", 6);
+
+	/* Directory. */
+	assertEqualInt(0, mkdir("dir", 0775));
+	write(filelist, "dir\n", 4);
+	/* All done. */
+	close(filelist);
+
+	umask(022);
+
+	/* Archive/dearchive with a variety of options. */
+	basic_cpio("copy", "", "", "2 blocks\n");
+	basic_cpio("copy_odc", "--format=odc", "", "2 blocks\n");
+	basic_cpio("copy_newc", "-H newc", "", "2 blocks\n");
+	basic_cpio("copy_cpio", "-H odc", "", "2 blocks\n");
+	basic_cpio("copy_ustar", "-H ustar", "", "9 blocks\n");
+	/* Copy in one step using -p */
+	passthrough("passthrough");
+
+	umask(oldumask);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_gcpio_compat_ref.bin.uu
@@ -0,0 +1,16 @@
+$FreeBSD: src/usr.bin/cpio/test/test_gcpio_compat_ref.bin.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_gcpio_compat_ref.bin
+MQW%9`*IWI('H`^@#`@````U'=YD%````"@!F:6QE```Q,C,T-38W.#D*QW%9
+M`*IWI('H`^@#`@````U'=YD)````"@!L:6YK9FEL90``,3(S-#4V-S at Y"L=Q
+M60"K=^VAZ`/H`P$````-1X29"`````0`<WEM;&EN:P!F:6QEQW%9`*YW_4'H
+M`^@#`@````U'A9D$``````!D:7(`QW$``````````````0`````````+````
+M``!44D%)3$52(2$A````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+1````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_option_m.cpio.uu
@@ -0,0 +1,16 @@
+$FreeBSD: src/usr.bin/cpio/test/test_option_m.cpio.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_option_m.cpio
+M,#<P-S`W,#`P,3,Q-#4P,#8T,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P
+M,#`P,#`P,#`P,#`P,#$P,#`P,#4P,#`P,#`P,#`P,&9I;&4`,#<P-S`W,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,3,P,#`P,#`P,#`P,%1204E,15(A(2$`````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+1````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/Makefile
@@ -0,0 +1,70 @@
+# $FreeBSD: src/usr.bin/cpio/test/Makefile,v 1.4 2008/08/24 05:49:36 kientzle Exp $
+
+# Where to find the cpio sources (for the internal unit tests)
+CPIO_SRCDIR=${.CURDIR}/..
+.PATH: ${CPIO_SRCDIR}
+
+# Some cpio sources are pulled in for white-box tests
+CPIO_SRCS= cmdline.c err.c pathmatch.c
+
+TESTS=	\
+	test_0.c				\
+	test_basic.c				\
+	test_format_newc.c			\
+	test_gcpio_compat.c			\
+	test_option_a.c				\
+	test_option_B.c				\
+	test_option_c.c				\
+	test_option_d.c				\
+	test_option_f.c				\
+	test_option_help.c			\
+	test_option_L.c				\
+	test_option_ell.c			\
+	test_option_m.c				\
+	test_option_t.c				\
+	test_option_u.c				\
+	test_option_version.c			\
+	test_option_y.c				\
+	test_option_z.c				\
+	test_owner_parse.c			\
+	test_passthrough_dotdot.c		\
+	test_passthrough_reverse.c		\
+	test_pathmatch.c
+
+# Build the test program
+SRCS= list.h					\
+	${CPIO_SRCS}				\
+	${TESTS}				\
+	main.c
+
+CLEANFILES+= list.h bsdcpio_test
+
+NO_MAN=yes
+
+PROG=bsdcpio_test
+DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
+CFLAGS+=	-DPLATFORM_CONFIG_H=\"config_freebsd.h\"
+CFLAGS+=	-I..
+LDADD= -larchive -lz -lbz2
+CFLAGS+= -static -g -O2 -Wall
+CFLAGS+= -I${.OBJDIR}
+CFLAGS+= -I${CPIO_SRCDIR}
+
+# Uncomment to link against dmalloc
+#LDADD+= -L/usr/local/lib -ldmalloc
+#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
+WARNS=6
+
+check test:	bsdcpio_test
+	${.OBJDIR}/bsdcpio_test -p ${.OBJDIR}/../bsdcpio -r ${.CURDIR}
+
+${.OBJDIR}/list.h list.h: ${TESTS} Makefile
+	(cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h
+
+clean:
+	rm -f ${CLEANFILES}
+	rm -f *~
+	-chmod -R +w /tmp/bsdcpio_test.*
+	rm -rf /tmp/bsdcpio_test.*
+
+.include <bsd.prog.mk>
--- /dev/null
+++ usr.bin/cpio/test/test_option_y.c
@@ -0,0 +1,52 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_y.c,v 1.2 2008/08/24 06:21:00 kientzle Exp $");
+
+DEFINE_TEST(test_option_y)
+{
+	char *p;
+	int fd;
+	int r;
+	size_t s;
+
+	/* Create a file. */
+	fd = open("f", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(1, write(fd, "a", 1));
+	close(fd);
+
+	/* Archive it with bzip2 compression. */
+	r = systemf("echo f | %s -oy >archive.out 2>archive.err",
+	    testprog);
+	failure("-y (bzip) option seems to be broken");
+	if (assertEqualInt(r, 0)) {
+		assertFileContents("1 block\n", 8, "archive.err");
+		/* Check that the archive file has a bzip2 signature. */
+		p = slurpfile(&s, "archive.out");
+		assert(s > 2);
+		assertEqualMem(p, "BZh9", 4);
+	}
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_m.c
@@ -0,0 +1,70 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_m.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+
+DEFINE_TEST(test_option_m)
+{
+	struct stat st;
+	int r;
+	time_t now;
+
+	/*
+	 * The reference archive has one file with an mtime in 1970, 1
+	 * second after the start of the epoch.
+	 */
+
+	/* Restored without -m, the result should have a current mtime. */
+	assertEqualInt(0, mkdir("without-m", 0755));
+	assertEqualInt(0, chdir("without-m"));
+	extract_reference_file("test_option_m.cpio");
+	r = systemf("%s -i < test_option_m.cpio >out 2>err", testprog);
+	now = time(NULL);
+	assertEqualInt(r, 0);
+	assertEmptyFile("out");
+	assertFileContents("1 block\n", 8, "err");
+	assertEqualInt(0, stat("file", &st));
+	/* Should have been created within the last few seconds. */
+	assert(st.st_mtime <= now);
+	assert(st.st_mtime > now - 5);
+
+	/* With -m, it should have an mtime in 1970. */
+	assertEqualInt(0, chdir(".."));
+	assertEqualInt(0, mkdir("with-m", 0755));
+	assertEqualInt(0, chdir("with-m"));
+	extract_reference_file("test_option_m.cpio");
+	r = systemf("%s -im < test_option_m.cpio >out 2>err", testprog);
+	now = time(NULL);
+	assertEqualInt(r, 0);
+	assertEmptyFile("out");
+	assertFileContents("1 block\n", 8, "err");
+	assertEqualInt(0, stat("file", &st));
+	/*
+	 * mtime in reference archive is '1' == 1 second after
+	 * midnight Jan 1, 1970 UTC.
+	 */
+	assertEqualInt(1, st.st_mtime);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_u.c
@@ -0,0 +1,84 @@
+/*-
+ * 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"
+#include <utime.h>
+__FBSDID("$FreeBSD: src/usr.bin/cpio/test/test_option_u.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+DEFINE_TEST(test_option_u)
+{
+	struct utimbuf times;
+	char *p;
+	size_t s;
+	int fd;
+	int r;
+
+	/* Create a file. */
+	fd = open("f", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(1, write(fd, "a", 1));
+	close(fd);
+
+	/* Copy the file to the "copy" dir. */
+	r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
+	    testprog);
+	assertEqualInt(r, 0);
+
+	/* Check that the file contains only a single "a" */
+	p = slurpfile(&s, "copy/f");
+	assertEqualInt(s, 1);
+	assertEqualMem(p, "a", 1);
+
+	/* Recreate the file with a single "b" */
+	fd = open("f", O_CREAT | O_TRUNC | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(1, write(fd, "b", 1));
+	close(fd);
+
+	/* Set the mtime to the distant past. */
+	memset(&times, 0, sizeof(times));
+	times.actime = 1;
+	times.modtime = 3;
+	assertEqualInt(0, utime("f", &times));
+
+	/* Copy the file to the "copy" dir. */
+	r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
+	    testprog);
+	assertEqualInt(r, 0);
+
+	/* Verify that the file hasn't changed (it wasn't overwritten) */
+	p = slurpfile(&s, "copy/f");
+	assertEqualInt(s, 1);
+	assertEqualMem(p, "a", 1);
+
+	/* Copy the file to the "copy" dir with -u (force) */
+	r = systemf("echo f | %s -pud copy >copy.out 2>copy.err",
+	    testprog);
+	assertEqualInt(r, 0);
+
+	/* Verify that the file has changed (it was overwritten) */
+	p = slurpfile(&s, "copy/f");
+	assertEqualInt(s, 1);
+	assertEqualMem(p, "b", 1);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_passthrough_dotdot.c
@@ -0,0 +1,89 @@
+/*-
+ * 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/usr.bin/cpio/test/test_passthrough_dotdot.c,v 1.4 2008/08/24 06:21:00 kientzle Exp $");
+
+/*
+ * Verify that "cpio -p .." works.
+ */
+
+DEFINE_TEST(test_passthrough_dotdot)
+{
+	struct stat st;
+	int fd, r;
+	int filelist;
+	int oldumask;
+
+	oldumask = umask(0);
+
+	/*
+	 * Create an assortment of files on disk.
+	 */
+	filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+
+	/* Directory. */
+	assertEqualInt(0, mkdir("dir", 0755));
+	assertEqualInt(0, chdir("dir"));
+
+	write(filelist, ".\n", 2);
+
+	/* File with 10 bytes content. */
+	fd = open("file", O_CREAT | O_WRONLY, 0642);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	write(filelist, "file\n", 5);
+
+	/* All done. */
+	close(filelist);
+
+
+	/*
+	 * Use cpio passthrough mode to copy files to another directory.
+	 */
+	r = systemf("%s -pdvm .. <../filelist >../stdout 2>../stderr",
+	    testprog);
+	failure("Error invoking %s -pd ..", testprog);
+	assertEqualInt(r, 0);
+
+	assertEqualInt(0, chdir(".."));
+
+	/* Verify stderr and stdout. */
+	assertFileContents("../.\n../file\n1 block\n", 21, "stderr");
+	assertEmptyFile("stdout");
+
+	/* Regular file. */
+	r = lstat("file", &st);
+	failure("Failed to stat file, errno=%d", errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st.st_mode));
+		assertEqualInt(0642, st.st_mode & 0777);
+		assertEqualInt(10, st.st_size);
+		assertEqualInt(1, st.st_nlink);
+	}
+
+	umask(oldumask);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_z.c
@@ -0,0 +1,52 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_z.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+DEFINE_TEST(test_option_z)
+{
+	char *p;
+	int fd;
+	int r;
+	size_t s;
+
+	/* Create a file. */
+	fd = open("f", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(1, write(fd, "a", 1));
+	close(fd);
+
+	/* Archive it with gzip compression. */
+	r = systemf("echo f | %s -oz >archive.out 2>archive.err",
+	    testprog);
+	failure("-z option seems to be broken");
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		/* Check that the archive file has a gzip signature. */
+		p = slurpfile(&s, "archive.out");
+		assert(s > 2);
+		assertEqualMem(p, "\x1f\x8b\x08\x00", 4);
+	}
+}
--- /dev/null
+++ usr.bin/cpio/test/test_format_newc.c
@@ -0,0 +1,260 @@
+/*-
+ * 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/usr.bin/cpio/test/test_format_newc.c,v 1.2 2008/08/22 02:09:10 kientzle Exp $");
+
+static int
+is_hex(const char *p, size_t l)
+{
+	while (l > 0) {
+		if ((*p >= '0' && *p <= '9')
+		    || (*p >= 'a' && *p <= 'f')
+		    || (*p >= 'A' && *p <= 'F'))
+		{
+			--l;
+			++p;
+		} else
+			return (0);
+
+	}
+	return (1);
+}
+
+static int
+from_hex(const char *p, size_t l)
+{
+	int r = 0;
+
+	while (l > 0) {
+		r *= 16;
+		if (*p >= 'a' && *p <= 'f')
+			r += *p + 10 - 'a';
+		else if (*p >= 'A' && *p <= 'F')
+			r += *p + 10 - 'A';
+		else
+			r += *p - '0';
+		--l;
+		++p;
+	}
+	return (r);
+}
+
+DEFINE_TEST(test_format_newc)
+{
+	int fd, list;
+	int r;
+	int devmajor, devminor, ino, gid;
+	time_t t, t2, now;
+	char *p, *e;
+	size_t s, fs, ns;
+	mode_t oldmask;
+
+	oldmask = umask(0);
+
+	/*
+	 * Create an assortment of files.
+	 * TODO: Extend this to cover more filetypes.
+	 */
+	list = open("list", O_CREAT | O_WRONLY, 0644);
+
+	/* "file1" */
+	fd = open("file1", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	assertEqualInt(6, write(list, "file1\n", 6));
+
+	/* "hardlink" */
+	assertEqualInt(0, link("file1", "hardlink"));
+	assertEqualInt(9, write(list, "hardlink\n", 9));
+
+	/* Another hardlink, but this one won't be archived. */
+	assertEqualInt(0, link("file1", "hardlink2"));
+
+	/* "symlink" */
+	assertEqualInt(0, symlink("file1", "symlink"));
+	assertEqualInt(8, write(list, "symlink\n", 8));
+
+	/* "dir" */
+	assertEqualInt(0, mkdir("dir", 0775));
+	assertEqualInt(4, write(list, "dir\n", 4));
+
+	/* Record some facts about what we just created: */
+	now = time(NULL); /* They were all created w/in last two seconds. */
+
+	/* Use the cpio program to create an archive. */
+	close(list);
+	r = systemf("%s -o --format=newc <list >newc.out 2>newc.err",
+	    testprog);
+	if (!assertEqualInt(r, 0))
+		return;
+
+	/* Verify that nothing went to stderr. */
+	assertFileContents("2 blocks\n", 9, "newc.err");
+
+	/* Verify that stdout is a well-formed cpio file in "newc" format. */
+	p = slurpfile(&s, "newc.out");
+	assertEqualInt(s, 1024);
+	e = p;
+
+	/*
+	 * Some of these assertions could be stronger, but it's
+	 * a little tricky because they depend on the local environment.
+	 */
+
+	/* First entry is "file1" */
+	assert(is_hex(e, 110)); /* Entire header is octal digits. */
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	ino = from_hex(e + 6, 8); /* ino */
+	assertEqualInt(0x81a4, from_hex(e + 14, 8)); /* Mode */
+	assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
+	gid = from_hex(e + 30, 8); /* gid */
+	assertEqualMem(e + 38, "00000003", 8); /* nlink */
+	t = from_hex(e + 46, 8); /* mtime */
+	failure("t=0x%08x now=0x%08x=%d", t, now, now);
+	assert(t <= now); /* File wasn't created in future. */
+	failure("t=0x%08x now - 2=0x%08x = %d", t, now - 2, now - 2);
+	assert(t >= now - 2); /* File was created w/in last 2 secs. */
+	failure("newc format stores body only with last appearance of a link\n"
+	    "       first appearance should be empty, so this file size\n"
+	    "       field should be zero");
+	assertEqualInt(0, from_hex(e + 54, 8)); /* File size */
+	fs = from_hex(e + 54, 8);
+	fs += 3 & -fs;
+	devmajor = from_hex(e + 62, 8); /* devmajor */
+	devminor = from_hex(e + 70, 8); /* devminor */
+	assert(is_hex(e + 78, 8)); /* rdevmajor */
+	assert(is_hex(e + 86, 8)); /* rdevminor */
+	assertEqualMem(e + 94, "00000006", 8); /* Name size */
+	ns = from_hex(e + 94, 8);
+	ns += 3 & (-ns - 2);
+	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
+	assertEqualMem(e + 110, "file1\0", 6); /* Name contents */
+	/* Since there's another link, no file contents here. */
+	/* But add in file size so that an error here doesn't cascade. */
+	e += 110 + fs + ns;
+
+	/* "symlink" pointing to "file1" */
+	assert(is_hex(e, 110));
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assert(is_hex(e + 6, 8)); /* ino */
+	assertEqualInt(0xa1ff, from_hex(e + 14, 8)); /* Mode */
+	assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
+	assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
+	assertEqualMem(e + 38, "00000001", 8); /* nlink */
+	t2 = from_hex(e + 46, 8); /* mtime */
+	failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
+	assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
+	assertEqualMem(e + 54, "00000005", 8); /* File size */
+	fs = from_hex(e + 54, 8);
+	fs += 3 & -fs;
+	assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
+	assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
+	assert(is_hex(e + 78, 8)); /* rdevmajor */
+	assert(is_hex(e + 86, 8)); /* rdevminor */
+	assertEqualMem(e + 94, "00000008", 8); /* Name size */
+	ns = from_hex(e + 94, 8);
+	ns += 3 & (-ns - 2);
+	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
+	assertEqualMem(e + 110, "symlink\0\0\0", 10); /* Name contents */
+	assertEqualMem(e + 110 + ns, "file1\0\0\0", 8); /* symlink target */
+	e += 110 + fs + ns;
+
+	/* "dir" */
+	assert(is_hex(e, 110));
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assert(is_hex(e + 6, 8)); /* ino */
+	assertEqualInt(0x41fd, from_hex(e + 14, 8)); /* Mode */
+	assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
+	assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
+	assertEqualMem(e + 38, "00000002", 8); /* nlink */
+	t2 = from_hex(e + 46, 8); /* mtime */
+	failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
+	assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
+	assertEqualMem(e + 54, "00000000", 8); /* File size */
+	fs = from_hex(e + 54, 8);
+	fs += 3 & -fs;
+	assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
+	assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
+	assert(is_hex(e + 78, 8)); /* rdevmajor */
+	assert(is_hex(e + 86, 8)); /* rdevminor */
+	assertEqualMem(e + 94, "00000004", 8); /* Name size */
+	ns = from_hex(e + 94, 8);
+	ns += 3 & (-ns - 2);
+	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
+	assertEqualMem(e + 110, "dir\0\0\0", 6); /* Name contents */
+	e += 110 + fs + ns;
+
+	/* Hardlink identical to "file1" */
+	/* Since we only wrote two of the three links to this
+	 * file, this link should get deferred by the hardlink logic. */
+	assert(is_hex(e, 110));
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	failure("If these aren't the same, then the hardlink detection failed to match them.");
+	assertEqualInt(ino, from_hex(e + 6, 8)); /* ino */
+	assertEqualInt(0x81a4, from_hex(e + 14, 8)); /* Mode */
+	assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
+	assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
+	assertEqualMem(e + 38, "00000003", 8); /* nlink */
+	t2 = from_hex(e + 46, 8); /* mtime */
+	failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
+	assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
+	assertEqualInt(10, from_hex(e + 54, 8)); /* File size */
+	fs = from_hex(e + 54, 8);
+	fs += 3 & -fs;
+	assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
+	assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
+	assert(is_hex(e + 78, 8)); /* rdevmajor */
+	assert(is_hex(e + 86, 8)); /* rdevminor */
+	assertEqualMem(e + 94, "00000009", 8); /* Name size */
+	ns = from_hex(e + 94, 8);
+	ns += 3 & (-ns - 2);
+	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
+	assertEqualMem(e + 110, "hardlink\0\0", 10); /* Name contents */
+	assertEqualMem(e + 110 + ns, "123456789\0\0\0", 12); /* File contents */
+	e += 110 + ns + fs;
+
+	/* Last entry is end-of-archive marker. */
+	assert(is_hex(e, 110));
+	assertEqualMem(e + 0, "070701", 6); /* Magic */
+	assertEqualMem(e + 8, "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); /* 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 */
+	assertEqualInt(11, from_hex(e + 94, 8)); /* name size */
+	assertEqualMem(e + 102, "00000000", 8); /* check field */
+	assertEqualMem(e + 110, "TRAILER!!!\0\0", 12); /* Name */
+
+	free(p);
+
+	umask(oldmask);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_owner_parse.c
@@ -0,0 +1,68 @@
+/*-
+ * 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/usr.bin/cpio/test/test_owner_parse.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+#include "../cpio.h"
+
+DEFINE_TEST(test_owner_parse)
+{
+	int uid, gid;
+
+	cpio_progname = "Ignore this message";
+
+	assertEqualInt(0, owner_parse("root", &uid, &gid));
+	assertEqualInt(0, uid);
+	assertEqualInt(-1, gid);
+
+
+	assertEqualInt(0, owner_parse("root:", &uid, &gid));
+	assertEqualInt(0, uid);
+	assertEqualInt(0, gid);
+
+	assertEqualInt(0, owner_parse("root.", &uid, &gid));
+	assertEqualInt(0, uid);
+	assertEqualInt(0, gid);
+
+	/*
+	 * TODO: Lookup current user/group name, build strings and
+	 * use those to verify username/groupname lookups for ordinary
+	 * users.
+	 */
+
+	/*
+	 * TODO: Rework owner_parse to either return a char * pointing
+	 * to an error message or accept a function pointer to an
+	 * error-reporting routine so that the following tests don't
+	 * generate any output.
+	 *
+	 * Alternatively, redirect stderr temporarily to suppress the output.
+	 */
+
+	assertEqualInt(1, owner_parse(":nonexistentgroup", &uid, &gid));
+	assertEqualInt(1, owner_parse("root:nonexistentgroup", &uid, &gid));
+	assertEqualInt(1,
+	    owner_parse("nonexistentuser:nonexistentgroup", &uid, &gid));
+}
--- /dev/null
+++ usr.bin/cpio/test/test_gcpio_compat_ref.crc.uu
@@ -0,0 +1,27 @@
+$FreeBSD: src/usr.bin/cpio/test/test_gcpio_compat_ref.crc.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_gcpio_compat_ref.crc
+M,#<P-S`R,#`S,S<W86$P,#`P.#%A-#`P,#`P,V4X,#`P,#`S93 at P,#`P,#`P
+M,C0W,&0Y.3<W,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`U,#`P,#`P,#!F:6QE```P-S`W,#(P,#,S-S=A83`P,#`X
+M,6$T,#`P,#`S93 at P,#`P,#-E.#`P,#`P,#`R-#<P9#DY-S<P,#`P,#`P83`P
+M,#`P,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#DP,#`P,#%E
+M-VQI;FMF:6QE```Q,C,T-38W.#D*```P-S`W,#(P,#,S-S=A8C`P,#!A,65D
+M,#`P,#`S93 at P,#`P,#-E.#`P,#`P,#`Q-#<P9#DY.#0P,#`P,#`P-#`P,#`P
+M,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#@P,#`P,#`P,'-Y
+M;6QI;FL```!F:6QE,#<P-S`R,#`S,S<W864P,#`P-#%F9#`P,#`P,V4X,#`P
+M,#`S93 at P,#`P,#`P,C0W,&0Y.3 at U,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`T,#`P,#`P,#!D:7(````P-S`W,#(P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,&(P,#`P,#`P,%1204E,15(A(2$`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+B````````````````````````````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_gcpio_compat.c
@@ -0,0 +1,129 @@
+/*-
+ * 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/usr.bin/cpio/test/test_gcpio_compat.c,v 1.2 2008/08/22 02:27:06 kientzle Exp $");
+
+
+static void
+unpack_test(const char *from, const char *options, const char *se)
+{
+	struct stat st, st2;
+	char buff[128];
+	int r;
+
+	/* Create a work dir named after the file we're unpacking. */
+	assertEqualInt(0, mkdir(from, 0775));
+	chdir(from);
+
+	/*
+	 * Use cpio to unpack the sample archive
+	 */
+	extract_reference_file(from);
+	r = systemf("%s -i %s < %s >unpack.out 2>unpack.err",
+	    testprog, options, from);
+	failure("Error invoking %s -i %s < %s",
+	    testprog, options, from);
+	assertEqualInt(r, 0);
+
+	/* Verify that nothing went to stderr. */
+	failure("Error invoking %s -i %s < %s", testprog, options, from);
+	assertFileContents(se, strlen(se), "unpack.err");
+
+	/*
+	 * Verify unpacked files.
+	 */
+
+	/* Regular file with 2 links. */
+	r = lstat("file", &st);
+	failure("Failed to stat file %s/file, errno=%d", from, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st.st_mode));
+		assertEqualInt(0644, st.st_mode & 0777);
+		failure("file %s/file", from);
+		assertEqualInt(10, st.st_size);
+		failure("file %s/file", from);
+		assertEqualInt(2, st.st_nlink);
+	}
+
+	/* Another name for the same file. */
+	r = lstat("linkfile", &st2);
+	failure("Failed to stat file %s/linkfile, errno=%d", from, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st2.st_mode));
+		assertEqualInt(0644, st2.st_mode & 0777);
+		failure("file %s/file", from);
+		assertEqualInt(10, st2.st_size);
+		failure("file %s/file", from);
+		assertEqualInt(2, st2.st_nlink);
+		failure("file and linkfile should be hardlinked");
+		assertEqualInt(st.st_dev, st2.st_dev);
+		failure("file %s/file", from);
+		assertEqualInt(st.st_ino, st2.st_ino);
+	}
+
+	/* Symlink */
+	r = lstat("symlink", &st);
+	failure("Failed to stat file %s/symlink, errno=%d", from, errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		failure("symlink should be a symlink; actual mode is %o",
+		    st.st_mode);
+		assert(S_ISLNK(st.st_mode));
+		if (S_ISLNK(st.st_mode)) {
+			r = readlink("symlink", buff, sizeof(buff));
+			assertEqualInt(r, 4);
+			buff[r] = '\0';
+			assertEqualString(buff, "file");
+		}
+	}
+
+	/* dir */
+	r = lstat("dir", &st);
+	if (r == 0) {
+		assertEqualInt(r, 0);
+		assert(S_ISDIR(st.st_mode));
+		assertEqualInt(0775, st.st_mode & 0777);
+	}
+
+	chdir("..");
+}
+
+DEFINE_TEST(test_gcpio_compat)
+{
+	int oldumask;
+
+	oldumask = umask(0);
+
+	/* Dearchive sample files with a variety of options. */
+	unpack_test("test_gcpio_compat_ref.bin", "", "1 block\n");
+	unpack_test("test_gcpio_compat_ref.crc", "", "2 blocks\n");
+	unpack_test("test_gcpio_compat_ref.newc", "", "2 blocks\n");
+	/* gcpio-2.9 only reads 6 blocks here */
+	unpack_test("test_gcpio_compat_ref.ustar", "", "7 blocks\n");
+
+	umask(oldumask);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_d.c
@@ -0,0 +1,68 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_d.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+
+DEFINE_TEST(test_option_d)
+{
+	struct stat st;
+	int r, fd;
+
+	/*
+	 * Create a file in a directory.
+	 */
+	assertEqualInt(0, mkdir("dir", 0755));
+	fd = open("dir/file", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	close(fd);
+
+	/* Create an archive. */
+	r = systemf("echo dir/file | %s -o > archive.cpio 2>archive.err", testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "archive.err");
+	assertEqualInt(0, stat("archive.cpio", &st));
+	assertEqualInt(512, st.st_size);
+
+	/* Dearchive without -d, this should fail. */
+	assertEqualInt(0, mkdir("without-d", 0755));
+	assertEqualInt(0, chdir("without-d"));
+	r = systemf("%s -i < ../archive.cpio >out 2>err", testprog);
+	assertEqualInt(r, 0);
+	assertEmptyFile("out");
+	/* And the file should not be restored. */
+	assert(0 != stat("dir/file", &st));
+
+	/* Dearchive with -d, this should succeed. */
+	assertEqualInt(0, chdir(".."));
+	assertEqualInt(0, mkdir("with-d", 0755));
+	assertEqualInt(0, chdir("with-d"));
+	r = systemf("%s -id < ../archive.cpio >out 2>err", testprog);
+	assertEqualInt(r, 0);
+	assertEmptyFile("out");
+	assertFileContents("1 block\n", 8, "err");
+	/* And the file should be restored. */
+	assertEqualInt(0, stat("dir/file", &st));
+}
--- /dev/null
+++ usr.bin/cpio/test/test_passthrough_reverse.c
@@ -0,0 +1,104 @@
+/*-
+ * 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/usr.bin/cpio/test/test_passthrough_reverse.c,v 1.2 2008/08/24 06:21:00 kientzle Exp $");
+
+/*
+ * As reported by Bernd Walter:  Some people are in the habit of
+ * using "find -d" to generate a list for cpio -p because that
+ * copies the top-level dir last, which preserves owner and mode
+ * information.  That's not necessary for bsdcpio (libarchive defers
+ * restoring directory information), but bsdcpio should still generate
+ * the correct results with this usage.
+ */
+
+DEFINE_TEST(test_passthrough_reverse)
+{
+	struct stat st;
+	int fd, r;
+	int filelist;
+	int oldumask;
+
+	oldumask = umask(0);
+
+	/*
+	 * Create an assortment of files on disk.
+	 */
+	filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+
+	/* Directory. */
+	assertEqualInt(0, mkdir("dir", 0743));
+
+	/* File with 10 bytes content. */
+	fd = open("dir/file", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	write(filelist, "dir/file\n", 9);
+
+	/* Write dir last. */
+	write(filelist, "dir\n", 4);
+
+	/* All done. */
+	close(filelist);
+
+
+	/*
+	 * Use cpio passthrough mode to copy files to another directory.
+	 */
+	r = systemf("%s -pdvm out <filelist >stdout 2>stderr", testprog);
+	failure("Error invoking %s -pd out", testprog);
+	assertEqualInt(r, 0);
+
+	assertEqualInt(0, chdir("out"));
+
+	/* Verify stderr and stdout. */
+	assertFileContents("out/dir/file\nout/dir\n1 block\n", 29,
+	    "../stderr");
+	assertEmptyFile("../stdout");
+
+	/* dir */
+	r = lstat("dir", &st);
+	if (r == 0) {
+		assertEqualInt(r, 0);
+		assert(S_ISDIR(st.st_mode));
+		failure("st.st_mode=0%o",  st.st_mode);
+		assertEqualInt(0743, st.st_mode & 0777);
+	}
+
+
+	/* Regular file. */
+	r = lstat("dir/file", &st);
+	failure("Failed to stat dir/file, errno=%d", errno);
+	assertEqualInt(r, 0);
+	if (r == 0) {
+		assert(S_ISREG(st.st_mode));
+		assertEqualInt(0644, st.st_mode & 0777);
+		assertEqualInt(10, st.st_size);
+		assertEqualInt(1, st.st_nlink);
+	}
+
+	umask(oldumask);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_help.c
@@ -0,0 +1,81 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_help.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+/*
+ * Test that "--help", "-h", and "-W help" options all work and
+ * generate reasonable output.
+ */
+
+static int
+in_first_line(const char *p, const char *substring)
+{
+	size_t l = strlen(substring);
+
+	while (*p != '\0' && *p != '\n') {
+		if (memcmp(p, substring, l) == 0)
+			return (1);
+		++p;
+	}
+	return (0);
+}
+
+DEFINE_TEST(test_option_help)
+{
+	int r;
+	char *p;
+	size_t plen;
+
+	/* Exercise --help option. */
+	r = systemf("%s --help >help.stdout 2>help.stderr", testprog);
+	failure("--help should generate nothing to stderr.");
+	assertEmptyFile("help.stderr");
+	/* Help message should start with name of program. */
+	p = slurpfile(&plen, "help.stdout");
+	failure("Help output should be long enough.");
+	assert(plen >= 7);
+	failure("First line of help output should contain string 'bsdcpio'");
+	assert(in_first_line(p, "bsdcpio"));
+	/*
+	 * TODO: Extend this check to further verify that --help output
+	 * looks approximately right.
+	 */
+	free(p);
+
+	/* -h option should generate the same output. */
+	r = systemf("%s -h >h.stdout 2>h.stderr", testprog);
+	failure("-h should generate nothing to stderr.");
+	assertEmptyFile("h.stderr");
+	failure("stdout should be same for -h and --help");
+	assertEqualFile("h.stdout", "help.stdout");
+
+	/* -W help should be another synonym. */
+	r = systemf("%s -W help >Whelp.stdout 2>Whelp.stderr", testprog);
+	failure("-W help should generate nothing to stderr.");
+	assertEmptyFile("Whelp.stderr");
+	failure("stdout should be same for -W help and --help");
+	assertEqualFile("Whelp.stdout", "help.stdout");
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_a.c
@@ -0,0 +1,159 @@
+/*-
+ * 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"
+#include <utime.h>
+__FBSDID("$FreeBSD: src/usr.bin/cpio/test/test_option_a.c,v 1.3 2008/08/24 06:21:00 kientzle Exp $");
+
+static struct {
+	const char *name;
+	time_t atime_sec;
+} files[] = {
+	{ "f0", 0 },
+	{ "f1", 0 },
+	{ "f2", 0 },
+	{ "f3", 0 },
+	{ "f4", 0 },
+	{ "f5", 0 }
+};
+
+/*
+ * Create a bunch of test files and record their atimes.
+ * For the atime preserve/change tests, the files must have
+ * atimes in the past.  We can accomplish this by explicitly invoking
+ * utime() on platforms that support it or by simply sleeping
+ * for a second after creating the files.  (Creating all of the files
+ * at once means we only need to sleep once.)
+ */
+static void
+test_create(void)
+{
+	struct stat st;
+	struct utimbuf times;
+	static const int numfiles = sizeof(files) / sizeof(files[0]);
+	int i;
+	int fd;
+
+	for (i = 0; i < numfiles; ++i) {
+		fd = open(files[i].name, O_CREAT | O_WRONLY, 0644);
+		assert(fd >= 0);
+		/*
+		 * Note: Have to write at least one byte to the file.
+		 * cpio doesn't bother reading the file if it's zero length,
+		 * so the atime never gets changed in that case, which
+		 * makes the tests below rather pointless.
+		 */
+		assertEqualInt(1, write(fd, "a", 1));
+		close(fd);
+
+		/* If utime() isn't supported on your platform, just
+		 * #ifdef this section out.  Most of the test below is
+		 * still valid. */
+		memset(&times, 0, sizeof(times));
+		times.actime = 1;
+		times.modtime = 3;
+		assertEqualInt(0, utime(files[i].name, &times));
+
+		/* Record whatever atime the file ended up with. */
+		/* If utime() is available, this should be 1, but there's
+		 * no harm in being careful. */
+		assertEqualInt(0, stat(files[i].name, &st));
+		files[i].atime_sec = st.st_atime;
+	}
+
+	/* Wait until the atime on the last file is actually in the past. */
+	/* If utime() is supported above, there's no sleep here which
+	 * makes the test faster. */
+	while (files[numfiles - 1].atime_sec >= time(NULL))
+		sleep(1);
+}
+
+DEFINE_TEST(test_option_a)
+{
+	struct stat st;
+	int r;
+	int f;
+	char buff[64];
+
+	/* Create all of the test files. */
+	test_create();
+
+	/* Sanity check; verify that atimes really do get modified. */
+	f = open(files[0].name, O_RDONLY);
+	assertEqualInt(1, read(f,buff, 1));
+	assertEqualInt(0, close(f));
+	assertEqualInt(0, stat("f0", &st));
+	if (st.st_atime == files[0].atime_sec) {
+		skipping("Cannot verify -a option\n"
+		    "      Your system appears to not support atime.");
+	}
+	else
+	{
+		/*
+		 * If this disk is mounted noatime, then we can't
+		 * verify correct operation without -a.
+		 */
+
+		/* Copy the file without -a; should change the atime. */
+		r = systemf("echo %s | %s -pd copy-no-a > copy-no-a.out 2>copy-no-a.err", files[1].name, testprog);
+		assertEqualInt(r, 0);
+		assertFileContents("1 block\n", 8, "copy-no-a.err");
+		assertEmptyFile("copy-no-a.out");
+		assertEqualInt(0, stat(files[1].name, &st));
+		failure("Copying file without -a should have changed atime.");
+		assert(st.st_atime != files[1].atime_sec);
+
+		/* Archive the file without -a; should change the atime. */
+		r = systemf("echo %s | %s -o > archive-no-a.out 2>archive-no-a.err", files[2].name, testprog);
+		assertEqualInt(r, 0);
+		assertFileContents("1 block\n", 8, "copy-no-a.err");
+		assertEqualInt(0, stat(files[2].name, &st));
+		failure("Archiving file without -a should have changed atime.");
+		assert(st.st_atime != files[2].atime_sec);
+	}
+
+	/*
+	 * We can, of course, still verify that the atime is unchanged
+	 * when using the -a option.
+	 */
+
+	/* Copy the file with -a; should not change the atime. */
+	r = systemf("echo %s | %s -pad copy-a > copy-a.out 2>copy-a.err",
+	    files[3].name, testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "copy-a.err");
+	assertEmptyFile("copy-a.out");
+	assertEqualInt(0, stat(files[3].name, &st));
+	failure("Copying file with -a should not have changed atime.");
+	assertEqualInt(st.st_atime, files[3].atime_sec);
+
+	/* Archive the file with -a; should not change the atime. */
+	r = systemf("echo %s | %s -oa > archive-a.out 2>archive-a.err",
+	    files[4].name, testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "copy-a.err");
+	assertEqualInt(0, stat(files[4].name, &st));
+	failure("Archiving file with -a should not have changed atime.");
+	assertEqualInt(st.st_atime, files[4].atime_sec);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_gcpio_compat_ref.newc.uu
@@ -0,0 +1,27 @@
+$FreeBSD: src/usr.bin/cpio/test/test_gcpio_compat_ref.newc.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_gcpio_compat_ref.newc
+M,#<P-S`Q,#`S,S<W86$P,#`P.#%A-#`P,#`P,V4X,#`P,#`S93 at P,#`P,#`P
+M,C0W,&0Y.3<W,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`U,#`P,#`P,#!F:6QE```P-S`W,#$P,#,S-S=A83`P,#`X
+M,6$T,#`P,#`S93 at P,#`P,#-E.#`P,#`P,#`R-#<P9#DY-S<P,#`P,#`P83`P
+M,#`P,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#DP,#`P,#`P
+M,&QI;FMF:6QE```Q,C,T-38W.#D*```P-S`W,#$P,#,S-S=A8C`P,#!A,65D
+M,#`P,#`S93 at P,#`P,#-E.#`P,#`P,#`Q-#<P9#DY.#0P,#`P,#`P-#`P,#`P
+M,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#@P,#`P,#`P,'-Y
+M;6QI;FL```!F:6QE,#<P-S`Q,#`S,S<W864P,#`P-#%F9#`P,#`P,V4X,#`P
+M,#`S93 at P,#`P,#`P,C0W,&0Y.3 at U,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`T,#`P,#`P,#!D:7(````P-S`W,#$P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,&(P,#`P,#`P,%1204E,15(A(2$`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+B````````````````````````````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_option_c.c
@@ -0,0 +1,198 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_c.c,v 1.1 2008/05/26 17:15:35 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);
+}
+
+static int
+from_octal(const char *p, size_t l)
+{
+	int r = 0;
+
+	while (l > 0) {
+		r *= 8;
+		r += *p - '0';
+		--l;
+		++p;
+	}
+	return (r);
+}
+
+DEFINE_TEST(test_option_c)
+{
+	int fd, filelist;
+	int r;
+	int dev, ino, gid;
+	time_t t, now;
+	char *p, *e;
+	size_t s;
+	mode_t oldmask;
+
+	oldmask = umask(0);
+
+	/*
+	 * Create an assortment of files.
+	 * TODO: Extend this to cover more filetypes.
+	 */
+	filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+
+	/* "file" */
+	fd = open("file", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	assertEqualInt(5, write(filelist, "file\n", 5));
+
+	/* "symlink" */
+	assertEqualInt(0, symlink("file", "symlink"));
+	assertEqualInt(8, write(filelist, "symlink\n", 8));
+
+	/* "dir" */
+	assertEqualInt(0, mkdir("dir", 0775));
+	/* Record some facts about what we just created: */
+	now = time(NULL); /* They were all created w/in last two seconds. */
+	assertEqualInt(4, write(filelist, "dir\n", 4));
+
+	/* Use the cpio program to create an archive. */
+	close(filelist);
+	r = systemf("%s -oc <filelist >basic.out 2>basic.err", testprog);
+	/* Verify that nothing went to stderr. */
+	assertFileContents("1 block\n", 8, "basic.err");
+
+	/* Assert that the program finished. */
+	failure("%s -oc crashed", testprog);
+	if (!assertEqualInt(r, 0))
+		return;
+
+	/* Verify that stdout is a well-formed cpio file in "odc" format. */
+	p = slurpfile(&s, "basic.out");
+	assertEqualInt(s, 512);
+	e = p;
+
+	/*
+	 * Some of these assertions could be stronger, but it's
+	 * a little tricky because they depend on the local environment.
+	 */
+
+	/* First entry is "file" */
+	assert(is_octal(e, 76)); /* Entire header is octal digits. */
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assert(is_octal(e + 6, 6)); /* dev */
+	dev = from_octal(e + 6, 6);
+	assert(is_octal(e + 12, 6)); /* ino */
+	ino = from_octal(e + 12, 6);
+	assertEqualMem(e + 18, "100644", 6); /* Mode */
+	assertEqualInt(from_octal(e + 24, 6), getuid()); /* uid */
+	assert(is_octal(e + 30, 6)); /* gid */
+	gid = from_octal(e + 30, 6);
+	assertEqualMem(e + 36, "000001", 6); /* nlink */
+	failure("file entries should not have rdev set (dev field was 0%o)",
+	    dev);
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	t = from_octal(e + 48, 11); /* mtime */
+	assert(t <= now); /* File wasn't created in future. */
+	assert(t >= now - 2); /* File was created w/in last 2 secs. */
+	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, "123456789\0", 10); /* File contents */
+	e += 91;
+
+	/* Second entry is "symlink" pointing to "file" */
+	assert(is_octal(e, 76)); /* Entire header is octal digits. */
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	assertEqualInt(dev, from_octal(e + 6, 6)); /* dev */
+	assert(dev != from_octal(e + 12, 6)); /* ino */
+	assertEqualMem(e + 18, "120777", 6); /* Mode */
+	assertEqualInt(from_octal(e + 24, 6), getuid()); /* uid */
+	assertEqualInt(gid, from_octal(e + 30, 6)); /* gid */
+	assertEqualMem(e + 36, "000001", 6); /* nlink */
+	failure("file entries should have rdev == 0 (dev was 0%o)",
+	    from_octal(e + 6, 6));
+	assertEqualMem(e + 42, "000000", 6); /* rdev */
+	t = from_octal(e + 48, 11); /* mtime */
+	assert(t <= now); /* File wasn't created in future. */
+	assert(t >= now - 2); /* File was created w/in last 2 secs. */
+	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); /* Symlink target. */
+	e += 88;
+
+	/* Second entry is "dir" */
+	assert(is_octal(e, 76));
+	assertEqualMem(e + 0, "070707", 6); /* Magic */
+	/* Dev should be same as first entry. */
+	assert(is_octal(e + 6, 6)); /* dev */
+	assertEqualInt(dev, from_octal(e + 6, 6));
+	/* Ino must be different from first entry. */
+	assert(is_octal(e + 12, 6)); /* ino */
+	assert(dev != from_octal(e + 12, 6));
+	assertEqualMem(e + 18, "040775", 6); /* Mode */
+	assertEqualInt(from_octal(e + 24, 6), getuid()); /* uid */
+	/* Gid should be same as first entry. */
+	assert(is_octal(e + 30, 6)); /* gid */
+	assertEqualInt(gid, from_octal(e + 30, 6));
+	assertEqualMem(e + 36, "000002", 6); /* Nlink */
+	t = from_octal(e + 48, 11); /* mtime */
+	assert(t <= now); /* File wasn't created in future. */
+	assert(t >= now - 2); /* File was created w/in last 2 secs. */
+	assertEqualMem(e + 59, "000004", 6); /* Name size */
+	assertEqualMem(e + 65, "00000000000", 11); /* File size */
+	assertEqualMem(e + 76, "dir\0", 4); /* name */
+	e += 80;
+
+	/* 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 */
+
+	free(p);
+
+	umask(oldmask);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_ell.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/usr.bin/cpio/test/test_option_ell.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+/*
+ * This is called "test_option_ell" instead of "test_option_l" to
+ * avoid any conflicts with "test_option_L" on case-insensitive
+ * filesystems.
+ */
+
+DEFINE_TEST(test_option_ell)
+{
+	struct stat st, st2;
+	int fd;
+	int r;
+
+	/* Create a file. */
+	fd = open("f", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(1, write(fd, "a", 1));
+	close(fd);
+
+	/* Stat it. */
+	assertEqualInt(0, stat("f", &st));
+
+	/* Copy the file to the "copy" dir. */
+	r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
+	    testprog);
+	assertEqualInt(r, 0);
+
+	/* Check that the copy is a true copy and not a link. */
+	assertEqualInt(0, stat("copy/f", &st2));
+	assert(st2.st_ino != st.st_ino);
+
+	/* Copy the file to the "link" dir with the -l option. */
+	r = systemf("echo f | %s -pld link >link.out 2>link.err",
+	    testprog);
+	assertEqualInt(r, 0);
+
+	/* Check that this is a link and not a copy. */
+	assertEqualInt(0, stat("link/f", &st2));
+	assert(st2.st_ino == st.st_ino);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_t.c
@@ -0,0 +1,47 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_t.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+
+DEFINE_TEST(test_option_t)
+{
+	int r;
+
+	/* List reference archive, make sure the TOC is correct. */
+	extract_reference_file("test_option_t.cpio");
+	r = systemf("%s -it < test_option_t.cpio >t.out 2>t.err", testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "t.err");
+	extract_reference_file("test_option_t.stdout");
+	assertEqualFile("t.out", "test_option_t.stdout");
+
+	/* List reference archive verbosely, make sure the TOC is correct. */
+	r = systemf("%s -itv < test_option_t.cpio >tv.out 2>tv.err", testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "tv.err");
+	extract_reference_file("test_option_tv.stdout");
+	assertEqualFile("tv.out", "test_option_tv.stdout");
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_B.c
@@ -0,0 +1,54 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_B.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+
+DEFINE_TEST(test_option_B)
+{
+	struct stat st;
+	int r, fd;
+
+	/*
+	 * Create a file on disk.
+	 */
+	fd = open("file", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	close(fd);
+
+	/* Create an archive without -B; this should be 512 bytes. */
+	r = systemf("echo file | %s -o > small.cpio 2>small.err", testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "small.err");
+	assertEqualInt(0, stat("small.cpio", &st));
+	assertEqualInt(512, st.st_size);
+
+	/* Create an archive with -B; this should be 5120 bytes. */
+	r = systemf("echo file | %s -oB > large.cpio 2>large.err", testprog);
+	assertEqualInt(r, 0);
+	assertFileContents("1 block\n", 8, "large.err");
+	assertEqualInt(0, stat("large.cpio", &st));
+	assertEqualInt(5120, st.st_size);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_version.c
@@ -0,0 +1,105 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_version.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+/*
+ * Test that --version option works and generates reasonable output.
+ */
+
+static void
+verify(const char *p, size_t s)
+{
+	const char *q = p;
+
+	/* Version message should start with name of program, then space. */
+	failure("version message too short:", p);
+	if (!assert(s > 6))
+		return;
+	failure("Version message should begin with 'bsdcpio': %s", p);
+	if (!assertEqualMem(q, "bsdcpio ", 8))
+		/* If we're not testing bsdcpio, don't keep going. */
+		return;
+	q += 8; s -= 8;
+	/* Version number is a series of digits and periods. */
+	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
+		++q;
+		--s;
+	}
+	/* Version number terminated by space. */
+	failure("Version: %s", p);
+	assert(s > 1);
+	/* Skip a single trailing a,b,c, or d. */
+	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
+		++q;
+	failure("Version: %s", p);
+	assert(*q == ' ');
+	++q; --s;
+	/* Separator. */
+	failure("Version: %s", p);
+	assertEqualMem(q, "-- ", 3);
+	q += 3; s -= 3;
+	/* libarchive name and version number */
+	assert(s > 11);
+	failure("Version: %s", p);
+	assertEqualMem(q, "libarchive ", 11);
+	q += 11; s -= 11;
+	/* Version number is a series of digits and periods. */
+	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
+		++q;
+		--s;
+	}
+	/* Skip a single trailing a,b,c, or d. */
+	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
+		++q;
+	/* All terminated by a newline. */
+	assert(s >= 1);
+	failure("Version: %s", p);
+	assertEqualMem(q, "\n", 1);
+}
+
+
+DEFINE_TEST(test_option_version)
+{
+	int r;
+	char *p;
+	size_t s;
+
+	r = systemf("%s --version >version.stdout 2>version.stderr", testprog);
+	if (r != 0)
+		r = systemf("%s -W version >version.stdout 2>version.stderr",
+		    testprog);
+	failure("Unable to run either %s --version or %s -W version",
+	    testprog, testprog);
+	if (!assert(r == 0))
+		return;
+
+	/* --version should generate nothing to stderr. */
+	assertEmptyFile("version.stderr");
+	/* Verify format of version message. */
+	p = slurpfile(&s, "version.stdout");
+	verify(p, s);
+	free(p);
+}
--- /dev/null
+++ usr.bin/cpio/test/test_0.c
@@ -0,0 +1,62 @@
+/*-
+ * 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/usr.bin/cpio/test/test_0.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+/*
+ * This first test does basic sanity checks on the environment.  For
+ * most of these, we just exit on failure.
+ */
+
+DEFINE_TEST(test_0)
+{
+	struct stat st;
+
+	failure("File %s does not exist?!", testprog);
+	if (!assertEqualInt(0, stat(testprog, &st)))
+		exit(1);
+
+	failure("%s is not executable?!", testprog);
+	if (!assert((st.st_mode & 0111) != 0))
+		exit(1);
+
+	/*
+	 * Try to succesfully run the program; this requires that
+	 * we know some option that will succeed.
+	 */
+	if (0 == systemf("%s --version >/dev/null", testprog)) {
+		/* This worked. */
+	} else if (0 == systemf("%s -W version >/dev/null", testprog)) {
+		/* This worked. */
+	} else {
+		failure("Unable to successfully run any of the following:\n"
+		    "  * %s --version\n"
+		    "  * %s -W version\n",
+		    testprog, testprog);
+		assert(0);
+	}
+
+	/* TODO: Ensure that our reference files are available. */
+}
--- /dev/null
+++ usr.bin/cpio/test/test.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2003-2006 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/usr.bin/cpio/test/test.h,v 1.2 2008/06/21 02:17:18 kientzle Exp $
+ */
+
+/* Every test program should #include "test.h" as the first thing. */
+
+/*
+ * The goal of this file (and the matching test.c) is to
+ * simplify the very repetitive test-*.c test programs.
+ */
+#if defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "../../config.h"
+#elif defined(__FreeBSD__)
+/* Building as part of FreeBSD system requires a pre-built config.h. */
+#include "../config_freebsd.h"
+#elif defined(_WIN32)
+/* Win32 can't run the 'configure' script. */
+#include "../config_windows.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#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 */
+#else
+#define	__FBSDID(a)     /* null */
+#endif
+
+/*
+ * Redefine DEFINE_TEST for use in defining the test functions.
+ */
+#undef DEFINE_TEST
+#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)
+
+/* 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)
+
+/* 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
+ * require that.  Instead, I insert a function call before each
+ * skipping() call to pass the file and line information down.  Crude,
+ * but effective.
+ */
+#define skipping	\
+  test_setup(__FILE__, __LINE__);test_skipping
+
+/* Function declarations.  These are defined in test_utility.c. */
+void failure(const char *fmt, ...);
+void test_setup(const char *, int);
+void test_skipping(const char *fmt, ...);
+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 program test harness.
+ */
+
+/* Pathname of exe to be tested. */
+char *testprog;
--- /dev/null
+++ usr.bin/cpio/test/test_option_L.c
@@ -0,0 +1,84 @@
+/*-
+ * 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/usr.bin/cpio/test/test_option_L.c,v 1.2 2008/08/24 06:21:00 kientzle Exp $");
+
+DEFINE_TEST(test_option_L)
+{
+	struct stat st;
+	int fd, filelist;
+	int r;
+
+	filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+
+	/* Create a file and a symlink to the file. */
+	fd = open("file", O_CREAT | O_WRONLY, 0644);
+	assert(fd >= 0);
+	assertEqualInt(10, write(fd, "123456789", 10));
+	close(fd);
+	write(filelist, "file\n", 5);
+
+	/* Symlink to above file. */
+	assertEqualInt(0, symlink("file", "symlink"));
+	write(filelist, "symlink\n", 8);
+
+	close(filelist);
+
+	r = systemf("cat filelist | %s -pd copy >copy.out 2>copy.err", testprog);
+	assertEqualInt(r, 0);
+	assertEqualInt(0, lstat("copy/symlink", &st));
+	failure("Regular -p without -L should preserve symlinks.");
+	assert(S_ISLNK(st.st_mode));
+
+	r = systemf("cat filelist | %s -pd -L copy-L >copy-L.out 2>copy-L.err", testprog);
+	assertEqualInt(r, 0);
+	assertEmptyFile("copy-L.out");
+	assertFileContents("1 block\n", 8, "copy-L.err");
+	assertEqualInt(0, lstat("copy-L/symlink", &st));
+	failure("-pdL should dereference symlinks and turn them into files.");
+	assert(!S_ISLNK(st.st_mode));
+
+	r = systemf("cat filelist | %s -o >archive.out 2>archive.err", testprog);
+	failure("Error invoking %s -o ", testprog);
+	assertEqualInt(r, 0);
+
+	assertEqualInt(0, mkdir("unpack", 0755));
+	r = systemf("cat archive.out | (cd unpack ; %s -i >unpack.out 2>unpack.err)", testprog);
+	failure("Error invoking %s -i", testprog);
+	assertEqualInt(r, 0);
+	assertEqualInt(0, lstat("unpack/symlink", &st));
+	assert(S_ISLNK(st.st_mode));
+
+	r = systemf("cat filelist | %s -oL >archive-L.out 2>archive-L.err", testprog);
+	failure("Error invoking %s -oL", testprog);
+	assertEqualInt(r, 0);
+
+	assertEqualInt(0, mkdir("unpack-L", 0755));
+	r = systemf("cat archive-L.out | (cd unpack-L ; %s -i >unpack-L.out 2>unpack-L.err)", testprog);
+	failure("Error invoking %s -i < archive-L.out", testprog);
+	assertEqualInt(r, 0);
+	assertEqualInt(0, lstat("unpack-L/symlink", &st));
+	assert(!S_ISLNK(st.st_mode));
+}
--- /dev/null
+++ usr.bin/cpio/test/test_option_tv.stdout.uu
@@ -0,0 +1,6 @@
+$FreeBSD: src/usr.bin/cpio/test/test_option_tv.stdout.uu,v 1.2 2008/11/29 20:22:02 kientzle Exp $
+begin 644 test_option_tv.stdout
+M+7)W+7(M+7(M+2`@(#$@=&EM("`@("`@=&EM("`@("`@("`@("`@(#`@1&5C
+/(#,Q("`Q.38Y(&9I;&4*
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_option_t.cpio.uu
@@ -0,0 +1,16 @@
+$FreeBSD: src/usr.bin/cpio/test/test_option_t.cpio.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_option_t.cpio
+M,#<P-S`W,#`P,3,Q-#4P,#8T,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P
+M,#`P,#`P,#`P,#`P,#$P,#`P,#4P,#`P,#`P,#`P,&9I;&4`,#<P-S`W,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,3,P,#`P,#`P,#`P,%1204E,15(A(2$`````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+1````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_gcpio_compat_ref.ustar.uu
@@ -0,0 +1,84 @@
+$FreeBSD: src/usr.bin/cpio/test/test_gcpio_compat_ref.ustar.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_gcpio_compat_ref.ustar
+M9FEL90``````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V-#0`,#`P,3<U,``P,#`Q-S4P`#`P,#`P,#`P,#$R
+M`#$P-S`S,S$T-38W`#`P,3$S-C,`,```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,'1I;0``
+M````````````````````````````````````=&EM````````````````````
+M```````````````````P,#`P,#`P`#`P,#`P,#``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````Q,C,T-38W.#D*````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&QI;FMF:6QE````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C0T`#`P,#$W-3``,#`P,3<U,``P,#`P,#`P,#`P,``Q,#<P,S,Q-#4V
+M-P`P,#$S,#<W`#%F:6QE````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!T:6T`````````````````
+M`````````````````````'1I;0``````````````````````````````````
+M````,#`P,#`P,``P,#`P,#`P````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````<WEM;&EN:P``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````#`P,#`W-34`,#`P,3<U,``P,#`Q-S4P`#`P,#`P
+M,#`P,#`P`#$P-S`S,S$T-C`T`#`P,3(W-C0`,F9I;&4`````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````!U<W1A<@`P
+M,'1I;0``````````````````````````````````````=&EM````````````
+M```````````````````````````P,#`P,#`P`#`P,#`P,#``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````!D:7(O````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````,#`P,#<W-0`P,#`Q
+M-S4P`#`P,#$W-3``,#`P,#`P,#`P,#``,3`W,#,S,30V,#4`,#`Q,3,P,0`U
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````'5S=&%R`#`P=&EM````````````````````````````````
+M``````!T:6T``````````````````````````````````````#`P,#`P,#``
+M,#`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````````````````````````````````````````````````````````````
+=````````````````````````````````````````
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_option_t.stdout.uu
@@ -0,0 +1,5 @@
+$FreeBSD: src/usr.bin/cpio/test/test_option_t.stdout.uu,v 1.1 2008/05/26 17:15:35 kientzle Exp $
+begin 644 test_option_t.stdout
+%9FEL90H`
+`
+end
--- /dev/null
+++ usr.bin/cpio/test/test_pathmatch.c
@@ -0,0 +1,165 @@
+/*-
+ * 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/usr.bin/cpio/test/test_pathmatch.c,v 1.1 2008/05/26 17:15:35 kientzle Exp $");
+
+#include "../pathmatch.h"
+
+/*
+ * Verify that the pattern matcher implements the wildcard logic specified
+ * in SUSv2 for the cpio command.  This is essentially the
+ * shell glob syntax:
+ *   * - matches any sequence of chars, including '/'
+ *   ? - matches any single char, including '/'
+ *   [...] - matches any of a set of chars, '-' specifies a range,
+ *        initial '!' is undefined
+ *
+ * The specification in SUSv2 is a bit incomplete, I assume the following:
+ *   Trailing '-' in [...] is not special.
+ */
+
+DEFINE_TEST(test_pathmatch)
+{
+	assertEqualInt(1, pathmatch("*","", 0));
+	assertEqualInt(1, pathmatch("*","a", 0));
+	assertEqualInt(1, pathmatch("*","abcd", 0));
+	/* SUSv2: * matches / */
+	assertEqualInt(1, pathmatch("*","abcd/efgh/ijkl", 0));
+	assertEqualInt(1, pathmatch("abcd*efgh/ijkl","abcd/efgh/ijkl", 0));
+	assertEqualInt(1, pathmatch("abcd***efgh/ijkl","abcd/efgh/ijkl", 0));
+	assertEqualInt(1, pathmatch("abcd***/efgh/ijkl","abcd/efgh/ijkl", 0));
+	assertEqualInt(0, pathmatch("?", "", 0));
+	assertEqualInt(0, pathmatch("?", "\0", 0));
+	assertEqualInt(1, pathmatch("?", "a", 0));
+	assertEqualInt(0, pathmatch("?", "ab", 0));
+	assertEqualInt(1, pathmatch("?", ".", 0));
+	assertEqualInt(1, pathmatch("?", "?", 0));
+	assertEqualInt(1, pathmatch("a", "a", 0));
+	assertEqualInt(0, pathmatch("a", "ab", 0));
+	assertEqualInt(0, pathmatch("a", "ab", 0));
+	assertEqualInt(1, pathmatch("a?c", "abc", 0));
+	/* SUSv2: ? matches / */
+	assertEqualInt(1, pathmatch("a?c", "a/c", 0));
+	assertEqualInt(1, pathmatch("a?*c*", "a/c", 0));
+	assertEqualInt(1, pathmatch("*a*", "a/c", 0));
+	assertEqualInt(1, pathmatch("*a*", "/a/c", 0));
+	assertEqualInt(1, pathmatch("*a*", "defaaaaaaa", 0));
+	assertEqualInt(0, pathmatch("a*", "defghi", 0));
+	assertEqualInt(0, pathmatch("*a*", "defghi", 0));
+	assertEqualInt(1, pathmatch("abc[def", "abc[def", 0));
+	assertEqualInt(0, pathmatch("abc[def]", "abc[def", 0));
+	assertEqualInt(0, pathmatch("abc[def", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[def]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[def]", "abce", 0));
+	assertEqualInt(1, pathmatch("abc[def]", "abcf", 0));
+	assertEqualInt(0, pathmatch("abc[def]", "abcg", 0));
+	assertEqualInt(1, pathmatch("abc[d*f]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[d*f]", "abc*", 0));
+	assertEqualInt(0, pathmatch("abc[d*f]", "abcdefghi", 0));
+	assertEqualInt(0, pathmatch("abc[d*", "abcdefghi", 0));
+	assertEqualInt(1, pathmatch("abc[d*", "abc[defghi", 0));
+	assertEqualInt(1, pathmatch("abc[d-f]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[d-f]", "abce", 0));
+	assertEqualInt(1, pathmatch("abc[d-f]", "abcf", 0));
+	assertEqualInt(0, pathmatch("abc[d-f]", "abcg", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abce", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abcf", 0));
+	assertEqualInt(0, pathmatch("abc[d-fh-k]", "abcg", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abch", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abci", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abcj", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-k]", "abck", 0));
+	assertEqualInt(0, pathmatch("abc[d-fh-k]", "abcl", 0));
+	assertEqualInt(0, pathmatch("abc[d-fh-k]", "abc-", 0));
+
+	/* I assume: Trailing '-' is non-special. */
+	assertEqualInt(0, pathmatch("abc[d-fh-]", "abcl", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-]", "abch", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-]", "abc-", 0));
+	assertEqualInt(1, pathmatch("abc[d-fh-]", "abc-", 0));
+
+	/* ']' can be backslash-quoted within a character class. */
+	assertEqualInt(1, pathmatch("abc[\\]]", "abc]", 0));
+	assertEqualInt(1, pathmatch("abc[\\]d]", "abc]", 0));
+	assertEqualInt(1, pathmatch("abc[\\]d]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[d\\]]", "abc]", 0));
+	assertEqualInt(1, pathmatch("abc[d\\]]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[d]e]", "abcde]", 0));
+	assertEqualInt(1, pathmatch("abc[d\\]e]", "abc]", 0));
+	assertEqualInt(0, pathmatch("abc[d\\]e]", "abcd]e", 0));
+	assertEqualInt(0, pathmatch("abc[d]e]", "abc]", 0));
+
+	/* backslash-quoted chars can appear as either end of a range. */
+	assertEqualInt(1, pathmatch("abc[\\d-f]gh", "abcegh", 0));
+	assertEqualInt(0, pathmatch("abc[\\d-f]gh", "abcggh", 0));
+	assertEqualInt(0, pathmatch("abc[\\d-f]gh", "abc\\gh", 0));
+	assertEqualInt(1, pathmatch("abc[d-\\f]gh", "abcegh", 0));
+	assertEqualInt(1, pathmatch("abc[\\d-\\f]gh", "abcegh", 0));
+	assertEqualInt(1, pathmatch("abc[\\d-\\f]gh", "abcegh", 0));
+	/* backslash-quoted '-' isn't special. */
+	assertEqualInt(0, pathmatch("abc[d\\-f]gh", "abcegh", 0));
+	assertEqualInt(1, pathmatch("abc[d\\-f]gh", "abc-gh", 0));
+
+	/* Leading '!' negates a character class. */
+	assertEqualInt(0, pathmatch("abc[!d]", "abcd", 0));
+	assertEqualInt(1, pathmatch("abc[!d]", "abce", 0));
+	assertEqualInt(1, pathmatch("abc[!d]", "abcc", 0));
+	assertEqualInt(0, pathmatch("abc[!d-z]", "abcq", 0));
+	assertEqualInt(1, pathmatch("abc[!d-gi-z]", "abch", 0));
+	assertEqualInt(1, pathmatch("abc[!fgijkl]", "abch", 0));
+	assertEqualInt(0, pathmatch("abc[!fghijkl]", "abch", 0));
+
+	/* Backslash quotes next character. */
+	assertEqualInt(0, pathmatch("abc\\[def]", "abc\\d", 0));
+	assertEqualInt(1, pathmatch("abc\\[def]", "abc[def]", 0));
+	assertEqualInt(0, pathmatch("abc\\\\[def]", "abc[def]", 0));
+	assertEqualInt(0, pathmatch("abc\\\\[def]", "abc\\[def]", 0));
+	assertEqualInt(1, pathmatch("abc\\\\[def]", "abc\\d", 0));
+
+	/*
+	 * Because '.' and '/' have special meanings, we can
+	 * identify many equivalent paths even if they're expressed
+	 * differently.
+	 */
+	assertEqualInt(1, pathmatch("./abc/./def/", "abc/def/", 0));
+	assertEqualInt(1, pathmatch("abc/def", "./././abc/./def", 0));
+	assertEqualInt(1, pathmatch("abc/def/././//", "./././abc/./def/", 0));
+	assertEqualInt(1, pathmatch(".////abc/.//def", "./././abc/./def", 0));
+	assertEqualInt(1, pathmatch("./abc?def/", "abc/def/", 0));
+	failure("\"?./\" is not the same as \"/./\"");
+	assertEqualInt(0, pathmatch("./abc?./def/", "abc/def/", 0));
+	failure("Trailing '/' should match no trailing '/'");
+	assertEqualInt(1, pathmatch("./abc/./def/", "abc/def", 0));
+	failure("Trailing '/./' is still the same directory.");
+	assertEqualInt(1, pathmatch("./abc/./def/./", "abc/def", 0));
+	failure("Trailing '/.' is still the same directory.");
+	assertEqualInt(1, pathmatch("./abc/./def/.", "abc/def", 0));
+	assertEqualInt(1, pathmatch("./abc/./def", "abc/def/", 0));
+	failure("Trailing '/./' is still the same directory.");
+	assertEqualInt(1, pathmatch("./abc/./def", "abc/def/./", 0));
+	failure("Trailing '/.' is still the same directory.");
+	assertEqualInt(1, pathmatch("./abc*/./def", "abc/def/.", 0));
+}


More information about the Midnightbsd-cvs mailing list