[Cmake-commits] [cmake-commits] hoffman committed #notes# NONE 1.1 Makefile NONE 1.1 README NONE 1.1 minitar.c NONE 1.1 notes NONE 1.1 tree.c NONE 1.1 tree.h NONE 1.1

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


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

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

/*-
 * There is 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 <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "tree.h"

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

struct tree_entry {
    struct tree_entry *next;
    char *name;
    size_t dirname_length;
    int fd;
    int flags;
};

/* 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 needsTraversal 4 /* This entry hasn't yet been traversed. */

/*
 * Local data for this package.
 */
struct tree {
    struct tree_entry   *stack;
    DIR *d;
    int  initialDirFd;
    int  flags;

    char    *buff;
    char    *basename;
    size_t   buff_length;
    size_t   path_length;
    size_t   dirname_length;

    int  depth;
    int  openCount;
    int  maxOpenCount;

    struct stat lst;
    struct stat st;
};

/* Definitions for tree.flags bitmap. */
#define needsReturn 8  /* Marks first entry as not having been returned yet. */
#define hasStat 16  /* The st entry is set. */
#define hasLstat 32 /* The lst entry is set. */


#define HAVE_DIRENT_D_NAMLEN 1
#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

#if 0
static void
dumpStack(struct tree *t)
{
    struct tree_entry *te;

    printf("\tbuff: %s\n", t->buff);
    printf("\tpwd: "); fflush(stdout); system("pwd");
    printf("\tstack:\n");
    for (te = t->stack; te != NULL; te = te->next) {
        printf("\t\tte->name: %s %s\n", te->name, te->flags & needsTraversal ? "" : "*");
    }
}
#endif

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

    te = malloc(sizeof(*te));
    memset(te, 0, sizeof(*te));
    te->next = t->stack;
    t->stack = te;
    te->fd = -1;
    te->name = strdup(path);
    te->flags = needsTraversal;
    te->dirname_length = t->dirname_length;
}

/*
 * Append a name to the current path.
 */
static void
tree_append(struct tree *t, const char *name, size_t name_length)
{
    if (t->buff != NULL)
        t->buff[t->dirname_length] = '\0';

    /* 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);
    }
    t->basename = t->buff + t->dirname_length;
    t->path_length = t->dirname_length + name_length;
    if (t->dirname_length > 0) {
        *t->basename++ = '/';
        t->path_length ++;
    }
    strcpy(t->basename, name);
}

/*
 * 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));
    t->initialDirFd = open(".", O_RDONLY);
    /*
     * During most of the traversal, items are set up and then
     * returned immediately from tree_next().  That doesn't work
     * for the very first entry, so we set a flag for this special
     * case.
     */
    t->flags = needsReturn;
    return (t);
}

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

    te = t->stack;
    t->depth--;
    if (te->flags & isDirLink) {
        fchdir(te->fd);
        close(te->fd);
        t->openCount--;
    } else {
        chdir("..");
    }
}

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

    te = t->stack;
    t->stack = te->next;
    t->dirname_length = te->dirname_length;
    free(te->name);
    free(te);
}

/*
 * Get the next item in the tree traversal.
 */
