[Cmake-commits] [cmake-commits] hoffman committed CMakeLists.txt NONE 1.1 Makefile NONE 1.1 bsdtar.1 NONE 1.1 bsdtar.c NONE 1.1 bsdtar.h NONE 1.1 bsdtar_platform.h NONE 1.1 bsdtar_windows.c NONE 1.1 bsdtar_windows.h NONE 1.1 cmdline.c NONE 1.1 config_freebsd.h NONE 1.1 getdate.c NONE 1.1 read.c NONE 1.1 subst.c NONE 1.1 tree.c NONE 1.1 tree.h NONE 1.1 util.c NONE 1.1 write.c NONE 1.1

cmake-commits at cmake.org cmake-commits at cmake.org
Fri Oct 30 13:10:54 EDT 2009


Update of /cvsroot/CMake/CMake/Utilities/cmlibarchive/tar
In directory public:/mounts/ram/cvs-serv26614/Utilities/cmlibarchive/tar

Added Files:
	CMakeLists.txt Makefile bsdtar.1 bsdtar.c bsdtar.h 
	bsdtar_platform.h bsdtar_windows.c bsdtar_windows.h cmdline.c 
	config_freebsd.h getdate.c read.c subst.c tree.c tree.h util.c 
	write.c 
Log Message:
Switch to using libarchive from libtar for cpack and cmake -E tar

This allows for a built in bzip and zip capability, so external tools 
will not be needed for these packagers.  The cmake -E tar xf should be
able to handle all compression types now as well.



--- NEW FILE: subst.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.
 */

#include "bsdtar_platform.h"
__FBSDID("$FreeBSD: src/usr.bin/tar/subst.c,v 1.4 2008/06/15 10:08:16 kientzle Exp $");

#if HAVE_REGEX_H
#include "bsdtar.h"

#include <errno.h>
#include <regex.h>
#include <stdlib.h>
#include <string.h>

#ifndef REG_BASIC
#define REG_BASIC 0
#endif

#include "err.h"

struct subst_rule {
    struct subst_rule *next;
    regex_t re;
    char *result;
    unsigned int global:1, print:1, symlink:1;
};

struct substitution {
    struct subst_rule *first_rule, *last_rule;
};

static void
init_substitution(struct bsdtar *bsdtar)
{
    struct substitution *subst;

    bsdtar->substitution = subst = malloc(sizeof(*subst));
    if (subst == NULL)
        lafe_errc(1, errno, "Out of memory");
    subst->first_rule = subst->last_rule = NULL;
}

void
add_substitution(struct bsdtar *bsdtar, const char *rule_text)
{
    struct subst_rule *rule;
    struct substitution *subst;
    const char *end_pattern, *start_subst;
    char *pattern;
    int r;

    if ((subst = bsdtar->substitution) == NULL) {
        init_substitution(bsdtar);
        subst = bsdtar->substitution;
    }

    rule = malloc(sizeof(*rule));
    if (rule == NULL)
        lafe_errc(1, errno, "Out of memory");
    rule->next = NULL;

    if (subst->last_rule == NULL)
        subst->first_rule = rule;
    else
        subst->last_rule->next = rule;
    subst->last_rule = rule;

    if (*rule_text == '\0')
        lafe_errc(1, 0, "Empty replacement string");
    end_pattern = strchr(rule_text + 1, *rule_text);
    if (end_pattern == NULL)
        lafe_errc(1, 0, "Invalid replacement string");

    pattern = malloc(end_pattern - rule_text);
    if (pattern == NULL)
        lafe_errc(1, errno, "Out of memory");
    memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
    pattern[end_pattern - rule_text - 1] = '\0';

    if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
        char buf[80];
        regerror(r, &rule->re, buf, sizeof(buf));
        lafe_errc(1, 0, "Invalid regular expression: %s", buf);
    }
    free(pattern);

    start_subst = end_pattern + 1;
    end_pattern = strchr(start_subst, *rule_text);
    if (end_pattern == NULL)
        lafe_errc(1, 0, "Invalid replacement string");

    rule->result = malloc(end_pattern - start_subst + 1);
    if (rule->result == NULL)
        lafe_errc(1, errno, "Out of memory");
    memcpy(rule->result, start_subst, end_pattern - start_subst);
    rule->result[end_pattern - start_subst] = '\0';

    rule->global = 0;
    rule->print = 0;
    rule->symlink = 0;

    while (*++end_pattern) {
        switch (*end_pattern) {
        case 'g':
        case 'G':
            rule->global = 1;
            break;
        case 'p':
        case 'P':
            rule->print = 1;
            break;
        case 's':
        case 'S':
            rule->symlink = 1;
            break;
        default:
            lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
        }
    }
}

static void
realloc_strncat(char **str, const char *append, size_t len)
{
    char *new_str;
    size_t old_len;

    if (*str == NULL)
        old_len = 0;
    else
        old_len = strlen(*str);

    new_str = malloc(old_len + len + 1);
    if (new_str == NULL)
        lafe_errc(1, errno, "Out of memory");
    memcpy(new_str, *str, old_len);
    memcpy(new_str + old_len, append, len);
    new_str[old_len + len] = '\0';
    free(*str);
    *str = new_str;
}

static void
realloc_strcat(char **str, const char *append)
{
    char *new_str;
    size_t old_len;

    if (*str == NULL)
        old_len = 0;
    else
        old_len = strlen(*str);

    new_str = malloc(old_len + strlen(append) + 1);
    if (new_str == NULL)
        lafe_errc(1, errno, "Out of memory");
    memcpy(new_str, *str, old_len);
    strcpy(new_str + old_len, append);
    free(*str);
    *str = new_str;
}

int
apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only)
{
    const char *path = name;
    regmatch_t matches[10];
    size_t i, j;
    struct subst_rule *rule;
    struct substitution *subst;
    int c, got_match, print_match;

    *result = NULL;

    if ((subst = bsdtar->substitution) == NULL)
        return 0;

    got_match = 0;
    print_match = 0;

    for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
        if (symlink_only && !rule->symlink)
            continue;
        if (regexec(&rule->re, name, 10, matches, 0))
            continue;

        got_match = 1;
        print_match |= rule->print;
        realloc_strncat(result, name, matches[0].rm_so);

        for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
            if (rule->result[i] == '~') {
                realloc_strncat(result, rule->result + j, i - j);
                realloc_strncat(result, name, matches[0].rm_eo);
                j = i + 1;
                continue;
            }
            if (rule->result[i] != '\\')
                continue;

            ++i;
            c = rule->result[i];
            switch (c) {
            case '~':
            case '\\':
                realloc_strncat(result, rule->result + j, i - j - 1);
                j = i;
                break;
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                realloc_strncat(result, rule->result + j, i - j - 1);
                if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
                    free(*result);
                    *result = NULL;
                    return -1;
                }
                realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
                j = i + 1;
                break;
            default:
                /* Just continue; */
                break;
            }

        }

        realloc_strcat(result, rule->result + j);

        name += matches[0].rm_eo;

        if (!rule->global)
            break;
    }

    if (got_match)
        realloc_strcat(result, name);

    if (print_match)
        fprintf(stderr, "%s >> %s\n", path, *result);

    return got_match;
}

void
cleanup_substitution(struct bsdtar *bsdtar)
{
    struct subst_rule *rule;
    struct substitution *subst;

    if ((subst = bsdtar->substitution) == NULL)
        return;

    while ((rule = subst->first_rule) != NULL) {
        subst->first_rule = rule->next;
        free(rule->result);
        free(rule);
    }
    free(subst);
}
#endif /* HAVE_REGEX_H */

