No subject


Wed Oct 7 22:37:18 EDT 2009


.Xr archive_read 3
API to manipulate an
.Nm archive
object to read entries and bodies from an archive stream.
Internally, the
.Nm archive
object is cast to an
.Nm archive_read
object, which holds all read-specific data.
The API has four layers:
The lowest layer is the I/O layer.
This layer can be overridden by clients, but most clients use
the packaged I/O callbacks provided, for example, by
.Xr archive_read_open_memory 3 ,
and
.Xr archive_read_open_fd 3 .
The compression layer calls the I/O layer to
read bytes and decompresses them for the format layer.
The format layer unpacks a stream of uncompressed bytes and
creates
.Nm archive_entry
objects from the incoming data.
The API layer tracks overall state
(for example, it prevents clients from reading data before reading a header)
and invokes the format and compression layer operations
through registered function pointers.
In particular, the API layer drives the format-detection process:
When opening the archive, it reads an initial block of data
and offers it to each registered compression handler.
The one with the highest bid is initialized with the first block.
Similarly, the format handlers are polled to see which handler
is the best for each archive.
(Prior to 2.4.0, the format bidders were invoked for each
entry, but this design hindered error recovery.)
.Ss I/O Layer and Client Callbacks
The read API goes to some lengths to be nice to clients.
As a result, there are few restrictions on the behavior of
the client callbacks.
.Pp
The client read callback is expected to provide a block
of data on each call.
A zero-length return does indicate end of file, but otherwise
blocks may be as small as one byte or as large as the entire file.
In particular, blocks may be of different sizes.
.Pp
The client skip callback returns the number of bytes actually
skipped, which may be much smaller than the skip requested.
The only requirement is that the skip not be larger.
In particular, clients are allowed to return zero for any
skip that they don't want to handle.
The skip callback must never be invoked with a negative value.
.Pp
Keep in mind that not all clients are reading from disk:
clients reading from networks may provide different-sized
blocks on every request and cannot skip at all;
advanced clients may use
.Xr mmap 2
to read the entire file into memory at once and return the
entire file to libarchive as a single block;
other clients may begin asynchronous I/O operations for the
next block on each request.
.Ss Decompresssion Layer
The decompression layer not only handles decompression,
it also buffers data so that the format handlers see a
much nicer I/O model.
The decompression API is a two stage peek/consume model.
A read_ahead request specifies a minimum read amount;
the decompression layer must provide a pointer to at least
that much data.
If more data is immediately available, it should return more:
the format layer handles bulk data reads by asking for a minimum
of one byte and then copying as much data as is available.
.Pp
A subsequent call to the
.Fn consume
function advances the read pointer.
Note that data returned from a
.Fn read_ahead
call is guaranteed to remain in place until
the next call to
.Fn read_ahead .
Intervening calls to
.Fn consume
should not cause the data to move.
.Pp
Skip requests must always be handled exactly.
Decompression handlers that cannot seek forward should
not register a skip handler;
the API layer fills in a generic skip handler that reads and discards data.
.Pp
A decompression handler has a specific lifecycle:
.Bl -tag -compact -width indent
.It Registration/Configuration
When the client invokes the public support function,
the decompression handler invokes the internal
.Fn __archive_read_register_compression
function to provide bid and initialization functions.
This function returns
.Cm NULL
on error or else a pointer to a
.Cm struct decompressor_t .
This structure contains a
.Va void * config
slot that can be used for storing any customization information.
.It Bid
The bid function is invoked with a pointer and size of a block of data.
The decompressor can access its config data
through the
.Va decompressor
element of the
.Cm archive_read
object.
The bid function is otherwise stateless.
In particular, it must not perform any I/O operations.
.Pp
The value returned by the bid function indicates its suitability
for handling this data stream.
A bid of zero will ensure that this decompressor is never invoked.
Return zero if magic number checks fail.
Otherwise, your initial implementation should return the number of bits
actually checked.
For example, if you verify two full bytes and three bits of another
byte, bid 19.
Note that the initial block may be very short;
be careful to only inspect the data you are given.
(The current decompressors require two bytes for correct bidding.)
.It Initialize
The winning bidder will have its init function called.
This function should initialize the remaining slots of the
.Va struct decompressor_t
object pointed to by the
.Va decompressor
element of the
.Va archive_read
object.
In particular, it should allocate any working data it needs
in the
.Va data
slot of that structure.
The init function is called with the block of data that
was used for tasting.
At this point, the decompressor is responsible for all I/O
requests to the client callbacks.
The decompressor is free to read more data as and when
necessary.
.It Satisfy I/O requests
The format handler will invoke the
.Va read_ahead ,
.Va consume ,
and
.Va skip
functions as needed.
.It Finish
The finish method is called only once when the archive is closed.
It should release anything stored in the
.Va data
and
.Va config
slots of the
.Va decompressor
object.
It should not invoke the client close callback.
.El
.Ss Format Layer
The read formats have a similar lifecycle to the decompression handlers:
.Bl -tag -compact -width indent
.It Registration
Allocate your private data and initialize your pointers.
.It Bid
Formats bid by invoking the
.Fn read_ahead
decompression method but not calling the
.Fn consume
method.
This allows each bidder to look ahead in the input stream.
Bidders should not look further ahead than necessary, as long
look aheads put pressure on the decompression layer to buffer
lots of data.
Most formats only require a few hundred bytes of look ahead;
look aheads of a few kilobytes are reasonable.
(The ISO9660 reader sometimes looks ahead by 48k, which
should be considered an upper limit.)
.It Read header
The header read is usually the most complex part of any format.
There are a few strategies worth mentioning:
For formats such as tar or cpio, reading and parsing the header is
straightforward since headers alternate with data.
For formats that store all header data at the beginning of the file,
the first header read request may have to read all headers into
memory and store that data, sorted by the location of the file
data.
Subsequent header read requests will skip forward to the
beginning of the file data and return the corresponding header.
.It Read Data
The read data interface supports sparse files; this requires that
each call return a block of data specifying the file offset and
size.
This may require you to carefully track the location so that you
can return accurate file offsets for each read.
Remember that the decompressor will return as much data as it has.
Generally, you will want to request one byte,
examine the return value to see how much data is available, and
possibly trim that to the amount you can use.
You should invoke consume for each block just before you return it.
.It Skip All Data
The skip data call should skip over all file data and trailing padding.
This is called automatically by the API layer just before each
header read.
It is also called in response to the client calling the public
.Fn data_skip
function.
.It Cleanup
On cleanup, the format should release all of its allocated memory.
.El
.Ss API Layer
XXX to do XXX
.Sh WRITE ARCHITECTURE
The write API has a similar set of four layers:
an API layer, a format layer, a compression layer, and an I/O layer.
The registration here is much simpler because only
one format and one compression can be registered at a time.
.Ss I/O Layer and Client Callbacks
XXX To be written XXX
.Ss Compression Layer
XXX To be written XXX
.Ss Format Layer
XXX To be written XXX
.Ss API Layer
XXX To be written XXX
.Sh WRITE_DISK ARCHITECTURE
The write_disk API is intended to look just like the write API
to clients.
Since it does not handle multiple formats or compression, it
is not layered internally.
.Sh GENERAL SERVICES
The
.Nm archive_read ,
.Nm archive_write ,
and
.Nm archive_write_disk
objects all contain an initial
.Nm archive
object which provides common support for a set of standard services.
(Recall that ANSI/ISO C90 guarantees that you can cast freely between
a pointer to a structure and a pointer to the first element of that
structure.)
The
.Nm archive
object has a magic value that indicates which API this object
is associated with,
slots for storing error information,
and function pointers for virtualized API functions.
.Sh MISCELLANEOUS NOTES
Connecting existing archiving libraries into libarchive is generally
quite difficult.
In particular, many existing libraries strongly assume that you
are reading from a file; they seek forwards and backwards as necessary
to locate various pieces of information.
In contrast, libarchive never seeks backwards in its input, which
sometimes requires very different approaches.
.Pp
For example, libarchive's ISO9660 support operates very differently
from most ISO9660 readers.
The libarchive support utilizes a work-queue design that
keeps a list of known entries sorted by their location in the input.
Whenever libarchive's ISO9660 implementation is asked for the next
header, checks this list to find the next item on the disk.
Directories are parsed when they are encountered and new
items are added to the list.
This design relies heavily on the ISO9660 image being optimized so that
directories always occur earlier on the disk than the files they
describe.
.Pp
Depending on the specific format, such approaches may not be possible.
The ZIP format specification, for example, allows archivers to store
key information only at the end of the file.
In theory, it is possible to create ZIP archives that cannot
be read without seeking.
Fortunately, such archives are very rare, and libarchive can read
most ZIP archives, though it cannot always extract as much information
as a dedicated ZIP program.
.Sh SEE ALSO
.Xr archive 3 ,
.Xr archive_entry 3 ,
.Xr archive_read 3 ,
.Xr archive_write 3 ,
.Xr archive_write_disk 3
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .
.Sh BUGS

--- NEW FILE: archive_read_disk.3 ---
.\" Copyright (c) 2003-2009 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$
.\"
.Dd March 10, 2009
.Dt archive_read_disk 3
.Os
.Sh NAME
.Nm archive_read_disk_new ,
.Nm archive_read_disk_set_symlink_logical ,
.Nm archive_read_disk_set_symlink_physical ,
.Nm archive_read_disk_set_symlink_hybrid ,
.Nm archive_read_disk_entry_from_file ,
.Nm archive_read_disk_gname ,
.Nm archive_read_disk_uname ,
.Nm archive_read_disk_set_uname_lookup ,
.Nm archive_read_disk_set_gname_lookup ,
.Nm archive_read_disk_set_standard_lookup ,
.Nm archive_read_close ,
.Nm archive_read_finish
.Nd functions for reading objects from disk
.Sh SYNOPSIS
.In archive.h
.Ft struct archive *
.Fn archive_read_disk_new "void"
.Ft int
.Fn archive_read_disk_set_symlink_logical "struct archive *"
.Ft int
.Fn archive_read_disk_set_symlink_physical "struct archive *"
.Ft int
.Fn archive_read_disk_set_symlink_hybrid "struct archive *"
.Ft int
.Fn archive_read_disk_gname "struct archive *" "gid_t"
.Ft int
.Fn archive_read_disk_uname "struct archive *" "uid_t"
.Ft int
.Fo archive_read_disk_set_gname_lookup
.Fa "struct archive *"
.Fa "void *"
.Fa "const char *(*lookup)(void *, gid_t)"
.Fa "void (*cleanup)(void *)"
.Fc
.Ft int
.Fo archive_read_disk_set_uname_lookup
.Fa "struct archive *"
.Fa "void *"
.Fa "const char *(*lookup)(void *, uid_t)"
.Fa "void (*cleanup)(void *)"
.Fc
.Ft int
.Fn archive_read_disk_set_standard_lookup "struct archive *"
.Ft int
.Fo archive_read_disk_entry_from_file
.Fa "struct archive *"
.Fa "struct archive_entry *"
.Fa "int fd"
.Fa "const struct stat *"
.Fc
.Ft int
.Fn archive_read_close "struct archive *"
.Ft int
.Fn archive_read_finish "struct archive *"
.Sh DESCRIPTION
These functions provide an API for reading information about
objects on disk.
In particular, they provide an interface for populating
.Tn struct archive_entry
objects.
.Bl -tag -width indent
.It Fn archive_read_disk_new
Allocates and initializes a
.Tn struct archive
object suitable for reading object information from disk.
.It Xo
.Fn archive_read_disk_set_symlink_logical ,
.Fn archive_read_disk_set_symlink_physical ,
.Fn archive_read_disk_set_symlink_hybrid
.Xc
This sets the mode used for handling symbolic links.
The
.Dq logical
mode follows all symbolic links.
The
.Dq physical
mode does not follow any symbolic links.
The
.Dq hybrid
mode currently behaves identically to the
.Dq logical
mode.
.It Xo
.Fn archive_read_disk_gname ,
.Fn archive_read_disk_uname
.Xc
Returns a user or group name given a gid or uid value.
By default, these always return a NULL string.
.It Xo
.Fn archive_read_disk_set_gname_lookup ,
.Fn archive_read_disk_set_uname_lookup
.Xc
These allow you to override the functions used for
user and group name lookups.
You may also provide a
.Tn void *
pointer to a private data structure and a cleanup function for
that data.
The cleanup function will be invoked when the
.Tn struct archive
object is destroyed or when new lookup functions are registered.
.It Fn archive_read_disk_set_standard_lookup
This convenience function installs a standard set of user
and group name lookup functions.
These functions use
.Xr getpwid 3
and
.Xr getgrid 3
to convert ids to names, defaulting to NULL if the names cannot
be looked up.
These functions also implement a simple memory cache to reduce
the number of calls to
.Xr getpwid 3
and
.Xr getgrid 3 .
.It Fn archive_read_disk_entry_from_file
Populates a
.Tn struct archive_entry
object with information about a particular file.
The
.Tn archive_entry
object must have already been created with
.Xr archive_entry_new 3
and at least one of the source path or path fields must already be set.
(If both are set, the source path will be used.)
.Pp
Information is read from disk using the path name from the
.Tn struct archive_entry
object.
If a file descriptor is provided, some information will be obtained using
that file descriptor, on platforms that support the appropriate
system calls.
.Pp
If a pointer to a
.Tn struct stat
is provided, information from that structure will be used instead
of reading from the disk where appropriate.
This can provide performance benefits in scenarios where
.Tn struct stat
information has already been read from the disk as a side effect
of some other operation.
(For example, directory traversal libraries often provide this information.)
.Pp
Where necessary, user and group ids are converted to user and group names
using the currently registered lookup functions above.
This affects the file ownership fields and ACL values in the
.Tn struct archive_entry
object.
.It Fn archive_read_close
This currently does nothing.
.It Fn archive_write_finish
Invokes
.Fn archive_write_close
if it was not invoked manually, then releases all resources.
.El
More information about the
.Va struct archive
object and the overall design of the library can be found in the
.Xr libarchive 3
overview.
.Sh EXAMPLE
The following illustrates basic usage of the library by
showing how to use it to copy an item on disk into an archive.
.Bd -literal -offset indent
void
file_to_archive(struct archive *a, const char *name)
{
  char buff[8192];
  size_t bytes_read;
  struct archive *ard;
  struct archive_entry *entry;
  int fd;

  ard = archive_read_disk_new();
  archive_read_disk_set_standard_lookup(ard);
  entry = archive_entry_new();
  fd = open(name, O_RDONLY);
  if (fd < 0)
     return;
  archive_entry_copy_sourcepath(entry, name);
  archive_read_disk_entry_from_file(ard, entry, fd, NULL);
  archive_write_header(a, entry);
  while ((bytes_read = read(fd, buff, sizeof(buff))) > 0)
    archive_write_data(a, buff, bytes_read);
  archive_write_finish_entry(a);
  archive_read_finish(ard);
  archive_entry_free(entry);
}
.Ed
.Sh RETURN VALUES
Most functions return
.Cm ARCHIVE_OK
(zero) on success, or one of several negative
error codes for errors.
Specific error codes include:
.Cm ARCHIVE_RETRY
for operations that might succeed if retried,
.Cm ARCHIVE_WARN
for unusual conditions that do not prevent further operations, and
.Cm ARCHIVE_FATAL
for serious errors that make remaining operations impossible.
The
.Xr archive_errno 3
and
.Xr archive_error_string 3
functions can be used to retrieve an appropriate error code and a
textual error message.
(See
.Xr archive_util 3
for details.)
.Pp
.Fn archive_read_disk_new
returns a pointer to a newly-allocated
.Tn struct archive
object or NULL if the allocation failed for any reason.
.Pp
.Fn archive_read_disk_gname
and
.Fn archive_read_disk_uname
return
.Tn const char *
pointers to the textual name or NULL if the lookup failed for any reason.
The returned pointer points to internal storage that
may be reused on the next call to either of these functions;
callers should copy the string if they need to continue accessing it.
.Pp
.Sh SEE ALSO
.Xr archive_read 3 ,
.Xr archive_write 3 ,
.Xr archive_write_disk 3 ,
.Xr tar 1 ,
.Xr libarchive 3
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
The
.Nm archive_read_disk
interface was added to
.Nm libarchive 2.6
and first appeared in
.Fx 8.0 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at freebsd.org .
.Sh BUGS
The
.Dq standard
user name and group name lookup functions are not the defaults because
.Xr getgrid 3
and
.Xr getpwid 3
are sometimes too large for particular applications.
The current design allows the application author to use a more
compact implementation when appropriate.
.Pp
The full list of metadata read from disk by
.Fn archive_read_disk_entry_from_file
is necessarily system-dependent.
.Pp
The
.Fn archive_read_disk_entry_from_file
function reads as much information as it can from disk.
Some method should be provided to limit this so that clients who
do not need ACLs, for instance, can avoid the extra work needed
to look up such information.
.Pp
This API should provide a set of methods for walking a directory tree.
That would make it a direct parallel of the
.Xr archive_read 3
API.
When such methods are implemented, the
.Dq hybrid
symbolic link mode will make sense.

--- NEW FILE: archive_check_magic.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.9 2008/12/06 05:52:01 kientzle Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.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
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <windows.h>
#include <winbase.h>
#endif

#include "archive_private.h"

static void
errmsg(const char *m)
{
    size_t s = strlen(m);
    ssize_t written;

    while (s > 0) {
        written = write(2, m, strlen(m));
        if (written <= 0)
            return;
        m += written;
        s -= written;
    }
}

static void
diediedie(void)
{
#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
    /* Cause a breakpoint exception  */
    DebugBreak();
#endif
    *(char *)0 = 1; /* Deliberately segfault and force a coredump. */
    _exit(1);   /* If that didn't work, just exit with an error. */
}

static const char *
state_name(unsigned s)
{
    switch (s) {
    case ARCHIVE_STATE_NEW:     return ("new");
    case ARCHIVE_STATE_HEADER:  return ("header");
    case ARCHIVE_STATE_DATA:    return ("data");
    case ARCHIVE_STATE_EOF:     return ("eof");
    case ARCHIVE_STATE_CLOSED:  return ("closed");
    case ARCHIVE_STATE_FATAL:   return ("fatal");
    default:            return ("??");
    }
}


static void
write_all_states(unsigned int states)
{
    unsigned int lowbit;

    /* A trick for computing the lowest set bit. */
    while ((lowbit = states & (1 + ~states)) != 0) {
        states &= ~lowbit;      /* Clear the low bit. */
        errmsg(state_name(lowbit));
        if (states != 0)
            errmsg("/");
    }
}

/*
 * Check magic value and current state; bail if it isn't valid.
 *
 * This is designed to catch serious programming errors that violate
 * the libarchive API.
 */
void
__archive_check_magic(struct archive *a, unsigned int magic,
    unsigned int state, const char *function)
{
    if (a->magic != magic) {
        errmsg("INTERNAL ERROR: Function ");
        errmsg(function);
        errmsg(" invoked with invalid struct archive structure.\n");
        diediedie();
    }

    if (state == ARCHIVE_STATE_ANY)
        return;

    if ((a->state & state) == 0) {
        errmsg("INTERNAL ERROR: Function '");
        errmsg(function);
        errmsg("' invoked with archive structure in state '");
        write_all_states(a->state);
        errmsg("', should be in state '");
        write_all_states(state);
        errmsg("'\n");
        diediedie();
    }
}

--- NEW FILE: archive_read_disk_set_standard_lookup.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD$");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"

#if defined(_WIN32) && !defined(__CYGWIN__)
int
archive_read_disk_set_standard_lookup(struct archive *a)
{
    archive_set_error(a, -1, "Standard lookups not available on Windows");
    return (ARCHIVE_FATAL);
}
#else /* ! (_WIN32 && !__CYGWIN__) */
#define name_cache_size 127

static const char * const NO_NAME = "(noname)";

struct name_cache {
    struct archive *archive;
    char   *buff;
    size_t  buff_size;
    int probes;
    int hits;
    size_t  size;
    struct {
        id_t id;
        const char *name;
    } cache[name_cache_size];
};

static const char * lookup_gname(void *, gid_t);
static const char * lookup_uname(void *, uid_t);
static void cleanup(void *);
static const char * lookup_gname_helper(struct name_cache *, id_t gid);
static const char * lookup_uname_helper(struct name_cache *, id_t uid);

/*
 * Installs functions that use getpwuid()/getgrgid()---along with
 * a simple cache to accelerate such lookups---into the archive_read_disk
 * object.  This is in a separate file because getpwuid()/getgrgid()
 * can pull in a LOT of library code (including NIS/LDAP functions, which
 * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
 * it inappropriate for some space-constrained applications.
 *
 * Applications that are size-sensitive may want to just use the
 * real default functions (defined in archive_read_disk.c) that just
 * use the uid/gid without the lookup.  Or define your own custom functions
 * if you prefer.
 */
int
archive_read_disk_set_standard_lookup(struct archive *a)
{
    struct name_cache *ucache = malloc(sizeof(struct name_cache));
    struct name_cache *gcache = malloc(sizeof(struct name_cache));

    if (ucache == NULL || gcache == NULL) {
        archive_set_error(a, ENOMEM,
            "Can't allocate uname/gname lookup cache");
        free(ucache);
        free(gcache);
        return (ARCHIVE_FATAL);
    }

    memset(ucache, 0, sizeof(*ucache));
    ucache->archive = a;
    ucache->size = name_cache_size;
    memset(gcache, 0, sizeof(*gcache));
    gcache->archive = a;
    gcache->size = name_cache_size;

    archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
    archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);

    return (ARCHIVE_OK);
}

static void
cleanup(void *data)
{
    struct name_cache *cache = (struct name_cache *)data;
    size_t i;

    if (cache != NULL) {
        for (i = 0; i < cache->size; i++) {
            if (cache->cache[i].name != NULL &&
                cache->cache[i].name != NO_NAME)
                free((void *)(uintptr_t)cache->cache[i].name);
        }
        free(cache->buff);
        free(cache);
    }
}

/*
 * Lookup uid/gid from uname/gname, return NULL if no match.
 */
static const char *
lookup_name(struct name_cache *cache,
    const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
{
    const char *name;
    int slot;


    cache->probes++;

    slot = id % cache->size;
    if (cache->cache[slot].name != NULL) {
        if (cache->cache[slot].id == id) {
            cache->hits++;
            if (cache->cache[slot].name == NO_NAME)
                return (NULL);
            return (cache->cache[slot].name);
        }
        if (cache->cache[slot].name != NO_NAME)
            free((void *)(uintptr_t)cache->cache[slot].name);
        cache->cache[slot].name = NULL;
    }

    name = (lookup_fn)(cache, id);
    if (name == NULL) {
        /* Cache and return the negative response. */
        cache->cache[slot].name = NO_NAME;
        cache->cache[slot].id = id;
        return (NULL);
    }

    cache->cache[slot].name = name;
    cache->cache[slot].id = id;
    return (cache->cache[slot].name);
}

static const char *
lookup_uname(void *data, uid_t uid)
{
    struct name_cache *uname_cache = (struct name_cache *)data;
    return (lookup_name(uname_cache,
            &lookup_uname_helper, (id_t)uid));
}

static const char *
lookup_uname_helper(struct name_cache *cache, id_t id)
{
    struct passwd   pwent, *result;
    int r;

    if (cache->buff_size == 0) {
        cache->buff_size = 256;
        cache->buff = malloc(cache->buff_size);
    }
    if (cache->buff == NULL)
        return (NULL);
    for (;;) {
        r = getpwuid_r((uid_t)id, &pwent,
                   cache->buff, cache->buff_size, &result);
        if (r == 0)
            break;
        if (r != ERANGE)
            break;
        /* ERANGE means our buffer was too small, but POSIX
         * doesn't tell us how big the buffer should be, so
         * we just double it and try again.  Because the buffer
         * is kept around in the cache object, we shouldn't
         * have to do this very often. */
        cache->buff_size *= 2;
        cache->buff = realloc(cache->buff, cache->buff_size);
        if (cache->buff == NULL)
            break;
    }
    if (r != 0) {
        archive_set_error(cache->archive, errno,
            "Can't lookup user for id %d", (int)id);
        return (NULL);
    }
    if (result == NULL)
        return (NULL);

    return strdup(result->pw_name);
}

static const char *
lookup_gname(void *data, gid_t gid)
{
    struct name_cache *gname_cache = (struct name_cache *)data;
    return (lookup_name(gname_cache,
            &lookup_gname_helper, (id_t)gid));
}

static const char *
lookup_gname_helper(struct name_cache *cache, id_t id)
{
    struct group    grent, *result;
    int r;

    if (cache->buff_size == 0) {
        cache->buff_size = 256;
        cache->buff = malloc(cache->buff_size);
    }
    if (cache->buff == NULL)
        return (NULL);
    for (;;) {
        r = getgrgid_r((gid_t)id, &grent,
                   cache->buff, cache->buff_size, &result);
        if (r == 0)
            break;
        if (r != ERANGE)
            break;
        /* ERANGE means our buffer was too small, but POSIX
         * doesn't tell us how big the buffer should be, so
         * we just double it and try again. */
        cache->buff_size *= 2;
        cache->buff = realloc(cache->buff, cache->buff_size);
        if (cache->buff == NULL)
            break;
    }
    if (r != 0) {
        archive_set_error(cache->archive, errno,
            "Can't lookup group for id %d", (int)id);
        return (NULL);
    }
    if (result == NULL)
        return (NULL);

    return strdup(result->gr_name);
}
#endif /* ! (_WIN32 && !__CYGWIN__) */

--- NEW FILE: archive_crc32.h ---
/*-
 * Copyright (c) 2009 Joerg  Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

/*
 * When zlib is unavailable, we should still be able to validate
 * uncompressed zip archives.  That requires us to be able to compute
 * the CRC32 check value.  This is a drop-in compatible replacement
 * for crc32() from zlib.  It's slower than the zlib implementation,
 * but still pretty fast: This runs about 300MB/s on my 3GHz P4
 * compared to about 800MB/s for the zlib implementation.
 */
static unsigned long
crc32(unsigned long crc, const void *_p, size_t len)
{
    unsigned long crc2, b, i;
    const unsigned char *p = _p;
    static volatile int crc_tbl_inited = 0;
    static unsigned long crc_tbl[256];

    if (!crc_tbl_inited) {
        for (b = 0; b < 256; ++b) {
            crc2 = b;
            for (i = 8; i > 0; --i) {
                if (crc2 & 1)
                    crc2 = (crc2 >> 1) ^ 0xedb88320UL;
                else    
                    crc2 = (crc2 >> 1);
            }
            crc_tbl[b] = crc2;
        }
        crc_tbl_inited = 1;
    }

    crc = crc ^ 0xffffffffUL;
    while (len--)
        crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
    return (crc ^ 0xffffffffUL);
}

--- NEW FILE: archive_string_sprintf.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.10 2008/03/14 22:00:09 kientzle Exp $");

/*
 * The use of printf()-family functions can be troublesome
 * for space-constrained applications.  In addition, correctly
 * implementing this function in terms of vsnprintf() requires
 * two calls (one to determine the size, another to format the
 * result), which in turn requires duplicating the argument list
 * using va_copy, which isn't yet universally available. <sigh>
 *
 * So, I've implemented a bare minimum of printf()-like capability
 * here.  This is only used to format error messages, so doesn't
 * require any floating-point support or field-width handling.
 */

#include <stdio.h>

#include "archive_string.h"
#include "archive_private.h"

/*
 * Utility functions to format signed/unsigned integers and append
 * them to an archive_string.
 */
static void
append_uint(struct archive_string *as, uintmax_t d, unsigned base)
{
    static const char *digits = "0123456789abcdef";
    if (d >= base)
        append_uint(as, d/base, base);
    archive_strappend_char(as, digits[d % base]);
}

static void
append_int(struct archive_string *as, intmax_t d, unsigned base)
{
    if (d < 0) {
        archive_strappend_char(as, '-');
        d = -d;
    }
    append_uint(as, d, base);
}


void
__archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    archive_string_vsprintf(as, fmt, ap);
    va_end(ap);
}

/*
 * Like 'vsprintf', but ensures the target is big enough, resizing if
 * necessary.
 */
void
__archive_string_vsprintf(struct archive_string *as, const char *fmt,
    va_list ap)
{
    char long_flag;
    intmax_t s; /* Signed integer temp. */
    uintmax_t u; /* Unsigned integer temp. */
    const char *p, *p2;

    if (__archive_string_ensure(as, 64) == NULL)
        __archive_errx(1, "Out of memory");

    if (fmt == NULL) {
        as->s[0] = 0;
        return;
    }

    for (p = fmt; *p != '\0'; p++) {
        const char *saved_p = p;

        if (*p != '%') {
            archive_strappend_char(as, *p);
            continue;
        }

        p++;

        long_flag = '\0';
        switch(*p) {
        case 'j':
            long_flag = 'j';
            p++;
            break;
        case 'l':
            long_flag = 'l';
            p++;
            break;
        }

        switch (*p) {
        case '%':
            __archive_strappend_char(as, '%');
            break;
        case 'c':
            s = va_arg(ap, int);
            __archive_strappend_char(as, s);
            break;
        case 'd':
            switch(long_flag) {
            case 'j': s = va_arg(ap, intmax_t); break;
            case 'l': s = va_arg(ap, long); break;
            default:  s = va_arg(ap, int); break;
            }
                append_int(as, s, 10);
            break;
        case 's':
            p2 = va_arg(ap, char *);
            archive_strcat(as, p2);
            break;
        case 'o': case 'u': case 'x': case 'X':
            /* Common handling for unsigned integer formats. */
            switch(long_flag) {
            case 'j': u = va_arg(ap, uintmax_t); break;
            case 'l': u = va_arg(ap, unsigned long); break;
            default:  u = va_arg(ap, unsigned int); break;
            }
            /* Format it in the correct base. */
            switch (*p) {
            case 'o': append_uint(as, u, 8); break;
            case 'u': append_uint(as, u, 10); break;
            default: append_uint(as, u, 16); break;
            }
            break;
        default:
            /* Rewind and print the initial '%' literally. */
            p = saved_p;
            archive_strappend_char(as, *p);
        }
    }
}

--- NEW FILE: archive_read_support_format_cpio.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.27 2008/12/06 06:45:15 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
/* #include <stdint.h> */ /* See archive_platform.h */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_private.h"

struct cpio_bin_header {
    unsigned char   c_magic[2];
    unsigned char   c_dev[2];
    unsigned char   c_ino[2];
    unsigned char   c_mode[2];
    unsigned char   c_uid[2];
    unsigned char   c_gid[2];
    unsigned char   c_nlink[2];
    unsigned char   c_rdev[2];
    unsigned char   c_mtime[4];
    unsigned char   c_namesize[2];
    unsigned char   c_filesize[4];
};

struct cpio_odc_header {
    char    c_magic[6];
    char    c_dev[6];
    char    c_ino[6];
    char    c_mode[6];
    char    c_uid[6];
    char    c_gid[6];
    char    c_nlink[6];
    char    c_rdev[6];
    char    c_mtime[11];
    char    c_namesize[6];
    char    c_filesize[11];
};

struct cpio_newc_header {
    char    c_magic[6];
    char    c_ino[8];
    char    c_mode[8];
    char    c_uid[8];
    char    c_gid[8];
    char    c_nlink[8];
    char    c_mtime[8];
    char    c_filesize[8];
    char    c_devmajor[8];
    char    c_devminor[8];
    char    c_rdevmajor[8];
    char    c_rdevminor[8];
    char    c_namesize[8];
    char    c_crc[8];
};

struct links_entry {
        struct links_entry      *next;
        struct links_entry      *previous;
        int                      links;
        dev_t                    dev;
        int64_t                  ino;
        char                    *name;
};

#define CPIO_MAGIC   0x13141516
struct cpio {
    int           magic;
    int         (*read_header)(struct archive_read *, struct cpio *,
                     struct archive_entry *, size_t *, size_t *);
    struct links_entry   *links_head;
    struct archive_string     entry_name;
    struct archive_string     entry_linkname;
    off_t             entry_bytes_remaining;
    off_t             entry_offset;
    off_t             entry_padding;
};

static int64_t  atol16(const char *, unsigned);
static int64_t  atol8(const char *, unsigned);
static int  archive_read_format_cpio_bid(struct archive_read *);
static int  archive_read_format_cpio_cleanup(struct archive_read *);
static int  archive_read_format_cpio_read_data(struct archive_read *,
            const void **, size_t *, off_t *);
static int  archive_read_format_cpio_read_header(struct archive_read *,
            struct archive_entry *);
static int  be4(const unsigned char *);
static int  find_odc_header(struct archive_read *);
static int  find_newc_header(struct archive_read *);
static int  header_bin_be(struct archive_read *, struct cpio *,
            struct archive_entry *, size_t *, size_t *);
static int  header_bin_le(struct archive_read *, struct cpio *,
            struct archive_entry *, size_t *, size_t *);
static int  header_newc(struct archive_read *, struct cpio *,
            struct archive_entry *, size_t *, size_t *);
static int  header_odc(struct archive_read *, struct cpio *,
            struct archive_entry *, size_t *, size_t *);
static int  is_octal(const char *, size_t);
static int  is_hex(const char *, size_t);
static int  le4(const unsigned char *);
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry);

int
archive_read_support_format_cpio(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct cpio *cpio;
    int r;

    cpio = (struct cpio *)malloc(sizeof(*cpio));
    if (cpio == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
        return (ARCHIVE_FATAL);
    }
    memset(cpio, 0, sizeof(*cpio));
    cpio->magic = CPIO_MAGIC;

    r = __archive_read_register_format(a,
        cpio,
        "cpio",
        archive_read_format_cpio_bid,
        NULL,
        archive_read_format_cpio_read_header,
        archive_read_format_cpio_read_data,
        NULL,
        archive_read_format_cpio_cleanup);

    if (r != ARCHIVE_OK)
        free(cpio);
    return (ARCHIVE_OK);
}


static int
archive_read_format_cpio_bid(struct archive_read *a)
{
    const void *h;
    const unsigned char *p;
    struct cpio *cpio;
    int bid;

    cpio = (struct cpio *)(a->format->data);

    if ((h = __archive_read_ahead(a, 6, NULL)) == NULL)
        return (-1);

    p = (const unsigned char *)h;
    bid = 0;
    if (memcmp(p, "070707", 6) == 0) {
        /* ASCII cpio archive (odc, POSIX.1) */
        cpio->read_header = header_odc;
        bid += 48;
        /*
         * XXX TODO:  More verification; Could check that only octal
         * digits appear in appropriate header locations. XXX
         */
    } else if (memcmp(p, "070701", 6) == 0) {
        /* ASCII cpio archive (SVR4 without CRC) */
        cpio->read_header = header_newc;
        bid += 48;
        /*
         * XXX TODO:  More verification; Could check that only hex
         * digits appear in appropriate header locations. XXX
         */
    } else if (memcmp(p, "070702", 6) == 0) {
        /* ASCII cpio archive (SVR4 with CRC) */
        /* XXX TODO: Flag that we should check the CRC. XXX */
        cpio->read_header = header_newc;
        bid += 48;
        /*
         * XXX TODO:  More verification; Could check that only hex
         * digits appear in appropriate header locations. XXX
         */
    } else if (p[0] * 256 + p[1] == 070707) {
        /* big-endian binary cpio archives */
        cpio->read_header = header_bin_be;
        bid += 16;
        /* Is more verification possible here? */
    } else if (p[0] + p[1] * 256 == 070707) {
        /* little-endian binary cpio archives */
        cpio->read_header = header_bin_le;
        bid += 16;
        /* Is more verification possible here? */
    } else
        return (ARCHIVE_WARN);

    return (bid);
}

static int
archive_read_format_cpio_read_header(struct archive_read *a,
    struct archive_entry *entry)
{
    struct cpio *cpio;
    const void *h;
    size_t namelength;
    size_t name_pad;
    int r;

    cpio = (struct cpio *)(a->format->data);
    r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));

    if (r < ARCHIVE_WARN)
        return (r);

    /* Read name from buffer. */
    h = __archive_read_ahead(a, namelength + name_pad, NULL);
    if (h == NULL)
        return (ARCHIVE_FATAL);
    __archive_read_consume(a, namelength + name_pad);
    archive_strncpy(&cpio->entry_name, (const char *)h, namelength);
    archive_entry_set_pathname(entry, cpio->entry_name.s);
    cpio->entry_offset = 0;

    /* If this is a symlink, read the link contents. */
    if (archive_entry_filetype(entry) == AE_IFLNK) {
        h = __archive_read_ahead(a, cpio->entry_bytes_remaining, NULL);
        if (h == NULL)
            return (ARCHIVE_FATAL);
        __archive_read_consume(a, cpio->entry_bytes_remaining);
        archive_strncpy(&cpio->entry_linkname, (const char *)h,
            cpio->entry_bytes_remaining);
        archive_entry_set_symlink(entry, cpio->entry_linkname.s);
        cpio->entry_bytes_remaining = 0;
    }

    /* XXX TODO: If the full mode is 0160200, then this is a Solaris
     * ACL description for the following entry.  Read this body
     * and parse it as a Solaris-style ACL, then read the next
     * header.  XXX */

    /* Compare name to "TRAILER!!!" to test for end-of-archive. */
    if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
        /* TODO: Store file location of start of block. */
        archive_set_error(&a->archive, 0, NULL);
        return (ARCHIVE_EOF);
    }

    /* Detect and record hardlinks to previously-extracted entries. */
    record_hardlink(cpio, entry);

    return (r);
}

static int
archive_read_format_cpio_read_data(struct archive_read *a,
    const void **buff, size_t *size, off_t *offset)
{
    ssize_t bytes_read;
    struct cpio *cpio;

    cpio = (struct cpio *)(a->format->data);
    if (cpio->entry_bytes_remaining > 0) {
        *buff = __archive_read_ahead(a, 1, &bytes_read);
        if (bytes_read <= 0)
            return (ARCHIVE_FATAL);
        if (bytes_read > cpio->entry_bytes_remaining)
            bytes_read = cpio->entry_bytes_remaining;
        *size = bytes_read;
        *offset = cpio->entry_offset;
        cpio->entry_offset += bytes_read;
        cpio->entry_bytes_remaining -= bytes_read;
        __archive_read_consume(a, bytes_read);
        return (ARCHIVE_OK);
    } else {
        while (cpio->entry_padding > 0) {
            *buff = __archive_read_ahead(a, 1, &bytes_read);
            if (bytes_read <= 0)
                return (ARCHIVE_FATAL);
            if (bytes_read > cpio->entry_padding)
                bytes_read = cpio->entry_padding;
            __archive_read_consume(a, bytes_read);
            cpio->entry_padding -= bytes_read;
        }
        *buff = NULL;
        *size = 0;
        *offset = cpio->entry_offset;
        return (ARCHIVE_EOF);
    }
}

/*
 * Skip forward to the next cpio newc header by searching for the
 * 07070[12] string.  This should be generalized and merged with
 * find_odc_header below.
 */
static int
is_hex(const char *p, size_t len)
{
    while (len-- > 0) {
        if ((*p >= '0' && *p <= '9')
            || (*p >= 'a' && *p <= 'f')
            || (*p >= 'A' && *p <= 'F'))
            ++p;
        else
            return (0);
    }
    return (1);
}

static int
find_newc_header(struct archive_read *a)
{
    const void *h;
    const char *p, *q;
    size_t skip, skipped = 0;
    ssize_t bytes;

    for (;;) {
        h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), &bytes);
        if (h == NULL)
            return (ARCHIVE_FATAL);
        p = h;
        q = p + bytes;

        /* Try the typical case first, then go into the slow search.*/
        if (memcmp("07070", p, 5) == 0
            && (p[5] == '1' || p[5] == '2')
            && is_hex(p, sizeof(struct cpio_newc_header)))
            return (ARCHIVE_OK);

        /*
         * Scan ahead until we find something that looks
         * like an odc header.
         */
        while (p + sizeof(struct cpio_newc_header) < q) {
            switch (p[5]) {
            case '1':
            case '2':
                if (memcmp("07070", p, 5) == 0
                    && is_hex(p, sizeof(struct cpio_newc_header))) {
                    skip = p - (const char *)h;
                    __archive_read_consume(a, skip);
                    skipped += skip;
                    if (skipped > 0) {
                        archive_set_error(&a->archive,
                            0,
                            "Skipped %d bytes before "
                            "finding valid header",
                            (int)skipped);
                        return (ARCHIVE_WARN);
                    }
                    return (ARCHIVE_OK);
                }
                p += 2;
                break;
            case '0':
                p++;
                break;
            default:
                p += 6;
                break;
            }
        }
        skip = p - (const char *)h;
        __archive_read_consume(a, skip);
        skipped += skip;
    }
}

static int
header_newc(struct archive_read *a, struct cpio *cpio,
    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
    const void *h;
    const struct cpio_newc_header *header;
    int r;

    r = find_newc_header(a);
    if (r < ARCHIVE_WARN)
        return (r);

    /* Read fixed-size portion of header. */
    h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), NULL);
    if (h == NULL)
        return (ARCHIVE_FATAL);
    __archive_read_consume(a, sizeof(struct cpio_newc_header));

    /* Parse out hex fields. */
    header = (const struct cpio_newc_header *)h;

    if (memcmp(header->c_magic, "070701", 6) == 0) {
        a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
        a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";
    } else if (memcmp(header->c_magic, "070702", 6) == 0) {
        a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
        a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";
    } else {
        /* TODO: Abort here? */
    }

    archive_entry_set_devmajor(entry, atol16(header->c_devmajor, sizeof(header->c_devmajor)));
    archive_entry_set_devminor(entry, atol16(header->c_devminor, sizeof(header->c_devminor)));
    archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino)));
    archive_entry_set_mode(entry, atol16(header->c_mode, sizeof(header->c_mode)));
    archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid)));
    archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid)));
    archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink)));
    archive_entry_set_rdevmajor(entry, atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)));
    archive_entry_set_rdevminor(entry, atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
    archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0);
    *namelength = atol16(header->c_namesize, sizeof(header->c_namesize));
    /* Pad name to 2 more than a multiple of 4. */
    *name_pad = (2 - *namelength) & 3;

    /*
     * Note: entry_bytes_remaining is at least 64 bits and
     * therefore guaranteed to be big enough for a 33-bit file
     * size.
     */
    cpio->entry_bytes_remaining =
        atol16(header->c_filesize, sizeof(header->c_filesize));
    archive_entry_set_size(entry, cpio->entry_bytes_remaining);
    /* Pad file contents to a multiple of 4. */
    cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
    return (r);
}

/*
 * Skip forward to the next cpio odc header by searching for the
 * 070707 string.  This is a hand-optimized search that could
 * probably be easily generalized to handle all character-based
 * cpio variants.
 */
static int
is_octal(const char *p, size_t len)
{
    while (len-- > 0) {
        if (*p < '0' || *p > '7')
            return (0);
            ++p;
    }
    return (1);
}

static int
find_odc_header(struct archive_read *a)
{
    const void *h;
    const char *p, *q;
    size_t skip, skipped = 0;
    ssize_t bytes;

    for (;;) {
        h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), &bytes);
        if (h == NULL)
            return (ARCHIVE_FATAL);
        p = h;
        q = p + bytes;

        /* Try the typical case first, then go into the slow search.*/
        if (memcmp("070707", p, 6) == 0
            && is_octal(p, sizeof(struct cpio_odc_header)))
            return (ARCHIVE_OK);

        /*
         * Scan ahead until we find something that looks
         * like an odc header.
         */
        while (p + sizeof(struct cpio_odc_header) < q) {
            switch (p[5]) {
            case '7':
                if (memcmp("070707", p, 6) == 0
                    && is_octal(p, sizeof(struct cpio_odc_header))) {
                    skip = p - (const char *)h;
                    __archive_read_consume(a, skip);
                    skipped += skip;
                    if (skipped > 0) {
                        archive_set_error(&a->archive,
                            0,
                            "Skipped %d bytes before "
                            "finding valid header",
                            (int)skipped);
                        return (ARCHIVE_WARN);
                    }
                    return (ARCHIVE_OK);
                }
                p += 2;
                break;
            case '0':
                p++;
                break;
            default:
                p += 6;
                break;
            }
        }
        skip = p - (const char *)h;
        __archive_read_consume(a, skip);
        skipped += skip;
    }
}

static int
header_odc(struct archive_read *a, struct cpio *cpio,
    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
    const void *h;
    int r;
    const struct cpio_odc_header *header;

    a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
    a->archive.archive_format_name = "POSIX octet-oriented cpio";

    /* Find the start of the next header. */
    r = find_odc_header(a);
    if (r < ARCHIVE_WARN)
        return (r);

    /* Read fixed-size portion of header. */
    h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL);
    if (h == NULL)
        return (ARCHIVE_FATAL);
    __archive_read_consume(a, sizeof(struct cpio_odc_header));

    /* Parse out octal fields. */
    header = (const struct cpio_odc_header *)h;

    archive_entry_set_dev(entry, atol8(header->c_dev, sizeof(header->c_dev)));
    archive_entry_set_ino(entry, atol8(header->c_ino, sizeof(header->c_ino)));
    archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode)));
    archive_entry_set_uid(entry, atol8(header->c_uid, sizeof(header->c_uid)));
    archive_entry_set_gid(entry, atol8(header->c_gid, sizeof(header->c_gid)));
    archive_entry_set_nlink(entry, atol8(header->c_nlink, sizeof(header->c_nlink)));
    archive_entry_set_rdev(entry, atol8(header->c_rdev, sizeof(header->c_rdev)));
    archive_entry_set_mtime(entry, atol8(header->c_mtime, sizeof(header->c_mtime)), 0);
    *namelength = atol8(header->c_namesize, sizeof(header->c_namesize));
    *name_pad = 0; /* No padding of filename. */

    /*
     * Note: entry_bytes_remaining is at least 64 bits and
     * therefore guaranteed to be big enough for a 33-bit file
     * size.
     */
    cpio->entry_bytes_remaining =
        atol8(header->c_filesize, sizeof(header->c_filesize));
    archive_entry_set_size(entry, cpio->entry_bytes_remaining);
    cpio->entry_padding = 0;
    return (r);
}

static int
header_bin_le(struct archive_read *a, struct cpio *cpio,
    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
    const void *h;
    const struct cpio_bin_header *header;

    a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
    a->archive.archive_format_name = "cpio (little-endian binary)";

    /* Read fixed-size portion of header. */
    h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL);
    if (h == NULL)
        return (ARCHIVE_FATAL);
    __archive_read_consume(a, sizeof(struct cpio_bin_header));

    /* Parse out binary fields. */
    header = (const struct cpio_bin_header *)h;

    archive_entry_set_dev(entry, header->c_dev[0] + header->c_dev[1] * 256);
    archive_entry_set_ino(entry, header->c_ino[0] + header->c_ino[1] * 256);
    archive_entry_set_mode(entry, header->c_mode[0] + header->c_mode[1] * 256);
    archive_entry_set_uid(entry, header->c_uid[0] + header->c_uid[1] * 256);
    archive_entry_set_gid(entry, header->c_gid[0] + header->c_gid[1] * 256);
    archive_entry_set_nlink(entry, header->c_nlink[0] + header->c_nlink[1] * 256);
    archive_entry_set_rdev(entry, header->c_rdev[0] + header->c_rdev[1] * 256);
    archive_entry_set_mtime(entry, le4(header->c_mtime), 0);
    *namelength = header->c_namesize[0] + header->c_namesize[1] * 256;
    *name_pad = *namelength & 1; /* Pad to even. */

    cpio->entry_bytes_remaining = le4(header->c_filesize);
    archive_entry_set_size(entry, cpio->entry_bytes_remaining);
    cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
    return (ARCHIVE_OK);
}

static int
header_bin_be(struct archive_read *a, struct cpio *cpio,
    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
    const void *h;
    const struct cpio_bin_header *header;

    a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
    a->archive.archive_format_name = "cpio (big-endian binary)";

    /* Read fixed-size portion of header. */
    h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL);
    if (h == NULL)
        return (ARCHIVE_FATAL);
    __archive_read_consume(a, sizeof(struct cpio_bin_header));

    /* Parse out binary fields. */
    header = (const struct cpio_bin_header *)h;
    archive_entry_set_dev(entry, header->c_dev[0] * 256 + header->c_dev[1]);
    archive_entry_set_ino(entry, header->c_ino[0] * 256 + header->c_ino[1]);
    archive_entry_set_mode(entry, header->c_mode[0] * 256 + header->c_mode[1]);
    archive_entry_set_uid(entry, header->c_uid[0] * 256 + header->c_uid[1]);
    archive_entry_set_gid(entry, header->c_gid[0] * 256 + header->c_gid[1]);
    archive_entry_set_nlink(entry, header->c_nlink[0] * 256 + header->c_nlink[1]);
    archive_entry_set_rdev(entry, header->c_rdev[0] * 256 + header->c_rdev[1]);
    archive_entry_set_mtime(entry, be4(header->c_mtime), 0);
    *namelength = header->c_namesize[0] * 256 + header->c_namesize[1];
    *name_pad = *namelength & 1; /* Pad to even. */

    cpio->entry_bytes_remaining = be4(header->c_filesize);
    archive_entry_set_size(entry, cpio->entry_bytes_remaining);
    cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
    return (ARCHIVE_OK);
}

static int
archive_read_format_cpio_cleanup(struct archive_read *a)
{
    struct cpio *cpio;

    cpio = (struct cpio *)(a->format->data);
        /* Free inode->name map */
        while (cpio->links_head != NULL) {
                struct links_entry *lp = cpio->links_head->next;

                if (cpio->links_head->name)
                        free(cpio->links_head->name);
                free(cpio->links_head);
                cpio->links_head = lp;
        }
    archive_string_free(&cpio->entry_name);
    free(cpio);
    (a->format->data) = NULL;
    return (ARCHIVE_OK);
}

static int
le4(const unsigned char *p)
{
    return ((p[0]<<16) + (p[1]<<24) + (p[2]<<0) + (p[3]<<8));
}


static int
be4(const unsigned char *p)
{
    return ((p[0]<<24) + (p[1]<<16) + (p[2]<<8) + (p[3]));
}

/*
 * Note that this implementation does not (and should not!) obey
 * locale settings; you cannot simply substitute strtol here, since
 * it does obey locale.
 */
static int64_t
atol8(const char *p, unsigned char_cnt)
{
    int64_t l;
    int digit;

    l = 0;
    while (char_cnt-- > 0) {
        if (*p >= '0' && *p <= '7')
            digit = *p - '0';
        else
            return (l);
        p++;
        l <<= 3;
        l |= digit;
    }
    return (l);
}

static int64_t
atol16(const char *p, unsigned char_cnt)
{
    int64_t l;
    int digit;

    l = 0;
    while (char_cnt-- > 0) {
        if (*p >= 'a' && *p <= 'f')
            digit = *p - 'a' + 10;
        else if (*p >= 'A' && *p <= 'F')
            digit = *p - 'A' + 10;
        else if (*p >= '0' && *p <= '9')
            digit = *p - '0';
        else
            return (l);
        p++;
        l <<= 4;
        l |= digit;
    }
    return (l);
}

static void
record_hardlink(struct cpio *cpio, struct archive_entry *entry)
{
    struct links_entry      *le;
    dev_t dev;
    int64_t ino;

    dev = archive_entry_dev(entry);
    ino = archive_entry_ino64(entry);

    /*
     * First look in the list of multiply-linked files.  If we've
     * already dumped it, convert this entry to a hard link entry.
     */
    for (le = cpio->links_head; le; le = le->next) {
        if (le->dev == dev && le->ino == ino) {
            archive_entry_copy_hardlink(entry, le->name);

            if (--le->links <= 0) {
                if (le->previous != NULL)
                    le->previous->next = le->next;
                if (le->next != NULL)
                    le->next->previous = le->previous;
                if (cpio->links_head == le)
                    cpio->links_head = le->next;
                free(le->name);
                free(le);
            }

            return;
        }
    }

    le = (struct links_entry *)malloc(sizeof(struct links_entry));
    if (le == NULL)
        __archive_errx(1, "Out of memory adding file to list");
    if (cpio->links_head != NULL)
        cpio->links_head->previous = le;
    le->next = cpio->links_head;
    le->previous = NULL;
    cpio->links_head = le;
    le->dev = dev;
    le->ino = ino;
    le->links = archive_entry_nlink(entry) - 1;
    le->name = strdup(archive_entry_pathname(entry));
    if (le->name == NULL)
        __archive_errx(1, "Out of memory adding file to list");
}

--- NEW FILE: archive_read_disk.c ---
/*-
 * Copyright (c) 2003-2009 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 "archive_platform.h"
__FBSDID("$FreeBSD$");

#include "archive.h"
#include "archive_string.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_disk_private.h"

static int  _archive_read_finish(struct archive *);
static int  _archive_read_close(struct archive *);
static const char *trivial_lookup_gname(void *, gid_t gid);
static const char *trivial_lookup_uname(void *, uid_t uid);

static struct archive_vtable *
archive_read_disk_vtable(void)
{
    static struct archive_vtable av;
    static int inited = 0;

    if (!inited) {
        av.archive_finish = _archive_read_finish;
        av.archive_close = _archive_read_close;
    }
    return (&av);
}

const char *
archive_read_disk_gname(struct archive *_a, gid_t gid)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    if (a->lookup_gname != NULL)
        return ((*a->lookup_gname)(a->lookup_gname_data, gid));
    return (NULL);
}

const char *
archive_read_disk_uname(struct archive *_a, uid_t uid)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    if (a->lookup_uname != NULL)
        return ((*a->lookup_uname)(a->lookup_uname_data, uid));
    return (NULL);
}

int
archive_read_disk_set_gname_lookup(struct archive *_a,
    void *private_data,
    const char * (*lookup_gname)(void *private, gid_t gid),
    void (*cleanup_gname)(void *private))
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
        ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");

    if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
        (a->cleanup_gname)(a->lookup_gname_data);

    a->lookup_gname = lookup_gname;
    a->cleanup_gname = cleanup_gname;
    a->lookup_gname_data = private_data;
    return (ARCHIVE_OK);
}

int
archive_read_disk_set_uname_lookup(struct archive *_a,
    void *private_data,
    const char * (*lookup_uname)(void *private, uid_t uid),
    void (*cleanup_uname)(void *private))
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
        ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");

    if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
        (a->cleanup_uname)(a->lookup_uname_data);

    a->lookup_uname = lookup_uname;
    a->cleanup_uname = cleanup_uname;
    a->lookup_uname_data = private_data;
    return (ARCHIVE_OK);
}

/*
 * Create a new archive_read_disk object and initialize it with global state.
 */
struct archive *
archive_read_disk_new(void)
{
    struct archive_read_disk *a;

    a = (struct archive_read_disk *)malloc(sizeof(*a));
    if (a == NULL)
        return (NULL);
    memset(a, 0, sizeof(*a));
    a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
    /* We're ready to write a header immediately. */
    a->archive.state = ARCHIVE_STATE_HEADER;
    a->archive.vtable = archive_read_disk_vtable();
    a->lookup_uname = trivial_lookup_uname;
    a->lookup_gname = trivial_lookup_gname;
    return (&a->archive);
}

static int
_archive_read_finish(struct archive *_a)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;

    if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
        (a->cleanup_gname)(a->lookup_gname_data);
    if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
        (a->cleanup_uname)(a->lookup_uname_data);
    archive_string_free(&a->archive.error_string);
    free(a);
    return (ARCHIVE_OK);
}

static int
_archive_read_close(struct archive *_a)
{
    (void)_a; /* UNUSED */
    return (ARCHIVE_OK);
}

int
archive_read_disk_set_symlink_logical(struct archive *_a)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    a->symlink_mode = 'L';
    a->follow_symlinks = 1;
    return (ARCHIVE_OK);
}

int
archive_read_disk_set_symlink_physical(struct archive *_a)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    a->symlink_mode = 'P';
    a->follow_symlinks = 0;
    return (ARCHIVE_OK);
}

int
archive_read_disk_set_symlink_hybrid(struct archive *_a)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    a->symlink_mode = 'H';
    a->follow_symlinks = 1; /* Follow symlinks initially. */
    return (ARCHIVE_OK);
}

/*
 * Trivial implementations of gname/uname lookup functions.
 * These are normally overridden by the client, but these stub
 * versions ensure that we always have something that works.
 */
static const char *
trivial_lookup_gname(void *private_data, gid_t gid)
{
    (void)private_data; /* UNUSED */
    (void)gid; /* UNUSED */
    return (NULL);
}

static const char *
trivial_lookup_uname(void *private_data, uid_t uid)
{
    (void)private_data; /* UNUSED */
    (void)uid; /* UNUSED */
    return (NULL);
}

--- NEW FILE: filter_fork.h ---
/*-
 * Copyright (c) 2007 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/filter_fork.h,v 1.1 2007/05/29 01:00:20 kientzle Exp $
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef FILTER_FORK_H
#define FILTER_FORK_H

pid_t
__archive_create_child(const char *path, int *child_stdin, int *child_stdout);

void
__archive_check_child(int in, int out);

#endif

--- NEW FILE: archive_write_set_format_ar.c ---
/*-
 * Copyright (c) 2007 Kai Wang
 * Copyright (c) 2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.8 2008/08/10 02:06:28 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

struct ar_w {
    uint64_t     entry_bytes_remaining;
    uint64_t     entry_padding;
    int      is_strtab;
    int      has_strtab;
    char        *strtab;
};

/*
 * Define structure of the "ar" header.
 */
#define AR_name_offset 0
#define AR_name_size 16
#define AR_date_offset 16
#define AR_date_size 12
#define AR_uid_offset 28
#define AR_uid_size 6
#define AR_gid_offset 34
#define AR_gid_size 6
#define AR_mode_offset 40
#define AR_mode_size 8
#define AR_size_offset 48
#define AR_size_size 10
#define AR_fmag_offset 58
#define AR_fmag_size 2

static int       archive_write_set_format_ar(struct archive_write *);
static int       archive_write_ar_header(struct archive_write *,
                 struct archive_entry *);
static ssize_t       archive_write_ar_data(struct archive_write *,
                 const void *buff, size_t s);
static int       archive_write_ar_destroy(struct archive_write *);
static int       archive_write_ar_finish(struct archive_write *);
static int       archive_write_ar_finish_entry(struct archive_write *);
static const char   *ar_basename(const char *path);
static int       format_octal(int64_t v, char *p, int s);
static int       format_decimal(int64_t v, char *p, int s);

int
archive_write_set_format_ar_bsd(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    int r = archive_write_set_format_ar(a);
    if (r == ARCHIVE_OK) {
        a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
        a->archive.archive_format_name = "ar (BSD)";
    }
    return (r);
}

int
archive_write_set_format_ar_svr4(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    int r = archive_write_set_format_ar(a);
    if (r == ARCHIVE_OK) {
        a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
        a->archive.archive_format_name = "ar (GNU/SVR4)";
    }
    return (r);
}

/*
 * Generic initialization.
 */
static int
archive_write_set_format_ar(struct archive_write *a)
{
    struct ar_w *ar;

    /* If someone else was already registered, unregister them. */
    if (a->format_destroy != NULL)
        (a->format_destroy)(a);

    ar = (struct ar_w *)malloc(sizeof(*ar));
    if (ar == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");
        return (ARCHIVE_FATAL);
    }
    memset(ar, 0, sizeof(*ar));
    a->format_data = ar;

    a->format_name = "ar";
    a->format_write_header = archive_write_ar_header;
    a->format_write_data = archive_write_ar_data;
    a->format_finish = archive_write_ar_finish;
    a->format_destroy = archive_write_ar_destroy;
    a->format_finish_entry = archive_write_ar_finish_entry;
    return (ARCHIVE_OK);
}

static int
archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
{
    int ret, append_fn;
    char buff[60];
    char *ss, *se;
    struct ar_w *ar;
    const char *pathname;
    const char *filename;
    int64_t size;

    ret = 0;
    append_fn = 0;
    ar = (struct ar_w *)a->format_data;
    ar->is_strtab = 0;
    filename = NULL;
    size = archive_entry_size(entry);


    /*
     * Reject files with empty name.
     */
    pathname = archive_entry_pathname(entry);
    if (*pathname == '\0') {
        archive_set_error(&a->archive, EINVAL,
            "Invalid filename");
        return (ARCHIVE_WARN);
    }

    /*
     * If we are now at the beginning of the archive,
     * we need first write the ar global header.
     */
    if (a->archive.file_position == 0)
        (a->compressor.write)(a, "!<arch>\n", 8);

    memset(buff, ' ', 60);
    strncpy(&buff[AR_fmag_offset], "`\n", 2);

    if (strcmp(pathname, "/") == 0 ) {
        /* Entry is archive symbol table in GNU format */
        buff[AR_name_offset] = '/';
        goto stat;
    }
    if (strcmp(pathname, "__.SYMDEF") == 0) {
        /* Entry is archive symbol table in BSD format */
        strncpy(buff + AR_name_offset, "__.SYMDEF", 9);
        goto stat;
    }
    if (strcmp(pathname, "//") == 0) {
        /*
         * Entry is archive filename table, inform that we should
         * collect strtab in next _data call.
         */
        ar->is_strtab = 1;
        buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
        /*
         * For archive string table, only ar_size filed should
         * be set.
         */
        goto size;
    }

    /*
     * Otherwise, entry is a normal archive member.
     * Strip leading paths from filenames, if any.
     */
    if ((filename = ar_basename(pathname)) == NULL) {
        /* Reject filenames with trailing "/" */
        archive_set_error(&a->archive, EINVAL,
            "Invalid filename");
        return (ARCHIVE_WARN);
    }

    if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
        /*
         * SVR4/GNU variant use a "/" to mark then end of the filename,
         * make it possible to have embedded spaces in the filename.
         * So, the longest filename here (without extension) is
         * actually 15 bytes.
         */
        if (strlen(filename) <= 15) {
            strncpy(&buff[AR_name_offset], 
                filename, strlen(filename));
            buff[AR_name_offset + strlen(filename)] = '/';
        } else {
            /*
             * For filename longer than 15 bytes, GNU variant
             * makes use of a string table and instead stores the
             * offset of the real filename to in the ar_name field.
             * The string table should have been written before.
             */
            if (ar->has_strtab <= 0) {
                archive_set_error(&a->archive, EINVAL,
                    "Can't find string table");
                return (ARCHIVE_WARN);
            }

            se = (char *)malloc(strlen(filename) + 3);
            if (se == NULL) {
                archive_set_error(&a->archive, ENOMEM,
                    "Can't allocate filename buffer");
                return (ARCHIVE_FATAL);
            }

            strncpy(se, filename, strlen(filename));
            strcpy(se + strlen(filename), "/\n");

            ss = strstr(ar->strtab, se);
            free(se);

            if (ss == NULL) {
                archive_set_error(&a->archive, EINVAL,
                    "Invalid string table");
                return (ARCHIVE_WARN);
            }

            /*
             * GNU variant puts "/" followed by digits into
             * ar_name field. These digits indicates the real
             * filename string's offset to the string table.
             */
            buff[AR_name_offset] = '/';
            if (format_decimal(ss - ar->strtab,
                buff + AR_name_offset + 1,
                AR_name_size - 1)) {
                archive_set_error(&a->archive, ERANGE,
                    "string table offset too large");
                return (ARCHIVE_WARN);
            }
        }
    } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) {
        /*
         * BSD variant: for any file name which is more than
         * 16 chars or contains one or more embedded space(s), the
         * string "#1/" followed by the ASCII length of the name is
         * put into the ar_name field. The file size (stored in the
         * ar_size field) is incremented by the length of the name.
         * The name is then written immediately following the
         * archive header.
         */
        if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
            strncpy(&buff[AR_name_offset], filename, strlen(filename));
            buff[AR_name_offset + strlen(filename)] = ' ';
        }
        else {
            strncpy(buff + AR_name_offset, "#1/", 3);
            if (format_decimal(strlen(filename),
                buff + AR_name_offset + 3,
                AR_name_size - 3)) {
                archive_set_error(&a->archive, ERANGE,
                    "File name too long");
                return (ARCHIVE_WARN);
            }
            append_fn = 1;
            size += strlen(filename);
        }
    }

stat:
    if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
        archive_set_error(&a->archive, ERANGE,
            "File modification time too large");
        return (ARCHIVE_WARN);
    }
    if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
        archive_set_error(&a->archive, ERANGE,
            "Numeric user ID too large");
        return (ARCHIVE_WARN);
    }
    if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
        archive_set_error(&a->archive, ERANGE,
            "Numeric group ID too large");
        return (ARCHIVE_WARN);
    }
    if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
        archive_set_error(&a->archive, ERANGE,
            "Numeric mode too large");
        return (ARCHIVE_WARN);
    }
    /*
     * Sanity Check: A non-pseudo archive member should always be
     * a regular file.
     */
    if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
        archive_set_error(&a->archive, EINVAL,
            "Regular file required for non-pseudo member");
        return (ARCHIVE_WARN);
    }

size:
    if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {
        archive_set_error(&a->archive, ERANGE,
            "File size out of range");
        return (ARCHIVE_WARN);
    }

    ret = (a->compressor.write)(a, buff, 60);
    if (ret != ARCHIVE_OK)
        return (ret);

    ar->entry_bytes_remaining = size;
    ar->entry_padding = ar->entry_bytes_remaining % 2;

    if (append_fn > 0) {
        ret = (a->compressor.write)(a, filename, strlen(filename));
        if (ret != ARCHIVE_OK)
            return (ret);
        ar->entry_bytes_remaining -= strlen(filename);
    }

    return (ARCHIVE_OK);
}

static ssize_t
archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
{
    struct ar_w *ar;
    int ret;

    ar = (struct ar_w *)a->format_data;
    if (s > ar->entry_bytes_remaining)
        s = ar->entry_bytes_remaining;

    if (ar->is_strtab > 0) {
        if (ar->has_strtab > 0) {
            archive_set_error(&a->archive, EINVAL,
                "More than one string tables exist");
            return (ARCHIVE_WARN);
        }

        ar->strtab = (char *)malloc(s);
        if (ar->strtab == NULL) {
            archive_set_error(&a->archive, ENOMEM,
                "Can't allocate strtab buffer");
            return (ARCHIVE_FATAL);
        }
        strncpy(ar->strtab, buff, s);
        ar->has_strtab = 1;
    }

    ret = (a->compressor.write)(a, buff, s);
    if (ret != ARCHIVE_OK)
        return (ret);

    ar->entry_bytes_remaining -= s;
    return (s);
}

static int
archive_write_ar_destroy(struct archive_write *a)
{
    struct ar_w *ar;

    ar = (struct ar_w *)a->format_data;

    if (ar == NULL)
        return (ARCHIVE_OK);

    if (ar->has_strtab > 0) {
        free(ar->strtab);
        ar->strtab = NULL;
    }

    free(ar);
    a->format_data = NULL;
    return (ARCHIVE_OK);
}

static int
archive_write_ar_finish(struct archive_write *a)
{
    int ret;

    /*
     * If we haven't written anything yet, we need to write
     * the ar global header now to make it a valid ar archive.
     */
    if (a->archive.file_position == 0) {
        ret = (a->compressor.write)(a, "!<arch>\n", 8);
        return (ret);
    }

    return (ARCHIVE_OK);
}

static int
archive_write_ar_finish_entry(struct archive_write *a)
{
    struct ar_w *ar;
    int ret;

    ar = (struct ar_w *)a->format_data;

    if (ar->entry_bytes_remaining != 0) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Entry remaining bytes larger than 0");
        return (ARCHIVE_WARN);
    }

    if (ar->entry_padding == 0) {
        return (ARCHIVE_OK);
    }

    if (ar->entry_padding != 1) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Padding wrong size: %d should be 1 or 0",
            ar->entry_padding);
        return (ARCHIVE_WARN);
    }

    ret = (a->compressor.write)(a, "\n", 1);
    return (ret);
}

/*
 * Format a number into the specified field using base-8.
 * NB: This version is slightly different from the one in
 * _ustar.c
 */
static int
format_octal(int64_t v, char *p, int s)
{
    int len;
    char *h;

    len = s;
    h = p;

    /* Octal values can't be negative, so use 0. */
    if (v < 0) {
        while (len-- > 0)
            *p++ = '0';
        return (-1);
    }

    p += s;     /* Start at the end and work backwards. */
    do {
        *--p = (char)('0' + (v & 7));
        v >>= 3;
    } while (--s > 0 && v > 0);

    if (v == 0) {
        memmove(h, p, len - s);
        p = h + len - s;
        while (s-- > 0)
            *p++ = ' ';
        return (0);
    }
    /* If it overflowed, fill field with max value. */
    while (len-- > 0)
        *p++ = '7';

    return (-1);
}

/*
 * Format a number into the specified field using base-10.
 */
static int
format_decimal(int64_t v, char *p, int s)
{
    int len;
    char *h;

    len = s;
    h = p;

    /* Negative values in ar header are meaningless , so use 0. */
    if (v < 0) {
        while (len-- > 0)
            *p++ = '0';
        return (-1);
    }

    p += s;
    do {
        *--p = (char)('0' + (v % 10));
        v /= 10;
    } while (--s > 0 && v > 0);

    if (v == 0) {
        memmove(h, p, len - s);
        p = h + len - s;
        while (s-- > 0)
            *p++ = ' ';
        return (0);
    }
    /* If it overflowed, fill field with max value. */
    while (len-- > 0)
        *p++ = '9';

    return (-1);
}

static const char *
ar_basename(const char *path)
{
    const char *endp, *startp;

    endp = path + strlen(path) - 1;
    /*
     * For filename with trailing slash(es), we return
     * NULL indicating an error.
     */
    if (*endp == '/')
        return (NULL);

    /* Find the start of the base */
    startp = endp;
    while (startp > path && *(startp - 1) != '/')
        startp--;
    
    return (startp);
}

--- NEW FILE: archive_write_set_format_cpio.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.14 2008/03/15 11:04:45 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

static ssize_t  archive_write_cpio_data(struct archive_write *,
            const void *buff, size_t s);
static int  archive_write_cpio_finish(struct archive_write *);
static int  archive_write_cpio_destroy(struct archive_write *);
static int  archive_write_cpio_finish_entry(struct archive_write *);
static int  archive_write_cpio_header(struct archive_write *,
            struct archive_entry *);
static int  format_octal(int64_t, void *, int);
static int64_t  format_octal_recursive(int64_t, char *, int);

struct cpio {
    uint64_t      entry_bytes_remaining;
};

struct cpio_header {
    char    c_magic[6];
    char    c_dev[6];
    char    c_ino[6];
    char    c_mode[6];
    char    c_uid[6];
    char    c_gid[6];
    char    c_nlink[6];
    char    c_rdev[6];
    char    c_mtime[11];
    char    c_namesize[6];
    char    c_filesize[11];
};

/*
 * Set output format to 'cpio' format.
 */
int
archive_write_set_format_cpio(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct cpio *cpio;

    /* If someone else was already registered, unregister them. */
    if (a->format_destroy != NULL)
        (a->format_destroy)(a);

    cpio = (struct cpio *)malloc(sizeof(*cpio));
    if (cpio == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
        return (ARCHIVE_FATAL);
    }
    memset(cpio, 0, sizeof(*cpio));
    a->format_data = cpio;

    a->pad_uncompressed = 1;
    a->format_name = "cpio";
    a->format_write_header = archive_write_cpio_header;
    a->format_write_data = archive_write_cpio_data;
    a->format_finish_entry = archive_write_cpio_finish_entry;
    a->format_finish = archive_write_cpio_finish;
    a->format_destroy = archive_write_cpio_destroy;
    a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
    a->archive.archive_format_name = "POSIX cpio";
    return (ARCHIVE_OK);
}

static int
archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
{
    struct cpio *cpio;
    const char *p, *path;
    int pathlength, ret;
    int64_t ino;
    struct cpio_header   h;

    cpio = (struct cpio *)a->format_data;
    ret = 0;

    path = archive_entry_pathname(entry);
    pathlength = strlen(path) + 1; /* Include trailing null. */

    memset(&h, 0, sizeof(h));
    format_octal(070707, &h.c_magic, sizeof(h.c_magic));
    format_octal(archive_entry_dev(entry), &h.c_dev, sizeof(h.c_dev));
    /*
     * TODO: Generate artificial inode numbers rather than just
     * re-using the ones off the disk.  That way, the 18-bit c_ino
     * field only limits the number of files in the archive.
     */
    ino = archive_entry_ino64(entry);
    if (ino < 0 || ino > 0777777) {
        archive_set_error(&a->archive, ERANGE,
            "large inode number truncated");
        ret = ARCHIVE_WARN;
    }

    format_octal(archive_entry_ino64(entry) & 0777777, &h.c_ino, sizeof(h.c_ino));
    format_octal(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
    format_octal(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
    format_octal(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
    format_octal(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
    if (archive_entry_filetype(entry) == AE_IFBLK
        || archive_entry_filetype(entry) == AE_IFCHR)
        format_octal(archive_entry_dev(entry), &h.c_rdev, sizeof(h.c_rdev));
    else
        format_octal(0, &h.c_rdev, sizeof(h.c_rdev));
    format_octal(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
    format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize));

    /* Non-regular files don't store bodies. */
    if (archive_entry_filetype(entry) != AE_IFREG)
        archive_entry_set_size(entry, 0);

    /* Symlinks get the link written as the body of the entry. */
    p = archive_entry_symlink(entry);
    if (p != NULL  &&  *p != '\0')
        format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
    else
        format_octal(archive_entry_size(entry),
            &h.c_filesize, sizeof(h.c_filesize));

    ret = (a->compressor.write)(a, &h, sizeof(h));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);

    ret = (a->compressor.write)(a, path, pathlength);
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);

    cpio->entry_bytes_remaining = archive_entry_size(entry);

    /* Write the symlink now. */
    if (p != NULL  &&  *p != '\0')
        ret = (a->compressor.write)(a, p, strlen(p));

    return (ret);
}

static ssize_t
archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s)
{
    struct cpio *cpio;
    int ret;

    cpio = (struct cpio *)a->format_data;
    if (s > cpio->entry_bytes_remaining)
        s = cpio->entry_bytes_remaining;

    ret = (a->compressor.write)(a, buff, s);
    cpio->entry_bytes_remaining -= s;
    if (ret >= 0)
        return (s);
    else
        return (ret);
}

/*
 * Format a number into the specified field.
 */
static int
format_octal(int64_t v, void *p, int digits)
{
    int64_t max;
    int ret;

    max = (((int64_t)1) << (digits * 3)) - 1;
    if (v >= 0  &&  v <= max) {
        format_octal_recursive(v, (char *)p, digits);
        ret = 0;
    } else {
        format_octal_recursive(max, (char *)p, digits);
        ret = -1;
    }
    return (ret);
}

static int64_t
format_octal_recursive(int64_t v, char *p, int s)
{
    if (s == 0)
        return (v);
    v = format_octal_recursive(v, p+1, s-1);
    *p = '0' + (v & 7);
    return (v >>= 3);
}

static int
archive_write_cpio_finish(struct archive_write *a)
{
    struct cpio *cpio;
    int er;
    struct archive_entry *trailer;

    cpio = (struct cpio *)a->format_data;
    trailer = archive_entry_new();
    /* nlink = 1 here for GNU cpio compat. */
    archive_entry_set_nlink(trailer, 1);
    archive_entry_set_pathname(trailer, "TRAILER!!!");
    er = archive_write_cpio_header(a, trailer);
    archive_entry_free(trailer);
    return (er);
}

static int
archive_write_cpio_destroy(struct archive_write *a)
{
    struct cpio *cpio;

    cpio = (struct cpio *)a->format_data;
    free(cpio);
    a->format_data = NULL;
    return (ARCHIVE_OK);
}

static int
archive_write_cpio_finish_entry(struct archive_write *a)
{
    struct cpio *cpio;
    int to_write, ret;

    cpio = (struct cpio *)a->format_data;
    ret = ARCHIVE_OK;
    while (cpio->entry_bytes_remaining > 0) {
        to_write = cpio->entry_bytes_remaining < a->null_length ?
            cpio->entry_bytes_remaining : a->null_length;
        ret = (a->compressor.write)(a, a->nulls, to_write);
        if (ret != ARCHIVE_OK)
            return (ret);
        cpio->entry_bytes_remaining -= to_write;
    }
    return (ret);
}

--- NEW FILE: archive_read_support_format_tar.c ---
/*-
 * 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,
[...2379 lines suppressed...]
            /* Else fall through and treat '%' as normal char */
        }
        *d++ = *s++;
    }
    *d = '\0';
    return (out);
}

static int
tohex(int c)
{
    if (c >= '0' && c <= '9')
        return (c - '0');
    else if (c >= 'A' && c <= 'F')
        return (c - 'A' + 10);
    else if (c >= 'a' && c <= 'f')
        return (c - 'a' + 10);
    else
        return (-1);
}

--- NEW FILE: archive_entry_strmode.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive_entry.h"
#include "archive_entry_private.h"

const char *
archive_entry_strmode(struct archive_entry *entry)
{
    static const mode_t permbits[] =
        { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
    char *bp = entry->strmode;
    mode_t mode;
    int i;

    /* Fill in a default string, then selectively override. */
    strcpy(bp, "?rwxrwxrwx ");

    mode = archive_entry_mode(entry);
    switch (archive_entry_filetype(entry)) {
    case AE_IFREG:  bp[0] = '-'; break;
    case AE_IFBLK:  bp[0] = 'b'; break;
    case AE_IFCHR:  bp[0] = 'c'; break;
    case AE_IFDIR:  bp[0] = 'd'; break;
    case AE_IFLNK:  bp[0] = 'l'; break;
    case AE_IFSOCK: bp[0] = 's'; break;
    case AE_IFIFO:  bp[0] = 'p'; break;
    default:
        if (archive_entry_hardlink(entry) != NULL) {
            bp[0] = 'h';
            break;
        }
    }

    for (i = 0; i < 9; i++)
        if (!(mode & permbits[i]))
            bp[i+1] = '-';

    if (mode & S_ISUID) {
        if (mode & 0100) bp[3] = 's';
        else bp[3] = 'S';
    }
    if (mode & S_ISGID) {
        if (mode & 0010) bp[6] = 's';
        else bp[6] = 'S';
    }
    if (mode & S_ISVTX) {
        if (mode & 0001) bp[9] = 't';
        else bp[9] = 'T';
    }
    if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
        bp[10] = '+';

    return (bp);
}

--- NEW FILE: archive_windows.c ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * Copyright (c) 2003-2007 Kees Zeelenberg
 * 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
[...1190 lines suppressed...]
DIGEST_INIT(SHA256, CALG_SHA256)
DIGEST_UPDATE(SHA256)
DIGEST_FINAL(SHA256, SHA256_DIGEST_LENGTH)
#endif

#ifdef CALG_SHA384
DIGEST_INIT(SHA384, CALG_SHA384)
DIGEST_UPDATE(SHA384)
DIGEST_FINAL(SHA384, SHA384_DIGEST_LENGTH)
#endif

#ifdef CALG_SHA512
DIGEST_INIT(SHA512, CALG_SHA512)
DIGEST_UPDATE(SHA512)
DIGEST_FINAL(SHA512, SHA384_DIGEST_LENGTH)
#endif

#endif /* !HAVE_OPENSSL_MD5_H && !HAVE_OPENSSL_SHA_H */

#endif /* _WIN32 && !__CYGWIN__ */

--- NEW FILE: archive_read_support_compression_all.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.7 2008/12/06 06:45:15 kientzle Exp $");

#include "archive.h"

int
archive_read_support_compression_all(struct archive *a)
{
    /* Bzip falls back to "bunzip2" command-line */
    archive_read_support_compression_bzip2(a);
    /* The decompress code doesn't use an outside library. */
    archive_read_support_compression_compress(a);
    /* Gzip decompress falls back to "gunzip" command-line. */
    archive_read_support_compression_gzip(a);
    /* The LZMA file format has a very weak signature, so it
     * may not be feasible to keep this here, but we'll try.
     * This will come back out if there are problems. */
    /* Lzma falls back to "unlzma" command-line program. */
    archive_read_support_compression_lzma(a);
    /* Xz falls back to "unxz" command-line program. */
    archive_read_support_compression_xz(a);

    /* Note: We always return ARCHIVE_OK here, even if some of the
     * above return ARCHIVE_WARN.  The intent here is to enable
     * "as much as possible."  Clients who need specific
     * compression should enable those individually so they can
     * verify the level of support. */
    /* Clear any warning messages set by the above functions. */
    archive_clear_error(a);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_write_set_format_cpio_newc.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio_newc.c,v 1.4 2008/03/15 11:04:45 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

static ssize_t  archive_write_newc_data(struct archive_write *,
            const void *buff, size_t s);
static int  archive_write_newc_finish(struct archive_write *);
static int  archive_write_newc_destroy(struct archive_write *);
static int  archive_write_newc_finish_entry(struct archive_write *);
static int  archive_write_newc_header(struct archive_write *,
            struct archive_entry *);
static int  format_hex(int64_t, void *, int);
static int64_t  format_hex_recursive(int64_t, char *, int);

struct cpio {
    uint64_t      entry_bytes_remaining;
    int       padding;
};

struct cpio_header_newc {
    char    c_magic[6];
    char    c_ino[8];
    char    c_mode[8];
    char    c_uid[8];
    char    c_gid[8];
    char    c_nlink[8];
    char    c_mtime[8];
    char    c_filesize[8];
    char    c_devmajor[8];
    char    c_devminor[8];
    char    c_rdevmajor[8];
    char    c_rdevminor[8];
    char    c_namesize[8];
    char    c_checksum[8];
};

/* Logic trick: difference between 'n' and next multiple of 4 */
#define PAD4(n) (3 & (1 + ~(n)))

/*
 * Set output format to 'cpio' format.
 */
int
archive_write_set_format_cpio_newc(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct cpio *cpio;

    /* If someone else was already registered, unregister them. */
    if (a->format_destroy != NULL)
        (a->format_destroy)(a);

    cpio = (struct cpio *)malloc(sizeof(*cpio));
    if (cpio == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
        return (ARCHIVE_FATAL);
    }
    memset(cpio, 0, sizeof(*cpio));
    a->format_data = cpio;

    a->pad_uncompressed = 1;
    a->format_name = "cpio";
    a->format_write_header = archive_write_newc_header;
    a->format_write_data = archive_write_newc_data;
    a->format_finish_entry = archive_write_newc_finish_entry;
    a->format_finish = archive_write_newc_finish;
    a->format_destroy = archive_write_newc_destroy;
    a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
    a->archive.archive_format_name = "SVR4 cpio nocrc";
    return (ARCHIVE_OK);
}

static int
archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
{
    struct cpio *cpio;
    const char *p, *path;
    int pathlength, ret;
    struct cpio_header_newc  h;
    int pad;

    cpio = (struct cpio *)a->format_data;
    ret = 0;

    path = archive_entry_pathname(entry);
    pathlength = strlen(path) + 1; /* Include trailing null. */

    memset(&h, 0, sizeof(h));
    format_hex(0x070701, &h.c_magic, sizeof(h.c_magic));
    format_hex(archive_entry_devmajor(entry), &h.c_devmajor, sizeof(h.c_devmajor));
    format_hex(archive_entry_devminor(entry), &h.c_devminor, sizeof(h.c_devminor));
    if (archive_entry_ino64(entry) > 0xffffffff) {
        archive_set_error(&a->archive, ERANGE, "large inode number truncated");
        ret = ARCHIVE_WARN;
    }

    format_hex(archive_entry_ino64(entry) & 0xffffffff, &h.c_ino, sizeof(h.c_ino));
    format_hex(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
    format_hex(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
    format_hex(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
    format_hex(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
    if (archive_entry_filetype(entry) == AE_IFBLK
        || archive_entry_filetype(entry) == AE_IFCHR) {
        format_hex(archive_entry_rdevmajor(entry), &h.c_rdevmajor, sizeof(h.c_rdevmajor));
        format_hex(archive_entry_rdevminor(entry), &h.c_rdevminor, sizeof(h.c_rdevminor));
    } else {
        format_hex(0, &h.c_rdevmajor, sizeof(h.c_rdevmajor));
        format_hex(0, &h.c_rdevminor, sizeof(h.c_rdevminor));
    }
    format_hex(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
    format_hex(pathlength, &h.c_namesize, sizeof(h.c_namesize));
    format_hex(0, &h.c_checksum, sizeof(h.c_checksum));

    /* Non-regular files don't store bodies. */
    if (archive_entry_filetype(entry) != AE_IFREG)
        archive_entry_set_size(entry, 0);

    /* Symlinks get the link written as the body of the entry. */
    p = archive_entry_symlink(entry);
    if (p != NULL  &&  *p != '\0')
        format_hex(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
    else
        format_hex(archive_entry_size(entry),
            &h.c_filesize, sizeof(h.c_filesize));

    ret = (a->compressor.write)(a, &h, sizeof(h));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);

    /* Pad pathname to even length. */
    ret = (a->compressor.write)(a, path, pathlength);
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    pad = PAD4(pathlength + sizeof(struct cpio_header_newc));
    if (pad)
        ret = (a->compressor.write)(a, "\0\0\0", pad);
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);

    cpio->entry_bytes_remaining = archive_entry_size(entry);
    cpio->padding = PAD4(cpio->entry_bytes_remaining);

    /* Write the symlink now. */
    if (p != NULL  &&  *p != '\0') {
        ret = (a->compressor.write)(a, p, strlen(p));
        if (ret != ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        pad = PAD4(strlen(p));
        ret = (a->compressor.write)(a, "\0\0\0", pad);
    }

    return (ret);
}

static ssize_t
archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
{
    struct cpio *cpio;
    int ret;

    cpio = (struct cpio *)a->format_data;
    if (s > cpio->entry_bytes_remaining)
        s = cpio->entry_bytes_remaining;

    ret = (a->compressor.write)(a, buff, s);
    cpio->entry_bytes_remaining -= s;
    if (ret >= 0)
        return (s);
    else
        return (ret);
}

/*
 * Format a number into the specified field.
 */
static int
format_hex(int64_t v, void *p, int digits)
{
    int64_t max;
    int ret;

    max = (((int64_t)1) << (digits * 4)) - 1;
    if (v >= 0  &&  v <= max) {
        format_hex_recursive(v, (char *)p, digits);
        ret = 0;
    } else {
        format_hex_recursive(max, (char *)p, digits);
        ret = -1;
    }
    return (ret);
}

static int64_t
format_hex_recursive(int64_t v, char *p, int s)
{
    if (s == 0)
        return (v);
    v = format_hex_recursive(v, p+1, s-1);
    *p = "0123456789abcdef"[v & 0xf];
    return (v >>= 4);
}

static int
archive_write_newc_finish(struct archive_write *a)
{
    struct cpio *cpio;
    int er;
    struct archive_entry *trailer;

    cpio = (struct cpio *)a->format_data;
    trailer = archive_entry_new();
    archive_entry_set_nlink(trailer, 1);
    archive_entry_set_pathname(trailer, "TRAILER!!!");
    er = archive_write_newc_header(a, trailer);
    archive_entry_free(trailer);
    return (er);
}

static int
archive_write_newc_destroy(struct archive_write *a)
{
    struct cpio *cpio;

    cpio = (struct cpio *)a->format_data;
    free(cpio);
    a->format_data = NULL;
    return (ARCHIVE_OK);
}

static int
archive_write_newc_finish_entry(struct archive_write *a)
{
    struct cpio *cpio;
    int to_write, ret;

    cpio = (struct cpio *)a->format_data;
    ret = ARCHIVE_OK;
    while (cpio->entry_bytes_remaining > 0) {
        to_write = cpio->entry_bytes_remaining < a->null_length ?
            cpio->entry_bytes_remaining : a->null_length;
        ret = (a->compressor.write)(a, a->nulls, to_write);
        if (ret != ARCHIVE_OK)
            return (ret);
        cpio->entry_bytes_remaining -= to_write;
    }
    ret = (a->compressor.write)(a, a->nulls, cpio->padding);
    return (ret);
}

--- NEW FILE: archive_windows.h ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * 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
 *    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$
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

/*
 * TODO: A lot of stuff in here isn't actually used by libarchive and
 * can be trimmed out.  Note that this file is used by libarchive and
 * libarchive_test but nowhere else.  (But note that it gets compiled
 * with many different Windows environments, including MinGW, Visual
 * Studio, and Cygwin.  Significant changes should be tested in all three.)
 */

/*
 * TODO: Don't use off_t in here.  Use __int64 instead.  Note that
 * Visual Studio and the Windows SDK define off_t as 32 bits; Win32's
 * more modern file handling APIs all use __int64 instead of off_t.
 */

#ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
#define LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED

/* Start of configuration for native Win32  */

#include <errno.h>
#define set_errno(val)  ((errno)=val)
#include <io.h>
#include <stdlib.h>   //brings in NULL
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <process.h>
#include <direct.h>
#include <windows.h>
//#define   EFTYPE 7

#if !defined(STDIN_FILENO)
#define STDIN_FILENO 0
#endif

#if !defined(STDOUT_FILENO)
#define STDOUT_FILENO 1
#endif

#if !defined(STDERR_FILENO)
#define STDERR_FILENO 2
#endif


#if defined(_MSC_VER)
/* TODO: Fix the code, don't suppress the warnings. */
#pragma warning(disable:4244)   /* 'conversion' conversion from 'type1' to 'type2', possible loss of data */
#pragma warning(default: 4365)  /* 'action':conversion from 'type_1' to 'type_2', signed/unsigned mismatch */
#endif

#ifndef NULL
#ifdef  __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

/* Replacement for major/minor/makedev. */
#define major(x) ((int)(0x00ff & ((x) >> 8)))
#define minor(x) ((int)(0xffff00ff & (x)))
#define makedev(maj,min) ((0xff00 & ((maj)<<8))|(0xffff00ff & (min)))

/* Alias the Windows _function to the POSIX equivalent. */
#define access      _access
#define chdir       __la_chdir
#define chmod       __la_chmod
#define close       _close
#define fcntl       __la_fcntl
#ifndef fileno
#define fileno      _fileno
#endif
#define fstat       __la_fstat
#define ftruncate   __la_ftruncate
#define futimes     __la_futimes
#define getcwd      _getcwd
#define link        __la_link
#define lseek       __la_lseek
#define lstat       __la_stat
#define mbstowcs    __la_mbstowcs
#define mkdir(d,m)  __la_mkdir(d, m)
#define mktemp      _mktemp
#define open        __la_open
#define read        __la_read
#define rmdir       __la_rmdir
#define stat(path,stref)        __la_stat(path,stref)
#define strdup      _strdup
#define tzset       _tzset
#define umask       _umask
#define unlink      __la_unlink
#define utimes      __la_utimes
#define waitpid     __la_waitpid
#define write       __la_write

#define O_RDONLY    _O_RDONLY
#define O_WRONLY    _O_WRONLY
#define O_TRUNC     _O_TRUNC
#define O_CREAT     _O_CREAT
#define O_EXCL      _O_EXCL

#ifndef _S_IFIFO
  #define   _S_IFIFO        0010000   /* pipe */
#endif
#ifndef _S_IFCHR
  #define   _S_IFCHR        0020000   /* character special */
#endif
#ifndef _S_IFDIR
  #define   _S_IFDIR        0040000   /* directory */
#endif
#ifndef _S_IFBLK
  #define   _S_IFBLK        0060000   /* block special */
#endif
#ifndef _S_IFLNK
  #define   _S_IFLNK        0120000   /* symbolic link */
#endif
#ifndef _S_IFSOCK
  #define   _S_IFSOCK       0140000   /* socket */
#endif
#ifndef _S_IFREG
  #define   _S_IFREG        0100000   /* regular */
#endif
#ifndef _S_IFMT
  #define   _S_IFMT         0170000   /* file type mask */
#endif

#define S_IFIFO     _S_IFIFO
//#define   S_IFCHR  _S_IFCHR
//#define   S_IFDIR  _S_IFDIR
#define S_IFBLK     _S_IFBLK
#define S_IFLNK     _S_IFLNK
#define S_IFSOCK    _S_IFSOCK
//#define   S_IFREG  _S_IFREG
//#define   S_IFMT   _S_IFMT

#define S_ISBLK(m)  (((m) & S_IFMT) == S_IFBLK) /* block special */
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */
#define S_ISCHR(m)  (((m) & S_IFMT) == S_IFCHR) /* char special */
#define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR) /* directory */
#define S_ISREG(m)  (((m) & S_IFMT) == S_IFREG) /* regular file */
#define S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */

#define _S_ISUID        0004000   /* set user id on execution */
#define _S_ISGID        0002000   /* set group id on execution */
#define _S_ISVTX        0001000   /* save swapped text even after use */

#define S_ISUID        _S_ISUID
#define S_ISGID        _S_ISGID
#define S_ISVTX        _S_ISVTX

#define _S_IRWXU         (_S_IREAD | _S_IWRITE | _S_IEXEC)
#define _S_IXUSR         _S_IEXEC  /* read permission, user */
#define _S_IWUSR         _S_IWRITE /* write permission, user */
#define _S_IRUSR         _S_IREAD  /* execute/search permission, user */
#define _S_IRWXG        (_S_IRWXU >> 3)
#define _S_IXGRP        (_S_IXUSR >> 3) /* read permission, group */
#define _S_IWGRP        (_S_IWUSR >> 3) /* write permission, group */
#define _S_IRGRP        (_S_IRUSR >> 3) /* execute/search permission, group */
#define _S_IRWXO        (_S_IRWXG >> 3) 
#define _S_IXOTH        (_S_IXGRP >> 3) /* read permission, other */
#define _S_IWOTH        (_S_IWGRP >> 3) /* write permission, other */
#define _S_IROTH        (_S_IRGRP  >> 3) /* execute/search permission, other */

#define S_IRWXU      _S_IRWXU
#define S_IXUSR      _S_IXUSR
#define S_IWUSR      _S_IWUSR
#define S_IRUSR      _S_IRUSR
#define S_IRWXG        _S_IRWXG
#define S_IXGRP        _S_IXGRP
#define S_IWGRP        _S_IWGRP
#define S_IRGRP        _S_IRGRP
#define S_IRWXO        _S_IRWXO
#define S_IXOTH        _S_IXOTH
#define S_IWOTH        _S_IWOTH
#define S_IROTH        _S_IROTH

#define F_DUPFD     0   /* Duplicate file descriptor.  */
#define F_GETFD     1   /* Get file descriptor flags.  */
#define F_SETFD     2   /* Set file descriptor flags.  */
#define F_GETFL     3   /* Get file status flags.  */
#define F_SETFL     4   /* Set file status flags.  */
#define F_GETOWN        5   /* Get owner (receiver of SIGIO).  */
#define F_SETOWN        6   /* Set owner (receiver of SIGIO).  */
#define F_GETLK     7   /* Get record locking info.  */
#define F_SETLK     8   /* Set record locking info (non-blocking).  */
#define F_SETLKW        9   /* Set record locking info (blocking).  */

/* XXX missing */
#define F_GETLK64   7   /* Get record locking info.  */
#define F_SETLK64   8   /* Set record locking info (non-blocking).  */
#define F_SETLKW64  9   /* Set record locking info (blocking).  */

/* File descriptor flags used with F_GETFD and F_SETFD.  */
#define FD_CLOEXEC  1   /* Close on exec.  */

//NOT SURE IF O_NONBLOCK is OK here but at least the 0x0004 flag is not used by anything else...
#define O_NONBLOCK 0x0004 /* Non-blocking I/O.  */
//#define   O_NDELAY   O_NONBLOCK

/* Symbolic constants for the access() function */
#if !defined(F_OK)
    #define R_OK    4       /*  Test for read permission    */
    #define W_OK    2       /*  Test for write permission   */
    #define X_OK    1       /*  Test for execute permission */
    #define F_OK    0       /*  Test for existence of file  */
#endif


#ifdef _LARGEFILE_SOURCE
# define __USE_LARGEFILE 1      /* declare fseeko and ftello */
#endif

#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
# define __USE_FILE_OFFSET64  1 /* replace 32-bit functions by 64-bit ones */
#endif

#if __USE_LARGEFILE && __USE_FILE_OFFSET64
/* replace stat and seek by their large-file equivalents */
#undef  stat
#define stat        _stati64

#undef  lseek
#define lseek       _lseeki64
#define lseek64     _lseeki64
#define tell        _telli64
#define tell64      _telli64

#ifdef __MINGW32__
# define fseek      fseeko64
# define fseeko     fseeko64
# define ftell      ftello64
# define ftello     ftello64
# define ftell64    ftello64
#endif /* __MINGW32__ */
#endif /* LARGE_FILES */

#ifdef USE_WINSOCK_TIMEVAL
/* Winsock timeval has long size tv_sec. */
#define __timeval timeval
#else
struct _timeval64i32 {
    time_t      tv_sec;
    long        tv_usec;
};
#define __timeval _timeval64i32
#endif

typedef int pid_t;

/* Message digest define */
#if !defined(HAVE_OPENSSL_MD5_H) && !defined(HAVE_OPENSSL_SHA_H)
#include <wincrypt.h>
typedef struct {
    int     valid;
    HCRYPTPROV cryptProv;
    HCRYPTHASH hash;
} Digest_CTX;
#endif

#if !defined(HAVE_OPENSSL_MD5_H) && defined(CALG_MD5)
#define MD5_DIGEST_LENGTH   16
#define HAVE_MD5 1
#define MD5_CTX Digest_CTX
#endif
#ifndef HAVE_OPENSSL_SHA_H
#ifdef CALG_SHA1
#define SHA1_DIGEST_LENGTH  20
#define HAVE_SHA1 1
#define SHA1_CTX Digest_CTX
#endif
#ifdef CALG_SHA256
#define SHA256_DIGEST_LENGTH    32
#define HAVE_SHA256 1
#define SHA256_CTX Digest_CTX
#endif
#ifdef CALG_SHA384
#define SHA384_DIGEST_LENGTH    48
#define HAVE_SHA384 1
#define SHA384_CTX Digest_CTX
#endif
#ifdef CALG_SHA512
#define SHA512_DIGEST_LENGTH    64
#define HAVE_SHA512 1
#define SHA512_CTX Digest_CTX
#endif
#endif /* HAVE_OPENSSL_SHA_H */

/* End of Win32 definitions. */

/* Tell libarchive code that we have simulations for these. */
#ifndef HAVE_FTRUNCATE
#define HAVE_FTRUNCATE 1
#endif
#ifndef HAVE_FUTIMES
#define HAVE_FUTIMES 1
#endif
#ifndef HAVE_UTIMES
#define HAVE_UTIMES 1
#endif
#ifndef HAVE_LINK
#define HAVE_LINK 1
#endif

/* Replacement POSIX function */
extern int   __la_chdir(const char *path);
extern int   __la_chmod(const char *path, mode_t mode);
extern int   __la_fcntl(int fd, int cmd, int val);
extern int   __la_fstat(int fd, struct stat *st);
extern int   __la_ftruncate(int fd, off_t length);
extern int   __la_futimes(int fd, const struct __timeval *times);
extern int   __la_link(const char *src, const char *dst);
extern __int64   __la_lseek(int fd, __int64 offset, int whence);
extern size_t    __la_mbstowcs(wchar_t *wcstr, const char *mbstr, size_t nwchars);
extern int   __la_mkdir(const char *path, mode_t mode);
extern int   __la_open(const char *path, int flags, ...);
extern ssize_t   __la_read(int fd, void *buf, size_t nbytes);
extern int   __la_rmdir(const char *path);
extern int   __la_stat(const char *path, struct stat *st);
extern int   __la_unlink(const char *path);
extern int   __la_utimes(const char *name, const struct __timeval *times);
extern pid_t     __la_waitpid(pid_t wpid, int *status, int option);
extern ssize_t   __la_write(int fd, const void *buf, size_t nbytes);

#define _stat64i32(path, st)    __la_stat(path, st)
#define _stat64(path, st)   __la_stat(path, st)
/* for status returned by la_waitpid */
#define WIFSIGNALED(sts)    0
#define WTERMSIG(sts)       0
#define WIFEXITED(sts)      ((sts & 0x100) == 0)
#define WEXITSTATUS(sts)    (sts & 0x0FF)

/* Message digest function */
#if !defined(HAVE_OPENSSL_MD5_H) && !defined(HAVE_OPENSSL_SHA_H)
#ifdef MD5_DIGEST_LENGTH
extern void  MD5_Init(Digest_CTX *ctx);
extern void  MD5_Update(Digest_CTX *ctx, const unsigned char *buf,
             size_t len);
extern void  MD5_Final(unsigned char buf[MD5_DIGEST_LENGTH],
             Digest_CTX *ctx);
#endif
#ifdef SHA1_DIGEST_LENGTH
extern void  SHA1_Init(Digest_CTX *ctx);
extern void  SHA1_Update(Digest_CTX *ctx, const unsigned char *buf,
             size_t len);
extern void  SHA1_Final(unsigned char buf[SHA1_DIGEST_LENGTH],
             Digest_CTX *ctx);
#endif
#ifdef SHA256_DIGEST_LENGTH
extern void  SHA256_Init(Digest_CTX *ctx);
extern void  SHA256_Update(Digest_CTX *ctx, const unsigned char *buf,
             size_t len);
extern void  SHA256_Final(unsigned char buf[SHA256_DIGEST_LENGTH],
             Digest_CTX *ctx);
#endif
#ifdef SHA384_DIGEST_LENGTH
extern void  SHA384_Init(Digest_CTX *ctx);
extern void  SHA384_Update(Digest_CTX *ctx, const unsigned char *buf,
             size_t len);
extern void  SHA384_Final(unsigned char buf[SHA384_DIGEST_LENGTH],
             Digest_CTX *ctx);
#endif
#ifdef SHA512_DIGEST_LENGTH
extern void  SHA512_Init(Digest_CTX *ctx);
extern void  SHA512_Update(Digest_CTX *ctx, const unsigned char *buf,
             size_t len);
extern void  SHA512_Final(unsigned char buf[SHA512_DIGEST_LENGTH],
             Digest_CTX *ctx);
#endif
#endif

#endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */

--- NEW FILE: Makefile ---
# $FreeBSD: src/lib/libarchive/Makefile,v 1.88 2008/08/31 07:21:46 kientzle Exp $
.include <bsd.own.mk>

LIB=    archive
DPADD=  ${LIBBZ2} ${LIBZ}
LDADD=  -lbz2 -lz

# FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system.
# It has no real relation to the libarchive version number.
SHLIB_MAJOR= 5

CFLAGS+=    -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
CFLAGS+=    -I${.OBJDIR}
#Uncomment to build with full lzma/xz support via liblzma
#CFLAGS+= -I/usr/local/include -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1
#LDADD+= -L/usr/local/lib -llzma

.if ${MK_OPENSSL} != "no"
CFLAGS+=    -DWITH_OPENSSL
.endif


WARNS?= 6

# Headers to be installed in /usr/include
INCS=   archive.h archive_entry.h

# Sources to be compiled.
SRCS=   archive_check_magic.c               \
    archive_entry.c                 \
    archive_entry_copy_stat.c           \
    archive_entry_stat.c                \
    archive_entry_strmode.c             \
    archive_entry_link_resolver.c           \
    archive_entry_xattr.c               \
    archive_read.c                  \
    archive_read_data_into_fd.c         \
    archive_read_disk.c             \
    archive_read_disk_entry_from_file.c     \
    archive_read_disk_set_standard_lookup.c     \
    archive_read_extract.c              \
    archive_read_open_fd.c              \
    archive_read_open_file.c            \
    archive_read_open_filename.c            \
    archive_read_open_memory.c          \
    archive_read_support_compression_all.c      \
    archive_read_support_compression_bzip2.c    \
    archive_read_support_compression_compress.c \
    archive_read_support_compression_gzip.c     \
    archive_read_support_compression_none.c     \
    archive_read_support_compression_program.c  \
    archive_read_support_compression_xz.c       \
    archive_read_support_format_all.c       \
    archive_read_support_format_ar.c        \
    archive_read_support_format_cpio.c      \
    archive_read_support_format_empty.c     \
    archive_read_support_format_iso9660.c       \
    archive_read_support_format_mtree.c     \
    archive_read_support_format_raw.c       \
    archive_read_support_format_tar.c       \
    archive_read_support_format_zip.c       \
    archive_string.c                \
    archive_string_sprintf.c            \
    archive_util.c                  \
    archive_virtual.c               \
    archive_write.c                 \
    archive_write_disk.c                \
    archive_write_disk_set_standard_lookup.c    \
    archive_write_open_fd.c             \
    archive_write_open_file.c           \
    archive_write_open_filename.c           \
    archive_write_open_memory.c         \
    archive_write_set_compression_bzip2.c       \
    archive_write_set_compression_compress.c    \
    archive_write_set_compression_gzip.c        \
    archive_write_set_compression_none.c        \
    archive_write_set_compression_program.c     \
    archive_write_set_compression_xz.c      \
    archive_write_set_format.c          \
    archive_write_set_format_ar.c           \
    archive_write_set_format_by_name.c      \
    archive_write_set_format_cpio.c         \
    archive_write_set_format_cpio_newc.c        \
    archive_write_set_format_mtree.c        \
    archive_write_set_format_pax.c          \
    archive_write_set_format_shar.c         \
    archive_write_set_format_ustar.c        \
    archive_write_set_format_zip.c          \
    filter_fork.c

# Man pages to be installed.
MAN=    archive_entry.3                 \
    archive_read.3                  \
    archive_read_disk.3             \
    archive_util.3                  \
    archive_write.3                 \
    archive_write_disk.3                \
    cpio.5                      \
    libarchive.3                    \
    libarchive-formats.5                \
    tar.5

# Symlink the man pages under each function name.
MLINKS+=    archive_entry.3 archive_entry_acl_add_entry.3
MLINKS+=    archive_entry.3 archive_entry_acl_add_entry_w.3
MLINKS+=    archive_entry.3 archive_entry_acl_clear.3
MLINKS+=    archive_entry.3 archive_entry_acl_count.3
MLINKS+=    archive_entry.3 archive_entry_acl_next.3
MLINKS+=    archive_entry.3 archive_entry_acl_next_w.3
MLINKS+=    archive_entry.3 archive_entry_acl_reset.3
MLINKS+=    archive_entry.3 archive_entry_acl_text_w.3
MLINKS+=    archive_entry.3 archive_entry_clear.3
MLINKS+=    archive_entry.3 archive_entry_clone.3
MLINKS+=    archive_entry.3 archive_entry_copy_fflags_text.3
MLINKS+=    archive_entry.3 archive_entry_copy_fflags_text_w.3
MLINKS+=    archive_entry.3 archive_entry_copy_gname.3
MLINKS+=    archive_entry.3 archive_entry_copy_gname_w.3
MLINKS+=    archive_entry.3 archive_entry_copy_hardlink_w.3
MLINKS+=    archive_entry.3 archive_entry_copy_link.3
MLINKS+=    archive_entry.3 archive_entry_copy_link_w.3
MLINKS+=    archive_entry.3 archive_entry_copy_pathname_w.3
MLINKS+=    archive_entry.3 archive_entry_copy_stat.3
MLINKS+=    archive_entry.3 archive_entry_copy_symlink_w.3
MLINKS+=    archive_entry.3 archive_entry_copy_uname.3
MLINKS+=    archive_entry.3 archive_entry_copy_uname_w.3
MLINKS+=    archive_entry.3 archive_entry_dev.3
MLINKS+=    archive_entry.3 archive_entry_devmajor.3
MLINKS+=    archive_entry.3 archive_entry_devminor.3
MLINKS+=    archive_entry.3 archive_entry_filetype.3
MLINKS+=    archive_entry.3 archive_entry_fflags.3
MLINKS+=    archive_entry.3 archive_entry_fflags_text.3
MLINKS+=    archive_entry.3 archive_entry_free.3
MLINKS+=    archive_entry.3 archive_entry_gid.3
MLINKS+=    archive_entry.3 archive_entry_gname.3
MLINKS+=    archive_entry.3 archive_entry_gname_w.3
MLINKS+=    archive_entry.3 archive_entry_hardlink.3
MLINKS+=    archive_entry.3 archive_entry_ino.3
MLINKS+=    archive_entry.3 archive_entry_mode.3
MLINKS+=    archive_entry.3 archive_entry_mtime.3
MLINKS+=    archive_entry.3 archive_entry_mtime_nsec.3
MLINKS+=    archive_entry.3 archive_entry_nlink.3
MLINKS+=    archive_entry.3 archive_entry_new.3
MLINKS+=    archive_entry.3 archive_entry_pathname.3
MLINKS+=    archive_entry.3 archive_entry_pathname_w.3
MLINKS+=    archive_entry.3 archive_entry_rdev.3
MLINKS+=    archive_entry.3 archive_entry_rdevmajor.3
MLINKS+=    archive_entry.3 archive_entry_rdevminor.3
MLINKS+=    archive_entry.3 archive_entry_set_atime.3
MLINKS+=    archive_entry.3 archive_entry_set_ctime.3
MLINKS+=    archive_entry.3 archive_entry_set_dev.3
MLINKS+=    archive_entry.3 archive_entry_set_devmajor.3
MLINKS+=    archive_entry.3 archive_entry_set_devminor.3
MLINKS+=    archive_entry.3 archive_entry_set_fflags.3
MLINKS+=    archive_entry.3 archive_entry_set_gid.3
MLINKS+=    archive_entry.3 archive_entry_set_gname.3
MLINKS+=    archive_entry.3 archive_entry_set_hardlink.3
MLINKS+=    archive_entry.3 archive_entry_set_link.3
MLINKS+=    archive_entry.3 archive_entry_set_mode.3
MLINKS+=    archive_entry.3 archive_entry_set_mtime.3
MLINKS+=    archive_entry.3 archive_entry_set_nlink.3
MLINKS+=    archive_entry.3 archive_entry_set_pathname.3
MLINKS+=    archive_entry.3 archive_entry_set_rdev.3
MLINKS+=    archive_entry.3 archive_entry_set_rdevmajor.3
MLINKS+=    archive_entry.3 archive_entry_set_rdevminor.3
MLINKS+=    archive_entry.3 archive_entry_set_size.3
MLINKS+=    archive_entry.3 archive_entry_set_symlink.3
MLINKS+=    archive_entry.3 archive_entry_set_uid.3
MLINKS+=    archive_entry.3 archive_entry_set_uname.3
MLINKS+=    archive_entry.3 archive_entry_size.3
MLINKS+=    archive_entry.3 archive_entry_stat.3
MLINKS+=    archive_entry.3 archive_entry_symlink.3
MLINKS+=    archive_entry.3 archive_entry_uid.3
MLINKS+=    archive_entry.3 archive_entry_uname.3
MLINKS+=    archive_entry.3 archive_entry_uname_w.3
MLINKS+=    archive_read.3 archive_read_data.3
MLINKS+=    archive_read.3 archive_read_data_block.3
MLINKS+=    archive_read.3 archive_read_data_into_buffer.3
MLINKS+=    archive_read.3 archive_read_data_into_fd.3
MLINKS+=    archive_read.3 archive_read_data_skip.3
MLINKS+=    archive_read.3 archive_read_extract.3
MLINKS+=    archive_read.3 archive_read_extract_set_progress_callback.3
MLINKS+=    archive_read.3 archive_read_extract_set_skip_file.3
MLINKS+=    archive_read.3 archive_read_finish.3
MLINKS+=    archive_read.3 archive_read_new.3
MLINKS+=    archive_read.3 archive_read_next_header.3
MLINKS+=    archive_read.3 archive_read_next_header2.3
MLINKS+=    archive_read.3 archive_read_open.3
MLINKS+=    archive_read.3 archive_read_open2.3
MLINKS+=    archive_read.3 archive_read_open_FILE.3
MLINKS+=    archive_read.3 archive_read_open_fd.3
MLINKS+=    archive_read.3 archive_read_open_file.3
MLINKS+=    archive_read.3 archive_read_open_filename.3
MLINKS+=    archive_read.3 archive_read_open_memory.3
MLINKS+=    archive_read.3 archive_read_support_compression_all.3
MLINKS+=    archive_read.3 archive_read_support_compression_bzip2.3
MLINKS+=    archive_read.3 archive_read_support_compression_compress.3
MLINKS+=    archive_read.3 archive_read_support_compression_gzip.3
MLINKS+=    archive_read.3 archive_read_support_compression_lzma.3
MLINKS+=    archive_read.3 archive_read_support_compression_none.3
MLINKS+=    archive_read.3 archive_read_support_compression_program.3
MLINKS+=    archive_read.3 archive_read_support_compression_program_signature.3
MLINKS+=    archive_read.3 archive_read_support_compression_xz.3
MLINKS+=    archive_read.3 archive_read_support_format_all.3
MLINKS+=    archive_read.3 archive_read_support_format_ar.3
MLINKS+=    archive_read.3 archive_read_support_format_cpio.3
MLINKS+=    archive_read.3 archive_read_support_format_empty.3
MLINKS+=    archive_read.3 archive_read_support_format_iso9660.3
MLINKS+=    archive_read.3 archive_read_support_format_raw.3
MLINKS+=    archive_read.3 archive_read_support_format_tar.3
MLINKS+=    archive_read.3 archive_read_support_format_zip.3
MLINKS+=    archive_read_disk.3 archive_read_disk_entry_from_file.3
MLINKS+=    archive_read_disk.3 archive_read_disk_gname.3
MLINKS+=    archive_read_disk.3 archive_read_disk_new.3
MLINKS+=    archive_read_disk.3 archive_read_disk_set_gname_lookup.3
MLINKS+=    archive_read_disk.3 archive_read_disk_set_standard_lookup.3
MLINKS+=    archive_read_disk.3 archive_read_disk_set_symlink_hybrid.3
MLINKS+=    archive_read_disk.3 archive_read_disk_set_symlink_logical.3
MLINKS+=    archive_read_disk.3 archive_read_disk_set_symlink_physical.3
MLINKS+=    archive_read_disk.3 archive_read_disk_set_uname_lookup.3
MLINKS+=    archive_read_disk.3 archive_read_disk_uname.3
MLINKS+=    archive_util.3 archive_clear_error.3
MLINKS+=    archive_util.3 archive_compression.3
MLINKS+=    archive_util.3 archive_compression_name.3
MLINKS+=    archive_util.3 archive_errno.3
MLINKS+=    archive_util.3 archive_error_string.3
MLINKS+=    archive_util.3 archive_file_count.3
MLINKS+=    archive_util.3 archive_format.3
MLINKS+=    archive_util.3 archive_format_name.3
MLINKS+=    archive_util.3 archive_set_error.3
MLINKS+=    archive_write.3 archive_write_close.3
MLINKS+=    archive_write.3 archive_write_data.3
MLINKS+=    archive_write.3 archive_write_finish.3
MLINKS+=    archive_write.3 archive_write_finish_entry.3
MLINKS+=    archive_write.3 archive_write_get_bytes_in_last_block.3
MLINKS+=    archive_write.3 archive_write_get_bytes_per_block.3
MLINKS+=    archive_write.3 archive_write_header.3
MLINKS+=    archive_write.3 archive_write_new.3
MLINKS+=    archive_write.3 archive_write_open.3
MLINKS+=    archive_write.3 archive_write_open_FILE.3
MLINKS+=    archive_write.3 archive_write_open_fd.3
MLINKS+=    archive_write.3 archive_write_open_file.3
MLINKS+=    archive_write.3 archive_write_open_filename.3
MLINKS+=    archive_write.3 archive_write_open_memory.3
MLINKS+=    archive_write.3 archive_write_set_bytes_in_last_block.3
MLINKS+=    archive_write.3 archive_write_set_bytes_per_block.3
MLINKS+=    archive_write.3 archive_write_set_callbacks.3
MLINKS+=    archive_write.3 archive_write_set_compression_bzip2.3
MLINKS+=    archive_write.3 archive_write_set_compression_compress.3
MLINKS+=    archive_write.3 archive_write_set_compression_gzip.3
MLINKS+=    archive_write.3 archive_write_set_compression_none.3
MLINKS+=    archive_write.3 archive_write_set_compression_program.3
MLINKS+=    archive_write.3 archive_write_set_format_pax.3
MLINKS+=    archive_write.3 archive_write_set_format_shar.3
MLINKS+=    archive_write.3 archive_write_set_format_ustar.3
MLINKS+=    archive_write_disk.3 archive_write_disk_new.3
MLINKS+=    archive_write_disk.3 archive_write_disk_set_group_lookup.3
MLINKS+=    archive_write_disk.3 archive_write_disk_set_options.3
MLINKS+=    archive_write_disk.3 archive_write_disk_set_skip_file.3
MLINKS+=    archive_write_disk.3 archive_write_disk_set_standard_lookup.3
MLINKS+=    archive_write_disk.3 archive_write_disk_set_user_lookup.3
MLINKS+=    libarchive.3 archive.3

.PHONY: check test
check test:
    cd ${.CURDIR}/test && make test

.include <bsd.lib.mk>

--- NEW FILE: archive_write_set_format_pax.c ---
/*-
 * 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,
[...1354 lines suppressed...]
    /* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */
    switch (len) {
    case 0: break;
    case 1:
        v = (((int)s[0] << 16) & 0xff0000);
        *d++ = digits[(v >> 18) & 0x3f];
        *d++ = digits[(v >> 12) & 0x3f];
        break;
    case 2:
        v = (((int)s[0] << 16) & 0xff0000)
            | (((int)s[1] << 8) & 0xff00);
        *d++ = digits[(v >> 18) & 0x3f];
        *d++ = digits[(v >> 12) & 0x3f];
        *d++ = digits[(v >> 6) & 0x3f];
        break;
    }
    /* Add trailing NUL character so output is a valid C string. */
    *d++ = '\0';
    return (out);
}

--- NEW FILE: archive_write_disk.c ---
/*-
 * 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
[...2561 lines suppressed...]
    if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
        return (1);
#elif HAVE_STRUCT_STAT_ST_MTIME_N
    /* older. */
    if (st->st_mtime_n < archive_entry_mtime_nsec(entry))
        return (1);
#elif HAVE_STRUCT_STAT_ST_UMTIME
    /* older. */
    if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry))
        return (1);
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
    /* older. */
    if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry))
        return (1);
#else
    /* This system doesn't have high-res timestamps. */
#endif
    /* Same age or newer, so not older. */
    return (0);
}

--- NEW FILE: archive_write.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write.c,v 1.27 2008/03/14 23:09:02 kientzle Exp $");

/*
 * This file contains the "essential" portions of the write API, that
 * is, stuff that will essentially always be used by any client that
 * actually needs to write a archive.  Optional pieces have been, as
 * far as possible, separated out into separate files to reduce
 * needlessly bloating statically-linked clients.
 */

#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

static struct archive_vtable *archive_write_vtable(void);

static int  _archive_write_close(struct archive *);
static int  _archive_write_finish(struct archive *);
static int  _archive_write_header(struct archive *, struct archive_entry *);
static int  _archive_write_finish_entry(struct archive *);
static ssize_t  _archive_write_data(struct archive *, const void *, size_t);

static struct archive_vtable *
archive_write_vtable(void)
{
    static struct archive_vtable av;
    static int inited = 0;

    if (!inited) {
        av.archive_close = _archive_write_close;
        av.archive_finish = _archive_write_finish;
        av.archive_write_header = _archive_write_header;
        av.archive_write_finish_entry = _archive_write_finish_entry;
        av.archive_write_data = _archive_write_data;
    }
    return (&av);
}

/*
 * Allocate, initialize and return an archive object.
 */
struct archive *
archive_write_new(void)
{
    struct archive_write *a;
    unsigned char *nulls;

    a = (struct archive_write *)malloc(sizeof(*a));
    if (a == NULL)
        return (NULL);
    memset(a, 0, sizeof(*a));
    a->archive.magic = ARCHIVE_WRITE_MAGIC;
    a->archive.state = ARCHIVE_STATE_NEW;
    a->archive.vtable = archive_write_vtable();
    /*
     * The value 10240 here matches the traditional tar default,
     * but is otherwise arbitrary.
     * TODO: Set the default block size from the format selected.
     */
    a->bytes_per_block = 10240;
    a->bytes_in_last_block = -1;    /* Default */

    /* Initialize a block of nulls for padding purposes. */
    a->null_length = 1024;
    nulls = (unsigned char *)malloc(a->null_length);
    if (nulls == NULL) {
        free(a);
        return (NULL);
    }
    memset(nulls, 0, a->null_length);
    a->nulls = nulls;
    /*
     * Set default compression, but don't set a default format.
     * Were we to set a default format here, we would force every
     * client to link in support for that format, even if they didn't
     * ever use it.
     */
    archive_write_set_compression_none(&a->archive);
    return (&a->archive);
}

/*
 * Set write options for the format. Returns 0 if successful.
 */
int
archive_write_set_format_options(struct archive *_a, const char *s)
{
    struct archive_write *a = (struct archive_write *)_a;
    char key[64], val[64];
    int len, r, ret = ARCHIVE_OK;

    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_format_options");
    archive_clear_error(&a->archive);

    if (s == NULL || *s == '\0')
        return (ARCHIVE_OK);
    if (a->format_options == NULL)
        /* This format does not support option. */
        return (ARCHIVE_OK);

    while ((len = __archive_parse_options(s, a->format_name,
        sizeof(key), key, sizeof(val), val)) > 0) {
        if (val[0] == '\0')
            r = a->format_options(a, key, NULL);
        else
            r = a->format_options(a, key, val);
        if (r == ARCHIVE_FATAL)
            return (r);
        if (r < ARCHIVE_OK) { /* This key was not handled. */
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Unsupported option ``%s''", key);
            ret = ARCHIVE_WARN;
        }
        s += len;
    }
    if (len < 0) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Malformed options string.");
        return (ARCHIVE_WARN);
    }
    return (ret);
}

/*
 * Set write options for the compressor. Returns 0 if successful.
 */
int
archive_write_set_compressor_options(struct archive *_a, const char *s)
{
    struct archive_write *a = (struct archive_write *)_a;
    char key[64], val[64];
    int len, r;
    int ret = ARCHIVE_OK;

    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
    archive_clear_error(&a->archive);

    if (s == NULL || *s == '\0')
        return (ARCHIVE_OK);
    if (a->compressor.options == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Unsupported option ``%s''", s);
        /* This compressor does not support option. */
        return (ARCHIVE_WARN);
    }

    while ((len = __archive_parse_options(s, a->archive.compression_name,
        sizeof(key), key, sizeof(val), val)) > 0) {
        if (val[0] == '\0')
            r = a->compressor.options(a, key, NULL);
        else
            r = a->compressor.options(a, key, val);
        if (r == ARCHIVE_FATAL)
            return (r);
        if (r < ARCHIVE_OK) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Unsupported option ``%s''", key);
            ret = ARCHIVE_WARN;
        }
        s += len;
    }
    if (len < 0) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Illegal format options.");
        return (ARCHIVE_WARN);
    }
    return (ret);
}

/*
 * Set write options for the format and the compressor. Returns 0 if successful.
 */
int
archive_write_set_options(struct archive *_a, const char *s)
{
    int r1, r2;

    r1 = archive_write_set_format_options(_a, s);
    if (r1 < ARCHIVE_WARN)
        return (r1);
    r2 = archive_write_set_compressor_options(_a, s);
    if (r2 < ARCHIVE_WARN)
        return (r2);
    if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN)
        return (ARCHIVE_WARN);
    return (ARCHIVE_OK);
}

/*
 * Set the block size.  Returns 0 if successful.
 */
int
archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
    a->bytes_per_block = bytes_per_block;
    return (ARCHIVE_OK);
}

/*
 * Get the current block size.  -1 if it has never been set.
 */
int
archive_write_get_bytes_per_block(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
    return (a->bytes_per_block);
}

/*
 * Set the size for the last block.
 * Returns 0 if successful.
 */
int
archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
    a->bytes_in_last_block = bytes;
    return (ARCHIVE_OK);
}

/*
 * Return the value set above.  -1 indicates it has not been set.
 */
int
archive_write_get_bytes_in_last_block(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
    return (a->bytes_in_last_block);
}


/*
 * dev/ino of a file to be rejected.  Used to prevent adding
 * an archive to itself recursively.
 */
int
archive_write_set_skip_file(struct archive *_a, dev_t d, ino_t i)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
    a->skip_file_dev = d;
    a->skip_file_ino = i;
    return (ARCHIVE_OK);
}


/*
 * Open the archive using the current settings.
 */
int
archive_write_open(struct archive *_a, void *client_data,
    archive_open_callback *opener, archive_write_callback *writer,
    archive_close_callback *closer)
{
    struct archive_write *a = (struct archive_write *)_a;
    int ret;

    ret = ARCHIVE_OK;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_open");
    archive_clear_error(&a->archive);
    a->archive.state = ARCHIVE_STATE_HEADER;
    a->client_data = client_data;
    a->client_writer = writer;
    a->client_opener = opener;
    a->client_closer = closer;
    ret = (a->compressor.init)(a);
    if (a->format_init && ret == ARCHIVE_OK)
        ret = (a->format_init)(a);
    return (ret);
}


/*
 * Close out the archive.
 *
 * Be careful: user might just call write_new and then write_finish.
 * Don't assume we actually wrote anything or performed any non-trivial
 * initialization.
 */
static int
_archive_write_close(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    int r = ARCHIVE_OK, r1 = ARCHIVE_OK;

    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_ANY, "archive_write_close");

    /* Finish the last entry. */
    if (a->archive.state & ARCHIVE_STATE_DATA)
        r = ((a->format_finish_entry)(a));

    /* Finish off the archive. */
    if (a->format_finish != NULL) {
        r1 = (a->format_finish)(a);
        if (r1 < r)
            r = r1;
    }

    /* Release format resources. */
    if (a->format_destroy != NULL) {
        r1 = (a->format_destroy)(a);
        if (r1 < r)
            r = r1;
    }

    /* Finish the compression and close the stream. */
    if (a->compressor.finish != NULL) {
        r1 = (a->compressor.finish)(a);
        if (r1 < r)
            r = r1;
    }

    /* Close out the client stream. */
    if (a->client_closer != NULL) {
        r1 = (a->client_closer)(&a->archive, a->client_data);
        if (r1 < r)
            r = r1;
    }

    a->archive.state = ARCHIVE_STATE_CLOSED;
    return (r);
}

/*
 * Destroy the archive structure.
 */
static int
_archive_write_finish(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    int r = ARCHIVE_OK;

    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_ANY, "archive_write_finish");
    if (a->archive.state != ARCHIVE_STATE_CLOSED)
        r = archive_write_close(&a->archive);

    /* Release various dynamic buffers. */
    free((void *)(uintptr_t)(const void *)a->nulls);
    archive_string_free(&a->archive.error_string);
    a->archive.magic = 0;
    free(a);
    return (r);
}

/*
 * Write the appropriate header.
 */
static int
_archive_write_header(struct archive *_a, struct archive_entry *entry)
{
    struct archive_write *a = (struct archive_write *)_a;
    int ret, r2;

    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
    archive_clear_error(&a->archive);

    /* In particular, "retry" and "fatal" get returned immediately. */
    ret = archive_write_finish_entry(&a->archive);
    if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
        return (ret);

    if (a->skip_file_dev != 0 &&
        archive_entry_dev(entry) == a->skip_file_dev &&
        a->skip_file_ino != 0 &&
        archive_entry_ino64(entry) == a->skip_file_ino) {
        archive_set_error(&a->archive, 0,
            "Can't add archive to itself");
        return (ARCHIVE_FAILED);
    }

    /* Format and write header. */
    r2 = ((a->format_write_header)(a, entry));
    if (r2 < ret)
        ret = r2;

    a->archive.state = ARCHIVE_STATE_DATA;
    return (ret);
}

static int
_archive_write_finish_entry(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    int ret = ARCHIVE_OK;

    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
        "archive_write_finish_entry");
    if (a->archive.state & ARCHIVE_STATE_DATA)
        ret = (a->format_finish_entry)(a);
    a->archive.state = ARCHIVE_STATE_HEADER;
    return (ret);
}

/*
 * Note that the compressor is responsible for blocking.
 */
static ssize_t
_archive_write_data(struct archive *_a, const void *buff, size_t s)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_DATA, "archive_write_data");
    archive_clear_error(&a->archive);
    return ((a->format_write_data)(a, buff, s));
}

--- NEW FILE: archive_write_set_compression_program.c ---
/*-
 * Copyright (c) 2007 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.3 2008/06/15 10:45:57 kientzle Exp $");

/* This capability is only available on POSIX systems. */
#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
    !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))
#include "archive.h"

/*
 * On non-Posix systems, allow the program to build, but choke if
 * this function is actually invoked.
 */
int
archive_write_set_compression_program(struct archive *_a, const char *cmd)
{
    archive_set_error(_a, -1,
        "External compression programs not supported on this platform");
    return (ARCHIVE_FATAL);
}

#else

#ifdef HAVE_SYS_WAIT_H
#  include <sys/wait.h>
#endif
#ifdef HAVE_ERRNO_H
#  include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#  include <fcntl.h>
#endif
#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#  include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"

#include "filter_fork.h"

struct private_data {
    char        *description;
    pid_t        child;
    int      child_stdin, child_stdout;

    char        *child_buf;
    size_t       child_buf_len, child_buf_avail;
};

static int  archive_compressor_program_finish(struct archive_write *);
static int  archive_compressor_program_init(struct archive_write *);
static int  archive_compressor_program_write(struct archive_write *,
            const void *, size_t);

/*
 * Allocate, initialize and return a archive object.
 */
int
archive_write_set_compression_program(struct archive *_a, const char *cmd)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compression_program");
    a->compressor.init = &archive_compressor_program_init;
    a->compressor.config = strdup(cmd);
    return (ARCHIVE_OK);
}

/*
 * Setup callback.
 */
static int
archive_compressor_program_init(struct archive_write *a)
{
    int ret;
    struct private_data *state;
    static const char *prefix = "Program: ";
    char *cmd = a->compressor.config;

    if (a->client_opener != NULL) {
        ret = (a->client_opener)(&a->archive, a->client_data);
        if (ret != ARCHIVE_OK)
            return (ret);
    }

    state = (struct private_data *)malloc(sizeof(*state));
    if (state == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression");
        return (ARCHIVE_FATAL);
    }
    memset(state, 0, sizeof(*state));

    a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
    state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
    strcpy(state->description, prefix);
    strcat(state->description, cmd);
    a->archive.compression_name = state->description;

    state->child_buf_len = a->bytes_per_block;
    state->child_buf_avail = 0;
    state->child_buf = malloc(state->child_buf_len);

    if (state->child_buf == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression buffer");
        free(state);
        return (ARCHIVE_FATAL);
    }

    if ((state->child = __archive_create_child(cmd,
         &state->child_stdin, &state->child_stdout)) == -1) {
        archive_set_error(&a->archive, EINVAL,
            "Can't initialise filter");
        free(state->child_buf);
        free(state);
        return (ARCHIVE_FATAL);
    }

    a->compressor.write = archive_compressor_program_write;
    a->compressor.finish = archive_compressor_program_finish;

    a->compressor.data = state;
    return (0);
}

static ssize_t
child_write(struct archive_write *a, const char *buf, size_t buf_len)
{
    struct private_data *state = a->compressor.data;
    ssize_t ret;

    if (state->child_stdin == -1)
        return (-1);

    if (buf_len == 0)
        return (-1);

restart_write:
    do {
        ret = write(state->child_stdin, buf, buf_len);
    } while (ret == -1 && errno == EINTR);

    if (ret > 0)
        return (ret);
    if (ret == 0) {
        close(state->child_stdin);
        state->child_stdin = -1;
        fcntl(state->child_stdout, F_SETFL, 0);
        return (0);
    }
    if (ret == -1 && errno != EAGAIN)
        return (-1);

    if (state->child_stdout == -1) {
        fcntl(state->child_stdin, F_SETFL, 0);
        __archive_check_child(state->child_stdin, state->child_stdout);
        goto restart_write;
    }

    do {
        ret = read(state->child_stdout,
            state->child_buf + state->child_buf_avail,
            state->child_buf_len - state->child_buf_avail);
    } while (ret == -1 && errno == EINTR);

    if (ret == 0 || (ret == -1 && errno == EPIPE)) {
        close(state->child_stdout);
        state->child_stdout = -1;
        fcntl(state->child_stdin, F_SETFL, 0);
        goto restart_write;
    }
    if (ret == -1 && errno == EAGAIN) {
        __archive_check_child(state->child_stdin, state->child_stdout);
        goto restart_write;
    }
    if (ret == -1)
        return (-1);

    state->child_buf_avail += ret;

    ret = (a->client_writer)(&a->archive, a->client_data,
        state->child_buf, state->child_buf_avail);
    if (ret <= 0)
        return (-1);

    if ((size_t)ret < state->child_buf_avail) {
        memmove(state->child_buf, state->child_buf + ret,
            state->child_buf_avail - ret);
    }
    state->child_buf_avail -= ret;
    a->archive.raw_position += ret;
    goto restart_write;
}

/*
 * Write data to the compressed stream.
 */
static int
archive_compressor_program_write(struct archive_write *a, const void *buff,
    size_t length)
{
    struct private_data *state;
    ssize_t ret;
    const char *buf;

    state = (struct private_data *)a->compressor.data;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    buf = buff;
    while (length > 0) {
        ret = child_write(a, buf, length);
        if (ret == -1 || ret == 0) {
            archive_set_error(&a->archive, EIO,
                "Can't write to filter");
            return (ARCHIVE_FATAL);
        }
        length -= ret;
        buf += ret;
    }

    a->archive.file_position += length;
    return (ARCHIVE_OK);
}


/*
 * Finish the compression...
 */
static int
archive_compressor_program_finish(struct archive_write *a)
{
    int ret, status;
    ssize_t bytes_read, bytes_written;
    struct private_data *state;

    state = (struct private_data *)a->compressor.data;
    ret = 0;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        ret = ARCHIVE_FATAL;
        goto cleanup;
    }

    /* XXX pad compressed data. */

    close(state->child_stdin);
    state->child_stdin = -1;
    fcntl(state->child_stdout, F_SETFL, 0);

    for (;;) {
        do {
            bytes_read = read(state->child_stdout,
                state->child_buf + state->child_buf_avail,
                state->child_buf_len - state->child_buf_avail);
        } while (bytes_read == -1 && errno == EINTR);

        if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
            break;

        if (bytes_read == -1) {
            archive_set_error(&a->archive, errno,
                "Read from filter failed unexpectedly.");
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }
        state->child_buf_avail += bytes_read;

        bytes_written = (a->client_writer)(&a->archive, a->client_data,
            state->child_buf, state->child_buf_avail);
        if (bytes_written <= 0) {
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }
        if ((size_t)bytes_written < state->child_buf_avail) {
            memmove(state->child_buf,
                state->child_buf + bytes_written,
                state->child_buf_avail - bytes_written);
        }
        state->child_buf_avail -= bytes_written;
        a->archive.raw_position += bytes_written;
    }

    /* XXX pad final compressed block. */

cleanup:
    /* Shut down the child. */
    if (state->child_stdin != -1)
        close(state->child_stdin);
    if (state->child_stdout != -1)
        close(state->child_stdout);
    while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
        continue;

    if (status != 0) {
        archive_set_error(&a->archive, EIO,
            "Filter exited with failure.");
        ret = ARCHIVE_FATAL;
    }

    /* Release our configuration data. */
    free(a->compressor.config);
    a->compressor.config = NULL;

    /* Release our private state data. */
    free(state->child_buf);
    free(state->description);
    free(state);
    return (ret);
}

#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */

--- NEW FILE: archive_write.3 ---
.\" 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/lib/libarchive/archive_write.3,v 1.25 2008/11/01 19:11:21 kientzle Exp $
.\"
.Dd May 11, 2008
.Dt archive_write 3
.Os
.Sh NAME
.Nm archive_write_new ,
.Nm archive_write_set_format_cpio ,
.Nm archive_write_set_format_pax ,
.Nm archive_write_set_format_pax_restricted ,
.Nm archive_write_set_format_shar ,
.Nm archive_write_set_format_shar_binary ,
.Nm archive_write_set_format_ustar ,
.Nm archive_write_get_bytes_per_block ,
.Nm archive_write_set_bytes_per_block ,
.Nm archive_write_set_bytes_in_last_block ,
.Nm archive_write_set_compression_bzip2 ,
.Nm archive_write_set_compression_compress ,
.Nm archive_write_set_compression_gzip ,
.Nm archive_write_set_compression_none ,
.Nm archive_write_set_compression_program ,
.Nm archive_write_set_compressor_options ,
.Nm archive_write_set_format_options ,
.Nm archive_write_set_options ,
.Nm archive_write_open ,
.Nm archive_write_open_fd ,
.Nm archive_write_open_FILE ,
.Nm archive_write_open_filename ,
.Nm archive_write_open_memory ,
.Nm archive_write_header ,
.Nm archive_write_data ,
.Nm archive_write_finish_entry ,
.Nm archive_write_close ,
.Nm archive_write_finish
.Nd functions for creating archives
.Sh SYNOPSIS
.In archive.h
.Ft struct archive *
.Fn archive_write_new "void"
.Ft int
.Fn archive_write_get_bytes_per_block "struct archive *"
.Ft int
.Fn archive_write_set_bytes_per_block "struct archive *" "int bytes_per_block"
.Ft int
.Fn archive_write_set_bytes_in_last_block "struct archive *" "int"
.Ft int
.Fn archive_write_set_compression_bzip2 "struct archive *"
.Ft int
.Fn archive_write_set_compression_compress "struct archive *"
.Ft int
.Fn archive_write_set_compression_gzip "struct archive *"
.Ft int
.Fn archive_write_set_compression_none "struct archive *"
.Ft int
.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
.Ft int
.Fn archive_write_set_format_cpio "struct archive *"
.Ft int
.Fn archive_write_set_format_pax "struct archive *"
.Ft int
.Fn archive_write_set_format_pax_restricted "struct archive *"
.Ft int
.Fn archive_write_set_format_shar "struct archive *"
.Ft int
.Fn archive_write_set_format_shar_binary "struct archive *"
.Ft int
.Fn archive_write_set_format_ustar "struct archive *"
.Ft int
.Fn archive_write_set_format_options "struct archive *" "const char *"
.Ft int
.Fn archive_write_set_compressor_options "struct archive *" "const char *"
.Ft int
.Fn archive_write_set_options "struct archive *" "const char *"
.Ft int
.Fo archive_write_open
.Fa "struct archive *"
.Fa "void *client_data"
.Fa "archive_open_callback *"
.Fa "archive_write_callback *"
.Fa "archive_close_callback *"
.Fc
.Ft int
.Fn archive_write_open_fd "struct archive *" "int fd"
.Ft int
.Fn archive_write_open_FILE "struct archive *" "FILE *file"
.Ft int
.Fn archive_write_open_filename "struct archive *" "const char *filename"
.Ft int
.Fo archive_write_open_memory
.Fa "struct archive *"
.Fa "void *buffer"
.Fa "size_t bufferSize"
.Fa "size_t *outUsed"
.Fc
.Ft int
.Fn archive_write_header "struct archive *" "struct archive_entry *"
.Ft ssize_t
.Fn archive_write_data "struct archive *" "const void *" "size_t"
.Ft int
.Fn archive_write_finish_entry "struct archive *"
.Ft int
.Fn archive_write_close "struct archive *"
.Ft int
.Fn archive_write_finish "struct archive *"
.Sh DESCRIPTION
These functions provide a complete API for creating streaming
archive files.
The general process is to first create the
.Tn struct archive
object, set any desired options, initialize the archive, append entries, then
close the archive and release all resources.
The following summary describes the functions in approximately
the order they are ordinarily used:
.Bl -tag -width indent
.It Fn archive_write_new
Allocates and initializes a
.Tn struct archive
object suitable for writing a tar archive.
.It Fn archive_write_set_bytes_per_block
Sets the block size used for writing the archive data.
Every call to the write callback function, except possibly the last one, will
use this value for the length.
The third parameter is a boolean that specifies whether or not the final block
written will be padded to the full block size.
If it is zero, the last block will not be padded.
If it is non-zero, padding will be added both before and after compression.
The default is to use a block size of 10240 bytes and to pad the last block.
Note that a block size of zero will suppress internal blocking
and cause writes to be sent directly to the write callback as they occur.
.It Fn archive_write_get_bytes_per_block
Retrieve the block size to be used for writing.
A value of -1 here indicates that the library should use default values.
A value of zero indicates that internal blocking is suppressed.
.It Fn archive_write_set_bytes_in_last_block
Sets the block size used for writing the last block.
If this value is zero, the last block will be padded to the same size
as the other blocks.
Otherwise, the final block will be padded to a multiple of this size.
In particular, setting it to 1 will cause the final block to not be padded.
For compressed output, any padding generated by this option
is applied only after the compression.
The uncompressed data is always unpadded.
The default is to pad the last block to the full block size (note that
.Fn archive_write_open_filename
will set this based on the file type).
Unlike the other
.Dq set
functions, this function can be called after the archive is opened.
.It Fn archive_write_get_bytes_in_last_block
Retrieve the currently-set value for last block size.
A value of -1 here indicates that the library should use default values.
.It Xo
.Fn archive_write_set_format_cpio ,
.Fn archive_write_set_format_pax ,
.Fn archive_write_set_format_pax_restricted ,
.Fn archive_write_set_format_shar ,
.Fn archive_write_set_format_shar_binary ,
.Fn archive_write_set_format_ustar
.Xc
Sets the format that will be used for the archive.
The library can write
POSIX octet-oriented cpio format archives,
POSIX-standard
.Dq pax interchange
format archives,
traditional
.Dq shar
archives,
enhanced
.Dq binary
shar archives that store a variety of file attributes and handle binary files,
and
POSIX-standard
.Dq ustar
archives.
The pax interchange format is a backwards-compatible tar format that
adds key/value attributes to each entry and supports arbitrary
filenames, linknames, uids, sizes, etc.
.Dq Restricted pax interchange format
is the library default; this is the same as pax format, but suppresses
the pax extended header for most normal files.
In most cases, this will result in ordinary ustar archives.
.It Xo
.Fn archive_write_set_compression_bzip2 ,
.Fn archive_write_set_compression_compress ,
.Fn archive_write_set_compression_gzip ,
.Fn archive_write_set_compression_none
.Xc
The resulting archive will be compressed as specified.
Note that the compressed output is always properly blocked.
.It Fn archive_write_set_compression_program
The archive will be fed into the specified compression program.
The output of that program is blocked and written to the client
write callbacks.
.It Xo
.Fn archive_write_set_compressor_options ,
.Fn archive_write_set_format_options ,
.Fn archive_write_set_options
.Xc
Specifies options that will be passed to the currently-enabled
compressor and/or format writer.
The argument is a comma-separated list of individual options.
Individual options have one of the following forms:
.Bl -tag -compact -width indent
.It Ar option=value
The option/value pair will be provided to every module.
Modules that do not accept an option with this name will ignore it.
.It Ar option
The option will be provided to every module with a value of
.Dq 1 .
.It Ar !option
The option will be provided to every module with a NULL value.
.It Ar module:option=value , Ar module:option , Ar module:!option
As above, but the corresponding option and value will be provided
only to modules whose name matches
.Ar module .
.El
The return value will be
.Cm ARCHIVE_OK
if any module accepts the option, or
.Cm ARCHIVE_WARN
if no module accepted the option, or
.Cm ARCHIVE_FATAL
if there was a fatal error while attempting to process the option.
.Pp
The currently supported options are:
.Bl -tag -compact -width indent
.It Compressor gzip
.Bl -tag -compact -width indent
.It Cm compression-level
The value is interpreted as a decimal integer specifying the
gzip compression level.
.El
.It Compressor xz
.Bl -tag -compact -width indent
.It Cm compression-level
The value is interpreted as a decimal integer specifying the
compression level.
.El
.It Format mtree
.Bl -tag -compact -width indent
.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname
Enable a particular keyword in the mtree output.
Prefix with an exclamation mark to disable the corresponding keyword.
The default is equivalent to
.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname .
.It Cm all
Enables all of the above keywords.
.It Cm use-set
Enables generation of
.Cm /set
lines that specify default values for the following files and/or directories.
.It Cm indent
XXX needs explanation XXX
.El
.El
.It Fn archive_write_open
Freeze the settings, open the archive, and prepare for writing entries.
This is the most generic form of this function, which accepts
pointers to three callback functions which will be invoked by
the compression layer to write the constructed archive.
.It Fn archive_write_open_fd
A convenience form of
.Fn archive_write_open
that accepts a file descriptor.
The
.Fn archive_write_open_fd
function is safe for use with tape drives or other
block-oriented devices.
.It Fn archive_write_open_FILE
A convenience form of
.Fn archive_write_open
that accepts a
.Ft "FILE *"
pointer.
Note that
.Fn archive_write_open_FILE
is not safe for writing to tape drives or other devices
that require correct blocking.
.It Fn archive_write_open_file
A deprecated synonym for
.Fn archive_write_open_filename .
.It Fn archive_write_open_filename
A convenience form of
.Fn archive_write_open
that accepts a filename.
A NULL argument indicates that the output should be written to standard output;
an argument of
.Dq -
will open a file with that name.
If you have not invoked
.Fn archive_write_set_bytes_in_last_block ,
then
.Fn archive_write_open_filename
will adjust the last-block padding depending on the file:
it will enable padding when writing to standard output or
to a character or block device node, it will disable padding otherwise.
You can override this by manually invoking
.Fn archive_write_set_bytes_in_last_block
before calling
.Fn archive_write_open .
The
.Fn archive_write_open_filename
function is safe for use with tape drives or other
block-oriented devices.
.It Fn archive_write_open_memory
A convenience form of
.Fn archive_write_open
that accepts a pointer to a block of memory that will receive
the archive.
The final
.Ft "size_t *"
argument points to a variable that will be updated
after each write to reflect how much of the buffer
is currently in use.
You should be careful to ensure that this variable
remains allocated until after the archive is
closed.
.It Fn archive_write_header
Build and write a header using the data in the provided
.Tn struct archive_entry
structure.
See
.Xr archive_entry 3
for information on creating and populating
.Tn struct archive_entry
objects.
.It Fn archive_write_data
Write data corresponding to the header just written.
Returns number of bytes written or -1 on error.
.It Fn archive_write_finish_entry
Close out the entry just written.
In particular, this writes out the final padding required by some formats.
Ordinarily, clients never need to call this, as it
is called automatically by
.Fn archive_write_next_header
and
.Fn archive_write_close
as needed.
.It Fn archive_write_close
Complete the archive and invoke the close callback.
.It Fn archive_write_finish
Invokes
.Fn archive_write_close
if it was not invoked manually, then releases all resources.
Note that this function was declared to return
.Ft void
in libarchive 1.x, which made it impossible to detect errors when
.Fn archive_write_close
was invoked implicitly from this function.
This is corrected beginning with libarchive 2.0.
.El
More information about the
.Va struct archive
object and the overall design of the library can be found in the
.Xr libarchive 3
overview.
.Sh IMPLEMENTATION
Compression support is built-in to libarchive, which uses zlib and bzlib
to handle gzip and bzip2 compression, respectively.
.Sh CLIENT CALLBACKS
To use this library, you will need to define and register
callback functions that will be invoked to write data to the
resulting archive.
These functions are registered by calling
.Fn archive_write_open :
.Bl -item -offset indent
.It
.Ft typedef int
.Fn archive_open_callback "struct archive *" "void *client_data"
.El
.Pp
The open callback is invoked by
.Fn archive_write_open .
It should return
.Cm ARCHIVE_OK
if the underlying file or data source is successfully
opened.
If the open fails, it should call
.Fn archive_set_error
to register an error code and message and return
.Cm ARCHIVE_FATAL .
.Bl -item -offset indent
.It
.Ft typedef ssize_t
.Fo archive_write_callback
.Fa "struct archive *"
.Fa "void *client_data"
.Fa "const void *buffer"
.Fa "size_t length"
.Fc
.El
.Pp
The write callback is invoked whenever the library
needs to write raw bytes to the archive.
For correct blocking, each call to the write callback function
should translate into a single
.Xr write 2
system call.
This is especially critical when writing archives to tape drives.
On success, the write callback should return the
number of bytes actually written.
On error, the callback should invoke
.Fn archive_set_error
to register an error code and message and return -1.
.Bl -item -offset indent
.It
.Ft typedef int
.Fn archive_close_callback "struct archive *" "void *client_data"
.El
.Pp
The close callback is invoked by archive_close when
the archive processing is complete.
The callback should return
.Cm ARCHIVE_OK
on success.
On failure, the callback should invoke
.Fn archive_set_error
to register an error code and message and
return
.Cm ARCHIVE_FATAL.
.Sh EXAMPLE
The following sketch illustrates basic usage of the library.
In this example,
the callback functions are simply wrappers around the standard
.Xr open 2 ,
.Xr write 2 ,
and
.Xr close 2
system calls.
.Bd -literal -offset indent
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

struct mydata {
    const char *name;
    int fd;
};

int
myopen(struct archive *a, void *client_data)
{
  struct mydata *mydata = client_data;

  mydata->fd = open(mydata->name, O_WRONLY | O_CREAT, 0644);
  if (mydata->fd >= 0)
    return (ARCHIVE_OK);
  else
    return (ARCHIVE_FATAL);
}

ssize_t
mywrite(struct archive *a, void *client_data, const void *buff, size_t n)
{
  struct mydata *mydata = client_data;

  return (write(mydata->fd, buff, n));
}

int
myclose(struct archive *a, void *client_data)
{
  struct mydata *mydata = client_data;

  if (mydata->fd > 0)
    close(mydata->fd);
  return (0);
}

void
write_archive(const char *outname, const char **filename)
{
  struct mydata *mydata = malloc(sizeof(struct mydata));
  struct archive *a;
  struct archive_entry *entry;
  struct stat st;
  char buff[8192];
  int len;
  int fd;

  a = archive_write_new();
  mydata->name = outname;
  archive_write_set_compression_gzip(a);
  archive_write_set_format_ustar(a);
  archive_write_open(a, mydata, myopen, mywrite, myclose);
  while (*filename) {
    stat(*filename, &st);
    entry = archive_entry_new();
    archive_entry_copy_stat(entry, &st);
    archive_entry_set_pathname(entry, *filename);
    archive_write_header(a, entry);
    fd = open(*filename, O_RDONLY);
    len = read(fd, buff, sizeof(buff));
    while ( len > 0 ) {
    archive_write_data(a, buff, len);
    len = read(fd, buff, sizeof(buff));
    }
    archive_entry_free(entry);
    filename++;
  }
  archive_write_finish(a);
}

int main(int argc, const char **argv)
{
    const char *outname;
    argv++;
    outname = argv++;
    write_archive(outname, argv);
    return 0;
}
.Ed
.Sh RETURN VALUES
Most functions return
.Cm ARCHIVE_OK
(zero) on success, or one of several non-zero
error codes for errors.
Specific error codes include:
.Cm ARCHIVE_RETRY
for operations that might succeed if retried,
.Cm ARCHIVE_WARN
for unusual conditions that do not prevent further operations, and
.Cm ARCHIVE_FATAL
for serious errors that make remaining operations impossible.
The
.Fn archive_errno
and
.Fn archive_error_string
functions can be used to retrieve an appropriate error code and a
textual error message.
.Pp
.Fn archive_write_new
returns a pointer to a newly-allocated
.Tn struct archive
object.
.Pp
.Fn archive_write_data
returns a count of the number of bytes actually written.
On error, -1 is returned and the
.Fn archive_errno
and
.Fn archive_error_string
functions will return appropriate values.
Note that if the client-provided write callback function
returns a non-zero value, that error will be propagated back to the caller
through whatever API function resulted in that call, which
may include
.Fn archive_write_header ,
.Fn archive_write_data ,
.Fn archive_write_close ,
or
.Fn archive_write_finish .
The client callback can call
.Fn archive_set_error
to provide values that can then be retrieved by
.Fn archive_errno
and
.Fn archive_error_string .
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr tar 5
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .
.Sh BUGS
There are many peculiar bugs in historic tar implementations that may cause
certain programs to reject archives written by this library.
For example, several historic implementations calculated header checksums
incorrectly and will thus reject valid archives; GNU tar does not fully support
pax interchange format; some old tar implementations required specific
field terminations.
.Pp
The default pax interchange format eliminates most of the historic
tar limitations and provides a generic key/value attribute facility
for vendor-defined extensions.
One oversight in POSIX is the failure to provide a standard attribute
for large device numbers.
This library uses
.Dq SCHILY.devminor
and
.Dq SCHILY.devmajor
for device numbers that exceed the range supported by the backwards-compatible
ustar header.
These keys are compatible with Joerg Schilling's
.Nm star
archiver.
Other implementations may not recognize these keys and will thus be unable
to correctly restore device nodes with large device numbers from archives
created by this library.

--- NEW FILE: out ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: archive_hash.h ---
/*-
 * Copyright (c) 2009 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

/*
 * Hash function support in various Operating Systems:
 *
 * NetBSD:
 * - MD5 and SHA1 in libc: without _ after algorithm name
 * - SHA2 in libc: with _ after algorithm name
 *
 * OpenBSD:
 * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name
 * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name
 *
 * DragonFly and FreeBSD (XXX not used yet):
 * - MD5 and SHA1 in libmd: without _ after algorithm name
 * - SHA256: with _ after algorithm name
 *
 * OpenSSL:
 * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name
 */

#if defined(HAVE_MD5_H) && defined(HAVE_MD5INIT)
#  include <md5.h>
#  define ARCHIVE_HAS_MD5
typedef MD5_CTX archive_md5_ctx;
#  define archive_md5_init(ctx)         MD5Init(ctx)
#  define archive_md5_final(ctx, buf)       MD5Final(buf, ctx)
#  define archive_md5_update(ctx, buf, n)   MD5Update(ctx, buf, n)
#elif defined(HAVE_OPENSSL_MD5_H)
#  include <openssl/md5.h>
#  define ARCHIVE_HAS_MD5
typedef MD5_CTX archive_md5_ctx;
#  define archive_md5_init(ctx)         MD5_Init(ctx)
#  define archive_md5_final(ctx, buf)       MD5_Final(buf, ctx)
#  define archive_md5_update(ctx, buf, n)   MD5_Update(ctx, buf, n)
#endif

#if defined(HAVE_RMD160_H) && defined(HAVE_RMD160INIT)
#  include <rmd160.h>
#  define ARCHIVE_HAS_RMD160
typedef RMD160_CTX archive_rmd160_ctx;
#  define archive_rmd160_init(ctx)      RMD160Init(ctx)
#  define archive_rmd160_final(ctx, buf)    RMD160Final(buf, ctx)
#  define archive_rmd160_update(ctx, buf, n)    RMD160Update(ctx, buf, n)
#elif defined(HAVE_OPENSSL_RIPEMD_H)
#  include <openssl/ripemd.h>
#  define ARCHIVE_HAS_RMD160
typedef RIPEMD160_CTX archive_rmd160_ctx;
#  define archive_rmd160_init(ctx)      RIPEMD160_Init(ctx)
#  define archive_rmd160_final(ctx, buf)    RIPEMD160_Final(buf, ctx)
#  define archive_rmd160_update(ctx, buf, n)    RIPEMD160_Update(ctx, buf, n)
#endif

#if defined(HAVE_SHA1_H) && defined(HAVE_SHA1INIT)
#  include <sha1.h>
#  define ARCHIVE_HAS_SHA1
typedef SHA1_CTX archive_sha1_ctx;
#  define archive_sha1_init(ctx)        SHA1Init(ctx)
#  define archive_sha1_final(ctx, buf)      SHA1Final(buf, ctx)
#  define archive_sha1_update(ctx, buf, n)  SHA1Update(ctx, buf, n)
#elif defined(HAVE_OPENSSL_SHA_H)
#  include <openssl/sha.h>
#  define ARCHIVE_HAS_SHA1
typedef SHA_CTX archive_sha1_ctx;
#  define archive_sha1_init(ctx)        SHA1_Init(ctx)
#  define archive_sha1_final(ctx, buf)      SHA1_Final(buf, ctx)
#  define archive_sha1_update(ctx, buf, n)  SHA1_Update(ctx, buf, n)
#endif

#if defined(HAVE_SHA2_H) && defined(HAVE_SHA256_INIT)
#  include <sha2.h>
#  define ARCHIVE_HAS_SHA256
typedef SHA256_CTX archive_sha256_ctx;
#  define archive_sha256_init(ctx)      SHA256_Init(ctx)
#  define archive_sha256_final(ctx, buf)    SHA256_Final(buf, ctx)
#  define archive_sha256_update(ctx, buf, n)    SHA256_Update(ctx, buf, n)
#elif defined(HAVE_SHA2_H) && defined(HAVE_SHA256INIT)
#  include <sha2.h>
#  define ARCHIVE_HAS_SHA256
typedef SHA256_CTX archive_sha256_ctx;
#  define archive_sha256_init(ctx)      SHA256Init(ctx)
#  define archive_sha256_final(ctx, buf)    SHA256Final(buf, ctx)
#  define archive_sha256_update(ctx, buf, n)    SHA256Update(ctx, buf, n)
#elif defined(HAVE_OPENSSL_SHA_H)
#  include <openssl/sha.h>
#  define ARCHIVE_HAS_SHA256
typedef SHA256_CTX archive_sha256_ctx;
#  define archive_sha256_init(ctx)      SHA256_Init(ctx)
#  define archive_sha256_final(ctx, buf)    SHA256_Final(buf, ctx)
#  define archive_sha256_update(ctx, buf, n)    SHA256_Update(ctx, buf, n)
#endif

#if defined(HAVE_SHA2_H) && defined(HAVE_SHA384_INIT)
#  include <sha2.h>
#  define ARCHIVE_HAS_SHA384
typedef SHA384_CTX archive_sha384_ctx;
#  define archive_sha384_init(ctx)      SHA384_Init(ctx)
#  define archive_sha384_final(ctx, buf)    SHA384_Final(buf, ctx)
#  define archive_sha384_update(ctx, buf, n)    SHA384_Update(ctx, buf, n)
#elif defined(HAVE_SHA2_H) && defined(HAVE_SHA384INIT)
#  include <sha2.h>
#  define ARCHIVE_HAS_SHA384
typedef SHA384_CTX archive_sha384_ctx;
#  define archive_sha384_init(ctx)      SHA384Init(ctx)
#  define archive_sha384_final(ctx, buf)    SHA384Final(buf, ctx)
#  define archive_sha384_update(ctx, buf, n)    SHA384Update(ctx, buf, n)
#elif defined(HAVE_OPENSSL_SHA_H)
#  include <openssl/sha.h>
#  define ARCHIVE_HAS_SHA384
typedef SHA512_CTX archive_sha384_ctx;
#  define archive_sha384_init(ctx)      SHA384_Init(ctx)
#  define archive_sha384_final(ctx, buf)    SHA384_Final(buf, ctx)
#  define archive_sha384_update(ctx, buf, n)    SHA384_Update(ctx, buf, n)
#endif

#if defined(HAVE_SHA2_H) && defined(HAVE_SHA512_INIT)
#  include <sha2.h>
#  define ARCHIVE_HAS_SHA512
typedef SHA512_CTX archive_sha512_ctx;
#  define archive_sha512_init(ctx)      SHA512_Init(ctx)
#  define archive_sha512_final(ctx, buf)    SHA512_Final(buf, ctx)
#  define archive_sha512_update(ctx, buf, n)    SHA512_Update(ctx, buf, n)
#elif defined(HAVE_SHA2_H) && defined(HAVE_SHA512INIT)
#  include <sha2.h>
#  define ARCHIVE_HAS_SHA512
typedef SHA512_CTX archive_sha512_ctx;
#  define archive_sha512_init(ctx)      SHA512Init(ctx)
#  define archive_sha512_final(ctx, buf)    SHA512Final(buf, ctx)
#  define archive_sha512_update(ctx, buf, n)    SHA512Update(ctx, buf, n)
#elif defined(HAVE_OPENSSL_SHA_H)
#  include <openssl/sha.h>
#  define ARCHIVE_HAS_SHA512
typedef SHA512_CTX archive_sha512_ctx;
#  define archive_sha512_init(ctx)      SHA512_Init(ctx)
#  define archive_sha512_final(ctx, buf)    SHA512_Final(buf, ctx)
#  define archive_sha512_update(ctx, buf, n)    SHA512_Update(ctx, buf, n)
#endif

--- NEW FILE: archive_read_support_compression_bzip2.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.19 2008/12/06 06:45:15 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_BZLIB_H
#include <bzlib.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"

#if HAVE_BZLIB_H
struct private_data {
    bz_stream    stream;
    char        *out_block;
    size_t       out_block_size;
    char         valid; /* True = decompressor is initialized */
    char         eof; /* True = found end of compressed data. */
};

/* Bzip2 filter */
static ssize_t  bzip2_filter_read(struct archive_read_filter *, const void **);
static int  bzip2_filter_close(struct archive_read_filter *);
#endif

/*
 * Note that we can detect bzip2 archives even if we can't decompress
 * them.  (In fact, we like detecting them because we can give better
 * error messages.)  So the bid framework here gets compiled even
 * if bzlib is unavailable.
 */
static int  bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
static int  bzip2_reader_init(struct archive_read_filter *);
static int  bzip2_reader_free(struct archive_read_filter_bidder *);

int
archive_read_support_compression_bzip2(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct archive_read_filter_bidder *reader = __archive_read_get_bidder(a);

    if (reader == NULL)
        return (ARCHIVE_FATAL);

    reader->data = NULL;
    reader->bid = bzip2_reader_bid;
    reader->init = bzip2_reader_init;
    reader->options = NULL;
    reader->free = bzip2_reader_free;
#if HAVE_BZLIB_H
    return (ARCHIVE_OK);
#else
    archive_set_error(_a, ARCHIVE_ERRNO_MISC,
        "Using external bunzip2 program");
    return (ARCHIVE_WARN);
#endif
}

static int
bzip2_reader_free(struct archive_read_filter_bidder *self){
    (void)self; /* UNUSED */
    return (ARCHIVE_OK);
}

/*
 * Test whether we can handle this data.
 *
 * This logic returns zero if any part of the signature fails.  It
 * also tries to Do The Right Thing if a very short buffer prevents us
 * from verifying as much as we would like.
 */
static int
bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
{
    const unsigned char *buffer;
    ssize_t avail;
    int bits_checked;

    (void)self; /* UNUSED */

    /* Minimal bzip2 archive is 14 bytes. */
    buffer = __archive_read_filter_ahead(filter, 14, &avail);
    if (buffer == NULL)
        return (0);

    /* First three bytes must be "BZh" */
    bits_checked = 0;
    if (buffer[0] != 'B' || buffer[1] != 'Z' || buffer[2] != 'h')
        return (0);
    bits_checked += 24;

    /* Next follows a compression flag which must be an ASCII digit. */
    if (buffer[3] < '1' || buffer[3] > '9')
        return (0);
    bits_checked += 5;

    /* After BZh[1-9], there must be either a data block
     * which begins with 0x314159265359 or an end-of-data
     * marker of 0x177245385090. */
    if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)
        bits_checked += 48;
    else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)
        bits_checked += 48;
    else
        return (0);

    return (bits_checked);
}

#ifndef HAVE_BZLIB_H

/*
 * If we don't have the library on this system, we can't actually do the
 * decompression.  We can, however, still detect compressed archives
 * and emit a useful message.
 */
static int
bzip2_reader_init(struct archive_read_filter *self)
{
    int r;

    r = __archive_read_program(self, "bunzip2");
    /* Note: We set the format here even if __archive_read_program()
     * above fails.  We do, after all, know what the format is
     * even if we weren't able to read it. */
    self->code = ARCHIVE_COMPRESSION_BZIP2;
    self->name = "bzip2";
    return (r);
}


#else

/*
 * Setup the callbacks.
 */
static int
bzip2_reader_init(struct archive_read_filter *self)
{
    static const size_t out_block_size = 64 * 1024;
    void *out_block;
    struct private_data *state;

    self->code = ARCHIVE_COMPRESSION_BZIP2;
    self->name = "bzip2";

    state = (struct private_data *)calloc(sizeof(*state), 1);
    out_block = (unsigned char *)malloc(out_block_size);
    if (self == NULL || state == NULL || out_block == NULL) {
        archive_set_error(&self->archive->archive, ENOMEM,
            "Can't allocate data for bzip2 decompression");
        free(out_block);
        free(state);
        return (ARCHIVE_FATAL);
    }

    self->data = state;
    state->out_block_size = out_block_size;
    state->out_block = out_block;
    self->read = bzip2_filter_read;
    self->skip = NULL; /* not supported */
    self->close = bzip2_filter_close;

    return (ARCHIVE_OK);
}

/*
 * Return the next block of decompressed data.
 */
static ssize_t
bzip2_filter_read(struct archive_read_filter *self, const void **p)
{
    struct private_data *state;
    size_t read_avail, decompressed;
    const char *read_buf;
    ssize_t ret;

    state = (struct private_data *)self->data;
    read_avail = 0;

    if (state->eof) {
        *p = NULL;
        return (0);
    }

    /* Empty our output buffer. */
    state->stream.next_out = state->out_block;
    state->stream.avail_out = state->out_block_size;

    /* Try to fill the output buffer. */
    for (;;) {
        if (!state->valid) {
            if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
                state->eof = 1;
                *p = state->out_block;
                decompressed = state->stream.next_out
                    - state->out_block;
                return (decompressed);
            }
            /* Initialize compression library. */
            ret = BZ2_bzDecompressInit(&(state->stream),
                       0 /* library verbosity */,
                       0 /* don't use low-mem algorithm */);

            /* If init fails, try low-memory algorithm instead. */
            if (ret == BZ_MEM_ERROR)
                ret = BZ2_bzDecompressInit(&(state->stream),
                       0 /* library verbosity */,
                       1 /* do use low-mem algo */);

            if (ret != BZ_OK) {
                const char *detail = NULL;
                int err = ARCHIVE_ERRNO_MISC;
                switch (ret) {
                case BZ_PARAM_ERROR:
                    detail = "invalid setup parameter";
                    break;
                case BZ_MEM_ERROR:
                    err = ENOMEM;
                    detail = "out of memory";
                    break;
                case BZ_CONFIG_ERROR:
                    detail = "mis-compiled library";
                    break;
                }
                archive_set_error(&self->archive->archive, err,
                    "Internal error initializing decompressor%s%s",
                    detail == NULL ? "" : ": ",
                    detail);
                return (ARCHIVE_FATAL);
            }
            state->valid = 1;
        }

        /* stream.next_in is really const, but bzlib
         * doesn't declare it so. <sigh> */
        read_buf =
            __archive_read_filter_ahead(self->upstream, 1, &ret);
        if (read_buf == NULL)
            return (ARCHIVE_FATAL);
        state->stream.next_in = (char *)(uintptr_t)read_buf;
        state->stream.avail_in = ret;
        /* There is no more data, return whatever we have. */
        if (ret == 0) {
            state->eof = 1;
            *p = state->out_block;
            decompressed = state->stream.next_out
                - state->out_block;
            return (decompressed);
        }

        /* Decompress as much as we can in one pass. */
        ret = BZ2_bzDecompress(&(state->stream));
        __archive_read_filter_consume(self->upstream,
            state->stream.next_in - read_buf);

        switch (ret) {
        case BZ_STREAM_END: /* Found end of stream. */
            switch (BZ2_bzDecompressEnd(&(state->stream))) {
            case BZ_OK:
                break;
            default:
                archive_set_error(&(self->archive->archive),
                      ARCHIVE_ERRNO_MISC,
                      "Failed to clean up decompressor");
                return (ARCHIVE_FATAL);
            }
            state->valid = 0;
            /* FALLTHROUGH */
        case BZ_OK: /* Decompressor made some progress. */
            /* If we filled our buffer, update stats and return. */
            if (state->stream.avail_out == 0) {
                *p = state->out_block;
                decompressed = state->stream.next_out
                    - state->out_block;
                return (decompressed);
            }
            break;
        default: /* Return an error. */
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC, "bzip decompression failed");
            return (ARCHIVE_FATAL);
        }
    }
}

/*
 * Clean up the decompressor.
 */
static int
bzip2_filter_close(struct archive_read_filter *self)
{
    struct private_data *state;
    int ret = ARCHIVE_OK;

    state = (struct private_data *)self->data;

    if (state->valid) {
        switch (BZ2_bzDecompressEnd(&state->stream)) {
        case BZ_OK:
            break;
        default:
            archive_set_error(&self->archive->archive,
                      ARCHIVE_ERRNO_MISC,
                      "Failed to clean up decompressor");
            ret = ARCHIVE_FATAL;
        }
    }

    free(state->out_block);
    free(state);
    return (ARCHIVE_OK);
}

#endif /* HAVE_BZLIB_H */

--- NEW FILE: archive_read_open_fd.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_fd.c,v 1.13 2007/06/26 03:06:48 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#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 "archive.h"

struct read_fd_data {
    int  fd;
    size_t   block_size;
    char     can_skip;
    void    *buffer;
};

static int  file_close(struct archive *, void *);
static ssize_t  file_read(struct archive *, void *, const void **buff);
#if ARCHIVE_API_VERSION < 2
static ssize_t  file_skip(struct archive *, void *, size_t request);
#else
static off_t    file_skip(struct archive *, void *, off_t request);
#endif

int
archive_read_open_fd(struct archive *a, int fd, size_t block_size)
{
    struct stat st;
    struct read_fd_data *mine;
    void *b;

    archive_clear_error(a);
    if (fstat(fd, &st) != 0) {
        archive_set_error(a, errno, "Can't stat fd %d", fd);
        return (ARCHIVE_FATAL);
    }

    mine = (struct read_fd_data *)malloc(sizeof(*mine));
    b = malloc(block_size);
    if (mine == NULL || b == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        free(mine);
        free(b);
        return (ARCHIVE_FATAL);
    }
    mine->block_size = block_size;
    mine->buffer = b;
    mine->fd = fd;
    /*
     * Skip support is a performance optimization for anything
     * that supports lseek().  On FreeBSD, only regular files and
     * raw disk devices support lseek() and there's no portable
     * way to determine if a device is a raw disk device, so we
     * only enable this optimization for regular files.
     */
    if (S_ISREG(st.st_mode)) {
        archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
        mine->can_skip = 1;
    } else
        mine->can_skip = 0;
#if defined(__CYGWIN__)
    setmode(mine->fd, O_BINARY);
#elif defined(_WIN32)
    _setmode(mine->fd, _O_BINARY);
#endif

    return (archive_read_open2(a, mine,
        NULL, file_read, file_skip, file_close));
}

static ssize_t
file_read(struct archive *a, void *client_data, const void **buff)
{
    struct read_fd_data *mine = (struct read_fd_data *)client_data;
    ssize_t bytes_read;

    *buff = mine->buffer;
    bytes_read = read(mine->fd, mine->buffer, mine->block_size);
    if (bytes_read < 0) {
        archive_set_error(a, errno, "Error reading fd %d", mine->fd);
    }
    return (bytes_read);
}

#if ARCHIVE_API_VERSION < 2
static ssize_t
file_skip(struct archive *a, void *client_data, size_t request)
#else
static off_t
file_skip(struct archive *a, void *client_data, off_t request)
#endif
{
    struct read_fd_data *mine = (struct read_fd_data *)client_data;
    off_t old_offset, new_offset;

    if (!mine->can_skip)
        return (0);

    /* Reduce request to the next smallest multiple of block_size */
    request = (request / mine->block_size) * mine->block_size;
    if (request == 0)
        return (0);

    /*
     * Hurray for lazy evaluation: if the first lseek fails, the second
     * one will not be executed.
     */
    if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
        ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
    {
        /* If seek failed once, it will probably fail again. */
        mine->can_skip = 0;

        if (errno == ESPIPE)
        {
            /*
             * Failure to lseek() can be caused by the file
             * descriptor pointing to a pipe, socket or FIFO.
             * Return 0 here, so the compression layer will use
             * read()s instead to advance the file descriptor.
             * It's slower of course, but works as well.
             */
            return (0);
        }
        /*
         * There's been an error other than ESPIPE. This is most
         * likely caused by a programmer error (too large request)
         * or a corrupted archive file.
         */
        archive_set_error(a, errno, "Error seeking");
        return (-1);
    }
    return (new_offset - old_offset);
}

static int
file_close(struct archive *a, void *client_data)
{
    struct read_fd_data *mine = (struct read_fd_data *)client_data;

    (void)a; /* UNUSED */
    free(mine->buffer);
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_read_support_compression_xz.c ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD$");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#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
#if HAVE_LZMA_H
#include <lzma.h>
#elif HAVE_LZMADEC_H
#include <lzmadec.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"

#if HAVE_LZMA_H && HAVE_LIBLZMA

struct private_data {
    lzma_stream  stream;
    unsigned char   *out_block;
    size_t       out_block_size;
    int64_t      total_out;
    char         eof; /* True = found end of compressed data. */
};

/* Combined lzma/xz filter */
static ssize_t  xz_filter_read(struct archive_read_filter *, const void **);
static int  xz_filter_close(struct archive_read_filter *);
static int  xz_lzma_bidder_init(struct archive_read_filter *);

#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC

struct private_data {
    lzmadec_stream   stream;
    unsigned char   *out_block;
    size_t       out_block_size;
    int64_t      total_out;
    char         eof; /* True = found end of compressed data. */
};

/* Lzma-only filter */
static ssize_t  lzma_filter_read(struct archive_read_filter *, const void **);
static int  lzma_filter_close(struct archive_read_filter *);
#endif

/*
 * Note that we can detect xz and lzma compressed files even if we
 * can't decompress them.  (In fact, we like detecting them because we
 * can give better error messages.)  So the bid framework here gets
 * compiled even if no lzma library is available.
 */
static int  xz_bidder_bid(struct archive_read_filter_bidder *,
            struct archive_read_filter *);
static int  xz_bidder_init(struct archive_read_filter *);
static int  lzma_bidder_bid(struct archive_read_filter_bidder *,
            struct archive_read_filter *);
static int  lzma_bidder_init(struct archive_read_filter *);

int
archive_read_support_compression_xz(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);

    archive_clear_error(_a);
    if (bidder == NULL)
        return (ARCHIVE_FATAL);

    bidder->data = NULL;
    bidder->bid = xz_bidder_bid;
    bidder->init = xz_bidder_init;
    bidder->options = NULL;
    bidder->free = NULL;
#if HAVE_LZMA_H && HAVE_LIBLZMA
    return (ARCHIVE_OK);
#else
    archive_set_error(_a, ARCHIVE_ERRNO_MISC,
        "Using external unxz program for xz decompression");
    return (ARCHIVE_WARN);
#endif
}

int
archive_read_support_compression_lzma(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);

    archive_clear_error(_a);
    if (bidder == NULL)
        return (ARCHIVE_FATAL);

    bidder->data = NULL;
    bidder->bid = lzma_bidder_bid;
    bidder->init = lzma_bidder_init;
    bidder->options = NULL;
    bidder->free = NULL;
#if HAVE_LZMA_H && HAVE_LIBLZMA
    return (ARCHIVE_OK);
#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC
    return (ARCHIVE_OK);
#else
    archive_set_error(_a, ARCHIVE_ERRNO_MISC,
        "Using external unlzma program for lzma decompression");
    return (ARCHIVE_WARN);
#endif
}

/*
 * Test whether we can handle this data.
 */
static int
xz_bidder_bid(struct archive_read_filter_bidder *self,
    struct archive_read_filter *filter)
{
    const unsigned char *buffer;
    ssize_t avail;
    int bits_checked;

    (void)self; /* UNUSED */

    buffer = __archive_read_filter_ahead(filter, 6, &avail);
    if (buffer == NULL)
        return (0);

    /*
     * Verify Header Magic Bytes : FD 37 7A 58 5A 00
     */
    bits_checked = 0;
    if (buffer[0] != 0xFD)
        return (0);
    bits_checked += 8;
    if (buffer[1] != 0x37)
        return (0);
    bits_checked += 8;
    if (buffer[2] != 0x7A)
        return (0);
    bits_checked += 8;
    if (buffer[3] != 0x58)
        return (0);
    bits_checked += 8;
    if (buffer[4] != 0x5A)
        return (0);
    bits_checked += 8;
    if (buffer[5] != 0x00)
        return (0);
    bits_checked += 8;

    return (bits_checked);
}

/*
 * Test whether we can handle this data.
 *
 * <sigh> LZMA has a rather poor file signature.  Zeros do not
 * make good signature bytes as a rule, and the only non-zero byte
 * here is an ASCII character.  For example, an uncompressed tar
 * archive whose first file is ']' would satisfy this check.  It may
 * be necessary to exclude LZMA from compression_all() because of
 * this.  Clients of libarchive would then have to explicitly enable
 * LZMA checking instead of (or in addition to) compression_all() when
 * they have other evidence (file name, command-line option) to go on.
 */
static int
lzma_bidder_bid(struct archive_read_filter_bidder *self,
    struct archive_read_filter *filter)
{
    const unsigned char *buffer;
    ssize_t avail;
    int bits_checked;

    (void)self; /* UNUSED */

    buffer = __archive_read_filter_ahead(filter, 6, &avail);
    if (buffer == NULL)
        return (0);

    /* First byte of raw LZMA stream is always 0x5d. */
    bits_checked = 0;
    if (buffer[0] != 0x5d)
        return (0);
    bits_checked += 8;

    /* Second through fifth bytes are dictionary code, stored in
     * little-endian order.  The two least-significant bytes are
     * always zero. */
    if (buffer[1] != 0 || buffer[2] != 0)
        return (0);
    bits_checked += 16;

    /* ??? TODO:  Fix this. ??? */
    /* NSIS format check uses this, but I've seen tar.lzma
     * archives where this byte is 0xff, not 0.  Can it
     * ever be anything other than 0 or 0xff?
     */
#if 0
    if (buffer[5] != 0)
        return (0);
    bits_checked += 8;
#endif

    /* TODO: The above test is still very weak.  It would be
     * good to do better. */

    return (bits_checked);
}

#if HAVE_LZMA_H && HAVE_LIBLZMA

/*
 * liblzma 4.999.7 and later support both lzma and xz streams.
 */
static int
xz_bidder_init(struct archive_read_filter *self)
{
    self->code = ARCHIVE_COMPRESSION_XZ;
    self->name = "xz";
    return (xz_lzma_bidder_init(self));
}

static int
lzma_bidder_init(struct archive_read_filter *self)
{
    self->code = ARCHIVE_COMPRESSION_LZMA;
    self->name = "lzma";
    return (xz_lzma_bidder_init(self));
}

/*
 * Setup the callbacks.
 */
static int
xz_lzma_bidder_init(struct archive_read_filter *self)
{
    static const size_t out_block_size = 64 * 1024;
    void *out_block;
    struct private_data *state;
    int ret;

    state = (struct private_data *)calloc(sizeof(*state), 1);
    out_block = (unsigned char *)malloc(out_block_size);
    if (state == NULL || out_block == NULL) {
        archive_set_error(&self->archive->archive, ENOMEM,
            "Can't allocate data for xz decompression");
        free(out_block);
        free(state);
        return (ARCHIVE_FATAL);
    }

    self->data = state;
    state->out_block_size = out_block_size;
    state->out_block = out_block;
    self->read = xz_filter_read;
    self->skip = NULL; /* not supported */
    self->close = xz_filter_close;

    state->stream.avail_in = 0;

    state->stream.next_out = state->out_block;
    state->stream.avail_out = state->out_block_size;

    /* Initialize compression library.
     * TODO: I don't know what value is best for memlimit.
     *       maybe, it needs to check memory size which
     *       running system has.
     */
    if (self->code == ARCHIVE_COMPRESSION_XZ)
        ret = lzma_stream_decoder(&(state->stream),
            (1U << 30),/* memlimit */
            LZMA_CONCATENATED);
    else
        ret = lzma_alone_decoder(&(state->stream),
            (1U << 30));/* memlimit */

    if (ret == LZMA_OK)
        return (ARCHIVE_OK);

    /* Library setup failed: Choose an error message and clean up. */
    switch (ret) {
    case LZMA_MEM_ERROR:
        archive_set_error(&self->archive->archive, ENOMEM,
            "Internal error initializing compression library: "
            "Cannot allocate memory");
        break;
    case LZMA_OPTIONS_ERROR:
        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "Invalid or unsupported options");
        break;
    default:
        archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing lzma library");
        break;
    }

    free(state->out_block);
    free(state);
    self->data = NULL;
    return (ARCHIVE_FATAL);
}

/*
 * Return the next block of decompressed data.
 */
static ssize_t
xz_filter_read(struct archive_read_filter *self, const void **p)
{
    struct private_data *state;
    size_t decompressed;
    ssize_t avail_in;
    int ret;

    state = (struct private_data *)self->data;

    /* Empty our output buffer. */
    state->stream.next_out = state->out_block;
    state->stream.avail_out = state->out_block_size;

    /* Try to fill the output buffer. */
    while (state->stream.avail_out > 0 && !state->eof) {
        state->stream.next_in =
            __archive_read_filter_ahead(self->upstream, 1, &avail_in);
        if (state->stream.next_in == NULL && avail_in < 0)
            return (ARCHIVE_FATAL);
        state->stream.avail_in = avail_in;

        /* Decompress as much as we can in one pass. */
        ret = lzma_code(&(state->stream),
            (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
        switch (ret) {
        case LZMA_STREAM_END: /* Found end of stream. */
            state->eof = 1;
            /* FALL THROUGH */
        case LZMA_OK: /* Decompressor made some progress. */
            __archive_read_filter_consume(self->upstream,
                avail_in - state->stream.avail_in);
            break;
        case LZMA_MEM_ERROR:
            archive_set_error(&self->archive->archive, ENOMEM,
                "Lzma library error: Cannot allocate memory");
            return (ARCHIVE_FATAL);
        case LZMA_MEMLIMIT_ERROR:
            archive_set_error(&self->archive->archive, ENOMEM,
                "Lzma library error: Out of memory");
            return (ARCHIVE_FATAL);
        case LZMA_FORMAT_ERROR:
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Lzma library error: format not recognized");
            return (ARCHIVE_FATAL);
        case LZMA_OPTIONS_ERROR:
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Lzma library error: Invalid options");
            return (ARCHIVE_FATAL);
        case LZMA_DATA_ERROR:
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Lzma library error: Corrupted input data");
            return (ARCHIVE_FATAL);
        case LZMA_BUF_ERROR:
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Lzma library error:  No progress is possible");
            return (ARCHIVE_FATAL);
        default:
            /* Return an error. */
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Lzma decompression failed:  Unknown error");
            return (ARCHIVE_FATAL);
        }
    }

    decompressed = state->stream.next_out - state->out_block;
    state->total_out += decompressed;
    if (decompressed == 0)
        *p = NULL;
    else
        *p = state->out_block;
    return (decompressed);
}

/*
 * Clean up the decompressor.
 */
static int
xz_filter_close(struct archive_read_filter *self)
{
    struct private_data *state;

    state = (struct private_data *)self->data;
    lzma_end(&(state->stream));
    free(state->out_block);
    free(state);
    return (ARCHIVE_OK);
}

#else

#if HAVE_LZMADEC_H && HAVE_LIBLZMADEC

/*
 * If we have the older liblzmadec library, then we can handle
 * LZMA streams but not XZ streams.
 */

/*
 * Setup the callbacks.
 */
static int
lzma_bidder_init(struct archive_read_filter *self)
{
    static const size_t out_block_size = 64 * 1024;
    void *out_block;
    struct private_data *state;
    ssize_t ret, avail_in;

    self->code = ARCHIVE_COMPRESSION_LZMA;
    self->name = "lzma";

    state = (struct private_data *)calloc(sizeof(*state), 1);
    out_block = (unsigned char *)malloc(out_block_size);
    if (state == NULL || out_block == NULL) {
        archive_set_error(&self->archive->archive, ENOMEM,
            "Can't allocate data for lzma decompression");
        free(out_block);
        free(state);
        return (ARCHIVE_FATAL);
    }

    self->data = state;
    state->out_block_size = out_block_size;
    state->out_block = out_block;
    self->read = lzma_filter_read;
    self->skip = NULL; /* not supported */
    self->close = lzma_filter_close;

    /* Prime the lzma library with 18 bytes of input. */
    state->stream.next_in = (unsigned char *)(uintptr_t)
        __archive_read_filter_ahead(self->upstream, 18, &avail_in);
    if (state->stream.next_in == NULL)
        return (ARCHIVE_FATAL);
    state->stream.avail_in = avail_in;
    state->stream.next_out = state->out_block;
    state->stream.avail_out = state->out_block_size;

    /* Initialize compression library. */
    ret = lzmadec_init(&(state->stream));
    __archive_read_filter_consume(self->upstream,
        avail_in - state->stream.avail_in);
    if (ret == LZMADEC_OK)
        return (ARCHIVE_OK);

    /* Library setup failed: Clean up. */
    archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
        "Internal error initializing lzma library");

    /* Override the error message if we know what really went wrong. */
    switch (ret) {
    case LZMADEC_HEADER_ERROR:
        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "invalid header");
        break;
    case LZMADEC_MEM_ERROR:
        archive_set_error(&self->archive->archive, ENOMEM,
            "Internal error initializing compression library: "
            "out of memory");
        break;
    }

    free(state->out_block);
    free(state);
    self->data = NULL;
    return (ARCHIVE_FATAL);
}

/*
 * Return the next block of decompressed data.
 */
static ssize_t
lzma_filter_read(struct archive_read_filter *self, const void **p)
{
    struct private_data *state;
    size_t decompressed;
    ssize_t avail_in, ret;

    state = (struct private_data *)self->data;

    /* Empty our output buffer. */
    state->stream.next_out = state->out_block;
    state->stream.avail_out = state->out_block_size;

    /* Try to fill the output buffer. */
    while (state->stream.avail_out > 0 && !state->eof) {
        state->stream.next_in = (unsigned char *)(uintptr_t)
            __archive_read_filter_ahead(self->upstream, 1, &avail_in);
        if (state->stream.next_in == NULL && avail_in < 0)
            return (ARCHIVE_FATAL);
        state->stream.avail_in = avail_in;

        /* Decompress as much as we can in one pass. */
        ret = lzmadec_decode(&(state->stream), avail_in == 0);
        switch (ret) {
        case LZMADEC_STREAM_END: /* Found end of stream. */
            state->eof = 1;
            /* FALL THROUGH */
        case LZMADEC_OK: /* Decompressor made some progress. */
            __archive_read_filter_consume(self->upstream,
                avail_in - state->stream.avail_in);
            break;
        case LZMADEC_BUF_ERROR: /* Insufficient input data? */
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Insufficient compressed data");
            return (ARCHIVE_FATAL);
        default:
            /* Return an error. */
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "Lzma decompression failed");
            return (ARCHIVE_FATAL);
        }
    }

    decompressed = state->stream.next_out - state->out_block;
    state->total_out += decompressed;
    if (decompressed == 0)
        *p = NULL;
    else
        *p = state->out_block;
    return (decompressed);
}

/*
 * Clean up the decompressor.
 */
static int
lzma_filter_close(struct archive_read_filter *self)
{
    struct private_data *state;
    int ret;

    state = (struct private_data *)self->data;
    ret = ARCHIVE_OK;
    switch (lzmadec_end(&(state->stream))) {
    case LZMADEC_OK:
        break;
    default:
        archive_set_error(&(self->archive->archive),
            ARCHIVE_ERRNO_MISC,
            "Failed to clean up %s compressor",
            self->archive->archive.compression_name);
        ret = ARCHIVE_FATAL;
    }

    free(state->out_block);
    free(state);
    return (ret);
}

#else

/*
 *
 * If we have no suitable library on this system, we can't actually do
 * the decompression.  We can, however, still detect compressed
 * archives and emit a useful message.
 *
 */
static int
lzma_bidder_init(struct archive_read_filter *self)
{
    int r;

    r = __archive_read_program(self, "unlzma");
    /* Note: We set the format here even if __archive_read_program()
     * above fails.  We do, after all, know what the format is
     * even if we weren't able to read it. */
    self->code = ARCHIVE_COMPRESSION_LZMA;
    self->name = "lzma";
    return (r);
}

#endif /* HAVE_LZMADEC_H */


static int
xz_bidder_init(struct archive_read_filter *self)
{
    int r;

    r = __archive_read_program(self, "unxz");
    /* Note: We set the format here even if __archive_read_program()
     * above fails.  We do, after all, know what the format is
     * even if we weren't able to read it. */
    self->code = ARCHIVE_COMPRESSION_XZ;
    self->name = "xz";
    return (r);
}


#endif /* HAVE_LZMA_H */

--- NEW FILE: o2 ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: archive_entry_stat.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_stat.c,v 1.2 2008/09/30 03:53:03 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#include "archive_entry.h"
#include "archive_entry_private.h"

const struct stat *
archive_entry_stat(struct archive_entry *entry)
{
    struct stat *st;
    if (entry->stat == NULL) {
        entry->stat = malloc(sizeof(*st));
        if (entry->stat == NULL)
            return (NULL);
        entry->stat_valid = 0;
    }

    /*
     * If none of the underlying fields have been changed, we
     * don't need to regenerate.  In theory, we could use a bitmap
     * here to flag only those items that have changed, but the
     * extra complexity probably isn't worth it.  It will be very
     * rare for anyone to change just one field then request a new
     * stat structure.
     */
    if (entry->stat_valid)
        return (entry->stat);

    st = entry->stat;
    /*
     * Use the public interfaces to extract items, so that
     * the appropriate conversions get invoked.
     */
    st->st_atime = archive_entry_atime(entry);
#if HAVE_STRUCT_STAT_ST_BIRTHTIME
    st->st_birthtime = archive_entry_birthtime(entry);
#endif
    st->st_ctime = archive_entry_ctime(entry);
    st->st_mtime = archive_entry_mtime(entry);
    st->st_dev = archive_entry_dev(entry);
    st->st_gid = archive_entry_gid(entry);
    st->st_uid = archive_entry_uid(entry);
    st->st_ino = archive_entry_ino64(entry);
    st->st_nlink = archive_entry_nlink(entry);
    st->st_rdev = archive_entry_rdev(entry);
    st->st_size = archive_entry_size(entry);
    st->st_mode = archive_entry_mode(entry);

    /*
     * On systems that support high-res timestamps, copy that
     * information into struct stat.
     */
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
    st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
    st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
    st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
    st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
    st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
    st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
#elif HAVE_STRUCT_STAT_ST_MTIME_N
    st->st_atime_n = archive_entry_atime_nsec(entry);
    st->st_ctime_n = archive_entry_ctime_nsec(entry);
    st->st_mtime_n = archive_entry_mtime_nsec(entry);
#elif HAVE_STRUCT_STAT_ST_UMTIME
    st->st_uatime = archive_entry_atime_nsec(entry) / 1000;
    st->st_uctime = archive_entry_ctime_nsec(entry) / 1000;
    st->st_umtime = archive_entry_mtime_nsec(entry) / 1000;
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
    st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000;
    st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000;
    st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000;
#endif
#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
    st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry);
#endif

    /*
     * TODO: On Linux, store 32 or 64 here depending on whether
     * the cached stat structure is a stat32 or a stat64.  This
     * will allow us to support both variants interchangably.
     */
    entry->stat_valid = 1;

    return (st);
}

--- NEW FILE: archive_write_disk.3 ---
.\" 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/lib/libarchive/archive_write_disk.3,v 1.4 2008/09/04 05:22:00 kientzle Exp $
.\"
.Dd August 5, 2008
.Dt archive_write_disk 3
.Os
.Sh NAME
.Nm archive_write_disk_new ,
.Nm archive_write_disk_set_options ,
.Nm archive_write_disk_set_skip_file ,
.Nm archive_write_disk_set_group_lookup ,
.Nm archive_write_disk_set_standard_lookup ,
.Nm archive_write_disk_set_user_lookup ,
.Nm archive_write_header ,
.Nm archive_write_data ,
.Nm archive_write_finish_entry ,
.Nm archive_write_close ,
.Nm archive_write_finish
.Nd functions for creating objects on disk
.Sh SYNOPSIS
.In archive.h
.Ft struct archive *
.Fn archive_write_disk_new "void"
.Ft int
.Fn archive_write_disk_set_options "struct archive *" "int flags"
.Ft int
.Fn archive_write_disk_set_skip_file "struct archive *" "dev_t" "ino_t"
.Ft int
.Fo archive_write_disk_set_group_lookup
.Fa "struct archive *"
.Fa "void *"
.Fa "gid_t (*)(void *, const char *gname, gid_t gid)"
.Fa "void (*cleanup)(void *)"
.Fc
.Ft int
.Fn archive_write_disk_set_standard_lookup "struct archive *"
.Ft int
.Fo archive_write_disk_set_user_lookup
.Fa "struct archive *"
.Fa "void *"
.Fa "uid_t (*)(void *, const char *uname, uid_t uid)"
.Fa "void (*cleanup)(void *)"
.Fc
.Ft int
.Fn archive_write_header "struct archive *" "struct archive_entry *"
.Ft ssize_t
.Fn archive_write_data "struct archive *" "const void *" "size_t"
.Ft int
.Fn archive_write_finish_entry "struct archive *"
.Ft int
.Fn archive_write_close "struct archive *"
.Ft int
.Fn archive_write_finish "struct archive *"
.Sh DESCRIPTION
These functions provide a complete API for creating objects on
disk from
.Tn struct archive_entry
descriptions.
They are most naturally used when extracting objects from an archive
using the
.Fn archive_read
interface.
The general process is to read
.Tn struct archive_entry
objects from an archive, then write those objects to a
.Tn struct archive
object created using the
.Fn archive_write_disk
family functions.
This interface is deliberately very similar to the
.Fn archive_write
interface used to write objects to a streaming archive.
.Bl -tag -width indent
.It Fn archive_write_disk_new
Allocates and initializes a
.Tn struct archive
object suitable for writing objects to disk.
.It Fn archive_write_disk_set_skip_file
Records the device and inode numbers of a file that should not be
overwritten.
This is typically used to ensure that an extraction process does not
overwrite the archive from which objects are being read.
This capability is technically unnecessary but can be a significant
performance optimization in practice.
.It Fn archive_write_disk_set_options
The options field consists of a bitwise OR of one or more of the
following values:
.Bl -tag -compact -width "indent"
.It Cm ARCHIVE_EXTRACT_OWNER
The user and group IDs should be set on the restored file.
By default, the user and group IDs are not restored.
.It Cm ARCHIVE_EXTRACT_PERM
Full permissions (including SGID, SUID, and sticky bits) should
be restored exactly as specified, without obeying the
current umask.
Note that SUID and SGID bits can only be restored if the
user and group ID of the object on disk are correct.
If
.Cm ARCHIVE_EXTRACT_OWNER
is not specified, then SUID and SGID bits will only be restored
if the default user and group IDs of newly-created objects on disk
happen to match those specified in the archive entry.
By default, only basic permissions are restored, and umask is obeyed.
.It Cm ARCHIVE_EXTRACT_TIME
The timestamps (mtime, ctime, and atime) should be restored.
By default, they are ignored.
Note that restoring of atime is not currently supported.
.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE
Existing files on disk will not be overwritten.
By default, existing regular files are truncated and overwritten;
existing directories will have their permissions updated;
other pre-existing objects are unlinked and recreated from scratch.
.It Cm ARCHIVE_EXTRACT_UNLINK
Existing files on disk will be unlinked before any attempt to
create them.
In some cases, this can prove to be a significant performance improvement.
By default, existing files are truncated and rewritten, but
the file is not recreated.
In particular, the default behavior does not break existing hard links.
.It Cm ARCHIVE_EXTRACT_ACL
Attempt to restore ACLs.
By default, extended ACLs are ignored.
.It Cm ARCHIVE_EXTRACT_FFLAGS
Attempt to restore extended file flags.
By default, file flags are ignored.
.It Cm ARCHIVE_EXTRACT_XATTR
Attempt to restore POSIX.1e extended attributes.
By default, they are ignored.
.It Cm ARCHIVE_EXTRACT_SECURE_SYMLINKS
Refuse to extract any object whose final location would be altered
by a symlink on disk.
This is intended to help guard against a variety of mischief
caused by archives that (deliberately or otherwise) extract
files outside of the current directory.
The default is not to perform this check.
If
.Cm ARCHIVE_EXTRACT_UNLINK
is specified together with this option, the library will
remove any intermediate symlinks it finds and return an
error only if such symlink could not be removed.
.It Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT
Refuse to extract a path that contains a
.Pa ..
element anywhere within it.
The default is to not refuse such paths.
Note that paths ending in
.Pa ..
always cause an error, regardless of this flag.
.It Cm ARCHIVE_EXTRACT_SPARSE
Scan data for blocks of NUL bytes and try to recreate them with holes.
This results in sparse files, independent of whether the archive format
supports or uses them.
.El
.It Xo
.Fn archive_write_disk_set_group_lookup ,
.Fn archive_write_disk_set_user_lookup
.Xc
The
.Tn struct archive_entry
objects contain both names and ids that can be used to identify users
and groups.
These names and ids describe the ownership of the file itself and
also appear in ACL lists.
By default, the library uses the ids and ignores the names, but
this can be overridden by registering user and group lookup functions.
To register, you must provide a lookup function which
accepts both a name and id and returns a suitable id.
You may also provide a
.Tn void *
pointer to a private data structure and a cleanup function for
that data.
The cleanup function will be invoked when the
.Tn struct archive
object is destroyed.
.It Fn archive_write_disk_set_standard_lookup
This convenience function installs a standard set of user
and group lookup functions.
These functions use
.Xr getpwnam 3
and
.Xr getgrnam 3
to convert names to ids, defaulting to the ids if the names cannot
be looked up.
These functions also implement a simple memory cache to reduce
the number of calls to
.Xr getpwnam 3
and
.Xr getgrnam 3 .
.It Fn archive_write_header
Build and write a header using the data in the provided
.Tn struct archive_entry
structure.
See
.Xr archive_entry 3
for information on creating and populating
.Tn struct archive_entry
objects.
.It Fn archive_write_data
Write data corresponding to the header just written.
Returns number of bytes written or -1 on error.
.It Fn archive_write_finish_entry
Close out the entry just written.
Ordinarily, clients never need to call this, as it
is called automatically by
.Fn archive_write_next_header
and
.Fn archive_write_close
as needed.
.It Fn archive_write_close
Set any attributes that could not be set during the initial restore.
For example, directory timestamps are not restored initially because
restoring a subsequent file would alter that timestamp.
Similarly, non-writable directories are initially created with
write permissions (so that their contents can be restored).
The
.Nm
library maintains a list of all such deferred attributes and
sets them when this function is invoked.
.It Fn archive_write_finish
Invokes
.Fn archive_write_close
if it was not invoked manually, then releases all resources.
.El
More information about the
.Va struct archive
object and the overall design of the library can be found in the
.Xr libarchive 3
overview.
Many of these functions are also documented under
.Xr archive_write 3 .
.Sh RETURN VALUES
Most functions return
.Cm ARCHIVE_OK
(zero) on success, or one of several non-zero
error codes for errors.
Specific error codes include:
.Cm ARCHIVE_RETRY
for operations that might succeed if retried,
.Cm ARCHIVE_WARN
for unusual conditions that do not prevent further operations, and
.Cm ARCHIVE_FATAL
for serious errors that make remaining operations impossible.
The
.Fn archive_errno
and
.Fn archive_error_string
functions can be used to retrieve an appropriate error code and a
textual error message.
.Pp
.Fn archive_write_disk_new
returns a pointer to a newly-allocated
.Tn struct archive
object.
.Pp
.Fn archive_write_data
returns a count of the number of bytes actually written.
On error, -1 is returned and the
.Fn archive_errno
and
.Fn archive_error_string
functions will return appropriate values.
.Sh SEE ALSO
.Xr archive_read 3 ,
.Xr archive_write 3 ,
.Xr tar 1 ,
.Xr libarchive 3
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
The
.Nm archive_write_disk
interface was added to
.Nm libarchive 2.0
and first appeared in
.Fx 6.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .
.Sh BUGS
Directories are actually extracted in two distinct phases.
Directories are created during
.Fn archive_write_header ,
but final permissions are not set until
.Fn archive_write_close .
This separation is necessary to correctly handle borderline
cases such as a non-writable directory containing
files, but can cause unexpected results.
In particular, directory permissions are not fully
restored until the archive is closed.
If you use
.Xr chdir 2
to change the current directory between calls to
.Fn archive_read_extract
or before calling
.Fn archive_read_close ,
you may confuse the permission-setting logic with
the result that directory permissions are restored
incorrectly.
.Pp
The library attempts to create objects with filenames longer than
.Cm PATH_MAX
by creating prefixes of the full path and changing the current directory.
Currently, this logic is limited in scope; the fixup pass does
not work correctly for such objects and the symlink security check
option disables the support for very long pathnames.
.Pp
Restoring the path
.Pa aa/../bb
does create each intermediate directory.
In particular, the directory
.Pa aa
is created as well as the final object
.Pa bb .
In theory, this can be exploited to create an entire directory heirarchy
with a single request.
Of course, this does not work if the
.Cm ARCHIVE_EXTRACT_NODOTDOT
option is specified.
.Pp
Implicit directories are always created obeying the current umask.
Explicit objects are created obeying the current umask unless
.Cm ARCHIVE_EXTRACT_PERM
is specified, in which case they current umask is ignored.
.Pp
SGID and SUID bits are restored only if the correct user and
group could be set.
If
.Cm ARCHIVE_EXTRACT_OWNER
is not specified, then no attempt is made to set the ownership.
In this case, SGID and SUID bits are restored only if the
user and group of the final object happen to match those specified
in the entry.
.Pp
The
.Dq standard
user-id and group-id lookup functions are not the defaults because
.Xr getgrnam 3
and
.Xr getpwnam 3
are sometimes too large for particular applications.
The current design allows the application author to use a more
compact implementation when appropriate.
.Pp
There should be a corresponding
.Nm archive_read_disk
interface that walks a directory heirarchy and returns archive
entry objects.
--- NEW FILE: archive_read_support_compression_program.c ---
/*-
 * Copyright (c) 2007 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.6 2008/12/06 06:45:15 kientzle Exp $");

#ifdef HAVE_SYS_WAIT_H
#  include <sys/wait.h>
#endif
#ifdef HAVE_ERRNO_H
#  include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#  include <fcntl.h>
#endif
#ifdef HAVE_LIMITS_H
#  include <limits.h>
#endif
#ifdef HAVE_SIGNAL_H
#  include <signal.h>
#endif
#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 "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"

int
archive_read_support_compression_program(struct archive *a, const char *cmd)
{
    return (archive_read_support_compression_program_signature(a, cmd, NULL, 0));
}


/* This capability is only available on POSIX systems. */
#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
    !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))

/*
 * On non-Posix systems, allow the program to build, but choke if
 * this function is actually invoked.
 */
int
archive_read_support_compression_program_signature(struct archive *_a,
    const char *cmd, void *signature, size_t signature_len)
{
    (void)_a; /* UNUSED */
    (void)cmd; /* UNUSED */
    (void)signature; /* UNUSED */
    (void)signature_len; /* UNUSED */

    archive_set_error(_a, -1,
        "External compression programs not supported on this platform");
    return (ARCHIVE_FATAL);
}

int
__archive_read_program(struct archive_read_filter *self, const char *cmd)
{
    (void)self; /* UNUSED */
    (void)cmd; /* UNUSED */

    archive_set_error(&self->archive->archive, -1,
        "External compression programs not supported on this platform");
    return (ARCHIVE_FATAL);
}

#else

#include "filter_fork.h"

/*
 * The bidder object stores the command and the signature to watch for.
 * The 'inhibit' entry here is used to ensure that unchecked filters never
 * bid twice in the same pipeline.
 */
struct program_bidder {
    char *cmd;
    void *signature;
    size_t signature_len;
    int inhibit;
};

static int  program_bidder_bid(struct archive_read_filter_bidder *,
            struct archive_read_filter *upstream);
static int  program_bidder_init(struct archive_read_filter *);
static int  program_bidder_free(struct archive_read_filter_bidder *);

/*
 * The actual filter needs to track input and output data.
 */
struct program_filter {
    char        *description;
    pid_t        child;
    int      exit_status;
    int      waitpid_return;
    int      child_stdin, child_stdout;

    char        *out_buf;
    size_t       out_buf_len;
};

static ssize_t  program_filter_read(struct archive_read_filter *,
            const void **);
static int  program_filter_close(struct archive_read_filter *);

int
archive_read_support_compression_program_signature(struct archive *_a,
    const char *cmd, const void *signature, size_t signature_len)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct archive_read_filter_bidder *bidder;
    struct program_bidder *state;

    /*
     * Get a bidder object from the read core.
     */
    bidder = __archive_read_get_bidder(a);
    if (bidder == NULL)
        return (ARCHIVE_FATAL);

    /*
     * Allocate our private state.
     */
    state = (struct program_bidder *)calloc(sizeof (*state), 1);
    if (state == NULL)
        return (ARCHIVE_FATAL);
    state->cmd = strdup(cmd);
    if (signature != NULL && signature_len > 0) {
        state->signature_len = signature_len;
        state->signature = malloc(signature_len);
        memcpy(state->signature, signature, signature_len);
    }

    /*
     * Fill in the bidder object.
     */
    bidder->data = state;
    bidder->bid = program_bidder_bid;
    bidder->init = program_bidder_init;
    bidder->options = NULL;
    bidder->free = program_bidder_free;
    return (ARCHIVE_OK);
}

static int
program_bidder_free(struct archive_read_filter_bidder *self)
{
    struct program_bidder *state = (struct program_bidder *)self->data;
    free(state->cmd);
    free(state->signature);
    free(self->data);
    return (ARCHIVE_OK);
}

/*
 * If we do have a signature, bid only if that matches.
 *
 * If there's no signature, we bid INT_MAX the first time
 * we're called, then never bid again.
 */
static int
program_bidder_bid(struct archive_read_filter_bidder *self,
    struct archive_read_filter *upstream)
{
    struct program_bidder *state = self->data;
    const char *p;

    /* If we have a signature, use that to match. */
    if (state->signature_len > 0) {
        p = __archive_read_filter_ahead(upstream,
            state->signature_len, NULL);
        if (p == NULL)
            return (0);
        /* No match, so don't bid. */
        if (memcmp(p, state->signature, state->signature_len) != 0)
            return (0);
        return (state->signature_len * 8);
    }

    /* Otherwise, bid once and then never bid again. */
    if (state->inhibit)
        return (0);
    state->inhibit = 1;
    return (INT_MAX);
}

/*
 * Shut down the child, return ARCHIVE_OK if it exited normally.
 *
 * Note that the return value is sticky; if we're called again,
 * we won't reap the child again, but we will return the same status
 * (including error message if the child came to a bad end).
 */
static int
child_stop(struct archive_read_filter *self, struct program_filter *state)
{
    /* Close our side of the I/O with the child. */
    if (state->child_stdin != -1) {
        close(state->child_stdin);
        state->child_stdin = -1;
    }
    if (state->child_stdout != -1) {
        close(state->child_stdout);
        state->child_stdout = -1;
    }

    if (state->child != 0) {
        /* Reap the child. */
        do {
            state->waitpid_return
                = waitpid(state->child, &state->exit_status, 0);
        } while (state->waitpid_return == -1 && errno == EINTR);
        state->child = 0;
    }

    if (state->waitpid_return < 0) {
        /* waitpid() failed?  This is ugly. */
        archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
            "Child process exited badly");
        return (ARCHIVE_WARN);
    }

    if (WIFSIGNALED(state->exit_status)) {
#ifdef SIGPIPE
        /* If the child died because we stopped reading before
         * it was done, that's okay.  Some archive formats
         * have padding at the end that we routinely ignore. */
        /* The alternative to this would be to add a step
         * before close(child_stdout) above to read from the
         * child until the child has no more to write. */
        if (WTERMSIG(state->exit_status) == SIGPIPE)
            return (ARCHIVE_OK);
#endif
        archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
            "Child process exited with signal %d",
            WTERMSIG(state->exit_status));
        return (ARCHIVE_WARN);
    }

    if (WIFEXITED(state->exit_status)) {
        if (WEXITSTATUS(state->exit_status) == 0)
            return (ARCHIVE_OK);

        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Child process exited with status %d",
            WEXITSTATUS(state->exit_status));
        return (ARCHIVE_WARN);
    }

    return (ARCHIVE_WARN);
}

/*
 * Use select() to decide whether the child is ready for read or write.
 */
static ssize_t
child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
{
    struct program_filter *state = self->data;
    ssize_t ret, requested, avail;
    const char *p;

    requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;

    for (;;) {
        do {
            ret = read(state->child_stdout, buf, requested);
        } while (ret == -1 && errno == EINTR);

        if (ret > 0)
            return (ret);
        if (ret == 0 || (ret == -1 && errno == EPIPE))
            /* Child has closed its output; reap the child
             * and return the status. */
            return (child_stop(self, state));
        if (ret == -1 && errno != EAGAIN)
            return (-1);

        if (state->child_stdin == -1) {
            /* Block until child has some I/O ready. */
            __archive_check_child(state->child_stdin,
                state->child_stdout);
            continue;
        }

        /* Get some more data from upstream. */
        p = __archive_read_filter_ahead(self->upstream, 1, &avail);
        if (p == NULL) {
            close(state->child_stdin);
            state->child_stdin = -1;
            fcntl(state->child_stdout, F_SETFL, 0);
            if (avail < 0)
                return (avail);
            continue;
        }

        do {
            ret = write(state->child_stdin, p, avail);
        } while (ret == -1 && errno == EINTR);

        if (ret > 0) {
            /* Consume whatever we managed to write. */
            __archive_read_filter_consume(self->upstream, ret);
        } else if (ret == -1 && errno == EAGAIN) {
            /* Block until child has some I/O ready. */
            __archive_check_child(state->child_stdin,
                state->child_stdout);
        } else {
            /* Write failed. */
            close(state->child_stdin);
            state->child_stdin = -1;
            fcntl(state->child_stdout, F_SETFL, 0);
            /* If it was a bad error, we're done; otherwise
             * it was EPIPE or EOF, and we can still read
             * from the child. */
            if (ret == -1 && errno != EPIPE)
                return (-1);
        }
    }
}

int
__archive_read_program(struct archive_read_filter *self, const char *cmd)
{
    struct program_filter   *state;
    static const size_t out_buf_len = 65536;
    char *out_buf;
    char *description;
    const char *prefix = "Program: ";

    state = (struct program_filter *)calloc(1, sizeof(*state));
    out_buf = (char *)malloc(out_buf_len);
    description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
    if (state == NULL || out_buf == NULL || description == NULL) {
        archive_set_error(&self->archive->archive, ENOMEM,
            "Can't allocate input data");
        free(state);
        free(out_buf);
        free(description);
        return (ARCHIVE_FATAL);
    }

    self->code = ARCHIVE_COMPRESSION_PROGRAM;
    state->description = description;
    strcpy(state->description, prefix);
    strcat(state->description, cmd);
    self->name = state->description;

    state->out_buf = out_buf;
    state->out_buf_len = out_buf_len;

    if ((state->child = __archive_create_child(cmd,
         &state->child_stdin, &state->child_stdout)) == -1) {
        free(state->out_buf);
        free(state);
        archive_set_error(&self->archive->archive, EINVAL,
            "Can't initialise filter");
        return (ARCHIVE_FATAL);
    }

    self->data = state;
    self->read = program_filter_read;
    self->skip = NULL;
    self->close = program_filter_close;

    /* XXX Check that we can read at least one byte? */
    return (ARCHIVE_OK);
}

static int
program_bidder_init(struct archive_read_filter *self)
{
    struct program_bidder   *bidder_state;

    bidder_state = (struct program_bidder *)self->bidder->data;
    return (__archive_read_program(self, bidder_state->cmd));
}

static ssize_t
program_filter_read(struct archive_read_filter *self, const void **buff)
{
    struct program_filter *state;
    ssize_t bytes;
    size_t total;
    char *p;

    state = (struct program_filter *)self->data;

    total = 0;
    p = state->out_buf;
    while (state->child_stdout != -1 && total < state->out_buf_len) {
        bytes = child_read(self, p, state->out_buf_len - total);
        if (bytes < 0)
            /* No recovery is possible if we can no longer
             * read from the child. */
            return (ARCHIVE_FATAL);
        if (bytes == 0)
            /* We got EOF from the child. */
            break;
        total += bytes;
        p += bytes;
    }

    *buff = state->out_buf;
    return (total);
}

static int
program_filter_close(struct archive_read_filter *self)
{
    struct program_filter   *state;
    int e;

    state = (struct program_filter *)self->data;
    e = child_stop(self, state);

    /* Release our private data. */
    free(state->out_buf);
    free(state->description);
    free(state);

    return (e);
}

#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */

--- NEW FILE: archive_write_set_compression_bzip2.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.13 2007/12/30 04:58:21 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_BZLIB_H
#include <bzlib.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"

#ifndef HAVE_BZLIB_H
int
archive_write_set_compression_bzip2(struct archive *a)
{
    archive_set_error(a, ARCHIVE_ERRNO_MISC,
        "bzip2 compression not supported on this platform");
    return (ARCHIVE_FATAL);
}
#else
/* Don't compile this if we don't have bzlib. */

struct private_data {
    bz_stream    stream;
    int64_t      total_in;
    char        *compressed;
    size_t       compressed_buffer_size;
};

struct private_config {
    int      compression_level;
};

/*
 * Yuck.  bzlib.h is not const-correct, so I need this one bit
 * of ugly hackery to convert a const * pointer to a non-const pointer.
 */
#define SET_NEXT_IN(st,src)                 \
    (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)

static int  archive_compressor_bzip2_finish(struct archive_write *);
static int  archive_compressor_bzip2_init(struct archive_write *);
static int  archive_compressor_bzip2_options(struct archive_write *,
            const char *, const char *);
static int  archive_compressor_bzip2_write(struct archive_write *,
            const void *, size_t);
static int  drive_compressor(struct archive_write *, struct private_data *,
            int finishing);

/*
 * Allocate, initialize and return an archive object.
 */
int
archive_write_set_compression_bzip2(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct private_config *config;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
    config = malloc(sizeof(*config));
    if (config == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Out of memory");
        return (ARCHIVE_FATAL);
    }
    a->compressor.config = config;
    a->compressor.finish = archive_compressor_bzip2_finish;
    config->compression_level = 9; /* default */
    a->compressor.init = &archive_compressor_bzip2_init;
    a->compressor.options = &archive_compressor_bzip2_options;
    a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
    a->archive.compression_name = "bzip2";
    return (ARCHIVE_OK);
}

/*
 * Setup callback.
 */
static int
archive_compressor_bzip2_init(struct archive_write *a)
{
    int ret;
    struct private_data *state;
    struct private_config *config;

    config = (struct private_config *)a->compressor.config;
    if (a->client_opener != NULL) {
        ret = (a->client_opener)(&a->archive, a->client_data);
        if (ret != 0)
            return (ret);
    }

    state = (struct private_data *)malloc(sizeof(*state));
    if (state == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression");
        return (ARCHIVE_FATAL);
    }
    memset(state, 0, sizeof(*state));

    state->compressed_buffer_size = a->bytes_per_block;
    state->compressed = (char *)malloc(state->compressed_buffer_size);

    if (state->compressed == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression buffer");
        free(state);
        return (ARCHIVE_FATAL);
    }

    state->stream.next_out = state->compressed;
    state->stream.avail_out = state->compressed_buffer_size;
    a->compressor.write = archive_compressor_bzip2_write;

    /* Initialize compression library */
    ret = BZ2_bzCompressInit(&(state->stream),
        config->compression_level, 0, 30);
    if (ret == BZ_OK) {
        a->compressor.data = state;
        return (ARCHIVE_OK);
    }

    /* Library setup failed: clean up. */
    archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
        "Internal error initializing compression library");
    free(state->compressed);
    free(state);

    /* Override the error message if we know what really went wrong. */
    switch (ret) {
    case BZ_PARAM_ERROR:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "invalid setup parameter");
        break;
    case BZ_MEM_ERROR:
        archive_set_error(&a->archive, ENOMEM,
            "Internal error initializing compression library: "
            "out of memory");
        break;
    case BZ_CONFIG_ERROR:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "mis-compiled library");
        break;
    }

    return (ARCHIVE_FATAL);

}

/*
 * Set write options.
 */
static int
archive_compressor_bzip2_options(struct archive_write *a, const char *key,
    const char *value)
{
    struct private_config *config;

    config = (struct private_config *)a->compressor.config;
    if (strcmp(key, "compression-level") == 0) {
        if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
            value[1] != '\0')
            return (ARCHIVE_WARN);
        config->compression_level = value[0] - '0';
        /* Make '0' be a synonym for '1'. */
        /* This way, bzip2 compressor supports the same 0..9
         * range of levels as gzip. */
        if (config->compression_level < 1)
            config->compression_level = 1;
        return (ARCHIVE_OK);
    }

    return (ARCHIVE_WARN);
}

/*
 * Write data to the compressed stream.
 *
 * Returns ARCHIVE_OK if all data written, error otherwise.
 */
static int
archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
    size_t length)
{
    struct private_data *state;

    state = (struct private_data *)a->compressor.data;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    /* Update statistics */
    state->total_in += length;

    /* Compress input data to output buffer */
    SET_NEXT_IN(state, buff);
    state->stream.avail_in = length;
    if (drive_compressor(a, state, 0))
        return (ARCHIVE_FATAL);
    a->archive.file_position += length;
    return (ARCHIVE_OK);
}


/*
 * Finish the compression.
 */
static int
archive_compressor_bzip2_finish(struct archive_write *a)
{
    ssize_t block_length;
    int ret;
    struct private_data *state;
    ssize_t target_block_length;
    ssize_t bytes_written;
    unsigned tocopy;

    ret = ARCHIVE_OK;
    state = (struct private_data *)a->compressor.data;
    if (state != NULL) {
        if (a->client_writer == NULL) {
            archive_set_error(&a->archive,
                ARCHIVE_ERRNO_PROGRAMMER,
                "No write callback is registered?\n"
                "This is probably an internal programming error.");
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }

        /* By default, always pad the uncompressed data. */
        if (a->pad_uncompressed) {
            tocopy = a->bytes_per_block -
                (state->total_in % a->bytes_per_block);
            while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
                SET_NEXT_IN(state, a->nulls);
                state->stream.avail_in = tocopy < a->null_length ?
                    tocopy : a->null_length;
                state->total_in += state->stream.avail_in;
                tocopy -= state->stream.avail_in;
                ret = drive_compressor(a, state, 0);
                if (ret != ARCHIVE_OK)
                    goto cleanup;
            }
        }

        /* Finish compression cycle. */
        if ((ret = drive_compressor(a, state, 1)))
            goto cleanup;

        /* Optionally, pad the final compressed block. */
        block_length = state->stream.next_out - state->compressed;

        /* Tricky calculation to determine size of last block. */
        target_block_length = block_length;
        if (a->bytes_in_last_block <= 0)
            /* Default or Zero: pad to full block */
            target_block_length = a->bytes_per_block;
        else
            /* Round length to next multiple of bytes_in_last_block. */
            target_block_length = a->bytes_in_last_block *
                ( (block_length + a->bytes_in_last_block - 1) /
                a->bytes_in_last_block);
        if (target_block_length > a->bytes_per_block)
            target_block_length = a->bytes_per_block;
        if (block_length < target_block_length) {
            memset(state->stream.next_out, 0,
                target_block_length - block_length);
            block_length = target_block_length;
        }

        /* Write the last block */
        bytes_written = (a->client_writer)(&a->archive, a->client_data,
            state->compressed, block_length);

        /* TODO: Handle short write of final block. */
        if (bytes_written <= 0)
            ret = ARCHIVE_FATAL;
        else {
            a->archive.raw_position += ret;
            ret = ARCHIVE_OK;
        }

        /* Cleanup: shut down compressor, release memory, etc. */
cleanup:
        switch (BZ2_bzCompressEnd(&(state->stream))) {
        case BZ_OK:
            break;
        default:
            archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
                "Failed to clean up compressor");
            ret = ARCHIVE_FATAL;
        }

        free(state->compressed);
        free(state);
    }
    /* Free configuration data even if we were never fully initialized. */
    free(a->compressor.config);
    a->compressor.config = NULL;
    return (ret);
}

/*
 * Utility function to push input data through compressor, writing
 * full output blocks as necessary.
 *
 * Note that this handles both the regular write case (finishing ==
 * false) and the end-of-archive case (finishing == true).
 */
static int
drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
{
    ssize_t bytes_written;
    int ret;

    for (;;) {
        if (state->stream.avail_out == 0) {
            bytes_written = (a->client_writer)(&a->archive,
                a->client_data, state->compressed,
                state->compressed_buffer_size);
            if (bytes_written <= 0) {
                /* TODO: Handle this write failure */
                return (ARCHIVE_FATAL);
            } else if ((size_t)bytes_written < state->compressed_buffer_size) {
                /* Short write: Move remainder to
                 * front and keep filling */
                memmove(state->compressed,
                    state->compressed + bytes_written,
                    state->compressed_buffer_size - bytes_written);
            }

            a->archive.raw_position += bytes_written;
            state->stream.next_out = state->compressed +
                state->compressed_buffer_size - bytes_written;
            state->stream.avail_out = bytes_written;
        }

        /* If there's nothing to do, we're done. */
        if (!finishing && state->stream.avail_in == 0)
            return (ARCHIVE_OK);

        ret = BZ2_bzCompress(&(state->stream),
            finishing ? BZ_FINISH : BZ_RUN);

        switch (ret) {
        case BZ_RUN_OK:
            /* In non-finishing case, did compressor
             * consume everything? */
            if (!finishing && state->stream.avail_in == 0)
                return (ARCHIVE_OK);
            break;
        case BZ_FINISH_OK:  /* Finishing: There's more work to do */
            break;
        case BZ_STREAM_END: /* Finishing: all done */
            /* Only occurs in finishing case */
            return (ARCHIVE_OK);
        default:
            /* Any other return value indicates an error */
            archive_set_error(&a->archive,
                ARCHIVE_ERRNO_PROGRAMMER,
                "Bzip2 compression failed;"
                " BZ2_bzCompress() returned %d",
                ret);
            return (ARCHIVE_FATAL);
        }
    }
}

#endif /* HAVE_BZLIB_H */

--- NEW FILE: archive_read_support_format_raw.c ---
/*-
 * Copyright (c) 2003-2009 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "archive_platform.h"
__FBSDID("$FreeBSD$");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_private.h"

struct raw_info {
    int64_t offset; /* Current position in the file. */
    int     end_of_file;
};

static int  archive_read_format_raw_bid(struct archive_read *);
static int  archive_read_format_raw_cleanup(struct archive_read *);
static int  archive_read_format_raw_read_data(struct archive_read *,
            const void **, size_t *, off_t *);
static int  archive_read_format_raw_read_data_skip(struct archive_read *);
static int  archive_read_format_raw_read_header(struct archive_read *,
            struct archive_entry *);

int
archive_read_support_format_raw(struct archive *_a)
{
    struct raw_info *info;
    struct archive_read *a = (struct archive_read *)_a;
    int r;

    info = (struct raw_info *)calloc(1, sizeof(*info));
    if (info == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate raw_info data");
        return (ARCHIVE_FATAL);
    }

    r = __archive_read_register_format(a,
        info,
        "raw",
        archive_read_format_raw_bid,
        NULL,
        archive_read_format_raw_read_header,
        archive_read_format_raw_read_data,
        archive_read_format_raw_read_data_skip,
        archive_read_format_raw_cleanup);
    if (r != ARCHIVE_OK)
        free(info);
    return (r);
}

/*
 * Bid 1 if this is a non-empty file.  Anyone who can really support
 * this should outbid us, so it should generally be safe to use "raw"
 * in conjunction with other formats.  But, this could really confuse
 * folks if there are bid errors or minor file damage, so we don't
 * include "raw" as part of support_format_all().
 */
static int
archive_read_format_raw_bid(struct archive_read *a)
{
    const char *p;

    if ((p = __archive_read_ahead(a, 1, NULL)) == NULL)
        return (-1);
    return (1);
}

/*
 * Mock up a fake header.
 */
static int
archive_read_format_raw_read_header(struct archive_read *a,
    struct archive_entry *entry)
{
    struct raw_info *info;

    info = (struct raw_info *)(a->format->data);
    if (info->end_of_file)
        return (ARCHIVE_EOF);

    a->archive.archive_format = ARCHIVE_FORMAT_RAW;
    a->archive.archive_format_name = "Raw data";
    archive_entry_set_pathname(entry, "data");
    /* XXX should we set mode to mimic a regular file? XXX */
    /* I'm deliberately leaving most fields unset here. */
    return (ARCHIVE_OK);
}

static int
archive_read_format_raw_read_data(struct archive_read *a,
    const void **buff, size_t *size, off_t *offset)
{
    struct raw_info *info;
    ssize_t avail;

    info = (struct raw_info *)(a->format->data);
    if (info->end_of_file)
        return (ARCHIVE_EOF);

    /* Get whatever bytes are immediately available. */
    *buff = __archive_read_ahead(a, 1, &avail);
    if (avail > 0) {
        /* Consume and return the bytes we just read */
        __archive_read_consume(a, avail);
        *size = avail;
        *offset = info->offset;
        info->offset += *size;
        return (ARCHIVE_OK);
    } else if (0 == avail) {
        /* Record and return end-of-file. */
        info->end_of_file = 1;
        *size = 0;
        *offset = info->offset;
        return (ARCHIVE_EOF);
    } else {
        /* Record and return an error. */
        *size = 0;
        *offset = info->offset;
        return (avail);
    }
    return (ARCHIVE_OK);
}

static int
archive_read_format_raw_read_data_skip(struct archive_read *a)
{
    struct raw_info *info;
    off_t bytes_skipped;
    int64_t request = 1024 * 1024 * 1024UL; /* Skip 1 GB at a time. */

    info = (struct raw_info *)(a->format->data);
    if (info->end_of_file)
        return (ARCHIVE_EOF);
    info->end_of_file = 1;

    for (;;) {
        bytes_skipped = __archive_read_skip_lenient(a, request);
        if (bytes_skipped < 0)
            return (ARCHIVE_FATAL);
        if (bytes_skipped < request)
            return (ARCHIVE_OK);
        /* We skipped all the bytes we asked for.  There might
         * be more, so try again. */
    }
}

static int
archive_read_format_raw_cleanup(struct archive_read *a)
{
    struct raw_info *info;

    info = (struct raw_info *)(a->format->data);
    free(info);
    a->format->data = NULL;
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_string.h ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.13 2008/12/06 05:56:43 kientzle Exp $
 *
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef ARCHIVE_STRING_H_INCLUDED
#define ARCHIVE_STRING_H_INCLUDED

#include <stdarg.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>  /* required for wchar_t on some systems */
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_WCHAR_H
#include <wchar.h>
#endif

/*
 * Basic resizable/reusable string support a la Java's "StringBuffer."
 *
 * Unlike sbuf(9), the buffers here are fully reusable and track the
 * length throughout.
 *
 * Note that all visible symbols here begin with "__archive" as they
 * are internal symbols not intended for anyone outside of this library
 * to see or use.
 */

struct archive_string {
    char    *s;  /* Pointer to the storage */
    size_t   length; /* Length of 's' */
    size_t   buffer_length; /* Length of malloc-ed storage */
};

/* Initialize an archive_string object on the stack or elsewhere. */
#define archive_string_init(a)  \
    do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)

/* Append a C char to an archive_string, resizing as necessary. */
struct archive_string *
__archive_strappend_char(struct archive_string *, char);
#define archive_strappend_char __archive_strappend_char

/* Convert a wide-char string to UTF-8 and append the result. */
struct archive_string *
__archive_strappend_w_utf8(struct archive_string *, const wchar_t *);
#define archive_strappend_w_utf8    __archive_strappend_w_utf8

/* Convert a wide-char string to current locale and append the result. */
/* Returns NULL if conversion fails. */
struct archive_string *
__archive_strappend_w_mbs(struct archive_string *, const wchar_t *);
#define archive_strappend_w_mbs __archive_strappend_w_mbs

/* Basic append operation. */
struct archive_string *
__archive_string_append(struct archive_string *as, const char *p, size_t s);

/* Copy one archive_string to another */
void
__archive_string_copy(struct archive_string *dest, struct archive_string *src);
#define archive_string_copy(dest, src) \
    __archive_string_copy(dest, src)

/* Concatenate one archive_string to another */
void
__archive_string_concat(struct archive_string *dest, struct archive_string *src);
#define archive_string_concat(dest, src) \
    __archive_string_concat(dest, src)

/* Ensure that the underlying buffer is at least as large as the request. */
struct archive_string *
__archive_string_ensure(struct archive_string *, size_t);
#define archive_string_ensure __archive_string_ensure

/* Append C string, which may lack trailing \0. */
/* The source is declared void * here because this gets used with
 * "signed char *", "unsigned char *" and "char *" arguments.
 * Declaring it "char *" as with some of the other functions just
 * leads to a lot of extra casts. */
struct archive_string *
__archive_strncat(struct archive_string *, const void *, size_t);
#define archive_strncat  __archive_strncat

/* Append a C string to an archive_string, resizing as necessary. */
#define archive_strcat(as,p) __archive_string_append((as),(p),strlen(p))

/* Copy a C string to an archive_string, resizing as necessary. */
#define archive_strcpy(as,p) \
    ((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p)))

/* Copy a C string to an archive_string with limit, resizing as necessary. */
#define archive_strncpy(as,p,l) \
    ((as)->length=0, archive_strncat((as), (p), (l)))

/* Return length of string. */
#define archive_strlen(a) ((a)->length)

/* Set string length to zero. */
#define archive_string_empty(a) ((a)->length = 0)

/* Release any allocated storage resources. */
void    __archive_string_free(struct archive_string *);
#define archive_string_free  __archive_string_free

/* Like 'vsprintf', but resizes the underlying string as necessary. */
void    __archive_string_vsprintf(struct archive_string *, const char *,
        va_list);
#define archive_string_vsprintf __archive_string_vsprintf

void    __archive_string_sprintf(struct archive_string *, const char *, ...);
#define archive_string_sprintf  __archive_string_sprintf

/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.
 * Returns NULL if conversion failed in any way. */
wchar_t *__archive_string_utf8_w(struct archive_string *as);


#endif

--- NEW FILE: filter_fork_windows.c ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

#if defined(_WIN32) && !defined(__CYGWIN__)

#include "filter_fork.h"

pid_t
__archive_create_child(const char *path, int *child_stdin, int *child_stdout)
{
    HANDLE childStdout[2], childStdin[2], childStdinWr, childStdoutRd;
    SECURITY_ATTRIBUTES secAtts;
    STARTUPINFO staInfo;
    PROCESS_INFORMATION childInfo;
    char cmd[MAX_PATH];
    DWORD mode;

    secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
    secAtts.bInheritHandle = TRUE;
    secAtts.lpSecurityDescriptor = NULL;
    if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0)
        goto fail;
    if (DuplicateHandle(GetCurrentProcess(), childStdout[0],
        GetCurrentProcess(), &childStdoutRd, 0, FALSE,
        DUPLICATE_SAME_ACCESS) == 0) {
        CloseHandle(childStdout[0]);
        CloseHandle(childStdout[1]);
        goto fail;
    }
    CloseHandle(childStdout[0]);

    if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0) {
        CloseHandle(childStdoutRd);
        CloseHandle(childStdout[1]);
        goto fail;
    }

    if (DuplicateHandle(GetCurrentProcess(), childStdin[1],
        GetCurrentProcess(), &childStdinWr, 0, FALSE,
        DUPLICATE_SAME_ACCESS) == 0) {
        CloseHandle(childStdoutRd);
        CloseHandle(childStdout[1]);
        CloseHandle(childStdin[0]);
        CloseHandle(childStdin[1]);
        goto fail;
    }
    CloseHandle(childStdin[1]);

    memset(&staInfo, 0, sizeof(staInfo));
    staInfo.cb = sizeof(staInfo);
    staInfo.hStdOutput = childStdout[1];
    staInfo.hStdInput = childStdin[0];
    staInfo.wShowWindow = SW_HIDE;
    staInfo.dwFlags = STARTF_USEFILLATTRIBUTE | STARTF_USECOUNTCHARS |
        STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    strncpy(cmd, path, sizeof(cmd)-1);
    cmd[sizeof(cmd)-1] = '\0';
    if (CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL,
        &staInfo, &childInfo) == 0) {
        CloseHandle(childStdoutRd);
        CloseHandle(childStdout[1]);
        CloseHandle(childStdin[0]);
        CloseHandle(childStdinWr);
        goto fail;
    }
    WaitForInputIdle(childInfo.hProcess, INFINITE);
    CloseHandle(childInfo.hProcess);
    CloseHandle(childInfo.hThread);

    mode = PIPE_NOWAIT;
    SetNamedPipeHandleState(childStdoutRd, &mode, NULL, NULL);
    *child_stdout = _open_osfhandle((intptr_t)childStdoutRd, _O_RDONLY);
    *child_stdin = _open_osfhandle((intptr_t)childStdinWr, _O_WRONLY);

    return (childInfo.dwProcessId);

fail:
    return (-1);
}

void
__archive_check_child(int in, int out)
{
    Sleep(100);
}

#endif /* _WIN32 && !__CYGWIN__ */

--- NEW FILE: archive_entry_private.h ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.6 2008/09/30 03:53:03 kientzle Exp $
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED

#include "archive_string.h"

/*
 * Handle wide character (i.e., Unicode) and non-wide character
 * strings transparently.
 */

struct aes {
    struct archive_string aes_mbs;
    struct archive_string aes_utf8;
    const wchar_t *aes_wcs;
    /* Bitmap of which of the above are valid.  Because we're lazy
     * about malloc-ing and reusing the underlying storage, we
     * can't rely on NULL pointers to indicate whether a string
     * has been set. */
    int aes_set;
#define AES_SET_MBS 1
#define AES_SET_UTF8 2
#define AES_SET_WCS 4
};

struct ae_acl {
    struct ae_acl *next;
    int type;           /* E.g., access or default */
    int tag;            /* E.g., user/group/other/mask */
    int permset;        /* r/w/x bits */
    int id;         /* uid/gid for user/group */
    struct aes name;        /* uname/gname */
};

struct ae_xattr {
    struct ae_xattr *next;

    char    *name;
    void    *value;
    size_t  size;
};

/*
 * Description of an archive entry.
 *
 * Basically, this is a "struct stat" with a few text fields added in.
 *
 * TODO: Add "comment", "charset", and possibly other entries
 * that are supported by "pax interchange" format.  However, GNU, ustar,
 * cpio, and other variants don't support these features, so they're not an
 * excruciatingly high priority right now.
 *
 * TODO: "pax interchange" format allows essentially arbitrary
 * key/value attributes to be attached to any entry.  Supporting
 * such extensions may make this library useful for special
 * applications (e.g., a package manager could attach special
 * package-management attributes to each entry).  There are tricky
 * API issues involved, so this is not going to happen until
 * there's a real demand for it.
 *
 * TODO: Design a good API for handling sparse files.
 */
struct archive_entry {
    /*
     * Note that ae_stat.st_mode & AE_IFMT  can be  0!
     *
     * This occurs when the actual file type of the object is not
     * in the archive.  For example, 'tar' archives store
     * hardlinks without marking the type of the underlying
     * object.
     */

    /*
     * Read archive_entry_copy_stat.c for an explanation of why I
     * don't just use "struct stat" instead of "struct aest" here
     * and why I have this odd pointer to a separately-allocated
     * struct stat.
     */
    void *stat;
    int  stat_valid; /* Set to 0 whenever a field in aest changes. */

    struct aest {
        int64_t     aest_atime;
        uint32_t    aest_atime_nsec;
        int64_t     aest_ctime;
        uint32_t    aest_ctime_nsec;
        int64_t     aest_mtime;
        uint32_t    aest_mtime_nsec;
        int64_t     aest_birthtime;
        uint32_t    aest_birthtime_nsec;
        gid_t       aest_gid;
        int64_t     aest_ino;
        mode_t      aest_mode;
        uint32_t    aest_nlink;
        uint64_t    aest_size;
        uid_t       aest_uid;
        /*
         * Because converting between device codes and
         * major/minor values is platform-specific and
         * inherently a bit risky, we only do that conversion
         * lazily.  That way, we will do a better job of
         * preserving information in those cases where no
         * conversion is actually required.
         */
        int     aest_dev_is_broken_down;
        dev_t       aest_dev;
        dev_t       aest_devmajor;
        dev_t       aest_devminor;
        int     aest_rdev_is_broken_down;
        dev_t       aest_rdev;
        dev_t       aest_rdevmajor;
        dev_t       aest_rdevminor;
    } ae_stat;

    int ae_set; /* bitmap of fields that are currently set */
#define AE_SET_HARDLINK 1
#define AE_SET_SYMLINK  2
#define AE_SET_ATIME    4
#define AE_SET_CTIME    8
#define AE_SET_MTIME    16
#define AE_SET_BIRTHTIME 32
#define AE_SET_SIZE 64

    /*
     * Use aes here so that we get transparent mbs<->wcs conversions.
     */
    struct aes ae_fflags_text;  /* Text fflags per fflagstostr(3) */
    unsigned long ae_fflags_set;        /* Bitmap fflags */
    unsigned long ae_fflags_clear;
    struct aes ae_gname;        /* Name of owning group */
    struct aes ae_hardlink; /* Name of target for hardlink */
    struct aes ae_pathname; /* Name of entry */
    struct aes ae_symlink;      /* symlink contents */
    struct aes ae_uname;        /* Name of owner */

    /* Not used within libarchive; useful for some clients. */
    struct aes ae_sourcepath;   /* Path this entry is sourced from. */

    /* ACL support. */
    struct ae_acl   *acl_head;
    struct ae_acl   *acl_p;
    int      acl_state; /* See acl_next for details. */
    wchar_t     *acl_text_w;

    /* extattr support. */
    struct ae_xattr *xattr_head;
    struct ae_xattr *xattr_p;

    /* Miscellaneous. */
    char         strmode[12];
};


#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */

--- NEW FILE: archive_string.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.17 2008/12/06 05:56:43 kientzle Exp $");

/*
 * Basic resizable string support, to simplify manipulating arbitrary-sized
 * strings while minimizing heap activity.
 */

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_WCHAR_H
#include <wchar.h>
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <windows.h>
#endif

#include "archive_private.h"
#include "archive_string.h"

struct archive_string *
__archive_string_append(struct archive_string *as, const char *p, size_t s)
{
    if (__archive_string_ensure(as, as->length + s + 1) == NULL)
        __archive_errx(1, "Out of memory");
    memcpy(as->s + as->length, p, s);
    as->s[as->length + s] = 0;
    as->length += s;
    return (as);
}

void
__archive_string_copy(struct archive_string *dest, struct archive_string *src)
{
    if (src->length == 0)
        dest->length = 0;
    else {
        if (__archive_string_ensure(dest, src->length + 1) == NULL)
            __archive_errx(1, "Out of memory");
        memcpy(dest->s, src->s, src->length);
        dest->length = src->length;
        dest->s[dest->length] = 0;
    }
}

void
__archive_string_concat(struct archive_string *dest, struct archive_string *src)
{
    if (src->length > 0) {
        if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL)
            __archive_errx(1, "Out of memory");
        memcpy(dest->s + dest->length, src->s, src->length);
        dest->length += src->length;
        dest->s[dest->length] = 0;
    }
}

void
__archive_string_free(struct archive_string *as)
{
    as->length = 0;
    as->buffer_length = 0;
    if (as->s != NULL) {
        free(as->s);
        as->s = NULL;
    }
}

/* Returns NULL on any allocation failure. */
struct archive_string *
__archive_string_ensure(struct archive_string *as, size_t s)
{
    /* If buffer is already big enough, don't reallocate. */
    if (as->s && (s <= as->buffer_length))
        return (as);

    /*
     * Growing the buffer at least exponentially ensures that
     * append operations are always linear in the number of
     * characters appended.  Using a smaller growth rate for
     * larger buffers reduces memory waste somewhat at the cost of
     * a larger constant factor.
     */
    if (as->buffer_length < 32)
        /* Start with a minimum 32-character buffer. */
        as->buffer_length = 32;
    else if (as->buffer_length < 8192)
        /* Buffers under 8k are doubled for speed. */
        as->buffer_length += as->buffer_length;
    else {
        /* Buffers 8k and over grow by at least 25% each time. */
        size_t old_length = as->buffer_length;
        as->buffer_length += as->buffer_length / 4;
        /* Be safe: If size wraps, release buffer and return NULL. */
        if (as->buffer_length < old_length) {
            free(as->s);
            as->s = NULL;
            return (NULL);
        }
    }
    /*
     * The computation above is a lower limit to how much we'll
     * grow the buffer.  In any case, we have to grow it enough to
     * hold the request.
     */
    if (as->buffer_length < s)
        as->buffer_length = s;
    /* Now we can reallocate the buffer. */
    as->s = (char *)realloc(as->s, as->buffer_length);
    if (as->s == NULL)
        return (NULL);
    return (as);
}

struct archive_string *
__archive_strncat(struct archive_string *as, const void *_p, size_t n)
{
    size_t s;
    const char *p, *pp;

    p = (const char *)_p;

    /* Like strlen(p), except won't examine positions beyond p[n]. */
    s = 0;
    pp = p;
    while (*pp && s < n) {
        pp++;
        s++;
    }
    return (__archive_string_append(as, p, s));
}

struct archive_string *
__archive_strappend_char(struct archive_string *as, char c)
{
    return (__archive_string_append(as, &c, 1));
}

/*
 * Translates a wide character string into UTF-8 and appends
 * to the archive_string.  Note: returns NULL if conversion fails,
 * but still leaves a best-effort conversion in the argument as.
 */
struct archive_string *
__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w)
{
    char *p;
    unsigned wc;
    char buff[256];
    struct archive_string *return_val = as;

    /*
     * Convert one wide char at a time into 'buff', whenever that
     * fills, append it to the string.
     */
    p = buff;
    while (*w != L'\0') {
        /* Flush the buffer when we have <=16 bytes free. */
        /* (No encoding has a single character >16 bytes.) */
        if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) {
            *p = '\0';
            archive_strcat(as, buff);
            p = buff;
        }
        wc = *w++;
        /* If this is a surrogate pair, assemble the full code point.*/
        /* Note: wc must not be wchar_t here, because the full code
         * point can be more than 16 bits! */
        if (wc >= 0xD800 && wc <= 0xDBff
            && *w >= 0xDC00 && *w <= 0xDFFF) {
            wc -= 0xD800;
            wc *= 0x400;
            wc += (*w - 0xDC00);
            wc += 0x10000;
            ++w;
        }
        /* Translate code point to UTF8 */
        if (wc <= 0x7f) {
            *p++ = (char)wc;
        } else if (wc <= 0x7ff) {
            *p++ = 0xc0 | ((wc >> 6) & 0x1f);
            *p++ = 0x80 | (wc & 0x3f);
        } else if (wc <= 0xffff) {
            *p++ = 0xe0 | ((wc >> 12) & 0x0f);
            *p++ = 0x80 | ((wc >> 6) & 0x3f);
            *p++ = 0x80 | (wc & 0x3f);
        } else if (wc <= 0x1fffff) {
            *p++ = 0xf0 | ((wc >> 18) & 0x07);
            *p++ = 0x80 | ((wc >> 12) & 0x3f);
            *p++ = 0x80 | ((wc >> 6) & 0x3f);
            *p++ = 0x80 | (wc & 0x3f);
        } else {
            /* Unicode has no codes larger than 0x1fffff. */
            /* TODO: use \uXXXX escape here instead of ? */
            *p++ = '?';
            return_val = NULL;
        }
    }
    *p = '\0';
    archive_strcat(as, buff);
    return (return_val);
}

static int
utf8_to_unicode(int *pwc, const char *s, size_t n)
{
        int ch;

        /*
     * Decode 1-4 bytes depending on the value of the first byte.
     */
        ch = (unsigned char)*s;
    if (ch == 0) {
        return (0); /* Standard:  return 0 for end-of-string. */
    }
    if ((ch & 0x80) == 0) {
                *pwc = ch & 0x7f;
        return (1);
        }
    if ((ch & 0xe0) == 0xc0) {
        if (n < 2)
            return (-1);
        if ((s[1] & 0xc0) != 0x80) return (-1);
                *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
        return (2);
        }
    if ((ch & 0xf0) == 0xe0) {
        if (n < 3)
            return (-1);
        if ((s[1] & 0xc0) != 0x80) return (-1);
        if ((s[2] & 0xc0) != 0x80) return (-1);
                *pwc = ((ch & 0x0f) << 12)
            | ((s[1] & 0x3f) << 6)
            | (s[2] & 0x3f);
        return (3);
        }
    if ((ch & 0xf8) == 0xf0) {
        if (n < 4)
            return (-1);
        if ((s[1] & 0xc0) != 0x80) return (-1);
        if ((s[2] & 0xc0) != 0x80) return (-1);
        if ((s[3] & 0xc0) != 0x80) return (-1);
                *pwc = ((ch & 0x07) << 18)
            | ((s[1] & 0x3f) << 12)
            | ((s[2] & 0x3f) << 6)
            | (s[3] & 0x3f);
        return (4);
        }
    /* Invalid first byte. */
    return (-1);
}

/*
 * Return a wide-character Unicode string by converting this archive_string
 * from UTF-8.  We assume that systems with 16-bit wchar_t always use
 * UTF16 and systems with 32-bit wchar_t can accept UCS4.
 */
wchar_t *
__archive_string_utf8_w(struct archive_string *as)
{
    wchar_t *ws, *dest;
    int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */
    const char *src;
    int n;
    int err;

    ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t));
    if (ws == NULL)
        __archive_errx(1, "Out of memory");
    err = 0;
    dest = ws;
    src = as->s;
    while (*src != '\0') {
        n = utf8_to_unicode(&wc, src, 8);
        if (n == 0)
            break;
        if (n < 0) {
            free(ws);
            return (NULL);
        }
        src += n;
        if (wc >= 0xDC00 && wc <= 0xDBFF) {
            /* This is a leading surrogate; some idiot
             * has translated UTF16 to UTF8 without combining
             * surrogates; rebuild the full code point before
             * continuing. */
            n = utf8_to_unicode(&wc2, src, 8);
            if (n < 0) {
                free(ws);
                return (NULL);
            }
            if (n == 0) /* Ignore the leading surrogate */
                break;
            if (wc2 < 0xDC00 || wc2 > 0xDFFF) {
                /* If the second character isn't a
                 * trailing surrogate, then someone
                 * has really screwed up and this is
                 * invalid. */
                free(ws);
                return (NULL);
            } else {
                src += n;
                wc -= 0xD800;
                wc *= 0x400;
                wc += wc2 - 0xDC00;
                wc += 0x10000;
            }
        }
        if ((sizeof(wchar_t) < 4) && (wc > 0xffff)) {
            /* We have a code point that won't fit into a
             * wchar_t; convert it to a surrogate pair. */
            wc -= 0x10000;
            *dest++ = ((wc >> 10) & 0x3ff) + 0xD800;
            *dest++ = (wc & 0x3ff) + 0xDC00;
        } else
            *dest++ = wc;
    }
    *dest++ = L'\0';
    return (ws);
}

#if defined(_WIN32) && !defined(__CYGWIN__)

/*
 * Translates a wide character string into current locale character set
 * and appends to the archive_string.  Note: returns NULL if conversion
 * fails.
 *
 * Win32 builds use WideCharToMultiByte from the Windows API.
 * (Maybe Cygwin should too?  WideCharToMultiByte will know a
 * lot more about local character encodings than the wcrtomb()
 * wrapper is going to know.)
 */
struct archive_string *
__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
{
    char *p;
    int l, wl;
    BOOL useDefaultChar = FALSE;

    wl = (int)wcslen(w);
    l = wl * 4 + 4;
    p = malloc(l);
    if (p == NULL)
        __archive_errx(1, "Out of memory");
    /* To check a useDefaultChar is to simulate error handling of
     * the my_wcstombs() which is running on non Windows system with
     * wctomb().
     * And to set NULL for last argument is necessary when a codepage
     * is not CP_ACP(current locale).
     */
    l = WideCharToMultiByte(CP_ACP, 0, w, wl, p, l, NULL, &useDefaultChar);
    if (l == 0) {
        free(p);
        return (NULL);
    }
    __archive_string_append(as, p, l);
    free(p);
    return (as);
}

#else

/*
 * Translates a wide character string into current locale character set
 * and appends to the archive_string.  Note: returns NULL if conversion
 * fails.
 *
 * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
 * one character at a time.  If a non-Windows platform doesn't have
 * either of these, fall back to the built-in UTF8 conversion.
 */
struct archive_string *
__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
{
#if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB)
    /* If there's no built-in locale support, fall back to UTF8 always. */
    return __archive_strappend_w_utf8(as, w);
#else
    /* We cannot use the standard wcstombs() here because it
     * cannot tell us how big the output buffer should be.  So
     * I've built a loop around wcrtomb() or wctomb() that
     * converts a character at a time and resizes the string as
     * needed.  We prefer wcrtomb() when it's available because
     * it's thread-safe. */
    int n;
    char *p;
    char buff[256];
#if HAVE_WCRTOMB
    mbstate_t shift_state;

    memset(&shift_state, 0, sizeof(shift_state));
#else
    /* Clear the shift state before starting. */
    wctomb(NULL, L'\0');
#endif

    /*
     * Convert one wide char at a time into 'buff', whenever that
     * fills, append it to the string.
     */
    p = buff;
    while (*w != L'\0') {
        /* Flush the buffer when we have <=16 bytes free. */
        /* (No encoding has a single character >16 bytes.) */
        if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - MB_CUR_MAX)) {
            *p = '\0';
            archive_strcat(as, buff);
            p = buff;
        }
#if HAVE_WCRTOMB
        n = wcrtomb(p, *w++, &shift_state);
#else
        n = wctomb(p, *w++);
#endif
        if (n == -1)
            return (NULL);
        p += n;
    }
    *p = '\0';
    archive_strcat(as, buff);
    return (as);
#endif
}

#endif /* _WIN32 && ! __CYGWIN__ */

--- NEW FILE: archive_write_set_format.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format.c,v 1.6 2008/08/31 07:21:46 kientzle Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#include "archive.h"
#include "archive_private.h"

/* A table that maps format codes to functions. */
static
struct { int code; int (*setter)(struct archive *); } codes[] =
{
    { ARCHIVE_FORMAT_CPIO,      archive_write_set_format_cpio },
    { ARCHIVE_FORMAT_CPIO_SVR4_NOCRC,   archive_write_set_format_cpio_newc },
    { ARCHIVE_FORMAT_CPIO_POSIX,    archive_write_set_format_cpio },
    { ARCHIVE_FORMAT_MTREE,     archive_write_set_format_mtree },
    { ARCHIVE_FORMAT_SHAR,      archive_write_set_format_shar },
    { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar },
    { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump },
    { ARCHIVE_FORMAT_TAR,   archive_write_set_format_pax_restricted },
    { ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax },
    { ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,
                archive_write_set_format_pax_restricted },
    { ARCHIVE_FORMAT_TAR_USTAR, archive_write_set_format_ustar },
    { ARCHIVE_FORMAT_ZIP,   archive_write_set_format_zip },
    { 0,        NULL }
};

int
archive_write_set_format(struct archive *a, int code)
{
    int i;

    for (i = 0; codes[i].code != 0; i++) {
        if (code == codes[i].code)
            return ((codes[i].setter)(a));
    }

    archive_set_error(a, EINVAL, "No such format");
    return (ARCHIVE_FATAL);
}

--- NEW FILE: cpio.5 ---
.\" Copyright (c) 2007 Tim Kientzle
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: src/lib/libarchive/cpio.5,v 1.2 2008/05/26 17:00:23 kientzle Exp $
.\"
.Dd October 5, 2007
.Dt CPIO 5
.Os
.Sh NAME
.Nm cpio
.Nd format of cpio archive files
.Sh DESCRIPTION
The
.Nm
archive format collects any number of files, directories, and other
file system objects (symbolic links, device nodes, etc.) into a single
stream of bytes.
.Ss General Format
Each file system object in a
.Nm
archive comprises a header record with basic numeric metadata
followed by the full pathname of the entry and the file data.
The header record stores a series of integer values that generally
follow the fields in
.Va struct stat .
(See
.Xr stat 2
for details.)
The variants differ primarily in how they store those integers
(binary, octal, or hexadecimal).
The header is followed by the pathname of the
entry (the length of the pathname is stored in the header)
and any file data.
The end of the archive is indicated by a special record with
the pathname
.Dq TRAILER!!! .
.Ss PWB format
XXX Any documentation of the original PWB/UNIX 1.0 format? XXX
.Ss Old Binary Format
The old binary
.Nm
format stores numbers as 2-byte and 4-byte binary values.
Each entry begins with a header in the following format:
.Bd -literal -offset indent
struct header_old_cpio {
        unsigned short   c_magic;
        unsigned short   c_dev;
        unsigned short   c_ino;
        unsigned short   c_mode;
        unsigned short   c_uid;
        unsigned short   c_gid;
        unsigned short   c_nlink;
        unsigned short   c_rdev;
    unsigned short   c_mtime[2];
        unsigned short   c_namesize;
    unsigned short   c_filesize[2];
};
.Ed
.Pp
The
.Va unsigned short
fields here are 16-bit integer values; the
.Va unsigned int
fields are 32-bit integer values.
The fields are as follows
.Bl -tag -width indent
.It Va magic
The integer value octal 070707.
This value can be used to determine whether this archive is
written with little-endian or big-endian integers.
.It Va dev , Va ino
The device and inode numbers from the disk.
These are used by programs that read
.Nm
archives to determine when two entries refer to the same file.
Programs that synthesize
.Nm
archives should be careful to set these to distinct values for each entry.
.It Va mode
The mode specifies both the regular permissions and the file type.
It consists of several bit fields as follows:
.Bl -tag -width "MMMMMMM" -compact
.It 0170000
This masks the file type bits.
.It 0140000
File type value for sockets.
.It 0120000
File type value for symbolic links.
For symbolic links, the link body is stored as file data.
.It 0100000
File type value for regular files.
.It 0060000
File type value for block special devices.
.It 0040000
File type value for directories.
.It 0020000
File type value for character special devices.
.It 0010000
File type value for named pipes or FIFOs.
.It 0004000
SUID bit.
.It 0002000
SGID bit.
.It 0001000
Sticky bit.
On some systems, this modifies the behavior of executables and/or directories.
.It 0000777
The lower 9 bits specify read/write/execute permissions
for world, group, and user following standard POSIX conventions.
.El
.It Va uid , Va gid
The numeric user id and group id of the owner.
.It Va nlink
The number of links to this file.
Directories always have a value of at least two here.
Note that hardlinked files include file data with every copy in the archive.
.It Va rdev
For block special and character special entries,
this field contains the associated device number.
For all other entry types, it should be set to zero by writers
and ignored by readers.
.It Va mtime
Modification time of the file, indicated as the number
of seconds since the start of the epoch,
00:00:00 UTC January 1, 1970.
The four-byte integer is stored with the most-significant 16 bits first
followed by the least-significant 16 bits.
Each of the two 16 bit values are stored in machine-native byte order.
.It Va namesize
The number of bytes in the pathname that follows the header.
This count includes the trailing NUL byte.
.It Va filesize
The size of the file.
Note that this archive format is limited to
four gigabyte file sizes.
See
.Va mtime
above for a description of the storage of four-byte integers.
.El
.Pp
The pathname immediately follows the fixed header.
If the
.Cm namesize
is odd, an additional NUL byte is added after the pathname.
The file data is then appended, padded with NUL
bytes to an even length.
.Pp
Hardlinked files are not given special treatment;
the full file contents are included with each copy of the
file.
.Ss Portable ASCII Format
.St -susv2
standardized an ASCII variant that is portable across all
platforms.
It is commonly known as the
.Dq old character
format or as the
.Dq odc
format.
It stores the same numeric fields as the old binary format, but
represents them as 6-character or 11-character octal values.
.Bd -literal -offset indent
struct cpio_odc_header {
        char    c_magic[6];
        char    c_dev[6];
        char    c_ino[6];
        char    c_mode[6];
        char    c_uid[6];
        char    c_gid[6];
        char    c_nlink[6];
        char    c_rdev[6];
        char    c_mtime[11];
        char    c_namesize[6];
        char    c_filesize[11];
};
.Ed
.Pp
The fields are identical to those in the old binary format.
The name and file body follow the fixed header.
Unlike the old binary format, there is no additional padding
after the pathname or file contents.
If the files being archived are themselves entirely ASCII, then
the resulting archive will be entirely ASCII, except for the
NUL byte that terminates the name field.
.Ss New ASCII Format
The "new" ASCII format uses 8-byte hexadecimal fields for
all numbers and separates device numbers into separate fields
for major and minor numbers.
.Bd -literal -offset indent
struct cpio_newc_header {
        char    c_magic[6];
        char    c_ino[8];
        char    c_mode[8];
        char    c_uid[8];
        char    c_gid[8];
        char    c_nlink[8];
        char    c_mtime[8];
        char    c_filesize[8];
        char    c_devmajor[8];
        char    c_devminor[8];
        char    c_rdevmajor[8];
        char    c_rdevminor[8];
        char    c_namesize[8];
        char    c_check[8];
};
.Ed
.Pp
Except as specified below, the fields here match those specified
for the old binary format above.
.Bl -tag -width indent
.It Va magic
The string
.Dq 070701 .
.It Va check
This field is always set to zero by writers and ignored by readers.
See the next section for more details.
.El
.Pp
The pathname is followed by NUL bytes so that the total size
of the fixed header plus pathname is a multiple of four.
Likewise, the file data is padded to a multiple of four bytes.
Note that this format supports only 4 gigabyte files (unlike the
older ASCII format, which supports 8 gigabyte files).
.Pp
In this format, hardlinked files are handled by setting the
filesize to zero for each entry except the last one that
appears in the archive.
.Ss New CRC Format
The CRC format is identical to the new ASCII format described
in the previous section except that the magic field is set
to
.Dq 070702
and the
.Va check
field is set to the sum of all bytes in the file data.
This sum is computed treating all bytes as unsigned values
and using unsigned arithmetic.
Only the least-significant 32 bits of the sum are stored.
.Ss HP variants
The
.Nm cpio
implementation distributed with HPUX used XXXX but stored
device numbers differently XXX.
.Ss Other Extensions and Variants
Sun Solaris uses additional file types to store extended file
data, including ACLs and extended attributes, as special
entries in cpio archives.
.Pp
XXX Others? XXX
.Sh BUGS
The
.Dq CRC
format is mis-named, as it uses a simple checksum and
not a cyclic redundancy check.
.Pp
The old binary format is limited to 16 bits for user id,
group id, device, and inode numbers.
It is limited to 4 gigabyte file sizes.
.Pp
The old ASCII format is limited to 18 bits for
the user id, group id, device, and inode numbers.
It is limited to 8 gigabyte file sizes.
.Pp
The new ASCII format is limited to 4 gigabyte file sizes.
.Pp
None of the cpio formats store user or group names,
which are essential when moving files between systems with
dissimilar user or group numbering.
.Pp
Especially when writing older cpio variants, it may be necessary
to map actual device/inode values to synthesized values that
fit the available fields.
With very large filesystems, this may be necessary even for
the newer formats.
.Sh SEE ALSO
.Xr cpio 1 ,
.Xr tar 5
.Sh STANDARDS
The
.Nm cpio
utility is no longer a part of POSIX or the Single Unix Standard.
It last appeared in
.St -susv2 .
It has been supplanted in subsequent standards by
.Xr pax 1 .
The portable ASCII format is currently part of the specification for the
.Xr pax 1
utility.
.Sh HISTORY
The original cpio utility was written by Dick Haight
while working in AT&T's Unix Support Group.
It appeared in 1977 as part of PWB/UNIX 1.0, the
.Dq Programmer's Work Bench
derived from
.At v6
that was used internally at AT&T.
Both the old binary and old character formats were in use
by 1980, according to the System III source released
by SCO under their
.Dq Ancient Unix
license.
The character format was adopted as part of
.St -p1003.1-88 .
XXX when did "newc" appear?  Who invented it?  When did HP come out with their variant?  When did Sun introduce ACLs and extended attributes? XXX

--- NEW FILE: archive_read_support_format_zip.c ---
/*-
 * Copyright (c) 2004 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.28 2008/12/06 06:45:15 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <time.h>
#ifdef HAVE_ZLIB_H
#include <cm_zlib.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_private.h"
#include "archive_endian.h"

#ifndef HAVE_ZLIB_H
#include "archive_crc32.h"
#endif

struct zip {
    /* entry_bytes_remaining is the number of bytes we expect. */
    int64_t         entry_bytes_remaining;
    int64_t         entry_offset;

    /* These count the number of bytes actually read for the entry. */
    int64_t         entry_compressed_bytes_read;
    int64_t         entry_uncompressed_bytes_read;

    /* Running CRC32 of the decompressed data */
    unsigned long       entry_crc32;

    unsigned        version;
    unsigned        system;
    unsigned        flags;
    unsigned        compression;
    const char *        compression_name;
    time_t          mtime;
    time_t          ctime;
    time_t          atime;
    mode_t          mode;
    uid_t           uid;
    gid_t           gid;

    /* Flags to mark progress of decompression. */
    char            decompress_init;
    char            end_of_entry;

    unsigned long       crc32;
    ssize_t         filename_length;
    ssize_t         extra_length;
    int64_t         uncompressed_size;
    int64_t         compressed_size;

    unsigned char       *uncompressed_buffer;
    size_t          uncompressed_buffer_size;
#ifdef HAVE_ZLIB_H
    z_stream        stream;
    char            stream_valid;
#endif

    struct archive_string   pathname;
    struct archive_string   extra;
    char    format_name[64];
};

#define ZIP_LENGTH_AT_END   8

struct zip_file_header {
    char    signature[4];
    char    version[2];
    char    flags[2];
    char    compression[2];
    char    timedate[4];
    char    crc32[4];
    char    compressed_size[4];
    char    uncompressed_size[4];
    char    filename_length[2];
    char    extra_length[2];
};

static const char *compression_names[] = {
    "uncompressed",
    "shrinking",
    "reduced-1",
    "reduced-2",
    "reduced-3",
    "reduced-4",
    "imploded",
    "reserved",
    "deflation"
};

static int  archive_read_format_zip_bid(struct archive_read *);
static int  archive_read_format_zip_cleanup(struct archive_read *);
static int  archive_read_format_zip_read_data(struct archive_read *,
            const void **, size_t *, off_t *);
static int  archive_read_format_zip_read_data_skip(struct archive_read *a);
static int  archive_read_format_zip_read_header(struct archive_read *,
            struct archive_entry *);
static int  zip_read_data_deflate(struct archive_read *a, const void **buff,
            size_t *size, off_t *offset);
static int  zip_read_data_none(struct archive_read *a, const void **buff,
            size_t *size, off_t *offset);
static int  zip_read_file_header(struct archive_read *a,
            struct archive_entry *entry, struct zip *zip);
static time_t   zip_time(const char *);
static void process_extra(const void* extra, struct zip* zip);

int
archive_read_support_format_zip(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct zip *zip;
    int r;

    zip = (struct zip *)malloc(sizeof(*zip));
    if (zip == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data");
        return (ARCHIVE_FATAL);
    }
    memset(zip, 0, sizeof(*zip));

    r = __archive_read_register_format(a,
        zip,
        "zip",
        archive_read_format_zip_bid,
        NULL,
        archive_read_format_zip_read_header,
        archive_read_format_zip_read_data,
        archive_read_format_zip_read_data_skip,
        archive_read_format_zip_cleanup);

    if (r != ARCHIVE_OK)
        free(zip);
    return (ARCHIVE_OK);
}


static int
archive_read_format_zip_bid(struct archive_read *a)
{
    const char *p;
    const void *buff;
    ssize_t bytes_avail, offset;

    if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
        return (-1);

    /*
     * Bid of 30 here is: 16 bits for "PK",
     * next 16-bit field has four options (-2 bits).
     * 16 + 16-2 = 30.
     */
    if (p[0] == 'P' && p[1] == 'K') {
        if ((p[2] == '\001' && p[3] == '\002')
            || (p[2] == '\003' && p[3] == '\004')
            || (p[2] == '\005' && p[3] == '\006')
            || (p[2] == '\007' && p[3] == '\010')
            || (p[2] == '0' && p[3] == '0'))
            return (30);
    }

    /*
     * Attempt to handle self-extracting archives
     * by noting a PE header and searching forward
     * up to 128k for a 'PK\003\004' marker.
     */
    if (p[0] == 'M' && p[1] == 'Z') {
        /*
         * TODO: Optimize by initializing 'offset' to an
         * estimate of the likely start of the archive data
         * based on values in the PE header.  Note that we
         * don't need to be exact, but we mustn't skip too
         * far.  The search below will compensate if we
         * undershoot.
         */
        offset = 0;
        while (offset < 124000) {
            /* Get 4k of data beyond where we stopped. */
            buff = __archive_read_ahead(a, offset + 4096,
                &bytes_avail);
            if (bytes_avail < offset + 1)
                break;
            p = (const char *)buff + offset;
            while (p + 9 < (const char *)buff + bytes_avail) {
                if (p[0] == 'P' && p[1] == 'K' /* signature */
                    && p[2] == 3 && p[3] == 4 /* File entry */
                    && p[8] == 8 /* compression == deflate */
                    && p[9] == 0 /* High byte of compression */
                    )
                {
                    return (30);
                }
                ++p;
            }
            offset = p - (const char *)buff;
        }
    }

    return (0);
}

/*
 * Search forward for a "PK\003\004" file header.  This handles the
 * case of self-extracting archives, where there is an executable
 * prepended to the ZIP archive.
 */
static int
skip_sfx(struct archive_read *a)
{
    const void *h;
    const char *p, *q;
    size_t skip;
    ssize_t bytes;

    /*
     * TODO: We should be able to skip forward by a bunch
     * by lifting some values from the PE header.  We don't
     * need to be exact (we're still going to search forward
     * to find the header), but it will speed things up and
     * reduce the chance of a false positive.
     */
    for (;;) {
        h = __archive_read_ahead(a, 4, &bytes);
        if (bytes < 4)
            return (ARCHIVE_FATAL);
        p = h;
        q = p + bytes;

        /*
         * Scan ahead until we find something that looks
         * like the zip header.
         */
        while (p + 4 < q) {
            switch (p[3]) {
            case '\004':
                /* TODO: Additional verification here. */
                if (memcmp("PK\003\004", p, 4) == 0) {
                    skip = p - (const char *)h;
                    __archive_read_consume(a, skip);
                    return (ARCHIVE_OK);
                }
                p += 4;
                break;
            case '\003': p += 1; break;
            case 'K': p += 2; break;
            case 'P': p += 3; break;
            default: p += 4; break;
            }
        }
        skip = p - (const char *)h;
        __archive_read_consume(a, skip);
    }
}

static int
archive_read_format_zip_read_header(struct archive_read *a,
    struct archive_entry *entry)
{
    const void *h;
    const char *signature;
    struct zip *zip;
    int r = ARCHIVE_OK, r1;

    a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
    if (a->archive.archive_format_name == NULL)
        a->archive.archive_format_name = "ZIP";

    zip = (struct zip *)(a->format->data);
    zip->decompress_init = 0;
    zip->end_of_entry = 0;
    zip->entry_uncompressed_bytes_read = 0;
    zip->entry_compressed_bytes_read = 0;
    zip->entry_crc32 = crc32(0, NULL, 0);
    if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
        return (ARCHIVE_FATAL);

    signature = (const char *)h;
    if (signature[0] == 'M' && signature[1] == 'Z') {
        /* This is an executable?  Must be self-extracting... */
        r = skip_sfx(a);
        if (r < ARCHIVE_WARN)
            return (r);
        if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
            return (ARCHIVE_FATAL);
        signature = (const char *)h;
    }

    if (signature[0] != 'P' || signature[1] != 'K') {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Bad ZIP file");
        return (ARCHIVE_FATAL);
    }

    /*
     * "PK00" signature is used for "split" archives that
     * only have a single segment.  This means we can just
     * skip the PK00; the first real file header should follow.
     */
    if (signature[2] == '0' && signature[3] == '0') {
        __archive_read_consume(a, 4);
        if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
            return (ARCHIVE_FATAL);
        signature = (const char *)h;
        if (signature[0] != 'P' || signature[1] != 'K') {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                "Bad ZIP file");
            return (ARCHIVE_FATAL);
        }
    }

    if (signature[2] == '\001' && signature[3] == '\002') {
        /* Beginning of central directory. */
        return (ARCHIVE_EOF);
    }

    if (signature[2] == '\003' && signature[3] == '\004') {
        /* Regular file entry. */
        r1 = zip_read_file_header(a, entry, zip);
        if (r1 != ARCHIVE_OK)
            return (r1);
        return (r);
    }

    if (signature[2] == '\005' && signature[3] == '\006') {
        /* End-of-archive record. */
        return (ARCHIVE_EOF);
    }

    if (signature[2] == '\007' && signature[3] == '\010') {
        /*
         * We should never encounter this record here;
         * see ZIP_LENGTH_AT_END handling below for details.
         */
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Bad ZIP file: Unexpected end-of-entry record");
        return (ARCHIVE_FATAL);
    }

    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
        "Damaged ZIP file or unsupported format variant (%d,%d)",
        signature[2], signature[3]);
    return (ARCHIVE_FATAL);
}

static int
zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
    struct zip *zip)
{
    const struct zip_file_header *p;
    const void *h;

    if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Truncated ZIP file header");
        return (ARCHIVE_FATAL);
    }

    zip->version = p->version[0];
    zip->system = p->version[1];
    zip->flags = archive_le16dec(p->flags);
    zip->compression = archive_le16dec(p->compression);
    if (zip->compression <
        sizeof(compression_names)/sizeof(compression_names[0]))
        zip->compression_name = compression_names[zip->compression];
    else
        zip->compression_name = "??";
    zip->mtime = zip_time(p->timedate);
    zip->ctime = 0;
    zip->atime = 0;
    zip->mode = 0;
    zip->uid = 0;
    zip->gid = 0;
    zip->crc32 = archive_le32dec(p->crc32);
    zip->filename_length = archive_le16dec(p->filename_length);
    zip->extra_length = archive_le16dec(p->extra_length);
    zip->uncompressed_size = archive_le32dec(p->uncompressed_size);
    zip->compressed_size = archive_le32dec(p->compressed_size);

    __archive_read_consume(a, sizeof(struct zip_file_header));


    /* Read the filename. */
    if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Truncated ZIP file header");
        return (ARCHIVE_FATAL);
    }
    if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL)
        __archive_errx(1, "Out of memory");
    archive_strncpy(&zip->pathname, h, zip->filename_length);
    __archive_read_consume(a, zip->filename_length);
    archive_entry_set_pathname(entry, zip->pathname.s);

    if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/')
        zip->mode = AE_IFDIR | 0777;
    else
        zip->mode = AE_IFREG | 0777;

    /* Read the extra data. */
    if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Truncated ZIP file header");
        return (ARCHIVE_FATAL);
    }
    process_extra(h, zip);
    __archive_read_consume(a, zip->extra_length);

    /* Populate some additional entry fields: */
    archive_entry_set_mode(entry, zip->mode);
    archive_entry_set_uid(entry, zip->uid);
    archive_entry_set_gid(entry, zip->gid);
    archive_entry_set_mtime(entry, zip->mtime, 0);
    archive_entry_set_ctime(entry, zip->ctime, 0);
    archive_entry_set_atime(entry, zip->atime, 0);
    /* Set the size only if it's meaningful. */
    if (0 == (zip->flags & ZIP_LENGTH_AT_END))
        archive_entry_set_size(entry, zip->uncompressed_size);

    zip->entry_bytes_remaining = zip->compressed_size;
    zip->entry_offset = 0;

    /* If there's no body, force read_data() to return EOF immediately. */
    if (0 == (zip->flags & ZIP_LENGTH_AT_END)
        && zip->entry_bytes_remaining < 1)
        zip->end_of_entry = 1;

    /* Set up a more descriptive format name. */
    sprintf(zip->format_name, "ZIP %d.%d (%s)",
        zip->version / 10, zip->version % 10,
        zip->compression_name);
    a->archive.archive_format_name = zip->format_name;

    return (ARCHIVE_OK);
}

/* Convert an MSDOS-style date/time into Unix-style time. */
static time_t
zip_time(const char *p)
{
    int msTime, msDate;
    struct tm ts;

    msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]);
    msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]);

    memset(&ts, 0, sizeof(ts));
    ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
    ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
    ts.tm_mday = msDate & 0x1f; /* Day of month. */
    ts.tm_hour = (msTime >> 11) & 0x1f;
    ts.tm_min = (msTime >> 5) & 0x3f;
    ts.tm_sec = (msTime << 1) & 0x3e;
    ts.tm_isdst = -1;
    return mktime(&ts);
}

static int
archive_read_format_zip_read_data(struct archive_read *a,
    const void **buff, size_t *size, off_t *offset)
{
    int r;
    struct zip *zip;

    zip = (struct zip *)(a->format->data);

    /*
     * If we hit end-of-entry last time, clean up and return
     * ARCHIVE_EOF this time.
     */
    if (zip->end_of_entry) {
        *offset = zip->entry_uncompressed_bytes_read;
        *size = 0;
        *buff = NULL;
        return (ARCHIVE_EOF);
    }

    switch(zip->compression) {
    case 0:  /* No compression. */
        r =  zip_read_data_none(a, buff, size, offset);
        break;
    case 8: /* Deflate compression. */
        r =  zip_read_data_deflate(a, buff, size, offset);
        break;
    default: /* Unsupported compression. */
        *buff = NULL;
        *size = 0;
        *offset = 0;
        /* Return a warning. */
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Unsupported ZIP compression method (%s)",
            zip->compression_name);
        if (zip->flags & ZIP_LENGTH_AT_END) {
            /*
             * ZIP_LENGTH_AT_END requires us to
             * decompress the entry in order to
             * skip it, but we don't know this
             * compression method, so we give up.
             */
            r = ARCHIVE_FATAL;
        } else {
            /* We can't decompress this entry, but we will
             * be able to skip() it and try the next entry. */
            r = ARCHIVE_WARN;
        }
        break;
    }
    if (r != ARCHIVE_OK)
        return (r);
    /* Update checksum */
    if (*size)
        zip->entry_crc32 = crc32(zip->entry_crc32, *buff, *size);
    /* If we hit the end, swallow any end-of-data marker. */
    if (zip->end_of_entry) {
        if (zip->flags & ZIP_LENGTH_AT_END) {
            const char *p;

            if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) {
                archive_set_error(&a->archive,
                    ARCHIVE_ERRNO_FILE_FORMAT,
                    "Truncated ZIP end-of-file record");
                return (ARCHIVE_FATAL);
            }
            zip->crc32 = archive_le32dec(p + 4);
            zip->compressed_size = archive_le32dec(p + 8);
            zip->uncompressed_size = archive_le32dec(p + 12);
            __archive_read_consume(a, 16);
        }
        /* Check file size, CRC against these values. */
        if (zip->compressed_size != zip->entry_compressed_bytes_read) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "ZIP compressed data is wrong size");
            return (ARCHIVE_WARN);
        }
        /* Size field only stores the lower 32 bits of the actual size. */
        if ((zip->uncompressed_size & UINT32_MAX)
            != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "ZIP uncompressed data is wrong size");
            return (ARCHIVE_WARN);
        }
        /* Check computed CRC against header */
        if (zip->crc32 != zip->entry_crc32) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "ZIP bad CRC: 0x%lx should be 0x%lx",
                zip->entry_crc32, zip->crc32);
            return (ARCHIVE_WARN);
        }
    }

    /* Return EOF immediately if this is a non-regular file. */
    if (AE_IFREG != (zip->mode & AE_IFMT))
        return (ARCHIVE_EOF);
    return (ARCHIVE_OK);
}

/*
 * Read "uncompressed" data.  According to the current specification,
 * if ZIP_LENGTH_AT_END is specified, then the size fields in the
 * initial file header are supposed to be set to zero.  This would, of
 * course, make it impossible for us to read the archive, since we
 * couldn't determine the end of the file data.  Info-ZIP seems to
 * include the real size fields both before and after the data in this
 * case (the CRC only appears afterwards), so this works as you would
 * expect.
 *
 * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
 * zip->end_of_entry if it consumes all of the data.
 */
static int
zip_read_data_none(struct archive_read *a, const void **buff,
    size_t *size, off_t *offset)
{
    struct zip *zip;
    ssize_t bytes_avail;

    zip = (struct zip *)(a->format->data);

    if (zip->entry_bytes_remaining == 0) {
        *buff = NULL;
        *size = 0;
        *offset = zip->entry_offset;
        zip->end_of_entry = 1;
        return (ARCHIVE_OK);
    }
    /*
     * Note: '1' here is a performance optimization.
     * Recall that the decompression layer returns a count of
     * available bytes; asking for more than that forces the
     * decompressor to combine reads by copying data.
     */
    *buff = __archive_read_ahead(a, 1, &bytes_avail);
    if (bytes_avail <= 0) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Truncated ZIP file data");
        return (ARCHIVE_FATAL);
    }
    if (bytes_avail > zip->entry_bytes_remaining)
        bytes_avail = zip->entry_bytes_remaining;
    __archive_read_consume(a, bytes_avail);
    *size = bytes_avail;
    *offset = zip->entry_offset;
    zip->entry_offset += *size;
    zip->entry_bytes_remaining -= *size;
    zip->entry_uncompressed_bytes_read += *size;
    zip->entry_compressed_bytes_read += *size;
    return (ARCHIVE_OK);
}

#ifdef HAVE_ZLIB_H
static int
zip_read_data_deflate(struct archive_read *a, const void **buff,
    size_t *size, off_t *offset)
{
    struct zip *zip;
    ssize_t bytes_avail;
    const void *compressed_buff;
    int r;

    zip = (struct zip *)(a->format->data);

    /* If the buffer hasn't been allocated, allocate it now. */
    if (zip->uncompressed_buffer == NULL) {
        zip->uncompressed_buffer_size = 32 * 1024;
        zip->uncompressed_buffer
            = (unsigned char *)malloc(zip->uncompressed_buffer_size);
        if (zip->uncompressed_buffer == NULL) {
            archive_set_error(&a->archive, ENOMEM,
                "No memory for ZIP decompression");
            return (ARCHIVE_FATAL);
        }
    }

    /* If we haven't yet read any data, initialize the decompressor. */
    if (!zip->decompress_init) {
        if (zip->stream_valid)
            r = inflateReset(&zip->stream);
        else
            r = inflateInit2(&zip->stream,
                -15 /* Don't check for zlib header */);
        if (r != Z_OK) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Can't initialize ZIP decompression.");
            return (ARCHIVE_FATAL);
        }
        /* Stream structure has been set up. */
        zip->stream_valid = 1;
        /* We've initialized decompression for this stream. */
        zip->decompress_init = 1;
    }

    /*
     * Note: '1' here is a performance optimization.
     * Recall that the decompression layer returns a count of
     * available bytes; asking for more than that forces the
     * decompressor to combine reads by copying data.
     */
    compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
    if (bytes_avail <= 0) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
            "Truncated ZIP file body");
        return (ARCHIVE_FATAL);
    }

    /*
     * A bug in zlib.h: stream.next_in should be marked 'const'
     * but isn't (the library never alters data through the
     * next_in pointer, only reads it).  The result: this ugly
     * cast to remove 'const'.
     */
    zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff;
    zip->stream.avail_in = bytes_avail;
    zip->stream.total_in = 0;
    zip->stream.next_out = zip->uncompressed_buffer;
    zip->stream.avail_out = zip->uncompressed_buffer_size;
    zip->stream.total_out = 0;

    r = inflate(&zip->stream, 0);
    switch (r) {
    case Z_OK:
        break;
    case Z_STREAM_END:
        zip->end_of_entry = 1;
        break;
    case Z_MEM_ERROR:
        archive_set_error(&a->archive, ENOMEM,
            "Out of memory for ZIP decompression");
        return (ARCHIVE_FATAL);
    default:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "ZIP decompression failed (%d)", r);
        return (ARCHIVE_FATAL);
    }

    /* Consume as much as the compressor actually used. */
    bytes_avail = zip->stream.total_in;
    __archive_read_consume(a, bytes_avail);
    zip->entry_bytes_remaining -= bytes_avail;
    zip->entry_compressed_bytes_read += bytes_avail;

    *offset = zip->entry_offset;
    *size = zip->stream.total_out;
    zip->entry_uncompressed_bytes_read += *size;
    *buff = zip->uncompressed_buffer;
    zip->entry_offset += *size;
    return (ARCHIVE_OK);
}
#else
static int
zip_read_data_deflate(struct archive_read *a, const void **buff,
    size_t *size, off_t *offset)
{
    *buff = NULL;
    *size = 0;
    *offset = 0;
    archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
        "libarchive compiled without deflate support (no libz)");
    return (ARCHIVE_FATAL);
}
#endif

static int
archive_read_format_zip_read_data_skip(struct archive_read *a)
{
    struct zip *zip;
    const void *buff = NULL;
    off_t bytes_skipped;

    zip = (struct zip *)(a->format->data);

    /* If we've already read to end of data, we're done. */
    if (zip->end_of_entry)
        return (ARCHIVE_OK);

    /*
     * If the length is at the end, we have no choice but
     * to decompress all the data to find the end marker.
     */
    if (zip->flags & ZIP_LENGTH_AT_END) {
        size_t size;
        off_t offset;
        int r;
        do {
            r = archive_read_format_zip_read_data(a, &buff,
                &size, &offset);
        } while (r == ARCHIVE_OK);
        return (r);
    }

    /*
     * If the length is at the beginning, we can skip the
     * compressed data much more quickly.
     */
    bytes_skipped = __archive_read_skip(a, zip->entry_bytes_remaining);
    if (bytes_skipped < 0)
        return (ARCHIVE_FATAL);

    /* This entry is finished and done. */
    zip->end_of_entry = 1;
    return (ARCHIVE_OK);
}

static int
archive_read_format_zip_cleanup(struct archive_read *a)
{
    struct zip *zip;

    zip = (struct zip *)(a->format->data);
#ifdef HAVE_ZLIB_H
    if (zip->stream_valid)
        inflateEnd(&zip->stream);
#endif
    free(zip->uncompressed_buffer);
    archive_string_free(&(zip->pathname));
    archive_string_free(&(zip->extra));
    free(zip);
    (a->format->data) = NULL;
    return (ARCHIVE_OK);
}

/*
 * The extra data is stored as a list of
 *  id1+size1+data1 + id2+size2+data2 ...
 *  triplets.  id and size are 2 bytes each.
 */
static void
process_extra(const void* extra, struct zip* zip)
{
    int offset = 0;
    const char *p = (const char *)extra;
    while (offset < zip->extra_length - 4)
    {
        unsigned short headerid = archive_le16dec(p + offset);
        unsigned short datasize = archive_le16dec(p + offset + 2);
        offset += 4;
        if (offset + datasize > zip->extra_length)
            break;
#ifdef DEBUG
        fprintf(stderr, "Header id 0x%04x, length %d\n",
            headerid, datasize);
#endif
        switch (headerid) {
        case 0x0001:
            /* Zip64 extended information extra field. */
            if (datasize >= 8)
                zip->uncompressed_size = archive_le64dec(p + offset);
            if (datasize >= 16)
                zip->compressed_size = archive_le64dec(p + offset + 8);
            break;
        case 0x5455:
        {
            /* Extended time field "UT". */
            int flags = p[offset];
            offset++;
            datasize--;
            /* Flag bits indicate which dates are present. */
            if (flags & 0x01)
            {
#ifdef DEBUG
                fprintf(stderr, "mtime: %lld -> %d\n",
                    (long long)zip->mtime,
                    archive_le32dec(p + offset));
#endif
                if (datasize < 4)
                    break;
                zip->mtime = archive_le32dec(p + offset);
                offset += 4;
                datasize -= 4;
            }
            if (flags & 0x02)
            {
                if (datasize < 4)
                    break;
                zip->atime = archive_le32dec(p + offset);
                offset += 4;
                datasize -= 4;
            }
            if (flags & 0x04)
            {
                if (datasize < 4)
                    break;
                zip->ctime = archive_le32dec(p + offset);
                offset += 4;
                datasize -= 4;
            }
            break;
        }
        case 0x7855:
            /* Info-ZIP Unix Extra Field (type 2) "Ux". */
#ifdef DEBUG
            fprintf(stderr, "uid %d gid %d\n",
                archive_le16dec(p + offset),
                archive_le16dec(p + offset + 2));
#endif
            if (datasize >= 2)
                zip->uid = archive_le16dec(p + offset);
            if (datasize >= 4)
                zip->gid = archive_le16dec(p + offset + 2);
            break;
        default:
            break;
        }
        offset += datasize;
    }
#ifdef DEBUG
    if (offset != zip->extra_length)
    {
        fprintf(stderr,
            "Extra data field contents do not match reported size!");
    }
#endif
}

--- NEW FILE: archive_write_set_compression_compress.c ---
/*-
 * Copyright (c) 2008 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*-
 * Copyright (c) 1985, 1986, 1992, 1993
 *  The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Diomidis Spinellis and James A. Woods, derived from original
 * work by Spencer Thomas and Joseph Orost.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_compress.c,v 1.1 2008/03/14 20:35:37 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"

#define HSIZE       69001   /* 95% occupancy */
#define HSHIFT      8   /* 8 - trunc(log2(HSIZE / 65536)) */
#define CHECK_GAP 10000     /* Ratio check interval. */

#define MAXCODE(bits)   ((1 << (bits)) - 1)

/*
 * the next two codes should not be changed lightly, as they must not
 * lie within the contiguous general code space.
 */
#define FIRST   257     /* First free entry. */
#define CLEAR   256     /* Table clear output code. */

struct private_data {
    off_t in_count, out_count, checkpoint;

    int code_len;           /* Number of bits/code. */
    int cur_maxcode;        /* Maximum code, given n_bits. */
    int max_maxcode;        /* Should NEVER generate this code. */
    int hashtab [HSIZE];
    unsigned short codetab [HSIZE];
    int first_free;     /* First unused entry. */
    int compress_ratio;

    int cur_code, cur_fcode;

    int bit_offset;
    unsigned char bit_buf;

    unsigned char   *compressed;
    size_t       compressed_buffer_size;
    size_t       compressed_offset;
};

static int  archive_compressor_compress_finish(struct archive_write *);
static int  archive_compressor_compress_init(struct archive_write *);
static int  archive_compressor_compress_write(struct archive_write *,
            const void *, size_t);

/*
 * Allocate, initialize and return a archive object.
 */
int
archive_write_set_compression_compress(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compression_compress");
    a->compressor.init = &archive_compressor_compress_init;
    a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
    a->archive.compression_name = "compress";
    return (ARCHIVE_OK);
}

/*
 * Setup callback.
 */
static int
archive_compressor_compress_init(struct archive_write *a)
{
    int ret;
    struct private_data *state;

    a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
    a->archive.compression_name = "compress";

    if (a->bytes_per_block < 4) {
        archive_set_error(&a->archive, EINVAL,
            "Can't write Compress header as single block");
        return (ARCHIVE_FATAL);
    }

    if (a->client_opener != NULL) {
        ret = (a->client_opener)(&a->archive, a->client_data);
        if (ret != ARCHIVE_OK)
            return (ret);
    }

    state = (struct private_data *)malloc(sizeof(*state));
    if (state == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression");
        return (ARCHIVE_FATAL);
    }
    memset(state, 0, sizeof(*state));

    state->compressed_buffer_size = a->bytes_per_block;
    state->compressed = malloc(state->compressed_buffer_size);

    if (state->compressed == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression buffer");
        free(state);
        return (ARCHIVE_FATAL);
    }

    a->compressor.write = archive_compressor_compress_write;
    a->compressor.finish = archive_compressor_compress_finish;

    state->max_maxcode = 0x10000;   /* Should NEVER generate this code. */
    state->in_count = 0;        /* Length of input. */
    state->bit_buf = 0;
    state->bit_offset = 0;
    state->out_count = 3;       /* Includes 3-byte header mojo. */
    state->compress_ratio = 0;
    state->checkpoint = CHECK_GAP;
    state->code_len = 9;
    state->cur_maxcode = MAXCODE(state->code_len);
    state->first_free = FIRST;

    memset(state->hashtab, 0xff, sizeof(state->hashtab));

    /* Prime output buffer with a gzip header. */
    state->compressed[0] = 0x1f; /* Compress */
    state->compressed[1] = 0x9d;
    state->compressed[2] = 0x90; /* Block mode, 16bit max */
    state->compressed_offset = 3;

    a->compressor.data = state;
    return (0);
}

/*-
 * Output the given code.
 * Inputs:
 *  code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
 *      that n_bits =< (long)wordsize - 1.
 * Outputs:
 *  Outputs code to the file.
 * Assumptions:
 *  Chars are 8 bits long.
 * Algorithm:
 *  Maintain a BITS character long buffer (so that 8 codes will
 * fit in it exactly).  Use the VAX insv instruction to insert each
 * code in turn.  When the buffer fills up empty it and start over.
 */

static unsigned char rmask[9] =
    {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};

static int
output_byte(struct archive_write *a, unsigned char c)
{
    struct private_data *state = a->compressor.data;
    ssize_t bytes_written;

    state->compressed[state->compressed_offset++] = c;
    ++state->out_count;

    if (state->compressed_buffer_size == state->compressed_offset) {
        bytes_written = (a->client_writer)(&a->archive,
            a->client_data,
            state->compressed, state->compressed_buffer_size);
        if (bytes_written <= 0)
            return ARCHIVE_FATAL;
        a->archive.raw_position += bytes_written;
        state->compressed_offset = 0;
    }

    return ARCHIVE_OK;
}

static int
output_code(struct archive_write *a, int ocode)
{
    struct private_data *state = a->compressor.data;
    int bits, ret, clear_flg, bit_offset;

    clear_flg = ocode == CLEAR;
    bits = state->code_len;

    /*
     * Since ocode is always >= 8 bits, only need to mask the first
     * hunk on the left.
     */
    bit_offset = state->bit_offset % 8;
    state->bit_buf |= (ocode << bit_offset) & 0xff;
    output_byte(a, state->bit_buf);

    bits = state->code_len - (8 - bit_offset);
    ocode >>= 8 - bit_offset;
    /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
    if (bits >= 8) {
        output_byte(a, ocode & 0xff);
        ocode >>= 8;
        bits -= 8;
    }
    /* Last bits. */
    state->bit_offset += state->code_len;
    state->bit_buf = ocode & rmask[bits];
    if (state->bit_offset == state->code_len * 8)
        state->bit_offset = 0;

    /*
     * If the next entry is going to be too big for the ocode size,
     * then increase it, if possible.
     */
    if (clear_flg || state->first_free > state->cur_maxcode) {
           /*
        * Write the whole buffer, because the input side won't
        * discover the size increase until after it has read it.
        */
        if (state->bit_offset > 0) {
            while (state->bit_offset < state->code_len * 8) {
                ret = output_byte(a, state->bit_buf);
                if (ret != ARCHIVE_OK)
                    return ret;
                state->bit_offset += 8;
                state->bit_buf = 0;
            }
        }
        state->bit_buf = 0;
        state->bit_offset = 0;

        if (clear_flg) {
            state->code_len = 9;
            state->cur_maxcode = MAXCODE(state->code_len);
        } else {
            state->code_len++;
            if (state->code_len == 16)
                state->cur_maxcode = state->max_maxcode;
            else
                state->cur_maxcode = MAXCODE(state->code_len);
        }
    }

    return (ARCHIVE_OK);
}

static int
output_flush(struct archive_write *a)
{
    struct private_data *state = a->compressor.data;
    int ret;

    /* At EOF, write the rest of the buffer. */
    if (state->bit_offset % 8) {
        state->code_len = (state->bit_offset % 8 + 7) / 8;
        ret = output_byte(a, state->bit_buf);
        if (ret != ARCHIVE_OK)
            return ret;
    }

    return (ARCHIVE_OK);
}

/*
 * Write data to the compressed stream.
 */
static int
archive_compressor_compress_write(struct archive_write *a, const void *buff,
    size_t length)
{
    struct private_data *state;
    int i;
    int ratio;
    int c, disp, ret;
    const unsigned char *bp;

    state = (struct private_data *)a->compressor.data;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    if (length == 0)
        return ARCHIVE_OK;

    bp = buff;

    if (state->in_count == 0) {
        state->cur_code = *bp++;
        ++state->in_count;
        --length;
    }

    while (length--) {
        c = *bp++;
        state->in_count++;
        state->cur_fcode = (c << 16) + state->cur_code;
        i = ((c << HSHIFT) ^ state->cur_code);  /* Xor hashing. */

        if (state->hashtab[i] == state->cur_fcode) {
            state->cur_code = state->codetab[i];
            continue;
        }
        if (state->hashtab[i] < 0)  /* Empty slot. */
            goto nomatch;
        /* Secondary hash (after G. Knott). */
        if (i == 0)
            disp = 1;
        else
            disp = HSIZE - i;
 probe:     
        if ((i -= disp) < 0)
            i += HSIZE;

        if (state->hashtab[i] == state->cur_fcode) {
            state->cur_code = state->codetab[i];
            continue;
        }
        if (state->hashtab[i] >= 0)
            goto probe;
 nomatch:   
        ret = output_code(a, state->cur_code);
        if (ret != ARCHIVE_OK)
            return ret;
        state->cur_code = c;
        if (state->first_free < state->max_maxcode) {
            state->codetab[i] = state->first_free++;    /* code -> hashtable */
            state->hashtab[i] = state->cur_fcode;
            continue;
        }
        if (state->in_count < state->checkpoint)
            continue;

        state->checkpoint = state->in_count + CHECK_GAP;

        if (state->in_count <= 0x007fffff)
            ratio = state->in_count * 256 / state->out_count;
        else if ((ratio = state->out_count / 256) == 0)
            ratio = 0x7fffffff;
        else
            ratio = state->in_count / ratio;

        if (ratio > state->compress_ratio)
            state->compress_ratio = ratio;
        else {
            state->compress_ratio = 0;
            memset(state->hashtab, 0xff, sizeof(state->hashtab));
            state->first_free = FIRST;
            ret = output_code(a, CLEAR);
            if (ret != ARCHIVE_OK)
                return ret;
        }
    }

    return (ARCHIVE_OK);
}


/*
 * Finish the compression...
 */
static int
archive_compressor_compress_finish(struct archive_write *a)
{
    ssize_t block_length, target_block_length, bytes_written;
    int ret;
    struct private_data *state;
    unsigned tocopy;

    state = (struct private_data *)a->compressor.data;
    ret = 0;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        ret = ARCHIVE_FATAL;
        goto cleanup;
    }

    /* By default, always pad the uncompressed data. */
    if (a->pad_uncompressed) {
        while (state->in_count % a->bytes_per_block != 0) {
            tocopy = a->bytes_per_block -
                (state->in_count % a->bytes_per_block);
            if (tocopy > a->null_length)
                tocopy = a->null_length;
            ret = archive_compressor_compress_write(a, a->nulls,
                tocopy);
            if (ret != ARCHIVE_OK)
                goto cleanup;
        }
    }

    ret = output_code(a, state->cur_code);
    if (ret != ARCHIVE_OK)
        goto cleanup;
    ret = output_flush(a);
    if (ret != ARCHIVE_OK)
        goto cleanup;

    /* Optionally, pad the final compressed block. */
    block_length = state->compressed_offset;

    /* Tricky calculation to determine size of last block. */
    if (a->bytes_in_last_block <= 0)
        /* Default or Zero: pad to full block */
        target_block_length = a->bytes_per_block;
    else
        /* Round length to next multiple of bytes_in_last_block. */
        target_block_length = a->bytes_in_last_block *
            ( (block_length + a->bytes_in_last_block - 1) /
            a->bytes_in_last_block);
    if (target_block_length > a->bytes_per_block)
        target_block_length = a->bytes_per_block;
    if (block_length < target_block_length) {
        memset(state->compressed + state->compressed_offset, 0,
            target_block_length - block_length);
        block_length = target_block_length;
    }

    /* Write the last block */
    bytes_written = (a->client_writer)(&a->archive, a->client_data,
        state->compressed, block_length);
    if (bytes_written <= 0)
        ret = ARCHIVE_FATAL;
    else
        a->archive.raw_position += bytes_written;

cleanup:
    free(state->compressed);
    free(state);
    return (ret);
}

--- NEW FILE: archive_endian.h ---
/*-
 * Copyright (c) 2002 Thomas Moestl <tmm at FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/archive_endian.h,v 1.4 2008/12/06 06:12:24 kientzle Exp $
 *
 * Borrowed from FreeBSD's <sys/endian.h>
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

/* Note:  This is a purely internal header! */
/* Do not use this outside of libarchive internal code! */

#ifndef ARCHIVE_ENDIAN_H_INCLUDED
#define ARCHIVE_ENDIAN_H_INCLUDED


/*
 * Disabling inline keyword for compilers known to choke on it:
 * - Watcom C++ in C code.  (For any version?)
 * - SGI MIPSpro
 * - Microsoft Visual C++ 6.0 (supposedly newer versions too)
 */
#if defined(__WATCOMC__) || defined(__sgi)
#define inline
#elif defined(_MSC_VER)
#define inline __inline
#endif

/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */

static inline uint16_t
archive_be16dec(const void *pp)
{
    unsigned char const *p = (unsigned char const *)pp;

    return ((p[0] << 8) | p[1]);
}

static inline uint32_t
archive_be32dec(const void *pp)
{
    unsigned char const *p = (unsigned char const *)pp;

    return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
}

static inline uint64_t
archive_be64dec(const void *pp)
{
    unsigned char const *p = (unsigned char const *)pp;

    return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
}

static inline uint16_t
archive_le16dec(const void *pp)
{
    unsigned char const *p = (unsigned char const *)pp;

    return ((p[1] << 8) | p[0]);
}

static inline uint32_t
archive_le32dec(const void *pp)
{
    unsigned char const *p = (unsigned char const *)pp;

    return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
}

static inline uint64_t
archive_le64dec(const void *pp)
{
    unsigned char const *p = (unsigned char const *)pp;

    return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
}

static inline void
archive_be16enc(void *pp, uint16_t u)
{
    unsigned char *p = (unsigned char *)pp;

    p[0] = (u >> 8) & 0xff;
    p[1] = u & 0xff;
}

static inline void
archive_be32enc(void *pp, uint32_t u)
{
    unsigned char *p = (unsigned char *)pp;

    p[0] = (u >> 24) & 0xff;
    p[1] = (u >> 16) & 0xff;
    p[2] = (u >> 8) & 0xff;
    p[3] = u & 0xff;
}

static inline void
archive_be64enc(void *pp, uint64_t u)
{
    unsigned char *p = (unsigned char *)pp;

    archive_be32enc(p, u >> 32);
    archive_be32enc(p + 4, u & 0xffffffff);
}

static inline void
archive_le16enc(void *pp, uint16_t u)
{
    unsigned char *p = (unsigned char *)pp;

    p[0] = u & 0xff;
    p[1] = (u >> 8) & 0xff;
}

static inline void
archive_le32enc(void *pp, uint32_t u)
{
    unsigned char *p = (unsigned char *)pp;

    p[0] = u & 0xff;
    p[1] = (u >> 8) & 0xff;
    p[2] = (u >> 16) & 0xff;
    p[3] = (u >> 24) & 0xff;
}

static inline void
archive_le64enc(void *pp, uint64_t u)
{
    unsigned char *p = (unsigned char *)pp;

    archive_le32enc(p, u & 0xffffffff);
    archive_le32enc(p + 4, u >> 32);
}

#endif

--- NEW FILE: archive_write_set_format_ustar.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.27 2008/05/26 17:00:23 kientzle Exp $");


#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

struct ustar {
    uint64_t    entry_bytes_remaining;
    uint64_t    entry_padding;
};

/*
 * Define structure of POSIX 'ustar' tar header.
 */
#define USTAR_name_offset 0
#define USTAR_name_size 100
#define USTAR_mode_offset 100
#define USTAR_mode_size 6
#define USTAR_mode_max_size 8
#define USTAR_uid_offset 108
#define USTAR_uid_size 6
#define USTAR_uid_max_size 8
#define USTAR_gid_offset 116
#define USTAR_gid_size 6
#define USTAR_gid_max_size 8
#define USTAR_size_offset 124
#define USTAR_size_size 11
#define USTAR_size_max_size 12
#define USTAR_mtime_offset 136
#define USTAR_mtime_size 11
#define USTAR_mtime_max_size 11
#define USTAR_checksum_offset 148
#define USTAR_checksum_size 8
#define USTAR_typeflag_offset 156
#define USTAR_typeflag_size 1
#define USTAR_linkname_offset 157
#define USTAR_linkname_size 100
#define USTAR_magic_offset 257
#define USTAR_magic_size 6
#define USTAR_version_offset 263
#define USTAR_version_size 2
#define USTAR_uname_offset 265
#define USTAR_uname_size 32
#define USTAR_gname_offset 297
#define USTAR_gname_size 32
#define USTAR_rdevmajor_offset 329
#define USTAR_rdevmajor_size 6
#define USTAR_rdevmajor_max_size 8
#define USTAR_rdevminor_offset 337
#define USTAR_rdevminor_size 6
#define USTAR_rdevminor_max_size 8
#define USTAR_prefix_offset 345
#define USTAR_prefix_size 155
#define USTAR_padding_offset 500
#define USTAR_padding_size 12

/*
 * A filled-in copy of the header for initialization.
 */
static const char template_header[] = {
    /* name: 100 bytes */
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,
    /* Mode, space-null termination: 8 bytes */
    '0','0','0','0','0','0', ' ','\0',
    /* uid, space-null termination: 8 bytes */
    '0','0','0','0','0','0', ' ','\0',
    /* gid, space-null termination: 8 bytes */
    '0','0','0','0','0','0', ' ','\0',
    /* size, space termation: 12 bytes */
    '0','0','0','0','0','0','0','0','0','0','0', ' ',
    /* mtime, space termation: 12 bytes */
    '0','0','0','0','0','0','0','0','0','0','0', ' ',
    /* Initial checksum value: 8 spaces */
    ' ',' ',' ',' ',' ',' ',' ',' ',
    /* Typeflag: 1 byte */
    '0',            /* '0' = regular file */
    /* Linkname: 100 bytes */
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,
    /* Magic: 6 bytes, Version: 2 bytes */
    'u','s','t','a','r','\0', '0','0',
    /* Uname: 32 bytes */
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    /* Gname: 32 bytes */
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    /* rdevmajor + space/null padding: 8 bytes */
    '0','0','0','0','0','0', ' ','\0',
    /* rdevminor + space/null padding: 8 bytes */
    '0','0','0','0','0','0', ' ','\0',
    /* Prefix: 155 bytes */
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,
    /* Padding: 12 bytes */
    0,0,0,0,0,0,0,0, 0,0,0,0
};

static ssize_t  archive_write_ustar_data(struct archive_write *a, const void *buff,
            size_t s);
static int  archive_write_ustar_destroy(struct archive_write *);
static int  archive_write_ustar_finish(struct archive_write *);
static int  archive_write_ustar_finish_entry(struct archive_write *);
static int  archive_write_ustar_header(struct archive_write *,
            struct archive_entry *entry);
static int  format_256(int64_t, char *, int);
static int  format_number(int64_t, char *, int size, int max, int strict);
static int  format_octal(int64_t, char *, int);
static int  write_nulls(struct archive_write *a, size_t);

/*
 * Set output format to 'ustar' format.
 */
int
archive_write_set_format_ustar(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct ustar *ustar;

    /* If someone else was already registered, unregister them. */
    if (a->format_destroy != NULL)
        (a->format_destroy)(a);

    /* Basic internal sanity test. */
    if (sizeof(template_header) != 512) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header));
        return (ARCHIVE_FATAL);
    }

    ustar = (struct ustar *)malloc(sizeof(*ustar));
    if (ustar == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data");
        return (ARCHIVE_FATAL);
    }
    memset(ustar, 0, sizeof(*ustar));
    a->format_data = ustar;

    a->pad_uncompressed = 1;    /* Mimic gtar in this respect. */
    a->format_name = "ustar";
    a->format_write_header = archive_write_ustar_header;
    a->format_write_data = archive_write_ustar_data;
    a->format_finish = archive_write_ustar_finish;
    a->format_destroy = archive_write_ustar_destroy;
    a->format_finish_entry = archive_write_ustar_finish_entry;
    a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
    a->archive.archive_format_name = "POSIX ustar";
    return (ARCHIVE_OK);
}

static int
archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
{
    char buff[512];
    int ret, ret2;
    struct ustar *ustar;

    ustar = (struct ustar *)a->format_data;

    /* Only regular files (not hardlinks) have data. */
    if (archive_entry_hardlink(entry) != NULL ||
        archive_entry_symlink(entry) != NULL ||
        !(archive_entry_filetype(entry) == AE_IFREG))
        archive_entry_set_size(entry, 0);

    if (AE_IFDIR == archive_entry_filetype(entry)) {
        const char *p;
        char *t;
        /*
         * Ensure a trailing '/'.  Modify the entry so
         * the client sees the change.
         */
        p = archive_entry_pathname(entry);
        if (p[strlen(p) - 1] != '/') {
            t = (char *)malloc(strlen(p) + 2);
            if (t == NULL) {
                archive_set_error(&a->archive, ENOMEM,
                "Can't allocate ustar data");
                return(ARCHIVE_FATAL);
            }
            strcpy(t, p);
            strcat(t, "/");
            archive_entry_copy_pathname(entry, t);
            free(t);
        }
    }

    ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1);
    if (ret < ARCHIVE_WARN)
        return (ret);
    ret2 = (a->compressor.write)(a, buff, 512);
    if (ret2 < ARCHIVE_WARN)
        return (ret2);
    if (ret2 < ret)
        ret = ret2;

    ustar->entry_bytes_remaining = archive_entry_size(entry);
    ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
    return (ret);
}

/*
 * Format a basic 512-byte "ustar" header.
 *
 * Returns -1 if format failed (due to field overflow).
 * Note that this always formats as much of the header as possible.
 * If "strict" is set to zero, it will extend numeric fields as
 * necessary (overwriting terminators or using base-256 extensions).
 *
 * This is exported so that other 'tar' formats can use it.
 */
int
__archive_write_format_header_ustar(struct archive_write *a, char h[512],
    struct archive_entry *entry, int tartype, int strict)
{
    unsigned int checksum;
    int i, ret;
    size_t copy_length;
    const char *p, *pp;
    int mytartype;

    ret = 0;
    mytartype = -1;
    /*
     * The "template header" already includes the "ustar"
     * signature, various end-of-field markers and other required
     * elements.
     */
    memcpy(h, &template_header, 512);

    /*
     * Because the block is already null-filled, and strings
     * are allowed to exactly fill their destination (without null),
     * I use memcpy(dest, src, strlen()) here a lot to copy strings.
     */

    pp = archive_entry_pathname(entry);
    if (strlen(pp) <= USTAR_name_size)
        memcpy(h + USTAR_name_offset, pp, strlen(pp));
    else {
        /* Store in two pieces, splitting at a '/'. */
        p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/');
        /*
         * Look for the next '/' if we chose the first character
         * as the separator.  (ustar format doesn't permit
         * an empty prefix.)
         */
        if (p == pp)
            p = strchr(p + 1, '/');
        /* Fail if the name won't fit. */
        if (!p) {
            /* No separator. */
            archive_set_error(&a->archive, ENAMETOOLONG,
                "Pathname too long");
            ret = ARCHIVE_FAILED;
        } else if (p[1] == '\0') {
            /*
             * The only feasible separator is a final '/';
             * this would result in a non-empty prefix and
             * an empty name, which POSIX doesn't
             * explicity forbid, but it just feels wrong.
             */
            archive_set_error(&a->archive, ENAMETOOLONG,
                "Pathname too long");
            ret = ARCHIVE_FAILED;
        } else if (p  > pp + USTAR_prefix_size) {
            /* Prefix is too long. */
            archive_set_error(&a->archive, ENAMETOOLONG,
                "Pathname too long");
            ret = ARCHIVE_FAILED;
        } else {
            /* Copy prefix and remainder to appropriate places */
            memcpy(h + USTAR_prefix_offset, pp, p - pp);
            memcpy(h + USTAR_name_offset, p + 1, pp + strlen(pp) - p - 1);
        }
    }

    p = archive_entry_hardlink(entry);
    if (p != NULL)
        mytartype = '1';
    else
        p = archive_entry_symlink(entry);
    if (p != NULL && p[0] != '\0') {
        copy_length = strlen(p);
        if (copy_length > USTAR_linkname_size) {
            archive_set_error(&a->archive, ENAMETOOLONG,
                "Link contents too long");
            ret = ARCHIVE_FAILED;
            copy_length = USTAR_linkname_size;
        }
        memcpy(h + USTAR_linkname_offset, p, copy_length);
    }

    p = archive_entry_uname(entry);
    if (p != NULL && p[0] != '\0') {
        copy_length = strlen(p);
        if (copy_length > USTAR_uname_size) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Username too long");
            ret = ARCHIVE_FAILED;
            copy_length = USTAR_uname_size;
        }
        memcpy(h + USTAR_uname_offset, p, copy_length);
    }

    p = archive_entry_gname(entry);
    if (p != NULL && p[0] != '\0') {
        copy_length = strlen(p);
        if (strlen(p) > USTAR_gname_size) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Group name too long");
            ret = ARCHIVE_FAILED;
            copy_length = USTAR_gname_size;
        }
        memcpy(h + USTAR_gname_offset, p, copy_length);
    }

    if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
        archive_set_error(&a->archive, ERANGE, "Numeric mode too large");
        ret = ARCHIVE_FAILED;
    }

    if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
        archive_set_error(&a->archive, ERANGE, "Numeric user ID too large");
        ret = ARCHIVE_FAILED;
    }

    if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
        archive_set_error(&a->archive, ERANGE, "Numeric group ID too large");
        ret = ARCHIVE_FAILED;
    }

    if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
        archive_set_error(&a->archive, ERANGE, "File size out of range");
        ret = ARCHIVE_FAILED;
    }

    if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
        archive_set_error(&a->archive, ERANGE,
            "File modification time too large");
        ret = ARCHIVE_FAILED;
    }

    if (archive_entry_filetype(entry) == AE_IFBLK
        || archive_entry_filetype(entry) == AE_IFCHR) {
        if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset,
            USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) {
            archive_set_error(&a->archive, ERANGE,
                "Major device number too large");
            ret = ARCHIVE_FAILED;
        }

        if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset,
            USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) {
            archive_set_error(&a->archive, ERANGE,
                "Minor device number too large");
            ret = ARCHIVE_FAILED;
        }
    }

    if (tartype >= 0) {
        h[USTAR_typeflag_offset] = tartype;
    } else if (mytartype >= 0) {
        h[USTAR_typeflag_offset] = mytartype;
    } else {
        switch (archive_entry_filetype(entry)) {
        case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
        case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
        case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
        case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
        case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
        case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
        case AE_IFSOCK:
            archive_set_error(&a->archive,
                ARCHIVE_ERRNO_FILE_FORMAT,
                "tar format cannot archive socket");
            return (ARCHIVE_FAILED);
        default:
            archive_set_error(&a->archive,
                ARCHIVE_ERRNO_FILE_FORMAT,
                "tar format cannot archive this (mode=0%lo)",
                (unsigned long)archive_entry_mode(entry));
            ret = ARCHIVE_FAILED;
        }
    }

    checksum = 0;
    for (i = 0; i < 512; i++)
        checksum += 255 & (unsigned int)h[i];
    h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
    /* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
    format_octal(checksum, h + USTAR_checksum_offset, 6);
    return (ret);
}

/*
 * Format a number into a field, with some intelligence.
 */
static int
format_number(int64_t v, char *p, int s, int maxsize, int strict)
{
    int64_t limit;

    limit = ((int64_t)1 << (s*3));

    /* "Strict" only permits octal values with proper termination. */
    if (strict)
        return (format_octal(v, p, s));

    /*
     * In non-strict mode, we allow the number to overwrite one or
     * more bytes of the field termination.  Even old tar
     * implementations should be able to handle this with no
     * problem.
     */
    if (v >= 0) {
        while (s <= maxsize) {
            if (v < limit)
                return (format_octal(v, p, s));
            s++;
            limit <<= 3;
        }
    }

    /* Base-256 can handle any number, positive or negative. */
    return (format_256(v, p, maxsize));
}

/*
 * Format a number into the specified field using base-256.
 */
static int
format_256(int64_t v, char *p, int s)
{
    p += s;
    while (s-- > 0) {
        *--p = (char)(v & 0xff);
        v >>= 8;
    }
    *p |= 0x80; /* Set the base-256 marker bit. */
    return (0);
}

/*
 * Format a number into the specified field.
 */
static int
format_octal(int64_t v, char *p, int s)
{
    int len;

    len = s;

    /* Octal values can't be negative, so use 0. */
    if (v < 0) {
        while (len-- > 0)
            *p++ = '0';
        return (-1);
    }

    p += s;     /* Start at the end and work backwards. */
    while (s-- > 0) {
        *--p = (char)('0' + (v & 7));
        v >>= 3;
    }

    if (v == 0)
        return (0);

    /* If it overflowed, fill field with max value. */
    while (len-- > 0)
        *p++ = '7';

    return (-1);
}

static int
archive_write_ustar_finish(struct archive_write *a)
{
    int r;

    if (a->compressor.write == NULL)
        return (ARCHIVE_OK);

    r = write_nulls(a, 512*2);
    return (r);
}

static int
archive_write_ustar_destroy(struct archive_write *a)
{
    struct ustar *ustar;

    ustar = (struct ustar *)a->format_data;
    free(ustar);
    a->format_data = NULL;
    return (ARCHIVE_OK);
}

static int
archive_write_ustar_finish_entry(struct archive_write *a)
{
    struct ustar *ustar;
    int ret;

    ustar = (struct ustar *)a->format_data;
    ret = write_nulls(a,
        ustar->entry_bytes_remaining + ustar->entry_padding);
    ustar->entry_bytes_remaining = ustar->entry_padding = 0;
    return (ret);
}

static int
write_nulls(struct archive_write *a, size_t padding)
{
    int ret;
    size_t to_write;

    while (padding > 0) {
        to_write = padding < a->null_length ? padding : a->null_length;
        ret = (a->compressor.write)(a, a->nulls, to_write);
        if (ret != ARCHIVE_OK)
            return (ret);
        padding -= to_write;
    }
    return (ARCHIVE_OK);
}

static ssize_t
archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
{
    struct ustar *ustar;
    int ret;

    ustar = (struct ustar *)a->format_data;
    if (s > ustar->entry_bytes_remaining)
        s = ustar->entry_bytes_remaining;
    ret = (a->compressor.write)(a, buff, s);
    ustar->entry_bytes_remaining -= s;
    if (ret != ARCHIVE_OK)
        return (ret);
    return (s);
}

--- NEW FILE: archive_entry_copy_stat.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_copy_stat.c,v 1.2 2008/09/30 03:53:03 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#include "archive_entry.h"

void
archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
{
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
    archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
    archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
    archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
    archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
    archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
    archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_MTIME_N
    archive_entry_set_atime(entry, st->st_atime, st->st_atime_n);
    archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n);
    archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n);
#elif HAVE_STRUCT_STAT_ST_UMTIME
    archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000);
    archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000);
    archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000);
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
    archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000);
    archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000);
    archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000);
#else
    archive_entry_set_atime(entry, st->st_atime, 0);
    archive_entry_set_ctime(entry, st->st_ctime, 0);
    archive_entry_set_mtime(entry, st->st_mtime, 0);
#if HAVE_STRUCT_STAT_ST_BIRTHTIME
    archive_entry_set_birthtime(entry, st->st_birthtime, 0);
#endif
#endif
#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
    archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);
#endif
    archive_entry_set_dev(entry, st->st_dev);
    archive_entry_set_gid(entry, st->st_gid);
    archive_entry_set_uid(entry, st->st_uid);
    archive_entry_set_ino(entry, st->st_ino);
    archive_entry_set_nlink(entry, st->st_nlink);
    archive_entry_set_rdev(entry, st->st_rdev);
    archive_entry_set_size(entry, st->st_size);
    archive_entry_set_mode(entry, st->st_mode);
}

--- NEW FILE: archive_read_extract.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"
#include "archive_write_disk_private.h"

struct extract {
    struct archive *ad; /* archive_write_disk object */

    /* Progress function invoked during extract. */
    void            (*extract_progress)(void *);
    void             *extract_progress_user_data;
};

static int  archive_read_extract_cleanup(struct archive_read *);
static int  copy_data(struct archive *ar, struct archive *aw);
static struct extract *get_extract(struct archive_read *);

static struct extract *
get_extract(struct archive_read *a)
{
    /* If we haven't initialized, do it now. */
    /* This also sets up a lot of global state. */
    if (a->extract == NULL) {
        a->extract = (struct extract *)malloc(sizeof(*a->extract));
        if (a->extract == NULL) {
            archive_set_error(&a->archive, ENOMEM, "Can't extract");
            return (NULL);
        }
        memset(a->extract, 0, sizeof(*a->extract));
        a->extract->ad = archive_write_disk_new();
        if (a->extract->ad == NULL) {
            archive_set_error(&a->archive, ENOMEM, "Can't extract");
            return (NULL);
        }
        archive_write_disk_set_standard_lookup(a->extract->ad);
        a->cleanup_archive_extract = archive_read_extract_cleanup;
    }
    return (a->extract);
}

int
archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
{
    struct extract *extract;

    extract = get_extract((struct archive_read *)_a);
    if (extract == NULL)
        return (ARCHIVE_FATAL);
    archive_write_disk_set_options(extract->ad, flags);
    return (archive_read_extract2(_a, entry, extract->ad));
}

int
archive_read_extract2(struct archive *_a, struct archive_entry *entry,
    struct archive *ad)
{
    struct archive_read *a = (struct archive_read *)_a;
    int r, r2;

    /* Set up for this particular entry. */
    archive_write_disk_set_skip_file(ad,
        a->skip_file_dev, a->skip_file_ino);
    r = archive_write_header(ad, entry);
    if (r < ARCHIVE_WARN)
        r = ARCHIVE_WARN;
    if (r != ARCHIVE_OK)
        /* If _write_header failed, copy the error. */
        archive_copy_error(&a->archive, ad);
    else
        /* Otherwise, pour data into the entry. */
        r = copy_data(_a, ad);
    r2 = archive_write_finish_entry(ad);
    if (r2 < ARCHIVE_WARN)
        r2 = ARCHIVE_WARN;
    /* Use the first message. */
    if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
        archive_copy_error(&a->archive, ad);
    /* Use the worst error return. */
    if (r2 < r)
        r = r2;
    return (r);
}

void
archive_read_extract_set_progress_callback(struct archive *_a,
    void (*progress_func)(void *), void *user_data)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct extract *extract = get_extract(a);
    if (extract != NULL) {
        extract->extract_progress = progress_func;
        extract->extract_progress_user_data = user_data;
    }
}

static int
copy_data(struct archive *ar, struct archive *aw)
{
    off_t offset;
    const void *buff;
    struct extract *extract;
    size_t size;
    int r;

    extract = get_extract((struct archive_read *)ar);
    for (;;) {
        r = archive_read_data_block(ar, &buff, &size, &offset);
        if (r == ARCHIVE_EOF)
            return (ARCHIVE_OK);
        if (r != ARCHIVE_OK)
            return (r);
        r = archive_write_data_block(aw, buff, size, offset);
        if (r < ARCHIVE_WARN)
            r = ARCHIVE_WARN;
        if (r != ARCHIVE_OK) {
            archive_set_error(ar, archive_errno(aw),
                "%s", archive_error_string(aw));
            return (r);
        }
        if (extract->extract_progress)
            (extract->extract_progress)
                (extract->extract_progress_user_data);
    }
}

/*
 * Cleanup function for archive_extract.
 */
static int
archive_read_extract_cleanup(struct archive_read *a)
{
    int ret = ARCHIVE_OK;

#if ARCHIVE_API_VERSION > 1
    ret =
#endif
        archive_write_finish(a->extract->ad);
    free(a->extract);
    a->extract = NULL;
    return (ret);
}

--- NEW FILE: archive_read.c ---
/*-
 * 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,
[...1202 lines suppressed...]
     * so even if client_skipper is provided, we may still
     * have to use ordinary reads to finish out the request.
     */
    while (request > 0) {
        const void* dummy_buffer;
        ssize_t bytes_read;
        dummy_buffer = __archive_read_filter_ahead(filter,
            1, &bytes_read);
        if (bytes_read < 0)
            return (bytes_read);
        if (bytes_read == 0) {
            return (total_bytes_skipped);
        }
        min = (size_t)(minimum(bytes_read, request));
        bytes_read = __archive_read_filter_consume(filter, min);
        total_bytes_skipped += bytes_read;
        request -= bytes_read;
    }
    return (total_bytes_skipped);
}

--- NEW FILE: archive_read_disk_entry_from_file.c ---
/*-
 * Copyright (c) 2003-2009 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD$");

#ifdef HAVE_SYS_TYPES_H
/* Mac OSX requires sys/types.h before sys/acl.h. */
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_ACL_H
#include <sys/acl.h>
#endif
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#endif
#ifdef HAVE_ACL_LIBACL_H
#include <acl/libacl.h>
#endif
#ifdef HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_disk_private.h"

/*
 * Linux and FreeBSD plug this obvious hole in POSIX.1e in
 * different ways.
 */
#if HAVE_ACL_GET_PERM
#define ACL_GET_PERM acl_get_perm
#elif HAVE_ACL_GET_PERM_NP
#define ACL_GET_PERM acl_get_perm_np
#endif

static int setup_acls_posix1e(struct archive_read_disk *,
    struct archive_entry *, int fd);
static int setup_xattrs(struct archive_read_disk *,
    struct archive_entry *, int fd);

int
archive_read_disk_entry_from_file(struct archive *_a,
    struct archive_entry *entry,
    int fd, const struct stat *st)
{
    struct archive_read_disk *a = (struct archive_read_disk *)_a;
    const char *path, *name;
    struct stat s;
    int initial_fd = fd;
    int r, r1;

    archive_clear_error(_a);
    path = archive_entry_sourcepath(entry);
    if (path == NULL)
        path = archive_entry_pathname(entry);

#ifdef EXT2_IOC_GETFLAGS
    /* Linux requires an extra ioctl to pull the flags.  Although
     * this is an extra step, it has a nice side-effect: We get an
     * open file descriptor which we can use in the subsequent lookups. */
    if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
        if (fd < 0)
            fd = open(pathname, O_RDONLY | O_NONBLOCK | O_BINARY);
        if (fd >= 0) {
            unsigned long stflags;
            int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
            if (r == 0 && stflags != 0)
                archive_entry_set_fflags(entry, stflags, 0);
        }
    }
#endif

    if (st == NULL) {
        /* TODO: On Windows, use GetFileInfoByHandle() here.
         * Using Windows stat() call is badly broken, but
         * even the stat() wrapper has problems because
         * 'struct stat' is broken on Windows.
         */
#if HAVE_FSTAT
        if (fd >= 0) {
            if (fstat(fd, &s) != 0)
                return (ARCHIVE_FATAL);
        } else
#endif
#if HAVE_LSTAT
        if (!a->follow_symlinks) {
            if (lstat(path, &s) != 0)
                return (ARCHIVE_FATAL);
        } else
#endif
        if (stat(path, &s) != 0)
            return (ARCHIVE_FATAL);
        st = &s;
    }
    archive_entry_copy_stat(entry, st);

    /* Lookup uname/gname */
    name = archive_read_disk_uname(_a, archive_entry_uid(entry));
    if (name != NULL)
        archive_entry_copy_uname(entry, name);
    name = archive_read_disk_gname(_a, archive_entry_gid(entry));
    if (name != NULL)
        archive_entry_copy_gname(entry, name);

#ifdef HAVE_STRUCT_STAT_ST_FLAGS
    /* On FreeBSD, we get flags for free with the stat. */
    /* TODO: Does this belong in copy_stat()? */
    if (st->st_flags != 0)
        archive_entry_set_fflags(entry, st->st_flags, 0);
#endif

#ifdef HAVE_READLINK
    if (S_ISLNK(st->st_mode)) {
        char linkbuffer[PATH_MAX + 1];
        int lnklen = readlink(path, linkbuffer, PATH_MAX);
        if (lnklen < 0) {
            archive_set_error(&a->archive, errno,
                "Couldn't read link data");
            return (ARCHIVE_WARN);
        }
        linkbuffer[lnklen] = 0;
        archive_entry_set_symlink(entry, linkbuffer);
    }
#endif

    r = setup_acls_posix1e(a, entry, fd);
    r1 = setup_xattrs(a, entry, fd);
    if (r1 < r)
        r = r1;
    /* If we opened the file earlier in this function, close it. */
    if (initial_fd != fd)
        close(fd);
    return (r);
}

#ifdef HAVE_POSIX_ACL
static void setup_acl_posix1e(struct archive_read_disk *a,
    struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);

static int
setup_acls_posix1e(struct archive_read_disk *a,
    struct archive_entry *entry, int fd)
{
    const char  *accpath;
    acl_t        acl;

    accpath = archive_entry_sourcepath(entry);
    if (accpath == NULL)
        accpath = archive_entry_pathname(entry);

    archive_entry_acl_clear(entry);

    /* Retrieve access ACL from file. */
    if (fd >= 0)
        acl = acl_get_fd(fd);
#if HAVE_ACL_GET_LINK_NP
    else if (!a->follow_symlinks)
        acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
#endif
    else
        acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
    if (acl != NULL) {
        setup_acl_posix1e(a, entry, acl,
            ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
        acl_free(acl);
    }

    /* Only directories can have default ACLs. */
    if (S_ISDIR(archive_entry_mode(entry))) {
        acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
        if (acl != NULL) {
            setup_acl_posix1e(a, entry, acl,
                ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
            acl_free(acl);
        }
    }
    return (ARCHIVE_OK);
}

/*
 * Translate POSIX.1e ACL into libarchive internal structure.
 */
static void
setup_acl_posix1e(struct archive_read_disk *a,
    struct archive_entry *entry, acl_t acl, int archive_entry_acl_type)
{
    acl_tag_t    acl_tag;
    acl_entry_t  acl_entry;
    acl_permset_t    acl_permset;
    int      s, ae_id, ae_tag, ae_perm;
    const char  *ae_name;

    s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
    while (s == 1) {
        ae_id = -1;
        ae_name = NULL;

        acl_get_tag_type(acl_entry, &acl_tag);
        if (acl_tag == ACL_USER) {
            ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
            ae_name = archive_read_disk_uname(&a->archive, ae_id);
            ae_tag = ARCHIVE_ENTRY_ACL_USER;
        } else if (acl_tag == ACL_GROUP) {
            ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
            ae_name = archive_read_disk_gname(&a->archive, ae_id);
            ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
        } else if (acl_tag == ACL_MASK) {
            ae_tag = ARCHIVE_ENTRY_ACL_MASK;
        } else if (acl_tag == ACL_USER_OBJ) {
            ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
        } else if (acl_tag == ACL_GROUP_OBJ) {
            ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
        } else if (acl_tag == ACL_OTHER) {
            ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
        } else {
            /* Skip types that libarchive can't support. */
            continue;
        }

        acl_get_permset(acl_entry, &acl_permset);
        ae_perm = 0;
        /*
         * acl_get_perm() is spelled differently on different
         * platforms; see above.
         */
        if (ACL_GET_PERM(acl_permset, ACL_EXECUTE))
            ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
        if (ACL_GET_PERM(acl_permset, ACL_READ))
            ae_perm |= ARCHIVE_ENTRY_ACL_READ;
        if (ACL_GET_PERM(acl_permset, ACL_WRITE))
            ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;

        archive_entry_acl_add_entry(entry,
            archive_entry_acl_type, ae_perm, ae_tag,
            ae_id, ae_name);

        s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
    }
}
#else
static int
setup_acls_posix1e(struct archive_read_disk *a,
    struct archive_entry *entry, int fd)
{
    (void)a;      /* UNUSED */
    (void)entry;  /* UNUSED */
    (void)fd;     /* UNUSED */
    return (ARCHIVE_OK);
}
#endif

#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR

/*
 * Linux extended attribute support.
 *
 * TODO:  By using a stack-allocated buffer for the first
 * call to getxattr(), we might be able to avoid the second
 * call entirely.  We only need the second call if the
 * stack-allocated buffer is too small.  But a modest buffer
 * of 1024 bytes or so will often be big enough.  Same applies
 * to listxattr().
 */


static int
setup_xattr(struct archive_read_disk *a,
    struct archive_entry *entry, const char *name, int fd)
{
    ssize_t size;
    void *value = NULL;
    const char *accpath;

    (void)fd; /* UNUSED */

    accpath = archive_entry_sourcepath(entry);
    if (accpath == NULL)
        accpath = archive_entry_pathname(entry);

    if (!a->follow_symlinks)
        size = lgetxattr(accpath, name, NULL, 0);
    else
        size = getxattr(accpath, name, NULL, 0);

    if (size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't query extended attribute");
        return (ARCHIVE_WARN);
    }

    if (size > 0 && (value = malloc(size)) == NULL) {
        archive_set_error(&a->archive, errno, "Out of memory");
        return (ARCHIVE_FATAL);
    }

    if (!a->follow_symlinks)
        size = lgetxattr(accpath, name, value, size);
    else
        size = getxattr(accpath, name, value, size);

    if (size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't read extended attribute");
        return (ARCHIVE_WARN);
    }

    archive_entry_xattr_add_entry(entry, name, value, size);

    free(value);
    return (ARCHIVE_OK);
}

static int
setup_xattrs(struct archive_read_disk *a,
    struct archive_entry *entry, int fd)
{
    char *list, *p;
    const char *path;
    ssize_t list_size;


    path = archive_entry_sourcepath(entry);
    if (path == NULL)
        path = archive_entry_pathname(entry);

    if (!a->follow_symlinks)
        list_size = llistxattr(path, NULL, 0);
    else
        list_size = listxattr(path, NULL, 0);

    if (list_size == -1) {
        if (errno == ENOTSUP)
            return (ARCHIVE_OK);
        archive_set_error(&a->archive, errno,
            "Couldn't list extended attributes");
        return (ARCHIVE_WARN);
    }

    if (list_size == 0)
        return (ARCHIVE_OK);

    if ((list = malloc(list_size)) == NULL) {
        archive_set_error(&a->archive, errno, "Out of memory");
        return (ARCHIVE_FATAL);
    }

    if (!a->follow_symlinks)
        list_size = llistxattr(path, list, list_size);
    else
        list_size = listxattr(path, list, list_size);

    if (list_size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't retrieve extended attributes");
        free(list);
        return (ARCHIVE_WARN);
    }

    for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
        if (strncmp(p, "system.", 7) == 0 ||
                strncmp(p, "xfsroot.", 8) == 0)
            continue;
        setup_xattr(a, entry, p, fd);
    }

    free(list);
    return (ARCHIVE_OK);
}

#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE

/*
 * FreeBSD extattr interface.
 */

/* TODO: Implement this.  Follow the Linux model above, but
 * with FreeBSD-specific system calls, of course.  Be careful
 * to not include the system extattrs that hold ACLs; we handle
 * those separately.
 */
int
setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
    int namespace, const char *name, const char *fullname, int fd);

int
setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
    int namespace, const char *name, const char *fullname, int fd)
{
    ssize_t size;
    void *value = NULL;
    const char *accpath;

    (void)fd; /* UNUSED */

    accpath = archive_entry_sourcepath(entry);
    if (accpath == NULL)
        accpath = archive_entry_pathname(entry);

    if (!a->follow_symlinks)
        size = extattr_get_link(accpath, namespace, name, NULL, 0);
    else
        size = extattr_get_file(accpath, namespace, name, NULL, 0);

    if (size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't query extended attribute");
        return (ARCHIVE_WARN);
    }

    if (size > 0 && (value = malloc(size)) == NULL) {
        archive_set_error(&a->archive, errno, "Out of memory");
        return (ARCHIVE_FATAL);
    }

    if (!a->follow_symlinks)
        size = extattr_get_link(accpath, namespace, name, value, size);
    else
        size = extattr_get_file(accpath, namespace, name, value, size);

    if (size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't read extended attribute");
        return (ARCHIVE_WARN);
    }

    archive_entry_xattr_add_entry(entry, fullname, value, size);

    free(value);
    return (ARCHIVE_OK);
}

static int
setup_xattrs(struct archive_read_disk *a,
    struct archive_entry *entry, int fd)
{
    char buff[512];
    char *list, *p;
    ssize_t list_size;
    const char *path;
    int namespace = EXTATTR_NAMESPACE_USER;

    path = archive_entry_sourcepath(entry);
    if (path == NULL)
        path = archive_entry_pathname(entry);

    if (!a->follow_symlinks)
        list_size = extattr_list_link(path, namespace, NULL, 0);
    else
        list_size = extattr_list_file(path, namespace, NULL, 0);

    if (list_size == -1 && errno == EOPNOTSUPP)
        return (ARCHIVE_OK);
    if (list_size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't list extended attributes");
        return (ARCHIVE_WARN);
    }

    if (list_size == 0)
        return (ARCHIVE_OK);

    if ((list = malloc(list_size)) == NULL) {
        archive_set_error(&a->archive, errno, "Out of memory");
        return (ARCHIVE_FATAL);
    }

    if (!a->follow_symlinks)
        list_size = extattr_list_link(path, namespace, list, list_size);
    else
        list_size = extattr_list_file(path, namespace, list, list_size);

    if (list_size == -1) {
        archive_set_error(&a->archive, errno,
            "Couldn't retrieve extended attributes");
        free(list);
        return (ARCHIVE_WARN);
    }

    p = list;
    while ((p - list) < list_size) {
        size_t len = 255 & (int)*p;
        char *name;

        strcpy(buff, "user.");
        name = buff + strlen(buff);
        memcpy(name, p + 1, len);
        name[len] = '\0';
        setup_xattr(a, entry, namespace, name, buff, fd);
        p += 1 + len;
    }

    free(list);
    return (ARCHIVE_OK);
}

#else

/*
 * Generic (stub) extended attribute support.
 */
static int
setup_xattrs(struct archive_read_disk *a,
    struct archive_entry *entry, int fd)
{
    (void)a;     /* UNUSED */
    (void)entry; /* UNUSED */
    (void)fd;    /* UNUSED */
    return (ARCHIVE_OK);
}

#endif

--- NEW FILE: archive_util.c ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.19 2008/10/21 12:10:30 des Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_string.h"

#if ARCHIVE_VERSION_NUMBER < 3000000
/* These disappear in libarchive 3.0 */
/* Deprecated. */
int
archive_api_feature(void)
{
    return (ARCHIVE_API_FEATURE);
}

/* Deprecated. */
int
archive_api_version(void)
{
    return (ARCHIVE_API_VERSION);
}

/* Deprecated synonym for archive_version_number() */
int
archive_version_stamp(void)
{
    return (archive_version_number());
}

/* Deprecated synonym for archive_version_string() */
const char *
archive_version(void)
{
    return (archive_version_string());
}
#endif

int
archive_version_number(void)
{
    return (ARCHIVE_VERSION_NUMBER);
}

const char *
archive_version_string(void)
{
    return (ARCHIVE_VERSION_STRING);
}

int
archive_errno(struct archive *a)
{
    return (a->archive_error_number);
}

const char *
archive_error_string(struct archive *a)
{

    if (a->error != NULL  &&  *a->error != '\0')
        return (a->error);
    else
        return ("(Empty error message)");
}

int
archive_file_count(struct archive *a)
{
    return (a->file_count);
}

int
archive_format(struct archive *a)
{
    return (a->archive_format);
}

const char *
archive_format_name(struct archive *a)
{
    return (a->archive_format_name);
}


int
archive_compression(struct archive *a)
{
    return (a->compression_code);
}

const char *
archive_compression_name(struct archive *a)
{
    return (a->compression_name);
}


/*
 * Return a count of the number of compressed bytes processed.
 */
int64_t
archive_position_compressed(struct archive *a)
{
    return (a->raw_position);
}

/*
 * Return a count of the number of uncompressed bytes processed.
 */
int64_t
archive_position_uncompressed(struct archive *a)
{
    return (a->file_position);
}

void
archive_clear_error(struct archive *a)
{
    archive_string_empty(&a->error_string);
    a->error = NULL;
}

void
archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
{
    va_list ap;

    a->archive_error_number = error_number;
    if (fmt == NULL) {
        a->error = NULL;
        return;
    }

    va_start(ap, fmt);
    archive_string_vsprintf(&(a->error_string), fmt, ap);
    va_end(ap);
    a->error = a->error_string.s;
}

void
archive_copy_error(struct archive *dest, struct archive *src)
{
    dest->archive_error_number = src->archive_error_number;

    archive_string_copy(&dest->error_string, &src->error_string);
    dest->error = dest->error_string.s;
}

void
__archive_errx(int retvalue, const char *msg)
{
    static const char *msg1 = "Fatal Internal Error in libarchive: ";
    size_t s;

    s = write(2, msg1, strlen(msg1));
    s = write(2, msg, strlen(msg));
    s = write(2, "\n", 1);
    (void)s; /* UNUSED */
    exit(retvalue);
}

/*
 * Parse option strings
 *  Detail of option format.
 *    - The option can accept:
 *     "opt-name", "!opt-name", "opt-name=value".
 *
 *    - The option entries are separated by comma.
 *        e.g  "compression=9,opt=XXX,opt-b=ZZZ"
 *
 *    - The name of option string consist of '-' and alphabet
 *      but character '-' cannot be used for the first character.
 *      (Regular expression is [a-z][-a-z]+)
 *
 *    - For a specfic format/filter, using the format name with ':'.
 *        e.g  "zip:compression=9"
 *        (This "compression=9" option entry is for "zip" format only)
 *
 *      If another entries follow it, those are not for
 *      the specfic format/filter.
 *        e.g  handle "zip:compression=9,opt=XXX,opt-b=ZZZ"
 *          "zip" format/filter handler will get "compression=9"
 *          all format/filter handler will get "opt=XXX"
 *          all format/filter handler will get "opt-b=ZZZ"
 *
 *    - Whitespace and tab are bypassed.
 *
 */
int
__archive_parse_options(const char *p, const char *fn, int keysize, char *key,
    int valsize, char *val)
{
    const char *p_org;
    int apply;
    int kidx, vidx;
    int negative; 
    enum {
        /* Requested for initialization. */
        INIT,
        /* Finding format/filter-name and option-name. */
        F_BOTH,
        /* Finding option-name only.
         * (already detected format/filter-name) */
        F_NAME,
        /* Getting option-value. */
        G_VALUE,
    } state;

    p_org = p;
    state = INIT;
    kidx = vidx = negative = 0;
    apply = 1;
    while (*p) {
        switch (state) {
        case INIT:
            kidx = vidx = 0;
            negative = 0;
            apply = 1;
            state = F_BOTH;
            break;
        case F_BOTH:
        case F_NAME:
            if ((*p >= 'a' && *p <= 'z') ||
                (*p >= '0' && *p <= '9') || *p == '-') {
                if (kidx == 0 && !(*p >= 'a' && *p <= 'z'))
                    /* Illegal sequence. */
                    return (-1);
                if (kidx >= keysize -1)
                    /* Too many characters. */
                    return (-1);
                key[kidx++] = *p++;
            } else if (*p == '!') {
                if (kidx != 0)
                    /* Illegal sequence. */
                    return (-1);
                negative = 1;
                ++p;
            } else if (*p == ',') {
                if (kidx == 0)
                    /* Illegal sequence. */
                    return (-1);
                if (!negative)
                    val[vidx++] = '1';
                /* We have got boolean option data. */
                ++p;
                if (apply)
                    goto complete;
                else
                    /* This option does not apply to the
                     * format which the fn variable
                     * indicate. */
                    state = INIT;
            } else if (*p == ':') {
                /* obuf data is format name */
                if (state == F_NAME)
                    /* We already found it. */
                    return (-1);
                if (kidx == 0)
                    /* Illegal sequence. */
                    return (-1);
                if (negative)
                    /* We cannot accept "!format-name:". */
                    return (-1);
                key[kidx] = '\0';
                if (strcmp(fn, key) != 0)
                    /* This option does not apply to the
                     * format which the fn variable
                     * indicate. */
                    apply = 0;
                kidx = 0;
                ++p;
                state = F_NAME;
            } else if (*p == '=') {
                if (kidx == 0)
                    /* Illegal sequence. */
                    return (-1);
                if (negative)
                    /* We cannot accept "!opt-name=value". */
                    return (-1);
                ++p;
                state = G_VALUE;
            } else if (*p == ' ') {
                /* Pass the space character */
                ++p;
            } else {
                /* Illegal character. */
                return (-1);
            }
            break;
        case G_VALUE:
            if (*p == ',') {
                if (vidx == 0)
                    /* Illegal sequence. */
                    return (-1);
                /* We have got option data. */
                ++p;
                if (apply)
                    goto complete;
                else
                    /* This option does not apply to the
                     * format which the fn variable
                     * indicate. */
                    state = INIT;
            } else if (*p == ' ') {
                /* Pass the space character */
                ++p;
            } else {
                if (vidx >= valsize -1)
                    /* Too many characters. */
                    return (-1);
                val[vidx++] = *p++;
            }
            break;
        } 
    }

    switch (state) {
    case F_BOTH:
    case F_NAME:
        if (kidx != 0) {
            if (!negative)
                val[vidx++] = '1';
            /* We have got boolean option. */
            if (apply)
                /* This option apply to the format which the
                 * fn variable indicate. */
                goto complete;
        }
        break;
    case G_VALUE:
        if (vidx == 0)
            /* Illegal sequence. */
            return (-1);
        /* We have got option value. */
        if (apply)
            /* This option apply to the format which the fn
             * variable indicate. */
            goto complete;
        break;
    case INIT:/* nothing */
        break;
    }

    /* End of Option string. */
    return (0);

complete:
    key[kidx] = '\0';
    val[vidx] = '\0';
    /* Return a size which we've consumed for detecting option */
    return ((int)(p - p_org));
}

--- NEW FILE: archive_read_support_format_iso9660.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * Copyright (c) 2009 Andreas Henriksson <andreas at fatal.se>
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
[...2036 lines suppressed...]
    fprintf(out, " a %d,",
        toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size));
    fprintf(out, " ext 0x%x,",
        toi(isodirrec + DR_extent_offset, DR_extent_size));
    fprintf(out, " s %d,",
        toi(isodirrec + DR_size_offset, DR_extent_size));
    fprintf(out, " f 0x%02x,",
        toi(isodirrec + DR_flags_offset, DR_flags_size));
    fprintf(out, " u %d,",
        toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));
    fprintf(out, " ilv %d,",
        toi(isodirrec + DR_interleave_offset, DR_interleave_size));
    fprintf(out, " seq %d,",
        toi(isodirrec + DR_volume_sequence_number_offset, DR_volume_sequence_number_size));
    fprintf(out, " nl %d:",
        toi(isodirrec + DR_name_len_offset, DR_name_len_size));
    fprintf(out, " `%.*s'",
        toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset);
}
#endif

--- NEW FILE: archive_read_private.h ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.7 2008/12/06 06:45:15 kientzle Exp $
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
#define ARCHIVE_READ_PRIVATE_H_INCLUDED

#include "archive.h"
#include "archive_string.h"
#include "archive_private.h"

struct archive_read;
struct archive_read_filter_bidder;
struct archive_read_filter;

/*
 * How bidding works for filters:
 *   * The bid manager reads the first block from the current source.
 *   * It shows that block to each registered bidder.
 *   * The bid manager creates a new filter structure for the winning
 *     bidder and gives the winning bidder a chance to initialize it.
 *   * The new filter becomes the top filter in the archive_read structure
 *     and we repeat the process.
 * This ends only when no bidder provides a non-zero bid.
 */
struct archive_read_filter_bidder {
    /* Configuration data for the bidder. */
    void *data;
    /* Taste the upstream filter to see if we handle this. */
    int (*bid)(struct archive_read_filter_bidder *,
        struct archive_read_filter *);
    /* Initialize a newly-created filter. */
    int (*init)(struct archive_read_filter *);
    /* Set an option for the filter bidder. */
    int (*options)(struct archive_read_filter_bidder *,
        const char *key, const char *value);
    /* Release the bidder's configuration data. */
    int (*free)(struct archive_read_filter_bidder *);
};

/*
 * This structure is allocated within the archive_read core
 * and initialized by archive_read and the init() method of the
 * corresponding bidder above.
 */
struct archive_read_filter {
    /* Essentially all filters will need these values, so
     * just declare them here. */
    struct archive_read_filter_bidder *bidder; /* My bidder. */
    struct archive_read_filter *upstream; /* Who I read from. */
    struct archive_read *archive; /* Associated archive. */
    /* Return next block. */
    ssize_t (*read)(struct archive_read_filter *, const void **);
    /* Skip forward this many bytes. */
    int64_t (*skip)(struct archive_read_filter *self, int64_t request);
    /* Close (just this filter) and free(self). */
    int (*close)(struct archive_read_filter *self);
    /* My private data. */
    void *data;

    const char  *name;
    int      code;

    /* Used by reblocking logic. */
    char        *buffer;
    size_t       buffer_size;
    char        *next;      /* Current read location. */
    size_t       avail;     /* Bytes in my buffer. */
    const void  *client_buff;   /* Client buffer information. */
    size_t       client_total;
    const char  *client_next;
    size_t       client_avail;
    int64_t      position;
    char         end_of_file;
    char         fatal;
};

/*
 * The client looks a lot like a filter, so we just wrap it here.
 *
 * TODO: Make archive_read_filter and archive_read_client identical so
 * that users of the library can easily register their own
 * transformation filters.  This will probably break the API/ABI and
 * so should be deferred at least until libarchive 3.0.
 */
struct archive_read_client {
    archive_read_callback   *reader;
    archive_skip_callback   *skipper;
    archive_close_callback  *closer;
};

struct archive_read {
    struct archive  archive;

    struct archive_entry    *entry;

    /* Dev/ino of the archive being read/written. */
    dev_t         skip_file_dev;
    ino_t         skip_file_ino;

    /*
     * Used by archive_read_data() to track blocks and copy
     * data to client buffers, filling gaps with zero bytes.
     */
    const char   *read_data_block;
    off_t         read_data_offset;
    off_t         read_data_output_offset;
    size_t        read_data_remaining;

    /* Callbacks to open/read/write/close client archive stream. */
    struct archive_read_client client;

    /* Registered filter bidders. */
    struct archive_read_filter_bidder bidders[8];

    /* Last filter in chain */
    struct archive_read_filter *filter;

    /* File offset of beginning of most recently-read header. */
    off_t         header_position;

    /*
     * Format detection is mostly the same as compression
     * detection, with one significant difference: The bidders
     * use the read_ahead calls above to examine the stream rather
     * than having the supervisor hand them a block of data to
     * examine.
     */

    struct archive_format_descriptor {
        void     *data;
        const char *name;
        int (*bid)(struct archive_read *);
        int (*options)(struct archive_read *, const char *key,
            const char *value);
        int (*read_header)(struct archive_read *, struct archive_entry *);
        int (*read_data)(struct archive_read *, const void **, size_t *, off_t *);
        int (*read_data_skip)(struct archive_read *);
        int (*cleanup)(struct archive_read *);
    }   formats[8];
    struct archive_format_descriptor    *format; /* Active format. */

    /*
     * Various information needed by archive_extract.
     */
    struct extract       *extract;
    int         (*cleanup_archive_extract)(struct archive_read *);
};

int __archive_read_register_format(struct archive_read *a,
        void *format_data,
        const char *name,
        int (*bid)(struct archive_read *),
        int (*options)(struct archive_read *, const char *, const char *),
        int (*read_header)(struct archive_read *, struct archive_entry *),
        int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
        int (*read_data_skip)(struct archive_read *),
        int (*cleanup)(struct archive_read *));

struct archive_read_filter_bidder
    *__archive_read_get_bidder(struct archive_read *a);

const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
const void *__archive_read_filter_ahead(struct archive_read_filter *,
    size_t, ssize_t *);
ssize_t __archive_read_consume(struct archive_read *, size_t);
ssize_t __archive_read_filter_consume(struct archive_read_filter *, size_t);
int64_t __archive_read_skip(struct archive_read *, int64_t);
int64_t __archive_read_skip_lenient(struct archive_read *, int64_t);
int64_t __archive_read_filter_skip(struct archive_read_filter *, int64_t);
int __archive_read_program(struct archive_read_filter *, const char *);
#endif

--- NEW FILE: archive_read_support_format_ar.c ---
/*-
 * Copyright (c) 2007 Kai Wang
 * Copyright (c) 2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.12 2008/12/17 19:02:42 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_private.h"

struct ar {
    off_t    entry_bytes_remaining;
    off_t    entry_offset;
    off_t    entry_padding;
    char    *strtab;
    size_t   strtab_size;
};

/*
 * Define structure of the "ar" header.
 */
#define AR_name_offset 0
#define AR_name_size 16
#define AR_date_offset 16
#define AR_date_size 12
#define AR_uid_offset 28
#define AR_uid_size 6
#define AR_gid_offset 34
#define AR_gid_size 6
#define AR_mode_offset 40
#define AR_mode_size 8
#define AR_size_offset 48
#define AR_size_size 10
#define AR_fmag_offset 58
#define AR_fmag_size 2

static int  archive_read_format_ar_bid(struct archive_read *a);
static int  archive_read_format_ar_cleanup(struct archive_read *a);
static int  archive_read_format_ar_read_data(struct archive_read *a,
            const void **buff, size_t *size, off_t *offset);
static int  archive_read_format_ar_skip(struct archive_read *a);
static int  archive_read_format_ar_read_header(struct archive_read *a,
            struct archive_entry *e);
static uint64_t ar_atol8(const char *p, unsigned char_cnt);
static uint64_t ar_atol10(const char *p, unsigned char_cnt);
static int  ar_parse_gnu_filename_table(struct archive_read *a);
static int  ar_parse_common_header(struct ar *ar, struct archive_entry *,
            const char *h);

int
archive_read_support_format_ar(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct ar *ar;
    int r;

    ar = (struct ar *)malloc(sizeof(*ar));
    if (ar == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate ar data");
        return (ARCHIVE_FATAL);
    }
    memset(ar, 0, sizeof(*ar));
    ar->strtab = NULL;

    r = __archive_read_register_format(a,
        ar,
        "ar",
        archive_read_format_ar_bid,
        NULL,
        archive_read_format_ar_read_header,
        archive_read_format_ar_read_data,
        archive_read_format_ar_skip,
        archive_read_format_ar_cleanup);

    if (r != ARCHIVE_OK) {
        free(ar);
        return (r);
    }
    return (ARCHIVE_OK);
}

static int
archive_read_format_ar_cleanup(struct archive_read *a)
{
    struct ar *ar;

    ar = (struct ar *)(a->format->data);
    if (ar->strtab)
        free(ar->strtab);
    free(ar);
    (a->format->data) = NULL;
    return (ARCHIVE_OK);
}

static int
archive_read_format_ar_bid(struct archive_read *a)
{
    struct ar *ar;
    const void *h;

    if (a->archive.archive_format != 0 &&
        (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
        ARCHIVE_FORMAT_AR)
        return(0);

    ar = (struct ar *)(a->format->data);

    /*
     * Verify the 8-byte file signature.
     * TODO: Do we need to check more than this?
     */
    if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)
        return (-1);
    if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
        return (64);
    }
    return (-1);
}

static int
archive_read_format_ar_read_header(struct archive_read *a,
    struct archive_entry *entry)
{
    char filename[AR_name_size + 1];
    struct ar *ar;
    uint64_t number; /* Used to hold parsed numbers before validation. */
    ssize_t bytes_read;
    size_t bsd_name_length, entry_size;
    char *p, *st;
    const void *b;
    const char *h;
    int r;

    ar = (struct ar*)(a->format->data);

    if (a->archive.file_position == 0) {
        /*
         * We are now at the beginning of the archive,
         * so we need first consume the ar global header.
         */
        __archive_read_consume(a, 8);
        /* Set a default format code for now. */
        a->archive.archive_format = ARCHIVE_FORMAT_AR;
    }

    /* Read the header for the next file entry. */
    if ((b = __archive_read_ahead(a, 60, &bytes_read)) == NULL)
        /* Broken header. */
        return (ARCHIVE_EOF);
    __archive_read_consume(a, 60);
    h = (const char *)b;

    /* Verify the magic signature on the file header. */
    if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
        archive_set_error(&a->archive, EINVAL,
            "Incorrect file header signature");
        return (ARCHIVE_WARN);
    }

    /* Copy filename into work buffer. */
    strncpy(filename, h + AR_name_offset, AR_name_size);
    filename[AR_name_size] = '\0';

    /*
     * Guess the format variant based on the filename.
     */
    if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {
        /* We don't already know the variant, so let's guess. */
        /*
         * Biggest clue is presence of '/': GNU starts special
         * filenames with '/', appends '/' as terminator to
         * non-special names, so anything with '/' should be
         * GNU except for BSD long filenames.
         */
        if (strncmp(filename, "#1/", 3) == 0)
            a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
        else if (strchr(filename, '/') != NULL)
            a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
        else if (strncmp(filename, "__.SYMDEF", 9) == 0)
            a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
        /*
         * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'
         * if name exactly fills 16-byte field?  If so, we
         * can't assume entries without '/' are BSD. XXX
         */
    }

    /* Update format name from the code. */
    if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)
        a->archive.archive_format_name = "ar (GNU/SVR4)";
    else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)
        a->archive.archive_format_name = "ar (BSD)";
    else
        a->archive.archive_format_name = "ar";

    /*
     * Remove trailing spaces from the filename.  GNU and BSD
     * variants both pad filename area out with spaces.
     * This will only be wrong if GNU/SVR4 'ar' implementations
     * omit trailing '/' for 16-char filenames and we have
     * a 16-char filename that ends in ' '.
     */
    p = filename + AR_name_size - 1;
    while (p >= filename && *p == ' ') {
        *p = '\0';
        p--;
    }

    /*
     * Remove trailing slash unless first character is '/'.
     * (BSD entries never end in '/', so this will only trim
     * GNU-format entries.  GNU special entries start with '/'
     * and are not terminated in '/', so we don't trim anything
     * that starts with '/'.)
     */
    if (filename[0] != '/' && *p == '/')
        *p = '\0';

    /*
     * '//' is the GNU filename table.
     * Later entries can refer to names in this table.
     */
    if (strcmp(filename, "//") == 0) {
        /* This must come before any call to _read_ahead. */
        ar_parse_common_header(ar, entry, h);
        archive_entry_copy_pathname(entry, filename);
        archive_entry_set_filetype(entry, AE_IFREG);
        /* Get the size of the filename table. */
        number = ar_atol10(h + AR_size_offset, AR_size_size);
        if (number > SIZE_MAX) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Filename table too large");
            return (ARCHIVE_FATAL);
        }
        entry_size = (size_t)number;
        if (entry_size == 0) {
            archive_set_error(&a->archive, EINVAL,
                "Invalid string table");
            return (ARCHIVE_WARN);
        }
        if (ar->strtab != NULL) {
            archive_set_error(&a->archive, EINVAL,
                "More than one string tables exist");
            return (ARCHIVE_WARN);
        }

        /* Read the filename table into memory. */
        st = malloc(entry_size);
        if (st == NULL) {
            archive_set_error(&a->archive, ENOMEM,
                "Can't allocate filename table buffer");
            return (ARCHIVE_FATAL);
        }
        ar->strtab = st;
        ar->strtab_size = entry_size;
        if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)
            return (ARCHIVE_FATAL);
        memcpy(st, b, entry_size);
        __archive_read_consume(a, entry_size);
        /* All contents are consumed. */
        ar->entry_bytes_remaining = 0;
        archive_entry_set_size(entry, ar->entry_bytes_remaining);

        /* Parse the filename table. */
        return (ar_parse_gnu_filename_table(a));
    }

    /*
     * GNU variant handles long filenames by storing /<number>
     * to indicate a name stored in the filename table.
     * XXX TODO: Verify that it's all digits... Don't be fooled
     * by "/9xyz" XXX
     */
    if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
        number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
        /*
         * If we can't look up the real name, warn and return
         * the entry with the wrong name.
         */
        if (ar->strtab == NULL || number > ar->strtab_size) {
            archive_set_error(&a->archive, EINVAL,
                "Can't find long filename for entry");
            archive_entry_copy_pathname(entry, filename);
            /* Parse the time, owner, mode, size fields. */
            ar_parse_common_header(ar, entry, h);
            return (ARCHIVE_WARN);
        }

        archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
        /* Parse the time, owner, mode, size fields. */
        return (ar_parse_common_header(ar, entry, h));
    }

    /*
     * BSD handles long filenames by storing "#1/" followed by the
     * length of filename as a decimal number, then prepends the
     * the filename to the file contents.
     */
    if (strncmp(filename, "#1/", 3) == 0) {
        /* Parse the time, owner, mode, size fields. */
        /* This must occur before _read_ahead is called again. */
        ar_parse_common_header(ar, entry, h);

        /* Parse the size of the name, adjust the file size. */
        number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
        bsd_name_length = (size_t)number;
        /* Guard against the filename + trailing NUL
         * overflowing a size_t and against the filename size
         * being larger than the entire entry. */
        if (number > (uint64_t)(bsd_name_length + 1)
            || (off_t)bsd_name_length > ar->entry_bytes_remaining) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Bad input file size");
            return (ARCHIVE_FATAL);
        }
        ar->entry_bytes_remaining -= bsd_name_length;
        /* Adjust file size reported to client. */
        archive_entry_set_size(entry, ar->entry_bytes_remaining);

        /* Read the long name into memory. */
        if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Truncated input file");
            return (ARCHIVE_FATAL);
        }
        __archive_read_consume(a, bsd_name_length);

        /* Store it in the entry. */
        p = (char *)malloc(bsd_name_length + 1);
        if (p == NULL) {
            archive_set_error(&a->archive, ENOMEM,
                "Can't allocate fname buffer");
            return (ARCHIVE_FATAL);
        }
        strncpy(p, b, bsd_name_length);
        p[bsd_name_length] = '\0';
        archive_entry_copy_pathname(entry, p);
        free(p);
        return (ARCHIVE_OK);
    }

    /*
     * "/" is the SVR4/GNU archive symbol table.
     */
    if (strcmp(filename, "/") == 0) {
        archive_entry_copy_pathname(entry, "/");
        /* Parse the time, owner, mode, size fields. */
        r = ar_parse_common_header(ar, entry, h);
        /* Force the file type to a regular file. */
        archive_entry_set_filetype(entry, AE_IFREG);
        return (r);
    }

    /*
     * "__.SYMDEF" is a BSD archive symbol table.
     */
    if (strcmp(filename, "__.SYMDEF") == 0) {
        archive_entry_copy_pathname(entry, filename);
        /* Parse the time, owner, mode, size fields. */
        return (ar_parse_common_header(ar, entry, h));
    }

    /*
     * Otherwise, this is a standard entry.  The filename
     * has already been trimmed as much as possible, based
     * on our current knowledge of the format.
     */
    archive_entry_copy_pathname(entry, filename);
    return (ar_parse_common_header(ar, entry, h));
}

static int
ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
    const char *h)
{
    uint64_t n;

    /* Copy remaining header */
    archive_entry_set_mtime(entry,
        (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
    archive_entry_set_uid(entry,
        (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size));
    archive_entry_set_gid(entry,
        (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
    archive_entry_set_mode(entry,
        (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
    n = ar_atol10(h + AR_size_offset, AR_size_size);

    ar->entry_offset = 0;
    ar->entry_padding = n % 2;
    archive_entry_set_size(entry, n);
    ar->entry_bytes_remaining = n;
    return (ARCHIVE_OK);
}

static int
archive_read_format_ar_read_data(struct archive_read *a,
    const void **buff, size_t *size, off_t *offset)
{
    ssize_t bytes_read;
    struct ar *ar;

    ar = (struct ar *)(a->format->data);

    if (ar->entry_bytes_remaining > 0) {
        *buff = __archive_read_ahead(a, 1, &bytes_read);
        if (bytes_read == 0) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Truncated ar archive");
            return (ARCHIVE_FATAL);
        }
        if (bytes_read < 0)
            return (ARCHIVE_FATAL);
        if (bytes_read > ar->entry_bytes_remaining)
            bytes_read = (ssize_t)ar->entry_bytes_remaining;
        *size = bytes_read;
        *offset = ar->entry_offset;
        ar->entry_offset += bytes_read;
        ar->entry_bytes_remaining -= bytes_read;
        __archive_read_consume(a, (size_t)bytes_read);
        return (ARCHIVE_OK);
    } else {
        while (ar->entry_padding > 0) {
            *buff = __archive_read_ahead(a, 1, &bytes_read);
            if (bytes_read <= 0)
                return (ARCHIVE_FATAL);
            if (bytes_read > ar->entry_padding)
                bytes_read = (ssize_t)ar->entry_padding;
            __archive_read_consume(a, (size_t)bytes_read);
            ar->entry_padding -= bytes_read;
        }
        *buff = NULL;
        *size = 0;
        *offset = ar->entry_offset;
        return (ARCHIVE_EOF);
    }
}

static int
archive_read_format_ar_skip(struct archive_read *a)
{
    off_t bytes_skipped;
    struct ar* ar;

    ar = (struct ar *)(a->format->data);

    bytes_skipped = __archive_read_skip(a,
        ar->entry_bytes_remaining + ar->entry_padding);
    if (bytes_skipped < 0)
        return (ARCHIVE_FATAL);

    ar->entry_bytes_remaining = 0;
    ar->entry_padding = 0;

    return (ARCHIVE_OK);
}

static int
ar_parse_gnu_filename_table(struct archive_read *a)
{
    struct ar *ar;
    char *p;
    size_t size;

    ar = (struct ar*)(a->format->data);
    size = ar->strtab_size;

    for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
        if (*p == '/') {
            *p++ = '\0';
            if (*p != '\n')
                goto bad_string_table;
            *p = '\0';
        }
    }
    /*
     * GNU ar always pads the table to an even size.
     * The pad character is either '\n' or '`'.
     */
    if (p != ar->strtab + size && *p != '\n' && *p != '`')
        goto bad_string_table;

    /* Enforce zero termination. */
    ar->strtab[size - 1] = '\0';

    return (ARCHIVE_OK);

bad_string_table:
    archive_set_error(&a->archive, EINVAL,
        "Invalid string table");
    free(ar->strtab);
    ar->strtab = NULL;
    return (ARCHIVE_WARN);
}

static uint64_t
ar_atol8(const char *p, unsigned char_cnt)
{
    uint64_t l, limit, last_digit_limit;
    unsigned int digit, base;

    base = 8;
    limit = UINT64_MAX / base;
    last_digit_limit = UINT64_MAX % base;

    while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
        p++;

    l = 0;
    digit = *p - '0';
    while (*p >= '0' && digit < base  && char_cnt-- > 0) {
        if (l>limit || (l == limit && digit > last_digit_limit)) {
            l = UINT64_MAX; /* Truncate on overflow. */
            break;
        }
        l = (l * base) + digit;
        digit = *++p - '0';
    }
    return (l);
}

static uint64_t
ar_atol10(const char *p, unsigned char_cnt)
{
    uint64_t l, limit, last_digit_limit;
    unsigned int base, digit;

    base = 10;
    limit = UINT64_MAX / base;
    last_digit_limit = UINT64_MAX % base;

    while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
        p++;
    l = 0;
    digit = *p - '0';
    while (*p >= '0' && digit < base  && char_cnt-- > 0) {
        if (l > limit || (l == limit && digit > last_digit_limit)) {
            l = UINT64_MAX; /* Truncate on overflow. */
            break;
        }
        l = (l * base) + digit;
        digit = *++p - '0';
    }
    return (l);
}

--- NEW FILE: archive_read_disk_private.h ---
/*-
 * Copyright (c) 2003-2009 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$
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED

struct archive_read_disk {
    struct archive  archive;

    /*
     * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid,
     * following an old BSD convention.  'L' follows all symlinks,
     * 'P' follows none, 'H' follows symlinks only for the first
     * item.
     */
    char    symlink_mode;

    /*
     * Since symlink interaction changes, we need to track whether
     * we're following symlinks for the current item.  'L' mode above
     * sets this true, 'P' sets it false, 'H' changes it as we traverse.
     */
    char    follow_symlinks;  /* Either 'L' or 'P'. */

    const char * (*lookup_gname)(void *private, gid_t gid);
    void    (*cleanup_gname)(void *private);
    void     *lookup_gname_data;
    const char * (*lookup_uname)(void *private, gid_t gid);
    void    (*cleanup_uname)(void *private);
    void     *lookup_uname_data;
};

#endif

--- NEW FILE: archive_write_disk_private.h ---
/*-
 * 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/lib/libarchive/archive_write_disk_private.h,v 1.1 2007/03/03 07:37:36 kientzle Exp $
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED

struct archive_write_disk;

#endif

--- NEW FILE: archive_write_open_fd.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_fd.c,v 1.9 2007/01/09 08:05:56 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#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 "archive.h"

struct write_fd_data {
    off_t       offset;
    int     fd;
};

static int  file_close(struct archive *, void *);
static int  file_open(struct archive *, void *);
static ssize_t  file_write(struct archive *, void *, const void *buff, size_t);

int
archive_write_open_fd(struct archive *a, int fd)
{
    struct write_fd_data *mine;

    mine = (struct write_fd_data *)malloc(sizeof(*mine));
    if (mine == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        return (ARCHIVE_FATAL);
    }
    mine->fd = fd;
#if defined(__CYGWIN__)
    setmode(mine->fd, O_BINARY);
#elif defined(_WIN32)
    _setmode(mine->fd, _O_BINARY);
#endif
    return (archive_write_open(a, mine,
            file_open, file_write, file_close));
}

static int
file_open(struct archive *a, void *client_data)
{
    struct write_fd_data *mine;
    struct stat st;

    mine = (struct write_fd_data *)client_data;

    if (fstat(mine->fd, &st) != 0) {
        archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd);
        return (ARCHIVE_FATAL);
    }

    /*
     * If this is a regular file, don't add it to itself.
     */
    if (S_ISREG(st.st_mode))
        archive_write_set_skip_file(a, st.st_dev, st.st_ino);

    /*
     * If client hasn't explicitly set the last block handling,
     * then set it here.
     */
    if (archive_write_get_bytes_in_last_block(a) < 0) {
        /* If the output is a block or character device, fifo,
         * or stdout, pad the last block, otherwise leave it
         * unpadded. */
        if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
            S_ISFIFO(st.st_mode) || (mine->fd == 1))
            /* Last block will be fully padded. */
            archive_write_set_bytes_in_last_block(a, 0);
        else
            archive_write_set_bytes_in_last_block(a, 1);
    }

    return (ARCHIVE_OK);
}

static ssize_t
file_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
    struct write_fd_data    *mine;
    ssize_t bytesWritten;

    mine = (struct write_fd_data *)client_data;
    bytesWritten = write(mine->fd, buff, length);
    if (bytesWritten <= 0) {
        archive_set_error(a, errno, "Write error");
        return (-1);
    }
    return (bytesWritten);
}

static int
file_close(struct archive *a, void *client_data)
{
    struct write_fd_data    *mine = (struct write_fd_data *)client_data;

    (void)a; /* UNUSED */
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: config_freebsd.h ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.15 2008/09/30 03:53:03 kientzle Exp $
 */

/* FreeBSD 5.0 and later have ACL and extattr support. */
#if __FreeBSD__ > 4
#define HAVE_ACL_CREATE_ENTRY 1
#define HAVE_ACL_GET_LINK_NP 1
#define HAVE_ACL_GET_PERM_NP 1
#define HAVE_ACL_INIT 1
#define HAVE_ACL_SET_FD 1
#define HAVE_ACL_SET_FD_NP 1
#define HAVE_ACL_SET_FILE 1
#define HAVE_ACL_USER 1
#define HAVE_EXTATTR_GET_FILE 1
#define HAVE_EXTATTR_LIST_FILE 1
#define HAVE_EXTATTR_SET_FD 1
#define HAVE_EXTATTR_SET_FILE 1
#define HAVE_SYS_ACL_H 1
#define HAVE_SYS_EXTATTR_H 1
#endif

#define HAVE_BZLIB_H 1
#define HAVE_CHFLAGS 1
#define HAVE_CHOWN 1
#define HAVE_DECL_INT64_MAX 1
#define HAVE_DECL_INT64_MIN 1
#define HAVE_DECL_SIZE_MAX 1
#define HAVE_DECL_SSIZE_MAX 1
#define HAVE_DECL_STRERROR_R 1
#define HAVE_DECL_UINT32_MAX 1
#define HAVE_DECL_UINT64_MAX 1
#define HAVE_DIRENT_H 1
#define HAVE_EFTYPE 1
#define HAVE_EILSEQ 1
#define HAVE_ERRNO_H 1
#define HAVE_FCHDIR 1
#define HAVE_FCHFLAGS 1
#define HAVE_FCHMOD 1
#define HAVE_FCHOWN 1
#define HAVE_FCNTL 1
#define HAVE_FCNTL_H 1
#define HAVE_FSEEKO 1
#define HAVE_FSTAT 1
#define HAVE_FTRUNCATE 1
#define HAVE_FUTIMES 1
#define HAVE_GETEUID 1
#define HAVE_GETPID 1
#define HAVE_GRP_H 1
#define HAVE_INTTYPES_H 1
#define HAVE_LCHFLAGS 1
#define HAVE_LCHMOD 1
#define HAVE_LCHOWN 1
#define HAVE_LIMITS_H 1
#define HAVE_LINK 1
#define HAVE_LSTAT 1
#define HAVE_LUTIMES 1
#define HAVE_MALLOC 1
#define HAVE_MD5 1
#define HAVE_MD5_H 1
#define HAVE_MEMMOVE 1
#define HAVE_MKDIR 1
#define HAVE_MKFIFO 1
#define HAVE_MKNOD 1
#define HAVE_OPENSSL_MD5_H 1
#define HAVE_OPENSSL_RIPEMD_H 1
#define HAVE_OPENSSL_SHA_H 1
#define HAVE_PIPE 1
#define HAVE_POLL 1
#define HAVE_POLL_H 1
#define HAVE_PWD_H 1
#define HAVE_READLINK 1
#define HAVE_RMD160 1
#define HAVE_SELECT 1
#define HAVE_SETENV 1
#define HAVE_SHA_H 1
#define HAVE_SHA1 1
#define HAVE_SHA256 1
#define HAVE_SHA256_H 1
#define HAVE_SHA384 1
#define HAVE_SHA512 1
#define HAVE_SIGNAL_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_STRERROR_R 1
#define HAVE_STRINGS_H 1
#define HAVE_STRING_H 1
#define HAVE_STRRCHR 1
#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
#define HAVE_STRUCT_STAT_ST_FLAGS 1
#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
#define HAVE_SYMLINK 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_SELECT_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_SYS_TIME_H 1
#define HAVE_SYS_TYPES_H 1
#undef  HAVE_SYS_UTIME_H
#define HAVE_SYS_WAIT_H 1
#define HAVE_TIMEGM 1
#define HAVE_TZSET 1
#define HAVE_UNISTD_H 1
#define HAVE_UNSETENV 1
#define HAVE_UTIME 1
#define HAVE_UTIMES 1
#define HAVE_UTIME_H 1
#define HAVE_VFORK 1
#define HAVE_WCHAR_H 1
#define HAVE_WCSCPY 1
#define HAVE_WCSLEN 1
#define HAVE_WCTOMB 1
#define HAVE_WMEMCMP 1
#define HAVE_WMEMCPY 1
#define HAVE_ZLIB_H 1
#define STDC_HEADERS 1
#define TIME_WITH_SYS_TIME 1

/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */
#if __FreeBSD__ < 5
#define intmax_t int64_t
#define uintmax_t uint64_t
#endif

--- NEW FILE: archive_read.3 ---
.\" 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/lib/libarchive/archive_read.3,v 1.37 2008/05/26 17:00:22 kientzle Exp $
.\"
.Dd April 13, 2009
.Dt archive_read 3
.Os
.Sh NAME
.Nm archive_read_new ,
.Nm archive_read_set_filter_options ,
.Nm archive_read_set_format_options ,
.Nm archive_read_set_options ,
.Nm archive_read_support_compression_all ,
.Nm archive_read_support_compression_bzip2 ,
.Nm archive_read_support_compression_compress ,
.Nm archive_read_support_compression_gzip ,
.Nm archive_read_support_compression_lzma ,
.Nm archive_read_support_compression_none ,
.Nm archive_read_support_compression_xz ,
.Nm archive_read_support_compression_program ,
.Nm archive_read_support_compression_program_signature ,
.Nm archive_read_support_format_all ,
.Nm archive_read_support_format_ar ,
.Nm archive_read_support_format_cpio ,
.Nm archive_read_support_format_empty ,
.Nm archive_read_support_format_iso9660 ,
.Nm archive_read_support_format_mtree,
.Nm archive_read_support_format_raw,
.Nm archive_read_support_format_tar ,
.Nm archive_read_support_format_zip ,
.Nm archive_read_open ,
.Nm archive_read_open2 ,
.Nm archive_read_open_fd ,
.Nm archive_read_open_FILE ,
.Nm archive_read_open_filename ,
.Nm archive_read_open_memory ,
.Nm archive_read_next_header ,
.Nm archive_read_next_header2 ,
.Nm archive_read_data ,
.Nm archive_read_data_block ,
.Nm archive_read_data_skip ,
.\" #if ARCHIVE_API_VERSION < 3
.Nm archive_read_data_into_buffer ,
.\" #endif
.Nm archive_read_data_into_fd ,
.Nm archive_read_extract ,
.Nm archive_read_extract2 ,
.Nm archive_read_extract_set_progress_callback ,
.Nm archive_read_close ,
.Nm archive_read_finish
.Nd functions for reading streaming archives
.Sh SYNOPSIS
.In archive.h
.Ft struct archive *
.Fn archive_read_new "void"
.Ft int
.Fn archive_read_support_compression_all "struct archive *"
.Ft int
.Fn archive_read_support_compression_bzip2 "struct archive *"
.Ft int
.Fn archive_read_support_compression_compress "struct archive *"
.Ft int
.Fn archive_read_support_compression_gzip "struct archive *"
.Ft int
.Fn archive_read_support_compression_lzma "struct archive *"
.Ft int
.Fn archive_read_support_compression_none "struct archive *"
.Ft int
.Fn archive_read_support_compression_xz "struct archive *"
.Ft int
.Fo archive_read_support_compression_program
.Fa "struct archive *"
.Fa "const char *cmd"
.Fc
.Ft int
.Fo archive_read_support_compression_program_signature
.Fa "struct archive *"
.Fa "const char *cmd"
.Fa "const void *signature"
.Fa "size_t signature_length"
.Fc
.Ft int
.Fn archive_read_support_format_all "struct archive *"
.Ft int
.Fn archive_read_support_format_ar "struct archive *"
.Ft int
.Fn archive_read_support_format_cpio "struct archive *"
.Ft int
.Fn archive_read_support_format_empty "struct archive *"
.Ft int
.Fn archive_read_support_format_iso9660 "struct archive *"
.Ft int
.Fn archive_read_support_format_mtree "struct archive *"
.Ft int
.Fn archive_read_support_format_raw "struct archive *"
.Ft int
.Fn archive_read_support_format_tar "struct archive *"
.Ft int
.Fn archive_read_support_format_zip "struct archive *"
.Ft int
.Fn archive_read_set_filter_options "struct archive *" "const char *"
.Ft int
.Fn archive_read_set_format_options "struct archive *" "const char *"
.Ft int
.Fn archive_read_set_options "struct archive *" "const char *"
.Ft int
.Fo archive_read_open
.Fa "struct archive *"
.Fa "void *client_data"
.Fa "archive_open_callback *"
.Fa "archive_read_callback *"
.Fa "archive_close_callback *"
.Fc
.Ft int
.Fo archive_read_open2
.Fa "struct archive *"
.Fa "void *client_data"
.Fa "archive_open_callback *"
.Fa "archive_read_callback *"
.Fa "archive_skip_callback *"
.Fa "archive_close_callback *"
.Fc
.Ft int
.Fn archive_read_open_FILE "struct archive *" "FILE *file"
.Ft int
.Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size"
.Ft int
.Fo archive_read_open_filename
.Fa "struct archive *"
.Fa "const char *filename"
.Fa "size_t block_size"
.Fc
.Ft int
.Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size"
.Ft int
.Fn archive_read_next_header "struct archive *" "struct archive_entry **"
.Ft int
.Fn archive_read_next_header2 "struct archive *" "struct archive_entry *"
.Ft ssize_t
.Fn archive_read_data "struct archive *" "void *buff" "size_t len"
.Ft int
.Fo archive_read_data_block
.Fa "struct archive *"
.Fa "const void **buff"
.Fa "size_t *len"
.Fa "off_t *offset"
.Fc
.Ft int
.Fn archive_read_data_skip "struct archive *"
.\" #if ARCHIVE_API_VERSION < 3
.Ft int
.Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len"
.\" #endif
.Ft int
.Fn archive_read_data_into_fd "struct archive *" "int fd"
.Ft int
.Fo archive_read_extract
.Fa "struct archive *"
.Fa "struct archive_entry *"
.Fa "int flags"
.Fc
.Ft int
.Fo archive_read_extract2
.Fa "struct archive *src"
.Fa "struct archive_entry *"
.Fa "struct archive *dest"
.Fc
.Ft void
.Fo archive_read_extract_set_progress_callback
.Fa "struct archive *"
.Fa "void (*func)(void *)"
.Fa "void *user_data"
.Fc
.Ft int
.Fn archive_read_close "struct archive *"
.Ft int
.Fn archive_read_finish "struct archive *"
.Sh DESCRIPTION
These functions provide a complete API for reading streaming archives.
The general process is to first create the
.Tn struct archive
object, set options, initialize the reader, iterate over the archive
headers and associated data, then close the archive and release all
resources.
The following summary describes the functions in approximately the
order they would be used:
.Bl -tag -compact -width indent
.It Fn archive_read_new
Allocates and initializes a
.Tn struct archive
object suitable for reading from an archive.
.It Xo
.Fn archive_read_support_compression_bzip2 ,
.Fn archive_read_support_compression_compress ,
.Fn archive_read_support_compression_gzip ,
.Fn archive_read_support_compression_lzma ,
.Fn archive_read_support_compression_none ,
.Fn archive_read_support_compression_xz
.Xc
Enables auto-detection code and decompression support for the
specified compression.
Returns
.Cm ARCHIVE_OK
if the compression is fully supported, or
.Cm ARCHIVE_WARN
if the compression is supported only through an external program.
Note that decompression using an external program is usually slower than
decompression through built-in libraries.
Note that
.Dq none
is always enabled by default.
.It Fn archive_read_support_compression_all
Enables all available decompression filters.
.It Fn archive_read_support_compression_program
Data is fed through the specified external program before being dearchived.
Note that this disables automatic detection of the compression format,
so it makes no sense to specify this in conjunction with any other
decompression option.
.It Fn archive_read_support_compression_program_signature
This feeds data through the specified external program
but only if the initial bytes of the data match the specified
signature value.
.It Xo
.Fn archive_read_support_format_all ,
.Fn archive_read_support_format_ar ,
.Fn archive_read_support_format_cpio ,
.Fn archive_read_support_format_empty ,
.Fn archive_read_support_format_iso9660 ,
.Fn archive_read_support_format_mtree ,
.Fn archive_read_support_format_tar ,
.Fn archive_read_support_format_zip
.Xc
Enables support---including auto-detection code---for the
specified archive format.
For example,
.Fn archive_read_support_format_tar
enables support for a variety of standard tar formats, old-style tar,
ustar, pax interchange format, and many common variants.
For convenience,
.Fn archive_read_support_format_all
enables support for all available formats.
Only empty archives are supported by default.
.It Fn archive_read_support_format_raw
The
.Dq raw
format handler allows libarchive to be used to read arbitrary data.
It treats any data stream as an archive with a single entry.
The pathname of this entry is
.Dq data ;
all other entry fields are unset.
This is not enabled by
.Fn archive_read_support_format_all
in order to avoid erroneous handling of damaged archives.
.It Xo
.Fn archive_read_set_filter_options ,
.Fn archive_read_set_format_options ,
.Fn archive_read_set_options
.Xc
Specifies options that will be passed to currently-registered
filters (including decompression filters) and/or format readers.
The argument is a comma-separated list of individual options.
Individual options have one of the following forms:
.Bl -tag -compact -width indent
.It Ar option=value
The option/value pair will be provided to every module.
Modules that do not accept an option with this name will ignore it.
.It Ar option
The option will be provided to every module with a value of
.Dq 1 .
.It Ar !option
The option will be provided to every module with a NULL value.
.It Ar module:option=value , Ar module:option , Ar module:!option
As above, but the corresponding option and value will be provided
only to modules whose name matches
.Ar module .
.El
The return value will be
.Cm ARCHIVE_OK
if any module accepts the option, or
.Cm ARCHIVE_WARN
if no module accepted the option, or
.Cm ARCHIVE_FATAL
if there was a fatal error while attempting to process the option.
.Pp
The currently supported options are:
.Bl -tag -compact -width indent
.It Format iso9660
.Bl -tag -compact -width indent
.It Cm joliet
Support Joliet extensions.
Defaults to enabled, use
.Cm !joliet
to disable.
.El
.El
.It Fn archive_read_open
The same as
.Fn archive_read_open2 ,
except that the skip callback is assumed to be
.Dv NULL .
.It Fn archive_read_open2
Freeze the settings, open the archive, and prepare for reading entries.
This is the most generic version of this call, which accepts
four callback functions.
Most clients will want to use
.Fn archive_read_open_filename ,
.Fn archive_read_open_FILE ,
.Fn archive_read_open_fd ,
or
.Fn archive_read_open_memory
instead.
The library invokes the client-provided functions to obtain
raw bytes from the archive.
.It Fn archive_read_open_FILE
Like
.Fn archive_read_open ,
except that it accepts a
.Ft "FILE *"
pointer.
This function should not be used with tape drives or other devices
that require strict I/O blocking.
.It Fn archive_read_open_fd
Like
.Fn archive_read_open ,
except that it accepts a file descriptor and block size rather than
a set of function pointers.
Note that the file descriptor will not be automatically closed at
end-of-archive.
This function is safe for use with tape drives or other blocked devices.
.It Fn archive_read_open_file
This is a deprecated synonym for
.Fn archive_read_open_filename .
.It Fn archive_read_open_filename
Like
.Fn archive_read_open ,
except that it accepts a simple filename and a block size.
A NULL filename represents standard input.
This function is safe for use with tape drives or other blocked devices.
.It Fn archive_read_open_memory
Like
.Fn archive_read_open ,
except that it accepts a pointer and size of a block of
memory containing the archive data.
.It Fn archive_read_next_header
Read the header for the next entry and return a pointer to
a
.Tn struct archive_entry .
This is a convenience wrapper around
.Fn archive_read_next_header2
that reuses an internal
.Tn struct archive_entry
object for each request.
.It Fn archive_read_next_header2
Read the header for the next entry and populate the provided
.Tn struct archive_entry .
.It Fn archive_read_data
Read data associated with the header just read.
Internally, this is a convenience function that calls
.Fn archive_read_data_block
and fills any gaps with nulls so that callers see a single
continuous stream of data.
.It Fn archive_read_data_block
Return the next available block of data for this entry.
Unlike
.Fn archive_read_data ,
the
.Fn archive_read_data_block
function avoids copying data and allows you to correctly handle
sparse files, as supported by some archive formats.
The library guarantees that offsets will increase and that blocks
will not overlap.
Note that the blocks returned from this function can be much larger
than the block size read from disk, due to compression
and internal buffer optimizations.
.It Fn archive_read_data_skip
A convenience function that repeatedly calls
.Fn archive_read_data_block
to skip all of the data for this archive entry.
.\" #if ARCHIVE_API_VERSION < 3
.It Fn archive_read_data_into_buffer
This function is deprecated and will be removed.
Use
.Fn archive_read_data
instead.
.\" #endif
.It Fn archive_read_data_into_fd
A convenience function that repeatedly calls
.Fn archive_read_data_block
to copy the entire entry to the provided file descriptor.
.It Fn archive_read_extract , Fn archive_read_extract_set_skip_file
A convenience function that wraps the corresponding
.Xr archive_write_disk 3
interfaces.
The first call to
.Fn archive_read_extract
creates a restore object using
.Xr archive_write_disk_new 3
and
.Xr archive_write_disk_set_standard_lookup 3 ,
then transparently invokes
.Xr archive_write_disk_set_options 3 ,
.Xr archive_write_header 3 ,
.Xr archive_write_data 3 ,
and
.Xr archive_write_finish_entry 3
to create the entry on disk and copy data into it.
The
.Va flags
argument is passed unmodified to
.Xr archive_write_disk_set_options 3 .
.It Fn archive_read_extract2
This is another version of
.Fn archive_read_extract
that allows you to provide your own restore object.
In particular, this allows you to override the standard lookup functions
using
.Xr archive_write_disk_set_group_lookup 3 ,
and
.Xr archive_write_disk_set_user_lookup 3 .
Note that
.Fn archive_read_extract2
does not accept a
.Va flags
argument; you should use
.Fn archive_write_disk_set_options
to set the restore options yourself.
.It Fn archive_read_extract_set_progress_callback
Sets a pointer to a user-defined callback that can be used
for updating progress displays during extraction.
The progress function will be invoked during the extraction of large
regular files.
The progress function will be invoked with the pointer provided to this call.
Generally, the data pointed to should include a reference to the archive
object and the archive_entry object so that various statistics
can be retrieved for the progress display.
.It Fn archive_read_close
Complete the archive and invoke the close callback.
.It Fn archive_read_finish
Invokes
.Fn archive_read_close
if it was not invoked manually, then release all resources.
Note: In libarchive 1.x, this function was declared to return
.Ft void ,
which made it impossible to detect certain errors when
.Fn archive_read_close
was invoked implicitly from this function.
The declaration is corrected beginning with libarchive 2.0.
.El
.Pp
Note that the library determines most of the relevant information about
the archive by inspection.
In particular, it automatically detects
.Xr gzip 1
or
.Xr bzip2 1
compression and transparently performs the appropriate decompression.
It also automatically detects the archive format.
.Pp
A complete description of the
.Tn struct archive
and
.Tn struct archive_entry
objects can be found in the overview manual page for
.Xr libarchive 3 .
.Sh CLIENT CALLBACKS
The callback functions must match the following prototypes:
.Bl -item -offset indent
.It
.Ft typedef ssize_t
.Fo archive_read_callback
.Fa "struct archive *"
.Fa "void *client_data"
.Fa "const void **buffer"
.Fc
.It
.\" #if ARCHIVE_API_VERSION < 2
.Ft typedef int
.Fo archive_skip_callback
.Fa "struct archive *"
.Fa "void *client_data"
.Fa "size_t request"
.Fc
.\" #else
.\" .Ft typedef off_t
.\" .Fo archive_skip_callback
.\" .Fa "struct archive *"
.\" .Fa "void *client_data"
.\" .Fa "off_t request"
.\" .Fc
.\" #endif
.It
.Ft typedef int
.Fn archive_open_callback "struct archive *" "void *client_data"
.It
.Ft typedef int
.Fn archive_close_callback "struct archive *" "void *client_data"
.El
.Pp
The open callback is invoked by
.Fn archive_open .
It should return
.Cm ARCHIVE_OK
if the underlying file or data source is successfully
opened.
If the open fails, it should call
.Fn archive_set_error
to register an error code and message and return
.Cm ARCHIVE_FATAL .
.Pp
The read callback is invoked whenever the library
requires raw bytes from the archive.
The read callback should read data into a buffer,
set the
.Li const void **buffer
argument to point to the available data, and
return a count of the number of bytes available.
The library will invoke the read callback again
only after it has consumed this data.
The library imposes no constraints on the size
of the data blocks returned.
On end-of-file, the read callback should
return zero.
On error, the read callback should invoke
.Fn archive_set_error
to register an error code and message and
return -1.
.Pp
The skip callback is invoked when the
library wants to ignore a block of data.
The return value is the number of bytes actually
skipped, which may differ from the request.
If the callback cannot skip data, it should return
zero.
If the skip callback is not provided (the
function pointer is
.Dv NULL ),
the library will invoke the read function
instead and simply discard the result.
A skip callback can provide significant
performance gains when reading uncompressed
archives from slow disk drives or other media
that can skip quickly.
.Pp
The close callback is invoked by archive_close when
the archive processing is complete.
The callback should return
.Cm ARCHIVE_OK
on success.
On failure, the callback should invoke
.Fn archive_set_error
to register an error code and message and
return
.Cm ARCHIVE_FATAL.
.Sh EXAMPLE
The following illustrates basic usage of the library.
In this example,
the callback functions are simply wrappers around the standard
.Xr open 2 ,
.Xr read 2 ,
and
.Xr close 2
system calls.
.Bd -literal -offset indent
void
list_archive(const char *name)
{
  struct mydata *mydata;
  struct archive *a;
  struct archive_entry *entry;

  mydata = malloc(sizeof(struct mydata));
  a = archive_read_new();
  mydata->name = name;
  archive_read_support_compression_all(a);
  archive_read_support_format_all(a);
  archive_read_open(a, mydata, myopen, myread, myclose);
  while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
    printf("%s\\n",archive_entry_pathname(entry));
    archive_read_data_skip(a);
  }
  archive_read_finish(a);
  free(mydata);
}

ssize_t
myread(struct archive *a, void *client_data, const void **buff)
{
  struct mydata *mydata = client_data;

  *buff = mydata->buff;
  return (read(mydata->fd, mydata->buff, 10240));
}

int
myopen(struct archive *a, void *client_data)
{
  struct mydata *mydata = client_data;

  mydata->fd = open(mydata->name, O_RDONLY);
  return (mydata->fd >= 0 ? ARCHIVE_OK : ARCHIVE_FATAL);
}

int
myclose(struct archive *a, void *client_data)
{
  struct mydata *mydata = client_data;

  if (mydata->fd > 0)
    close(mydata->fd);
  return (ARCHIVE_OK);
}
.Ed
.Sh RETURN VALUES
Most functions return zero on success, non-zero on error.
The possible return codes include:
.Cm ARCHIVE_OK
(the operation succeeded),
.Cm ARCHIVE_WARN
(the operation succeeded but a non-critical error was encountered),
.Cm ARCHIVE_EOF
(end-of-archive was encountered),
.Cm ARCHIVE_RETRY
(the operation failed but can be retried),
and
.Cm ARCHIVE_FATAL
(there was a fatal error; the archive should be closed immediately).
Detailed error codes and textual descriptions are available from the
.Fn archive_errno
and
.Fn archive_error_string
functions.
.Pp
.Fn archive_read_new
returns a pointer to a freshly allocated
.Tn struct archive
object.
It returns
.Dv NULL
on error.
.Pp
.Fn archive_read_data
returns a count of bytes actually read or zero at the end of the entry.
On error, a value of
.Cm ARCHIVE_FATAL ,
.Cm ARCHIVE_WARN ,
or
.Cm ARCHIVE_RETRY
is returned and an error code and textual description can be retrieved from the
.Fn archive_errno
and
.Fn archive_error_string
functions.
.Pp
The library expects the client callbacks to behave similarly.
If there is an error, you can use
.Fn archive_set_error
to set an appropriate error code and description,
then return one of the non-zero values above.
(Note that the value eventually returned to the client may
not be the same; many errors that are not critical at the level
of basic I/O can prevent the archive from being properly read,
thus most I/O errors eventually cause
.Cm ARCHIVE_FATAL
to be returned.)
.\" .Sh ERRORS
.Sh SEE ALSO
.Xr tar 1 ,
.Xr archive 3 ,
.Xr archive_util 3 ,
.Xr tar 5
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .
.Sh BUGS
Many traditional archiver programs treat
empty files as valid empty archives.
For example, many implementations of
.Xr tar 1
allow you to append entries to an empty file.
Of course, it is impossible to determine the format of an empty file
by inspecting the contents, so this library treats empty files as
having a special
.Dq empty
format.

--- NEW FILE: archive_write_open_memory.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $");

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "archive.h"

/*
 * This is a little tricky.  I used to allow the
 * compression handling layer to fork the compressor,
 * which means this write function gets invoked in
 * a separate process.  That would, of course, make it impossible
 * to actually use the data stored into memory here.
 * Fortunately, none of the compressors fork today and
 * I'm reluctant to use that route in the future but, if
 * forking compressors ever do reappear, this will have
 * to get a lot more complicated.
 */

struct write_memory_data {
    size_t  used;
    size_t  size;
    size_t * client_size;
    unsigned char * buff;
};

static int  memory_write_close(struct archive *, void *);
static int  memory_write_open(struct archive *, void *);
static ssize_t  memory_write(struct archive *, void *, const void *buff, size_t);

/*
 * Client provides a pointer to a block of memory to receive
 * the data.  The 'size' param both tells us the size of the
 * client buffer and lets us tell the client the final size.
 */
int
archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used)
{
    struct write_memory_data *mine;

    mine = (struct write_memory_data *)malloc(sizeof(*mine));
    if (mine == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        return (ARCHIVE_FATAL);
    }
    memset(mine, 0, sizeof(*mine));
    mine->buff = buff;
    mine->size = buffSize;
    mine->client_size = used;
    return (archive_write_open(a, mine,
            memory_write_open, memory_write, memory_write_close));
}

static int
memory_write_open(struct archive *a, void *client_data)
{
    struct write_memory_data *mine;
    mine = client_data;
    mine->used = 0;
    if (mine->client_size != NULL)
        *mine->client_size = mine->used;
    /* Disable padding if it hasn't been set explicitly. */
    if (-1 == archive_write_get_bytes_in_last_block(a))
        archive_write_set_bytes_in_last_block(a, 1);
    return (ARCHIVE_OK);
}

/*
 * Copy the data into the client buffer.
 * Note that we update mine->client_size on every write.
 * In particular, this means the client can follow exactly
 * how much has been written into their buffer at any time.
 */
static ssize_t
memory_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
    struct write_memory_data *mine;
    mine = client_data;

    if (mine->used + length > mine->size) {
        archive_set_error(a, ENOMEM, "Buffer exhausted");
        return (ARCHIVE_FATAL);
    }
    memcpy(mine->buff + mine->used, buff, length);
    mine->used += length;
    if (mine->client_size != NULL)
        *mine->client_size = mine->used;
    return (length);
}

static int
memory_write_close(struct archive *a, void *client_data)
{
    struct write_memory_data *mine;
    (void)a; /* UNUSED */
    mine = client_data;
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_entry_copy_bhfi.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD$");

#include "archive_entry.h"

#if defined(_WIN32) && !defined(__CYGWIN__)

#define EPOC_TIME   (116444736000000000ULL)

__inline static void
fileTimeToUtc(const FILETIME *filetime, time_t *time, long *ns)
{
    ULARGE_INTEGER utc;

    utc.HighPart = filetime->dwHighDateTime;
    utc.LowPart  = filetime->dwLowDateTime;
    if (utc.QuadPart >= EPOC_TIME) {
        utc.QuadPart -= EPOC_TIME;
        *time = (time_t)(utc.QuadPart / 10000000);  /* milli seconds base */
        *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
    } else {
        *time = 0;
        *ns = 0;
    }
}

void
archive_entry_copy_bhfi(struct archive_entry *entry,
            BY_HANDLE_FILE_INFORMATION *bhfi)
{
    time_t secs;
    long nsecs;

    fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
    archive_entry_set_atime(entry, secs, nsecs);
    fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
    archive_entry_set_mtime(entry, secs, nsecs);
    fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
    archive_entry_set_birthtime(entry, secs, nsecs);
    archive_entry_set_dev(entry, bhfi->dwVolumeSerialNumber);
    archive_entry_set_ino64(entry, (((int64_t)bhfi->nFileIndexHigh) << 32)
        + bhfi->nFileIndexLow);
    archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
    archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32)
        + bhfi->nFileSizeLow);
//  archive_entry_set_mode(entry, st->st_mode);
}
#endif

--- NEW FILE: archive_virtual.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_virtual.c,v 1.1 2007/03/03 07:37:36 kientzle Exp $");

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"

int
archive_write_close(struct archive *a)
{
    return ((a->vtable->archive_close)(a));
}

int
archive_read_close(struct archive *a)
{
    return ((a->vtable->archive_close)(a));
}

#if ARCHIVE_API_VERSION > 1
int
archive_write_finish(struct archive *a)
{
    return ((a->vtable->archive_finish)(a));
}
#else
/* Temporarily allow library to compile with either 1.x or 2.0 API. */
void
archive_write_finish(struct archive *a)
{
    (void)(a->vtable->archive_finish)(a);
}
#endif

int
archive_read_finish(struct archive *a)
{
    return ((a->vtable->archive_finish)(a));
}

int
archive_write_header(struct archive *a, struct archive_entry *entry)
{
    ++a->file_count;
    return ((a->vtable->archive_write_header)(a, entry));
}

int
archive_write_finish_entry(struct archive *a)
{
    return ((a->vtable->archive_write_finish_entry)(a));
}

#if ARCHIVE_API_VERSION > 1
ssize_t
#else
/* Temporarily allow library to compile with either 1.x or 2.0 API. */
int
#endif
archive_write_data(struct archive *a, const void *buff, size_t s)
{
    return ((a->vtable->archive_write_data)(a, buff, s));
}

ssize_t
archive_write_data_block(struct archive *a, const void *buff, size_t s, off_t o)
{
    return ((a->vtable->archive_write_data_block)(a, buff, s, o));
}

--- NEW FILE: archive_read_support_format_mtree.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * Copyright (c) 2008 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
[...1270 lines suppressed...]
            }
            if (u[0] != '\\')
                continue;
            if (u[1] == '\\') {
                ++u;
                continue;
            }
            if (u[1] == '\n') {
                memmove(u, u + 1,
                    total_size - (u - mtree->line.s) + 1);
                --total_size;
                ++u;
                break;
            }
            if (u[1] == '\0')
                break;
        }
        find_off = u - mtree->line.s;
    }
}

--- NEW FILE: archive_write_set_format_shar.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * Copyright (c) 2008 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.20 2008/08/31 07:10:40 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

struct shar {
    int          dump;
    int          end_of_line;
    struct archive_entry    *entry;
    int          has_data;
    char            *last_dir;

    /* Line buffer for uuencoded dump format */
    char             outbuff[45];
    size_t           outpos;

    int          wrote_header;
    struct archive_string    work;
    struct archive_string    quoted_name;
};

static int  archive_write_shar_finish(struct archive_write *);
static int  archive_write_shar_destroy(struct archive_write *);
static int  archive_write_shar_header(struct archive_write *,
            struct archive_entry *);
static ssize_t  archive_write_shar_data_sed(struct archive_write *,
            const void * buff, size_t);
static ssize_t  archive_write_shar_data_uuencode(struct archive_write *,
            const void * buff, size_t);
static int  archive_write_shar_finish_entry(struct archive_write *);

/*
 * Copy the given string to the buffer, quoting all shell meta characters
 * found.
 */
static void
shar_quote(struct archive_string *buf, const char *str, int in_shell)
{
    static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
    size_t len;

    while (*str != '\0') {
        if ((len = strcspn(str, meta)) != 0) {
            archive_strncat(buf, str, len);
            str += len;
        } else if (*str == '\n') {
            if (in_shell)
                archive_strcat(buf, "\"\n\"");
            else
                archive_strcat(buf, "\\n");
            ++str;
        } else {
            archive_strappend_char(buf, '\\');
            archive_strappend_char(buf, *str);
            ++str;
        }
    }
}

/*
 * Set output format to 'shar' format.
 */
int
archive_write_set_format_shar(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct shar *shar;

    /* If someone else was already registered, unregister them. */
    if (a->format_destroy != NULL)
        (a->format_destroy)(a);

    shar = (struct shar *)malloc(sizeof(*shar));
    if (shar == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
        return (ARCHIVE_FATAL);
    }
    memset(shar, 0, sizeof(*shar));
    archive_string_init(&shar->work);
    archive_string_init(&shar->quoted_name);
    a->format_data = shar;

    a->pad_uncompressed = 0;
    a->format_name = "shar";
    a->format_write_header = archive_write_shar_header;
    a->format_finish = archive_write_shar_finish;
    a->format_destroy = archive_write_shar_destroy;
    a->format_write_data = archive_write_shar_data_sed;
    a->format_finish_entry = archive_write_shar_finish_entry;
    a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
    a->archive.archive_format_name = "shar";
    return (ARCHIVE_OK);
}

/*
 * An alternate 'shar' that uses uudecode instead of 'sed' to encode
 * file contents and can therefore be used to archive binary files.
 * In addition, this variant also attempts to restore ownership, file modes,
 * and other extended file information.
 */
int
archive_write_set_format_shar_dump(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct shar *shar;

    archive_write_set_format_shar(&a->archive);
    shar = (struct shar *)a->format_data;
    shar->dump = 1;
    a->format_write_data = archive_write_shar_data_uuencode;
    a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
    a->archive.archive_format_name = "shar dump";
    return (ARCHIVE_OK);
}

static int
archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
{
    const char *linkname;
    const char *name;
    char *p, *pp;
    struct shar *shar;

    shar = (struct shar *)a->format_data;
    if (!shar->wrote_header) {
        archive_strcat(&shar->work, "#!/bin/sh\n");
        archive_strcat(&shar->work, "# This is a shell archive\n");
        shar->wrote_header = 1;
    }

    /* Save the entry for the closing. */
    if (shar->entry)
        archive_entry_free(shar->entry);
    shar->entry = archive_entry_clone(entry);
    name = archive_entry_pathname(entry);

    /* Handle some preparatory issues. */
    switch(archive_entry_filetype(entry)) {
    case AE_IFREG:
        /* Only regular files have non-zero size. */
        break;
    case AE_IFDIR:
        archive_entry_set_size(entry, 0);
        /* Don't bother trying to recreate '.' */
        if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
            return (ARCHIVE_OK);
        break;
    case AE_IFIFO:
    case AE_IFCHR:
    case AE_IFBLK:
        /* All other file types have zero size in the archive. */
        archive_entry_set_size(entry, 0);
        break;
    default:
        archive_entry_set_size(entry, 0);
        if (archive_entry_hardlink(entry) == NULL &&
            archive_entry_symlink(entry) == NULL) {
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "shar format cannot archive this");
            return (ARCHIVE_WARN);
        }
    }

    archive_string_empty(&shar->quoted_name);
    shar_quote(&shar->quoted_name, name, 1);

    /* Stock preparation for all file types. */
    archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);

    if (archive_entry_filetype(entry) != AE_IFDIR) {
        /* Try to create the dir. */
        p = strdup(name);
        pp = strrchr(p, '/');
        /* If there is a / character, try to create the dir. */
        if (pp != NULL) {
            *pp = '\0';

            /* Try to avoid a lot of redundant mkdir commands. */
            if (strcmp(p, ".") == 0) {
                /* Don't try to "mkdir ." */
                free(p);
            } else if (shar->last_dir == NULL) {
                archive_strcat(&shar->work, "mkdir -p ");
                shar_quote(&shar->work, p, 1);
                archive_strcat(&shar->work,
                    " > /dev/null 2>&1\n");
                shar->last_dir = p;
            } else if (strcmp(p, shar->last_dir) == 0) {
                /* We've already created this exact dir. */
                free(p);
            } else if (strlen(p) < strlen(shar->last_dir) &&
                strncmp(p, shar->last_dir, strlen(p)) == 0) {
                /* We've already created a subdir. */
                free(p);
            } else {
                archive_strcat(&shar->work, "mkdir -p ");
                shar_quote(&shar->work, p, 1);
                archive_strcat(&shar->work,
                    " > /dev/null 2>&1\n");
                shar->last_dir = p;
            }
        } else {
            free(p);
        }
    }

    /* Handle file-type specific issues. */
    shar->has_data = 0;
    if ((linkname = archive_entry_hardlink(entry)) != NULL) {
        archive_strcat(&shar->work, "ln -f ");
        shar_quote(&shar->work, linkname, 1);
        archive_string_sprintf(&shar->work, " %s\n",
            shar->quoted_name.s);
    } else if ((linkname = archive_entry_symlink(entry)) != NULL) {
        archive_strcat(&shar->work, "ln -fs ");
        shar_quote(&shar->work, linkname, 1);
        archive_string_sprintf(&shar->work, " %s\n",
            shar->quoted_name.s);
    } else {
        switch(archive_entry_filetype(entry)) {
        case AE_IFREG:
            if (archive_entry_size(entry) == 0) {
                /* More portable than "touch." */
                archive_string_sprintf(&shar->work,
                    "test -e \"%s\" || :> \"%s\"\n",
                    shar->quoted_name.s, shar->quoted_name.s);
            } else {
                if (shar->dump) {
                    archive_string_sprintf(&shar->work,
                        "uudecode -p > %s << 'SHAR_END'\n",
                        shar->quoted_name.s);
                    archive_string_sprintf(&shar->work,
                        "begin %o ",
                        archive_entry_mode(entry) & 0777);
                    shar_quote(&shar->work, name, 0);
                    archive_strcat(&shar->work, "\n");
                } else {
                    archive_string_sprintf(&shar->work,
                        "sed 's/^X//' > %s << 'SHAR_END'\n",
                        shar->quoted_name.s);
                }
                shar->has_data = 1;
                shar->end_of_line = 1;
                shar->outpos = 0;
            }
            break;
        case AE_IFDIR:
            archive_string_sprintf(&shar->work,
                "mkdir -p %s > /dev/null 2>&1\n",
                shar->quoted_name.s);
            /* Record that we just created this directory. */
            if (shar->last_dir != NULL)
                free(shar->last_dir);

            shar->last_dir = strdup(name);
            /* Trim a trailing '/'. */
            pp = strrchr(shar->last_dir, '/');
            if (pp != NULL && pp[1] == '\0')
                *pp = '\0';
            /*
             * TODO: Put dir name/mode on a list to be fixed
             * up at end of archive.
             */
            break;
        case AE_IFIFO:
            archive_string_sprintf(&shar->work,
                "mkfifo %s\n", shar->quoted_name.s);
            break;
        case AE_IFCHR:
            archive_string_sprintf(&shar->work,
                "mknod %s c %d %d\n", shar->quoted_name.s,
                archive_entry_rdevmajor(entry),
                archive_entry_rdevminor(entry));
            break;
        case AE_IFBLK:
            archive_string_sprintf(&shar->work,
                "mknod %s b %d %d\n", shar->quoted_name.s,
                archive_entry_rdevmajor(entry),
                archive_entry_rdevminor(entry));
            break;
        default:
            return (ARCHIVE_WARN);
        }
    }

    return (ARCHIVE_OK);
}

static ssize_t
archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
{
    static const size_t ensured = 65533;
    struct shar *shar;
    const char *src;
    char *buf, *buf_end;
    int ret;
    size_t written = n;

    shar = (struct shar *)a->format_data;
    if (!shar->has_data || n == 0)
        return (0);

    src = (const char *)buff;

    /*
     * ensure is the number of bytes in buffer before expanding the
     * current character.  Each operation writes the current character
     * and optionally the start-of-new-line marker.  This can happen
     * twice before entering the loop, so make sure three additional
     * bytes can be written.
     */
    if (archive_string_ensure(&shar->work, ensured + 3) == NULL)
        __archive_errx(1, "Out of memory");

    if (shar->work.length > ensured) {
        ret = (*a->compressor.write)(a, shar->work.s,
            shar->work.length);
        if (ret != ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        archive_string_empty(&shar->work);
    }
    buf = shar->work.s + shar->work.length;
    buf_end = shar->work.s + ensured;

    if (shar->end_of_line) {
        *buf++ = 'X';
        shar->end_of_line = 0;
    }

    while (n-- != 0) {
        if ((*buf++ = *src++) == '\n') {
            if (n == 0)
                shar->end_of_line = 1;
            else
                *buf++ = 'X';
        }

        if (buf >= buf_end) {
            shar->work.length = buf - shar->work.s;
            ret = (*a->compressor.write)(a, shar->work.s,
                shar->work.length);
            if (ret != ARCHIVE_OK)
                return (ARCHIVE_FATAL);
            archive_string_empty(&shar->work);
            buf = shar->work.s;
        }
    }

    shar->work.length = buf - shar->work.s;

    return (written);
}

#define UUENC(c)    (((c)!=0) ? ((c) & 077) + ' ': '`')

static void
uuencode_group(const char _in[3], char out[4])
{
    const unsigned char *in = (const unsigned char *)_in;
    int t;

    t = (in[0] << 16) | (in[1] << 8) | in[2];
    out[0] = UUENC( 0x3f & (t >> 18) );
    out[1] = UUENC( 0x3f & (t >> 12) );
    out[2] = UUENC( 0x3f & (t >> 6) );
    out[3] = UUENC( 0x3f & t );
}

static void
uuencode_line(struct shar *shar, const char *inbuf, size_t len)
{
    char tmp_buf[3], *buf;
    size_t alloc_len;

    /* len <= 45 -> expanded to 60 + len byte + new line */
    alloc_len = shar->work.length + 62;
    if (archive_string_ensure(&shar->work, alloc_len) == NULL)
        __archive_errx(1, "Out of memory");

    buf = shar->work.s + shar->work.length;
    *buf++ = UUENC(len);
    while (len >= 3) {
        uuencode_group(inbuf, buf);
        len -= 3;
        inbuf += 3;
        buf += 4;
    }
    if (len != 0) {
        tmp_buf[0] = inbuf[0];
        if (len == 1)
            tmp_buf[1] = '\0';
        else
            tmp_buf[1] = inbuf[1];
        tmp_buf[2] = '\0';
        uuencode_group(inbuf, buf);
        buf += 4;
    }
    *buf++ = '\n';
    if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62))
        __archive_errx(1, "Buffer overflow");
    shar->work.length = buf - shar->work.s;
}

static ssize_t
archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
    size_t length)
{
    struct shar *shar;
    const char *src;
    size_t n;
    int ret;

    shar = (struct shar *)a->format_data;
    if (!shar->has_data)
        return (ARCHIVE_OK);
    src = (const char *)buff;

    if (shar->outpos != 0) {
        n = 45 - shar->outpos;
        if (n > length)
            n = length;
        memcpy(shar->outbuff + shar->outpos, src, n);
        if (shar->outpos + n < 45) {
            shar->outpos += n;
            return length;
        }
        uuencode_line(shar, shar->outbuff, 45);
        src += n;
        n = length - n;
    } else {
        n = length;
    }

    while (n >= 45) {
        uuencode_line(shar, src, 45);
        src += 45;
        n -= 45;

        if (shar->work.length < 65536)
            continue;
        ret = (*a->compressor.write)(a, shar->work.s,
            shar->work.length);
        if (ret != ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        archive_string_empty(&shar->work);
    }
    if (n != 0) {
        memcpy(shar->outbuff, src, n);
        shar->outpos = n;
    }
    return (length);
}

static int
archive_write_shar_finish_entry(struct archive_write *a)
{
    const char *g, *p, *u;
    struct shar *shar;
    int ret;

    shar = (struct shar *)a->format_data;
    if (shar->entry == NULL)
        return (0);

    if (shar->dump) {
        /* Finish uuencoded data. */
        if (shar->has_data) {
            if (shar->outpos > 0)
                uuencode_line(shar, shar->outbuff,
                    shar->outpos);
            archive_strcat(&shar->work, "`\nend\n");
            archive_strcat(&shar->work, "SHAR_END\n");
        }
        /* Restore file mode, owner, flags. */
        /*
         * TODO: Don't immediately restore mode for
         * directories; defer that to end of script.
         */
        archive_string_sprintf(&shar->work, "chmod %o ",
            archive_entry_mode(shar->entry) & 07777);
        shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
        archive_strcat(&shar->work, "\n");

        u = archive_entry_uname(shar->entry);
        g = archive_entry_gname(shar->entry);
        if (u != NULL || g != NULL) {
            archive_strcat(&shar->work, "chown ");
            if (u != NULL)
                shar_quote(&shar->work, u, 1);
            if (g != NULL) {
                archive_strcat(&shar->work, ":");
                shar_quote(&shar->work, g, 1);
            }
            shar_quote(&shar->work,
                archive_entry_pathname(shar->entry), 1);
            archive_strcat(&shar->work, "\n");
        }

        if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
            archive_string_sprintf(&shar->work, "chflags %s ",
                p, archive_entry_pathname(shar->entry));
            shar_quote(&shar->work,
                archive_entry_pathname(shar->entry), 1);
            archive_strcat(&shar->work, "\n");
        }

        /* TODO: restore ACLs */

    } else {
        if (shar->has_data) {
            /* Finish sed-encoded data:  ensure last line ends. */
            if (!shar->end_of_line)
                archive_strappend_char(&shar->work, '\n');
            archive_strcat(&shar->work, "SHAR_END\n");
        }
    }

    archive_entry_free(shar->entry);
    shar->entry = NULL;

    if (shar->work.length < 65536)
        return (ARCHIVE_OK);

    ret = (*a->compressor.write)(a, shar->work.s, shar->work.length);
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    archive_string_empty(&shar->work);

    return (ARCHIVE_OK);
}

static int
archive_write_shar_finish(struct archive_write *a)
{
    struct shar *shar;
    int ret;

    /*
     * TODO: Accumulate list of directory names/modes and
     * fix them all up at end-of-archive.
     */

    shar = (struct shar *)a->format_data;

    /*
     * Only write the end-of-archive markers if the archive was
     * actually started.  This avoids problems if someone sets
     * shar format, then sets another format (which would invoke
     * shar_finish to free the format-specific data).
     */
    if (shar->wrote_header == 0)
        return (ARCHIVE_OK);

    archive_strcat(&shar->work, "exit\n");

    ret = (*a->compressor.write)(a, shar->work.s, shar->work.length);
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);

    /* Shar output is never padded. */
    archive_write_set_bytes_in_last_block(&a->archive, 1);
    /*
     * TODO: shar should also suppress padding of
     * uncompressed data within gzip/bzip2 streams.
     */

    return (ARCHIVE_OK);
}

static int
archive_write_shar_destroy(struct archive_write *a)
{
    struct shar *shar;

    shar = (struct shar *)a->format_data;
    if (shar == NULL)
        return (ARCHIVE_OK);

    archive_entry_free(shar->entry);
    free(shar->last_dir);
    archive_string_free(&(shar->work));
    archive_string_free(&(shar->quoted_name));
    free(shar);
    a->format_data = NULL;
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_write_set_compression_none.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.16 2007/12/30 04:58:22 kientzle Exp $");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"

static int  archive_compressor_none_finish(struct archive_write *a);
static int  archive_compressor_none_init(struct archive_write *);
static int  archive_compressor_none_write(struct archive_write *,
            const void *, size_t);

struct archive_none {
    char    *buffer;
    ssize_t  buffer_size;
    char    *next;      /* Current insert location */
    ssize_t  avail;     /* Free space left in buffer */
};

int
archive_write_set_compression_none(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compression_none");
    a->compressor.init = &archive_compressor_none_init;
    return (0);
}

/*
 * Setup callback.
 */
static int
archive_compressor_none_init(struct archive_write *a)
{
    int ret;
    struct archive_none *state;

    a->archive.compression_code = ARCHIVE_COMPRESSION_NONE;
    a->archive.compression_name = "none";

    if (a->client_opener != NULL) {
        ret = (a->client_opener)(&a->archive, a->client_data);
        if (ret != 0)
            return (ret);
    }

    state = (struct archive_none *)malloc(sizeof(*state));
    if (state == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for output buffering");
        return (ARCHIVE_FATAL);
    }
    memset(state, 0, sizeof(*state));

    state->buffer_size = a->bytes_per_block;
    if (state->buffer_size != 0) {
        state->buffer = (char *)malloc(state->buffer_size);
        if (state->buffer == NULL) {
            archive_set_error(&a->archive, ENOMEM,
                "Can't allocate output buffer");
            free(state);
            return (ARCHIVE_FATAL);
        }
    }

    state->next = state->buffer;
    state->avail = state->buffer_size;

    a->compressor.data = state;
    a->compressor.write = archive_compressor_none_write;
    a->compressor.finish = archive_compressor_none_finish;
    return (ARCHIVE_OK);
}

/*
 * Write data to the stream.
 */
static int
archive_compressor_none_write(struct archive_write *a, const void *vbuff,
    size_t length)
{
    const char *buff;
    ssize_t remaining, to_copy;
    ssize_t bytes_written;
    struct archive_none *state;

    state = (struct archive_none *)a->compressor.data;
    buff = (const char *)vbuff;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    remaining = length;

    /*
     * If there is no buffer for blocking, just pass the data
     * straight through to the client write callback.  In
     * particular, this supports "no write delay" operation for
     * special applications.  Just set the block size to zero.
     */
    if (state->buffer_size == 0) {
        while (remaining > 0) {
            bytes_written = (a->client_writer)(&a->archive,
                a->client_data, buff, remaining);
            if (bytes_written <= 0)
                return (ARCHIVE_FATAL);
            a->archive.raw_position += bytes_written;
            remaining -= bytes_written;
            buff += bytes_written;
        }
        a->archive.file_position += length;
        return (ARCHIVE_OK);
    }

    /* If the copy buffer isn't empty, try to fill it. */
    if (state->avail < state->buffer_size) {
        /* If buffer is not empty... */
        /* ... copy data into buffer ... */
        to_copy = (remaining > state->avail) ?
            state->avail : remaining;
        memcpy(state->next, buff, to_copy);
        state->next += to_copy;
        state->avail -= to_copy;
        buff += to_copy;
        remaining -= to_copy;
        /* ... if it's full, write it out. */
        if (state->avail == 0) {
            bytes_written = (a->client_writer)(&a->archive,
                a->client_data, state->buffer, state->buffer_size);
            if (bytes_written <= 0)
                return (ARCHIVE_FATAL);
            /* XXX TODO: if bytes_written < state->buffer_size */
            a->archive.raw_position += bytes_written;
            state->next = state->buffer;
            state->avail = state->buffer_size;
        }
    }

    while (remaining > state->buffer_size) {
        /* Write out full blocks directly to client. */
        bytes_written = (a->client_writer)(&a->archive,
            a->client_data, buff, state->buffer_size);
        if (bytes_written <= 0)
            return (ARCHIVE_FATAL);
        a->archive.raw_position += bytes_written;
        buff += bytes_written;
        remaining -= bytes_written;
    }

    if (remaining > 0) {
        /* Copy last bit into copy buffer. */
        memcpy(state->next, buff, remaining);
        state->next += remaining;
        state->avail -= remaining;
    }

    a->archive.file_position += length;
    return (ARCHIVE_OK);
}


/*
 * Finish the compression.
 */
static int
archive_compressor_none_finish(struct archive_write *a)
{
    ssize_t block_length;
    ssize_t target_block_length;
    ssize_t bytes_written;
    int ret;
    int ret2;
    struct archive_none *state;

    state = (struct archive_none *)a->compressor.data;
    ret = ret2 = ARCHIVE_OK;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    /* If there's pending data, pad and write the last block */
    if (state->next != state->buffer) {
        block_length = state->buffer_size - state->avail;

        /* Tricky calculation to determine size of last block */
        target_block_length = block_length;
        if (a->bytes_in_last_block <= 0)
            /* Default or Zero: pad to full block */
            target_block_length = a->bytes_per_block;
        else
            /* Round to next multiple of bytes_in_last_block. */
            target_block_length = a->bytes_in_last_block *
                ( (block_length + a->bytes_in_last_block - 1) /
                a->bytes_in_last_block);
        if (target_block_length > a->bytes_per_block)
            target_block_length = a->bytes_per_block;
        if (block_length < target_block_length) {
            memset(state->next, 0,
                target_block_length - block_length);
            block_length = target_block_length;
        }
        bytes_written = (a->client_writer)(&a->archive,
            a->client_data, state->buffer, block_length);
        if (bytes_written <= 0)
            ret = ARCHIVE_FATAL;
        else {
            a->archive.raw_position += bytes_written;
            ret = ARCHIVE_OK;
        }
    }
    if (state->buffer)
        free(state->buffer);
    free(state);
    a->compressor.data = NULL;

    return (ret);
}

--- NEW FILE: archive_read_open_filename.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.21 2008/02/19 06:10:48 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#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 "archive.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif

struct read_file_data {
    int  fd;
    size_t   block_size;
    void    *buffer;
    mode_t   st_mode;  /* Mode bits for opened file. */
    char     can_skip; /* This file supports skipping. */
    char     filename[1]; /* Must be last! */
};

static int  file_close(struct archive *, void *);
static ssize_t  file_read(struct archive *, void *, const void **buff);
#if ARCHIVE_API_VERSION < 2
static ssize_t  file_skip(struct archive *, void *, size_t request);
#else
static off_t    file_skip(struct archive *, void *, off_t request);
#endif

int
archive_read_open_file(struct archive *a, const char *filename,
    size_t block_size)
{
    return (archive_read_open_filename(a, filename, block_size));
}

int
archive_read_open_filename(struct archive *a, const char *filename,
    size_t block_size)
{
    struct stat st;
    struct read_file_data *mine;
    void *b;
    int fd;

    archive_clear_error(a);
    if (filename == NULL || filename[0] == '\0') {
        /* We used to invoke archive_read_open_fd(a,0,block_size)
         * here, but that doesn't (and shouldn't) handle the
         * end-of-file flush when reading stdout from a pipe.
         * Basically, read_open_fd() is intended for folks who
         * are willing to handle such details themselves.  This
         * API is intended to be a little smarter for folks who
         * want easy handling of the common case.
         */
        filename = ""; /* Normalize NULL to "" */
        fd = 0;
#if defined(__CYGWIN__)
        setmode(0, O_BINARY);
#elif defined(_WIN32)
        _setmode(0, _O_BINARY);
#endif
    } else {
        fd = open(filename, O_RDONLY | O_BINARY);
        if (fd < 0) {
            archive_set_error(a, errno,
                "Failed to open '%s'", filename);
            return (ARCHIVE_FATAL);
        }
    }
    if (fstat(fd, &st) != 0) {
        archive_set_error(a, errno, "Can't stat '%s'", filename);
        return (ARCHIVE_FATAL);
    }

    mine = (struct read_file_data *)calloc(1,
        sizeof(*mine) + strlen(filename));
    b = malloc(block_size);
    if (mine == NULL || b == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        free(mine);
        free(b);
        return (ARCHIVE_FATAL);
    }
    strcpy(mine->filename, filename);
    mine->block_size = block_size;
    mine->buffer = b;
    mine->fd = fd;
    /* Remember mode so close can decide whether to flush. */
    mine->st_mode = st.st_mode;
    /* If we're reading a file from disk, ensure that we don't
       overwrite it with an extracted file. */
    if (S_ISREG(st.st_mode)) {
        archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
        /*
         * Enabling skip here is a performance optimization
         * for anything that supports lseek().  On FreeBSD
         * (and probably many other systems), only regular
         * files and raw disk devices support lseek() (on
         * other input types, lseek() returns success but
         * doesn't actually change the file pointer, which
         * just completely screws up the position-tracking
         * logic).  In addition, I've yet to find a portable
         * way to determine if a device is a raw disk device.
         * So I don't see a way to do much better than to only
         * enable this optimization for regular files.
         */
        mine->can_skip = 1;
    }
    return (archive_read_open2(a, mine,
        NULL, file_read, file_skip, file_close));
}

static ssize_t
file_read(struct archive *a, void *client_data, const void **buff)
{
    struct read_file_data *mine = (struct read_file_data *)client_data;
    ssize_t bytes_read;

    *buff = mine->buffer;
    bytes_read = read(mine->fd, mine->buffer, mine->block_size);
    if (bytes_read < 0) {
        if (mine->filename[0] == '\0')
            archive_set_error(a, errno, "Error reading stdin");
        else
            archive_set_error(a, errno, "Error reading '%s'",
                mine->filename);
    }
    return (bytes_read);
}

#if ARCHIVE_API_VERSION < 2
static ssize_t
file_skip(struct archive *a, void *client_data, size_t request)
#else
static off_t
file_skip(struct archive *a, void *client_data, off_t request)
#endif
{
    struct read_file_data *mine = (struct read_file_data *)client_data;
    off_t old_offset, new_offset;

    if (!mine->can_skip) /* We can't skip, so ... */
        return (0); /* ... skip zero bytes. */

    /* Reduce request to the next smallest multiple of block_size */
    request = (request / mine->block_size) * mine->block_size;
    if (request == 0)
        return (0);

    /*
     * Hurray for lazy evaluation: if the first lseek fails, the second
     * one will not be executed.
     */
    if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
        ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
    {
        /* If skip failed once, it will probably fail again. */
        mine->can_skip = 0;

        if (errno == ESPIPE)
        {
            /*
             * Failure to lseek() can be caused by the file
             * descriptor pointing to a pipe, socket or FIFO.
             * Return 0 here, so the compression layer will use
             * read()s instead to advance the file descriptor.
             * It's slower of course, but works as well.
             */
            return (0);
        }
        /*
         * There's been an error other than ESPIPE. This is most
         * likely caused by a programmer error (too large request)
         * or a corrupted archive file.
         */
        if (mine->filename[0] == '\0')
            /*
             * Should never get here, since lseek() on stdin ought
             * to return an ESPIPE error.
             */
            archive_set_error(a, errno, "Error seeking in stdin");
        else
            archive_set_error(a, errno, "Error seeking in '%s'",
                mine->filename);
        return (-1);
    }
    return (new_offset - old_offset);
}

static int
file_close(struct archive *a, void *client_data)
{
    struct read_file_data *mine = (struct read_file_data *)client_data;

    (void)a; /* UNUSED */

    /* Only flush and close if open succeeded. */
    if (mine->fd >= 0) {
        /*
         * Sometimes, we should flush the input before closing.
         *   Regular files: faster to just close without flush.
         *   Devices: must not flush (user might need to
         *      read the "next" item on a non-rewind device).
         *   Pipes and sockets:  must flush (otherwise, the
         *      program feeding the pipe or socket may complain).
         * Here, I flush everything except for regular files and
         * device nodes.
         */
        if (!S_ISREG(mine->st_mode)
            && !S_ISCHR(mine->st_mode)
            && !S_ISBLK(mine->st_mode)) {
            ssize_t bytesRead;
            do {
                bytesRead = read(mine->fd, mine->buffer,
                    mine->block_size);
            } while (bytesRead > 0);
        }
        /* If a named file was opened, then it needs to be closed. */
        if (mine->filename[0] != '\0')
            close(mine->fd);
    }
    free(mine->buffer);
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_util.3 ---
.\" 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/lib/libarchive/archive_util.3,v 1.8 2008/03/10 14:44:40 jkoshy Exp $
.\"
.Dd January 8, 2005
.Dt archive_util 3
.Os
.Sh NAME
.Nm archive_clear_error ,
.Nm archive_compression ,
.Nm archive_compression_name ,
.Nm archive_copy_error ,
.Nm archive_errno ,
.Nm archive_error_string ,
.Nm archive_file_count ,
.Nm archive_format ,
.Nm archive_format_name ,
.Nm archive_set_error
.Nd libarchive utility functions
.Sh SYNOPSIS
.In archive.h
.Ft void
.Fn archive_clear_error "struct archive *"
.Ft int
.Fn archive_compression "struct archive *"
.Ft const char *
.Fn archive_compression_name "struct archive *"
.Ft void
.Fn archive_copy_error "struct archive *" "struct archive *"
.Ft int
.Fn archive_errno "struct archive *"
.Ft const char *
.Fn archive_error_string "struct archive *"
.Ft int
.Fn archive_file_count "struct archive *"
.Ft int
.Fn archive_format "struct archive *"
.Ft const char *
.Fn archive_format_name "struct archive *"
.Ft void
.Fo archive_set_error
.Fa "struct archive *"
.Fa "int error_code"
.Fa "const char *fmt"
.Fa "..."
.Fc
.Sh DESCRIPTION
These functions provide access to various information about the
.Tn struct archive
object used in the
.Xr libarchive 3
library.
.Bl -tag -compact -width indent
.It Fn archive_clear_error
Clears any error information left over from a previous call.
Not generally used in client code.
.It Fn archive_compression
Returns a numeric code indicating the current compression.
This value is set by
.Fn archive_read_open .
.It Fn archive_compression_name
Returns a text description of the current compression suitable for display.
.It Fn archive_copy_error
Copies error information from one archive to another.
.It Fn archive_errno
Returns a numeric error code (see
.Xr errno 2 )
indicating the reason for the most recent error return.
.It Fn archive_error_string
Returns a textual error message suitable for display.
The error message here is usually more specific than that
obtained from passing the result of
.Fn archive_errno
to
.Xr strerror 3 .
.It Fn archive_file_count
Returns a count of the number of files processed by this archive object.
The count is incremented by calls to
.Xr archive_write_header
or
.Xr archive_read_next_header .
.It Fn archive_format
Returns a numeric code indicating the format of the current
archive entry.
This value is set by a successful call to
.Fn archive_read_next_header .
Note that it is common for this value to change from
entry to entry.
For example, a tar archive might have several entries that
utilize GNU tar extensions and several entries that do not.
These entries will have different format codes.
.It Fn archive_format_name
A textual description of the format of the current entry.
.It Fn archive_set_error
Sets the numeric error code and error description that will be returned
by
.Fn archive_errno
and
.Fn archive_error_string .
This function should be used within I/O callbacks to set system-specific
error codes and error descriptions.
This function accepts a printf-like format string and arguments.
However, you should be careful to use only the following printf
format specifiers:
.Dq %c ,
.Dq %d ,
.Dq %jd ,
.Dq %jo ,
.Dq %ju ,
.Dq %jx ,
.Dq %ld ,
.Dq %lo ,
.Dq %lu ,
.Dq %lx ,
.Dq %o ,
.Dq %u ,
.Dq %s ,
.Dq %x ,
.Dq %% .
Field-width specifiers and other printf features are
not uniformly supported and should not be used.
.El
.Sh SEE ALSO
.Xr archive_read 3 ,
.Xr archive_write 3 ,
.Xr libarchive 3 ,
.Xr printf 3
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .

--- NEW FILE: archive_write_set_format_by_name.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.9 2008/09/01 02:50:53 kientzle Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"

/* A table that maps names to functions. */
static
struct { const char *name; int (*setter)(struct archive *); } names[] =
{
    { "ar",     archive_write_set_format_ar_bsd },
    { "arbsd",  archive_write_set_format_ar_bsd },
    { "argnu",  archive_write_set_format_ar_svr4 },
    { "arsvr4", archive_write_set_format_ar_svr4 },
    { "cpio",   archive_write_set_format_cpio },
    { "mtree",  archive_write_set_format_mtree },
    { "newc",   archive_write_set_format_cpio_newc },
    { "odc",    archive_write_set_format_cpio },
    { "pax",    archive_write_set_format_pax },
    { "posix",  archive_write_set_format_pax },
    { "shar",   archive_write_set_format_shar },
    { "shardump",   archive_write_set_format_shar_dump },
    { "ustar",  archive_write_set_format_ustar },
    { "zip",    archive_write_set_format_zip },
    { NULL,     NULL }
};

int
archive_write_set_format_by_name(struct archive *a, const char *name)
{
    int i;

    for (i = 0; names[i].name != NULL; i++) {
        if (strcmp(name, names[i].name) == 0)
            return ((names[i].setter)(a));
    }

    archive_set_error(a, EINVAL, "No such format '%s'", name);
    return (ARCHIVE_FATAL);
}

--- NEW FILE: archive_read_support_format_empty.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.4 2008/12/06 06:45:15 kientzle Exp $");

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_read_private.h"

static int  archive_read_format_empty_bid(struct archive_read *);
static int  archive_read_format_empty_read_data(struct archive_read *,
            const void **, size_t *, off_t *);
static int  archive_read_format_empty_read_header(struct archive_read *,
            struct archive_entry *);
int
archive_read_support_format_empty(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    int r;

    r = __archive_read_register_format(a,
        NULL,
        NULL,
        archive_read_format_empty_bid,
        NULL,
        archive_read_format_empty_read_header,
        archive_read_format_empty_read_data,
        NULL,
        NULL);

    return (r);
}


static int
archive_read_format_empty_bid(struct archive_read *a)
{
    ssize_t avail;

    (void)__archive_read_ahead(a, 1, &avail);
    if (avail != 0)
        return (-1);
    return (1);
}

static int
archive_read_format_empty_read_header(struct archive_read *a,
    struct archive_entry *entry)
{
    (void)a; /* UNUSED */
    (void)entry; /* UNUSED */

    a->archive.archive_format = ARCHIVE_FORMAT_EMPTY;
    a->archive.archive_format_name = "Empty file";

    return (ARCHIVE_EOF);
}

static int
archive_read_format_empty_read_data(struct archive_read *a,
    const void **buff, size_t *size, off_t *offset)
{
    (void)a; /* UNUSED */
    (void)buff; /* UNUSED */
    (void)size; /* UNUSED */
    (void)offset; /* UNUSED */

    return (ARCHIVE_EOF);
}

--- NEW FILE: filter_fork.c ---
/*-
 * Copyright (c) 2007 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

/* This capability is only available on POSIX systems. */
#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
    (defined(HAVE_FORK) || defined(HAVE_VFORK))

__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.5 2008/09/12 05:33:00 kientzle Exp $");

#if defined(HAVE_POLL)
#  if defined(HAVE_POLL_H)
#    include <poll.h>
#  elif defined(HAVE_SYS_POLL_H)
#    include <sys/poll.h>
#  endif
#elif defined(HAVE_SELECT)
#  if defined(HAVE_SYS_SELECT_H)
#    include <sys/select.h>
#  elif defined(HAVE_UNISTD_H)
#    include <unistd.h>
#  endif
#endif
#ifdef HAVE_FCNTL_H
#  include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include "filter_fork.h"

pid_t
__archive_create_child(const char *path, int *child_stdin, int *child_stdout)
{
    pid_t child;
    int stdin_pipe[2], stdout_pipe[2], tmp;

    if (pipe(stdin_pipe) == -1)
        goto state_allocated;
    if (stdin_pipe[0] == 1 /* stdout */) {
        if ((tmp = dup(stdin_pipe[0])) == -1)
            goto stdin_opened;
        close(stdin_pipe[0]);
        stdin_pipe[0] = tmp;
    }
    if (pipe(stdout_pipe) == -1)
        goto stdin_opened;
    if (stdout_pipe[1] == 0 /* stdin */) {
        if ((tmp = dup(stdout_pipe[1])) == -1)
            goto stdout_opened;
        close(stdout_pipe[1]);
        stdout_pipe[1] = tmp;
    }

#if HAVE_VFORK
    switch ((child = vfork())) {
#else
    switch ((child = fork())) {
#endif
    case -1:
        goto stdout_opened;
    case 0:
        close(stdin_pipe[1]);
        close(stdout_pipe[0]);
        if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)
            _exit(254);
        if (stdin_pipe[0] != 0 /* stdin */)
            close(stdin_pipe[0]);
        if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)
            _exit(254);
        if (stdout_pipe[1] != 1 /* stdout */)
            close(stdout_pipe[1]);
        execlp(path, path, (char *)NULL);
        _exit(254);
    default:
        close(stdin_pipe[0]);
        close(stdout_pipe[1]);

        *child_stdin = stdin_pipe[1];
        fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
        *child_stdout = stdout_pipe[0];
        fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
    }

    return child;

stdout_opened:
    close(stdout_pipe[0]);
    close(stdout_pipe[1]);
stdin_opened:
    close(stdin_pipe[0]);
    close(stdin_pipe[1]);
state_allocated:
    return -1;
}

void
__archive_check_child(int in, int out)
{
#if defined(HAVE_POLL)
    struct pollfd fds[2];
    int idx;

    idx = 0;
    if (in != -1) {
        fds[idx].fd = in;
        fds[idx].events = POLLOUT;
        ++idx;
    }
    if (out != -1) {
        fds[idx].fd = out;
        fds[idx].events = POLLIN;
        ++idx;
    }

    poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
#elif defined(HAVE_SELECT)
    fd_set fds_in, fds_out, fds_error;

    FD_ZERO(&fds_in);
    FD_ZERO(&fds_out);
    FD_ZERO(&fds_error);
    if (out != -1) {
        FD_SET(out, &fds_in);
        FD_SET(out, &fds_error);
    }
    if (in != -1) {
        FD_SET(in, &fds_out);
        FD_SET(in, &fds_error);
    }
    select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
#else
    sleep(1);
#endif
}

#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */

--- NEW FILE: CMakeLists.txt ---

############################################
#
# How to build libarchive
#
############################################

# Public headers
SET(include_HEADERS
  archive.h
  archive_entry.h
)

# Sources and private headers
SET(libarchive_SOURCES
  archive_check_magic.c
  archive_endian.h
  archive_entry.c
  archive_entry.h
  archive_entry_copy_stat.c
  archive_entry_copy_bhfi.c
  archive_entry_link_resolver.c
  archive_entry_private.h
  archive_entry_stat.c
  archive_entry_strmode.c
  archive_entry_xattr.c
  archive_platform.h
  archive_private.h
  archive_read.c
  archive_read_data_into_fd.c
  archive_read_disk.c
  archive_read_disk_entry_from_file.c
  archive_read_disk_private.h
  archive_read_disk_set_standard_lookup.c
  archive_read_extract.c
  archive_read_open_fd.c
  archive_read_open_file.c
  archive_read_open_filename.c
  archive_read_open_memory.c
  archive_read_private.h
  archive_read_support_compression_all.c
  archive_read_support_compression_bzip2.c
  archive_read_support_compression_compress.c
  archive_read_support_compression_gzip.c
  archive_read_support_compression_none.c
  archive_read_support_compression_program.c
  archive_read_support_compression_xz.c
  archive_read_support_format_all.c
  archive_read_support_format_ar.c
  archive_read_support_format_cpio.c
  archive_read_support_format_empty.c
  archive_read_support_format_iso9660.c
  archive_read_support_format_mtree.c
  archive_read_support_format_raw.c
  archive_read_support_format_tar.c
  archive_read_support_format_zip.c
  archive_string.c
  archive_string.h
  archive_string_sprintf.c
  archive_util.c
  archive_virtual.c
  archive_write.c
  archive_write_disk.c
  archive_write_disk_private.h
  archive_write_disk_set_standard_lookup.c
  archive_write_private.h
  archive_write_open_fd.c
  archive_write_open_file.c
  archive_write_open_filename.c
  archive_write_open_memory.c
  archive_write_set_compression_bzip2.c
  archive_write_set_compression_compress.c
  archive_write_set_compression_gzip.c
  archive_write_set_compression_none.c
  archive_write_set_compression_program.c
  archive_write_set_compression_xz.c
  archive_write_set_format.c
  archive_write_set_format_ar.c
  archive_write_set_format_by_name.c
  archive_write_set_format_cpio.c
  archive_write_set_format_cpio_newc.c
  archive_write_set_format_mtree.c
  archive_write_set_format_pax.c
  archive_write_set_format_shar.c
  archive_write_set_format_ustar.c
  archive_write_set_format_zip.c
  filter_fork.c
  filter_fork.h
)

# Man pages
SET(libarchive_MANS
  archive_entry.3
  archive_read.3
  archive_read_disk.3
  archive_util.3
  archive_write.3
  archive_write_disk.3
  cpio.5
  libarchive.3
  libarchive_internals.3
  libarchive-formats.5
  mtree.5
  tar.5
)

IF(WIN32 AND NOT CYGWIN)
#  LIST(APPEND libarchive_SOURCES archive_entry_copy_bhfi.c)
  LIST(APPEND libarchive_SOURCES archive_windows.c)
  LIST(APPEND libarchive_SOURCES archive_windows.h)
  LIST(APPEND libarchive_SOURCES filter_fork_windows.c)
ENDIF(WIN32 AND NOT CYGWIN)

IF(BUILD_ARCHIVE_WITHIN_CMAKE)
  # when building inside the CMake tree only use static
  # and call the library cmlibarchive
  ADD_LIBRARY(cmlibarchive STATIC ${libarchive_SOURCES} ${include_HEADERS})
ELSE()
  # Libarchive is a shared library
  ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
  TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
  SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION})
  SET_TARGET_PROPERTIES(archive PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
  
  # archive_static is a static library
  ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS})
  SET_TARGET_PROPERTIES(archive_static PROPERTIES COMPILE_DEFINITIONS
    LIBARCHIVE_STATIC)
  SET_TARGET_PROPERTIES(archive_static PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
  #  On Posix systems, libarchive.so and libarchive.a can co-exist.
  IF(NOT WIN32 OR CYGWIN)
    SET_TARGET_PROPERTIES(archive_static PROPERTIES OUTPUT_NAME archive)
  ENDIF(NOT WIN32 OR CYGWIN)
  
  # How to install the libraries
  INSTALL(TARGETS archive archive_static
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)
  INSTALL_MAN(${libarchive_MANS})
  INSTALL(FILES ${include_HEADERS} DESTINATION include)
  ADD_SUBDIRECTORY(test)
ENDIF()

--- NEW FILE: archive_write_disk_set_standard_lookup.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk_set_standard_lookup.c,v 1.4 2007/05/29 01:00:19 kientzle Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"
#include "archive_write_disk_private.h"

struct bucket {
    char    *name;
    int  hash;
    id_t     id;
};

static const size_t cache_size = 127;
static unsigned int hash(const char *);
static gid_t    lookup_gid(void *, const char *uname, gid_t);
static uid_t    lookup_uid(void *, const char *uname, uid_t);
static void cleanup(void *);

/*
 * Installs functions that use getpwnam()/getgrnam()---along with
 * a simple cache to accelerate such lookups---into the archive_write_disk
 * object.  This is in a separate file because getpwnam()/getgrnam()
 * can pull in a LOT of library code (including NIS/LDAP functions, which
 * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
 * it inappropriate for some space-constrained applications.
 *
 * Applications that are size-sensitive may want to just use the
 * real default functions (defined in archive_write_disk.c) that just
 * use the uid/gid without the lookup.  Or define your own custom functions
 * if you prefer.
 *
 * TODO: Replace these hash tables with simpler move-to-front LRU
 * lists with a bounded size (128 items?).  The hash is a bit faster,
 * but has a bad pathology in which it thrashes a single bucket.  Even
 * walking a list of 128 items is a lot faster than calling
 * getpwnam()!
 */
int
archive_write_disk_set_standard_lookup(struct archive *a)
{
    struct bucket *ucache = malloc(cache_size * sizeof(struct bucket));
    struct bucket *gcache = malloc(cache_size * sizeof(struct bucket));
    memset(ucache, 0, cache_size * sizeof(struct bucket));
    memset(gcache, 0, cache_size * sizeof(struct bucket));
    archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);
    archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);
    return (ARCHIVE_OK);
}

static gid_t
lookup_gid(void *private_data, const char *gname, gid_t gid)
{
    int h;
    struct bucket *b;
    struct bucket *gcache = (struct bucket *)private_data;

    /* If no gname, just use the gid provided. */
    if (gname == NULL || *gname == '\0')
        return (gid);

    /* Try to find gname in the cache. */
    h = hash(gname);
    b = &gcache[h % cache_size ];
    if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
        return ((gid_t)b->id);

    /* Free the cache slot for a new entry. */
    if (b->name != NULL)
        free(b->name);
    b->name = strdup(gname);
    /* Note: If strdup fails, that's okay; we just won't cache. */
    b->hash = h;
#if HAVE_GRP_H
    {
        char _buffer[128];
        size_t bufsize = 128;
        char *buffer = _buffer;
        struct group    grent, *result;
        int r;

        for (;;) {
            r = getgrnam_r(gname, &grent, buffer, bufsize, &result);
            if (r == 0)
                break;
            if (r != ERANGE)
                break;
            bufsize *= 2;
            if (buffer != _buffer)
                free(buffer);
            buffer = malloc(bufsize);
            if (buffer == NULL)
                break;
        }
        if (result != NULL)
            gid = result->gr_gid;
        if (buffer != _buffer)
            free(buffer);
    }
#elif defined(_WIN32) && !defined(__CYGWIN__)
    /* TODO: do a gname->gid lookup for Windows. */
#else
    #error No way to perform gid lookups on this platform
#endif
    b->id = gid;

    return (gid);
}

static uid_t
lookup_uid(void *private_data, const char *uname, uid_t uid)
{
    int h;
    struct bucket *b;
    struct bucket *ucache = (struct bucket *)private_data;

    /* If no uname, just use the uid provided. */
    if (uname == NULL || *uname == '\0')
        return (uid);

    /* Try to find uname in the cache. */
    h = hash(uname);
    b = &ucache[h % cache_size ];
    if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
        return ((uid_t)b->id);

    /* Free the cache slot for a new entry. */
    if (b->name != NULL)
        free(b->name);
    b->name = strdup(uname);
    /* Note: If strdup fails, that's okay; we just won't cache. */
    b->hash = h;
#if HAVE_PWD_H
    {
        char _buffer[128];
        size_t bufsize = 128;
        char *buffer = _buffer;
        struct passwd   pwent, *result;
        int r;

        for (;;) {
            r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);
            if (r == 0)
                break;
            if (r != ERANGE)
                break;
            bufsize *= 2;
            if (buffer != _buffer)
                free(buffer);
            buffer = malloc(bufsize);
            if (buffer == NULL)
                break;
        }
        if (result != NULL)
            uid = result->pw_uid;
        if (buffer != _buffer)
            free(buffer);
    }
#elif defined(_WIN32) && !defined(__CYGWIN__)
    /* TODO: do a uname->uid lookup for Windows. */
#else
    #error No way to look up uids on this platform
#endif
    b->id = uid;

    return (uid);
}

static void
cleanup(void *private)
{
    size_t i;
    struct bucket *cache = (struct bucket *)private;

    for (i = 0; i < cache_size; i++)
        free(cache[i].name);
    free(cache);
}


static unsigned int
hash(const char *p)
{
    /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
       as used by ELF for hashing function names. */
    unsigned g, h = 0;
    while (*p != '\0') {
        h = ( h << 4 ) + *p++;
        if (( g = h & 0xF0000000 )) {
            h ^= g >> 24;
            h &= 0x0FFFFFFF;
        }
    }
    return h;
}

--- NEW FILE: archive_read_support_compression_gzip.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.17 2008/12/06 06:45:15 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
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ZLIB_H
#include <cm_zlib.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"

#ifdef HAVE_ZLIB_H
struct private_data {
    z_stream     stream;
    char         in_stream;
    unsigned char   *out_block;
    size_t       out_block_size;
    int64_t      total_out;
    unsigned long    crc;
    char         eof; /* True = found end of compressed data. */
};

/* Gzip Filter. */
static ssize_t  gzip_filter_read(struct archive_read_filter *, const void **);
static int  gzip_filter_close(struct archive_read_filter *);
#endif

/*
 * Note that we can detect gzip archives even if we can't decompress
 * them.  (In fact, we like detecting them because we can give better
 * error messages.)  So the bid framework here gets compiled even
 * if zlib is unavailable.
 *
 * TODO: If zlib is unavailable, gzip_bidder_init() should
 * use the compress_program framework to try to fire up an external
 * gunzip program.
 */
static int  gzip_bidder_bid(struct archive_read_filter_bidder *,
            struct archive_read_filter *);
static int  gzip_bidder_init(struct archive_read_filter *);

int
archive_read_support_compression_gzip(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);

    if (bidder == NULL)
        return (ARCHIVE_FATAL);

    bidder->data = NULL;
    bidder->bid = gzip_bidder_bid;
    bidder->init = gzip_bidder_init;
    bidder->options = NULL;
    bidder->free = NULL; /* No data, so no cleanup necessary. */
    /* Signal the extent of gzip support with the return value here. */
#if HAVE_ZLIB_H
    return (ARCHIVE_OK);
#else
    archive_set_error(_a, ARCHIVE_ERRNO_MISC,
        "Using external gunzip program");
    return (ARCHIVE_WARN);
#endif
}

/*
 * Read and verify the header.
 *
 * Returns zero if the header couldn't be validated, else returns
 * number of bytes in header.  If pbits is non-NULL, it receives a
 * count of bits verified, suitable for use by bidder.
 */
static int
peek_at_header(struct archive_read_filter *filter, int *pbits)
{
    const unsigned char *p;
    ssize_t avail, len;
    int bits = 0;
    int header_flags;

    /* Start by looking at the first ten bytes of the header, which
     * is all fixed layout. */
    len = 10;
    p = __archive_read_filter_ahead(filter, len, &avail);
    if (p == NULL || avail == 0)
        return (0);
    if (p[0] != 037)
        return (0);
    bits += 8;
    if (p[1] != 0213)
        return (0);
    bits += 8;
    if (p[2] != 8) /* We only support deflation. */
        return (0);
    bits += 8;
    if ((p[3] & 0xE0)!= 0)  /* No reserved flags set. */
        return (0);
    bits += 3;
    header_flags = p[3];
    /* Bytes 4-7 are mod time. */
    /* Byte 8 is deflate flags. */
    /* XXXX TODO: return deflate flags back to consume_header for use
       in initializing the decompressor. */
    /* Byte 9 is OS. */

    /* Optional extra data:  2 byte length plus variable body. */
    if (header_flags & 4) {
        p = __archive_read_filter_ahead(filter, len + 2, &avail);
        if (p == NULL)
            return (0);
        len += ((int)p[len + 1] << 8) | (int)p[len];
        len += 2;
    }

    /* Null-terminated optional filename. */
    if (header_flags & 8) {
        do {
            ++len;
            if (avail < len)
                p = __archive_read_filter_ahead(filter,
                    len, &avail);
            if (p == NULL)
                return (0);
        } while (p[len - 1] != 0);
    }

    /* Null-terminated optional comment. */
    if (header_flags & 16) {
        do {
            ++len;
            if (avail < len)
                p = __archive_read_filter_ahead(filter,
                    len, &avail);
            if (p == NULL)
                return (0);
        } while (p[len - 1] != 0);
    }

    /* Optional header CRC */
    if ((header_flags & 2)) {
        p = __archive_read_filter_ahead(filter, len + 2, &avail);
        if (p == NULL)
            return (0);
#if 0
    int hcrc = ((int)p[len + 1] << 8) | (int)p[len];
    int crc = /* XXX TODO: Compute header CRC. */;
    if (crc != hcrc)
        return (0);
    bits += 16;
#endif
        len += 2;
    }

    if (pbits != NULL)
        *pbits = bits;
    return (len);
}

/*
 * Bidder just verifies the header and returns the number of verified bits.
 */
static int
gzip_bidder_bid(struct archive_read_filter_bidder *self,
    struct archive_read_filter *filter)
{
    int bits_checked;

    (void)self; /* UNUSED */

    if (peek_at_header(filter, &bits_checked))
        return (bits_checked);
    return (0);
}


#ifndef HAVE_ZLIB_H

/*
 * If we don't have the library on this system, we can't do the
 * decompression directly.  We can, however, try to run gunzip
 * in case that's available.
 */
static int
gzip_bidder_init(struct archive_read_filter *self)
{
    int r;

    r = __archive_read_program(self, "gunzip");
    /* Note: We set the format here even if __archive_read_program()
     * above fails.  We do, after all, know what the format is
     * even if we weren't able to read it. */
    self->code = ARCHIVE_COMPRESSION_GZIP;
    self->name = "gzip";
    return (r);
}

#else

/*
 * Initialize the filter object.
 */
static int
gzip_bidder_init(struct archive_read_filter *self)
{
    struct private_data *state;
    static const size_t out_block_size = 64 * 1024;
    void *out_block;

    self->code = ARCHIVE_COMPRESSION_GZIP;
    self->name = "gzip";

    state = (struct private_data *)calloc(sizeof(*state), 1);
    out_block = (unsigned char *)malloc(out_block_size);
    if (state == NULL || out_block == NULL) {
        free(out_block);
        free(state);
        archive_set_error(&self->archive->archive, ENOMEM,
            "Can't allocate data for gzip decompression");
        return (ARCHIVE_FATAL);
    }

    self->data = state;
    state->out_block_size = out_block_size;
    state->out_block = out_block;
    self->read = gzip_filter_read;
    self->skip = NULL; /* not supported */
    self->close = gzip_filter_close;

    state->in_stream = 0; /* We're not actually within a stream yet. */

    return (ARCHIVE_OK);
}

static int
consume_header(struct archive_read_filter *self)
{
    struct private_data *state;
    ssize_t avail;
    size_t len;
    int ret;

    state = (struct private_data *)self->data;

    /* If this is a real header, consume it. */
    len = peek_at_header(self->upstream, NULL);
    if (len == 0)
        return (ARCHIVE_EOF);
    __archive_read_filter_consume(self->upstream, len);

    /* Initialize CRC accumulator. */
    state->crc = crc32(0L, NULL, 0);

    /* Initialize compression library. */
    state->stream.next_in = (unsigned char *)(uintptr_t)
        __archive_read_filter_ahead(self->upstream, 1, &avail);
    state->stream.avail_in = avail;
    ret = inflateInit2(&(state->stream),
        -15 /* Don't check for zlib header */);

    /* Decipher the error code. */
    switch (ret) {
    case Z_OK:
        state->in_stream = 1;
        return (ARCHIVE_OK);
    case Z_STREAM_ERROR:
        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "invalid setup parameter");
        break;
    case Z_MEM_ERROR:
        archive_set_error(&self->archive->archive, ENOMEM,
            "Internal error initializing compression library: "
            "out of memory");
        break;
    case Z_VERSION_ERROR:
        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "invalid library version");
        break;
    default:
        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            " Zlib error %d", ret);
        break;
    }
    return (ARCHIVE_FATAL);
}

static int
consume_trailer(struct archive_read_filter *self)
{
    struct private_data *state;
    const unsigned char *p;
    ssize_t avail;

    state = (struct private_data *)self->data;

    state->in_stream = 0;
    switch (inflateEnd(&(state->stream))) {
    case Z_OK:
        break;
    default:
        archive_set_error(&self->archive->archive,
            ARCHIVE_ERRNO_MISC,
            "Failed to clean up gzip decompressor");
        return (ARCHIVE_FATAL);
    }

    /* GZip trailer is a fixed 8 byte structure. */
    p = __archive_read_filter_ahead(self->upstream, 8, &avail);
    if (p == NULL || avail == 0)
        return (ARCHIVE_FATAL);

    /* XXX TODO: Verify the length and CRC. */

    /* We've verified the trailer, so consume it now. */
    __archive_read_filter_consume(self->upstream, 8);

    return (ARCHIVE_OK);
}

static ssize_t
gzip_filter_read(struct archive_read_filter *self, const void **p)
{
    struct private_data *state;
    size_t decompressed;
    ssize_t avail_in;
    int ret;

    state = (struct private_data *)self->data;

    /* Empty our output buffer. */
    state->stream.next_out = state->out_block;
    state->stream.avail_out = state->out_block_size;

    /* Try to fill the output buffer. */
    while (state->stream.avail_out > 0 && !state->eof) {
        /* If we're not in a stream, read a header
         * and initialize the decompression library. */
        if (!state->in_stream) {
            ret = consume_header(self);
            if (ret == ARCHIVE_EOF) {
                state->eof = 1;
                break;
            }
            if (ret < ARCHIVE_OK)
                return (ret);
        }

        /* Peek at the next available data. */
        /* ZLib treats stream.next_in as const but doesn't declare
         * it so, hence this ugly cast. */
        state->stream.next_in = (unsigned char *)(uintptr_t)
            __archive_read_filter_ahead(self->upstream, 1, &avail_in);
        if (state->stream.next_in == NULL)
            return (ARCHIVE_FATAL);
        state->stream.avail_in = avail_in;

        /* Decompress and consume some of that data. */
        ret = inflate(&(state->stream), 0);
        switch (ret) {
        case Z_OK: /* Decompressor made some progress. */
            __archive_read_filter_consume(self->upstream,
                avail_in - state->stream.avail_in);
            break;
        case Z_STREAM_END: /* Found end of stream. */
            __archive_read_filter_consume(self->upstream,
                avail_in - state->stream.avail_in);
            /* Consume the stream trailer; release the
             * decompression library. */
            ret = consume_trailer(self);
            break;
        default:
            /* Return an error. */
            archive_set_error(&self->archive->archive,
                ARCHIVE_ERRNO_MISC,
                "gzip decompression failed");
            return (ARCHIVE_FATAL);
        }
    }

    /* We've read as much as we can. */
    decompressed = state->stream.next_out - state->out_block;
    state->total_out += decompressed;
    if (decompressed == 0)
        *p = NULL;
    else
        *p = state->out_block;
    return (decompressed);
}

/*
 * Clean up the decompressor.
 */
static int
gzip_filter_close(struct archive_read_filter *self)
{
    struct private_data *state;
    int ret;

    state = (struct private_data *)self->data;
    ret = ARCHIVE_OK;

    if (state->in_stream) {
        switch (inflateEnd(&(state->stream))) {
        case Z_OK:
            break;
        default:
            archive_set_error(&(self->archive->archive),
                ARCHIVE_ERRNO_MISC,
                "Failed to clean up gzip compressor");
            ret = ARCHIVE_FATAL;
        }
    }

    free(state->out_block);
    free(state);
    return (ret);
}

#endif /* HAVE_ZLIB_H */

--- NEW FILE: archive_entry_xattr.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.55 2008/12/23 05:01:43 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#else
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>   /* for Linux file flags */
#endif
/*
 * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
 * As the include guards don't agree, the order of include is important.
 */
#ifdef HAVE_LINUX_EXT2_FS_H
#include <linux/ext2_fs.h>  /* for Linux file flags */
#endif
#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
#include <ext2fs/ext2_fs.h> /* for Linux file flags */
#endif
#include <stddef.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_WCHAR_H
#include <wchar.h>
#endif

#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_entry_private.h"

/*
 * extended attribute handling
 */

void
archive_entry_xattr_clear(struct archive_entry *entry)
{
    struct ae_xattr *xp;

    while (entry->xattr_head != NULL) {
        xp = entry->xattr_head->next;
        free(entry->xattr_head->name);
        free(entry->xattr_head->value);
        free(entry->xattr_head);
        entry->xattr_head = xp;
    }

    entry->xattr_head = NULL;
}

void
archive_entry_xattr_add_entry(struct archive_entry *entry,
    const char *name, const void *value, size_t size)
{
    struct ae_xattr *xp;

    for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
        ;

    if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
        /* XXX Error XXX */
        return;

    xp->name = strdup(name);
    if ((xp->value = malloc(size)) != NULL) {
        memcpy(xp->value, value, size);
        xp->size = size;
    } else
        xp->size = 0;

    xp->next = entry->xattr_head;
    entry->xattr_head = xp;
}


/*
 * returns number of the extended attribute entries
 */
int
archive_entry_xattr_count(struct archive_entry *entry)
{
    struct ae_xattr *xp;
    int count = 0;

    for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
        count++;

    return count;
}

int
archive_entry_xattr_reset(struct archive_entry * entry)
{
    entry->xattr_p = entry->xattr_head;

    return archive_entry_xattr_count(entry);
}

int
archive_entry_xattr_next(struct archive_entry * entry,
    const char **name, const void **value, size_t *size)
{
    if (entry->xattr_p) {
        *name = entry->xattr_p->name;
        *value = entry->xattr_p->value;
        *size = entry->xattr_p->size;

        entry->xattr_p = entry->xattr_p->next;

        return (ARCHIVE_OK);
    } else {
        *name = NULL;
        *value = NULL;
        *size = (size_t)0;
        return (ARCHIVE_WARN);
    }
}

/*
 * end of xattr handling
 */

--- NEW FILE: archive_read_support_compression_compress.c ---
/*-
 * 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.
 */

/*
 * This code borrows heavily from "compress" source code, which is
 * protected by the following copyright.  (Clause 3 dropped by request
 * of the Regents.)
 */

/*-
 * Copyright (c) 1985, 1986, 1992, 1993
 *  The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Diomidis Spinellis and James A. Woods, derived from original
 * work by Spencer Thomas and Joseph Orost.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.11 2008/12/06 06:45:15 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
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_read_private.h"

/*
 * Because LZW decompression is pretty simple, I've just implemented
 * the whole decompressor here (cribbing from "compress" source code,
 * of course), rather than relying on an external library.  I have
 * made an effort to clarify and simplify the algorithm, so the
 * names and structure here don't exactly match those used by compress.
 */

struct private_data {
    /* Input variables. */
    const unsigned char *next_in;
    size_t           avail_in;
    int          bit_buffer;
    int          bits_avail;
    size_t           bytes_in_section;

    /* Output variables. */
    size_t           out_block_size;
    void            *out_block;

    /* Decompression status variables. */
    int          use_reset_code;
    int          end_of_stream; /* EOF status. */
    int          maxcode;   /* Largest code. */
    int          maxcode_bits;  /* Length of largest code. */
    int          section_end_code; /* When to increase bits. */
    int          bits;      /* Current code length. */
    int          oldcode;   /* Previous code. */
    int          finbyte;   /* Last byte of prev code. */

    /* Dictionary. */
    int          free_ent;       /* Next dictionary entry. */
    unsigned char        suffix[65536];
    uint16_t         prefix[65536];

    /*
     * Scratch area for expanding dictionary entries.  Note:
     * "worst" case here comes from compressing /dev/zero: the
     * last code in the dictionary will code a sequence of
     * 65536-256 zero bytes.  Thus, we need stack space to expand
     * a 65280-byte dictionary entry.  (Of course, 32640:1
     * compression could also be considered the "best" case. ;-)
     */
    unsigned char       *stackp;
    unsigned char        stack[65300];
};

static int  compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
static int  compress_bidder_init(struct archive_read_filter *);
static int  compress_bidder_free(struct archive_read_filter_bidder *);

static ssize_t  compress_filter_read(struct archive_read_filter *, const void **);
static int  compress_filter_close(struct archive_read_filter *);

static int  getbits(struct archive_read_filter *, int n);
static int  next_code(struct archive_read_filter *);

int
archive_read_support_compression_compress(struct archive *_a)
{
    struct archive_read *a = (struct archive_read *)_a;
    struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a);

    if (bidder == NULL)
        return (ARCHIVE_FATAL);

    bidder->data = NULL;
    bidder->bid = compress_bidder_bid;
    bidder->init = compress_bidder_init;
    bidder->options = NULL;
    bidder->free = compress_bidder_free;
    return (ARCHIVE_OK);
}

/*
 * Test whether we can handle this data.
 *
 * This logic returns zero if any part of the signature fails.  It
 * also tries to Do The Right Thing if a very short buffer prevents us
 * from verifying as much as we would like.
 */
static int
compress_bidder_bid(struct archive_read_filter_bidder *self,
    struct archive_read_filter *filter)
{
    const unsigned char *buffer;
    ssize_t avail;
    int bits_checked;

    (void)self; /* UNUSED */

    buffer = __archive_read_filter_ahead(filter, 2, &avail);

    if (buffer == NULL)
        return (0);

    bits_checked = 0;
    if (buffer[0] != 037)   /* Verify first ID byte. */
        return (0);
    bits_checked += 8;

    if (buffer[1] != 0235)  /* Verify second ID byte. */
        return (0);
    bits_checked += 8;

    /*
     * TODO: Verify more.
     */

    return (bits_checked);
}

/*
 * Setup the callbacks.
 */
static int
compress_bidder_init(struct archive_read_filter *self)
{
    struct private_data *state;
    static const size_t out_block_size = 64 * 1024;
    void *out_block;
    int code;

    self->code = ARCHIVE_COMPRESSION_COMPRESS;
    self->name = "compress (.Z)";

    state = (struct private_data *)calloc(sizeof(*state), 1);
    out_block = malloc(out_block_size);
    if (state == NULL || out_block == NULL) {
        free(out_block);
        free(state);
        archive_set_error(&self->archive->archive, ENOMEM,
            "Can't allocate data for %s decompression",
            self->name);
        return (ARCHIVE_FATAL);
    }

    self->data = state;
    state->out_block_size = out_block_size;
    state->out_block = out_block;
    self->read = compress_filter_read;
    self->skip = NULL; /* not supported */
    self->close = compress_filter_close;

    /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */

    code = getbits(self, 8); /* Skip first signature byte. */
    code = getbits(self, 8); /* Skip second signature byte. */

    code = getbits(self, 8);
    state->maxcode_bits = code & 0x1f;
    state->maxcode = (1 << state->maxcode_bits);
    state->use_reset_code = code & 0x80;

    /* Initialize decompressor. */
    state->free_ent = 256;
    state->stackp = state->stack;
    if (state->use_reset_code)
        state->free_ent++;
    state->bits = 9;
    state->section_end_code = (1<<state->bits) - 1;
    state->oldcode = -1;
    for (code = 255; code >= 0; code--) {
        state->prefix[code] = 0;
        state->suffix[code] = code;
    }
    next_code(self);

    return (ARCHIVE_OK);
}

/*
 * Return a block of data from the decompression buffer.  Decompress more
 * as necessary.
 */
static ssize_t
compress_filter_read(struct archive_read_filter *self, const void **pblock)
{
    struct private_data *state;
    unsigned char *p, *start, *end;
    int ret;

    state = (struct private_data *)self->data;
    if (state->end_of_stream) {
        *pblock = NULL;
        return (0);
    }
    p = start = (unsigned char *)state->out_block;
    end = start + state->out_block_size;

    while (p < end && !state->end_of_stream) {
        if (state->stackp > state->stack) {
            *p++ = *--state->stackp;
        } else {
            ret = next_code(self);
            if (ret == -1)
                state->end_of_stream = ret;
            else if (ret != ARCHIVE_OK)
                return (ret);
        }
    }

    *pblock = start;
    return (p - start);
}

/*
 * Clean up the reader.
 */
static int
compress_bidder_free(struct archive_read_filter_bidder *self)
{
    self->data = NULL;
    return (ARCHIVE_OK);
}

/*
 * Close and release the filter.
 */
static int
compress_filter_close(struct archive_read_filter *self)
{
    struct private_data *state = (struct private_data *)self->data;

    free(state->out_block);
    free(state);
    return (ARCHIVE_OK);
}

/*
 * Process the next code and fill the stack with the expansion
 * of the code.  Returns ARCHIVE_FATAL if there is a fatal I/O or
 * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
 */
static int
next_code(struct archive_read_filter *self)
{
    struct private_data *state = (struct private_data *)self->data;
    int code, newcode;

    static int debug_buff[1024];
    static unsigned debug_index;

    code = newcode = getbits(self, state->bits);
    if (code < 0)
        return (code);

    debug_buff[debug_index++] = code;
    if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0]))
        debug_index = 0;

    /* If it's a reset code, reset the dictionary. */
    if ((code == 256) && state->use_reset_code) {
        /*
         * The original 'compress' implementation blocked its
         * I/O in a manner that resulted in junk bytes being
         * inserted after every reset.  The next section skips
         * this junk.  (Yes, the number of *bytes* to skip is
         * a function of the current *bit* length.)
         */
        int skip_bytes =  state->bits -
            (state->bytes_in_section % state->bits);
        skip_bytes %= state->bits;
        state->bits_avail = 0; /* Discard rest of this byte. */
        while (skip_bytes-- > 0) {
            code = getbits(self, 8);
            if (code < 0)
                return (code);
        }
        /* Now, actually do the reset. */
        state->bytes_in_section = 0;
        state->bits = 9;
        state->section_end_code = (1 << state->bits) - 1;
        state->free_ent = 257;
        state->oldcode = -1;
        return (next_code(self));
    }

    if (code > state->free_ent) {
        /* An invalid code is a fatal error. */
        archive_set_error(&(self->archive->archive), -1,
            "Invalid compressed data");
        return (ARCHIVE_FATAL);
    }

    /* Special case for KwKwK string. */
    if (code >= state->free_ent) {
        *state->stackp++ = state->finbyte;
        code = state->oldcode;
    }

    /* Generate output characters in reverse order. */
    while (code >= 256) {
        *state->stackp++ = state->suffix[code];
        code = state->prefix[code];
    }
    *state->stackp++ = state->finbyte = code;

    /* Generate the new entry. */
    code = state->free_ent;
    if (code < state->maxcode && state->oldcode >= 0) {
        state->prefix[code] = state->oldcode;
        state->suffix[code] = state->finbyte;
        ++state->free_ent;
    }
    if (state->free_ent > state->section_end_code) {
        state->bits++;
        state->bytes_in_section = 0;
        if (state->bits == state->maxcode_bits)
            state->section_end_code = state->maxcode;
        else
            state->section_end_code = (1 << state->bits) - 1;
    }

    /* Remember previous code. */
    state->oldcode = newcode;
    return (ARCHIVE_OK);
}

/*
 * Return next 'n' bits from stream.
 *
 * -1 indicates end of available data.
 */
static int
getbits(struct archive_read_filter *self, int n)
{
    struct private_data *state = (struct private_data *)self->data;
    int code;
    ssize_t ret;
    static const int mask[] = {
        0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
        0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
    };

    while (state->bits_avail < n) {
        if (state->avail_in <= 0) {
            state->next_in
                = __archive_read_filter_ahead(self->upstream,
                1, &ret);
            if (ret == 0)
                return (-1);
            if (ret < 0 || state->next_in == NULL)
                return (ARCHIVE_FATAL);
            state->avail_in = ret;
            __archive_read_filter_consume(self->upstream, ret);
        }
        state->bit_buffer |= *state->next_in++ << state->bits_avail;
        state->avail_in--;
        state->bits_avail += 8;
        state->bytes_in_section++;
    }

    code = state->bit_buffer;
    state->bit_buffer >>= n;
    state->bits_avail -= n;

    return (code & mask[n]);
}

--- NEW FILE: archive_private.h ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.32 2008/12/06 06:23:37 kientzle Exp $
 */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef ARCHIVE_PRIVATE_H_INCLUDED
#define ARCHIVE_PRIVATE_H_INCLUDED

#include "archive.h"
#include "archive_string.h"

#if defined(__GNUC__) && (__GNUC__ > 2 || \
              (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
#define __LA_DEAD   __attribute__((__noreturn__))
#else
#define __LA_DEAD
#endif

#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU)
#define ARCHIVE_READ_MAGIC  (0xdeb0c5U)
#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)

#define ARCHIVE_STATE_ANY   0xFFFFU
#define ARCHIVE_STATE_NEW   1U
#define ARCHIVE_STATE_HEADER    2U
#define ARCHIVE_STATE_DATA  4U
#define ARCHIVE_STATE_DATA_END  8U
#define ARCHIVE_STATE_EOF   0x10U
#define ARCHIVE_STATE_CLOSED    0x20U
#define ARCHIVE_STATE_FATAL 0x8000U

struct archive_vtable {
    int (*archive_close)(struct archive *);
    int (*archive_finish)(struct archive *);
    int (*archive_write_header)(struct archive *,
        struct archive_entry *);
    int (*archive_write_finish_entry)(struct archive *);
    ssize_t (*archive_write_data)(struct archive *,
        const void *, size_t);
    ssize_t (*archive_write_data_block)(struct archive *,
        const void *, size_t, off_t);
};

struct archive {
    /*
     * The magic/state values are used to sanity-check the
     * client's usage.  If an API function is called at a
     * ridiculous time, or the client passes us an invalid
     * pointer, these values allow me to catch that.
     */
    unsigned int    magic;
    unsigned int    state;

    /*
     * Some public API functions depend on the "real" type of the
     * archive object.
     */
    struct archive_vtable *vtable;

    int       archive_format;
    const char   *archive_format_name;

    int   compression_code; /* Currently active compression. */
    const char *compression_name;

    /* Position in UNCOMPRESSED data stream. */
    int64_t       file_position;
    /* Position in COMPRESSED data stream. */
    int64_t       raw_position;
    /* Number of file entries processed. */
    int       file_count;

    int       archive_error_number;
    const char   *error;
    struct archive_string   error_string;
};

/* Check magic value and state; exit if it isn't valid. */
void    __archive_check_magic(struct archive *, unsigned int magic,
        unsigned int state, const char *func);

void    __archive_errx(int retvalue, const char *msg) __LA_DEAD;

int __archive_parse_options(const char *p, const char *fn,
        int keysize, char *key, int valsize, char *val);

#define err_combine(a,b)    ((a) < (b) ? (a) : (b))

#endif

--- NEW FILE: archive_entry.c ---
/*-
 * 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,
[...2078 lines suppressed...]


#ifdef TEST
#include <stdio.h>
int
main(int argc, char **argv)
{
    struct archive_entry *entry = archive_entry_new();
    unsigned long set, clear;
    const wchar_t *remainder;

    remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
    archive_entry_fflags(entry, &set, &clear);

    wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);

    wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
    return (0);
}
#endif

--- NEW FILE: libarchive.3 ---
.\" 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/lib/libarchive/libarchive.3,v 1.11 2007/01/09 08:05:56 kientzle Exp $
.\"
.Dd August 19, 2006
.Dt LIBARCHIVE 3
.Os
.Sh NAME
.Nm libarchive
.Nd functions for reading and writing streaming archives
.Sh LIBRARY
.Lb libarchive
.Sh OVERVIEW
The
.Nm
library provides a flexible interface for reading and writing
streaming archive files such as tar and cpio.
The library is inherently stream-oriented; readers serially iterate through
the archive, writers serially add things to the archive.
In particular, note that there is no built-in support for
random access nor for in-place modification.
.Pp
When reading an archive, the library automatically detects the
format and the compression.
The library currently has read support for:
.Bl -bullet -compact
.It
old-style tar archives,
.It
most variants of the POSIX
.Dq ustar
format,
.It
the POSIX
.Dq pax interchange
format,
.It
GNU-format tar archives,
.It
most common cpio archive formats,
.It
ISO9660 CD images (with or without RockRidge extensions),
.It
Zip archives.
.El
The library automatically detects archives compressed with
.Xr gzip 1 ,
.Xr bzip2 1 ,
or
.Xr compress 1
and decompresses them transparently.
.Pp
When writing an archive, you can specify the compression
to be used and the format to use.
The library can write
.Bl -bullet -compact
.It
POSIX-standard
.Dq ustar
archives,
.It
POSIX
.Dq pax interchange format
archives,
.It
POSIX octet-oriented cpio archives,
.It
two different variants of shar archives.
.El
Pax interchange format is an extension of the tar archive format that
eliminates essentially all of the limitations of historic tar formats
in a standard fashion that is supported
by POSIX-compliant
.Xr pax 1
implementations on many systems as well as several newer implementations of
.Xr tar 1 .
Note that the default write format will suppress the pax extended
attributes for most entries; explicitly requesting pax format will
enable those attributes for all entries.
.Pp
The read and write APIs are accessed through the
.Fn archive_read_XXX
functions and the
.Fn archive_write_XXX
functions, respectively, and either can be used independently
of the other.
.Pp
The rest of this manual page provides an overview of the library
operation.
More detailed information can be found in the individual manual
pages for each API or utility function.
.Sh READING AN ARCHIVE
To read an archive, you must first obtain an initialized
.Tn struct archive
object from
.Fn archive_read_new .
You can then modify this object for the desired operations with the
various
.Fn archive_read_set_XXX
and
.Fn archive_read_support_XXX
functions.
In particular, you will need to invoke appropriate
.Fn archive_read_support_XXX
functions to enable the corresponding compression and format
support.
Note that these latter functions perform two distinct operations:
they cause the corresponding support code to be linked into your
program, and they enable the corresponding auto-detect code.
Unless you have specific constraints, you will generally want
to invoke
.Fn archive_read_support_compression_all
and
.Fn archive_read_support_format_all
to enable auto-detect for all formats and compression types
currently supported by the library.
.Pp
Once you have prepared the
.Tn struct archive
object, you call
.Fn archive_read_open
to actually open the archive and prepare it for reading.
There are several variants of this function;
the most basic expects you to provide pointers to several
functions that can provide blocks of bytes from the archive.
There are convenience forms that allow you to
specify a filename, file descriptor,
.Ft "FILE *"
object, or a block of memory from which to read the archive data.
Note that the core library makes no assumptions about the
size of the blocks read;
callback functions are free to read whatever block size is
most appropriate for the medium.
.Pp
Each archive entry consists of a header followed by a certain
amount of data.
You can obtain the next header with
.Fn archive_read_next_header ,
which returns a pointer to an
.Tn struct archive_entry
structure with information about the current archive element.
If the entry is a regular file, then the header will be followed
by the file data.
You can use
.Fn archive_read_data
(which works much like the
.Xr read 2
system call)
to read this data from the archive.
You may prefer to use the higher-level
.Fn archive_read_data_skip ,
which reads and discards the data for this entry,
.Fn archive_read_data_to_buffer ,
which reads the data into an in-memory buffer,
.Fn archive_read_data_to_file ,
which copies the data to the provided file descriptor, or
.Fn archive_read_extract ,
which recreates the specified entry on disk and copies data
from the archive.
In particular, note that
.Fn archive_read_extract
uses the
.Tn struct archive_entry
structure that you provide it, which may differ from the
entry just read from the archive.
In particular, many applications will want to override the
pathname, file permissions, or ownership.
.Pp
Once you have finished reading data from the archive, you
should call
.Fn archive_read_close
to close the archive, then call
.Fn archive_read_finish
to release all resources, including all memory allocated by the library.
.Pp
The
.Xr archive_read 3
manual page provides more detailed calling information for this API.
.Sh WRITING AN ARCHIVE
You use a similar process to write an archive.
The
.Fn archive_write_new
function creates an archive object useful for writing,
the various
.Fn archive_write_set_XXX
functions are used to set parameters for writing the archive, and
.Fn archive_write_open
completes the setup and opens the archive for writing.
.Pp
Individual archive entries are written in a three-step
process:
You first initialize a
.Tn struct archive_entry
structure with information about the new entry.
At a minimum, you should set the pathname of the
entry and provide a
.Va struct stat
with a valid
.Va st_mode
field, which specifies the type of object and
.Va st_size
field, which specifies the size of the data portion of the object.
The
.Fn archive_write_header
function actually writes the header data to the archive.
You can then use
.Fn archive_write_data
to write the actual data.
.Pp
After all entries have been written, use the
.Fn archive_write_finish
function to release all resources.
.Pp
The
.Xr archive_write 3
manual page provides more detailed calling information for this API.
.Sh DESCRIPTION
Detailed descriptions of each function are provided by the
corresponding manual pages.
.Pp
All of the functions utilize an opaque
.Tn struct archive
datatype that provides access to the archive contents.
.Pp
The
.Tn struct archive_entry
structure contains a complete description of a single archive
entry.
It uses an opaque interface that is fully documented in
.Xr archive_entry 3 .
.Pp
Users familiar with historic formats should be aware that the newer
variants have eliminated most restrictions on the length of textual fields.
Clients should not assume that filenames, link names, user names, or
group names are limited in length.
In particular, pax interchange format can easily accommodate pathnames
in arbitrary character sets that exceed
.Va PATH_MAX .
.Sh RETURN VALUES
Most functions return zero on success, non-zero on error.
The return value indicates the general severity of the error, ranging
from
.Cm ARCHIVE_WARN ,
which indicates a minor problem that should probably be reported
to the user, to
.Cm ARCHIVE_FATAL ,
which indicates a serious problem that will prevent any further
operations on this archive.
On error, the
.Fn archive_errno
function can be used to retrieve a numeric error code (see
.Xr errno 2 ) .
The
.Fn archive_error_string
returns a textual error message suitable for display.
.Pp
.Fn archive_read_new
and
.Fn archive_write_new
return pointers to an allocated and initialized
.Tn struct archive
object.
.Pp
.Fn archive_read_data
and
.Fn archive_write_data
return a count of the number of bytes actually read or written.
A value of zero indicates the end of the data for this entry.
A negative value indicates an error, in which case the
.Fn archive_errno
and
.Fn archive_error_string
functions can be used to obtain more information.
.Sh ENVIRONMENT
There are character set conversions within the
.Xr archive_entry 3
functions that are impacted by the currently-selected locale.
.Sh SEE ALSO
.Xr tar 1 ,
.Xr archive_entry 3 ,
.Xr archive_read 3 ,
.Xr archive_util 3 ,
.Xr archive_write 3 ,
.Xr tar 5
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .
.Sh BUGS
Some archive formats support information that is not supported by
.Tn struct archive_entry .
Such information cannot be fully archived or restored using this library.
This includes, for example, comments, character sets,
or the arbitrary key/value pairs that can appear in
pax interchange format archives.
.Pp
Conversely, of course, not all of the information that can be
stored in an
.Tn struct archive_entry
is supported by all formats.
For example, cpio formats do not support nanosecond timestamps;
old tar formats do not support large device numbers.

--- NEW FILE: archive_entry.h ---
/*-
 * 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.
 *
 * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.31 2008/12/06 06:18:46 kientzle Exp $
 */

#ifndef ARCHIVE_ENTRY_H_INCLUDED
#define ARCHIVE_ENTRY_H_INCLUDED

/*
 * Note: archive_entry.h is for use outside of libarchive; the
 * configuration headers (config.h, archive_platform.h, etc.) are
 * purely internal.  Do NOT use HAVE_XXX configuration macros to
 * control the behavior of this header!  If you must conditionalize,
 * use predefined compiler and/or platform macros.
 */

#include <sys/types.h>
#include <stddef.h>  /* for wchar_t */
#include <time.h>

#if defined(_WIN32) && !defined(__CYGWIN__)
#include <windows.h>
#endif

/* Get appropriate definitions of standard POSIX-style types. */
/* These should match the types used in 'struct stat' */
#if defined(_WIN32) && !defined(__CYGWIN__)
#define __LA_INT64_T    __int64
#define __LA_UID_T  short
#define __LA_GID_T  short
#define __LA_DEV_T  unsigned int
#define __LA_MODE_T unsigned short
#else
#include <unistd.h>
#define __LA_INT64_T    int64_t
#define __LA_UID_T  uid_t
#define __LA_GID_T  gid_t
#define __LA_DEV_T  dev_t
#define __LA_MODE_T mode_t
#endif

/*
 * XXX Is this defined for all Windows compilers?  If so, in what
 * header?  It would be nice to remove the __LA_INO_T indirection and
 * just use plain ino_t everywhere.  Likewise for the other types just
 * above.
 */
#define __LA_INO_T  ino_t


/*
 * On Windows, define LIBARCHIVE_STATIC if you're building or using a
 * .lib.  The default here assumes you're building a DLL.  Only
 * libarchive source should ever define __LIBARCHIVE_BUILD.
 */
#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
# ifdef __LIBARCHIVE_BUILD
#  ifdef __GNUC__
#   define __LA_DECL    __attribute__((dllexport)) extern
#  else
#   define __LA_DECL    __declspec(dllexport)
#  endif
# else
#  ifdef __GNUC__
#   define __LA_DECL    __attribute__((dllimport)) extern
#  else
#   define __LA_DECL    __declspec(dllimport)
#  endif
# endif
#else
/* Static libraries on all platforms and shared libraries on non-Windows. */
# define __LA_DECL
#endif

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Description of an archive entry.
 *
 * You can think of this as "struct stat" with some text fields added in.
 *
 * TODO: Add "comment", "charset", and possibly other entries that are
 * supported by "pax interchange" format.  However, GNU, ustar, cpio,
 * and other variants don't support these features, so they're not an
 * excruciatingly high priority right now.
 *
 * TODO: "pax interchange" format allows essentially arbitrary
 * key/value attributes to be attached to any entry.  Supporting
 * such extensions may make this library useful for special
 * applications (e.g., a package manager could attach special
 * package-management attributes to each entry).
 */
struct archive_entry;

/*
 * File-type constants.  These are returned from archive_entry_filetype()
 * and passed to archive_entry_set_filetype().
 *
 * These values match S_XXX defines on every platform I've checked,
 * including Windows, AIX, Linux, Solaris, and BSD.  They're
 * (re)defined here because platforms generally don't define the ones
 * they don't support.  For example, Windows doesn't define S_IFLNK or
 * S_IFBLK.  Instead of having a mass of conditional logic and system
 * checks to define any S_XXX values that aren't supported locally,
 * I've just defined a new set of such constants so that
 * libarchive-based applications can manipulate and identify archive
 * entries properly even if the hosting platform can't store them on
 * disk.
 *
 * These values are also used directly within some portable formats,
 * such as cpio.  If you find a platform that varies from these, the
 * correct solution is to leave these alone and translate from these
 * portable values to platform-native values when entries are read from
 * or written to disk.
 */
#define AE_IFMT     0170000
#define AE_IFREG    0100000
#define AE_IFLNK    0120000
#define AE_IFSOCK   0140000
#define AE_IFCHR    0020000
#define AE_IFBLK    0060000
#define AE_IFDIR    0040000
#define AE_IFIFO    0010000

/*
 * Basic object manipulation
 */

__LA_DECL struct archive_entry  *archive_entry_clear(struct archive_entry *);
/* The 'clone' function does a deep copy; all of the strings are copied too. */
__LA_DECL struct archive_entry  *archive_entry_clone(struct archive_entry *);
__LA_DECL void           archive_entry_free(struct archive_entry *);
__LA_DECL struct archive_entry  *archive_entry_new(void);

/*
 * Retrieve fields from an archive_entry.
 *
 * There are a number of implicit conversions among these fields.  For
 * example, if a regular string field is set and you read the _w wide
 * character field, the entry will implicitly convert narrow-to-wide
 * using the current locale.  Similarly, dev values are automatically
 * updated when you write devmajor or devminor and vice versa.
 *
 * In addition, fields can be "set" or "unset."  Unset string fields
 * return NULL, non-string fields have _is_set() functions to test
 * whether they've been set.  You can "unset" a string field by
 * assigning NULL; non-string fields have _unset() functions to
 * unset them.
 *
 * Note: There is one ambiguity in the above; string fields will
 * also return NULL when implicit character set conversions fail.
 * This is usually what you want.
 */
__LA_DECL time_t     archive_entry_atime(struct archive_entry *);
__LA_DECL long       archive_entry_atime_nsec(struct archive_entry *);
__LA_DECL int        archive_entry_atime_is_set(struct archive_entry *);
__LA_DECL time_t     archive_entry_birthtime(struct archive_entry *);
__LA_DECL long       archive_entry_birthtime_nsec(struct archive_entry *);
__LA_DECL int        archive_entry_birthtime_is_set(struct archive_entry *);
__LA_DECL time_t     archive_entry_ctime(struct archive_entry *);
__LA_DECL long       archive_entry_ctime_nsec(struct archive_entry *);
__LA_DECL int        archive_entry_ctime_is_set(struct archive_entry *);
__LA_DECL dev_t      archive_entry_dev(struct archive_entry *);
__LA_DECL dev_t      archive_entry_devmajor(struct archive_entry *);
__LA_DECL dev_t      archive_entry_devminor(struct archive_entry *);
__LA_DECL __LA_MODE_T    archive_entry_filetype(struct archive_entry *);
__LA_DECL void       archive_entry_fflags(struct archive_entry *,
                unsigned long * /* set */,
                unsigned long * /* clear */);
__LA_DECL const char    *archive_entry_fflags_text(struct archive_entry *);
__LA_DECL __LA_GID_T     archive_entry_gid(struct archive_entry *);
__LA_DECL const char    *archive_entry_gname(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *);
__LA_DECL const char    *archive_entry_hardlink(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *);
__LA_DECL __LA_INO_T     archive_entry_ino(struct archive_entry *);
__LA_DECL __LA_INT64_T   archive_entry_ino64(struct archive_entry *);
__LA_DECL __LA_MODE_T    archive_entry_mode(struct archive_entry *);
__LA_DECL time_t     archive_entry_mtime(struct archive_entry *);
__LA_DECL long       archive_entry_mtime_nsec(struct archive_entry *);
__LA_DECL int        archive_entry_mtime_is_set(struct archive_entry *);
__LA_DECL unsigned int   archive_entry_nlink(struct archive_entry *);
__LA_DECL const char    *archive_entry_pathname(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *);
__LA_DECL dev_t      archive_entry_rdev(struct archive_entry *);
__LA_DECL dev_t      archive_entry_rdevmajor(struct archive_entry *);
__LA_DECL dev_t      archive_entry_rdevminor(struct archive_entry *);
__LA_DECL const char    *archive_entry_sourcepath(struct archive_entry *);
__LA_DECL __LA_INT64_T   archive_entry_size(struct archive_entry *);
__LA_DECL int        archive_entry_size_is_set(struct archive_entry *);
__LA_DECL const char    *archive_entry_strmode(struct archive_entry *);
__LA_DECL const char    *archive_entry_symlink(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
__LA_DECL __LA_UID_T     archive_entry_uid(struct archive_entry *);
__LA_DECL const char    *archive_entry_uname(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *);

/*
 * Set fields in an archive_entry.
 *
 * Note that string 'set' functions do not copy the string, only the pointer.
 * In contrast, 'copy' functions do copy the object pointed to.
 *
 * Note: As of libarchive 2.4, 'set' functions do copy the string and
 * are therefore exact synonyms for the 'copy' versions.  The 'copy'
 * names will be retired in libarchive 3.0.
 */

__LA_DECL void  archive_entry_set_atime(struct archive_entry *, time_t, long);
__LA_DECL void  archive_entry_unset_atime(struct archive_entry *);
#if defined(_WIN32) && !defined(__CYGWIN__)
__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *,
                                       BY_HANDLE_FILE_INFORMATION *);
#endif
__LA_DECL void  archive_entry_set_birthtime(struct archive_entry *, time_t, long);
__LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);
__LA_DECL void  archive_entry_set_ctime(struct archive_entry *, time_t, long);
__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);
__LA_DECL void  archive_entry_set_dev(struct archive_entry *, dev_t);
__LA_DECL void  archive_entry_set_devmajor(struct archive_entry *, dev_t);
__LA_DECL void  archive_entry_set_devminor(struct archive_entry *, dev_t);
__LA_DECL void  archive_entry_set_filetype(struct archive_entry *, unsigned int);
__LA_DECL void  archive_entry_set_fflags(struct archive_entry *,
        unsigned long /* set */, unsigned long /* clear */);
/* Returns pointer to start of first invalid token, or NULL if none. */
/* Note that all recognized tokens are processed, regardless. */
__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
        const char *);
__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
        const wchar_t *);
__LA_DECL void  archive_entry_set_gid(struct archive_entry *, __LA_GID_T);
__LA_DECL void  archive_entry_set_gname(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_gname(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
__LA_DECL int   archive_entry_update_gname_utf8(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_set_hardlink(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_hardlink(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
#if ARCHIVE_VERSION_NUMBER >= 3000000
/* Starting with libarchive 3.0, this will be synonym for ino64. */
__LA_DECL void  archive_entry_set_ino(struct archive_entry *, int64_t);
#else
__LA_DECL void  archive_entry_set_ino(struct archive_entry *, unsigned long);
#endif
__LA_DECL void  archive_entry_set_ino64(struct archive_entry *, __LA_INT64_T);
__LA_DECL void  archive_entry_set_link(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_link(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
__LA_DECL int   archive_entry_update_link_utf8(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
__LA_DECL void  archive_entry_set_mtime(struct archive_entry *, time_t, long);
__LA_DECL void  archive_entry_unset_mtime(struct archive_entry *);
__LA_DECL void  archive_entry_set_nlink(struct archive_entry *, unsigned int);
__LA_DECL void  archive_entry_set_pathname(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_pathname(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
__LA_DECL int   archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
__LA_DECL void  archive_entry_set_rdev(struct archive_entry *, dev_t);
__LA_DECL void  archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
__LA_DECL void  archive_entry_set_rdevminor(struct archive_entry *, dev_t);
__LA_DECL void  archive_entry_set_size(struct archive_entry *, __LA_INT64_T);
__LA_DECL void  archive_entry_unset_size(struct archive_entry *);
__LA_DECL void  archive_entry_copy_sourcepath(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_set_symlink(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_symlink(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
__LA_DECL void  archive_entry_set_uid(struct archive_entry *, __LA_UID_T);
__LA_DECL void  archive_entry_set_uname(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_uname(struct archive_entry *, const char *);
__LA_DECL void  archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
__LA_DECL int   archive_entry_update_uname_utf8(struct archive_entry *, const char *);
/*
 * Routines to bulk copy fields to/from a platform-native "struct
 * stat."  Libarchive used to just store a struct stat inside of each
 * archive_entry object, but this created issues when trying to
 * manipulate archives on systems different than the ones they were
 * created on.
 *
 * TODO: On Linux, provide both stat32 and stat64 versions of these functions.
 */
__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
__LA_DECL void  archive_entry_copy_stat(struct archive_entry *, const struct stat *);


/*
 * ACL routines.  This used to simply store and return text-format ACL
 * strings, but that proved insufficient for a number of reasons:
 *   = clients need control over uname/uid and gname/gid mappings
 *   = there are many different ACL text formats
 *   = would like to be able to read/convert archives containing ACLs
 *     on platforms that lack ACL libraries
 *
 *  This last point, in particular, forces me to implement a reasonably
 *  complete set of ACL support routines.
 *
 *  TODO: Extend this to support NFSv4/NTFS permissions.  That should
 *  allow full ACL support on Mac OS, in particular, which uses
 *  POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions.
 */

/*
 * Permission bits mimic POSIX.1e.  Note that I've not followed POSIX.1e's
 * "permset"/"perm" abstract type nonsense.  A permset is just a simple
 * bitmap, following long-standing Unix tradition.
 */
#define ARCHIVE_ENTRY_ACL_EXECUTE   1
#define ARCHIVE_ENTRY_ACL_WRITE     2
#define ARCHIVE_ENTRY_ACL_READ      4

/* We need to be able to specify either or both of these. */
#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS   256
#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT  512

/* Tag values mimic POSIX.1e */
#define ARCHIVE_ENTRY_ACL_USER      10001   /* Specified user. */
#define ARCHIVE_ENTRY_ACL_USER_OBJ  10002   /* User who owns the file. */
#define ARCHIVE_ENTRY_ACL_GROUP     10003   /* Specified group. */
#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004   /* Group who owns the file. */
#define ARCHIVE_ENTRY_ACL_MASK      10005   /* Modify group access. */
#define ARCHIVE_ENTRY_ACL_OTHER     10006   /* Public. */

/*
 * Set the ACL by clearing it and adding entries one at a time.
 * Unlike the POSIX.1e ACL routines, you must specify the type
 * (access/default) for each entry.  Internally, the ACL data is just
 * a soup of entries.  API calls here allow you to retrieve just the
 * entries of interest.  This design (which goes against the spirit of
 * POSIX.1e) is useful for handling archive formats that combine
 * default and access information in a single ACL list.
 */
__LA_DECL void   archive_entry_acl_clear(struct archive_entry *);
__LA_DECL void   archive_entry_acl_add_entry(struct archive_entry *,
        int /* type */, int /* permset */, int /* tag */,
        int /* qual */, const char * /* name */);
__LA_DECL void   archive_entry_acl_add_entry_w(struct archive_entry *,
        int /* type */, int /* permset */, int /* tag */,
        int /* qual */, const wchar_t * /* name */);

/*
 * To retrieve the ACL, first "reset", then repeatedly ask for the
 * "next" entry.  The want_type parameter allows you to request only
 * access entries or only default entries.
 */
__LA_DECL int    archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
__LA_DECL int    archive_entry_acl_next(struct archive_entry *, int /* want_type */,
        int * /* type */, int * /* permset */, int * /* tag */,
        int * /* qual */, const char ** /* name */);
__LA_DECL int    archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
        int * /* type */, int * /* permset */, int * /* tag */,
        int * /* qual */, const wchar_t ** /* name */);

/*
 * Construct a text-format ACL.  The flags argument is a bitmask that
 * can include any of the following:
 *
 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries.
 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries.
 * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
 *    each ACL entry.  (As used by 'star'.)
 * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
 *    default ACL entry.
 */
#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID    1024
#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT    2048
__LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
            int /* flags */);

/* Return a count of entries matching 'want_type' */
__LA_DECL int    archive_entry_acl_count(struct archive_entry *, int /* want_type */);

/*
 * Private ACL parser.  This is private because it handles some
 * very weird formats that clients should not be messing with.
 * Clients should only deal with their platform-native formats.
 * Because of the need to support many formats cleanly, new arguments
 * are likely to get added on a regular basis.  Clients who try to use
 * this interface are likely to be surprised when it changes.
 *
 * You were warned!
 *
 * TODO: Move this declaration out of the public header and into
 * a private header.  Warnings above are silly.
 */
__LA_DECL int        __archive_entry_acl_parse_w(struct archive_entry *,
            const wchar_t *, int /* type */);

/*
 * extended attributes
 */

__LA_DECL void   archive_entry_xattr_clear(struct archive_entry *);
__LA_DECL void   archive_entry_xattr_add_entry(struct archive_entry *,
        const char * /* name */, const void * /* value */,
        size_t /* size */);

/*
 * To retrieve the xattr list, first "reset", then repeatedly ask for the
 * "next" entry.
 */

__LA_DECL int   archive_entry_xattr_count(struct archive_entry *);
__LA_DECL int   archive_entry_xattr_reset(struct archive_entry *);
__LA_DECL int   archive_entry_xattr_next(struct archive_entry *,
        const char ** /* name */, const void ** /* value */, size_t *);

/*
 * Utility to match up hardlinks.
 *
 * The 'struct archive_entry_linkresolver' is a cache of archive entries
 * for files with multiple links.  Here's how to use it:
 *   1. Create a lookup object with archive_entry_linkresolver_new()
 *   2. Tell it the archive format you're using.
 *   3. Hand each archive_entry to archive_entry_linkify().
 *      That function will return 0, 1, or 2 entries that should
 *      be written.
 *   4. Call archive_entry_linkify(resolver, NULL) until
 *      no more entries are returned.
 *   5. Call archive_entry_link_resolver_free(resolver) to free resources.
 *
 * The entries returned have their hardlink and size fields updated
 * appropriately.  If an entry is passed in that does not refer to
 * a file with multiple links, it is returned unchanged.  The intention
 * is that you should be able to simply filter all entries through
 * this machine.
 *
 * To make things more efficient, be sure that each entry has a valid
 * nlinks value.  The hardlink cache uses this to track when all links
 * have been found.  If the nlinks value is zero, it will keep every
 * name in the cache indefinitely, which can use a lot of memory.
 *
 * Note that archive_entry_size() is reset to zero if the file
 * body should not be written to the archive.  Pay attention!
 */
struct archive_entry_linkresolver;

/*
 * There are three different strategies for marking hardlinks.
 * The descriptions below name them after the best-known
 * formats that rely on each strategy:
 *
 * "Old cpio" is the simplest, it always returns any entry unmodified.
 *    As far as I know, only cpio formats use this.  Old cpio archives
 *    store every link with the full body; the onus is on the dearchiver
 *    to detect and properly link the files as they are restored.
 * "tar" is also pretty simple; it caches a copy the first time it sees
 *    any link.  Subsequent appearances are modified to be hardlink
 *    references to the first one without any body.  Used by all tar
 *    formats, although the newest tar formats permit the "old cpio" strategy
 *    as well.  This strategy is very simple for the dearchiver,
 *    and reasonably straightforward for the archiver.
 * "new cpio" is trickier.  It stores the body only with the last
 *    occurrence.  The complication is that we might not
 *    see every link to a particular file in a single session, so
 *    there's no easy way to know when we've seen the last occurrence.
 *    The solution here is to queue one link until we see the next.
 *    At the end of the session, you can enumerate any remaining
 *    entries by calling archive_entry_linkify(NULL) and store those
 *    bodies.  If you have a file with three links l1, l2, and l3,
 *    you'll get the following behavior if you see all three links:
 *           linkify(l1) => NULL   (the resolver stores l1 internally)
 *           linkify(l2) => l1     (resolver stores l2, you write l1)
 *           linkify(l3) => l2, l3 (all links seen, you can write both).
 *    If you only see l1 and l2, you'll get this behavior:
 *           linkify(l1) => NULL
 *           linkify(l2) => l1
 *           linkify(NULL) => l2   (at end, you retrieve remaining links)
 *    As the name suggests, this strategy is used by newer cpio variants.
 *    It's noticably more complex for the archiver, slightly more complex
 *    for the dearchiver than the tar strategy, but makes it straightforward
 *    to restore a file using any link by simply continuing to scan until
 *    you see a link that is stored with a body.  In contrast, the tar
 *    strategy requires you to rescan the archive from the beginning to
 *    correctly extract an arbitrary link.
 */

__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
__LA_DECL void archive_entry_linkresolver_set_strategy(
    struct archive_entry_linkresolver *, int /* format_code */);
__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
    struct archive_entry **, struct archive_entry **);

#ifdef __cplusplus
}
#endif

/* This is meaningless outside of this header. */
#undef __LA_DECL

#endif /* !ARCHIVE_ENTRY_H_INCLUDED */

--- NEW FILE: archive_write_set_format_zip.c ---
/*-
 * Copyright (c) 2008 Anselm Strauss
 * Copyright (c) 2009 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Development supported by Google Summer of Code 2008.
 */

/*
 * The current implementation is very limited:
 *
 *   - No encryption support.
 *   - No ZIP64 support.
 *   - No support for splitting and spanning.
 *   - Only supports regular file and folder entries.
 *
 * Note that generally data in ZIP files is little-endian encoded,
 * with some exceptions.
 *
 * TODO: Since Libarchive is generally 64bit oriented, but this implementation
 * does not yet support sizes exceeding 32bit, it is highly fragile for
 * big archives. This should change when ZIP64 is finally implemented, otherwise
 * some serious checking has to be done.
 *
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD$");

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_ZLIB_H
#include <cm_zlib.h>
#endif

#include "archive.h"
#include "archive_endian.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_write_private.h"

#ifndef HAVE_ZLIB_H
#include "archive_crc32.h"
#endif

#define ZIP_SIGNATURE_LOCAL_FILE_HEADER 0x04034b50
#define ZIP_SIGNATURE_DATA_DESCRIPTOR 0x08074b50
#define ZIP_SIGNATURE_FILE_HEADER 0x02014b50
#define ZIP_SIGNATURE_CENTRAL_DIRECTORY_END 0x06054b50
#define ZIP_SIGNATURE_EXTRA_TIMESTAMP 0x5455
#define ZIP_SIGNATURE_EXTRA_UNIX 0x7855
#define ZIP_VERSION_EXTRACT 0x0014 /* ZIP version 2.0 is needed. */
#define ZIP_VERSION_BY 0x0314 /* Made by UNIX, using ZIP version 2.0. */
#define ZIP_FLAGS 0x08 /* Flagging bit 3 (count from 0) for using data descriptor. */

enum compression {
    COMPRESSION_STORE = 0
#ifdef HAVE_ZLIB_H
    ,
    COMPRESSION_DEFLATE = 8
#endif
};

static ssize_t archive_write_zip_data(struct archive_write *, const void *buff, size_t s);
static int archive_write_zip_finish(struct archive_write *);
static int archive_write_zip_destroy(struct archive_write *);
static int archive_write_zip_finish_entry(struct archive_write *);
static int archive_write_zip_header(struct archive_write *, struct archive_entry *);
static unsigned int dos_time(const time_t);
static size_t path_length(struct archive_entry *);
static int write_path(struct archive_entry *, struct archive_write *);

struct zip_local_file_header {
    char signature[4];
    char version[2];
    char flags[2];
    char compression[2];
    char timedate[4];
    char crc32[4];
    char compressed_size[4];
    char uncompressed_size[4];
    char filename_length[2];
    char extra_length[2];
};

struct zip_file_header {
    char signature[4];
    char version_by[2];
    char version_extract[2];
    char flags[2];
    char compression[2];
    char timedate[4];
    char crc32[4];
    char compressed_size[4];
    char uncompressed_size[4];
    char filename_length[2];
    char extra_length[2];
    char comment_length[2];
    char disk_number[2];
    char attributes_internal[2];
    char attributes_external[4];
    char offset[4];
};

struct zip_data_descriptor {
    char signature[4]; /* Not mandatory, but recommended by specification. */
    char crc32[4];
    char compressed_size[4];
    char uncompressed_size[4];
};

struct zip_extra_data_local {
    char time_id[2];
    char time_size[2];
    char time_flag[1];
    char mtime[4];
    char atime[4];
    char ctime[4];
    char unix_id[2];
    char unix_size[2];
    char unix_uid[2];
    char unix_gid[2];
};

struct zip_extra_data_central {
    char time_id[2];
    char time_size[2];
    char time_flag[1];
    char mtime[4];
    char unix_id[2];
    char unix_size[2];
};

struct zip_file_header_link {
    struct zip_file_header_link *next;
    struct archive_entry *entry;
    off_t offset;
    unsigned long crc32;
    off_t compressed_size;
    enum compression compression;
};

struct zip {
    struct zip_data_descriptor data_descriptor;
    struct zip_file_header_link *central_directory;
    struct zip_file_header_link *central_directory_end;
    int64_t offset;
    int64_t written_bytes;
    int64_t remaining_data_bytes;
    enum compression compression;

#ifdef HAVE_ZLIB_H
    z_stream stream;
    size_t len_buf;
    unsigned char *buf;
#endif
};

struct zip_central_directory_end {
    char signature[4];
    char disk[2];
    char start_disk[2];
    char entries_disk[2];
    char entries[2];
    char size[4];
    char offset[4];
    char comment_length[2];
};

static int
archive_write_zip_options(struct archive_write *a, const char *key,
    const char *value)
{
    struct zip *zip = a->format_data;

    if (strcmp(key, "compression") == 0) {
        if (strcmp(value, "deflate") == 0) {
#ifdef HAVE_ZLIB_H
            zip->compression = COMPRESSION_DEFLATE;
#else
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "deflate compression not supported");
            return ARCHIVE_WARN;
#endif
        } else if (strcmp(value, "store") == 0)
            zip->compression = COMPRESSION_STORE;
        else
            return (ARCHIVE_WARN);
        return (ARCHIVE_OK);
    }
    return (ARCHIVE_WARN);
}

int
archive_write_set_format_zip(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct zip *zip;

    /* If another format was already registered, unregister it. */
    if (a->format_destroy != NULL)
        (a->format_destroy)(a);

    zip = (struct zip *) malloc(sizeof(*zip));
    if (zip == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data");
        return (ARCHIVE_FATAL);
    }
    zip->central_directory = NULL;
    zip->central_directory_end = NULL;
    zip->offset = 0;
    zip->written_bytes = 0;
    zip->remaining_data_bytes = 0;

#ifdef HAVE_ZLIB_H
    zip->compression = COMPRESSION_DEFLATE;
    zip->len_buf = 65536;
    zip->buf = malloc(zip->len_buf);
    if (zip->buf == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate compression buffer");
        return (ARCHIVE_FATAL);
    }
#else
    zip->compression = COMPRESSION_STORE;
#endif

    a->format_data = zip;

    a->pad_uncompressed = 0; /* Actually not needed for now, since no compression support yet. */
    a->format_name = "zip";
    a->format_options = archive_write_zip_options;
    a->format_write_header = archive_write_zip_header;
    a->format_write_data = archive_write_zip_data;
    a->format_finish_entry = archive_write_zip_finish_entry;
    a->format_finish = archive_write_zip_finish;
    a->format_destroy = archive_write_zip_destroy;
    a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
    a->archive.archive_format_name = "ZIP";

    archive_le32enc(&zip->data_descriptor.signature,
        ZIP_SIGNATURE_DATA_DESCRIPTOR);

    return (ARCHIVE_OK);
}

static int
archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
{
    struct zip *zip;
    struct zip_local_file_header h;
    struct zip_extra_data_local e;
    struct zip_data_descriptor *d;
    struct zip_file_header_link *l;
    int ret;
    int64_t size;
    mode_t type;

    /* Entries other than a regular file or a folder are skipped. */
    type = archive_entry_filetype(entry);
    if ((type != AE_IFREG) & (type != AE_IFDIR)) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filetype not supported");
        return ARCHIVE_FAILED;
    };

    /* Directory entries should have a size of 0. */
    if (type == AE_IFDIR)
        archive_entry_set_size(entry, 0);

    zip = a->format_data;
    d = &zip->data_descriptor;
    size = archive_entry_size(entry);
    zip->remaining_data_bytes = size;

    /* Append archive entry to the central directory data. */
    l = (struct zip_file_header_link *) malloc(sizeof(*l));
    if (l == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data");
        return (ARCHIVE_FATAL);
    }
    l->entry = archive_entry_clone(entry);
    /* Initialize the CRC variable and potentially the local crc32(). */
    l->crc32 = crc32(0, NULL, 0);
    l->compression = zip->compression;
    l->compressed_size = 0;
    l->next = NULL;
    if (zip->central_directory == NULL) {
        zip->central_directory = l;
    } else {
        zip->central_directory_end->next = l;
    }
    zip->central_directory_end = l;

    /* Store the offset of this header for later use in central directory. */
    l->offset = zip->written_bytes;

    memset(&h, 0, sizeof(h));
    archive_le32enc(&h.signature, ZIP_SIGNATURE_LOCAL_FILE_HEADER);
    archive_le16enc(&h.version, ZIP_VERSION_EXTRACT);
    archive_le16enc(&h.flags, ZIP_FLAGS);
    archive_le16enc(&h.compression, zip->compression);
    archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(entry)));
    archive_le16enc(&h.filename_length, path_length(entry));

    switch (zip->compression) {
    case COMPRESSION_STORE:
        /* Setting compressed and uncompressed sizes even when specification says
         * to set to zero when using data descriptors. Otherwise the end of the
         * data for an entry is rather difficult to find. */
        archive_le32enc(&h.compressed_size, size);
        archive_le32enc(&h.uncompressed_size, size);
        break;
#ifdef HAVE_ZLIB_H
    case COMPRESSION_DEFLATE:
        archive_le32enc(&h.uncompressed_size, size);

        zip->stream.zalloc = Z_NULL;
        zip->stream.zfree = Z_NULL;
        zip->stream.opaque = Z_NULL;
        zip->stream.next_out = zip->buf;
        zip->stream.avail_out = zip->len_buf;
        if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
            -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
            archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor");
            return (ARCHIVE_FATAL);
        }
        break;
#endif
    }

    /* Formatting extra data. */
    archive_le16enc(&h.extra_length, sizeof(e));
    archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP);
    archive_le16enc(&e.time_size, sizeof(e.time_flag) +
        sizeof(e.mtime) + sizeof(e.atime) + sizeof(e.ctime));
    e.time_flag[0] = 0x07;
    archive_le32enc(&e.mtime, archive_entry_mtime(entry));
    archive_le32enc(&e.atime, archive_entry_atime(entry));
    archive_le32enc(&e.ctime, archive_entry_ctime(entry));
        
    archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX);
    archive_le16enc(&e.unix_size, sizeof(e.unix_uid) + sizeof(e.unix_gid));
    archive_le16enc(&e.unix_uid, archive_entry_uid(entry));
    archive_le16enc(&e.unix_gid, archive_entry_gid(entry));

    archive_le32enc(&d->uncompressed_size, size);

    ret = (a->compressor.write)(a, &h, sizeof(h));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    zip->written_bytes += sizeof(h);

    ret = write_path(entry, a);
    if (ret <= ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    zip->written_bytes += ret;

    ret = (a->compressor.write)(a, &e, sizeof(e));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    zip->written_bytes += sizeof(e);

    return (ARCHIVE_OK);
}

static ssize_t
archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
{
    int ret;
    struct zip *zip = a->format_data;
    struct zip_file_header_link *l = zip->central_directory_end;

    if (s > zip->remaining_data_bytes)
        s = zip->remaining_data_bytes;

    if (s == 0) return 0;

    switch (zip->compression) {
    case COMPRESSION_STORE:
        ret = (a->compressor.write)(a, buff, s);
        if (ret != ARCHIVE_OK) return (ret);
        zip->written_bytes += s;
        zip->remaining_data_bytes -= s;
        l->compressed_size += s;
        l->crc32 = crc32(l->crc32, buff, s);
        return (s);
#if HAVE_ZLIB_H
    case COMPRESSION_DEFLATE:
        zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
        zip->stream.avail_in = s;
        do {
            ret = deflate(&zip->stream, Z_NO_FLUSH);
            if (ret == Z_STREAM_ERROR)
                return (ARCHIVE_FATAL);
            if (zip->stream.avail_out == 0) {
                ret = (a->compressor.write)(a, zip->buf, zip->len_buf);
                if (ret != ARCHIVE_OK)
                    return (ret);
                l->compressed_size += zip->len_buf;
                zip->written_bytes += zip->len_buf;
                zip->stream.next_out = zip->buf;
                zip->stream.avail_out = zip->len_buf;
            }
        } while (zip->stream.avail_in != 0);
        zip->remaining_data_bytes -= s;
        /* If we have it, use zlib's fast crc32() */
        l->crc32 = crc32(l->crc32, buff, s);
        return (s);
#endif

    default:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Invalid ZIP compression type");
        return ARCHIVE_FATAL;
    }
}

static int
archive_write_zip_finish_entry(struct archive_write *a)
{
    /* Write the data descripter after file data has been written. */
    int ret;
    struct zip *zip = a->format_data;
    struct zip_data_descriptor *d = &zip->data_descriptor;
    struct zip_file_header_link *l = zip->central_directory_end;
#if HAVE_ZLIB_H
    size_t reminder;
#endif

    switch(zip->compression) {
    case COMPRESSION_STORE:
        break;
#if HAVE_ZLIB_H
    case COMPRESSION_DEFLATE:
        for (;;) {
            ret = deflate(&zip->stream, Z_FINISH);
            if (ret == Z_STREAM_ERROR)
                return (ARCHIVE_FATAL);
            reminder = zip->len_buf - zip->stream.avail_out;
            ret = (a->compressor.write)(a, zip->buf, reminder);
            if (ret != ARCHIVE_OK)
                return (ret);
            l->compressed_size += reminder;
            zip->written_bytes += reminder;
            zip->stream.next_out = zip->buf;
            if (zip->stream.avail_out != 0)
                break;
            zip->stream.avail_out = zip->len_buf;
        }
        deflateEnd(&zip->stream);
        break;
#endif
    }

    archive_le32enc(&d->crc32, l->crc32);
    archive_le32enc(&d->compressed_size, l->compressed_size);
    ret = (a->compressor.write)(a, d, sizeof(*d));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    zip->written_bytes += sizeof(*d);
    return (ARCHIVE_OK);
}

static int
archive_write_zip_finish(struct archive_write *a)
{
    struct zip *zip;
    struct zip_file_header_link *l;
    struct zip_file_header h;
    struct zip_central_directory_end end;
    struct zip_extra_data_central e;
    off_t offset_start, offset_end;
    int entries;
    int ret;

    zip = a->format_data;
    l = zip->central_directory;

    /*
     * Formatting central directory file header fields that are fixed for all entries.
     * Fields not used (and therefor 0) are:
     *
     *   - comment_length
     *   - disk_number
     *   - attributes_internal
     */
    memset(&h, 0, sizeof(h));
    archive_le32enc(&h.signature, ZIP_SIGNATURE_FILE_HEADER);
    archive_le16enc(&h.version_by, ZIP_VERSION_BY);
    archive_le16enc(&h.version_extract, ZIP_VERSION_EXTRACT);
    archive_le16enc(&h.flags, ZIP_FLAGS);

    entries = 0;
    offset_start = zip->written_bytes;

    /* Formatting individual header fields per entry and
     * writing each entry. */
    while (l != NULL) {
        archive_le16enc(&h.compression, l->compression);
        archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(l->entry)));
        archive_le32enc(&h.crc32, l->crc32);
        archive_le32enc(&h.compressed_size, l->compressed_size);
        archive_le32enc(&h.uncompressed_size, archive_entry_size(l->entry));
        archive_le16enc(&h.filename_length, path_length(l->entry));
        archive_le16enc(&h.extra_length, sizeof(e));
        archive_le16enc(&h.attributes_external[2], archive_entry_mode(l->entry));
        archive_le32enc(&h.offset, l->offset);

        /* Formatting extra data. */
        archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP);
        archive_le16enc(&e.time_size, sizeof(e.mtime) + sizeof(e.time_flag));
        e.time_flag[0] = 0x07;
        archive_le32enc(&e.mtime, archive_entry_mtime(l->entry));
        archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX);
        archive_le16enc(&e.unix_size, 0x0000);

        ret = (a->compressor.write)(a, &h, sizeof(h));
        if (ret != ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        zip->written_bytes += sizeof(h);

        ret = write_path(l->entry, a);
        if (ret <= ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        zip->written_bytes += ret;

        ret = (a->compressor.write)(a, &e, sizeof(e));
        if (ret != ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        zip->written_bytes += sizeof(e);

        l = l->next;
        entries++;
    }
    offset_end = zip->written_bytes;

    /* Formatting end of central directory. */
    memset(&end, 0, sizeof(end));
    archive_le32enc(&end.signature, ZIP_SIGNATURE_CENTRAL_DIRECTORY_END);
    archive_le16enc(&end.entries_disk, entries);
    archive_le16enc(&end.entries, entries);
    archive_le32enc(&end.size, offset_end - offset_start);
    archive_le32enc(&end.offset, offset_start);

    /* Writing end of central directory. */
    ret = (a->compressor.write)(a, &end, sizeof(end));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    zip->written_bytes += sizeof(end);
    return (ARCHIVE_OK);
}

static int
archive_write_zip_destroy(struct archive_write *a)
{
    struct zip *zip;
    struct zip_file_header_link *l;

    zip = a->format_data;
    while (zip->central_directory != NULL) {
       l = zip->central_directory;
       zip->central_directory = l->next;
       archive_entry_free(l->entry);
       free(l);
    }
#ifdef HAVE_ZLIB_H
    free(zip->buf);
#endif
    free(zip);
    a->format_data = NULL;
    return (ARCHIVE_OK);
}

/* Convert into MSDOS-style date/time. */
static unsigned int
dos_time(const time_t unix_time)
{
    struct tm *t;
    unsigned int dt;

    /* This will not preserve time when creating/extracting the archive
     * on two systems with different time zones. */
    t = localtime(&unix_time);

    dt = 0;
    dt += ((t->tm_year - 80) & 0x7f) << 9;
    dt += ((t->tm_mon + 1) & 0x0f) << 5;
    dt += (t->tm_mday & 0x1f);
    dt <<= 16;
    dt += (t->tm_hour & 0x1f) << 11;
    dt += (t->tm_min & 0x3f) << 5;
    dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
    return dt;
}

static size_t
path_length(struct archive_entry *entry)
{
    mode_t type;
    const char *path;

    type = archive_entry_filetype(entry);
    path = archive_entry_pathname(entry);

    if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
        return strlen(path) + 1;
    } else {
        return strlen(path);
    }
}

static int
write_path(struct archive_entry *entry, struct archive_write *archive)
{
    int ret;
    const char *path;
    mode_t type;
    size_t written_bytes;

    path = archive_entry_pathname(entry);
    type = archive_entry_filetype(entry);
    written_bytes = 0;

    ret = (archive->compressor.write)(archive, path, strlen(path));
    if (ret != ARCHIVE_OK)
        return (ARCHIVE_FATAL);
    written_bytes += strlen(path);

    /* Folders are recognized by a traling slash. */
    if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
        ret = (archive->compressor.write)(archive, "/", 1);
        if (ret != ARCHIVE_OK)
            return (ARCHIVE_FATAL);
        written_bytes += 1;
    }

    return written_bytes;
}

--- NEW FILE: archive_read_open_file.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_file.c,v 1.20 2007/06/26 03:06:48 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#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 "archive.h"

struct read_FILE_data {
    FILE    *f;
    size_t   block_size;
    void    *buffer;
    char     can_skip;
};

static int  file_close(struct archive *, void *);
static ssize_t  file_read(struct archive *, void *, const void **buff);
#if ARCHIVE_API_VERSION < 2
static ssize_t  file_skip(struct archive *, void *, size_t request);
#else
static off_t    file_skip(struct archive *, void *, off_t request);
#endif

int
archive_read_open_FILE(struct archive *a, FILE *f)
{
    struct stat st;
    struct read_FILE_data *mine;
    size_t block_size = 128 * 1024;
    void *b;

    archive_clear_error(a);
    mine = (struct read_FILE_data *)malloc(sizeof(*mine));
    b = malloc(block_size);
    if (mine == NULL || b == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        free(mine);
        free(b);
        return (ARCHIVE_FATAL);
    }
    mine->block_size = block_size;
    mine->buffer = b;
    mine->f = f;
    /*
     * If we can't fstat() the file, it may just be that it's not
     * a file.  (FILE * objects can wrap many kinds of I/O
     * streams, some of which don't support fileno()).)
     */
    if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {
        archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
        /* Enable the seek optimization only for regular files. */
        mine->can_skip = 1;
    } else
        mine->can_skip = 0;

#if defined(__CYGWIN__)
    setmode(fileno(mine->f), O_BINARY);
#elif defined(_WIN32)
    _setmode(_fileno(mine->f), _O_BINARY);
#endif

    return (archive_read_open2(a, mine, NULL, file_read,
            file_skip, file_close));
}

static ssize_t
file_read(struct archive *a, void *client_data, const void **buff)
{
    struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
    ssize_t bytes_read;

    *buff = mine->buffer;
    bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
    if (bytes_read < 0) {
        archive_set_error(a, errno, "Error reading file");
    }
    return (bytes_read);
}

#if ARCHIVE_API_VERSION < 2
static ssize_t
file_skip(struct archive *a, void *client_data, size_t request)
#else
static off_t
file_skip(struct archive *a, void *client_data, off_t request)
#endif
{
    struct read_FILE_data *mine = (struct read_FILE_data *)client_data;

    (void)a; /* UNUSED */

    /*
     * If we can't skip, return 0 as the amount we did step and
     * the caller will work around by reading and discarding.
     */
    if (!mine->can_skip)
        return (0);
    if (request == 0)
        return (0);

#if HAVE_FSEEKO
    if (fseeko(mine->f, request, SEEK_CUR) != 0)
#else
    if (fseek(mine->f, request, SEEK_CUR) != 0)
#endif
    {
        mine->can_skip = 0;
        return (0);
    }
    return (request);
}

static int
file_close(struct archive *a, void *client_data)
{
    struct read_FILE_data *mine = (struct read_FILE_data *)client_data;

    (void)a; /* UNUSED */
    if (mine->buffer != NULL)
        free(mine->buffer);
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_write_set_compression_gzip.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.16 2008/02/21 03:21:50 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 <time.h>
#ifdef HAVE_ZLIB_H
#include <cm_zlib.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"

#ifndef HAVE_ZLIB_H
int
archive_write_set_compression_gzip(struct archive *a)
{
    archive_set_error(a, ARCHIVE_ERRNO_MISC,
        "gzip compression not supported on this platform");
    return (ARCHIVE_FATAL);
}
#else
/* Don't compile this if we don't have zlib. */

struct private_data {
    z_stream     stream;
    int64_t      total_in;
    unsigned char   *compressed;
    size_t       compressed_buffer_size;
    unsigned long    crc;
};

struct private_config {
    int      compression_level;
};


/*
 * Yuck.  zlib.h is not const-correct, so I need this one bit
 * of ugly hackery to convert a const * pointer to a non-const pointer.
 */
#define SET_NEXT_IN(st,src)                 \
    (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)

static int  archive_compressor_gzip_finish(struct archive_write *);
static int  archive_compressor_gzip_init(struct archive_write *);
static int  archive_compressor_gzip_options(struct archive_write *,
            const char *, const char *);
static int  archive_compressor_gzip_write(struct archive_write *,
            const void *, size_t);
static int  drive_compressor(struct archive_write *, struct private_data *,
            int finishing);


/*
 * Allocate, initialize and return a archive object.
 */
int
archive_write_set_compression_gzip(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    struct private_config *config;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
    config = malloc(sizeof(*config));
    if (config == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Out of memory");
        return (ARCHIVE_FATAL);
    }
    a->compressor.config = config;
    a->compressor.finish = &archive_compressor_gzip_finish;
    config->compression_level = Z_DEFAULT_COMPRESSION;
    a->compressor.init = &archive_compressor_gzip_init;
    a->compressor.options = &archive_compressor_gzip_options;
    a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
    a->archive.compression_name = "gzip";
    return (ARCHIVE_OK);
}

/*
 * Setup callback.
 */
static int
archive_compressor_gzip_init(struct archive_write *a)
{
    int ret;
    struct private_data *state;
    struct private_config *config;
    time_t t;

    config = (struct private_config *)a->compressor.config;

    if (a->client_opener != NULL) {
        ret = (a->client_opener)(&a->archive, a->client_data);
        if (ret != ARCHIVE_OK)
            return (ret);
    }

    /*
     * The next check is a temporary workaround until the gzip
     * code can be overhauled some.  The code should not require
     * that compressed_buffer_size == bytes_per_block.  Removing
     * this assumption will allow us to compress larger chunks at
     * a time, which should improve overall performance
     * marginally.  As a minor side-effect, such a cleanup would
     * allow us to support truly arbitrary block sizes.
     */
    if (a->bytes_per_block < 10) {
        archive_set_error(&a->archive, EINVAL,
            "GZip compressor requires a minimum 10 byte block size");
        return (ARCHIVE_FATAL);
    }

    state = (struct private_data *)malloc(sizeof(*state));
    if (state == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression");
        return (ARCHIVE_FATAL);
    }
    memset(state, 0, sizeof(*state));

    /*
     * See comment above.  We should set compressed_buffer_size to
     * max(bytes_per_block, 65536), but the code can't handle that yet.
     */
    state->compressed_buffer_size = a->bytes_per_block;
    state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
    state->crc = crc32(0L, NULL, 0);

    if (state->compressed == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression buffer");
        free(state);
        return (ARCHIVE_FATAL);
    }

    state->stream.next_out = state->compressed;
    state->stream.avail_out = state->compressed_buffer_size;

    /* Prime output buffer with a gzip header. */
    t = time(NULL);
    state->compressed[0] = 0x1f; /* GZip signature bytes */
    state->compressed[1] = 0x8b;
    state->compressed[2] = 0x08; /* "Deflate" compression */
    state->compressed[3] = 0; /* No options */
    state->compressed[4] = (t)&0xff;  /* Timestamp */
    state->compressed[5] = (t>>8)&0xff;
    state->compressed[6] = (t>>16)&0xff;
    state->compressed[7] = (t>>24)&0xff;
    state->compressed[8] = 0; /* No deflate options */
    state->compressed[9] = 3; /* OS=Unix */
    state->stream.next_out += 10;
    state->stream.avail_out -= 10;

    a->compressor.write = archive_compressor_gzip_write;

    /* Initialize compression library. */
    ret = deflateInit2(&(state->stream),
        config->compression_level,
        Z_DEFLATED,
        -15 /* < 0 to suppress zlib header */,
        8,
        Z_DEFAULT_STRATEGY);

    if (ret == Z_OK) {
        a->compressor.data = state;
        return (0);
    }

    /* Library setup failed: clean up. */
    archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error "
        "initializing compression library");
    free(state->compressed);
    free(state);

    /* Override the error message if we know what really went wrong. */
    switch (ret) {
    case Z_STREAM_ERROR:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing "
            "compression library: invalid setup parameter");
        break;
    case Z_MEM_ERROR:
        archive_set_error(&a->archive, ENOMEM, "Internal error initializing "
            "compression library");
        break;
    case Z_VERSION_ERROR:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing "
            "compression library: invalid library version");
        break;
    }

    return (ARCHIVE_FATAL);
}

/*
 * Set write options.
 */
static int
archive_compressor_gzip_options(struct archive_write *a, const char *key,
    const char *value)
{
    struct private_config *config;

    config = (struct private_config *)a->compressor.config;
    if (strcmp(key, "compression-level") == 0) {
        if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
            value[1] != '\0')
            return (ARCHIVE_WARN);
        config->compression_level = value[0] - '0';
        return (ARCHIVE_OK);
    }

    return (ARCHIVE_WARN);
}

/*
 * Write data to the compressed stream.
 */
static int
archive_compressor_gzip_write(struct archive_write *a, const void *buff,
    size_t length)
{
    struct private_data *state;
    int ret;

    state = (struct private_data *)a->compressor.data;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    /* Update statistics */
    state->crc = crc32(state->crc, (const Bytef *)buff, length);
    state->total_in += length;

    /* Compress input data to output buffer */
    SET_NEXT_IN(state, buff);
    state->stream.avail_in = length;
    if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
        return (ret);

    a->archive.file_position += length;
    return (ARCHIVE_OK);
}

/*
 * Finish the compression...
 */
static int
archive_compressor_gzip_finish(struct archive_write *a)
{
    ssize_t block_length, target_block_length, bytes_written;
    int ret;
    struct private_data *state;
    unsigned tocopy;
    unsigned char trailer[8];

    state = (struct private_data *)a->compressor.data;
    ret = 0;
    if (state != NULL) {
        if (a->client_writer == NULL) {
            archive_set_error(&a->archive,
                ARCHIVE_ERRNO_PROGRAMMER,
                "No write callback is registered?  "
                "This is probably an internal programming error.");
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }

        /* By default, always pad the uncompressed data. */
        if (a->pad_uncompressed) {
            tocopy = a->bytes_per_block -
                (state->total_in % a->bytes_per_block);
            while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
                SET_NEXT_IN(state, a->nulls);
                state->stream.avail_in = tocopy < a->null_length ?
                    tocopy : a->null_length;
                state->crc = crc32(state->crc, a->nulls,
                    state->stream.avail_in);
                state->total_in += state->stream.avail_in;
                tocopy -= state->stream.avail_in;
                ret = drive_compressor(a, state, 0);
                if (ret != ARCHIVE_OK)
                    goto cleanup;
            }
        }

        /* Finish compression cycle */
        if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
            goto cleanup;

        /* Build trailer: 4-byte CRC and 4-byte length. */
        trailer[0] = (state->crc)&0xff;
        trailer[1] = (state->crc >> 8)&0xff;
        trailer[2] = (state->crc >> 16)&0xff;
        trailer[3] = (state->crc >> 24)&0xff;
        trailer[4] = (state->total_in)&0xff;
        trailer[5] = (state->total_in >> 8)&0xff;
        trailer[6] = (state->total_in >> 16)&0xff;
        trailer[7] = (state->total_in >> 24)&0xff;

        /* Add trailer to current block. */
        tocopy = 8;
        if (tocopy > state->stream.avail_out)
            tocopy = state->stream.avail_out;
        memcpy(state->stream.next_out, trailer, tocopy);
        state->stream.next_out += tocopy;
        state->stream.avail_out -= tocopy;

        /* If it overflowed, flush and start a new block. */
        if (tocopy < 8) {
            bytes_written = (a->client_writer)(&a->archive, a->client_data,
                state->compressed, state->compressed_buffer_size);
            if (bytes_written <= 0) {
                ret = ARCHIVE_FATAL;
                goto cleanup;
            }
            a->archive.raw_position += bytes_written;
            state->stream.next_out = state->compressed;
            state->stream.avail_out = state->compressed_buffer_size;
            memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
            state->stream.next_out += 8-tocopy;
            state->stream.avail_out -= 8-tocopy;
        }

        /* Optionally, pad the final compressed block. */
        block_length = state->stream.next_out - state->compressed;

        /* Tricky calculation to determine size of last block. */
        target_block_length = block_length;
        if (a->bytes_in_last_block <= 0)
            /* Default or Zero: pad to full block */
            target_block_length = a->bytes_per_block;
        else
            /* Round length to next multiple of bytes_in_last_block. */
            target_block_length = a->bytes_in_last_block *
                ( (block_length + a->bytes_in_last_block - 1) /
                a->bytes_in_last_block);
        if (target_block_length > a->bytes_per_block)
            target_block_length = a->bytes_per_block;
        if (block_length < target_block_length) {
            memset(state->stream.next_out, 0,
                target_block_length - block_length);
            block_length = target_block_length;
        }

        /* Write the last block */
        bytes_written = (a->client_writer)(&a->archive, a->client_data,
            state->compressed, block_length);
        if (bytes_written <= 0) {
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }
        a->archive.raw_position += bytes_written;

        /* Cleanup: shut down compressor, release memory, etc. */
    cleanup:
        switch (deflateEnd(&(state->stream))) {
        case Z_OK:
            break;
        default:
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "Failed to clean up compressor");
            ret = ARCHIVE_FATAL;
        }
        free(state->compressed);
        free(state);
    }
    /* Clean up config area even if we never initialized. */
    free(a->compressor.config);
    a->compressor.config = NULL;
    return (ret);
}

/*
 * Utility function to push input data through compressor,
 * writing full output blocks as necessary.
 *
 * Note that this handles both the regular write case (finishing ==
 * false) and the end-of-archive case (finishing == true).
 */
static int
drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
{
    ssize_t bytes_written;
    int ret;

    for (;;) {
        if (state->stream.avail_out == 0) {
            bytes_written = (a->client_writer)(&a->archive,
                a->client_data, state->compressed,
                state->compressed_buffer_size);
            if (bytes_written <= 0) {
                /* TODO: Handle this write failure */
                return (ARCHIVE_FATAL);
            } else if ((size_t)bytes_written < state->compressed_buffer_size) {
                /* Short write: Move remaining to
                 * front of block and keep filling */
                memmove(state->compressed,
                    state->compressed + bytes_written,
                    state->compressed_buffer_size - bytes_written);
            }
            a->archive.raw_position += bytes_written;
            state->stream.next_out
                = state->compressed +
                state->compressed_buffer_size - bytes_written;
            state->stream.avail_out = bytes_written;
        }

        /* If there's nothing to do, we're done. */
        if (!finishing && state->stream.avail_in == 0)
            return (ARCHIVE_OK);

        ret = deflate(&(state->stream),
            finishing ? Z_FINISH : Z_NO_FLUSH );

        switch (ret) {
        case Z_OK:
            /* In non-finishing case, check if compressor
             * consumed everything */
            if (!finishing && state->stream.avail_in == 0)
                return (ARCHIVE_OK);
            /* In finishing case, this return always means
             * there's more work */
            break;
        case Z_STREAM_END:
            /* This return can only occur in finishing case. */
            return (ARCHIVE_OK);
        default:
            /* Any other return value indicates an error. */
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "GZip compression failed:"
                " deflate() call returned status %d",
                ret);
            return (ARCHIVE_FATAL);
        }
    }
}

#endif /* HAVE_ZLIB_H */

--- NEW FILE: archive_write_open_filename.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.20 2008/02/19 05:46:58 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#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 "archive.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif

struct write_file_data {
    int     fd;
    char        filename[1];
};

static int  file_close(struct archive *, void *);
static int  file_open(struct archive *, void *);
static ssize_t  file_write(struct archive *, void *, const void *buff, size_t);

int
archive_write_open_file(struct archive *a, const char *filename)
{
    return (archive_write_open_filename(a, filename));
}

int
archive_write_open_filename(struct archive *a, const char *filename)
{
    struct write_file_data *mine;

    if (filename == NULL || filename[0] == '\0')
        return (archive_write_open_fd(a, 1));

    mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename));
    if (mine == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        return (ARCHIVE_FATAL);
    }
    strcpy(mine->filename, filename);
    mine->fd = -1;
    return (archive_write_open(a, mine,
        file_open, file_write, file_close));
}

static int
file_open(struct archive *a, void *client_data)
{
    int flags;
    struct write_file_data *mine;
    struct stat st;

    mine = (struct write_file_data *)client_data;
    flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;

    /*
     * Open the file.
     */
    mine->fd = open(mine->filename, flags, 0666);
    if (mine->fd < 0) {
        archive_set_error(a, errno, "Failed to open '%s'",
            mine->filename);
        return (ARCHIVE_FATAL);
    }

    if (fstat(mine->fd, &st) != 0) {
               archive_set_error(a, errno, "Couldn't stat '%s'",
                   mine->filename);
               return (ARCHIVE_FATAL);
    }

    /*
     * Set up default last block handling.
     */
    if (archive_write_get_bytes_in_last_block(a) < 0) {
        if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
            S_ISFIFO(st.st_mode))
            /* Pad last block when writing to device or FIFO. */
            archive_write_set_bytes_in_last_block(a, 0);
        else
            /* Don't pad last block otherwise. */
            archive_write_set_bytes_in_last_block(a, 1);
    }

    /*
     * If the output file is a regular file, don't add it to
     * itself.  If it's a device file, it's okay to add the device
     * entry to the output archive.
     */
    if (S_ISREG(st.st_mode))
        archive_write_set_skip_file(a, st.st_dev, st.st_ino);

    return (ARCHIVE_OK);
}

static ssize_t
file_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
    struct write_file_data  *mine;
    ssize_t bytesWritten;

    mine = (struct write_file_data *)client_data;
    bytesWritten = write(mine->fd, buff, length);
    if (bytesWritten <= 0) {
        archive_set_error(a, errno, "Write error");
        return (-1);
    }
    return (bytesWritten);
}

static int
file_close(struct archive *a, void *client_data)
{
    struct write_file_data  *mine = (struct write_file_data *)client_data;

    (void)a; /* UNUSED */
    close(mine->fd);
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_write_set_compression_xz.c ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"

__FBSDID("$FreeBSD$");

#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 <time.h>
#ifdef HAVE_LZMA_H
#include <lzma.h>
#endif

#include "archive.h"
#include "archive_private.h"
#include "archive_write_private.h"

#ifndef HAVE_LZMA_H
int
archive_write_set_compression_xz(struct archive *a)
{
    archive_set_error(a, ARCHIVE_ERRNO_MISC,
        "xz compression not supported on this platform");
    return (ARCHIVE_FATAL);
}

int
archive_write_set_compression_lzma(struct archive *a)
{
    archive_set_error(a, ARCHIVE_ERRNO_MISC,
        "lzma compression not supported on this platform");
    return (ARCHIVE_FATAL);
}
#else
/* Don't compile this if we don't have liblzma. */

struct private_data {
    lzma_stream  stream;
    lzma_filter  lzmafilters[2];
    lzma_options_lzma lzma_opt;
    int64_t      total_in;
    unsigned char   *compressed;
    size_t       compressed_buffer_size;
};

struct private_config {
    int      compression_level;
};

static int  archive_compressor_xz_init(struct archive_write *);
static int  archive_compressor_xz_options(struct archive_write *,
            const char *, const char *);
static int  archive_compressor_xz_finish(struct archive_write *);
static int  archive_compressor_xz_write(struct archive_write *,
            const void *, size_t);
static int  drive_compressor(struct archive_write *, struct private_data *,
            int finishing);


/*
 * Allocate, initialize and return a archive object.
 */
int
archive_write_set_compression_xz(struct archive *_a)
{
    struct private_config *config;
    struct archive_write *a = (struct archive_write *)_a;
    __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
        ARCHIVE_STATE_NEW, "archive_write_set_compression_xz");
    config = calloc(1, sizeof(*config));
    if (config == NULL) {
        archive_set_error(&a->archive, ENOMEM, "Out of memory");
        return (ARCHIVE_FATAL);
    }
    a->compressor.config = config;
    a->compressor.finish = archive_compressor_xz_finish;
    config->compression_level = LZMA_PRESET_DEFAULT;
    a->compressor.init = &archive_compressor_xz_init;
    a->compressor.options = &archive_compressor_xz_options;
    a->archive.compression_code = ARCHIVE_COMPRESSION_XZ;
    a->archive.compression_name = "xz";
    return (ARCHIVE_OK);
}

/* LZMA is handled identically, we just need a different compression
 * code set.  (The liblzma setup looks at the code to determine
 * the one place that XZ and LZMA require different handling.) */
int
archive_write_set_compression_lzma(struct archive *_a)
{
    struct archive_write *a = (struct archive_write *)_a;
    int r = archive_write_set_compression_xz(_a);
    if (r != ARCHIVE_OK)
        return (r);
    a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA;
    a->archive.compression_name = "lzma";
    return (ARCHIVE_OK);
}

static int
archive_compressor_xz_init_stream(struct archive_write *a,
    struct private_data *state)
{
    int ret;

    state->stream = (lzma_stream)LZMA_STREAM_INIT;
    state->stream.next_out = state->compressed;
    state->stream.avail_out = state->compressed_buffer_size;
    if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ)
        ret = lzma_stream_encoder(&(state->stream),
            state->lzmafilters, LZMA_CHECK_CRC64);
    else
        ret = lzma_alone_encoder(&(state->stream), &state->lzma_opt);
    if (ret == LZMA_OK)
        return (ARCHIVE_OK);

    switch (ret) {
    case LZMA_MEM_ERROR:
        archive_set_error(&a->archive, ENOMEM,
            "Internal error initializing compression library: "
            "Cannot allocate memory");
        break;
    default:
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library: "
            "It's a bug in liblzma");
        break;
    }
    return (ARCHIVE_FATAL);
}

/*
 * Setup callback.
 */
static int
archive_compressor_xz_init(struct archive_write *a)
{
    int ret;
    struct private_data *state;
    struct private_config *config;

    if (a->client_opener != NULL) {
        ret = (a->client_opener)(&a->archive, a->client_data);
        if (ret != ARCHIVE_OK)
            return (ret);
    }

    state = (struct private_data *)malloc(sizeof(*state));
    if (state == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression");
        return (ARCHIVE_FATAL);
    }
    memset(state, 0, sizeof(*state));
    config = a->compressor.config;

    /*
     * See comment above.  We should set compressed_buffer_size to
     * max(bytes_per_block, 65536), but the code can't handle that yet.
     */
    state->compressed_buffer_size = a->bytes_per_block;
    state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
    if (state->compressed == NULL) {
        archive_set_error(&a->archive, ENOMEM,
            "Can't allocate data for compression buffer");
        free(state);
        return (ARCHIVE_FATAL);
    }
    a->compressor.write = archive_compressor_xz_write;

    /* Initialize compression library. */
    if (lzma_lzma_preset(&state->lzma_opt, config->compression_level)) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing compression library");
        free(state->compressed);
        free(state);
    }
    state->lzmafilters[0].id = LZMA_FILTER_LZMA2;
    state->lzmafilters[0].options = &state->lzma_opt;
    state->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
    ret = archive_compressor_xz_init_stream(a, state);
    if (ret == LZMA_OK) {
        a->compressor.data = state;
        return (0);
    }
    /* Library setup failed: clean up. */
    free(state->compressed);
    free(state);

    return (ARCHIVE_FATAL);
}

/*
 * Set write options.
 */
static int
archive_compressor_xz_options(struct archive_write *a, const char *key,
    const char *value)
{
    struct private_config *config;

    config = (struct private_config *)a->compressor.config;
    if (strcmp(key, "compression-level") == 0) {
        if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
            value[1] != '\0')
            return (ARCHIVE_WARN);
        config->compression_level = value[0] - '0';
        if (config->compression_level > 6)
            config->compression_level = 6;
        return (ARCHIVE_OK);
    }

    return (ARCHIVE_WARN);
}

/*
 * Write data to the compressed stream.
 */
static int
archive_compressor_xz_write(struct archive_write *a, const void *buff,
    size_t length)
{
    struct private_data *state;
    int ret;

    state = (struct private_data *)a->compressor.data;
    if (a->client_writer == NULL) {
        archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
            "No write callback is registered?  "
            "This is probably an internal programming error.");
        return (ARCHIVE_FATAL);
    }

    /* Update statistics */
    state->total_in += length;

    /* Compress input data to output buffer */
    state->stream.next_in = buff;
    state->stream.avail_in = length;
    if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
        return (ret);

    a->archive.file_position += length;
    return (ARCHIVE_OK);
}


/*
 * Finish the compression...
 */
static int
archive_compressor_xz_finish(struct archive_write *a)
{
    ssize_t block_length, target_block_length, bytes_written;
    int ret;
    struct private_data *state;
    unsigned tocopy;

    ret = ARCHIVE_OK;
    state = (struct private_data *)a->compressor.data;
    if (state != NULL) {
        if (a->client_writer == NULL) {
            archive_set_error(&a->archive,
                ARCHIVE_ERRNO_PROGRAMMER,
                "No write callback is registered?  "
                "This is probably an internal programming error.");
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }

        /* By default, always pad the uncompressed data. */
        if (a->pad_uncompressed) {
            tocopy = a->bytes_per_block -
                (state->total_in % a->bytes_per_block);
            while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
                state->stream.next_in = a->nulls;
                state->stream.avail_in = tocopy < a->null_length ?
                    tocopy : a->null_length;
                state->total_in += state->stream.avail_in;
                tocopy -= state->stream.avail_in;
                ret = drive_compressor(a, state, 0);
                if (ret != ARCHIVE_OK)
                    goto cleanup;
            }
        }

        /* Finish compression cycle */
        if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
            goto cleanup;

        /* Optionally, pad the final compressed block. */
        block_length = state->stream.next_out - state->compressed;

        /* Tricky calculation to determine size of last block. */
        target_block_length = block_length;
        if (a->bytes_in_last_block <= 0)
            /* Default or Zero: pad to full block */
            target_block_length = a->bytes_per_block;
        else
            /* Round length to next multiple of bytes_in_last_block. */
            target_block_length = a->bytes_in_last_block *
                ( (block_length + a->bytes_in_last_block - 1) /
                a->bytes_in_last_block);
        if (target_block_length > a->bytes_per_block)
            target_block_length = a->bytes_per_block;
        if (block_length < target_block_length) {
            memset(state->stream.next_out, 0,
                target_block_length - block_length);
            block_length = target_block_length;
        }

        /* Write the last block */
        bytes_written = (a->client_writer)(&a->archive, a->client_data,
            state->compressed, block_length);
        if (bytes_written <= 0) {
            ret = ARCHIVE_FATAL;
            goto cleanup;
        }
        a->archive.raw_position += bytes_written;

        /* Cleanup: shut down compressor, release memory, etc. */
    cleanup:
        lzma_end(&(state->stream));
        free(state->compressed);
        free(state);
    }
    free(a->compressor.config);
    a->compressor.config = NULL;
    return (ret);
}

/*
 * Utility function to push input data through compressor,
 * writing full output blocks as necessary.
 *
 * Note that this handles both the regular write case (finishing ==
 * false) and the end-of-archive case (finishing == true).
 */
static int
drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
{
    ssize_t bytes_written;
    int ret;

    for (;;) {
        if (state->stream.avail_out == 0) {
            bytes_written = (a->client_writer)(&a->archive,
                a->client_data, state->compressed,
                state->compressed_buffer_size);
            if (bytes_written <= 0) {
                /* TODO: Handle this write failure */
                return (ARCHIVE_FATAL);
            } else if ((size_t)bytes_written < state->compressed_buffer_size) {
                /* Short write: Move remaining to
                 * front of block and keep filling */
                memmove(state->compressed,
                    state->compressed + bytes_written,
                    state->compressed_buffer_size - bytes_written);
            }
            a->archive.raw_position += bytes_written;
            state->stream.next_out
                = state->compressed +
                state->compressed_buffer_size - bytes_written;
            state->stream.avail_out = bytes_written;
        }

        /* If there's nothing to do, we're done. */
        if (!finishing && state->stream.avail_in == 0)
            return (ARCHIVE_OK);

        ret = lzma_code(&(state->stream),
            finishing ? LZMA_FINISH : LZMA_RUN );

        switch (ret) {
        case LZMA_OK:
            /* In non-finishing case, check if compressor
             * consumed everything */
            if (!finishing && state->stream.avail_in == 0)
                return (ARCHIVE_OK);
            /* In finishing case, this return always means
             * there's more work */
            break;
        case LZMA_STREAM_END:
            /* This return can only occur in finishing case. */
            if (finishing)
                return (ARCHIVE_OK);
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "lzma compression data error");
            return (ARCHIVE_FATAL);
        case LZMA_MEMLIMIT_ERROR:
            archive_set_error(&a->archive, ENOMEM,
                "lzma compression error: "
                "%ju MiB would have been needed",
                (lzma_memusage(&(state->stream)) + 1024 * 1024 -1)
                / (1024 * 1024));
            return (ARCHIVE_FATAL);
        default:
            /* Any other return value indicates an error. */
            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                "lzma compression failed:"
                " lzma_code() call returned status %d",
                ret);
            return (ARCHIVE_FATAL);
        }
    }
}

#endif /* HAVE_LZMA_H */

--- NEW FILE: archive_read_support_format_all.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.10 2007/12/30 04:58:21 kientzle Exp $");

#include "archive.h"

int
archive_read_support_format_all(struct archive *a)
{
    archive_read_support_format_ar(a);
    archive_read_support_format_cpio(a);
    archive_read_support_format_empty(a);
    archive_read_support_format_iso9660(a);
    archive_read_support_format_mtree(a);
    archive_read_support_format_tar(a);
    archive_read_support_format_zip(a);
    return (ARCHIVE_OK);
}

--- NEW FILE: tar.5 ---
.\" Copyright (c) 2003-2009 Tim Kientzle
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: src/lib/libarchive/tar.5,v 1.18 2008/05/26 17:00:23 kientzle Exp $
.\"
.Dd April 19, 2009
.Dt tar 5
.Os
.Sh NAME
.Nm tar
.Nd format of tape archive files
.Sh DESCRIPTION
The
.Nm
archive format collects any number of files, directories, and other
file system objects (symbolic links, device nodes, etc.) into a single
stream of bytes.
The format was originally designed to be used with
tape drives that operate with fixed-size blocks, but is widely used as
a general packaging mechanism.
.Ss General Format
A
.Nm
archive consists of a series of 512-byte records.
Each file system object requires a header record which stores basic metadata
(pathname, owner, permissions, etc.) and zero or more records containing any
file data.
The end of the archive is indicated by two records consisting
entirely of zero bytes.
.Pp
For compatibility with tape drives that use fixed block sizes,
programs that read or write tar files always read or write a fixed
number of records with each I/O operation.
These
.Dq blocks
are always a multiple of the record size.
The most common block size\(emand the maximum supported by historic
implementations\(emis 10240 bytes or 20 records.
(Note: the terms
.Dq block
and
.Dq record
here are not entirely standard; this document follows the
convention established by John Gilmore in documenting
.Nm pdtar . )
.Ss Old-Style Archive Format
The original tar archive format has been extended many times to
include additional information that various implementors found
necessary.
This section describes the variant implemented by the tar command
included in
.At v7 ,
which seems to be the earliest widely-used version of the tar program.
.Pp
The header record for an old-style
.Nm
archive consists of the following:
.Bd -literal -offset indent
struct header_old_tar {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char linkflag[1];
    char linkname[100];
    char pad[255];
};
.Ed
All unused bytes in the header record are filled with nulls.
.Bl -tag -width indent
.It Va name
Pathname, stored as a null-terminated string.
Early tar implementations only stored regular files (including
hardlinks to those files).
One common early convention used a trailing "/" character to indicate
a directory name, allowing directory permissions and owner information
to be archived and restored.
.It Va mode
File mode, stored as an octal number in ASCII.
.It Va uid , Va gid
User id and group id of owner, as octal numbers in ASCII.
.It Va size
Size of file, as octal number in ASCII.
For regular files only, this indicates the amount of data
that follows the header.
In particular, this field was ignored by early tar implementations
when extracting hardlinks.
Modern writers should always store a zero length for hardlink entries.
.It Va mtime
Modification time of file, as an octal number in ASCII.
This indicates the number of seconds since the start of the epoch,
00:00:00 UTC January 1, 1970.
Note that negative values should be avoided
here, as they are handled inconsistently.
.It Va checksum
Header checksum, stored as an octal number in ASCII.
To compute the checksum, set the checksum field to all spaces,
then sum all bytes in the header using unsigned arithmetic.
This field should be stored as six octal digits followed by a null and a space
character.
Note that many early implementations of tar used signed arithmetic
for the checksum field, which can cause interoperability problems
when transferring archives between systems.
Modern robust readers compute the checksum both ways and accept the
header if either computation matches.
.It Va linkflag , Va linkname
In order to preserve hardlinks and conserve tape, a file
with multiple links is only written to the archive the first
time it is encountered.
The next time it is encountered, the
.Va linkflag
is set to an ASCII
.Sq 1
and the
.Va linkname
field holds the first name under which this file appears.
(Note that regular files have a null value in the
.Va linkflag
field.)
.El
.Pp
Early tar implementations varied in how they terminated these fields.
The tar command in
.At v7
used the following conventions (this is also documented in early BSD manpages):
the pathname must be null-terminated;
the mode, uid, and gid fields must end in a space and a null byte;
the size and mtime fields must end in a space;
the checksum is terminated by a null and a space.
Early implementations filled the numeric fields with leading spaces.
This seems to have been common practice until the
.St -p1003.1-88
standard was released.
For best portability, modern implementations should fill the numeric
fields with leading zeros.
.Ss Pre-POSIX Archives
An early draft of
.St -p1003.1-88
served as the basis for John Gilmore's
.Nm pdtar
program and many system implementations from the late 1980s
and early 1990s.
These archives generally follow the POSIX ustar
format described below with the following variations:
.Bl -bullet -compact -width indent
.It
The magic value is
.Dq ustar\ \&
(note the following space).
The version field contains a space character followed by a null.
.It
The numeric fields are generally filled with leading spaces
(not leading zeros as recommended in the final standard).
.It
The prefix field is often not used, limiting pathnames to
the 100 characters of old-style archives.
.El
.Ss POSIX ustar Archives
.St -p1003.1-88
defined a standard tar file format to be read and written
by compliant implementations of
.Xr tar 1 .
This format is often called the
.Dq ustar
format, after the magic value used
in the header.
(The name is an acronym for
.Dq Unix Standard TAR . )
It extends the historic format with new fields:
.Bd -literal -offset indent
struct header_posix_ustar {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char typeflag[1];
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char prefix[155];
    char pad[12];
};
.Ed
.Bl -tag -width indent
.It Va typeflag
Type of entry.
POSIX extended the earlier
.Va linkflag
field with several new type values:
.Bl -tag -width indent -compact
.It Dq 0
Regular file.
NUL should be treated as a synonym, for compatibility purposes.
.It Dq 1
Hard link.
.It Dq 2
Symbolic link.
.It Dq 3
Character device node.
.It Dq 4
Block device node.
.It Dq 5
Directory.
.It Dq 6
FIFO node.
.It Dq 7
Reserved.
.It Other
A POSIX-compliant implementation must treat any unrecognized typeflag value
as a regular file.
In particular, writers should ensure that all entries
have a valid filename so that they can be restored by readers that do not
support the corresponding extension.
Uppercase letters "A" through "Z" are reserved for custom extensions.
Note that sockets and whiteout entries are not archivable.
.El
It is worth noting that the
.Va size
field, in particular, has different meanings depending on the type.
For regular files, of course, it indicates the amount of data
following the header.
For directories, it may be used to indicate the total size of all
files in the directory, for use by operating systems that pre-allocate
directory space.
For all other types, it should be set to zero by writers and ignored
by readers.
.It Va magic
Contains the magic value
.Dq ustar
followed by a NUL byte to indicate that this is a POSIX standard archive.
Full compliance requires the uname and gname fields be properly set.
.It Va version
Version.
This should be
.Dq 00
(two copies of the ASCII digit zero) for POSIX standard archives.
.It Va uname , Va gname
User and group names, as null-terminated ASCII strings.
These should be used in preference to the uid/gid values
when they are set and the corresponding names exist on
the system.
.It Va devmajor , Va devminor
Major and minor numbers for character device or block device entry.
.It Va prefix
First part of pathname.
If the pathname is too long to fit in the 100 bytes provided by the standard
format, it can be split at any
.Pa /
character with the first portion going here.
If the prefix field is not empty, the reader will prepend
the prefix value and a
.Pa /
character to the regular name field to obtain the full pathname.
.El
.Pp
Note that all unused bytes must be set to
.Dv NUL .
.Pp
Field termination is specified slightly differently by POSIX
than by previous implementations.
The
.Va magic ,
.Va uname ,
and
.Va gname
fields must have a trailing
.Dv NUL .
The
.Va pathname ,
.Va linkname ,
and
.Va prefix
fields must have a trailing
.Dv NUL
unless they fill the entire field.
(In particular, it is possible to store a 256-character pathname if it
happens to have a
.Pa /
as the 156th character.)
POSIX requires numeric fields to be zero-padded in the front, and allows
them to be terminated with either space or
.Dv NUL
characters.
.Pp
Currently, most tar implementations comply with the ustar
format, occasionally extending it by adding new fields to the
blank area at the end of the header record.
.Ss Pax Interchange Format
There are many attributes that cannot be portably stored in a
POSIX ustar archive.
.St -p1003.1-2001
defined a
.Dq pax interchange format
that uses two new types of entries to hold text-formatted
metadata that applies to following entries.
Note that a pax interchange format archive is a ustar archive in every
respect.
The new data is stored in ustar-compatible archive entries that use the
.Dq x
or
.Dq g
typeflag.
In particular, older implementations that do not fully support these
extensions will extract the metadata into regular files, where the
metadata can be examined as necessary.
.Pp
An entry in a pax interchange format archive consists of one or
two standard ustar entries, each with its own header and data.
The first optional entry stores the extended attributes
for the following entry.
This optional first entry has an "x" typeflag and a size field that
indicates the total size of the extended attributes.
The extended attributes themselves are stored as a series of text-format
lines encoded in the portable UTF-8 encoding.
Each line consists of a decimal number, a space, a key string, an equals
sign, a value string, and a new line.
The decimal number indicates the length of the entire line, including the
initial length field and the trailing newline.
An example of such a field is:
.Dl 25 ctime=1084839148.1212\en
Keys in all lowercase are standard keys.
Vendors can add their own keys by prefixing them with an all uppercase
vendor name and a period.
Note that, unlike the historic header, numeric values are stored using
decimal, not octal.
A description of some common keys follows:
.Bl -tag -width indent
.It Cm atime , Cm ctime , Cm mtime
File access, inode change, and modification times.
These fields can be negative or include a decimal point and a fractional value.
.It Cm uname , Cm uid , Cm gname , Cm gid
User name, group name, and numeric UID and GID values.
The user name and group name stored here are encoded in UTF8
and can thus include non-ASCII characters.
The UID and GID fields can be of arbitrary length.
.It Cm linkpath
The full path of the linked-to file.
Note that this is encoded in UTF8 and can thus include non-ASCII characters.
.It Cm path
The full pathname of the entry.
Note that this is encoded in UTF8 and can thus include non-ASCII characters.
.It Cm realtime.* , Cm security.*
These keys are reserved and may be used for future standardization.
.It Cm size
The size of the file.
Note that there is no length limit on this field, allowing conforming
archives to store files much larger than the historic 8GB limit.
.It Cm SCHILY.*
Vendor-specific attributes used by Joerg Schilling's
.Nm star
implementation.
.It Cm SCHILY.acl.access , Cm SCHILY.acl.default
Stores the access and default ACLs as textual strings in a format
that is an extension of the format specified by POSIX.1e draft 17.
In particular, each user or group access specification can include a fourth
colon-separated field with the numeric UID or GID.
This allows ACLs to be restored on systems that may not have complete
user or group information available (such as when NIS/YP or LDAP services
are temporarily unavailable).
.It Cm SCHILY.devminor , Cm SCHILY.devmajor
The full minor and major numbers for device nodes.
.It Cm SCHILY.fflags
The file flags.
.It Cm SCHILY.realsize
The full size of the file on disk.
XXX explain? XXX
.It Cm SCHILY.dev, Cm SCHILY.ino , Cm SCHILY.nlinks
The device number, inode number, and link count for the entry.
In particular, note that a pax interchange format archive using Joerg
Schilling's
.Cm SCHILY.*
extensions can store all of the data from
.Va struct stat .
.It Cm LIBARCHIVE.xattr. Ns Ar namespace Ns . Ns Ar key
Libarchive stores POSIX.1e-style extended attributes using
keys of this form.
The
.Ar key
value is URL-encoded:
All non-ASCII characters and the two special characters
.Dq =
and
.Dq %
are encoded as
.Dq %
followed by two uppercase hexadecimal digits.
The value of this key is the extended attribute value
encoded in base 64.
XXX Detail the base-64 format here XXX
.It Cm VENDOR.*
XXX document other vendor-specific extensions XXX
.El
.Pp
Any values stored in an extended attribute override the corresponding
values in the regular tar header.
Note that compliant readers should ignore the regular fields when they
are overridden.
This is important, as existing archivers are known to store non-compliant
values in the standard header fields in this situation.
There are no limits on length for any of these fields.
In particular, numeric fields can be arbitrarily large.
All text fields are encoded in UTF8.
Compliant writers should store only portable 7-bit ASCII characters in
the standard ustar header and use extended
attributes whenever a text value contains non-ASCII characters.
.Pp
In addition to the
.Cm x
entry described above, the pax interchange format
also supports a
.Cm g
entry.
The
.Cm g
entry is identical in format, but specifies attributes that serve as
defaults for all subsequent archive entries.
The
.Cm g
entry is not widely used.
.Pp
Besides the new
.Cm x
and
.Cm g
entries, the pax interchange format has a few other minor variations
from the earlier ustar format.
The most troubling one is that hardlinks are permitted to have
data following them.
This allows readers to restore any hardlink to a file without
having to rewind the archive to find an earlier entry.
However, it creates complications for robust readers, as it is no longer
clear whether or not they should ignore the size field for hardlink entries.
.Ss GNU Tar Archives
The GNU tar program started with a pre-POSIX format similar to that
described earlier and has extended it using several different mechanisms:
It added new fields to the empty space in the header (some of which was later
used by POSIX for conflicting purposes);
it allowed the header to be continued over multiple records;
and it defined new entries that modify following entries
(similar in principle to the
.Cm x
entry described above, but each GNU special entry is single-purpose,
unlike the general-purpose
.Cm x
entry).
As a result, GNU tar archives are not POSIX compatible, although
more lenient POSIX-compliant readers can successfully extract most
GNU tar archives.
.Bd -literal -offset indent
struct header_gnu_tar {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char typeflag[1];
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char atime[12];
    char ctime[12];
    char offset[12];
    char longnames[4];
    char unused[1];
    struct {
        char offset[12];
        char numbytes[12];
    } sparse[4];
    char isextended[1];
    char realsize[12];
    char pad[17];
};
.Ed
.Bl -tag -width indent
.It Va typeflag
GNU tar uses the following special entry types, in addition to
those defined by POSIX:
.Bl -tag -width indent
.It "7"
GNU tar treats type "7" records identically to type "0" records,
except on one obscure RTOS where they are used to indicate the
pre-allocation of a contiguous file on disk.
.It "D"
This indicates a directory entry.
Unlike the POSIX-standard "5"
typeflag, the header is followed by data records listing the names
of files in this directory.
Each name is preceded by an ASCII "Y"
if the file is stored in this archive or "N" if the file is not
stored in this archive.
Each name is terminated with a null, and
an extra null marks the end of the name list.
The purpose of this
entry is to support incremental backups; a program restoring from
such an archive may wish to delete files on disk that did not exist
in the directory when the archive was made.
.Pp
Note that the "D" typeflag specifically violates POSIX, which requires
that unrecognized typeflags be restored as normal files.
In this case, restoring the "D" entry as a file could interfere
with subsequent creation of the like-named directory.
.It "K"
The data for this entry is a long linkname for the following regular entry.
.It "L"
The data for this entry is a long pathname for the following regular entry.
.It "M"
This is a continuation of the last file on the previous volume.
GNU multi-volume archives guarantee that each volume begins with a valid
entry header.
To ensure this, a file may be split, with part stored at the end of one volume,
and part stored at the beginning of the next volume.
The "M" typeflag indicates that this entry continues an existing file.
Such entries can only occur as the first or second entry
in an archive (the latter only if the first entry is a volume label).
The
.Va size
field specifies the size of this entry.
The
.Va offset
field at bytes 369-380 specifies the offset where this file fragment
begins.
The
.Va realsize
field specifies the total size of the file (which must equal
.Va size
plus
.Va offset ) .
When extracting, GNU tar checks that the header file name is the one it is
expecting, that the header offset is in the correct sequence, and that
the sum of offset and size is equal to realsize.
.It "N"
Type "N" records are no longer generated by GNU tar.
They contained a
list of files to be renamed or symlinked after extraction; this was
originally used to support long names.
The contents of this record
are a text description of the operations to be done, in the form
.Dq Rename %s to %s\en
or
.Dq Symlink %s to %s\en ;
in either case, both
filenames are escaped using K&R C syntax.
Due to security concerns, "N" records are now generally ignored
when reading archives.
.It "S"
This is a
.Dq sparse
regular file.
Sparse files are stored as a series of fragments.
The header contains a list of fragment offset/length pairs.
If more than four such entries are required, the header is
extended as necessary with
.Dq extra
header extensions (an older format that is no longer used), or
.Dq sparse
extensions.
.It "V"
The
.Va name
field should be interpreted as a tape/volume header name.
This entry should generally be ignored on extraction.
.El
.It Va magic
The magic field holds the five characters
.Dq ustar
followed by a space.
Note that POSIX ustar archives have a trailing null.
.It Va version
The version field holds a space character followed by a null.
Note that POSIX ustar archives use two copies of the ASCII digit
.Dq 0 .
.It Va atime , Va ctime
The time the file was last accessed and the time of
last change of file information, stored in octal as with
.Va mtime .
.It Va longnames
This field is apparently no longer used.
.It Sparse Va offset / Va numbytes
Each such structure specifies a single fragment of a sparse
file.
The two fields store values as octal numbers.
The fragments are each padded to a multiple of 512 bytes
in the archive.
On extraction, the list of fragments is collected from the
header (including any extension headers), and the data
is then read and written to the file at appropriate offsets.
.It Va isextended
If this is set to non-zero, the header will be followed by additional
.Dq sparse header
records.
Each such record contains information about as many as 21 additional
sparse blocks as shown here:
.Bd -literal -offset indent
struct gnu_sparse_header {
    struct {
        char offset[12];
        char numbytes[12];
    } sparse[21];
    char    isextended[1];
    char    padding[7];
};
.Ed
.It Va realsize
A binary representation of the file's complete size, with a much larger range
than the POSIX file size.
In particular, with
.Cm M
type files, the current entry is only a portion of the file.
In that case, the POSIX size field will indicate the size of this
entry; the
.Va realsize
field will indicate the total size of the file.
.El
.Ss GNU tar pax archives
GNU tar 1.14 (XXX check this XXX) and later will write
pax interchange format archives when you specify the
.Fl -posix
flag.
This format uses custom keywords to store sparse file information.
There have been three iterations of this support, referred to
as
.Dq 0.0 ,
.Dq 0.1 ,
and
.Dq 1.0 .
.Bl -tag -width indent
.It Cm GNU.sparse.numblocks , Cm GNU.sparse.offset , Cm GNU.sparse.numbytes , Cm  GNU.sparse.size
The
.Dq 0.0
format used an initial
.Cm GNU.sparse.numblocks
attribute to indicate the number of blocks in the file, a pair of
.Cm GNU.sparse.offset
and
.Cm GNU.sparse.numbytes
to indicate the offset and size of each block,
and a single
.Cm GNU.sparse.size
to indicate the full size of the file.
This is not the same as the size in the tar header because the
latter value does not include the size of any holes.
This format required that the order of attributes be preserved and
relied on readers accepting multiple appearances of the same attribute
names, which is not officially permitted by the standards.
.It Cm GNU.sparse.map
The
.Dq 0.1
format used a single attribute that stored a comma-separated
list of decimal numbers.
Each pair of numbers indicated the offset and size, respectively,
of a block of data.
This does not work well if the archive is extracted by an archiver
that does not recognize this extension, since many pax implementations
simply discard unrecognized attributes.
.It Cm GNU.sparse.major , Cm GNU.sparse.minor , Cm GNU.sparse.name , Cm GNU.sparse.realsize
The
.Dq 1.0
format stores the sparse block map in one or more 512-byte blocks
prepended to the file data in the entry body.
The pax attributes indicate the existence of this map
(via the
.Cm GNU.sparse.major
and
.Cm GNU.sparse.minor
fields)
and the full size of the file.
The
.Cm GNU.sparse.name
holds the true name of the file.
To avoid confusion, the name stored in the regular tar header
is a modified name so that extraction errors will be apparent
to users.
.El
.Ss Solaris Tar
XXX More Details Needed XXX
.Pp
Solaris tar (beginning with SunOS XXX 5.7 ?? XXX) supports an
.Dq extended
format that is fundamentally similar to pax interchange format,
with the following differences:
.Bl -bullet -compact -width indent
.It
Extended attributes are stored in an entry whose type is
.Cm X ,
not
.Cm x ,
as used by pax interchange format.
The detailed format of this entry appears to be the same
as detailed above for the
.Cm x
entry.
.It
An additional
.Cm A
entry is used to store an ACL for the following regular entry.
The body of this entry contains a seven-digit octal number
followed by a zero byte, followed by the
textual ACL description.
The octal value is the number of ACL entries
plus a constant that indicates the ACL type: 01000000
for POSIX.1e ACLs and 03000000 for NFSv4 ACLs.
.El
.Ss AIX Tar
XXX More details needed XXX
.Ss Mac OS X Tar
The tar distributed with Apple's Mac OS X stores most regular files
as two separate entries in the tar archive.
The two entries have the same name except that the first
one has
.Dq ._
added to the beginning of the name.
This first entry stores the
.Dq resource fork
with additional attributes for the file.
The Mac OS X
.Fn CopyFile
API is used to separate a file on disk into separate
resource and data streams and to reassemble those separate
streams when the file is restored to disk.
.Ss Other Extensions
One obvious extension to increase the size of files is to
eliminate the terminating characters from the various
numeric fields.
For example, the standard only allows the size field to contain
11 octal digits, reserving the twelfth byte for a trailing
NUL character.
Allowing 12 octal digits allows file sizes up to 64 GB.
.Pp
Another extension, utilized by GNU tar, star, and other newer
.Nm
implementations, permits binary numbers in the standard numeric fields.
This is flagged by setting the high bit of the first byte.
This permits 95-bit values for the length and time fields
and 63-bit values for the uid, gid, and device numbers.
GNU tar supports this extension for the
length, mtime, ctime, and atime fields.
Joerg Schilling's star program supports this extension for
all numeric fields.
Note that this extension is largely obsoleted by the extended attribute
record provided by the pax interchange format.
.Pp
Another early GNU extension allowed base-64 values rather than octal.
This extension was short-lived and is no longer supported by any
implementation.
.Sh SEE ALSO
.Xr ar 1 ,
.Xr pax 1 ,
.Xr tar 1
.Sh STANDARDS
The
.Nm tar
utility is no longer a part of POSIX or the Single Unix Standard.
It last appeared in
.St -susv2 .
It has been supplanted in subsequent standards by
.Xr pax 1 .
The ustar format is currently part of the specification for the
.Xr pax 1
utility.
The pax interchange file format is new with
.St -p1003.1-2001 .
.Sh HISTORY
A
.Nm tar
command appeared in Seventh Edition Unix, which was released in January, 1979.
It replaced the
.Nm tp
program from Fourth Edition Unix which in turn replaced the
.Nm tap
program from First Edition Unix.
John Gilmore's
.Nm pdtar
public-domain implementation (circa 1987) was highly influential
and formed the basis of
.Nm GNU tar .
Joerg Shilling's
.Nm star
archiver is another open-source (GPL) archiver (originally developed
circa 1985) which features complete support for pax interchange
format.

--- NEW FILE: libarchive-formats.5 ---
.\" 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/lib/libarchive/libarchive-formats.5,v 1.16 2008/05/26 17:00:23 kientzle Exp $
.\"
.Dd July 17, 2009
.Dt libarchive-formats 5
.Os
.Sh NAME
.Nm libarchive-formats
.Nd archive formats supported by the libarchive library
.Sh DESCRIPTION
The
.Xr libarchive 3
library reads and writes a variety of streaming archive formats.
Generally speaking, all of these archive formats consist of a series of
.Dq entries .
Each entry stores a single file system object, such as a file, directory,
or symbolic link.
.Pp
The following provides a brief description of each format supported
by libarchive, with some information about recognized extensions or
limitations of the current library support.
Note that just because a format is supported by libarchive does not
imply that a program that uses libarchive will support that format.
Applications that use libarchive specify which formats they wish
to support, though many programs do use libarchive convenience
functions to enable all supported formats.
.Ss Tar Formats
The
.Xr libarchive 3
library can read most tar archives.
However, it only writes POSIX-standard
.Dq ustar
and
.Dq pax interchange
formats.
.Pp
All tar formats store each entry in one or more 512-byte records.
The first record is used for file metadata, including filename,
timestamp, and mode information, and the file data is stored in
subsequent records.
Later variants have extended this by either appropriating undefined
areas of the header record, extending the header to multiple records,
or by storing special entries that modify the interpretation of
subsequent entries.
.Pp
.Bl -tag -width indent
.It Cm gnutar
The
.Xr libarchive 3
library can read GNU-format tar archives.
It currently supports the most popular GNU extensions, including
modern long filename and linkname support, as well as atime and ctime data.
The libarchive library does not support multi-volume
archives, nor the old GNU long filename format.
It can read GNU sparse file entries, including the new POSIX-based
formats, but cannot write GNU sparse file entries.
.It Cm pax
The
.Xr libarchive 3
library can read and write POSIX-compliant pax interchange format
archives.
Pax interchange format archives are an extension of the older ustar
format that adds a separate entry with additional attributes stored
as key/value pairs immediately before each regular entry.
The presence of these additional entries is the only difference between
pax interchange format and the older ustar format.
The extended attributes are of unlimited length and are stored
as UTF-8 Unicode strings.
Keywords defined in the standard are in all lowercase; vendors are allowed
to define custom keys by preceding them with the vendor name in all uppercase.
When writing pax archives, libarchive uses many of the SCHILY keys
defined by Joerg Schilling's
.Dq star
archiver and a few LIBARCHIVE keys.
The libarchive library can read most of the SCHILY keys
and most of the GNU keys introduced by GNU tar.
It silently ignores any keywords that it does not understand.
.It Cm restricted pax
The libarchive library can also write pax archives in which it
attempts to suppress the extended attributes entry whenever
possible.
The result will be identical to a ustar archive unless the
extended attributes entry is required to store a long file
name, long linkname, extended ACL, file flags, or if any of the standard
ustar data (user name, group name, UID, GID, etc) cannot be fully
represented in the ustar header.
In all cases, the result can be dearchived by any program that
can read POSIX-compliant pax interchange format archives.
Programs that correctly read ustar format (see below) will also be
able to read this format; any extended attributes will be extracted as
separate files stored in
.Pa PaxHeader
directories.
.It Cm ustar
The libarchive library can both read and write this format.
This format has the following limitations:
.Bl -bullet -compact
.It
Device major and minor numbers are limited to 21 bits.
Nodes with larger numbers will not be added to the archive.
.It
Path names in the archive are limited to 255 bytes.
(Shorter if there is no / character in exactly the right place.)
.It
Symbolic links and hard links are stored in the archive with
the name of the referenced file.
This name is limited to 100 bytes.
.It
Extended attributes, file flags, and other extended
security information cannot be stored.
.It
Archive entries are limited to 2 gigabytes in size.
.El
Note that the pax interchange format has none of these restrictions.
.El
.It Solaris extensions
Libarchive recognizes ACL and extended attribute records written
by Solaris tar.
Currently, libarchive only has support for old-style ACLs; the
newer NFSv4 ACLs are recognized but discarded.
.Pp
The libarchive library can also read a variety of commonly-used extensions to
the basic tar format.
In particular, it supports base-256 values in certain numeric fields.
This essentially removes the limitations on file size, modification time,
and device numbers.
.Pp
The first tar program appeared in Seventh Edition Unix in 1979.
The first official standard for the tar file format was the
.Dq ustar
(Unix Standard Tar) format defined by POSIX in 1988.
POSIX.1-2001 extended the ustar format to create the
.Dq pax interchange
format.
.Ss Cpio Formats
The libarchive library can read a number of common cpio variants and can write
.Dq odc
and
.Dq newc
format archives.
A cpio archive stores each entry as a fixed-size header followed
by a variable-length filename and variable-length data.
Unlike the tar format, the cpio format does only minimal padding
of the header or file data.
There are several cpio variants, which differ primarily in
how they store the initial header: some store the values as
octal or hexadecimal numbers in ASCII, others as binary values of
varying byte order and length.
.Bl -tag -width indent
.It Cm binary
The libarchive library transparently reads both big-endian and little-endian
variants of the original binary cpio format.
This format used 32-bit binary values for file size and mtime,
and 16-bit binary values for the other fields.
.It Cm odc
The libarchive library can both read and write this
POSIX-standard format, which is officially known as the
.Dq cpio interchange format
or the
.Dq octet-oriented cpio archive format
and sometimes unofficially referred to as the
.Dq old character format .
This format stores the header contents as octal values in ASCII.
It is standard, portable, and immune from byte-order confusion.
File sizes and mtime are limited to 33 bits (8GB file size),
other fields are limited to 18 bits.
.It Cm SVR4
The libarchive library can read both CRC and non-CRC variants of
this format.
The SVR4 format uses eight-digit hexadecimal values for
all header fields.
This limits file size to 4GB, and also limits the mtime and
other fields to 32 bits.
The SVR4 format can optionally include a CRC of the file
contents, although libarchive does not currently verify this CRC.
.El
.Pp
Cpio first appeared in PWB/UNIX 1.0, which was released within
AT&T in 1977.
PWB/UNIX 1.0 formed the basis of System III Unix, released outside
of AT&T in 1981.
This makes cpio older than tar, although cpio was not included
in Version 7 AT&T Unix.
As a result, the tar command became much better known in universities
and research groups that used Version 7.
The combination of the
.Nm find
and
.Nm cpio
utilities provided very precise control over file selection.
Unfortunately, the format has many limitations that make it unsuitable
for widespread use.
Only the POSIX format permits files over 4GB, and its 18-bit
limit for most other fields makes it unsuitable for modern systems.
In addition, cpio formats only store numeric UID/GID values (not
usernames and group names), which can make it very difficult to correctly
transfer archives across systems with dissimilar user numbering.
.Ss Shar Formats
A
.Dq shell archive
is a shell script that, when executed on a POSIX-compliant
system, will recreate a collection of file system objects.
The libarchive library can write two different kinds of shar archives:
.Bl -tag -width indent
.It Cm shar
The traditional shar format uses a limited set of POSIX
commands, including
.Xr echo 1 ,
.Xr mkdir 1 ,
and
.Xr sed 1 .
It is suitable for portably archiving small collections of plain text files.
However, it is not generally well-suited for large archives
(many implementations of
.Xr sh 1
have limits on the size of a script) nor should it be used with non-text files.
.It Cm shardump
This format is similar to shar but encodes files using
.Xr uuencode 1
so that the result will be a plain text file regardless of the file contents.
It also includes additional shell commands that attempt to reproduce as
many file attributes as possible, including owner, mode, and flags.
The additional commands used to restore file attributes make
shardump archives less portable than plain shar archives.
.El
.Ss ISO9660 format
Libarchive can read and extract from files containing ISO9660-compliant
CDROM images.
In many cases, this can remove the need to burn a physical CDROM
just in order to read the files contained in an ISO9660 image.
It also avoids security and complexity issues that come with
virtual mounts and loopback devices.
Libarchive supports the most common Rockridge extensions and has partial
support for Joliet extensions.
If both extensions are present, the Joliet extensions will be
used and the Rockridge extensions will be ignored.
In particular, this can create problems with hardlinks and symlinks,
which are supported by Rockridge but not by Joliet.
.Ss Zip format
Libarchive can read and write zip format archives that have
uncompressed entries and entries compressed with the
.Dq deflate
algorithm.
Older zip compression algorithms are not supported.
It can extract jar archives, archives that use Zip64 extensions and many
self-extracting zip archives.
Libarchive reads Zip archives as they are being streamed,
which allows it to read archives of arbitrary size.
It currently does not use the central directory; this
limits libarchive's ability to support some self-extracting
archives and ones that have been modified in certain ways.
.Ss Archive (library) file format
The Unix archive format (commonly created by the
.Xr ar 1
archiver) is a general-purpose format which is
used almost exclusively for object files to be
read by the link editor
.Xr ld 1 .
The ar format has never been standardised.
There are two common variants:
the GNU format derived from SVR4,
and the BSD format, which first appeared in 4.4BSD.
The two differ primarily in their handling of filenames
longer than 15 characters:
the GNU/SVR4 variant writes a filename table at the beginning of the archive;
the BSD format stores each long filename in an extension
area adjacent to the entry.
Libarchive can read both extensions,
including archives that may include both types of long filenames.
Programs using libarchive can write GNU/SVR4 format
if they provide a filename table to be written into
the archive before any of the entries.
Any entries whose names are not in the filename table
will be written using BSD-style long filenames.
This can cause problems for programs such as
GNU ld that do not support the BSD-style long filenames.
.Ss mtree
Libarchive can read and write files in
.Xr mtree 5
format.
This format is not a true archive format, but rather a textual description
of a file hierarchy in which each line specifies the name of a file and
provides specific metadata about that file.
Libarchive can read all of the keywords supported by both
the NetBSD and FreeBSD versions of
.Xr mtree 1 ,
although many of the keywords cannot currently be stored in an
.Tn archive_entry
object.
When writing, libarchive supports use of the
.Xr archive_write_set_options 3
interface to specify which keywords should be included in the
output.
If libarchive was compiled with access to suitable
cryptographic libraries (such as the OpenSSL libraries),
it can compute hash entries such as
.Cm sha512
or
.Cm md5
from file data being written to the mtree writer.
.Pp
When reading an mtree file, libarchive will locate the corresponding
files on disk using the
.Cm contents
keyword if present or the regular filename.
If it can locate and open the file on disk, it will use that
to fill in any metadata that is missing from the mtree file
and will read the file contents and return those to the program
using libarchive.
If it cannot locate and open the file on disk, libarchive
will return an error for any attempt to read the entry
body.
.Sh SEE ALSO
.Xr ar 1 ,
.Xr cpio 1 ,
.Xr mkisofs 1 ,
.Xr shar 1 ,
.Xr tar 1 ,
.Xr zip 1 ,
.Xr zlib 3 ,
.Xr cpio 5 ,
.Xr mtree 5 ,
.Xr tar 5

--- NEW FILE: archive_entry_link_resolver.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_link_resolver.c,v 1.4 2008/09/05 06:15:25 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "archive.h"
#include "archive_entry.h"

/*
 * This is mostly a pretty straightforward hash table implementation.
 * The only interesting bit is the different strategies used to
 * match up links.  These strategies match those used by various
 * archiving formats:
 *   tar - content stored with first link, remainder refer back to it.
 *       This requires us to match each subsequent link up with the
 *       first appearance.
 *   cpio - Old cpio just stored body with each link, match-ups were
 *       implicit.  This is trivial.
 *   new cpio - New cpio only stores body with last link, match-ups
 *       are implicit.  This is actually quite tricky; see the notes
 *       below.
 */

/* Users pass us a format code, we translate that into a strategy here. */
#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR  0
#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3

/* Initial size of link cache. */
#define links_cache_initial_size 1024

struct links_entry {
    struct links_entry  *next;
    struct links_entry  *previous;
    int          links; /* # links not yet seen */
    int          hash;
    struct archive_entry    *canonical;
    struct archive_entry    *entry;
};

struct archive_entry_linkresolver {
    struct links_entry  **buckets;
    struct links_entry   *spare;
    unsigned long         number_entries;
    size_t            number_buckets;
    int           strategy;
};

static struct links_entry *find_entry(struct archive_entry_linkresolver *,
            struct archive_entry *);
static void grow_hash(struct archive_entry_linkresolver *);
static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
            struct archive_entry *);
static struct links_entry *next_entry(struct archive_entry_linkresolver *);

struct archive_entry_linkresolver *
archive_entry_linkresolver_new(void)
{
    struct archive_entry_linkresolver *res;
    size_t i;

    res = malloc(sizeof(struct archive_entry_linkresolver));
    if (res == NULL)
        return (NULL);
    memset(res, 0, sizeof(struct archive_entry_linkresolver));
    res->number_buckets = links_cache_initial_size;
    res->buckets = malloc(res->number_buckets *
        sizeof(res->buckets[0]));
    if (res->buckets == NULL) {
        free(res);
        return (NULL);
    }
    for (i = 0; i < res->number_buckets; i++)
        res->buckets[i] = NULL;
    return (res);
}

void
archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
    int fmt)
{
    int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;

    switch (fmtbase) {
    case ARCHIVE_FORMAT_CPIO:
        switch (fmt) {
        case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
        case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
            res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
            break;
        default:
            res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
            break;
        }
        break;
    case ARCHIVE_FORMAT_MTREE:
        res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
        break;
    case ARCHIVE_FORMAT_TAR:
        res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
        break;
    default:
        res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
        break;
    }
}

void
archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
{
    struct links_entry *le;

    if (res == NULL)
        return;

    if (res->buckets != NULL) {
        while ((le = next_entry(res)) != NULL)
            archive_entry_free(le->entry);
        free(res->buckets);
        res->buckets = NULL;
    }
    free(res);
}

void
archive_entry_linkify(struct archive_entry_linkresolver *res,
    struct archive_entry **e, struct archive_entry **f)
{
    struct links_entry *le;
    struct archive_entry *t;

    *f = NULL; /* Default: Don't return a second entry. */

    if (*e == NULL) {
        le = next_entry(res);
        if (le != NULL) {
            *e = le->entry;
            le->entry = NULL;
        }
        return;
    }

    /* If it has only one link, then we're done. */
    if (archive_entry_nlink(*e) == 1)
        return;
    /* Directories never have hardlinks. */
    if (archive_entry_filetype(*e) == AE_IFDIR)
        return;

    switch (res->strategy) {
    case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
        le = find_entry(res, *e);
        if (le != NULL) {
            archive_entry_unset_size(*e);
            archive_entry_copy_hardlink(*e,
                archive_entry_pathname(le->canonical));
        } else
            insert_entry(res, *e);
        return;
    case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
        le = find_entry(res, *e);
        if (le != NULL) {
            archive_entry_copy_hardlink(*e,
                archive_entry_pathname(le->canonical));
        } else
            insert_entry(res, *e);
        return;
    case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
        /* This one is trivial. */
        return;
    case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
        le = find_entry(res, *e);
        if (le != NULL) {
            /*
             * Put the new entry in le, return the
             * old entry from le.
             */
            t = *e;
            *e = le->entry;
            le->entry = t;
            /* Make the old entry into a hardlink. */
            archive_entry_unset_size(*e);
            archive_entry_copy_hardlink(*e,
                archive_entry_pathname(le->canonical));
            /* If we ran out of links, return the
             * final entry as well. */
            if (le->links == 0) {
                *f = le->entry;
                le->entry = NULL;
            }
        } else {
            /*
             * If we haven't seen it, tuck it away
             * for future use.
             */
            le = insert_entry(res, *e);
            le->entry = *e;
            *e = NULL;
        }
        return;
    default:
        break;
    }
    return;
}

static struct links_entry *
find_entry(struct archive_entry_linkresolver *res,
    struct archive_entry *entry)
{
    struct links_entry  *le;
    int          hash, bucket;
    dev_t            dev;
    int64_t          ino;

    /* Free a held entry. */
    if (res->spare != NULL) {
        archive_entry_free(res->spare->canonical);
        archive_entry_free(res->spare->entry);
        free(res->spare);
        res->spare = NULL;
    }

    /* If the links cache overflowed and got flushed, don't bother. */
    if (res->buckets == NULL)
        return (NULL);

    dev = archive_entry_dev(entry);
    ino = archive_entry_ino64(entry);
    hash = (int)(dev ^ ino);

    /* Try to locate this entry in the links cache. */
    bucket = hash % res->number_buckets;
    for (le = res->buckets[bucket]; le != NULL; le = le->next) {
        if (le->hash == hash
            && dev == archive_entry_dev(le->canonical)
            && ino == archive_entry_ino64(le->canonical)) {
            /*
             * Decrement link count each time and release
             * the entry if it hits zero.  This saves
             * memory and is necessary for detecting
             * missed links.
             */
            --le->links;
            if (le->links > 0)
                return (le);
            /* Remove it from this hash bucket. */
            if (le->previous != NULL)
                le->previous->next = le->next;
            if (le->next != NULL)
                le->next->previous = le->previous;
            if (res->buckets[bucket] == le)
                res->buckets[bucket] = le->next;
            res->number_entries--;
            /* Defer freeing this entry. */
            res->spare = le;
            return (le);
        }
    }
    return (NULL);
}

static struct links_entry *
next_entry(struct archive_entry_linkresolver *res)
{
    struct links_entry  *le;
    size_t           bucket;

    /* Free a held entry. */
    if (res->spare != NULL) {
        archive_entry_free(res->spare->canonical);
        free(res->spare);
        res->spare = NULL;
    }

    /* If the links cache overflowed and got flushed, don't bother. */
    if (res->buckets == NULL)
        return (NULL);

    /* Look for next non-empty bucket in the links cache. */
    for (bucket = 0; bucket < res->number_buckets; bucket++) {
        le = res->buckets[bucket];
        if (le != NULL) {
            /* Remove it from this hash bucket. */
            if (le->next != NULL)
                le->next->previous = le->previous;
            res->buckets[bucket] = le->next;
            res->number_entries--;
            /* Defer freeing this entry. */
            res->spare = le;
            return (le);
        }
    }
    return (NULL);
}

static struct links_entry *
insert_entry(struct archive_entry_linkresolver *res,
    struct archive_entry *entry)
{
    struct links_entry *le;
    int          hash, bucket;

    /* Add this entry to the links cache. */
    le = malloc(sizeof(struct links_entry));
    if (le == NULL)
        return (NULL);
    memset(le, 0, sizeof(*le));
    le->canonical = archive_entry_clone(entry);

    /* If the links cache is getting too full, enlarge the hash table. */
    if (res->number_entries > res->number_buckets * 2)
        grow_hash(res);

    hash = archive_entry_dev(entry) ^ archive_entry_ino64(entry);
    bucket = hash % res->number_buckets;

    /* If we could allocate the entry, record it. */
    if (res->buckets[bucket] != NULL)
        res->buckets[bucket]->previous = le;
    res->number_entries++;
    le->next = res->buckets[bucket];
    le->previous = NULL;
    res->buckets[bucket] = le;
    le->hash = hash;
    le->links = archive_entry_nlink(entry) - 1;
    return (le);
}

static void
grow_hash(struct archive_entry_linkresolver *res)
{
    struct links_entry *le, **new_buckets;
    size_t new_size;
    size_t i, bucket;

    /* Try to enlarge the bucket list. */
    new_size = res->number_buckets * 2;
    new_buckets = malloc(new_size * sizeof(struct links_entry *));

    if (new_buckets != NULL) {
        memset(new_buckets, 0,
            new_size * sizeof(struct links_entry *));
        for (i = 0; i < res->number_buckets; i++) {
            while (res->buckets[i] != NULL) {
                /* Remove entry from old bucket. */
                le = res->buckets[i];
                res->buckets[i] = le->next;

                /* Add entry to new bucket. */
                bucket = le->hash % new_size;

                if (new_buckets[bucket] != NULL)
                    new_buckets[bucket]->previous =
                        le;
                le->next = new_buckets[bucket];
                le->previous = NULL;
                new_buckets[bucket] = le;
            }
        }
        free(res->buckets);
        res->buckets = new_buckets;
        res->number_buckets = new_size;
    }
}

--- NEW FILE: archive_read_data_into_fd.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.16 2008/05/23 05:01:29 cperciva Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "archive.h"
#include "archive_private.h"

/* Maximum amount of data to write at one time. */
#define MAX_WRITE   (1024 * 1024)

/*
 * This implementation minimizes copying of data and is sparse-file aware.
 */
int
archive_read_data_into_fd(struct archive *a, int fd)
{
    int r;
    const void *buff;
    size_t size, bytes_to_write;
    ssize_t bytes_written, total_written;
    off_t offset;
    off_t output_offset;

    __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd");

    total_written = 0;
    output_offset = 0;

    while ((r = archive_read_data_block(a, &buff, &size, &offset)) ==
        ARCHIVE_OK) {
        const char *p = buff;
        if (offset > output_offset) {
            output_offset = lseek(fd,
                offset - output_offset, SEEK_CUR);
            if (output_offset != offset) {
                archive_set_error(a, errno, "Seek error");
                return (ARCHIVE_FATAL);
            }
        }
        while (size > 0) {
            bytes_to_write = size;
            if (bytes_to_write > MAX_WRITE)
                bytes_to_write = MAX_WRITE;
            bytes_written = write(fd, p, bytes_to_write);
            if (bytes_written < 0) {
                archive_set_error(a, errno, "Write error");
                return (ARCHIVE_FATAL);
            }
            output_offset += bytes_written;
            total_written += bytes_written;
            p += bytes_written;
            size -= bytes_written;
        }
    }

    if (r != ARCHIVE_EOF)
        return (r);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_platform.h ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.32 2008/12/06 05:53:05 kientzle Exp $
 */

/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */

/*
 * This header is the first thing included in any of the libarchive
 * source files.  As far as possible, platform-specific issues should
 * be dealt with here and not within individual source files.  I'm
 * actively trying to minimize #if blocks within the main source,
 * since they obfuscate the code.
 */

#ifndef ARCHIVE_PLATFORM_H_INCLUDED
#define ARCHIVE_PLATFORM_H_INCLUDED

/* archive.h and archive_entry.h require this. */
#define __LIBARCHIVE_BUILD 1

#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 the library hasn't been (automatically or manually) configured. */
#error Oops: No config.h and no pre-built configuration in archive_platform.h.
#endif

/* It should be possible to get rid of this by extending the feature-test
 * macros to cover Windows API functions, probably along with non-trivial
 * refactoring of code to find structures that sit more cleanly on top of
 * either Windows or Posix APIs. */
#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
#include "archive_windows.h"
#endif

/*
 * The config files define a lot of feature macros.  The following
 * uses those macros to select/define replacements and include key
 * headers as required.
 */

/* 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

/* Try to get standard C99-style integer type definitions. */
#if HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#if HAVE_STDINT_H
#include <stdint.h>
#endif

/* Some platforms lack the standard *_MAX definitions. */
#if !HAVE_DECL_SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif
#if !HAVE_DECL_SSIZE_MAX
#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
#endif
#if !HAVE_DECL_UINT32_MAX
#define UINT32_MAX (~(uint32_t)0)
#endif
#if !HAVE_DECL_UINT64_MAX
#define UINT64_MAX (~(uint64_t)0)
#endif
#if !HAVE_DECL_INT64_MAX
#define INT64_MAX ((int64_t)(UINT64_MAX >> 1))
#endif
#if !HAVE_DECL_INT64_MIN
#define INT64_MIN ((int64_t)(~INT64_MAX))
#endif

/*
 * If this platform has <sys/acl.h>, acl_create(), acl_init(),
 * acl_set_file(), and ACL_USER, we assume it has the rest of the
 * POSIX.1e draft functions used in archive_read_extract.c.
 */
#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE && HAVE_ACL_USER
#define HAVE_POSIX_ACL  1
#endif

/*
 * If we can't restore metadata using a file descriptor, then
 * for compatibility's sake, close files before trying to restore metadata.
 */
#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
#define CAN_RESTORE_METADATA_FD
#endif

/* Set up defaults for internal error codes. */
#ifndef ARCHIVE_ERRNO_FILE_FORMAT
#if HAVE_EFTYPE
#define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
#else
#if HAVE_EILSEQ
#define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
#else
#define ARCHIVE_ERRNO_FILE_FORMAT EINVAL
#endif
#endif
#endif

#ifndef ARCHIVE_ERRNO_PROGRAMMER
#define ARCHIVE_ERRNO_PROGRAMMER EINVAL
#endif

#ifndef ARCHIVE_ERRNO_MISC
#define ARCHIVE_ERRNO_MISC (-1)
#endif

#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */

--- NEW FILE: archive_entry.3 ---
.\" 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/lib/libarchive/archive_entry.3,v 1.18 2008/05/26 17:00:22 kientzle Exp $
.\"
.Dd May 12, 2008
.Dt archive_entry 3
.Os
.Sh NAME
.Nm archive_entry_acl_add_entry ,
.Nm archive_entry_acl_add_entry_w ,
.Nm archive_entry_acl_clear ,
.Nm archive_entry_acl_count ,
.Nm archive_entry_acl_next ,
.Nm archive_entry_acl_next_w ,
.Nm archive_entry_acl_reset ,
.Nm archive_entry_acl_text_w ,
.Nm archive_entry_atime ,
.Nm archive_entry_atime_nsec ,
.Nm archive_entry_clear ,
.Nm archive_entry_clone ,
.Nm archive_entry_copy_fflags_text ,
.Nm archive_entry_copy_fflags_text_w ,
.Nm archive_entry_copy_gname ,
.Nm archive_entry_copy_gname_w ,
.Nm archive_entry_copy_hardlink ,
.Nm archive_entry_copy_hardlink_w ,
.Nm archive_entry_copy_link ,
.Nm archive_entry_copy_link_w ,
.Nm archive_entry_copy_pathname_w ,
.Nm archive_entry_copy_sourcepath ,
.Nm archive_entry_copy_stat ,
.Nm archive_entry_copy_symlink ,
.Nm archive_entry_copy_symlink_w ,
.Nm archive_entry_copy_uname ,
.Nm archive_entry_copy_uname_w ,
.Nm archive_entry_dev ,
.Nm archive_entry_devmajor ,
.Nm archive_entry_devminor ,
.Nm archive_entry_filetype ,
.Nm archive_entry_fflags ,
.Nm archive_entry_fflags_text ,
.Nm archive_entry_free ,
.Nm archive_entry_gid ,
.Nm archive_entry_gname ,
.Nm archive_entry_hardlink ,
.Nm archive_entry_ino ,
.Nm archive_entry_mode ,
.Nm archive_entry_mtime ,
.Nm archive_entry_mtime_nsec ,
.Nm archive_entry_nlink ,
.Nm archive_entry_new ,
.Nm archive_entry_pathname ,
.Nm archive_entry_pathname_w ,
.Nm archive_entry_rdev ,
.Nm archive_entry_rdevmajor ,
.Nm archive_entry_rdevminor ,
.Nm archive_entry_set_atime ,
.Nm archive_entry_set_ctime ,
.Nm archive_entry_set_dev ,
.Nm archive_entry_set_devmajor ,
.Nm archive_entry_set_devminor ,
.Nm archive_entry_set_filetype ,
.Nm archive_entry_set_fflags ,
.Nm archive_entry_set_gid ,
.Nm archive_entry_set_gname ,
.Nm archive_entry_set_hardlink ,
.Nm archive_entry_set_link ,
.Nm archive_entry_set_mode ,
.Nm archive_entry_set_mtime ,
.Nm archive_entry_set_pathname ,
.Nm archive_entry_set_rdevmajor ,
.Nm archive_entry_set_rdevminor ,
.Nm archive_entry_set_size ,
.Nm archive_entry_set_symlink ,
.Nm archive_entry_set_uid ,
.Nm archive_entry_set_uname ,
.Nm archive_entry_size ,
.Nm archive_entry_sourcepath ,
.Nm archive_entry_stat ,
.Nm archive_entry_symlink ,
.Nm archive_entry_uid ,
.Nm archive_entry_uname
.Nd functions for manipulating archive entry descriptions
.Sh SYNOPSIS
.In archive_entry.h
.Ft void
.Fo archive_entry_acl_add_entry
.Fa "struct archive_entry *"
.Fa "int type"
.Fa "int permset"
.Fa "int tag"
.Fa "int qual"
.Fa "const char *name"
.Fc
.Ft void
.Fo archive_entry_acl_add_entry_w
.Fa "struct archive_entry *"
.Fa "int type"
.Fa "int permset"
.Fa "int tag"
.Fa "int qual"
.Fa "const wchar_t *name"
.Fc
.Ft void
.Fn archive_entry_acl_clear "struct archive_entry *"
.Ft int
.Fn archive_entry_acl_count "struct archive_entry *" "int type"
.Ft int
.Fo archive_entry_acl_next
.Fa "struct archive_entry *"
.Fa "int want_type"
.Fa "int *type"
.Fa "int *permset"
.Fa "int *tag"
.Fa "int *qual"
.Fa "const char **name"
.Fc
.Ft int
.Fo archive_entry_acl_next_w
.Fa "struct archive_entry *"
.Fa "int want_type"
.Fa "int *type"
.Fa "int *permset"
.Fa "int *tag"
.Fa "int *qual"
.Fa "const wchar_t **name"
.Fc
.Ft int
.Fn archive_entry_acl_reset "struct archive_entry *" "int want_type"
.Ft const wchar_t *
.Fn archive_entry_acl_text_w "struct archive_entry *" "int flags"
.Ft time_t
.Fn archive_entry_atime "struct archive_entry *"
.Ft long
.Fn archive_entry_atime_nsec "struct archive_entry *"
.Ft "struct archive_entry *"
.Fn archive_entry_clear "struct archive_entry *"
.Ft struct archive_entry *
.Fn archive_entry_clone "struct archive_entry *"
.Ft const char * *
.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const char *"
.Ft const wchar_t *
.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
.Ft void
.Fn archive_entry_copy_gname "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *"
.Ft void
.Fn archive_entry_copy_hardlink "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *"
.Ft void
.Fn archive_entry_copy_sourcepath "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *"
.Ft void
.Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
.Ft void
.Fn archive_entry_copy_symlink "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_copy_symlink_w "struct archive_entry *" "const wchar_t *"
.Ft void
.Fn archive_entry_copy_uname "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *"
.Ft dev_t
.Fn archive_entry_dev "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_devmajor "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_devminor "struct archive_entry *"
.Ft mode_t
.Fn archive_entry_filetype "struct archive_entry *"
.Ft void
.Fo archive_entry_fflags
.Fa "struct archive_entry *"
.Fa "unsigned long *set"
.Fa "unsigned long *clear"
.Fc
.Ft const char *
.Fn archive_entry_fflags_text "struct archive_entry *"
.Ft void
.Fn archive_entry_free "struct archive_entry *"
.Ft const char *
.Fn archive_entry_gname "struct archive_entry *"
.Ft const char *
.Fn archive_entry_hardlink "struct archive_entry *"
.Ft ino_t
.Fn archive_entry_ino "struct archive_entry *"
.Ft mode_t
.Fn archive_entry_mode "struct archive_entry *"
.Ft time_t
.Fn archive_entry_mtime "struct archive_entry *"
.Ft long
.Fn archive_entry_mtime_nsec "struct archive_entry *"
.Ft unsigned int
.Fn archive_entry_nlink "struct archive_entry *"
.Ft struct archive_entry *
.Fn archive_entry_new "void"
.Ft const char *
.Fn archive_entry_pathname "struct archive_entry *"
.Ft const wchar_t *
.Fn archive_entry_pathname_w "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_rdev "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_rdevmajor "struct archive_entry *"
.Ft dev_t
.Fn archive_entry_rdevminor "struct archive_entry *"
.Ft void
.Fn archive_entry_set_dev "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_devmajor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_devminor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_filetype "struct archive_entry *" "unsigned int"
.Ft void
.Fo archive_entry_set_fflags
.Fa "struct archive_entry *"
.Fa "unsigned long set"
.Fa "unsigned long clear"
.Fc
.Ft void
.Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
.Ft void
.Fn archive_entry_set_gname "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_hardlink "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_ino "struct archive_entry *" "unsigned long"
.Ft void
.Fn archive_entry_set_link "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_mode "struct archive_entry *" "mode_t"
.Ft void
.Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos"
.Ft void
.Fn archive_entry_set_nlink "struct archive_entry *" "unsigned int"
.Ft void
.Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_rdev "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t"
.Ft void
.Fn archive_entry_set_size "struct archive_entry *" "int64_t"
.Ft void
.Fn archive_entry_set_symlink "struct archive_entry *" "const char *"
.Ft void
.Fn archive_entry_set_uid "struct archive_entry *" "uid_t"
.Ft void
.Fn archive_entry_set_uname "struct archive_entry *" "const char *"
.Ft int64_t
.Fn archive_entry_size "struct archive_entry *"
.Ft const char *
.Fn archive_entry_sourcepath "struct archive_entry *"
.Ft const struct stat *
.Fn archive_entry_stat "struct archive_entry *"
.Ft const char *
.Fn archive_entry_symlink "struct archive_entry *"
.Ft const char *
.Fn archive_entry_uname "struct archive_entry *"
.Sh DESCRIPTION
These functions create and manipulate data objects that
represent entries within an archive.
You can think of a
.Tn struct archive_entry
as a heavy-duty version of
.Tn struct stat :
it includes everything from
.Tn struct stat
plus associated pathname, textual group and user names, etc.
These objects are used by
.Xr libarchive 3
to represent the metadata associated with a particular
entry in an archive.
.Ss Create and Destroy
There are functions to allocate, destroy, clear, and copy
.Va archive_entry
objects:
.Bl -tag -compact -width indent
.It Fn archive_entry_clear
Erases the object, resetting all internal fields to the
same state as a newly-created object.
This is provided to allow you to quickly recycle objects
without thrashing the heap.
.It Fn archive_entry_clone
A deep copy operation; all text fields are duplicated.
.It Fn archive_entry_free
Releases the
.Tn struct archive_entry
object.
.It Fn archive_entry_new
Allocate and return a blank
.Tn struct archive_entry
object.
.El
.Ss Set and Get Functions
Most of the functions here set or read entries in an object.
Such functions have one of the following forms:
.Bl -tag -compact -width indent
.It Fn archive_entry_set_XXXX
Stores the provided data in the object.
In particular, for strings, the pointer is stored,
not the referenced string.
.It Fn archive_entry_copy_XXXX
As above, except that the referenced data is copied
into the object.
.It Fn archive_entry_XXXX
Returns the specified data.
In the case of strings, a const-qualified pointer to
the string is returned.
.El
String data can be set or accessed as wide character strings
or normal
.Va char
strings.
The functions that use wide character strings are suffixed with
.Cm _w .
Note that these are different representations of the same data:
For example, if you store a narrow string and read the corresponding
wide string, the object will transparently convert formats
using the current locale.
Similarly, if you store a wide string and then store a
narrow string for the same data, the previously-set wide string will
be discarded in favor of the new data.
.Pp
There are a few set/get functions that merit additional description:
.Bl -tag -compact -width indent
.It Fn archive_entry_set_link
This function sets the symlink field if it is already set.
Otherwise, it sets the hardlink field.
.El
.Ss File Flags
File flags are transparently converted between a bitmap
representation and a textual format.
For example, if you set the bitmap and ask for text, the library
will build a canonical text format.
However, if you set a text format and request a text format,
you will get back the same text, even if it is ill-formed.
If you need to canonicalize a textual flags string, you should first set the
text form, then request the bitmap form, then use that to set the bitmap form.
Setting the bitmap format will clear the internal text representation
and force it to be reconstructed when you next request the text form.
.Pp
The bitmap format consists of two integers, one containing bits
that should be set, the other specifying bits that should be
cleared.
Bits not mentioned in either bitmap will be ignored.
Usually, the bitmap of bits to be cleared will be set to zero.
In unusual circumstances, you can force a fully-specified set
of file flags by setting the bitmap of flags to clear to the complement
of the bitmap of flags to set.
(This differs from
.Xr fflagstostr 3 ,
which only includes names for set bits.)
Converting a bitmap to a textual string is a platform-specific
operation; bits that are not meaningful on the current platform
will be ignored.
.Pp
The canonical text format is a comma-separated list of flag names.
The
.Fn archive_entry_copy_fflags_text
and
.Fn archive_entry_copy_fflags_text_w
functions parse the provided text and sets the internal bitmap values.
This is a platform-specific operation; names that are not meaningful
on the current platform will be ignored.
The function returns a pointer to the start of the first name that was not
recognized, or NULL if every name was recognized.
Note that every name--including names that follow an unrecognized name--will
be evaluated, and the bitmaps will be set to reflect every name that is
recognized.
(In particular, this differs from
.Xr strtofflags 3 ,
which stops parsing at the first unrecognized name.)
.Ss ACL Handling
XXX This needs serious help.
XXX
.Pp
An
.Dq Access Control List
(ACL) is a list of permissions that grant access to particular users or
groups beyond what would normally be provided by standard POSIX mode bits.
The ACL handling here addresses some deficiencies in the POSIX.1e draft 17 ACL
specification.
In particular, POSIX.1e draft 17 specifies several different formats, but
none of those formats include both textual user/group names and numeric
UIDs/GIDs.
.Pp
XXX explain ACL stuff XXX
.\" .Sh EXAMPLE
.\" .Sh RETURN VALUES
.\" .Sh ERRORS
.Sh SEE ALSO
.Xr archive 3
.Sh HISTORY
The
.Nm libarchive
library first appeared in
.Fx 5.3 .
.Sh AUTHORS
.An -nosplit
The
.Nm libarchive
library was written by
.An Tim Kientzle Aq kientzle at acm.org .
.\" .Sh BUGS

--- NEW FILE: config_windows.h ---
/* config.h.  Generated from config.h.in by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */

#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif

#ifndef CONFIG_H_INCLUDED
#define CONFIG_H_INCLUDED

///////////////////////////////////////////////////////////////////////////
//  Check for Watcom and Microsoft Visual C compilers (WIN32 only)  ///////
///////////////////////////////////////////////////////////////////////////
#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
  #define   IS_WIN32  1

  #if defined(__TURBOC__) || defined(__BORLANDC__) /* Borland compilers */
  #elif defined( __WATCOMC__ ) || defined(__WATCOMCPP__) /* Watcom compilers */
    #define IS_WATCOM  1
    /* Define to 1 if __INT64 is defined */
  #elif defined(__IBMC__) || defined(__IBMCPP__) /* IBM compilers */
  #elif defined( __SC__ ) /* Symantec C++ compilers */
  #elif defined( M_I86 ) && defined( MSDOS ) /* Microsoft DOS/Win 16 compilers */
  #elif defined( _M_IX86 ) || defined( _68K_ ) /* Microsoft Win32 compilers */
    #define IS_VISUALC 1
    /* Define to 1 if __INT64 is defined */
  #else
  #endif

  /* Define to 1 if UID should be unsigned */
  #define   USE_UNSIGNED_UID 1

  /* Define to 1 if UID should be unsigned */
  #define   USE_UNSIGNED_GID 1
#endif
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

/* Define to 1 if you have the `acl_create_entry' function. */
/* #undef HAVE_ACL_CREATE_ENTRY */

/* Define to 1 if you have the `acl_get_perm' function. */
/* #undef HAVE_ACL_GET_PERM */

/* Define to 1 if you have the `acl_get_perm_np' function. */
/* #undef HAVE_ACL_GET_PERM_NP */

/* Define to 1 if you have the `acl_init' function. */
/* #undef HAVE_ACL_INIT */

/* Define to 1 if the system has the type `acl_permset_t'. */
/* #undef HAVE_ACL_PERMSET_T */

/* Define to 1 if you have the `acl_set_fd' function. */
/* #undef HAVE_ACL_SET_FD */

/* Define to 1 if you have the `acl_set_fd_np' function. */
/* #undef HAVE_ACL_SET_FD_NP */

/* Define to 1 if you have the `acl_set_file' function. */
/* #undef HAVE_ACL_SET_FILE */

/* True for systems with POSIX ACL support */
/* #undef HAVE_ACL_USER */

/* Define to 1 if you have the <attr/xattr.h> header file. */
/* #undef HAVE_ATTR_XATTR_H */

/* Define to 1 if you have the <bzlib.h> header file. */
/* #undef HAVE_BZLIB_H */

/* Define to 1 if you have the `chflags' function. */
/* #undef HAVE_CHFLAGS */

/* Define to 1 if you have the `chown' function. */
/* #undef HAVE_CHOWN */

/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
   don't. */
#if defined(_MSC_VER)
/* #undef HAVE_DECL_INT64_MAX */
#else
#define HAVE_DECL_INT64_MAX 1
#endif

/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
   don't. */
#if defined(_MSC_VER)
/* #undef HAVE_DECL_INT64_MIN */
#else
#define HAVE_DECL_INT64_MIN 1
#endif

/* Define to 1 if you have the declaration of `optarg', and to 0 if you don't.
   */
/* #undef HAVE_DECL_OPTARG */

/* Define to 1 if you have the declaration of `optind', and to 0 if you don't.
   */
/* #undef HAVE_DECL_OPTIND */

/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
   don't. */
#if defined(_MSC_VER)
    #if _MSC_VER >= 1400
    #define HAVE_DECL_SIZE_MAX 1
    #else
    /* #undef HAVE_DECL_SIZE_MAX */
    #endif
#else
#define HAVE_DECL_SIZE_MAX 1
#endif

/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
   don't. */
/* #undef HAVE_DECL_SSIZE_MAX */

/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
   don't. */
/* #undef HAVE_DECL_STRERROR_R */

/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
   don't. */
#if defined(_MSC_VER)
/* #undef HAVE_DECL_UINT32_MAX */
#else
#define HAVE_DECL_UINT32_MAX 1
#endif

/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
   don't. */
#if defined(_MSC_VER)
/* #undef HAVE_DECL_UINT64_MAX */
#else
#define HAVE_DECL_UINT64_MAX 1
#endif

/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
   */
/* #undef HAVE_DIRENT_H */

/* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */

/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
/* #undef HAVE_DOPRNT */

/* Define to 1 if nl_langinfo supports D_MD_ORDER */
/* #undef HAVE_D_MD_ORDER */

/* A possible errno value for invalid file format errors */
/* #undef HAVE_EFTYPE */

/* A possible errno value for invalid file format errors */
#define HAVE_EILSEQ 1

/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1

/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
/* #undef HAVE_EXT2FS_EXT2_FS_H */

/* Define to 1 if you have the `fchdir' function. */
/* #undef HAVE_FCHDIR */

/* Define to 1 if you have the `fchflags' function. */
/* #undef HAVE_FCHFLAGS */

/* Define to 1 if you have the `fchmod' function. */
/* #undef HAVE_FCHMOD */

/* Define to 1 if you have the `fchown' function. */
/* #undef HAVE_FCHOWN */

/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1

/* Define to 1 if you have the fcntl() function. */
/* #undef HAVE_FCNTL_FN */

/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
/* #undef HAVE_FSEEKO */

/* Define to 1 if you have the `fsetxattr' function. */
/* #undef HAVE_FSETXATTR */

/* Define to 1 if you have the `ftruncate' function. */
#define HAVE_FTRUNCATE 1

/* Define to 1 if you have the `futimes' function. */
#define HAVE_FUTIMES 1

/* Define to 1 if you have the `geteuid' function. */
/* #undef HAVE_GETEUID */

/* Define to 1 if you have the `getxattr' function. */
/* #undef HAVE_GETXATTR */

/* Define to 1 if you have the <grp.h> header file. */
/* #undef HAVE_GRP_H */

/* Define to 1 if the system has the type `intmax_t'. */
/* #undef HAVE_INTMAX_T */

/* Define to 1 if you have the <inttypes.h> header file. */
/* #undef HAVE_INTTYPES_H */

/* Define to 1 if you have the <langinfo.h> header file. */
/* #undef HAVE_LANGINFO_H */

/* Define to 1 if you have the `lchflags' function. */
/* #undef HAVE_LCHFLAGS */

/* Define to 1 if you have the `lchmod' function. */
/* #undef HAVE_LCHMOD */

/* Define to 1 if you have the `lchown' function. */
/* #undef HAVE_LCHOWN */

/* Define to 1 if you have the `lgetxattr' function. */
/* #undef HAVE_LGETXATTR */

/* Define to 1 if you have the `acl' library (-lacl). */
/* #undef HAVE_LIBACL */

/* Define to 1 if you have the `attr' library (-lattr). */
/* #undef HAVE_LIBATTR */

/* Define to 1 if you have the `bz2' library (-lbz2). */
/* #undef HAVE_LIBBZ2 */

/* Define to 1 if you have the `z' library (-lz). */
/* #undef HAVE_LIBZ */

/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1

/* Define to 1 if you have the <linux/ext2_fs.h> header file. */
/* #undef HAVE_LINUX_EXT2_FS_H */

/* Define to 1 if you have the <linux/fs.h> header file. */
/* #undef HAVE_LINUX_FS_H */

/* Define to 1 if you have the `listxattr' function. */
/* #undef HAVE_LISTXATTR */

/* Define to 1 if you have the `llistxattr' function. */
/* #undef HAVE_LLISTXATTR */

/* Define to 1 if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1

/* Define to 1 if the system has the type `long long int'. */
#define HAVE_LONG_LONG_INT 1

/* Define to 1 if you have the `lsetxattr' function. */
/* #undef HAVE_LSETXATTR */

/* Define to 1 if `lstat' has the bug that it succeeds when given the
   zero-length file name argument. */
/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */

/* Define to 1 if you have the `lutimes' function. */
/* #undef HAVE_LUTIMES */

/* Define to 1 if you have the `memmove' function. */
#define HAVE_MEMMOVE 1

/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1

/* Define to 1 if you have the `mkdir' function. */
#define HAVE_MKDIR 1

/* Define to 1 if you have the `mkfifo' function. */
/* #undef HAVE_MKFIFO */

/* Define to 1 if you have the `mknod' function. */
/* #undef HAVE_MKNOD */

/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
/* #undef HAVE_NDIR_H */

/* Define to 1 if you have the `nl_langinfo' function. */
/* #undef HAVE_NL_LANGINFO */

/* Define to 1 if you have the <paths.h> header file. */
/* #undef HAVE_PATHS_H */

/* Define to 1 if you have the `poll' function. */
/* #undef HAVE_POLL */

/* Define to 1 if you have the <poll.h> header file. */
/* #undef HAVE_POLL_H */

/* Define to 1 if you have the <pwd.sh.h> header file. */
/* #undef HAVE_PWD_H */

/* Define to 1 if you have the `select' function. */
/* #undef HAVE_SELECT */

/* Define to 1 if you have the `setlocale' function. */
#define HAVE_SETLOCALE 1

/* Define to 1 if `stat' has the bug that it succeeds when given the
   zero-length file name argument. */
/* #undef HAVE_STAT_EMPTY_STRING_BUG */

/* Define to 1 if you have the <stdarg.h> header file. */
#define HAVE_STDARG_H 1

/* Define to 1 if you have the <stdint.h> header file. */
#if defined(_MSC_VER)
/* #undef HAVE_STDINT_H */
#else
#define HAVE_STDINT_H 1
#endif

/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the `strchr' function. */
#define HAVE_STRCHR 1

/* Define to 1 if you have the `strdup' function. */
#define HAVE_STRDUP 1

/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 1

/* Define to 1 if you have the `strerror_r' function. */
/* #undef HAVE_STRERROR_R */

/* Define to 1 if you have the `strftime' function. */
#define HAVE_STRFTIME 1

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1

/* Define to 1 if you have the `strrchr' function. */
#define HAVE_STRRCHR 1

/* Define to 1 if `st_mtimespec.tv_nsec' is member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC */

/* Define to 1 if `st_mtim.tv_nsec' is member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */

/* Define to 1 if you have the <sys/acl.h> header file. */
/* #undef HAVE_SYS_ACL_H */

/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
   */
/* #undef HAVE_SYS_DIR_H */

/* Define to 1 if you have the <sys/ioctl.h> header file. */
/* #undef HAVE_SYS_IOCTL_H */

/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
   */
/* #undef HAVE_SYS_NDIR_H */

/* Define to 1 if you have the <sys/param.h> header file. */
/* #undef HAVE_SYS_PARAM_H */

/* Define to 1 if you have the <sys/poll.h> header file. */
/* #undef HAVE_SYS_POLL_H */

/* Define to 1 if you have the <sys/select.h> header file. */
/* #undef HAVE_SYS_SELECT_H */

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#if defined(_MSC_VER)
/* #undef HAVE_SYS_TIME_H */
#else
#define HAVE_SYS_TIME_H 1
#endif

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1

/* Define to 1 if you have the <sys/utime.h> header file. */
#define HAVE_SYS_UTIME_H 1

/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
/* #undef HAVE_SYS_WAIT_H */

/* Define to 1 if you have the `timegm' function. */
/* #undef HAVE_TIMEGM */

/* Define to 1 if you have the <time.h> header file. */
#define HAVE_TIME_H 1

/* Define to 1 if the system has the type `uintmax_t'. */
#if defined(_MSC_VER)
/* #undef HAVE_UINTMAX_T */
#else
#define HAVE_UINTMAX_T 1
#endif

/* Define to 1 if you have the <unistd.h> header file. */
#if defined(_MSC_VER)
/* #undef HAVE_UNISTD_H */
#else
#define HAVE_UNISTD_H 1
#endif

/* Define to 1 if the system has the type `unsigned long long'. */
#define HAVE_UNSIGNED_LONG_LONG 1

/* Define to 1 if the system has the type `unsigned long long int'. */
#define HAVE_UNSIGNED_LONG_LONG_INT 1

/* Define to 1 if you have the `utime' function. */
#define HAVE_UTIME 1

/* Define to 1 if you have the `utimes' function. */
#define HAVE_UTIMES 1

/* Define to 1 if you have the <utime.h> header file. */
/* #undef HAVE_UTIME_H */

/* Define to 1 if you have the `vprintf' function. */
#define HAVE_VPRINTF 1

/* Define to 1 if you have the <wchar.h> header file. */
#define HAVE_WCHAR_H 1

/* Define to 1 if you have the `wcscpy' function. */
#define HAVE_WCSCPY 1

/* Define to 1 if you have the `wcslen' function. */
#define HAVE_WCSLEN 1

/* Define to 1 if you have the `wctomb' function. */
#define HAVE_WCTOMB 1

/* Define to 1 if you have the `wmemcmp' function. */
/* #undef HAVE_WMEMCMP */

/* Define to 1 if you have the `wmemcpy' function. */
/* #undef HAVE_WMEMCPY */

/* Define to 1 if you have the <zlib.h> header file. */
/* #undef HAVE_ZLIB_H */

/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
   slash. */
/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */

/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
   */
/* #undef MAJOR_IN_MKDEV */

/* Define to 1 if `major', `minor', and `makedev' are declared in
   <sysmacros.h>. */
/* #undef MAJOR_IN_SYSMACROS */

/* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O */

/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1

/* Define to 1 if strerror_r returns char *. */
/* #undef STRERROR_R_CHAR_P */

/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1

/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */

/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
/* #undef _LARGEFILE_SOURCE */

/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */

/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
   <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the
   #define  below would cause a syntax error. */
/* #undef _UINT64_T */

/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */

/* Define to `int' if <sys/types.h> doesn't define. */
#if (USE_UNSIGNED_GID)
#define gid_t unsigned int
#else
#define gid_t int
#endif

/* Define to `unsigned long' if <sys/types.h> does not define. */
#define id_t int

/* Define to the type of a signed integer type of width exactly 64 bits if
   such a type exists and the standard includes do not define it. */
#if defined(_MSC_VER)
#define int64_t __int64
#else
/* #undef int64_t */
#endif

/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do
   not define. */
#if defined(_MSC_VER)
#define intmax_t long long
#else
/* #undef intmax_t */
#endif

/* Define to `int' if <sys/types.h> does not define. */
#if defined(_MSC_VER)
#define mode_t unsigned short
#else
/* #undef mode_t */
#endif

/* Define to `long long' if <sys/types.h> does not define. */
/* #undef off_t */

/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */

/* Define to `int' if <sys/types.h> doesn't define. */
#if (USE_UNSIGNED_UID)
#define uid_t unsigned int
#else
#define uid_t int
#endif

/* Define to the type of an unsigned integer type of width exactly 64 bits if
   such a type exists and the standard includes do not define it. */
#if defined(_MSC_VER)
#define uint64_t unsigned __int64
#else
/* #undef uint64_t */
#endif

/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>
   do not define. */
#if defined(_MSC_VER)
#define uintmax_t unsigned long long
#else
/* #undef uintmax_t */
#endif

/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef uintptr_t */

/* Define to `unsigned int' if <sys/types.h> does not define. */
#if defined(_MSC_VER)
#define pid_t unsigned int
#else
/* #undef pid_t */
#endif

#if defined(_MSC_VER)
#define uint32_t unsigned long
#define uint16_t unsigned short
  #ifdef _WIN64
    #define ssize_t __int64
  #else
    #define ssize_t long
  #endif
#endif

#include "archive_windows.h"

#endif /* CONFIG_H_INCLUDED */

--- NEW FILE: mtree.5 ---
.\" Copyright (c) 1989, 1990, 1993
.\"     The Regents of the University of California.  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.
.\" 4. Neither the name of the University nor the names of its contributors
.\"    may be used to endorse or promote products derived from this software
.\"    without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\"     From: @(#)mtree.8       8.2 (Berkeley) 12/11/93
.\" $FreeBSD$
.\"
.Dd August 20, 2007
.Dt MTREE 5
.Os
.Sh NAME
.Nm mtree
.Nd format of mtree dir hierarchy files
.Sh DESCRIPTION
The
.Nm
format is a textual format that describes a collection of filesystem objects.
Such files are typically used to create or verify directory hierarchies.
.Ss General Format
An
.Nm
file consists of a series of lines, each providing information
about a single filesystem object.
Leading whitespace is always ignored.
.Pp
When encoding file or pathnames, any backslash character or
character outside of the 95 printable ASCII characters must be
encoded as a a backslash followed by three
octal digits.
When reading mtree files, any appearance of a backslash
followed by three octal digits should be converted into the
corresponding character.
.Pp
Each line is interpreted independently as one of the following types:
.Bl -tag -width Cm
.It Signature
The first line of any mtree file must begin with
.Dq #mtree .
If a file contains any full path entries, the first line should
begin with
.Dq #mtree v2.0 ,
otherwise, the first line should begin with
.Dq #mtree v1.0 .
.It Blank
Blank lines are ignored.
.It Comment
Lines beginning with
.Cm #
are ignored.
.It Special
Lines beginning with
.Cm /
are special commands that influence
the interpretation of later lines.
.It Relative
If the first whitespace-delimited word has no
.Cm /
characters,
it is the name of a file in the current directory.
Any relative entry that describes a directory changes the
current directory.
.It dot-dot
As a special case, a relative entry with the filename
.Pa ..
changes the current directory to the parent directory.
Options on dot-dot entries are always ignored.
.It Full
If the first whitespace-delimited word has a
.Cm /
character after
the first character, it is the pathname of a file relative to the
starting directory.
There can be multiple full entries describing the same file.
.El
.Pp
Some tools that process
.Nm
files may require that multiple lines describing the same file
occur consecutively.
It is not permitted for the same file to be mentioned using
both a relative and a full file specification.
.Ss Special commands
Two special commands are currently defined:
.Bl -tag -width Cm
.It Cm /set
This command defines default values for one or more keywords.
It is followed on the same line by one or more whitespace-separated
keyword definitions.
These definitions apply to all following files that do not specify
a value for that keyword.
.It Cm /unset
This command removes any default value set by a previous
.Cm /set
command.
It is followed on the same line by one or more keywords
separated by whitespace.
.El
.Ss Keywords
After the filename, a full or relative entry consists of zero
or more whitespace-separated keyword definitions.
Each such definition consists of a key from the following
list immediately followed by an '=' sign
and a value.
Software programs reading mtree files should warn about
unrecognized keywords.
.Pp
Currently supported keywords are as follows:
.Bl -tag -width Cm
.It Cm cksum
The checksum of the file using the default algorithm specified by
the
.Xr cksum 1
utility.
.It Cm contents
The full pathname of a file that holds the contents of this file.
.It Cm flags
The file flags as a symbolic name.
See
.Xr chflags 1
for information on these names.
If no flags are to be set the string
.Dq none
may be used to override the current default.
.It Cm gid
The file group as a numeric value.
.It Cm gname
The file group as a symbolic name.
.It Cm ignore
Ignore any file hierarchy below this file.
.It Cm link
The target of the symbolic link when type=link.
.It Cm md5
The MD5 message digest of the file.
.It Cm md5digest
A synonym for
.Cm md5 .
.It Cm mode
The current file's permissions as a numeric (octal) or symbolic
value.
.It Cm nlink
The number of hard links the file is expected to have.
.It Cm nochange
Make sure this file or directory exists but otherwise ignore all attributes.
.It Cm ripemd160digest
The
.Tn RIPEMD160
message digest of the file.
.It Cm rmd160
A synonym for
.Cm ripemd160digest .
.It Cm rmd160digest
A synonym for
.Cm ripemd160digest .
.It Cm sha1
The
.Tn FIPS
160-1
.Pq Dq Tn SHA-1
message digest of the file.
.It Cm sha1digest
A synonym for
.Cm sha1 .
.It Cm sha256
The
.Tn FIPS
180-2
.Pq Dq Tn SHA-256
message digest of the file.
.It Cm sha256digest
A synonym for
.Cm sha256 .
.It Cm size
The size, in bytes, of the file.
.It Cm time
The last modification time of the file.
.It Cm type
The type of the file; may be set to any one of the following:
.Pp
.Bl -tag -width Cm -compact
.It Cm block
block special device
.It Cm char
character special device
.It Cm dir
directory
.It Cm fifo
fifo
.It Cm file
regular file
.It Cm link
symbolic link
.It Cm socket
socket
.El
.It Cm uid
The file owner as a numeric value.
.It Cm uname
The file owner as a symbolic name.
.El
.Pp
.Sh SEE ALSO
.Xr cksum 1 ,
.Xr find 1 ,
.Xr mtree 8
.Sh BUGS
The
.Fx
implementation of mtree does not currently support
the
.Nm
2.0
format.
The requirement for a
.Dq #mtree
signature line is new and not yet widely implemented.
.Sh HISTORY
The
.Nm
utility appeared in
.Bx 4.3 Reno .
The
.Tn MD5
digest capability was added in
.Fx 2.1 ,
in response to the widespread use of programs which can spoof
.Xr cksum 1 .
The
.Tn SHA-1
and
.Tn RIPEMD160
digests were added in
.Fx 4.0 ,
as new attacks have demonstrated weaknesses in
.Tn MD5 .
The
.Tn SHA-256
digest was added in
.Fx 6.0 .
Support for file flags was added in
.Fx 4.0 ,
and mostly comes from
.Nx .
The
.Dq full
entry format was added by
.Nx .

--- NEW FILE: archive_read_open_memory.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $");

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "archive.h"

/*
 * Glue to read an archive from a block of memory.
 *
 * This is mostly a huge help in building test harnesses;
 * test programs can build archives in memory and read them
 * back again without having to mess with files on disk.
 */

struct read_memory_data {
    unsigned char   *buffer;
    unsigned char   *end;
    ssize_t  read_size;
};

static int  memory_read_close(struct archive *, void *);
static int  memory_read_open(struct archive *, void *);
#if ARCHIVE_API_VERSION < 2
static ssize_t  memory_read_skip(struct archive *, void *, size_t request);
#else
static off_t    memory_read_skip(struct archive *, void *, off_t request);
#endif
static ssize_t  memory_read(struct archive *, void *, const void **buff);

int
archive_read_open_memory(struct archive *a, void *buff, size_t size)
{
    return archive_read_open_memory2(a, buff, size, size);
}

/*
 * Don't use _open_memory2() in production code; the archive_read_open_memory()
 * version is the one you really want.  This is just here so that
 * test harnesses can exercise block operations inside the library.
 */
int
archive_read_open_memory2(struct archive *a, void *buff,
    size_t size, size_t read_size)
{
    struct read_memory_data *mine;

    mine = (struct read_memory_data *)malloc(sizeof(*mine));
    if (mine == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        return (ARCHIVE_FATAL);
    }
    memset(mine, 0, sizeof(*mine));
    mine->buffer = (unsigned char *)buff;
    mine->end = mine->buffer + size;
    mine->read_size = read_size;
    return (archive_read_open2(a, mine, memory_read_open,
            memory_read, memory_read_skip, memory_read_close));
}

/*
 * There's nothing to open.
 */
static int
memory_read_open(struct archive *a, void *client_data)
{
    (void)a; /* UNUSED */
    (void)client_data; /* UNUSED */
    return (ARCHIVE_OK);
}

/*
 * This is scary simple:  Just advance a pointer.  Limiting
 * to read_size is not technically necessary, but it exercises
 * more of the internal logic when used with a small block size
 * in a test harness.  Production use should not specify a block
 * size; then this is much faster.
 */
static ssize_t
memory_read(struct archive *a, void *client_data, const void **buff)
{
    struct read_memory_data *mine = (struct read_memory_data *)client_data;
    ssize_t size;

    (void)a; /* UNUSED */
    *buff = mine->buffer;
    size = mine->end - mine->buffer;
    if (size > mine->read_size)
        size = mine->read_size;
        mine->buffer += size;
    return (size);
}

/*
 * Advancing is just as simple.  Again, this is doing more than
 * necessary in order to better exercise internal code when used
 * as a test harness.
 */
#if ARCHIVE_API_VERSION < 2
static ssize_t
memory_read_skip(struct archive *a, void *client_data, size_t skip)
#else
static off_t
memory_read_skip(struct archive *a, void *client_data, off_t skip)
#endif
{
    struct read_memory_data *mine = (struct read_memory_data *)client_data;

    (void)a; /* UNUSED */
    if ((off_t)skip > (off_t)(mine->end - mine->buffer))
        skip = mine->end - mine->buffer;
    /* Round down to block size. */
    skip /= mine->read_size;
    skip *= mine->read_size;
    mine->buffer += skip;
    return (skip);
}

/*
 * Close is just cleaning up our one small bit of data.
 */
static int
memory_read_close(struct archive *a, void *client_data)
{
    struct read_memory_data *mine = (struct read_memory_data *)client_data;
    (void)a; /* UNUSED */
    free(mine);
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_write_set_format_mtree.c ---
/*-
 * Copyright (c) 2009 Michihiro NAKAJIMA
 * Copyright (c) 2008 Joerg Sonnenberger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
[...1011 lines suppressed...]
    mtree->keys = DEFAULT_KEYS;
    mtree->dironly = 0;
    mtree->indent = 0;
    archive_string_init(&mtree->ebuf);
    archive_string_init(&mtree->buf);
    a->format_data = mtree;
    a->format_destroy = archive_write_mtree_destroy;

    a->pad_uncompressed = 0;
    a->format_name = "mtree";
    a->format_options = archive_write_mtree_options;
    a->format_write_header = archive_write_mtree_header;
    a->format_finish = archive_write_mtree_finish;
    a->format_write_data = archive_write_mtree_data;
    a->format_finish_entry = archive_write_mtree_finish_entry;
    a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
    a->archive.archive_format_name = "mtree";

    return (ARCHIVE_OK);
}

--- NEW FILE: archive_read_support_compression_none.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.20 2008/12/06 06:45:15 kientzle Exp $");

#include "archive.h"

/*
 * Uncompressed streams are handled implicitly by the read core,
 * so this is now a no-op.
 */
int
archive_read_support_compression_none(struct archive *a)
{
    (void)a; /* UNUSED */
    return (ARCHIVE_OK);
}

--- NEW FILE: archive_write_open_file.c ---
/*-
 * Copyright (c) 2003-2007 Tim Kientzle
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "archive_platform.h"
__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#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 "archive.h"

struct write_FILE_data {
    FILE        *f;
};

static int  file_close(struct archive *, void *);
static int  file_open(struct archive *, void *);
static ssize_t  file_write(struct archive *, void *, const void *buff, size_t);

int
archive_write_open_FILE(struct archive *a, FILE *f)
{
    struct write_FILE_data *mine;

    mine = (struct write_FILE_data *)malloc(sizeof(*mine));
    if (mine == NULL) {
        archive_set_error(a, ENOMEM, "No memory");
        return (ARCHIVE_FATAL);
    }
    mine->f = f;
    return (archive_write_open(a, mine,
            file_open, file_write, file_close));
}

static int
file_open(struct archive *a, void *client_data)
{
    (void)a; /* UNUSED */
    (void)client_data; /* UNUSED */

    return (ARCHIVE_OK);
}

static ssize_t
file_write(struct archive *a, void *client_data, const void *buff, size_t length)
{
    struct write_FILE_data  *mine;
    size_t  bytesWritten;

    mine = client_data;
    bytesWritten = fwrite(buff, 1, length, mine->f);
    if (bytesWritten < length) {
        archive_set_error(a, errno, "Write error");
        return (-1);
    }
    return (bytesWritten);
}

static int
file_close(struct archive *a, void *client_data)
{
    struct write_FILE_data  *mine = client_data;

    (void)a; /* UNUSED */
    free(mine);
    return (ARCHIVE_OK);
}



More information about the Cmake-commits mailing list