int
tree_next(struct tree *t)
{
    struct dirent *de = NULL;

    /* Handle the startup case by returning the initial entry. */
    if (t->flags & needsReturn) {
        t->flags &= ~needsReturn;
        return (1);
    }

    while (t->stack != NULL) {
        /* If there's an open dir, get the next entry from there. */
        while (t->d != NULL) {
            de = readdir(t->d);
            if (de == NULL) {
                closedir(t->d);
                t->d = NULL;
            } else if (de->d_name[0] == '.'
                && de->d_name[1] == '\0') {
                /* Skip '.' */
            } else if (de->d_name[0] == '.'
                && de->d_name[1] == '.'
                && de->d_name[2] == '\0') {
                /* Skip '..' */
            } else {
                /*
                 * Append the path to the current path
                 * and return it.
                 */
                tree_append(t, de->d_name, D_NAMELEN(de));
                t->flags &= ~hasLstat;
                t->flags &= ~hasStat;
                return (1);
            }
        }

        /* If the current dir needs to be traversed, set it up. */
        if (t->stack->flags & needsTraversal) {
            tree_append(t, t->stack->name, strlen(t->stack->name));
            t->stack->flags &= ~needsTraversal;
            /* If it is a link, set up fd for the ascent. */
            if (t->stack->flags & isDirLink) {
                t->stack->fd = open(".", O_RDONLY);
                t->openCount++;
                if (t->openCount > t->maxOpenCount)
                    t->maxOpenCount = t->openCount;
            }
            if (chdir(t->stack->name) == 0) {
                t->depth++;
                t->dirname_length = t->path_length;
                t->d = opendir(".");
            } else
                tree_pop(t);
            continue;
        }

        /* We've done everything necessary for the top stack entry. */
        tree_ascend(t);
        tree_pop(t);
    }
    return (0);
}

/*
 * Called by the client to mark the directory just returned from
 * tree_next() as needing to be visited.
 */
void
tree_descend(struct tree *t)
{
    const struct stat *s = tree_current_lstat(t);

    if (S_ISDIR(s->st_mode)) {
        tree_add(t, t->basename);
        t->stack->flags |= isDir;
    }

    if (S_ISLNK(s->st_mode) && S_ISDIR(tree_current_stat(t)->st_mode)) {
        tree_add(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)) {
        stat(t->basename, &t->st);
        t->flags |= hasStat;
    }
    return (&t->st);
}

/*
 * Get the lstat() data for the entry just returned from tree_next().
 */
const struct stat *
tree_current_lstat(struct tree *t)
{
    if (!(t->flags & hasLstat)) {
        lstat(t->basename, &t->lst);
        t->flags |= hasLstat;
    }
    return (&t->lst);
}

/*
 * 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);
    /* chdir() back to where we started. */
    if (t->initialDirFd >= 0) {
        fchdir(t->initialDirFd);
        close(t->initialDirFd);
        t->initialDirFd = -1;
    }
    free(t);
}


#if 0
/* Main function for testing. */
#include <stdio.h>

int main(int argc, char **argv)
{
    size_t max_path_len = 0;
    int max_depth = 0;

    system("pwd");
    while (*++argv) {
        struct tree *t = tree_open(*argv);
        while (tree_next(t)) {
            size_t path_len = tree_current_pathlen(t);
            int depth = tree_current_depth(t);
            if (path_len > max_path_len)
                max_path_len = path_len;
            if (depth > max_depth)
                max_depth = depth;
            printf("%s\n", tree_current_path(t));
            if (S_ISDIR(tree_current_lstat(t)->st_mode))
                tree_descend(t); /* Descend into every dir. */
        }
        tree_close(t);
        printf("Max path length: %d\n", max_path_len);
        printf("Max depth: %d\n", max_depth);
        printf("Final open count: %d\n", t->openCount);
        printf("Max open count: %d\n", t->maxOpenCount);
        fflush(stdout);
        system("pwd");
    }
    return (0);
}
#endif

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

/*-
 * 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>

struct tree;

struct tree *tree_open(const char *);
/* Returns TRUE if there is a next entry.  Zero if there is no next entry. */
int tree_next(struct tree *);
/* Return information about the current entry. */
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.
 */
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,
 * you should take advantage of it here if you need it.
 */
const struct stat *tree_current_stat(struct tree *);
const struct stat *tree_current_lstat(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 *);
void tree_close(struct tree *);