--- NEW FILE: tree.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 is a new directory-walking system that addresses a number
 * of problems I've had with fts(3).  In particular, it has no
 * pathname-length limits (other than the size of 'int'), handles
 * deep logical traversals, uses considerably less memory, and has
 * an opaque interface (easier to modify in the future).
 *
 * Internally, it keeps a single list of "tree_entry" items that
 * represent filesystem objects that require further attention.
 * Non-directories are not kept in memory: they are pulled from
 * readdir(), returned to the client, then freed as soon as possible.
 * Any directory entry to be traversed gets pushed onto the stack.
 *
 * There is surprisingly little information that needs to be kept for
 * each item on the stack.  Just the name, depth (represented here as the
 * string length of the parent directory's pathname), and some markers
 * indicating how to get back to the parent (via chdir("..") for a
 * regular dir or via fchdir(2) for a symlink).
 */
#include "bsdtar_platform.h"
__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.9 2008/11/27 05:49:52 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.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
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
#include <windows.h>
#endif

#include "tree.h"

/*
 * TODO:
 *    1) Loop checking.
 *    3) Arbitrary logical traversals by closing/reopening intermediate fds.
 */

struct tree_entry {
    int depth;
    struct tree_entry *next;
    struct tree_entry *parent;
    char *name;
    size_t dirname_length;
    dev_t dev;
    ino_t ino;
    int flags;
    /* How to return back to the parent of a symlink. */
#ifdef HAVE_FCHDIR
    int symlink_parent_fd;
#elif defined(_WIN32) && !defined(__CYGWIN__)
    char *symlink_parent_path;
#else
#error fchdir function required.
#endif
};

/* Definitions for tree_entry.flags bitmap. */
#define isDir 1 /* This entry is a regular directory. */
#define isDirLink 2 /* This entry is a symbolic link to a directory. */
#define needsFirstVisit 4 /* This is an initial entry. */
#define needsDescent 8 /* This entry needs to be previsited. */
#define needsOpen 16 /* This is a directory that needs to be opened. */
#define needsAscent 32 /* This entry needs to be postvisited. */

/*
 * On Windows, "first visit" is handled as a pattern to be handed to
 * _findfirst().  This is consistent with Windows conventions that
 * file patterns are handled within the application.  On Posix,
 * "first visit" is just returned to the client.
 */

/*
 * Local data for this package.
 */
struct tree {
    struct tree_entry   *stack;
    struct tree_entry   *current;
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
    HANDLE d;
    BY_HANDLE_FILE_INFORMATION fileInfo;
#define INVALID_DIR_HANDLE INVALID_HANDLE_VALUE
    WIN32_FIND_DATA _findData;
    WIN32_FIND_DATA *findData;
#else
    DIR *d;
#define INVALID_DIR_HANDLE NULL
    struct dirent *de;
#endif
    int  flags;
    int  visit_type;
    int  tree_errno; /* Error code from last failed operation. */

    /* Dynamically-sized buffer for holding path */
    char    *buff;
    size_t   buff_length;

    const char *basename; /* Last path element */
    size_t   dirname_length; /* Leading dir length */
    size_t   path_length; /* Total path length */

    int  depth;
    int  openCount;
    int  maxOpenCount;

    struct stat lst;
    struct stat st;
};

/* Definitions for tree.flags bitmap. */
#define hasStat 16  /* The st entry is valid. */
#define hasLstat 32 /* The lst entry is valid. */
#define hasFileInfo 64 /* The Windows fileInfo entry is valid. */

#if defined(_WIN32) && !defined(__CYGWIN__)
static int
tree_dir_next_windows(struct tree *t, const char *pattern);
#else
static int
tree_dir_next_posix(struct tree *t);
#endif

#ifdef HAVE_DIRENT_D_NAMLEN
/* BSD extension; avoids need for a strlen() call. */
#define D_NAMELEN(dp)   (dp)->d_namlen
#else
#define D_NAMELEN(dp)   (strlen((dp)->d_name))
#endif

#include <stdio.h>
void
tree_dump(struct tree *t, FILE *out)
{
    char buff[300];
    struct tree_entry *te;

    fprintf(out, "\tdepth: %d\n", t->depth);
    fprintf(out, "\tbuff: %s\n", t->buff);
    fprintf(out, "\tpwd: %s\n", getcwd(buff, sizeof(buff)));
    fprintf(out, "\tbasename: %s\n", t->basename);
    fprintf(out, "\tstack:\n");
    for (te = t->stack; te != NULL; te = te->next) {
        fprintf(out, "\t\t%s%d:\"%s\" %s%s%s%s%s\n",
            t->current == te ? "*" : " ",
            te->depth,
            te->name,
            te->flags & needsFirstVisit ? "V" : "",
            te->flags & needsDescent ? "D" : "",
            te->flags & needsOpen ? "O" : "",
            te->flags & needsAscent ? "A" : "",
            (t->current == te && t->d) ? "+" : ""
        );
    }
}

/*
 * Add a directory path to the current stack.
 */
static void
tree_push(struct tree *t, const char *path)
{
    struct tree_entry *te;

    te = malloc(sizeof(*te));
    memset(te, 0, sizeof(*te));
    te->next = t->stack;
    te->parent = t->current;
    if (te->parent)
        te->depth = te->parent->depth + 1;
    t->stack = te;
#ifdef HAVE_FCHDIR
    te->symlink_parent_fd = -1;
    te->name = strdup(path);
#elif defined(_WIN32) && !defined(__CYGWIN__)
    te->symlink_parent_path = NULL;
    te->name = _strdup(path);
#endif
    te->flags = needsDescent | needsOpen | needsAscent;
    te->dirname_length = t->dirname_length;
}

/*
 * Append a name to the current dir path.
 */
static void
tree_append(struct tree *t, const char *name, size_t name_length)
{
    char *p;

    if (t->buff != NULL)
        t->buff[t->dirname_length] = '\0';
    /* Strip trailing '/' from name, unless entire name is "/". */
    while (name_length > 1 &&
        (name[name_length - 1] == '/' || name[name_length - 1] == '/'))
        name_length--;

    /* Resize pathname buffer as needed. */
    while (name_length + 1 + t->dirname_length >= t->buff_length) {
        t->buff_length *= 2;
        if (t->buff_length < 1024)
            t->buff_length = 1024;
        t->buff = realloc(t->buff, t->buff_length);
        if (t->buff == NULL)
            abort();
    }
    p = t->buff + t->dirname_length;
    t->path_length = t->dirname_length + name_length;
    /* Add a separating '/' if it's needed. */
    if (t->dirname_length > 0 && p[-1] != '/') {
        *p++ = '/';
        t->path_length ++;
    }
#if HAVE_STRNCPY_S
    strncpy_s(p, t->buff_length - (p - t->buff), name, name_length);
#else
    strncpy(p, name, name_length);
#endif
    p[name_length] = '\0';
    t->basename = p;
}

/*
 * Open a directory tree for traversal.
 */
struct tree *
tree_open(const char *path)
{
    struct tree *t;

    t = malloc(sizeof(*t));
    memset(t, 0, sizeof(*t));
//  tree_append(t, path, strlen(path));
    /* First item is set up a lot like a symlink traversal. */
    tree_push(t, path);
    t->stack->flags = needsFirstVisit | isDirLink;
#ifdef HAVE_FCHDIR
    t->stack->symlink_parent_fd = open(".", O_RDONLY);
    t->openCount++;
#elif defined(_WIN32) && !defined(__CYGWIN__)
    t->stack->symlink_parent_path = _getcwd(NULL, 0);
#endif
    t->d = INVALID_DIR_HANDLE;
    return (t);
}

/*
 * We've finished a directory; ascend back to the parent.
 */
static int
tree_ascend(struct tree *t)
{
    struct tree_entry *te;
    int r = 0;

    te = t->stack;
    t->depth--;
    if (te->flags & isDirLink) {
#ifdef HAVE_FCHDIR
        if (fchdir(te->symlink_parent_fd) != 0) {
            t->tree_errno = errno;
            r = TREE_ERROR_FATAL;
        }
        close(te->symlink_parent_fd);
#elif defined(_WIN32) && !defined(__CYGWIN__)
        if (SetCurrentDirectory(te->symlink_parent_path) == 0) {
            t->tree_errno = errno;
            r = TREE_ERROR_FATAL;
        }
        free(te->symlink_parent_path);
        te->symlink_parent_path = NULL;
#endif
        t->openCount--;
    } else {
#if defined(_WIN32) && !defined(__CYGWIN__)
        if (SetCurrentDirectory("..") == 0) {
#else
        if (chdir("..") != 0) {
#endif
            t->tree_errno = errno;
            r = TREE_ERROR_FATAL;
        }
    }
    return (r);
}

/*
 * Pop the working stack.
 */
static void
tree_pop(struct tree *t)
{
    struct tree_entry *te;

    if (t->buff)
        t->buff[t->dirname_length] = '\0';
    if (t->stack == t->current && t->current != NULL)
        t->current = t->current->parent;
    te = t->stack;
    t->stack = te->next;
    t->dirname_length = te->dirname_length;
    if (t->buff) {
        t->basename = t->buff + t->dirname_length;
        while (t->basename[0] == '/')
            t->basename++;
    }
    free(te->name);
    free(te);
}

/*
 * Get the next item in the tree traversal.
 */
int
tree_next(struct tree *t)
{
    int r;

    /* If we're called again after a fatal error, that's an API
     * violation.  Just crash now. */
    if (t->visit_type == TREE_ERROR_FATAL) {
        fprintf(stderr, "Unable to continue traversing"
            " directory heirarchy after a fatal error.");
        abort();
    }

    while (t->stack != NULL) {
        /* If there's an open dir, get the next entry from there. */
        if (t->d != INVALID_DIR_HANDLE) {
#if defined(_WIN32) && !defined(__CYGWIN__)
            r = tree_dir_next_windows(t, NULL);
#else
            r = tree_dir_next_posix(t);
#endif
            if (r == 0)
                continue;
            return (r);
        }

        if (t->stack->flags & needsFirstVisit) {
#if defined(_WIN32) && !defined(__CYGWIN__)
            char *d = strdup(t->stack->name);
            char *p, *pattern;
            //tree_pop(t);
            t->stack->flags &= ~needsFirstVisit;
            if (strchr(d, '*') || strchr(d, '?')) {
                // It has a wildcard in it...
                if ((p = strrchr(d, '\\')) != NULL) {
                    pattern = strdup(p + 1);
                    p[1] = '\0';
                    chdir(d);
                    tree_append(t, d, strlen(d));
                    free(d);
                } else {
                    pattern = d;
                }
                r = tree_dir_next_windows(t, pattern);
                free(pattern);
                if (r == 0)
                    continue;
                return (r);
            }
            // Not a pattern, handle it as-is...
#endif
            /* Top stack item needs a regular visit. */
            t->current = t->stack;
            tree_append(t, t->stack->name, strlen(t->stack->name));
            //t->dirname_length = t->path_length;
            //tree_pop(t);
            t->stack->flags &= ~needsFirstVisit;
            return (t->visit_type = TREE_REGULAR);
        } else if (t->stack->flags & needsDescent) {
            /* Top stack item is dir to descend into. */
            t->current = t->stack;
            tree_append(t, t->stack->name, strlen(t->stack->name));
            t->stack->flags &= ~needsDescent;
            /* If it is a link, set up fd for the ascent. */
            if (t->stack->flags & isDirLink) {
#ifdef HAVE_FCHDIR
                t->stack->symlink_parent_fd = open(".", O_RDONLY);
                t->openCount++;
                if (t->openCount > t->maxOpenCount)
                    t->maxOpenCount = t->openCount;
#elif defined(_WIN32) && !defined(__CYGWIN__)
                t->stack->symlink_parent_path = _getcwd(NULL, 0);
#endif
            }
            t->dirname_length = t->path_length;
#if defined(_WIN32) && !defined(__CYGWIN__)
            if (t->path_length == 259 || !SetCurrentDirectory(t->stack->name) != 0)
#else
            if (chdir(t->stack->name) != 0)
#endif
            {
                /* chdir() failed; return error */
                tree_pop(t);
                t->tree_errno = errno;
                return (t->visit_type = TREE_ERROR_DIR);
            }
            t->depth++;
            return (t->visit_type = TREE_POSTDESCENT);
        } else if (t->stack->flags & needsOpen) {
            t->stack->flags &= ~needsOpen;
#if defined(_WIN32) && !defined(__CYGWIN__)
            r = tree_dir_next_windows(t, "*");
#else
            r = tree_dir_next_posix(t);
#endif
            if (r == 0)
                continue;
            return (r);
        } else if (t->stack->flags & needsAscent) {
                /* Top stack item is dir and we're done with it. */
            r = tree_ascend(t);
            tree_pop(t);
            t->visit_type = r != 0 ? r : TREE_POSTASCENT;
            return (t->visit_type);
        } else {
            /* Top item on stack is dead. */
            tree_pop(t);
            t->flags &= ~hasLstat;
            t->flags &= ~hasStat;
        }
    }
    return (t->visit_type = 0);
}

#if defined(_WIN32) && !defined(__CYGWIN__)
static int
tree_dir_next_windows(struct tree *t, const char *pattern)
{
    const char *name;
    size_t namelen;
    int r;

    for (;;) {
        if (pattern != NULL) {
            t->d = FindFirstFile(pattern, &t->_findData);
            if (t->d == INVALID_DIR_HANDLE) {
                r = tree_ascend(t); /* Undo "chdir" */
                tree_pop(t);
                t->tree_errno = errno;
                t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
                return (t->visit_type);
            }
            t->findData = &t->_findData;
            pattern = NULL;
        } else if (!FindNextFile(t->d, &t->_findData)) {
            FindClose(t->d);
            t->d = INVALID_DIR_HANDLE;
            t->findData = NULL;
            return (0);
        }
        name = t->findData->cFileName;
        namelen = strlen(name);
        t->flags &= ~hasLstat;
        t->flags &= ~hasStat;
        if (name[0] == '.' && name[1] == '\0')
            continue;
        if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
            continue;
        tree_append(t, name, namelen);
        return (t->visit_type = TREE_REGULAR);
    }
}
#else
static int
tree_dir_next_posix(struct tree *t)
{
    int r;
    const char *name;
    size_t namelen;

    if (t->d == NULL) {
        if ((t->d = opendir(".")) == NULL) {
            r = tree_ascend(t); /* Undo "chdir" */
            tree_pop(t);
            t->tree_errno = errno;
            t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
            return (t->visit_type);
        }
    }
    for (;;) {
        t->de = readdir(t->d);
        if (t->de == NULL) {
            closedir(t->d);
            t->d = INVALID_DIR_HANDLE;
            return (0);
        }
        name = t->de->d_name;
        namelen = D_NAMELEN(t->de);
        t->flags &= ~hasLstat;
        t->flags &= ~hasStat;
        if (name[0] == '.' && name[1] == '\0')
            continue;
        if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
            continue;
        tree_append(t, name, namelen);
        return (t->visit_type = TREE_REGULAR);
    }
}
#endif

/*
 * Return error code.
 */
int
tree_errno(struct tree *t)
{
    return (t->tree_errno);
}

/*
 * Called by the client to mark the directory just returned from
 * tree_next() as needing to be visited.
 */
void
tree_descend(struct tree *t)
{
    if (t->visit_type != TREE_REGULAR)
        return;

    if (tree_current_is_physical_dir(t)) {
        tree_push(t, t->basename);
        t->stack->flags |= isDir;
    } else if (tree_current_is_dir(t)) {
        tree_push(t, t->basename);
        t->stack->flags |= isDirLink;
    }
}

/*
 * Get the stat() data for the entry just returned from tree_next().
 */
const struct stat *
tree_current_stat(struct tree *t)
{
    if (!(t->flags & hasStat)) {
        if (stat(tree_current_access_path(t), &t->st) != 0)
            return NULL;
        t->flags |= hasStat;
    }
    return (&t->st);
}

#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
const BY_HANDLE_FILE_INFORMATION *
tree_current_file_information(struct tree *t)
{
    if (!(t->flags & hasFileInfo)) {
        HANDLE h = CreateFile(tree_current_access_path(t),
            0, 0, NULL,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
            NULL);
        if (h == INVALID_HANDLE_VALUE)
            return NULL;
        if (!GetFileInformationByHandle(h, &t->fileInfo)) {
            CloseHandle(h);
            return NULL;
        }
        CloseHandle(h);
        t->flags |= hasFileInfo;
    }
    return (&t->fileInfo);
}
#endif
/*
 * Get the lstat() data for the entry just returned from tree_next().
 */
const struct stat *
tree_current_lstat(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
    return (tree_current_stat(t));
#else
    if (!(t->flags & hasLstat)) {
        if (lstat(tree_current_access_path(t), &t->lst) != 0)
            return NULL;
        t->flags |= hasLstat;
    }
    return (&t->lst);
#endif
}

/*
 * Test whether current entry is a dir or link to a dir.
 */
int
tree_current_is_dir(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
    if (t->findData)
        return (t->findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
    if (tree_current_file_information(t))
        return (t->fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
    return (0);
#else
    const struct stat *st;
    /*
     * If we already have lstat() info, then try some
     * cheap tests to determine if this is a dir.
     */
    if (t->flags & hasLstat) {
        /* If lstat() says it's a dir, it must be a dir. */
        if (S_ISDIR(tree_current_lstat(t)->st_mode))
            return 1;
        /* Not a dir; might be a link to a dir. */
        /* If it's not a link, then it's not a link to a dir. */
        if (!S_ISLNK(tree_current_lstat(t)->st_mode))
            return 0;
        /*
         * It's a link, but we don't know what it's a link to,
         * so we'll have to use stat().
         */
    }

    st = tree_current_stat(t);
    /* If we can't stat it, it's not a dir. */
    if (st == NULL)
        return 0;
    /* Use the definitive test.  Hopefully this is cached. */
    return (S_ISDIR(st->st_mode));
#endif
}

/*
 * Test whether current entry is a physical directory.  Usually, we
 * already have at least one of stat() or lstat() in memory, so we
 * use tricks to try to avoid an extra trip to the disk.
 */
int
tree_current_is_physical_dir(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
    if (tree_current_is_physical_link(t))
        return (0);
    return (tree_current_is_dir(t));
#else
    const struct stat *st;

    /*
     * If stat() says it isn't a dir, then it's not a dir.
     * If stat() data is cached, this check is free, so do it first.
     */
    if ((t->flags & hasStat)
        && (!S_ISDIR(tree_current_stat(t)->st_mode)))
        return 0;

    /*
     * Either stat() said it was a dir (in which case, we have
     * to determine whether it's really a link to a dir) or
     * stat() info wasn't available.  So we use lstat(), which
     * hopefully is already cached.
     */

    st = tree_current_lstat(t);
    /* If we can't stat it, it's not a dir. */
    if (st == NULL)
        return 0;
    /* Use the definitive test.  Hopefully this is cached. */
    return (S_ISDIR(st->st_mode));
#endif
}

/*
 * Test whether current entry is a symbolic link.
 */
int
tree_current_is_physical_link(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
    if (t->findData)
        return ((t->findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
                && (t->findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK));
    return (0);
#else
    const struct stat *st = tree_current_lstat(t);
    if (st == NULL)
        return 0;
    return (S_ISLNK(st->st_mode));
#endif
}

/*
 * Return the access path for the entry just returned from tree_next().
 */
const char *
tree_current_access_path(struct tree *t)
{
    return (t->basename);
}

/*
 * Return the full path for the entry just returned from tree_next().
 */
const char *
tree_current_path(struct tree *t)
{
    return (t->buff);
}

/*
 * Return the length of the path for the entry just returned from tree_next().
 */
size_t
tree_current_pathlen(struct tree *t)
{
    return (t->path_length);
}

/*
 * Return the nesting depth of the entry just returned from tree_next().
 */
int
tree_current_depth(struct tree *t)
{
    return (t->depth);
}

/*
 * Terminate the traversal and release any resources.
 */
void
tree_close(struct tree *t)
{
    /* Release anything remaining in the stack. */
    while (t->stack != NULL)
        tree_pop(t);
    if (t->buff)
        free(t->buff);
    /* TODO: Ensure that premature close() resets cwd */
#if 0
#ifdef HAVE_FCHDIR
    if (t->initialDirFd >= 0) {
        int s = fchdir(t->initialDirFd);
        (void)s; /* UNUSED */
        close(t->initialDirFd);
        t->initialDirFd = -1;
    }
#elif defined(_WIN32) && !defined(__CYGWIN__)
    if (t->initialDir != NULL) {
        SetCurrentDir(t->initialDir);
        free(t->initialDir);
        t->initialDir = NULL;
    }
#endif
#endif
    free(t);
}

--- NEW FILE: Makefile ---
# $FreeBSD: src/usr.bin/tar/Makefile,v 1.40 2008/12/06 07:38:14 kientzle Exp $

.PATH: ${.CURDIR}/../libarchive_fe

PROG=   bsdtar
BSDTAR_VERSION_STRING=2.7.900a
SRCS=   bsdtar.c cmdline.c getdate.c read.c subst.c tree.c util.c write.c
SRCS+=  err.c line_reader.c matching.c pathmatch.c
WARNS?= 5
DPADD=  ${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
LDADD=  -larchive -lbz2 -lz -lmd -lcrypto
CFLAGS+=    -DBSDTAR_VERSION_STRING=\"${BSDTAR_VERSION_STRING}\"
CFLAGS+=    -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
CFLAGS+=    -I"${.CURDIR}" -I"${.CURDIR}/../libarchive_fe"
SYMLINKS=   bsdtar ${BINDIR}/tar
MLINKS= bsdtar.1 tar.1
DEBUG_FLAGS=-g

.PHONY: check test
check test: $(PROG) bsdtar.1.gz
    cd ${.CURDIR}/test && make test

.include <bsd.prog.mk>

--- NEW FILE: CMakeLists.txt ---
############################################
#
# How to build bsdtar
#
############################################
IF (ENABLE_TAR)

  SET(bsdtar_SOURCES
    bsdtar.c
    bsdtar.h
    bsdtar_platform.h
    cmdline.c
    getdate.c
    read.c
    subst.c
    tree.c
    tree.h
    util.c
    write.c
    ../libarchive_fe/err.c
    ../libarchive_fe/err.h
    ../libarchive_fe/lafe_platform.h
    ../libarchive_fe/line_reader.c
    ../libarchive_fe/line_reader.h
    ../libarchive_fe/matching.c
    ../libarchive_fe/matching.h
    ../libarchive_fe/pathmatch.c
    ../libarchive_fe/pathmatch.h
  )
  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../libarchive_fe)
  IF(WIN32 AND NOT CYGWIN)
    LIST(APPEND bsdtar_SOURCES bsdtar_windows.c)
    LIST(APPEND bsdtar_SOURCES bsdtar_windows.h)
  ENDIF(WIN32 AND NOT CYGWIN)

  # bsdtar documentation
  SET(bsdtar_MANS bsdtar.1)

  # How to build bsdtar
  ADD_EXECUTABLE(bsdtar ${bsdtar_SOURCES})
  IF(ENABLE_TAR_SHARED)
    TARGET_LINK_LIBRARIES(bsdtar archive ${ADDITIONAL_LIBS})
  ELSE(ENABLE_TAR_SHARED)
    TARGET_LINK_LIBRARIES(bsdtar archive_static ${ADDITIONAL_LIBS})
  ENDIF(ENABLE_TAR_SHARED)
  # On Windows, DLL must end up in same dir with EXEs
  IF(WIN32 AND NOT CYGWIN)
    SET_TARGET_PROPERTIES(bsdtar PROPERTIES
      RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
  ENDIF(WIN32 AND NOT CYGWIN)
  GET_TARGET_PROPERTY(BSDTAR bsdtar LOCATION)

  # Installation rules
  INSTALL(TARGETS bsdtar RUNTIME DESTINATION bin)
  INSTALL_MAN(${bsdtar_MANS})
ENDIF(ENABLE_TAR)

add_subdirectory(test)

--- NEW FILE: bsdtar.1 ---
.\" Copyright (c) 2003-2007 Tim Kientzle
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.46 2008/12/06 07:37:55 kientzle Exp $
.\"
.Dd May 26, 2009
.Dt BSDTAR 1
.Os
.Sh NAME
.Nm tar
.Nd manipulate tape archives
.Sh SYNOPSIS
.Nm
.Op Ar bundled-flags Ao args Ac
.Op Ao Ar file Ac | Ao Ar pattern Ac ...
.Nm
.Brq Fl c
.Op Ar options
.Op Ar files | Ar directories
.Nm
.Brq Fl r | Fl u
.Fl f Ar archive-file
.Op Ar options
.Op Ar files | Ar directories
.Nm
.Brq Fl t | Fl x
.Op Ar options
.Op Ar patterns
.Sh DESCRIPTION
.Nm
creates and manipulates streaming archive files.
This implementation can extract from tar, pax, cpio, zip, jar, ar,
and ISO 9660 cdrom images and can create tar, pax, cpio, ar,
and shar archives.
.Pp
The first synopsis form shows a
.Dq bundled
option word.
This usage is provided for compatibility with historical implementations.
See COMPATIBILITY below for details.
.Pp
The other synopsis forms show the preferred usage.
The first option to
.Nm
is a mode indicator from the following list:
.Bl -tag -compact -width indent
.It Fl c
Create a new archive containing the specified items.
.It Fl r
Like
.Fl c ,
but new entries are appended to the archive.
Note that this only works on uncompressed archives stored in regular files.
The
.Fl f
option is required.
.It Fl t
List archive contents to stdout.
.It Fl u
Like
.Fl r ,
but new entries are added only if they have a modification date
newer than the corresponding entry in the archive.
Note that this only works on uncompressed archives stored in regular files.
The
.Fl f
option is required.
.It Fl x
Extract to disk from the archive.
If a file with the same name appears more than once in the archive,
each copy will be extracted, with later copies overwriting (replacing)
earlier copies.
.El
.Pp
In
.Fl c ,
.Fl r ,
or
.Fl u
mode, each specified file or directory is added to the
archive in the order specified on the command line.
By default, the contents of each directory are also archived.
.Pp
In extract or list mode, the entire command line
is read and parsed before the archive is opened.
The pathnames or patterns on the command line indicate
which items in the archive should be processed.
Patterns are shell-style globbing patterns as
documented in
.Xr tcsh 1 .
.Sh OPTIONS
Unless specifically stated otherwise, options are applicable in
all operating modes.
.Bl -tag -width indent
.It Cm @ Ns Pa archive
(c and r mode only)
The specified archive is opened and the entries
in it will be appended to the current archive.
As a simple example,
.Dl Nm Fl c Fl f Pa - Pa newfile Cm @ Ns Pa original.tar
writes a new archive to standard output containing a file
.Pa newfile
and all of the entries from
.Pa original.tar .
In contrast,
.Dl Nm Fl c Fl f Pa - Pa newfile Pa original.tar
creates a new archive with only two entries.
Similarly,
.Dl Nm Fl czf Pa - Fl -format Cm pax Cm @ Ns Pa -
reads an archive from standard input (whose format will be determined
automatically) and converts it into a gzip-compressed
pax-format archive on stdout.
In this way,
.Nm
can be used to convert archives from one format to another.
.It Fl b Ar blocksize
Specify the block size, in 512-byte records, for tape drive I/O.
As a rule, this argument is only needed when reading from or writing
to tape drives, and usually not even then as the default block size of
20 records (10240 bytes) is very common.
.It Fl C Ar directory
In c and r mode, this changes the directory before adding
the following files.
In x mode, change directories after opening the archive
but before extracting entries from the archive.
.It Fl -check-links
(c and r modes only)
Issue a warning message unless all links to each file are archived.
.It Fl -chroot
(x mode only)
.Fn chroot
to the current directory after processing any
.Fl C
options and before extracting any files.
.It Fl -exclude Ar pattern
Do not process files or directories that match the
specified pattern.
Note that exclusions take precedence over patterns or filenames
specified on the command line.
.It Fl -format Ar format
(c, r, u mode only)
Use the specified format for the created archive.
Supported formats include
.Dq cpio ,
.Dq pax ,
.Dq shar ,
and
.Dq ustar .
Other formats may also be supported; see
.Xr libarchive-formats 5
for more information about currently-supported formats.
In r and u modes, when extending an existing archive, the format specified
here must be compatible with the format of the existing archive on disk.
.It Fl f Ar file
Read the archive from or write the archive to the specified file.
The filename can be
.Pa -
for standard input or standard output.
If not specified, the default tape device will be used.
(On
.Fx ,
the default tape device is
.Pa /dev/sa0 . )
.It Fl H
(c and r mode only)
Symbolic links named on the command line will be followed; the
target of the link will be archived, not the link itself.
.It Fl h
(c and r mode only)
Synonym for
.Fl L .
.It Fl I
Synonym for
.Fl T .
.It Fl -include Ar pattern
Process only files or directories that match the specified pattern.
Note that exclusions specified with
.Fl -exclude
take precedence over inclusions.
If no inclusions are explicitly specified, all entries are processed by
default.
The
.Fl -include
option is especially useful when filtering archives.
For example, the command
.Dl Nm Fl c Fl f Pa new.tar Fl -include='*foo*' Cm @ Ns Pa old.tgz
creates a new archive
.Pa new.tar
containing only the entries from
.Pa old.tgz
containing the string
.Sq foo .
.It Fl j
(c mode only)
Compress the resulting archive with
.Xr bzip2 1 .
In extract or list modes, this option is ignored.
Note that, unlike other
.Nm tar
implementations, this implementation recognizes bzip2 compression
automatically when reading archives.
.It Fl k
(x mode only)
Do not overwrite existing files.
In particular, if a file appears more than once in an archive,
later copies will not overwrite earlier copies.
.It Fl -keep-newer-files
(x mode only)
Do not overwrite existing files that are newer than the
versions appearing in the archive being extracted.
.It Fl L
(c and r mode only)
All symbolic links will be followed.
Normally, symbolic links are archived as such.
With this option, the target of the link will be archived instead.
.It Fl l
This is a synonym for the
.Fl -check-links
option.
.It Fl m
(x mode only)
Do not extract modification time.
By default, the modification time is set to the time stored in the archive.
.It Fl n
(c, r, u modes only)
Do not recursively archive the contents of directories.
.It Fl -newer Ar date
(c, r, u modes only)
Only include files and directories newer than the specified date.
This compares ctime entries.
.It Fl -newer-mtime Ar date
(c, r, u modes only)
Like
.Fl -newer ,
except it compares mtime entries instead of ctime entries.
.It Fl -newer-than Pa file
(c, r, u modes only)
Only include files and directories newer than the specified file.
This compares ctime entries.
.It Fl -newer-mtime-than Pa file
(c, r, u modes only)
Like
.Fl -newer-than ,
except it compares mtime entries instead of ctime entries.
.It Fl -nodump
(c and r modes only)
Honor the nodump file flag by skipping this file.
.It Fl -null
(use with
.Fl I ,
.Fl T ,
or
.Fl X )
Filenames or patterns are separated by null characters,
not by newlines.
This is often used to read filenames output by the
.Fl print0
option to
.Xr find 1 .
.It Fl -numeric-owner
(x mode only)
Ignore symbolic user and group names when restoring archives to disk,
only numeric uid and gid values will be obeyed.
.It Fl O
(x, t modes only)
In extract (-x) mode, files will be written to standard out rather than
being extracted to disk.
In list (-t) mode, the file listing will be written to stderr rather than
the usual stdout.
.It Fl o
(x mode)
Use the user and group of the user running the program rather
than those specified in the archive.
Note that this has no significance unless
.Fl p
is specified, and the program is being run by the root user.
In this case, the file modes and flags from
the archive will be restored, but ACLs or owner information in
the archive will be discarded.
.It Fl o
(c, r, u mode)
A synonym for
.Fl -format Ar ustar
.It Fl -one-file-system
(c, r, and u modes)
Do not cross mount points.
.It Fl -options Ar options
Select optional behaviors for particular modules.
The argument is a text string containing comma-separated
keywords and values.
These are passed to the modules that handle particular
formats to control how those formats will behave.
Each option has one of the following forms:
.Bl -tag -compact -width indent
.It Ar key=value
The key will be set to the specified value in every module that supports it.
Modules that do not support this key will ignore it.
.It Ar key
The key will be enabled in every module that supports it.
This is equivalent to
.Ar key Ns Cm =1 .
.It Ar !key
The key will be disabled in every module that supports it.
.It Ar module:key=value , Ar module:key , Ar module:!key
As above, but the corresponding key and value will be provided
only to modules whose name matches
.Ar module .
.El
The currently supported modules and keys are:
.Bl -tag -compact -width indent
.It Cm iso9660:joliet
Support Joliet extensions.
This is enabled by default, use
.Cm !joliet
or
.Cm iso9660:!joliet
to disable.
.It Cm iso9660:rock-ridge
Support Rock Ridge extensions.
This is enabled by default, use
.Cm !rock-ridge
or
.Cm iso9660:!rock-ridge
to disable.
.It Cm gzip:compression-level
A decimal integer from 0 to 9 specifying the gzip compression level.
.It Cm xz:compression-level
A decimal integer from 0 to 9 specifying the xz compression level.
.It Cm mtree: Ns Ar keyword
The mtree writer module allows you to specify which mtree keywords
will be included in the output.
Supported keywords include:
.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 .
The default is equivalent to:
.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname .
.It Cm mtree:all
Enables all of the above keywords.
You can also use
.Cm mtree:!all
to disable all keywords.
.It Cm mtree:use-set
Enable generation of
.Cm /set
lines in the output.
.It Cm mtree:indent
Produce human-readable output by indenting options and splitting lines
to fit into 80 columns.
.It Cm zip:compression Ns = Ns Ar type
Use
.Ar type
as compression method.
Supported values are store (uncompressed) and deflate (gzip algorithm).
.El
If a provided option is not supported by any module, that
is a fatal error.
.It Fl P
Preserve pathnames.
By default, absolute pathnames (those that begin with a /
character) have the leading slash removed both when creating archives
and extracting from them.
Also,
.Nm
will refuse to extract archive entries whose pathnames contain
.Pa ..
or whose target directory would be altered by a symlink.
This option suppresses these behaviors.
.It Fl p
(x mode only)
Preserve file permissions.
Attempt to restore the full permissions, including owner, file modes, file
flags and ACLs, if available, for each item extracted from the archive.
By default, newly-created files are owned by the user running
.Nm ,
the file mode is restored for newly-created regular files, and
all other types of entries receive default permissions.
If
.Nm
is being run by root, the default is to restore the owner unless the
.Fl o
option is also specified.
.It Fl q ( Fl -fast-read )
(x and t mode only)
Extract or list only the first archive entry that matches each pattern
or filename operand.
Exit as soon as each specified pattern or filename has been matched.
By default, the archive is always read to the very end, since
there can be multiple entries with the same name and, by convention,
later entries overwrite earlier entries.
This option is provided as a performance optimization.
.It Fl S
(x mode only)
Extract files as sparse files.
For every block on disk, check first if it contains only NULL bytes and seek
over it otherwise.
This works similiar to the conv=sparse option of dd.
.It Fl -strip-components Ar count
(x mode only)
Remove the specified number of leading path elements.
Pathnames with fewer elements will be silently skipped.
Note that the pathname is edited after checking inclusion/exclusion patterns
but before security checks.
.It Fl s Ar pattern
Modify file or archive member names according to
.Pa pattern .
The pattern has the format
.Ar /old/new/ Ns Op gps
where
.Ar old
is a basic regular expression,
.Ar new
is the replacement string of the matched part,
and the optional trailing letters modify
how the replacement is handled.
If
.Ar old
is not matched, the pattern is skipped.
Within
.Ar new ,
~ is substituted with the match, \1 to \9 with the content of
the corresponding captured group.
The optional trailing g specifies that matching should continue
after the matched part and stopped on the first unmatched pattern.
The optional trailing s specifies that the pattern applies to the value
of symbolic links.
The optional trailing p specifies that after a successful substitution
the original path name and the new path name should be printed to
standard error.
.It Fl T Ar filename
In x or t mode,
.Nm
will read the list of names to be extracted from
.Pa filename .
In c mode,
.Nm
will read names to be archived from
.Pa filename .
The special name
.Dq -C
on a line by itself will cause the current directory to be changed to
the directory specified on the following line.
Names are terminated by newlines unless
.Fl -null
is specified.
Note that
.Fl -null
also disables the special handling of lines containing
.Dq -C .
.It Fl U
(x mode only)
Unlink files before creating them.
Without this option,
.Nm
overwrites existing files, which preserves existing hardlinks.
With this option, existing hardlinks will be broken, as will any
symlink that would affect the location of an extracted file.
.It Fl -use-compress-program Ar program
Pipe the input (in x or t mode) or the output (in c mode) through
.Pa program
instead of using the builtin compression support.
.It Fl v
Produce verbose output.
In create and extract modes,
.Nm
will list each file name as it is read from or written to
the archive.
In list mode,
.Nm
will produce output similar to that of
.Xr ls 1 .
Additional
.Fl v
options will provide additional detail.
.It Fl -version
Print version of
.Nm
and
.Nm libarchive ,
and exit.
.It Fl w
Ask for confirmation for every action.
.It Fl X Ar filename
Read a list of exclusion patterns from the specified file.
See
.Fl -exclude
for more information about the handling of exclusions.
.It Fl y
(c mode only)
Compress the resulting archive with
.Xr bzip2 1 .
In extract or list modes, this option is ignored.
Note that, unlike other
.Nm tar
implementations, this implementation recognizes bzip2 compression
automatically when reading archives.
.It Fl z
(c mode only)
Compress the resulting archive with
.Xr gzip 1 .
In extract or list modes, this option is ignored.
Note that, unlike other
.Nm tar
implementations, this implementation recognizes gzip compression
automatically when reading archives.
.It Fl Z
(c mode only)
Compress the resulting archive with
.Xr compress 1 .
In extract or list modes, this option is ignored.
Note that, unlike other
.Nm tar
implementations, this implementation recognizes compress compression
automatically when reading archives.
.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
.Nm :
.Bl -tag -width ".Ev BLOCKSIZE"
.It Ev LANG
The locale to use.
See
.Xr environ 7
for more information.
.It Ev TAPE
The default tape device.
The
.Fl f
option overrides this.
.It Ev TZ
The timezone to use when displaying dates.
See
.Xr environ 7
for more information.
.El
.Sh FILES
.Bl -tag -width ".Ev BLOCKSIZE"
.It Pa /dev/sa0
The default tape device, if not overridden by the
.Ev TAPE
environment variable or the
.Fl f
option.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
The following creates a new archive
called
.Ar file.tar.gz
that contains two files
.Ar source.c
and
.Ar source.h :
.Dl Nm Fl czf Pa file.tar.gz Pa source.c Pa source.h
.Pp
To view a detailed table of contents for this
archive:
.Dl Nm Fl tvf Pa file.tar.gz
.Pp
To extract all entries from the archive on
the default tape drive:
.Dl Nm Fl x
.Pp
To examine the contents of an ISO 9660 cdrom image:
.Dl Nm Fl tf Pa image.iso
.Pp
To move file hierarchies, invoke
.Nm
as
.Dl Nm Fl cf Pa - Fl C Pa srcdir\ . | Nm Fl xpf Pa - Fl C Pa destdir
or more traditionally
.Dl cd srcdir \&; Nm Fl cf Pa -\ . | ( cd destdir \&; Nm Fl xpf Pa - )
.Pp
In create mode, the list of files and directories to be archived
can also include directory change instructions of the form
.Cm -C Ns Pa foo/baz
and archive inclusions of the form
.Cm @ Ns Pa archive-file .
For example, the command line
.Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm -C Ns Pa /tmp Pa foo2
will create a new archive
.Pa new.tar .
.Nm
will read the file
.Pa foo1
from the current directory and add it to the output archive.
It will then read each entry from
.Pa old.tgz
and add those entries to the output archive.
Finally, it will switch to the
.Pa /tmp
directory and add
.Pa foo2
to the output archive.
.Pp
An input file in
.Xr mtree 5
format can be used to create an output archive with arbitrary ownership,
permissions, or names that differ from existing data on disk:
.Pp
.Dl $ cat input.mtree
.Dl #mtree
.Dl usr/bin uid=0 gid=0 mode=0755 type=dir
.Dl usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls
.Dl $ tar -cvf output.tar @input.mtree
.Pp
The
.Fl -newer
and
.Fl -newer-mtime
switches accept a variety of common date and time specifications, including
.Dq 12 Mar 2005 7:14:29pm ,
.Dq 2005-03-12 19:14 ,
.Dq 5 minutes ago ,
and
.Dq 19:14 PST May 1 .
.Pp
The
.Fl -options
argument can be used to control various details of archive generation
or reading.
For example, you can generate mtree output which only contains
.Cm type , Cm time ,
and
.Cm uid
keywords:
.Dl Nm Fl cf Pa file.tar Fl -format=mtree Fl -options='!all,type,time,uid' Pa dir
or you can set the compression level used by gzip or xz compression:
.Dl Nm Fl czf Pa file.tar Fl -options='compression-level=9' .
For more details, see the explanation of the
.Fn archive_read_set_options
and
.Fn archive_write_set_options
API calls that are described in
.Xr archive_read 3
and
.Xr archive_write 3 .
.Sh COMPATIBILITY
The bundled-arguments format is supported for compatibility
with historic implementations.
It consists of an initial word (with no leading - character) in which
each character indicates an option.
Arguments follow as separate words.
The order of the arguments must match the order
of the corresponding characters in the bundled command word.
For example,
.Dl Nm Cm tbf 32 Pa file.tar
specifies three flags
.Cm t ,
.Cm b ,
and
.Cm f .
The
.Cm b
and
.Cm f
flags both require arguments,
so there must be two additional items
on the command line.
The
.Ar 32
is the argument to the
.Cm b
flag, and
.Ar file.tar
is the argument to the
.Cm f
flag.
.Pp
The mode options c, r, t, u, and x and the options
b, f, l, m, o, v, and w comply with SUSv2.
.Pp
For maximum portability, scripts that invoke
.Nm tar
should use the bundled-argument format above, should limit
themselves to the
.Cm c ,
.Cm t ,
and
.Cm x
modes, and the
.Cm b ,
.Cm f ,
.Cm m ,
.Cm v ,
and
.Cm w
options.
.Pp
Additional long options are provided to improve compatibility with other
tar implementations.
.Sh SECURITY
Certain security issues are common to many archiving programs, including
.Nm .
In particular, carefully-crafted archives can request that
.Nm
extract files to locations outside of the target directory.
This can potentially be used to cause unwitting users to overwrite
files they did not intend to overwrite.
If the archive is being extracted by the superuser, any file
on the system can potentially be overwritten.
There are three ways this can happen.
Although
.Nm
has mechanisms to protect against each one,
savvy users should be aware of the implications:
.Bl -bullet -width indent
.It
Archive entries can have absolute pathnames.
By default,
.Nm
removes the leading
.Pa /
character from filenames before restoring them to guard against this problem.
.It
Archive entries can have pathnames that include
.Pa ..
components.
By default,
.Nm
will not extract files containing
.Pa ..
components in their pathname.
.It
Archive entries can exploit symbolic links to restore
files to other directories.
An archive can restore a symbolic link to another directory,
then use that link to restore a file into that directory.
To guard against this,
.Nm
checks each extracted path for symlinks.
If the final path element is a symlink, it will be removed
and replaced with the archive entry.
If
.Fl U
is specified, any intermediate symlink will also be unconditionally removed.
If neither
.Fl U
nor
.Fl P
is specified,
.Nm
will refuse to extract the entry.
.El
To protect yourself, you should be wary of any archives that
come from untrusted sources.
You should examine the contents of an archive with
.Dl Nm Fl tf Pa filename
before extraction.
You should use the
.Fl k
option to ensure that
.Nm
will not overwrite any existing files or the
.Fl U
option to remove any pre-existing files.
You should generally not extract archives while running with super-user
privileges.
Note that the
.Fl P
option to
.Nm
disables the security checks above and allows you to extract
an archive while preserving any absolute pathnames,
.Pa ..
components, or symlinks to other directories.
.Sh SEE ALSO
.Xr bzip2 1 ,
.Xr compress 1 ,
.Xr cpio 1 ,
.Xr gzip 1 ,
.Xr mt 1 ,
.Xr pax 1 ,
.Xr shar 1 ,
.Xr libarchive 3 ,
.Xr libarchive-formats 5 ,
.Xr tar 5
.Sh STANDARDS
There is no current POSIX standard for the tar command; it appeared
in
.St -p1003.1-96
but was dropped from
.St -p1003.1-2001 .
The options used by this implementation were developed by surveying a
number of existing tar implementations as well as the old POSIX specification
for tar and the current POSIX specification for pax.
.Pp
The ustar and pax interchange file formats are defined by
.St -p1003.1-2001
for the pax command.
.Sh HISTORY
A
.Nm tar
command appeared in Seventh Edition Unix, which was released in January, 1979.
There have been numerous other implementations,
many of which extended the file format.
John Gilmore's
.Nm pdtar
public-domain implementation (circa November, 1987)
was quite influential, and formed the basis of GNU tar.
GNU tar was included as the standard system tar
in
.Fx
beginning with
.Fx 1.0 .
.Pp
This is a complete re-implementation based on the
.Xr libarchive 3
library.
.Sh BUGS
This program follows
.St -p1003.1-96
for the definition of the
.Fl l
option.
Note that GNU tar prior to version 1.15 treated
.Fl l
as a synonym for the
.Fl -one-file-system
option.
.Pp
The
.Fl C Pa dir
option may differ from historic implementations.
.Pp
All archive output is written in correctly-sized blocks, even
if the output is being compressed.
Whether or not the last output block is padded to a full
block size varies depending on the format and the
output device.
For tar and cpio formats, the last block of output is padded
to a full block size if the output is being
written to standard output or to a character or block device such as
a tape drive.
If the output is being written to a regular file, the last block
will not be padded.
Many compressors, including
.Xr gzip 1
and
.Xr bzip2 1 ,
complain about the null padding when decompressing an archive created by
.Nm ,
although they still extract it correctly.
.Pp
The compression and decompression is implemented internally, so
there may be insignificant differences between the compressed output
generated by
.Dl Nm Fl czf Pa - file
and that generated by
.Dl Nm Fl cf Pa - file | Nm gzip
.Pp
The default should be to read and write archives to the standard I/O paths,
but tradition (and POSIX) dictates otherwise.
.Pp
The
.Cm r
and
.Cm u
modes require that the archive be uncompressed
and located in a regular file on disk.
Other archives can be modified using
.Cm c
mode with the
.Pa @archive-file
extension.
.Pp
To archive a file called
.Pa @foo
or
.Pa -foo
you must specify it as
.Pa ./@foo
or
.Pa ./-foo ,
respectively.
.Pp
In create mode, a leading
.Pa ./
is always removed.
A leading
.Pa /
is stripped unless the
.Fl P
option is specified.
.Pp
There needs to be better support for file selection on both create
and extract.
.Pp
There is not yet any support for multi-volume archives or for archiving
sparse files.
.Pp
Converting between dissimilar archive formats (such as tar and cpio) using the
.Cm @ Ns Pa -
convention can cause hard link information to be lost.
(This is a consequence of the incompatible ways that different archive
formats store hardlink information.)
.Pp
There are alternative long options for many of the short options that
are deliberately not documented.

--- NEW FILE: tree.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/usr.bin/tar/tree.h,v 1.4 2008/11/27 05:49:52 kientzle Exp $
 */

/*-
 * A set of routines for traversing directory trees.
 * Similar in concept to the fts library, but with a few
 * important differences:
 *    * Uses less memory.  In particular, fts stores an entire directory
 *      in memory at a time.  This package only keeps enough subdirectory
 *      information in memory to track the traversal.  Information
 *      about non-directories is discarded as soon as possible.
 *    * Supports very deep logical traversals.  The fts package
 *      uses "non-chdir" approach for logical traversals.  This
 *      package does use a chdir approach for logical traversals
 *      and can therefore handle pathnames much longer than PATH_MAX.
 *    * Supports deep physical traversals "out of the box."
 *      Due to the memory optimizations above, there's no need to
 *      limit dir names to 32k.
 */

#include <sys/stat.h>
#include <stdio.h>

struct tree;

/* Initiate/terminate a tree traversal. */
struct tree *tree_open(const char * /* pathname */);
void tree_close(struct tree *);

/*
 * tree_next() returns Zero if there is no next entry, non-zero if
 * there is.  Note that directories are visited three times.
 * Directories are always visited first as part of enumerating their
 * parent; that is a "regular" visit.  If tree_descend() is invoked at
 * that time, the directory is added to a work list and will
 * subsequently be visited two more times: once just after descending
 * into the directory ("postdescent") and again just after ascending
 * back to the parent ("postascent").
 *
 * TREE_ERROR_DIR is returned if the descent failed (because the
 * directory couldn't be opened, for instance).  This is returned
 * instead of TREE_POSTDESCENT/TREE_POSTASCENT.  TREE_ERROR_DIR is not a
 * fatal error, but it does imply that the relevant subtree won't be
 * visited.  TREE_ERROR_FATAL is returned for an error that left the
 * traversal completely hosed.  Right now, this is only returned for
 * chdir() failures during ascent.
 */
#define TREE_REGULAR    1
#define TREE_POSTDESCENT    2
#define TREE_POSTASCENT 3
#define TREE_ERROR_DIR  -1
#define TREE_ERROR_FATAL -2

int tree_next(struct tree *);

/* Errno value associated with the last traversal error. */
int tree_errno(struct tree *);

/*
 * Request that current entry be visited.  If you invoke it on every
 * directory, you'll get a physical traversal.  This is ignored if the
 * current entry isn't a directory or a link to a directory.  So, if
 * you invoke this on every returned path, you'll get a full logical
 * traversal.
 */
void tree_descend(struct tree *);

/*
 * Return information about the current entry.
 */

/* Current depth in the traversal. */
int tree_current_depth(struct tree *);

/*
 * The current full pathname, length of the full pathname, and a name
 * that can be used to access the file.  Because tree does use chdir
 * extensively, the access path is almost never the same as the full
 * current path.
 *
 * TODO: Flesh out this interface to provide other information.  In
 * particular, Windows can provide file size, mode, and some permission
 * information without invoking stat() at all.
 *
 * TODO: On platforms that support it, use openat()-style operations
 * to eliminate the chdir() operations entirely while still supporting
 * arbitrarily deep traversals.  This makes access_path troublesome to
 * support, of course, which means we'll need a rich enough interface
 * that clients can function without it.  (In particular, we'll need
 * tree_current_open() that returns an open file descriptor.)
 *
 * TODO: Provide tree_current_archive_entry().
 */
const char *tree_current_path(struct tree *);
size_t tree_current_pathlen(struct tree *);
const char *tree_current_access_path(struct tree *);

/*
 * Request the lstat() or stat() data for the current path.  Since the
 * tree package needs to do some of this anyway, and caches the
 * results, you should take advantage of it here if you need it rather
 * than make a redundant stat() or lstat() call of your own.
 */
const struct stat *tree_current_stat(struct tree *);
const struct stat *tree_current_lstat(struct tree *);

/* The following functions use tricks to avoid a certain number of
 * stat()/lstat() calls. */
/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
int tree_current_is_physical_dir(struct tree *);
/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */
int tree_current_is_physical_link(struct tree *);
/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
int tree_current_is_dir(struct tree *);

/* For testing/debugging: Dump the internal status to the given filehandle. */
void tree_dump(struct tree *, FILE *);

--- NEW FILE: bsdtar_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/usr.bin/tar/bsdtar_platform.h,v 1.26 2008/12/06 07:37:14 kientzle Exp $
 */

/*
 * This header is the first thing included in any of the bsdtar
 * source files.  As far as possible, platform-specific issues should
 * be dealt with here and not within individual source files.
 */

#ifndef BSDTAR_PLATFORM_H_INCLUDED
#define BSDTAR_PLATFORM_H_INCLUDED

#if defined(PLATFORM_CONFIG_H)
/* Use hand-built config.h in environments that need it. */
#include PLATFORM_CONFIG_H
#else
/* Not having a config.h of some sort is a serious problem. */
#include "config.h"
#endif

/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
#ifdef __FreeBSD__
#include <sys/cdefs.h>  /* For __FBSDID */
#else
/* Just leaving this macro replacement empty leads to a dangling semicolon. */
#define __FBSDID(a)     struct _undefined_hack
#endif

#ifdef HAVE_LIBARCHIVE
/* If we're using the platform libarchive, include system headers. */
#include <archive.h>
#include <archive_entry.h>
#else
/* Otherwise, include user headers. */
#include "archive.h"
#include "archive_entry.h"
#endif

#ifdef HAVE_LIBACL
#include <acl/libacl.h>
#endif

/*
 * Include "dirent.h" (or it's equivalent on several different platforms).
 *
 * This is slightly modified from the GNU autoconf recipe.
 * In particular, FreeBSD includes d_namlen in it's dirent structure,
 * so my configure script includes an explicit test for the d_namlen
 * field.
 */
#if HAVE_DIRENT_H
# include <dirent.h>
# if HAVE_DIRENT_D_NAMLEN
#  define DIRENT_NAMLEN(dirent) (dirent)->d_namlen
# else
#  define DIRENT_NAMLEN(dirent) strlen((dirent)->d_name)
# endif
#else
# define dirent direct
# define DIRENT_NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif


/*
 * We need to be able to display a filesize using printf().  The type
 * and format string here must be compatible with one another and
 * large enough for any file.
 */
#if HAVE_UINTMAX_T
#define BSDTAR_FILESIZE_TYPE    uintmax_t
#define BSDTAR_FILESIZE_PRINTF  "%ju"
#else
#if HAVE_UNSIGNED_LONG_LONG
#define BSDTAR_FILESIZE_TYPE    unsigned long long
#define BSDTAR_FILESIZE_PRINTF  "%llu"
#else
#define BSDTAR_FILESIZE_TYPE    unsigned long
#define BSDTAR_FILESIZE_PRINTF  "%lu"
#endif
#endif

#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
#define ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_ctimespec.tv_nsec
#define ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_mtimespec.tv_nsec
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
#define ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_ctim.tv_nsec
#define ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_mtim.tv_nsec
#elif HAVE_STRUCT_STAT_ST_MTIME_N
#define ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_ctime_n
#define ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_mtime_n
#elif HAVE_STRUCT_STAT_ST_UMTIME
#define ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_uctime * 1000
#define ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_umtime * 1000
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
#define ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_ctime_usec * 1000
#define ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_mtime_usec * 1000
#else
#define ARCHIVE_STAT_CTIME_NANOS(st)    (0)
#define ARCHIVE_STAT_MTIME_NANOS(st)    (0)
#endif

/* How to mark functions that don't return. */
/* This facilitates use of some newer static code analysis tools. */
#undef __LA_DEAD
#if defined(__GNUC__) && (__GNUC__ > 2 || \
              (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
#define __LA_DEAD   __attribute__((__noreturn__))
#else
#define __LA_DEAD
#endif

#if defined(_WIN32) && !defined(__CYGWIN__)
#include "bsdtar_windows.h"
#endif

#endif /* !BSDTAR_PLATFORM_H_INCLUDED */

--- NEW FILE: cmdline.c ---
/*-
 * 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.
 */

/*
 * Command line parser for tar.
 */

#include "bsdtar_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 "bsdtar.h"
#include "err.h"

/*
 * Short options for tar.  Please keep this sorted.
 */
static const char *short_options
    = "Bb:C:cf:HhI:JjkLlmnOoPpqrSs:T:tUuvW:wX:xyZz";

/*
 * Long options for tar.  Please keep this list sorted.
 *
 * The symbolic names for options that lack a short equivalent are
 * defined in bsdtar.h.  Also note that so far I've found no need
 * to support optional arguments to long options.  That would be
 * a small change to the code below.
 */

static struct option {
    const char *name;
    int required;      /* 1 if this option requires an argument. */
    int equivalent;    /* Equivalent short option. */
} tar_longopts[] = {
    { "absolute-paths",       0, 'P' },
    { "append",               0, 'r' },
    { "block-size",           1, 'b' },
    { "bunzip2",              0, 'j' },
    { "bzip",                 0, 'j' },
    { "bzip2",                0, 'j' },
    { "cd",                   1, 'C' },
    { "check-links",          0, OPTION_CHECK_LINKS },
    { "chroot",               0, OPTION_CHROOT },
    { "compress",             0, 'Z' },
    { "confirmation",         0, 'w' },
    { "create",               0, 'c' },
    { "dereference",      0, 'L' },
    { "directory",            1, 'C' },
    { "exclude",              1, OPTION_EXCLUDE },
    { "exclude-from",         1, 'X' },
    { "extract",              0, 'x' },
    { "fast-read",            0, 'q' },
    { "file",                 1, 'f' },
    { "files-from",           1, 'T' },
    { "format",               1, OPTION_FORMAT },
    { "options",              1, OPTION_OPTIONS },
    { "gunzip",               0, 'z' },
    { "gzip",                 0, 'z' },
    { "help",                 0, OPTION_HELP },
    { "include",              1, OPTION_INCLUDE },
    { "interactive",          0, 'w' },
    { "insecure",             0, 'P' },
    { "keep-newer-files",     0, OPTION_KEEP_NEWER_FILES },
    { "keep-old-files",       0, 'k' },
    { "list",                 0, 't' },
    { "lzma",                 0, OPTION_LZMA },
    { "modification-time",    0, 'm' },
    { "newer",        1, OPTION_NEWER_CTIME },
    { "newer-ctime",      1, OPTION_NEWER_CTIME },
    { "newer-ctime-than",     1, OPTION_NEWER_CTIME_THAN },
    { "newer-mtime",      1, OPTION_NEWER_MTIME },
    { "newer-mtime-than",     1, OPTION_NEWER_MTIME_THAN },
    { "newer-than",       1, OPTION_NEWER_CTIME_THAN },
    { "nodump",               0, OPTION_NODUMP },
    { "norecurse",            0, 'n' },
    { "no-recursion",         0, 'n' },
    { "no-same-owner",    0, OPTION_NO_SAME_OWNER },
    { "no-same-permissions",  0, OPTION_NO_SAME_PERMISSIONS },
    { "null",         0, OPTION_NULL },
    { "numeric-owner",    0, OPTION_NUMERIC_OWNER },
    { "one-file-system",      0, OPTION_ONE_FILE_SYSTEM },
    { "posix",        0, OPTION_POSIX },
    { "preserve-permissions", 0, 'p' },
    { "read-full-blocks",     0, 'B' },
    { "same-owner",           0, OPTION_SAME_OWNER },
    { "same-permissions",     0, 'p' },
    { "strip-components",     1, OPTION_STRIP_COMPONENTS },
    { "to-stdout",            0, 'O' },
    { "totals",       0, OPTION_TOTALS },
    { "uncompress",           0, 'Z' },
    { "unlink",       0, 'U' },
    { "unlink-first",     0, 'U' },
    { "update",               0, 'u' },
    { "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM },
    { "verbose",              0, 'v' },
    { "version",              0, OPTION_VERSION },
    { "xz",                   0, 'J' },
    { NULL, 0, 0 }
};

/*
 * This getopt implementation has two key features that common
 * getopt_long() implementations lack.  Apart from those, it's a
 * straightforward option parser, considerably simplified by not
 * needing to support the wealth of exotic getopt_long() features.  It
 * has, of course, been shamelessly tailored for bsdtar.  (If you're
 * looking for a generic getopt_long() implementation for your
 * project, I recommend Gregory Pietsch's public domain getopt_long()
 * implementation.)  The two additional features are:
 *
 * Old-style tar arguments: The original tar implementation treated
 * the first argument word as a list of single-character option
 * letters.  All arguments follow as separate words.  For example,
 *    tar xbf 32 /dev/tape
 * Here, the "xbf" is three option letters, "32" is the argument for
 * "b" and "/dev/tape" is the argument for "f".  We support this usage
 * if the first command-line argument does not begin with '-'.  We
 * also allow regular short and long options to follow, e.g.,
 *    tar xbf 32 /dev/tape -P --format=pax
 *
 * -W long options: There's an obscure GNU convention (only rarely
 * supported even there) that allows "-W option=argument" as an
 * alternative way to support long options.  This was supported in
 * early bsdtar as a way to access long options on platforms that did
 * not support getopt_long() and is preserved here for backwards
 * compatibility.  (Of course, if I'd started with a custom
 * command-line parser from the beginning, I would have had normal
 * long option support on every platform so that hack wouldn't have
 * been necessary.  Oh, well.  Some mistakes you just have to live
 * with.)
 *
 * TODO: We should be able to use this to pull files and intermingled
 * options (such as -C) from the command line in write mode.  That
 * will require a little rethinking of the argument handling in
 * bsdtar.c.
 *
 * TODO: If we want to support arbitrary command-line options from -T
 * input (as GNU tar does), we may need to extend this to handle option
 * words from sources other than argv/arc.  I'm not really sure if I
 * like that feature of GNU tar, so it's certainly not a priority.
 */

int
bsdtar_getopt(struct bsdtar *bsdtar)
{
    enum { state_start = 0, state_old_tar, state_next_word,
           state_short, state_long };
    static int state = state_start;
    static char *opt_word;

    const struct option *popt, *match = NULL, *match2 = NULL;
    const char *p, *long_prefix = "--";
    size_t optlength;
    int opt = '?';
    int required = 0;

    bsdtar->optarg = NULL;

    /* First time through, initialize everything. */
    if (state == state_start) {
        /* Skip program name. */
        ++bsdtar->argv;
        --bsdtar->argc;
        if (*bsdtar->argv == NULL)
            return (-1);
        /* Decide between "new style" and "old style" arguments. */
        if (bsdtar->argv[0][0] == '-') {
            state = state_next_word;
        } else {
            state = state_old_tar;
            opt_word = *bsdtar->argv++;
            --bsdtar->argc;
        }
    }

    /*
     * We're parsing old-style tar arguments
     */
    if (state == state_old_tar) {
        /* Get the next option character. */
        opt = *opt_word++;
        if (opt == '\0') {
            /* New-style args can follow old-style. */
            state = state_next_word;
        } else {
            /* See if it takes an argument. */
            p = strchr(short_options, opt);
            if (p == NULL)
                return ('?');
            if (p[1] == ':') {
                bsdtar->optarg = *bsdtar->argv;
                if (bsdtar->optarg == NULL) {
                    lafe_warnc(0,
                        "Option %c requires an argument",
                        opt);
                    return ('?');
                }
                ++bsdtar->argv;
                --bsdtar->argc;
            }
        }
    }

    /*
     * We're ready to look at the next word in argv.
     */
    if (state == state_next_word) {
        /* No more arguments, so no more options. */
        if (bsdtar->argv[0] == NULL)
            return (-1);
        /* Doesn't start with '-', so no more options. */
        if (bsdtar->argv[0][0] != '-')
            return (-1);
        /* "--" marks end of options; consume it and return. */
        if (strcmp(bsdtar->argv[0], "--") == 0) {
            ++bsdtar->argv;
            --bsdtar->argc;
            return (-1);
        }
        /* Get next word for parsing. */
        opt_word = *bsdtar->argv++;
        --bsdtar->argc;
        if (opt_word[1] == '-') {
            /* Set up long option parser. */
            state = state_long;
            opt_word += 2; /* Skip leading '--' */
        } else {
            /* Set up short option parser. */
            state = state_short;
            ++opt_word;  /* Skip leading '-' */
        }
    }

    /*
     * We're parsing a group of POSIX-style single-character options.
     */
    if (state == state_short) {
        /* Peel next option off of a group of short options. */
        opt = *opt_word++;
        if (opt == '\0') {
            /* End of this group; recurse to get next option. */
            state = state_next_word;
            return bsdtar_getopt(bsdtar);
        }

        /* Does this option take an argument? */
        p = strchr(short_options, opt);
        if (p == NULL)
            return ('?');
        if (p[1] == ':')
            required = 1;

        /* If it takes an argument, parse that. */
        if (required) {
            /* If arg is run-in, opt_word already points to it. */
            if (opt_word[0] == '\0') {
                /* Otherwise, pick up the next word. */
                opt_word = *bsdtar->argv;
                if (opt_word == NULL) {
                    lafe_warnc(0,
                        "Option -%c requires an argument",
                        opt);
                    return ('?');
                }
                ++bsdtar->argv;
                --bsdtar->argc;
            }
            if (opt == 'W') {
                state = state_long;
                long_prefix = "-W "; /* For clearer errors. */
            } else {
                state = state_next_word;
                bsdtar->optarg = opt_word;
            }
        }
    }

    /* We're reading a long option, including -W long=arg convention. */
    if (state == state_long) {
        /* After this long option, we'll be starting a new word. */
        state = state_next_word;

        /* Option name ends at '=' if there is one. */
        p = strchr(opt_word, '=');
        if (p != NULL) {
            optlength = (size_t)(p - opt_word);
            bsdtar->optarg = (char *)(uintptr_t)(p + 1);
        } else {
            optlength = strlen(opt_word);
        }

        /* Search the table for an unambiguous match. */
        for (popt = tar_longopts; popt->name != NULL; popt++) {
            /* Short-circuit if first chars don't match. */
            if (popt->name[0] != opt_word[0])
                continue;
            /* If option is a prefix of name in table, record it.*/
            if (strncmp(opt_word, popt->name, optlength) == 0) {
                match2 = match; /* Record up to two matches. */
                match = popt;
                /* If it's an exact match, we're done. */
                if (strlen(popt->name) == optlength) {
                    match2 = NULL; /* Forget the others. */
                    break;
                }
            }
        }

        /* Fail if there wasn't a unique match. */
        if (match == NULL) {
            lafe_warnc(0,
                "Option %s%s is not supported",
                long_prefix, opt_word);
            return ('?');
        }
        if (match2 != NULL) {
            lafe_warnc(0,
                "Ambiguous option %s%s (matches --%s and --%s)",
                long_prefix, opt_word, match->name, match2->name);
            return ('?');
        }

        /* We've found a unique match; does it need an argument? */
        if (match->required) {
            /* Argument required: get next word if necessary. */
            if (bsdtar->optarg == NULL) {
                bsdtar->optarg = *bsdtar->argv;
                if (bsdtar->optarg == NULL) {
                    lafe_warnc(0,
                        "Option %s%s requires an argument",
                        long_prefix, match->name);
                    return ('?');
                }
                ++bsdtar->argv;
                --bsdtar->argc;
            }
        } else {
            /* Argument forbidden: fail if there is one. */
            if (bsdtar->optarg != NULL) {
                lafe_warnc(0,
                    "Option %s%s does not allow an argument",
                    long_prefix, match->name);
                return ('?');
            }
        }
        return (match->equivalent);
    }

    return (opt);
}

--- 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/usr.bin/tar/config_freebsd.h,v 1.8 2008/11/29 20:06:53 kientzle Exp $
 */

/* A default configuration for FreeBSD, used if there is no config.h. */

#include <sys/param.h>  /* __FreeBSD_version */

#undef  HAVE_ATTR_XATTR_H
#define HAVE_CHROOT 1
#define HAVE_DIRENT_D_NAMLEN 1
#define HAVE_DIRENT_H 1
#define HAVE_D_MD_ORDER 1
#define HAVE_ERRNO_H 1
#undef  HAVE_EXT2FS_EXT2_FS_H
#define HAVE_FCHDIR 1
#define HAVE_FCNTL_H 1
#define HAVE_GRP_H 1
#define HAVE_LANGINFO_H 1
#undef  HAVE_LIBACL
#define HAVE_LIBARCHIVE 1
#define HAVE_LIMITS_H 1
#define HAVE_LINK 1
#undef  HAVE_LINUX_EXT2_FS_H
#undef  HAVE_LINUX_FS_H
#define HAVE_LOCALE_H 1
#define HAVE_MBTOWC 1
#undef  HAVE_NDIR_H
#if __FreeBSD_version >= 450002 /* nl_langinfo introduced */
#define HAVE_NL_LANGINFO 1
#endif
#define HAVE_PATHS_H 1
#define HAVE_PWD_H 1
#define HAVE_READLINK 1
#define HAVE_REGEX_H 1
#define HAVE_SETLOCALE 1
#define HAVE_SIGNAL_H 1
#define HAVE_STDARG_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRING_H 1
#define HAVE_STRUCT_STAT_ST_FLAGS 1
#undef  HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
#undef  HAVE_STRUCT_STAT_ST_MTIME_N
#undef  HAVE_STRUCT_STAT_ST_MTIME_USEC
#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
#undef  HAVE_STRUCT_STAT_ST_UMTIME
#define HAVE_SYMLINK 1
#undef  HAVE_SYS_DIR_H
#define HAVE_SYS_IOCTL_H 1
#undef  HAVE_SYS_NDIR_H
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_TIME_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_UINTMAX_T 1
#define HAVE_UNISTD_H 1
#define HAVE_UNSIGNED_LONG_LONG
#define HAVE_WCTYPE_H 1
#define HAVE_ZLIB_H 1
#undef  MAJOR_IN_MKDEV
#define STDC_HEADERS 1

--- NEW FILE: bsdtar.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/usr.bin/tar/bsdtar.h,v 1.37 2008/12/06 07:37:14 kientzle Exp $
 */

#include "bsdtar_platform.h"
#include <stdio.h>

#include "matching.h"

#define DEFAULT_BYTES_PER_BLOCK (20*512)

/*
 * The internal state for the "bsdtar" program.
 *
 * Keeping all of the state in a structure like this simplifies memory
 * leak testing (at exit, anything left on the heap is suspect).  A
 * pointer to this structure is passed to most bsdtar internal
 * functions.
 */
struct bsdtar {
    /* Options */
    const char   *filename; /* -f filename */
    const char   *create_format; /* -F format */
    char         *pending_chdir; /* -C dir */
    const char   *names_from_file; /* -T file */
    time_t        newer_ctime_sec; /* --newer/--newer-than */
    long          newer_ctime_nsec; /* --newer/--newer-than */
    time_t        newer_mtime_sec; /* --newer-mtime */
    long          newer_mtime_nsec; /* --newer-mtime-than */
    int       bytes_per_block; /* -b block_size */
    int       verbose;   /* -v */
    int       extract_flags; /* Flags for extract operation */
    int       strip_components; /* Remove this many leading dirs */
    char          mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */
    char          symlink_mode; /* H or L, per BSD conventions */
    char          create_compression; /* j, y, or z */
    const char   *compress_program;
    char          option_absolute_paths; /* -P */
    char          option_chroot; /* --chroot */
    char          option_dont_traverse_mounts; /* --one-file-system */
    char          option_fast_read; /* --fast-read */
    const char   *option_options; /* --options */
    char          option_honor_nodump; /* --nodump */
    char          option_interactive; /* -w */
    char          option_no_owner; /* -o */
    char          option_no_subdirs; /* -n */
    char          option_null; /* --null */
    char          option_numeric_owner; /* --numeric-owner */
    char          option_stdout; /* -O */
    char          option_totals; /* --totals */
    char          option_unlink_first; /* -U */
    char          option_warn_links; /* --check-links */
    char          day_first; /* show day before month in -tv output */

    /* If >= 0, then close this when done. */
    int       fd;

    /* Miscellaneous state information */
    int       argc;
    char        **argv;
    const char   *optarg;
    size_t        gs_width; /* For 'list_item' in read.c */
    size_t        u_width; /* for 'list_item' in read.c */
    uid_t         user_uid; /* UID running this program */
    int       return_value; /* Value returned by main() */
    char          warned_lead_slash; /* Already displayed warning */
    char          next_line_is_dir; /* Used for -C parsing in -cT */

    /*
     * Data for various subsystems.  Full definitions are located in
     * the file where they are used.
     */
    struct archive      *diskreader;    /* for write.c */
    struct archive_entry_linkresolver *resolver; /* for write.c */
    struct archive_dir  *archive_dir;   /* for write.c */
    struct name_cache   *gname_cache;   /* for write.c */
    char            *buff;      /* for write.c */
    struct lafe_matching    *matching;  /* for matching.c */
    struct security     *security;  /* for read.c */
    struct name_cache   *uname_cache;   /* for write.c */
    struct siginfo_data *siginfo;   /* for siginfo.c */
    struct substitution *substitution;  /* for subst.c */
};

/* Fake short equivalents for long options that otherwise lack them. */
enum {
    OPTION_CHECK_LINKS = 1,
    OPTION_CHROOT,
    OPTION_EXCLUDE,
    OPTION_FORMAT,
    OPTION_OPTIONS,
    OPTION_HELP,
    OPTION_INCLUDE,
    OPTION_KEEP_NEWER_FILES,
    OPTION_LZMA,
    OPTION_NEWER_CTIME,
    OPTION_NEWER_CTIME_THAN,
    OPTION_NEWER_MTIME,
    OPTION_NEWER_MTIME_THAN,
    OPTION_NODUMP,
    OPTION_NO_SAME_OWNER,
    OPTION_NO_SAME_PERMISSIONS,
    OPTION_NULL,
    OPTION_NUMERIC_OWNER,
    OPTION_ONE_FILE_SYSTEM,
    OPTION_POSIX,
    OPTION_SAME_OWNER,
    OPTION_STRIP_COMPONENTS,
    OPTION_TOTALS,
    OPTION_USE_COMPRESS_PROGRAM,
    OPTION_VERSION
};


int bsdtar_getopt(struct bsdtar *);
void    do_chdir(struct bsdtar *);
int edit_pathname(struct bsdtar *, struct archive_entry *);
int need_report(void);
int pathcmp(const char *a, const char *b);
void    safe_fprintf(FILE *, const char *fmt, ...);
void    set_chdir(struct bsdtar *, const char *newdir);
void    tar_mode_c(struct bsdtar *bsdtar);
void    tar_mode_r(struct bsdtar *bsdtar);
void    tar_mode_t(struct bsdtar *bsdtar);
void    tar_mode_u(struct bsdtar *bsdtar);
void    tar_mode_x(struct bsdtar *bsdtar);
void    usage(void);
int yes(const char *fmt, ...);

#if HAVE_REGEX_H
void    add_substitution(struct bsdtar *, const char *);
int apply_substitution(struct bsdtar *, const char *, char **, int);
void    cleanup_substitution(struct bsdtar *);
#endif

--- NEW FILE: util.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 "bsdtar_platform.h"
__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.23 2008/12/15 06:00:25 kientzle Exp $");

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>  /* Linux doesn't define mode_t, etc. in sys/stat.h. */
#endif
#include <ctype.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_WCTYPE_H
#include <wctype.h>
#else
/* If we don't have wctype, we need to hack up some version of iswprint(). */
#define iswprint isprint
#endif

#include "bsdtar.h"
#include "err.h"

static size_t   bsdtar_expand_char(char *, size_t, char);
static const char *strip_components(const char *path, int elements);

/* TODO:  Hack up a version of mbtowc for platforms with no wide
 * character support at all.  I think the following might suffice,
 * but it needs careful testing.
 * #if !HAVE_MBTOWC
 * #define mbtowc(wcp, p, n) ((*wcp = *p), 1)
 * #endif
 */

/*
 * Print a string, taking care with any non-printable characters.
 *
 * Note that we use a stack-allocated buffer to receive the formatted
 * string if we can.  This is partly performance (avoiding a call to
 * malloc()), partly out of expedience (we have to call vsnprintf()
 * before malloc() anyway to find out how big a buffer we need; we may
 * as well point that first call at a small local buffer in case it
 * works), but mostly for safety (so we can use this to print messages
 * about out-of-memory conditions).
 */

void
safe_fprintf(FILE *f, const char *fmt, ...)
{
    char fmtbuff_stack[256]; /* Place to format the printf() string. */
    char outbuff[256]; /* Buffer for outgoing characters. */
    char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */
    char *fmtbuff;  /* Pointer to fmtbuff_stack or fmtbuff_heap. */
    int fmtbuff_length;
    int length, n;
    va_list ap;
    const char *p;
    unsigned i;
    wchar_t wc;
    char try_wc;

    /* Use a stack-allocated buffer if we can, for speed and safety. */
    fmtbuff_heap = NULL;
    fmtbuff_length = sizeof(fmtbuff_stack);
    fmtbuff = fmtbuff_stack;

    /* Try formatting into the stack buffer. */
    va_start(ap, fmt);
    length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap);
    va_end(ap);

    /* If the result was too large, allocate a buffer on the heap. */
    if (length >= fmtbuff_length) {
        fmtbuff_length = length+1;
        fmtbuff_heap = malloc(fmtbuff_length);

        /* Reformat the result into the heap buffer if we can. */
        if (fmtbuff_heap != NULL) {
            fmtbuff = fmtbuff_heap;
            va_start(ap, fmt);
            length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap);
            va_end(ap);
        } else {
            /* Leave fmtbuff pointing to the truncated
             * string in fmtbuff_stack. */
            length = sizeof(fmtbuff_stack) - 1;
        }
    }

    /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit
     * more portable, so we use that here instead. */
    n = mbtowc(NULL, NULL, 1); /* Reset the shift state. */

    /* Write data, expanding unprintable characters. */
    p = fmtbuff;
    i = 0;
    try_wc = 1;
    while (*p != '\0') {

        /* Convert to wide char, test if the wide
         * char is printable in the current locale. */
        if (try_wc && (n = mbtowc(&wc, p, length)) != -1) {
            length -= n;
            if (iswprint(wc) && wc != L'\\') {
                /* Printable, copy the bytes through. */
                while (n-- > 0)
                    outbuff[i++] = *p++;
            } else {
                /* Not printable, format the bytes. */
                while (n-- > 0)
                    i += bsdtar_expand_char(
                        outbuff, i, *p++);
            }
        } else {
            /* After any conversion failure, don't bother
             * trying to convert the rest. */
            i += bsdtar_expand_char(outbuff, i, *p++);
            try_wc = 0;
        }

        /* If our output buffer is full, dump it and keep going. */
        if (i > (sizeof(outbuff) - 20)) {
            outbuff[i++] = '\0';
            fprintf(f, "%s", outbuff);
            i = 0;
        }
    }
    outbuff[i++] = '\0';
    fprintf(f, "%s", outbuff);

    /* If we allocated a heap-based formatting buffer, free it now. */
    if (fmtbuff_heap != NULL)
        free(fmtbuff_heap);
}

/*
 * Render an arbitrary sequence of bytes into printable ASCII characters.
 */
static size_t
bsdtar_expand_char(char *buff, size_t offset, char c)
{
    size_t i = offset;

    if (isprint((unsigned char)c) && c != '\\')
        buff[i++] = c;
    else {
        buff[i++] = '\\';
        switch (c) {
        case '\a': buff[i++] = 'a'; break;
        case '\b': buff[i++] = 'b'; break;
        case '\f': buff[i++] = 'f'; break;
        case '\n': buff[i++] = 'n'; break;
#if '\r' != '\n'
        /* On some platforms, \n and \r are the same. */
        case '\r': buff[i++] = 'r'; break;
#endif
        case '\t': buff[i++] = 't'; break;
        case '\v': buff[i++] = 'v'; break;
        case '\\': buff[i++] = '\\'; break;
        default:
            sprintf(buff + i, "%03o", 0xFF & (int)c);
            i += 3;
        }
    }

    return (i - offset);
}

int
yes(const char *fmt, ...)
{
    char buff[32];
    char *p;
    ssize_t l;

    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fprintf(stderr, " (y/N)? ");
    fflush(stderr);

    l = read(2, buff, sizeof(buff) - 1);
    if (l <= 0)
        return (0);
    buff[l] = 0;

    for (p = buff; *p != '\0'; p++) {
        if (isspace((unsigned char)*p))
            continue;
        switch(*p) {
        case 'y': case 'Y':
            return (1);
        case 'n': case 'N':
            return (0);
        default:
            return (0);
        }
    }

    return (0);
}

/*-
 * The logic here for -C <dir> attempts to avoid
 * chdir() as long as possible.  For example:
 * "-C /foo -C /bar file"          needs chdir("/bar") but not chdir("/foo")
 * "-C /foo -C bar file"           needs chdir("/foo/bar")
 * "-C /foo -C bar /file1"         does not need chdir()
 * "-C /foo -C bar /file1 file2"   needs chdir("/foo/bar") before file2
 *
 * The only correct way to handle this is to record a "pending" chdir
 * request and combine multiple requests intelligently until we
 * need to process a non-absolute file.  set_chdir() adds the new dir
 * to the pending list; do_chdir() actually executes any pending chdir.
 *
 * This way, programs that build tar command lines don't have to worry
 * about -C with non-existent directories; such requests will only
 * fail if the directory must be accessed.
 *
 * TODO: Make this handle Windows paths correctly.
 */
void
set_chdir(struct bsdtar *bsdtar, const char *newdir)
{
    if (newdir[0] == '/') {
        /* The -C /foo -C /bar case; dump first one. */
        free(bsdtar->pending_chdir);
        bsdtar->pending_chdir = NULL;
    }
    if (bsdtar->pending_chdir == NULL)
        /* Easy case: no previously-saved dir. */
        bsdtar->pending_chdir = strdup(newdir);
    else {
        /* The -C /foo -C bar case; concatenate */
        char *old_pending = bsdtar->pending_chdir;
        size_t old_len = strlen(old_pending);
        bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
        if (old_pending[old_len - 1] == '/')
            old_pending[old_len - 1] = '\0';
        if (bsdtar->pending_chdir != NULL)
            sprintf(bsdtar->pending_chdir, "%s/%s",
                old_pending, newdir);
        free(old_pending);
    }
    if (bsdtar->pending_chdir == NULL)
        lafe_errc(1, errno, "No memory");
}

void
do_chdir(struct bsdtar *bsdtar)
{
    if (bsdtar->pending_chdir == NULL)
        return;

    if (chdir(bsdtar->pending_chdir) != 0) {
        lafe_errc(1, 0, "could not chdir to '%s'\n",
            bsdtar->pending_chdir);
    }
    free(bsdtar->pending_chdir);
    bsdtar->pending_chdir = NULL;
}

const char *
strip_components(const char *p, int elements)
{
    /* Skip as many elements as necessary. */
    while (elements > 0) {
        switch (*p++) {
        case '/':
#if defined(_WIN32) && !defined(__CYGWIN__)
        case '\\': /* Support \ path sep on Windows ONLY. */
#endif
            elements--;
            break;
        case '\0':
            /* Path is too short, skip it. */
            return (NULL);
        }
    }

    /* Skip any / characters.  This handles short paths that have
     * additional / termination.  This also handles the case where
     * the logic above stops in the middle of a duplicate //
     * sequence (which would otherwise get converted to an
     * absolute path). */
    for (;;) {
        switch (*p) {
        case '/':
#if defined(_WIN32) && !defined(__CYGWIN__)
        case '\\': /* Support \ path sep on Windows ONLY. */
#endif
            ++p;
            break;
        case '\0':
            return (NULL);
        default:
            return (p);
        }
    }
}

/*
 * Handle --strip-components and any future path-rewriting options.
 * Returns non-zero if the pathname should not be extracted.
 *
 * TODO: Support pax-style regex path rewrites.
 */
int
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
{
    const char *name = archive_entry_pathname(entry);
#if HAVE_REGEX_H
    char *subst_name;
    int r;
#endif

#if HAVE_REGEX_H
    r = apply_substitution(bsdtar, name, &subst_name, 0);
    if (r == -1) {
        lafe_warnc(0, "Invalid substitution, skipping entry");
        return 1;
    }
    if (r == 1) {
        archive_entry_copy_pathname(entry, subst_name);
        if (*subst_name == '\0') {
            free(subst_name);
            return -1;
        } else
            free(subst_name);
        name = archive_entry_pathname(entry);
    }

    if (archive_entry_hardlink(entry)) {
        r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 1);
        if (r == -1) {
            lafe_warnc(0, "Invalid substitution, skipping entry");
            return 1;
        }
        if (r == 1) {
            archive_entry_copy_hardlink(entry, subst_name);
            free(subst_name);
        }
    }
    if (archive_entry_symlink(entry) != NULL) {
        r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1);
        if (r == -1) {
            lafe_warnc(0, "Invalid substitution, skipping entry");
            return 1;
        }
        if (r == 1) {
            archive_entry_copy_symlink(entry, subst_name);
            free(subst_name);
        }
    }
#endif

    /* Strip leading dir names as per --strip-components option. */
    if (bsdtar->strip_components > 0) {
        const char *linkname = archive_entry_hardlink(entry);

        name = strip_components(name, bsdtar->strip_components);
        if (name == NULL)
            return (1);

        if (linkname != NULL) {
            linkname = strip_components(linkname,
                bsdtar->strip_components);
            if (linkname == NULL)
                return (1);
            archive_entry_copy_hardlink(entry, linkname);
        }
    }

    /* By default, don't write or restore absolute pathnames. */
    if (!bsdtar->option_absolute_paths) {
        const char *rp, *p = name;
        int slashonly = 1;

        /* Remove leading "//./" or "//?/" or "//?/UNC/"
         * (absolute path prefixes used by Windows API) */
        if ((p[0] == '/' || p[0] == '\\') &&
            (p[1] == '/' || p[1] == '\\') &&
            (p[2] == '.' || p[2] == '?') &&
            (p[3] == '/' || p[3] == '\\'))
        {
            if (p[2] == '?' &&
                (p[4] == 'U' || p[4] == 'u') &&
                (p[5] == 'N' || p[5] == 'n') &&
                (p[6] == 'C' || p[6] == 'c') &&
                (p[7] == '/' || p[7] == '\\'))
                p += 8;
            else
                p += 4;
            slashonly = 0;
        }
        do {
            rp = p;
            /* Remove leading drive letter from archives created
             * on Windows. */
            if (((p[0] >= 'a' && p[0] <= 'z') ||
                 (p[0] >= 'A' && p[0] <= 'Z')) &&
                 p[1] == ':') {
                p += 2;
                slashonly = 0;
            }
            /* Remove leading "/../", "//", etc. */
            while (p[0] == '/' || p[0] == '\\') {
                if (p[1] == '.' && p[2] == '.' &&
                    (p[3] == '/' || p[3] == '\\')) {
                    p += 3; /* Remove "/..", leave "/"
                             * for next pass. */
                    slashonly = 0;
                } else
                    p += 1; /* Remove "/". */
            }
        } while (rp != p);

        if (p != name && !bsdtar->warned_lead_slash) {
            /* Generate a warning the first time this happens. */
            if (slashonly)
                lafe_warnc(0,
                    "Removing leading '%c' from member names",
                    name[0]);
            else
                lafe_warnc(0,
                    "Removing leading drive letter from "
                    "member names");
            bsdtar->warned_lead_slash = 1;
        }

        /* Special case: Stripping everything yields ".". */
        if (*p == '\0')
            name = ".";
        else
            name = p;
    } else {
        /* Strip redundant leading '/' characters. */
        while (name[0] == '/' && name[1] == '/')
            name++;
    }

    /* Safely replace name in archive_entry. */
    if (name != archive_entry_pathname(entry)) {
        char *q = strdup(name);
        archive_entry_copy_pathname(entry, q);
        free(q);
    }
    return (0);
}

/*
 * Like strcmp(), but try to be a little more aware of the fact that
 * we're comparing two paths.  Right now, it just handles leading
 * "./" and trailing '/' specially, so that "a/b/" == "./a/b"
 *
 * TODO: Make this better, so that "./a//b/./c/" == "a/b/c"
 * TODO: After this works, push it down into libarchive.
 * TODO: Publish the path normalization routines in libarchive so
 * that bsdtar can normalize paths and use fast strcmp() instead
 * of this.
 *
 * Note: This is currently only used within write.c, so should
 * not handle \ path separators.
 */

int
pathcmp(const char *a, const char *b)
{
    /* Skip leading './' */
    if (a[0] == '.' && a[1] == '/' && a[2] != '\0')
        a += 2;
    if (b[0] == '.' && b[1] == '/' && b[2] != '\0')
        b += 2;
    /* Find the first difference, or return (0) if none. */
    while (*a == *b) {
        if (*a == '\0')
            return (0);
        a++;
        b++;
    }
    /*
     * If one ends in '/' and the other one doesn't,
     * they're the same.
     */
    if (a[0] == '/' && a[1] == '\0' && b[0] == '\0')
        return (0);
    if (a[0] == '\0' && b[0] == '/' && b[1] == '\0')
        return (0);
    /* They're really different, return the correct sign. */
    return (*(const unsigned char *)a - *(const unsigned char *)b);
}

--- NEW FILE: bsdtar_windows.h ---
/*-
 * 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.
 *
 * $FreeBSD$
 */

#ifndef BSDTAR_WINDOWS_H
#define BSDTAR_WINDOWS_H 1
#include <direct.h>
#include <windows.h>

#ifndef PRId64
#define PRId64 "I64"
#endif
#define geteuid()   0

#ifndef S_IFIFO
#define S_IFIFO 0010000 /* pipe */
#endif

#include <string.h>  /* Must include before redefining 'strdup' */
#define strdup _strdup
#define read _read
#define getcwd _getcwd

#define chdir __tar_chdir
int __tar_chdir(const char *);

#ifndef S_ISREG
#define S_ISREG(a)  (a & _S_IFREG)
#endif
#ifndef S_ISBLK
#define S_ISBLK(a)  (0)
#endif

#endif /* BSDTAR_WINDOWS_H */

--- NEW FILE: 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,
[...1090 lines suppressed...]

    if (bsdtar->create_compression != 0)
        lafe_errc(1, 0,
            "Cannot append to %s with compression", bsdtar->filename);

    if (stat(bsdtar->filename, &s) != 0)
        return;

    if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
        lafe_errc(1, 0,
            "Cannot append to %s: not a regular file.",
            bsdtar->filename);

/* Is this an appropriate check here on Windows? */
/*
    if (GetFileType(handle) != FILE_TYPE_DISK)
        lafe_errc(1, 0, "Cannot append");
*/

}

--- NEW FILE: getdate.c ---
/*
 * This code is in the public domain and has no copyright.
 *
 * This is a plain C recursive-descent translation of an old
 * public-domain YACC grammar that has been used for parsing dates in
 * very many open-source projects.
 *
 * Since the original authors were generous enough to donate their
 * work to the public domain, I feel compelled to match their
 * generosity.
 *
 * Tim Kientzle, February 2009.
 */

/*
 * Header comment from original getdate.y:
 */

/*
[...998 lines suppressed...]
#if defined(TEST)

/* ARGSUSED */
int
main(int argc, char **argv)
{
    time_t  d;

    while (*++argv != NULL) {
        (void)printf("Input: %s\n", *argv);
        d = get_date(*argv);
        if (d == -1)
            (void)printf("Bad format - couldn't convert.\n");
        else
            (void)printf("Output: %s\n", ctime(&d));
    }
    exit(0);
    /* NOTREACHED */
}
#endif  /* defined(TEST) */

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

#include "bsdtar_platform.h"
__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.93 2008/11/08 04:43:24 kientzle Exp $");

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#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_LANGINFO_H
#include <langinfo.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_ZLIB_H
#include <cm_zlib.h>
#endif

#include "bsdtar.h"
#include "err.h"

/*
 * Per POSIX.1-1988, tar defaults to reading/writing archives to/from
 * the default tape device for the system.  Pick something reasonable here.
 */
#ifdef __linux
#define _PATH_DEFTAPE "/dev/st0"
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
#define _PATH_DEFTAPE "\\\\.\\tape0"
#endif

#ifndef _PATH_DEFTAPE
#define _PATH_DEFTAPE "/dev/tape"
#endif

static struct bsdtar *_bsdtar;

#if defined(SIGINFO) || defined(SIGUSR1)
static volatile int siginfo_occurred;

static void
siginfo_handler(int sig)
{
    (void)sig; /* UNUSED */
    siginfo_occurred = 1;
}

int
need_report(void)
{
    int r = siginfo_occurred;
    siginfo_occurred = 0;
    return (r);
}
#else
int
need_report(void)
{
    return (0);
}
#endif

/* External function to parse a date/time string */
time_t get_date(time_t, const char *);

static void      long_help(void);
static void      only_mode(struct bsdtar *, const char *opt,
                 const char *valid);
static void      set_mode(struct bsdtar *, char opt);
static void      version(void);

/* A basic set of security flags to request from libarchive. */
#define SECURITY                    \
    (ARCHIVE_EXTRACT_SECURE_SYMLINKS        \
     | ARCHIVE_EXTRACT_SECURE_NODOTDOT)

int
main(int argc, char **argv)
{
    struct bsdtar       *bsdtar, bsdtar_storage;
    int          opt, t;
    char             option_o;
    char             possible_help_request;
    char             buff[16];
    time_t           now;

    /*
     * Use a pointer for consistency, but stack-allocated storage
     * for ease of cleanup.
     */
    _bsdtar = bsdtar = &bsdtar_storage;
    memset(bsdtar, 0, sizeof(*bsdtar));
    bsdtar->fd = -1; /* Mark as "unused" */
    option_o = 0;

#if defined(SIGINFO) || defined(SIGUSR1)
    { /* Catch SIGINFO and SIGUSR1, if they exist. */
        struct sigaction sa;
        sa.sa_handler = siginfo_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
#ifdef SIGINFO
        if (sigaction(SIGINFO, &sa, NULL))
            lafe_errc(1, errno, "sigaction(SIGINFO) failed");
#endif
#ifdef SIGUSR1
        /* ... and treat SIGUSR1 the same way as SIGINFO. */
        if (sigaction(SIGUSR1, &sa, NULL))
            lafe_errc(1, errno, "sigaction(SIGUSR1) failed");
#endif
    }
#endif


    /* Need lafe_progname before calling lafe_warnc. */
    if (*argv == NULL)
        lafe_progname = "bsdtar";
    else {
#if defined(_WIN32) && !defined(__CYGWIN__)
        lafe_progname = strrchr(*argv, '\\');
#else
        lafe_progname = strrchr(*argv, '/');
#endif
        if (lafe_progname != NULL)
            lafe_progname++;
        else
            lafe_progname = *argv;
    }

    time(&now);

#if HAVE_SETLOCALE
    if (setlocale(LC_ALL, "") == NULL)
        lafe_warnc(0, "Failed to set default locale");
#endif
#if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER)
    bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd');
#endif
    possible_help_request = 0;

    /* Look up uid of current user for future reference */
    bsdtar->user_uid = geteuid();

    /* Default: open tape drive. */
    bsdtar->filename = getenv("TAPE");
    if (bsdtar->filename == NULL)
        bsdtar->filename = _PATH_DEFTAPE;

    /* Default: preserve mod time on extract */
    bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;

    /* Default: Perform basic security checks. */
    bsdtar->extract_flags |= SECURITY;

#ifndef _WIN32
    /* On POSIX systems, assume --same-owner and -p when run by
     * the root user.  This doesn't make any sense on Windows. */
    if (bsdtar->user_uid == 0) {
        /* --same-owner */
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
        /* -p */
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
    }
#endif

    bsdtar->argv = argv;
    bsdtar->argc = argc;

    /*
     * Comments following each option indicate where that option
     * originated:  SUSv2, POSIX, GNU tar, star, etc.  If there's
     * no such comment, then I don't know of anyone else who
     * implements that option.
     */
    while ((opt = bsdtar_getopt(bsdtar)) != -1) {
        switch (opt) {
        case 'B': /* GNU tar */
            /* libarchive doesn't need this; just ignore it. */
            break;
        case 'b': /* SUSv2 */
            t = atoi(bsdtar->optarg);
            if (t <= 0 || t > 1024)
                lafe_errc(1, 0,
                    "Argument to -b is out of range (1..1024)");
            bsdtar->bytes_per_block = 512 * t;
            break;
        case 'C': /* GNU tar */
            set_chdir(bsdtar, bsdtar->optarg);
            break;
        case 'c': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case OPTION_CHECK_LINKS: /* GNU tar */
            bsdtar->option_warn_links = 1;
            break;
        case OPTION_CHROOT: /* NetBSD */
            bsdtar->option_chroot = 1;
            break;
        case OPTION_EXCLUDE: /* GNU tar */
            if (lafe_exclude(&bsdtar->matching, bsdtar->optarg))
                lafe_errc(1, 0,
                    "Couldn't exclude %s\n", bsdtar->optarg);
            break;
        case OPTION_FORMAT: /* GNU tar, others */
            bsdtar->create_format = bsdtar->optarg;
            break;
        case OPTION_OPTIONS:
            bsdtar->option_options = bsdtar->optarg;
            break;
        case 'f': /* SUSv2 */
            bsdtar->filename = bsdtar->optarg;
            if (strcmp(bsdtar->filename, "-") == 0)
                bsdtar->filename = NULL;
            break;
        case 'H': /* BSD convention */
            bsdtar->symlink_mode = 'H';
            break;
        case 'h': /* Linux Standards Base, gtar; synonym for -L */
            bsdtar->symlink_mode = 'L';
            /* Hack: -h by itself is the "help" command. */
            possible_help_request = 1;
            break;
        case OPTION_HELP: /* GNU tar, others */
            long_help();
            exit(0);
            break;
        case 'I': /* GNU tar */
            /*
             * TODO: Allow 'names' to come from an archive,
             * not just a text file.  Design a good UI for
             * allowing names and mode/owner to be read
             * from an archive, with contents coming from
             * disk.  This can be used to "refresh" an
             * archive or to design archives with special
             * permissions without having to create those
             * permissions on disk.
             */
            bsdtar->names_from_file = bsdtar->optarg;
            break;
        case OPTION_INCLUDE:
            /*
             * Noone else has the @archive extension, so
             * noone else needs this to filter entries
             * when transforming archives.
             */
            if (lafe_include(&bsdtar->matching, bsdtar->optarg))
                lafe_errc(1, 0,
                    "Failed to add %s to inclusion list",
                    bsdtar->optarg);
            break;
        case 'j': /* GNU tar */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'J': /* GNU tar 1.21 and later */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'k': /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
            break;
        case OPTION_KEEP_NEWER_FILES: /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
            break;
        case 'L': /* BSD convention */
            bsdtar->symlink_mode = 'L';
            break;
            case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
            /* GNU tar 1.13  used -l for --one-file-system */
            bsdtar->option_warn_links = 1;
            break;
        case OPTION_LZMA:
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'm': /* SUSv2 */
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
            break;
        case 'n': /* GNU tar */
            bsdtar->option_no_subdirs = 1;
            break;
            /*
         * Selecting files by time:
         *    --newer-?time='date' Only files newer than 'date'
         *    --newer-?time-than='file' Only files newer than time
         *         on specified file (useful for incremental backups)
         * TODO: Add corresponding "older" options to reverse these.
         */
        case OPTION_NEWER_CTIME: /* GNU tar */
            bsdtar->newer_ctime_sec = get_date(now, bsdtar->optarg);
            break;
        case OPTION_NEWER_CTIME_THAN:
            {
                struct stat st;
                if (stat(bsdtar->optarg, &st) != 0)
                    lafe_errc(1, 0,
                        "Can't open file %s", bsdtar->optarg);
                bsdtar->newer_ctime_sec = st.st_ctime;
                bsdtar->newer_ctime_nsec =
                    ARCHIVE_STAT_CTIME_NANOS(&st);
            }
            break;
        case OPTION_NEWER_MTIME: /* GNU tar */
            bsdtar->newer_mtime_sec = get_date(now, bsdtar->optarg);
            break;
        case OPTION_NEWER_MTIME_THAN:
            {
                struct stat st;
                if (stat(bsdtar->optarg, &st) != 0)
                    lafe_errc(1, 0,
                        "Can't open file %s", bsdtar->optarg);
                bsdtar->newer_mtime_sec = st.st_mtime;
                bsdtar->newer_mtime_nsec =
                    ARCHIVE_STAT_MTIME_NANOS(&st);
            }
            break;
        case OPTION_NODUMP: /* star */
            bsdtar->option_honor_nodump = 1;
            break;
        case OPTION_NO_SAME_OWNER: /* GNU tar */
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
            break;
        case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
            break;
        case OPTION_NULL: /* GNU tar */
            bsdtar->option_null++;
            break;
        case OPTION_NUMERIC_OWNER: /* GNU tar */
            bsdtar->option_numeric_owner++;
            break;
        case 'O': /* GNU tar */
            bsdtar->option_stdout = 1;
            break;
        case 'o': /* SUSv2 and GNU conflict here, but not fatally */
            option_o = 1; /* Record it and resolve it later. */
            break;
        case OPTION_ONE_FILE_SYSTEM: /* GNU tar */
            bsdtar->option_dont_traverse_mounts = 1;
            break;
#if 0
        /*
         * The common BSD -P option is not necessary, since
         * our default is to archive symlinks, not follow
         * them.  This is convenient, as -P conflicts with GNU
         * tar anyway.
         */
        case 'P': /* BSD convention */
            /* Default behavior, no option necessary. */
            break;
#endif
        case 'P': /* GNU tar */
            bsdtar->extract_flags &= ~SECURITY;
            bsdtar->option_absolute_paths = 1;
            break;
        case 'p': /* GNU tar, star */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
            break;
        case OPTION_POSIX: /* GNU tar */
            bsdtar->create_format = "pax";
            break;
        case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
            bsdtar->option_fast_read = 1;
            break;
        case 'r': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case 'S': /* NetBSD pax-as-tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE;
            break;
        case 's': /* NetBSD pax-as-tar */
#if HAVE_REGEX_H
            add_substitution(bsdtar, bsdtar->optarg);
#else
            lafe_warnc(0,
                "-s is not supported by this version of bsdtar");
            usage();
#endif
            break;
        case OPTION_SAME_OWNER: /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
            break;
        case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
            bsdtar->strip_components = atoi(bsdtar->optarg);
            break;
        case 'T': /* GNU tar */
            bsdtar->names_from_file = bsdtar->optarg;
            break;
        case 't': /* SUSv2 */
            set_mode(bsdtar, opt);
            bsdtar->verbose++;
            break;
        case OPTION_TOTALS: /* GNU tar */
            bsdtar->option_totals++;
            break;
        case 'U': /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
            bsdtar->option_unlink_first = 1;
            break;
        case 'u': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case 'v': /* SUSv2 */
            bsdtar->verbose++;
            break;
        case OPTION_VERSION: /* GNU convention */
            version();
            break;
#if 0
        /*
         * The -W longopt feature is handled inside of
         * bsdtar_getopt(), so -W is not available here.
         */
        case 'W': /* Obscure GNU convention. */
            break;
#endif
        case 'w': /* SUSv2 */
            bsdtar->option_interactive = 1;
            break;
        case 'X': /* GNU tar */
            if (lafe_exclude_from_file(&bsdtar->matching, bsdtar->optarg))
                lafe_errc(1, 0,
                    "failed to process exclusions from file %s",
                    bsdtar->optarg);
            break;
        case 'x': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case 'y': /* FreeBSD version of GNU tar */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'Z': /* GNU tar */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'z': /* GNU tar, star, many others */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case OPTION_USE_COMPRESS_PROGRAM:
            bsdtar->compress_program = bsdtar->optarg;
            break;
        default:
            usage();
        }
    }

    /*
     * Sanity-check options.
     */

    /* If no "real" mode was specified, treat -h as --help. */
    if ((bsdtar->mode == '\0') && possible_help_request) {
        long_help();
        exit(0);
    }

    /* Otherwise, a mode is required. */
    if (bsdtar->mode == '\0')
        lafe_errc(1, 0,
            "Must specify one of -c, -r, -t, -u, -x");

    /* Check boolean options only permitted in certain modes. */
    if (bsdtar->option_dont_traverse_mounts)
        only_mode(bsdtar, "--one-file-system", "cru");
    if (bsdtar->option_fast_read)
        only_mode(bsdtar, "--fast-read", "xt");
    if (bsdtar->option_honor_nodump)
        only_mode(bsdtar, "--nodump", "cru");
    if (option_o > 0) {
        switch (bsdtar->mode) {
        case 'c':
            /*
             * In GNU tar, -o means "old format."  The
             * "ustar" format is the closest thing
             * supported by libarchive.
             */
            bsdtar->create_format = "ustar";
            /* TODO: bsdtar->create_format = "v7"; */
            break;
        case 'x':
            /* POSIX-compatible behavior. */
            bsdtar->option_no_owner = 1;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
            break;
        default:
            only_mode(bsdtar, "-o", "xc");
            break;
        }
    }
    if (bsdtar->option_no_subdirs)
        only_mode(bsdtar, "-n", "cru");
    if (bsdtar->option_stdout)
        only_mode(bsdtar, "-O", "xt");
    if (bsdtar->option_unlink_first)
        only_mode(bsdtar, "-U", "x");
    if (bsdtar->option_warn_links)
        only_mode(bsdtar, "--check-links", "cr");

    /* Check other parameters only permitted in certain modes. */
    if (bsdtar->create_compression != '\0') {
        strcpy(buff, "-?");
        buff[1] = bsdtar->create_compression;
        only_mode(bsdtar, buff, "cxt");
    }
    if (bsdtar->create_format != NULL)
        only_mode(bsdtar, "--format", "cru");
    if (bsdtar->symlink_mode != '\0') {
        strcpy(buff, "-?");
        buff[1] = bsdtar->symlink_mode;
        only_mode(bsdtar, buff, "cru");
    }
    if (bsdtar->strip_components != 0)
        only_mode(bsdtar, "--strip-components", "xt");

    switch(bsdtar->mode) {
    case 'c':
        tar_mode_c(bsdtar);
        break;
    case 'r':
        tar_mode_r(bsdtar);
        break;
    case 't':
        tar_mode_t(bsdtar);
        break;
    case 'u':
        tar_mode_u(bsdtar);
        break;
    case 'x':
        tar_mode_x(bsdtar);
        break;
    }

    lafe_cleanup_exclusions(&bsdtar->matching);
#if HAVE_REGEX_H
    cleanup_substitution(bsdtar);
#endif

    if (bsdtar->return_value != 0)
        lafe_warnc(0,
            "Error exit delayed from previous errors.");
    return (bsdtar->return_value);
}

static void
set_mode(struct bsdtar *bsdtar, char opt)
{
    if (bsdtar->mode != '\0' && bsdtar->mode != opt)
        lafe_errc(1, 0,
            "Can't specify both -%c and -%c", opt, bsdtar->mode);
    bsdtar->mode = opt;
}

/*
 * Verify that the mode is correct.
 */
static void
only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes)
{
    if (strchr(valid_modes, bsdtar->mode) == NULL)
        lafe_errc(1, 0,
            "Option %s is not permitted in mode -%c",
            opt, bsdtar->mode);
}


void
usage(void)
{
    const char  *p;

    p = lafe_progname;

    fprintf(stderr, "Usage:\n");
    fprintf(stderr, "  List:    %s -tf <archive-filename>\n", p);
    fprintf(stderr, "  Extract: %s -xf <archive-filename>\n", p);
    fprintf(stderr, "  Create:  %s -cf <archive-filename> [filenames...]\n", p);
    fprintf(stderr, "  Help:    %s --help\n", p);
    exit(1);
}

static void
version(void)
{
    printf("bsdtar %s - %s\n",
        BSDTAR_VERSION_STRING,
        archive_version());
    exit(0);
}

static const char *long_help_msg =
    "First option must be a mode specifier:\n"
    "  -c Create  -r Add/Replace  -t List  -u Update  -x Extract\n"
    "Common Options:\n"
    "  -b #  Use # 512-byte records per I/O block\n"
    "  -f <filename>  Location of archive (default " _PATH_DEFTAPE ")\n"
    "  -v    Verbose\n"
    "  -w    Interactive\n"
    "Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n"
    "  <file>, <dir>  add these items to archive\n"
    "  -z, -j, -J, --lzma  Compress archive with gzip/bzip2/xz/lzma\n"
    "  --format {ustar|pax|cpio|shar}  Select archive format\n"
    "  --exclude <pattern>  Skip files that match pattern\n"
    "  -C <dir>  Change to <dir> before processing remaining files\n"
    "  @<archive>  Add entries from <archive> to output\n"
    "List: %p -t [options] [<patterns>]\n"
    "  <patterns>  If specified, list only entries that match\n"
    "Extract: %p -x [options] [<patterns>]\n"
    "  <patterns>  If specified, extract only entries that match\n"
    "  -k    Keep (don't overwrite) existing files\n"
    "  -m    Don't restore modification times\n"
    "  -O    Write entries to stdout, don't restore to disk\n"
    "  -p    Restore permissions (including ACLs, owner, file flags)\n";


/*
 * Note that the word 'bsdtar' will always appear in the first line
 * of output.
 *
 * In particular, /bin/sh scripts that need to test for the presence
 * of bsdtar can use the following template:
 *
 * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \
 *          echo bsdtar; else echo not bsdtar; fi
 */
static void
long_help(void)
{
    const char  *prog;
    const char  *p;

    prog = lafe_progname;

    fflush(stderr);

    p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : "";
    printf("%s%s: manipulate archive files\n", prog, p);

    for (p = long_help_msg; *p != '\0'; p++) {
        if (*p == '%') {
            if (p[1] == 'p') {
                fputs(prog, stdout);
                p++;
            } else
                putchar('%');
        } else
            putchar(*p);
    }
    version();
}

--- NEW FILE: 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,
 * 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 "bsdtar_platform.h"
__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle Exp $");

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "bsdtar.h"
#include "err.h"

struct progress_data {
    struct bsdtar *bsdtar;
    struct archive *archive;
    struct archive_entry *entry;
};

static void list_item_verbose(struct bsdtar *, FILE *,
            struct archive_entry *);
static void read_archive(struct bsdtar *bsdtar, char mode);

void
tar_mode_t(struct bsdtar *bsdtar)
{
    read_archive(bsdtar, 't');
    if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
        bsdtar->return_value = 1;
}

void
tar_mode_x(struct bsdtar *bsdtar)
{
    read_archive(bsdtar, 'x');

    if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
        bsdtar->return_value = 1;
}

static void
progress_func(void *cookie)
{
    struct progress_data *progress_data = cookie;
    struct bsdtar *bsdtar = progress_data->bsdtar;
    struct archive *a = progress_data->archive;
    struct archive_entry *entry = progress_data->entry;
    uintmax_t comp, uncomp;

    if (!need_report())
        return;

    if (bsdtar->verbose)
        fprintf(stderr, "\n");
    if (a != NULL) {
        comp = archive_position_compressed(a);
        uncomp = archive_position_uncompressed(a);
        fprintf(stderr,
            "In: %ju bytes, compression %d%%;",
            comp, (int)((uncomp - comp) * 100 / uncomp));
        fprintf(stderr, "  Out: %d files, %ju bytes\n",
            archive_file_count(a), uncomp);
    }
    if (entry != NULL) {
        safe_fprintf(stderr, "Current: %s (%ju bytes)",
            archive_entry_pathname(entry),
            (uintmax_t)archive_entry_size(entry));
        fprintf(stderr, "\n");
    }
}

/*
 * Handle 'x' and 't' modes.
 */
static void
read_archive(struct bsdtar *bsdtar, char mode)
{
    struct progress_data    progress_data;
    FILE             *out;
    struct archive       *a;
    struct archive_entry     *entry;
    const struct stat    *st;
    int           r;

    while (*bsdtar->argv) {
        lafe_include(&bsdtar->matching, *bsdtar->argv);
        bsdtar->argv++;
    }

    if (bsdtar->names_from_file != NULL)
        lafe_include_from_file(&bsdtar->matching,
            bsdtar->names_from_file, bsdtar->option_null);

    a = archive_read_new();
    if (bsdtar->compress_program != NULL)
        archive_read_support_compression_program(a, bsdtar->compress_program);
    else
        archive_read_support_compression_all(a);
    archive_read_support_format_all(a);
    if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options))
        lafe_errc(1, 0, archive_error_string(a));
    if (archive_read_open_file(a, bsdtar->filename,
        bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block :
        DEFAULT_BYTES_PER_BLOCK))
        lafe_errc(1, 0, "Error opening archive: %s",
            archive_error_string(a));

    do_chdir(bsdtar);

    if (mode == 'x') {
        /* Set an extract callback so that we can handle SIGINFO. */
        progress_data.bsdtar = bsdtar;
        progress_data.archive = a;
        archive_read_extract_set_progress_callback(a, progress_func,
            &progress_data);
    }

    if (mode == 'x' && bsdtar->option_chroot) {
#if HAVE_CHROOT
        if (chroot(".") != 0)
            lafe_errc(1, errno, "Can't chroot to \".\"");
#else
        lafe_errc(1, 0,
            "chroot isn't supported on this platform");
#endif
    }

    for (;;) {
        /* Support --fast-read option */
        if (bsdtar->option_fast_read &&
            lafe_unmatched_inclusions(bsdtar->matching) == 0)
            break;

        r = archive_read_next_header(a, &entry);
        progress_data.entry = entry;
        if (r == ARCHIVE_EOF)
            break;
        if (r < ARCHIVE_OK)
            lafe_warnc(0, "%s", archive_error_string(a));
        if (r <= ARCHIVE_WARN)
            bsdtar->return_value = 1;
        if (r == ARCHIVE_RETRY) {
            /* Retryable error: try again */
            lafe_warnc(0, "Retrying...");
            continue;
        }
        if (r == ARCHIVE_FATAL)
            break;

        if (bsdtar->option_numeric_owner) {
            archive_entry_set_uname(entry, NULL);
            archive_entry_set_gname(entry, NULL);
        }

        /*
         * Exclude entries that are too old.
         */
        st = archive_entry_stat(entry);
        if (bsdtar->newer_ctime_sec > 0) {
            if (st->st_ctime < bsdtar->newer_ctime_sec)
                continue; /* Too old, skip it. */
            if (st->st_ctime == bsdtar->newer_ctime_sec
                && ARCHIVE_STAT_CTIME_NANOS(st)
                <= bsdtar->newer_ctime_nsec)
                continue; /* Too old, skip it. */
        }
        if (bsdtar->newer_mtime_sec > 0) {
            if (st->st_mtime < bsdtar->newer_mtime_sec)
                continue; /* Too old, skip it. */
            if (st->st_mtime == bsdtar->newer_mtime_sec
                && ARCHIVE_STAT_MTIME_NANOS(st)
                <= bsdtar->newer_mtime_nsec)
                continue; /* Too old, skip it. */
        }

        /*
         * Note that pattern exclusions are checked before
         * pathname rewrites are handled.  This gives more
         * control over exclusions, since rewrites always lose
         * information.  (For example, consider a rewrite
         * s/foo[0-9]/foo/.  If we check exclusions after the
         * rewrite, there would be no way to exclude foo1/bar
         * while allowing foo2/bar.)
         */
        if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry)))
            continue; /* Excluded by a pattern test. */

        if (mode == 't') {
            /* Perversely, gtar uses -O to mean "send to stderr"
             * when used with -t. */
            out = bsdtar->option_stdout ? stderr : stdout;

            /*
             * TODO: Provide some reasonable way to
             * preview rewrites.  gtar always displays
             * the unedited path in -t output, which means
             * you cannot easily preview rewrites.
             */
            if (bsdtar->verbose < 2)
                safe_fprintf(out, "%s",
                    archive_entry_pathname(entry));
            else
                list_item_verbose(bsdtar, out, entry);
            fflush(out);
            r = archive_read_data_skip(a);
            if (r == ARCHIVE_WARN) {
                fprintf(out, "\n");
                lafe_warnc(0, "%s",
                    archive_error_string(a));
            }
            if (r == ARCHIVE_RETRY) {
                fprintf(out, "\n");
                lafe_warnc(0, "%s",
                    archive_error_string(a));
            }
            if (r == ARCHIVE_FATAL) {
                fprintf(out, "\n");
                lafe_warnc(0, "%s",
                    archive_error_string(a));
                bsdtar->return_value = 1;
                break;
            }
            fprintf(out, "\n");
        } else {
            /* Note: some rewrite failures prevent extraction. */
            if (edit_pathname(bsdtar, entry))
                continue; /* Excluded by a rewrite failure. */

            if (bsdtar->option_interactive &&
                !yes("extract '%s'", archive_entry_pathname(entry)))
                continue;

            /*
             * Format here is from SUSv2, including the
             * deferred '\n'.
             */
            if (bsdtar->verbose) {
                safe_fprintf(stderr, "x %s",
                    archive_entry_pathname(entry));
                fflush(stderr);
            }

            // TODO siginfo_printinfo(bsdtar, 0);

            if (bsdtar->option_stdout)
                r = archive_read_data_into_fd(a, 1);
            else
                r = archive_read_extract(a, entry,
                    bsdtar->extract_flags);
            if (r != ARCHIVE_OK) {
                if (!bsdtar->verbose)
                    safe_fprintf(stderr, "%s",
                        archive_entry_pathname(entry));
                safe_fprintf(stderr, ": %s",
                    archive_error_string(a));
                if (!bsdtar->verbose)
                    fprintf(stderr, "\n");
                bsdtar->return_value = 1;
            }
            if (bsdtar->verbose)
                fprintf(stderr, "\n");
            if (r == ARCHIVE_FATAL)
                break;
        }
    }


    r = archive_read_close(a);
    if (r != ARCHIVE_OK)
        lafe_warnc(0, "%s", archive_error_string(a));
    if (r <= ARCHIVE_WARN)
        bsdtar->return_value = 1;

    if (bsdtar->verbose > 2)
        fprintf(stdout, "Archive Format: %s,  Compression: %s\n",
            archive_format_name(a), archive_compression_name(a));

    archive_read_finish(a);
}