--- NEW FILE: notes ---
create(const char* filename)
{
  struct archive *a;
  a = archive_write_new();
// pick a compression type
  archive_write_set_compression_bzip2(a);
  archive_write_set_compression_compress(a);
  archive_write_set_compression_gzip(a);
  archive_write_set_compression_none(a);
  
// what does this do???
  archive_write_set_format_ustar(a); // is this what we want?
// maybe this:
  archive_write_set_format_pax(a);

  archive_write_open_file(a, filename);

  struct archive* disk = archive_read_disk_new();
  archive_read_disk_set_standard_lookup(disk);
  while (*argv != NULL)
    {
    struct tree *t = tree_open(*argv);
    while (tree_next(t)) 
      { 
      entry = archive_entry_new();
      archive_entry_set_pathname(entry, tree_current_path(t));
      archive_read_disk_entry_from_file(disk, entry, -1,
                                        tree_current_stat(t));
      if (verbose)
        {
        msg("a ");
        msg(tree_current_path(t));
        }
      archive_write_header(a, entry);
      int fd = open(tree_current_access_path(t), O_RDONLY);
      char buff[16384];
      len = read(fd, buff, sizeof(buff));
      while (len > 0)
        {
        archive_write_data(a, buff, len);
        len = read(fd, buff, sizeof(buff));
        }
      close(fd);
      archive_entry_free(entry);
      if (verbose)
        msg("\n");
      }
      }
  
}

--- NEW FILE: Makefile ---

#
# Adjust the following to control which options minitar gets
# built with.  See comments in minitar.c for details.
#
CFLAGS=             \
    -DNO_BZIP2_CREATE   \
    -DNO_BZIP2_EXTRACT  \
    -DNO_COMPRESS_EXTRACT   \
    -DNO_CPIO_EXTRACT   \
    -DNO_CREATE     \
    -DNO_GZIP_CREATE    \
    -DNO_GZIP_EXTRACT   \
    -DNO_LOOKUP

# Omit 'tree.o' if you're not including create support
#OBJS= minitar.o tree.o
OBJS= minitar.o

all: minitar

minitar: $(OBJS)
    cc -o minitar -static $(OBJS) -larchive -lz -lbz2
    strip minitar
    ls -l minitar

minitar.o: minitar.c

tree.o: tree.c

clean::
    rm -f *.o
    rm -f minitar
    rm -f *~

--- NEW FILE: #notes# ---
create(const char* filename)
{
  struct archive *a;
  a = archive_write_new();
// pick a compression type
  archive_write_set_compression_bzip2(a);
  archive_write_set_compression_compress(a);
  archive_write_set_compression_gzip(a);
  archive_write_set_compression_none(a);
  
// what does this do???
  archive_write_set_format_ustar(a); // is this what we want?
// maybe this:
  archive_write_set_format_pax(a);

  archive_write_open_file(a, filename);

  struct archive* disk = archive_read_disk_new();
  archive_read_disk_set_standard_lookup(disk);
  while (*argv != NULL)
    {
    struct tree *t = tree_open(*argv);
    while (tree_next(t)) 
      { 
      entry = archive_entry_new();
      archive_entry_set_pathname(entry, tree_current_path(t));
      struct stat s;
      stat(fname, &s);
      archive_read_disk_entry_from_file(disk, entry, -1, &s);
      if (verbose)
        {
        msg("a ");
        msg(tree_current_path(t));
        }
      archive_write_header(a, entry);
      int fd = open(tree_current_access_path(t), O_RDONLY);
      char buff[16384];
      len = read(fd, buff, sizeof(buff));
      while (len > 0)
        {
        archive_write_data(a, buff, len);
        len = read(fd, buff, sizeof(buff));
        }
      close(fd);
      archive_entry_free(entry);
      if (verbose)
        msg("\n");
      }
      }
  
}

--- NEW FILE: minitar.c ---
/*-
 * This file is in the public domain.
 * Do with it as you will.
 */