/*
 * Display information about the current file.
 *
 * The format here roughly duplicates the output of 'ls -l'.
 * This is based on SUSv2, where 'tar tv' is documented as
 * listing additional information in an "unspecified format,"
 * and 'pax -l' is documented as using the same format as 'ls -l'.
 */
static void
list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
{
    char             tmp[100];
    size_t           w;
    const char      *p;
    const char      *fmt;
    time_t           tim;
    static time_t        now;

    /*
     * We avoid collecting the entire list in memory at once by
     * listing things as we see them.  However, that also means we can't
     * just pre-compute the field widths.  Instead, we start with guesses
     * and just widen them as necessary.  These numbers are completely
     * arbitrary.
     */
    if (!bsdtar->u_width) {
        bsdtar->u_width = 6;
        bsdtar->gs_width = 13;
    }
    if (!now)
        time(&now);
    fprintf(out, "%s %d ",
        archive_entry_strmode(entry),
        archive_entry_nlink(entry));

    /* Use uname if it's present, else uid. */
    p = archive_entry_uname(entry);
    if ((p == NULL) || (*p == '\0')) {
        sprintf(tmp, "%lu ",
            (unsigned long)archive_entry_uid(entry));
        p = tmp;
    }
    w = strlen(p);
    if (w > bsdtar->u_width)
        bsdtar->u_width = w;
    fprintf(out, "%-*s ", (int)bsdtar->u_width, p);

    /* Use gname if it's present, else gid. */
    p = archive_entry_gname(entry);
    if (p != NULL && p[0] != '\0') {
        fprintf(out, "%s", p);
        w = strlen(p);
    } else {
        sprintf(tmp, "%lu",
            (unsigned long)archive_entry_gid(entry));
        w = strlen(tmp);
        fprintf(out, "%s", tmp);
    }

    /*
     * Print device number or file size, right-aligned so as to make
     * total width of group and devnum/filesize fields be gs_width.
     * If gs_width is too small, grow it.
     */
    if (archive_entry_filetype(entry) == AE_IFCHR
        || archive_entry_filetype(entry) == AE_IFBLK) {
        sprintf(tmp, "%lu,%lu",
            (unsigned long)archive_entry_rdevmajor(entry),
            (unsigned long)archive_entry_rdevminor(entry));
    } else {
        /*
         * Note the use of platform-dependent macros to format
         * the filesize here.  We need the format string and the
         * corresponding type for the cast.
         */
        sprintf(tmp, BSDTAR_FILESIZE_PRINTF,
            (BSDTAR_FILESIZE_TYPE)archive_entry_size(entry));
    }
    if (w + strlen(tmp) >= bsdtar->gs_width)
        bsdtar->gs_width = w+strlen(tmp)+1;
    fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);

    /* Format the time using 'ls -l' conventions. */
    tim = archive_entry_mtime(entry);