/*-
 * This is a compact "tar" program whose primary goal is small size.
 * Statically linked, it can be very small indeed.  This serves a number
 * of goals:
 *   o a testbed for libarchive (to check for link pollution),
 *   o a useful tool for space-constrained systems (boot floppies, etc),
 *   o a place to experiment with new implementation ideas for bsdtar,
 *   o a small program to demonstrate libarchive usage.
 *
 * Use the following macros to suppress features:
 *   NO_BZIP2 - Implies NO_BZIP2_CREATE and NO_BZIP2_EXTRACT
 *   NO_BZIP2_CREATE - Suppress bzip2 compression support.
 *   NO_BZIP2_EXTRACT - Suppress bzip2 auto-detection and decompression.
 *   NO_COMPRESS - Implies NO_COMPRESS_CREATE and NO_COMPRESS_EXTRACT
 *   NO_COMPRESS_CREATE - Suppress compress(1) compression support
 *   NO_COMPRESS_EXTRACT - Suppress compress(1) auto-detect and decompression.
 *   NO_CREATE - Suppress all archive creation support.
 *   NO_CPIO_EXTRACT - Suppress auto-detect and dearchiving of cpio archives.
 *   NO_GZIP - Implies NO_GZIP_CREATE and NO_GZIP_EXTRACT
 *   NO_GZIP_CREATE - Suppress gzip compression support.
 *   NO_GZIP_EXTRACT - Suppress gzip auto-detection and decompression.
 *   NO_LOOKUP - Try to avoid getpw/getgr routines, which can be very large
 *   NO_TAR_EXTRACT - Suppress tar extraction
 *
 * With all of the above macros defined (except NO_TAR_EXTRACT), you
 * get a very small program that can recognize and extract essentially
 * any uncompressed tar archive.  On FreeBSD 5.1, this minimal program
 * is under 64k, statically linked, which compares rather favorably to
 *         main(){printf("hello, world");}
 * which is over 60k statically linked on the same operating system.
 * Without any of the above macros, you get a static executable of
 * about 180k with a lot of very sophisticated modern features.
 * Obviously, it's trivial to add support for ISO, Zip, mtree,
 * lzma/xz, etc.  Just fill in the appropriate setup calls.
 */

#include <sys/types.h>
__FBSDID("$FreeBSD$");

#include <sys/stat.h>

#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifndef NO_CREATE
#include "tree.h"
#endif

/*
 * NO_CREATE implies NO_BZIP2_CREATE and NO_GZIP_CREATE and NO_COMPRESS_CREATE.
 */
#ifdef NO_CREATE
#undef NO_BZIP2_CREATE
#define NO_BZIP2_CREATE
#undef NO_COMPRESS_CREATE
#define NO_COMPRESS_CREATE
#undef NO_GZIP_CREATE
#define NO_GZIP_CREATE
#endif

/*
 * The combination of NO_BZIP2_CREATE and NO_BZIP2_EXTRACT is
 * equivalent to NO_BZIP2.
 */
#ifdef NO_BZIP2_CREATE
#ifdef NO_BZIP2_EXTRACT
#undef NO_BZIP2
#define NO_BZIP2
#endif
#endif

#ifdef NO_BZIP2
#undef NO_BZIP2_EXTRACT
#define NO_BZIP2_EXTRACT
#undef NO_BZIP2_CREATE
#define NO_BZIP2_CREATE
#endif

/*
 * The combination of NO_COMPRESS_CREATE and NO_COMPRESS_EXTRACT is
 * equivalent to NO_COMPRESS.
 */
#ifdef NO_COMPRESS_CREATE
#ifdef NO_COMPRESS_EXTRACT
#undef NO_COMPRESS
#define NO_COMPRESS
#endif
#endif

#ifdef NO_COMPRESS
#undef NO_COMPRESS_EXTRACT
#define NO_COMPRESS_EXTRACT
#undef NO_COMPRESS_CREATE
#define NO_COMPRESS_CREATE
#endif

/*
 * The combination of NO_GZIP_CREATE and NO_GZIP_EXTRACT is
 * equivalent to NO_GZIP.
 */
#ifdef NO_GZIP_CREATE
#ifdef NO_GZIP_EXTRACT
#undef NO_GZIP
#define NO_GZIP
#endif
#endif

#ifdef NO_GZIP
#undef NO_GZIP_EXTRACT
#define NO_GZIP_EXTRACT
#undef NO_GZIP_CREATE
#define NO_GZIP_CREATE
#endif

#ifndef NO_CREATE
static void create(const char *filename, int compress, const char **argv);
#endif
static void errmsg(const char *);
static void extract(const char *filename, int do_extract, int flags);
static int  copy_data(struct archive *, struct archive *);
static void msg(const char *);
static void usage(void);

static int verbose = 0;

int
main(int argc, const char **argv)
{
    const char *filename = NULL;
    int compress, flags, mode, opt;

    (void)argc;
    mode = 'x';
    verbose = 0;
    compress = '\0';
    flags = ARCHIVE_EXTRACT_TIME;

    /* Among other sins, getopt(3) pulls in printf(3). */
    while (*++argv != NULL && **argv == '-') {
        const char *p = *argv + 1;

        while ((opt = *p++) != '\0') {
            switch (opt) {
#ifndef NO_CREATE
            case 'c':
                mode = opt;
                break;
#endif
            case 'f':
                if (*p != '\0')
                    filename = p;
                else
                    filename = *++argv;
                p += strlen(p);
                break;
#ifndef NO_BZIP2_CREATE
            case 'j':
                compress = opt;
                break;
#endif
            case 'p':
                flags |= ARCHIVE_EXTRACT_PERM;
                flags |= ARCHIVE_EXTRACT_ACL;
                flags |= ARCHIVE_EXTRACT_FFLAGS;
                break;
            case 't':
                mode = opt;
                break;
            case 'v':
                verbose++;
                break;
            case 'x':
                mode = opt;
                break;
#ifndef NO_BZIP2_CREATE
            case 'y':
                compress = opt;
                break;
#endif
#ifndef NO_COMPRESS_CREATE
            case 'Z':
                compress = opt;
                break;
#endif
#ifndef NO_GZIP_CREATE
            case 'z':
                compress = opt;
                break;
#endif
            default:
                usage();
            }
        }
    }

    switch (mode) {
#ifndef NO_CREATE
    case 'c':
        create(filename, compress, argv);
        break;
#endif
    case 't':
        extract(filename, 0, flags);
        break;
    case 'x':
        extract(filename, 1, flags);
        break;
    }

    return (0);
}


#ifndef NO_CREATE
static char buff[16384];

static void
create(const char *filename, int compress, const char **argv)
{
    struct archive *a;
    struct archive *disk;
    struct archive_entry *entry;
    ssize_t len;
    int fd;

    a = archive_write_new();
    switch (compress) {
#ifndef NO_BZIP2_CREATE
    case 'j': case 'y':
        archive_write_set_compression_bzip2(a);
        break;
#endif
#ifndef NO_COMPRESS_CREATE
    case 'Z':
        archive_write_set_compression_compress(a);
        break;
#endif
#ifndef NO_GZIP_CREATE
    case 'z':
        archive_write_set_compression_gzip(a);
        break;
#endif
    default:
        archive_write_set_compression_none(a);
        break;
    }
    archive_write_set_format_ustar(a);
    if (strcmp(filename, "-") == 0)
        filename = NULL;
    archive_write_open_file(a, filename);

    disk = archive_read_disk_new();
#ifndef NO_LOOKUP
    archive_read_disk_set_standard_lookup(disk);
#endif
    while (*argv != NULL) {
        struct tree *t = tree_open(*argv);
        while (tree_next(t)) {
            entry = archive_entry_new();
            archive_entry_set_pathname(entry, tree_current_path(t));
            archive_read_disk_entry_from_file(disk, entry, -1,
                tree_current_stat(t));
            if (verbose) {
                msg("a ");
                msg(tree_current_path(t));
            }
            archive_write_header(a, entry);
            fd = open(tree_current_access_path(t), O_RDONLY);
            len = read(fd, buff, sizeof(buff));
            while (len > 0) {
                archive_write_data(a, buff, len);
                len = read(fd, buff, sizeof(buff));
            }
            close(fd);
            archive_entry_free(entry);
            if (verbose)
                msg("\n");
        }
        argv++;
    }
    archive_write_close(a);
    archive_write_finish(a);
}
#endif