#define HALF_YEAR (time_t)365 * 86400 / 2
#if defined(_WIN32) && !defined(__CYGWIN__)
#define DAY_FMT  "%d"  /* Windows' strftime function does not support %e format. */
#else
#define DAY_FMT  "%e"  /* Day number without leading zeros */
#endif
    if (tim < now - HALF_YEAR || tim > now + HALF_YEAR)
        fmt = bsdtar->day_first ? DAY_FMT " %b  %Y" : "%b " DAY_FMT "  %Y";
    else
        fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M";
    strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
    fprintf(out, " %s ", tmp);
    safe_fprintf(out, "%s", archive_entry_pathname(entry));

    /* Extra information for links. */
    if (archive_entry_hardlink(entry)) /* Hard link */
        safe_fprintf(out, " link to %s",
            archive_entry_hardlink(entry));
    else if (archive_entry_symlink(entry)) /* Symbolic link */
        safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
}

--- NEW FILE: bsdtar_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.
 *
 * $FreeBSD$
 */

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

#include "bsdtar_platform.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <stddef.h>
#include <sys/utime.h>
#include <sys/stat.h>
#include <process.h>
#include <stdlib.h>
#include <wchar.h>
#include <windows.h>
#include <sddl.h>

#include "bsdtar.h"
#include "err.h"