static void
extract(const char *filename, int do_extract, int flags)
{
    struct archive *a;
    struct archive *ext;
    struct archive_entry *entry;
    int r;

    a = archive_read_new();
    ext = archive_write_disk_new();
    archive_write_disk_set_options(ext, flags);
#ifndef NO_BZIP2_EXTRACT
    archive_read_support_compression_bzip2(a);
#endif
#ifndef NO_GZIP_EXTRACT
    archive_read_support_compression_gzip(a);
#endif
#ifndef NO_COMPRESS_EXTRACT
    archive_read_support_compression_compress(a);
#endif
#ifndef NO_TAR_EXTRACT
    archive_read_support_format_tar(a);
#endif
#ifndef NO_CPIO_EXTRACT
    archive_read_support_format_cpio(a);
#endif
#ifndef NO_LOOKUP
    archive_write_disk_set_standard_lookup(ext);
#endif
    if (filename != NULL && strcmp(filename, "-") == 0)
        filename = NULL;
    if ((r = archive_read_open_file(a, filename, 10240))) {
        errmsg(archive_error_string(a));
        errmsg("\n");
        exit(r);
    }
    for (;;) {
        r = archive_read_next_header(a, &entry);
        if (r == ARCHIVE_EOF)
            break;
        if (r != ARCHIVE_OK) {
            errmsg(archive_error_string(a));
            errmsg("\n");
            exit(1);
        }
        if (verbose && do_extract)
            msg("x ");
        if (verbose || !do_extract)
            msg(archive_entry_pathname(entry));
        if (do_extract) {
            r = archive_write_header(ext, entry);
            if (r != ARCHIVE_OK)
                errmsg(archive_error_string(a));
            else
                copy_data(a, ext);
        }
        if (verbose || !do_extract)
            msg("\n");
    }
    archive_read_close(a);
    archive_read_finish(a);
    exit(0);
}

static int
copy_data(struct archive *ar, struct archive *aw)
{
    int r;
    const void *buff;
    size_t size;
    off_t offset;

    for (;;) {
        r = archive_read_data_block(ar, &buff, &size, &offset);
        if (r == ARCHIVE_EOF) {
            errmsg(archive_error_string(ar));
            return (ARCHIVE_OK);
        }
        if (r != ARCHIVE_OK)
            return (r);
        r = archive_write_data_block(aw, buff, size, offset);
        if (r != ARCHIVE_OK) {
            errmsg(archive_error_string(ar));
            return (r);
        }
    }
}

static void
msg(const char *m)
{
    write(1, m, strlen(m));
}

static void
errmsg(const char *m)
{
    write(2, m, strlen(m));
}

static void
usage(void)
{
/* Many program options depend on compile options. */
    const char *m = "Usage: minitar [-"
#ifndef NO_CREATE
        "c"
#endif
#ifndef NO_BZIP2
        "j"
#endif
        "tvx"
#ifndef NO_BZIP2
        "y"
#endif
#ifndef NO_COMPRESS
        "Z"
#endif
#ifndef NO_GZIP
        "z"
#endif
        "] [-f file] [file]\n";

    errmsg(m);
    exit(1);
}

--- NEW FILE: README ---
"minitar" is a minimal example of a program that uses libarchive to
read/write various archive formats.  It's a more ambitious version of
'untar.c' that includes compile-time options to enable/disable various
features, including non-tar formats, archive creation, and automatic
decompression support.

I use this as a test bed to check for "link pollution," ensuring that
a program using libarchive does not pull in unnecessary code.

The "minitar" program is also a good starting point for anyone who
wants to use libarchive for their own purposes, as it demonstrates
basic usage of the library.



More information about the Cmake-commits mailing list