/* This may actually not be needed anymore.
 * TODO: Review the error handling for chdir() failures and
 * simply dump this if it's not really needed. */
static void __tar_dosmaperr(unsigned long);

/*
 * Prepend "\\?\" to the path name and convert it to unicode to permit
 * an extended-length path for a maximum total path length of 32767
 * characters.
 * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
 */
static wchar_t *
permissive_name(const char *name)
{
    wchar_t *wn, *wnp;
    wchar_t *ws, *wsp;
    size_t l, len, slen, alloclen;
    int unc;

    len = strlen(name);
    wn = malloc((len + 1) * sizeof(wchar_t));
    if (wn == NULL)
        return (NULL);
    l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len);
    if (l == 0) {
        free(wn);
        return (NULL);
    }
    wn[l] = L'\0';

    /* Get a full path names */
    l = GetFullPathNameW(wn, 0, NULL, NULL);
    if (l == 0) {
        free(wn);
        return (NULL);
    }
    wnp = malloc(l * sizeof(wchar_t));
    if (wnp == NULL) {
        free(wn);
        return (NULL);
    }
    len = GetFullPathNameW(wn, l, wnp, NULL);
    free(wn);
    wn = wnp;

    if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
        wnp[2] == L'?' && wnp[3] == L'\\')
        /* We have already permissive names. */
        return (wn);

    if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
        wnp[2] == L'.' && wnp[3] == L'\\') {
        /* Device names */
        if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
             (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
            wnp[5] == L':' && wnp[6] == L'\\')
            wnp[2] = L'?';/* Not device names. */
        return (wn);
    }

    unc = 0;
    if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
        wchar_t *p = &wnp[2];

        /* Skip server-name letters. */
        while (*p != L'\\' && *p != L'\0')
            ++p;
        if (*p == L'\\') {
            wchar_t *rp = ++p;
            /* Skip share-name letters. */
            while (*p != L'\\' && *p != L'\0')
                ++p;
            if (*p == L'\\' && p != rp) {
                /* Now, match patterns such as
                 * "\\server-name\share-name\" */
                wnp += 2;
                len -= 2;
                unc = 1;
            }
        }
    }

    alloclen = slen = 4 + (unc * 4) + len + 1;
    ws = wsp = malloc(slen * sizeof(wchar_t));
    if (ws == NULL) {
        free(wn);
        return (NULL);
    }
    /* prepend "\\?\" */
    wcsncpy(wsp, L"\\\\?\\", 4);
    wsp += 4;
    slen -= 4;
    if (unc) {
        /* append "UNC\" ---> "\\?\UNC\" */
        wcsncpy(wsp, L"UNC\\", 4);
        wsp += 4;
        slen -= 4;
    }
    wcsncpy(wsp, wnp, slen);
    free(wn);
    ws[alloclen - 1] = L'\0';
    return (ws);
}

int
__tar_chdir(const char *path)
{
    wchar_t *ws;
    int r;

    r = SetCurrentDirectoryA(path);
    if (r == 0) {
        if (GetLastError() != ERROR_FILE_NOT_FOUND) {
            __tar_dosmaperr(GetLastError());
            return (-1);
        }
    } else
        return (0);
    ws = permissive_name(path);
    if (ws == NULL) {
        errno = EINVAL;
        return (-1);
    }
    r = SetCurrentDirectoryW(ws);
    free(ws);
    if (r == 0) {
        __tar_dosmaperr(GetLastError());
        return (-1);
    }
    return (0);
}

/*
 * The following function was modified from PostgreSQL sources and is
 * subject to the copyright below.
 */
/*-------------------------------------------------------------------------
 *
 * win32error.c
 *    Map win32 error codes to errno values
 *
 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *    $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
PostgreSQL Database Management System
(formerly known as Postgres, then as Postgres95)

Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group

Portions Copyright (c) 1994, The Regents of the University of California

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement
is hereby granted, provided that the above copyright notice and this
paragraph and the following two paragraphs appear in all copies.

IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/

static const struct {
    DWORD       winerr;
    int     doserr;
} doserrors[] =
{
    {   ERROR_INVALID_FUNCTION, EINVAL  },
    {   ERROR_FILE_NOT_FOUND, ENOENT    },
    {   ERROR_PATH_NOT_FOUND, ENOENT    },
    {   ERROR_TOO_MANY_OPEN_FILES, EMFILE   },
    {   ERROR_ACCESS_DENIED, EACCES },
    {   ERROR_INVALID_HANDLE, EBADF },
    {   ERROR_ARENA_TRASHED, ENOMEM },
    {   ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
    {   ERROR_INVALID_BLOCK, ENOMEM },
    {   ERROR_BAD_ENVIRONMENT, E2BIG    },
    {   ERROR_BAD_FORMAT, ENOEXEC   },
    {   ERROR_INVALID_ACCESS, EINVAL    },
    {   ERROR_INVALID_DATA, EINVAL  },
    {   ERROR_INVALID_DRIVE, ENOENT },
    {   ERROR_CURRENT_DIRECTORY, EACCES },
    {   ERROR_NOT_SAME_DEVICE, EXDEV    },
    {   ERROR_NO_MORE_FILES, ENOENT },
    {   ERROR_LOCK_VIOLATION, EACCES    },
    {   ERROR_SHARING_VIOLATION, EACCES },
    {   ERROR_BAD_NETPATH, ENOENT   },
    {   ERROR_NETWORK_ACCESS_DENIED, EACCES },
    {   ERROR_BAD_NET_NAME, ENOENT  },
    {   ERROR_FILE_EXISTS, EEXIST   },
    {   ERROR_CANNOT_MAKE, EACCES   },
    {   ERROR_FAIL_I24, EACCES  },
    {   ERROR_INVALID_PARAMETER, EINVAL },
    {   ERROR_NO_PROC_SLOTS, EAGAIN },
    {   ERROR_DRIVE_LOCKED, EACCES  },
    {   ERROR_BROKEN_PIPE, EPIPE    },
    {   ERROR_DISK_FULL, ENOSPC },
    {   ERROR_INVALID_TARGET_HANDLE, EBADF  },
    {   ERROR_INVALID_HANDLE, EINVAL    },
    {   ERROR_WAIT_NO_CHILDREN, ECHILD  },
    {   ERROR_CHILD_NOT_COMPLETE, ECHILD    },
    {   ERROR_DIRECT_ACCESS_HANDLE, EBADF   },
    {   ERROR_NEGATIVE_SEEK, EINVAL },
    {   ERROR_SEEK_ON_DEVICE, EACCES    },
    {   ERROR_DIR_NOT_EMPTY, ENOTEMPTY  },
    {   ERROR_NOT_LOCKED, EACCES    },
    {   ERROR_BAD_PATHNAME, ENOENT  },
    {   ERROR_MAX_THRDS_REACHED, EAGAIN },
    {   ERROR_LOCK_FAILED, EACCES   },
    {   ERROR_ALREADY_EXISTS, EEXIST    },
    {   ERROR_FILENAME_EXCED_RANGE, ENOENT  },
    {   ERROR_NESTING_NOT_ALLOWED, EAGAIN   },
    {   ERROR_NOT_ENOUGH_QUOTA, ENOMEM  }
};

static void
__tar_dosmaperr(unsigned long e)
{
    int         i;

    if (e == 0) {
        errno = 0;
        return;
    }

    for (i = 0; i < sizeof(doserrors); i++) {
        if (doserrors[i].winerr == e) {
            errno = doserrors[i].doserr;
            return;
        }
    }

    /* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
    errno = EINVAL;
    return;
}

#endif



More information about the Cmake-commits mailing list