summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@gmail.com>2008-04-30 18:11:33 -0400
committerTim Kientzle <kientzle@gmail.com>2008-04-30 18:11:33 -0400
commit6d04d9655f29085945526bc4f9921c4724895aa2 (patch)
treee17c677d005265f518cf7d40217d7f27027362d7 /examples
parent1d718c3b69287bc706f6a9e0e25d92eb74bbe8cf (diff)
downloadlibarchive-6d04d9655f29085945526bc4f9921c4724895aa2.tar.gz
Include 'examples' and 'contrib' dirs from libarchive portable distro.
SVN-Revision: 8
Diffstat (limited to 'examples')
-rw-r--r--examples/minitar/Makefile28
-rw-r--r--examples/minitar/README12
-rw-r--r--examples/minitar/minitar.c433
-rw-r--r--examples/minitar/tree.c423
-rw-r--r--examples/minitar/tree.h78
-rw-r--r--examples/tarfilter.c113
-rw-r--r--examples/untar.c211
7 files changed, 1298 insertions, 0 deletions
diff --git a/examples/minitar/Makefile b/examples/minitar/Makefile
new file mode 100644
index 00000000..485eb8d4
--- /dev/null
+++ b/examples/minitar/Makefile
@@ -0,0 +1,28 @@
+CFLAGS= \
+ -DNO_BZIP2_CREATE \
+ -DNO_BZIP2_EXTRACT \
+ -DNO_COMPRESS_EXTRACT \
+ -DNO_CPIO_EXTRACT \
+ -DNO_CREATE \
+ -DNO_GZIP_CREATE \
+ -DNO_GZIP_EXTRACT \
+
+# 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 $> -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 *~
diff --git a/examples/minitar/README b/examples/minitar/README
new file mode 100644
index 00000000..83f646cd
--- /dev/null
+++ b/examples/minitar/README
@@ -0,0 +1,12 @@
+"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.
diff --git a/examples/minitar/minitar.c b/examples/minitar/minitar.c
new file mode 100644
index 00000000..c9439169
--- /dev/null
+++ b/examples/minitar/minitar.c
@@ -0,0 +1,433 @@
+/*-
+ * 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.
+ */
+
+/*-
+ * This is a compact "tar" program whose primary goal is small size.
+ * Statically linked, it can be under 64k. 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 control what features get incorporated:
+ * 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_EXTRACT - Suppress compress(1) auto-detect and decompression.
+ * NO_COMPRESS - Implies NO_COMPRESS_EXTRACT
+ * 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_TAR_EXTRACT - Suppress tar extraction
+ *
+ * With all of the above options (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. Without any of the above options,
+ * you get a static executable of about 180k with a lot of very
+ * sophisticated modern features.
+ *
+ * Compare this to over 60k for: main(){printf("hello, world!");}
+ */
+
+#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.
+ */
+#ifdef NO_CREATE
+#undef NO_BZIP2_CREATE
+#define NO_BZIP2_CREATE
+#undef NO_GZIP_CREATE
+#define NO_GZIP_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
+
+/*
+ * 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
+
+/*
+ * NO_COMPRESS_EXTRACT and NO_COMPRESS are equivalent.
+ */
+#ifdef NO_COMPRESS_EXTRACT
+#undef NO_COMPRESS
+#define NO_COMPRESS
+#endif
+
+#ifdef NO_COMPRESS
+#undef NO_COMPRESS_EXTRACT
+#define NO_COMPRESS_EXTRACT
+#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_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_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_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);
+ while (*argv != NULL) {
+ struct tree *t = tree_open(*argv);
+ while (tree_next(t)) {
+ entry = archive_entry_new();
+ archive_entry_copy_stat(entry, tree_current_stat(t));
+ archive_entry_set_pathname(entry, tree_current_path(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
+ 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_GZIP
+ "z"
+#endif
+ "] [-f file] [file]\n";
+
+ errmsg(m);
+ exit(1);
+}
+
+#if 0
+/*
+ * These override functions in libc (which are called by libarchive).
+ * The libc functions are pretty large; this bit of subterfuge
+ * reduces the size of the executable by about 70%.
+ */
+struct passwd *getpwnam(const char *);
+struct group *getgrnam(const char *);
+
+struct passwd *
+getpwnam(const char *login)
+{
+ (void)login;
+ return (NULL);
+}
+
+struct group *
+getgrnam(const char *name)
+{
+ (void)name;
+ return (NULL);
+}
+#endif
diff --git a/examples/minitar/tree.c b/examples/minitar/tree.c
new file mode 100644
index 00000000..8af0b4d8
--- /dev/null
+++ b/examples/minitar/tree.c
@@ -0,0 +1,423 @@
+/*-
+ * 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
diff --git a/examples/minitar/tree.h b/examples/minitar/tree.h
new file mode 100644
index 00000000..554e6c2d
--- /dev/null
+++ b/examples/minitar/tree.h
@@ -0,0 +1,78 @@
+/*-
+ * 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 *);
diff --git a/examples/tarfilter.c b/examples/tarfilter.c
new file mode 100644
index 00000000..b7e08cfc
--- /dev/null
+++ b/examples/tarfilter.c
@@ -0,0 +1,113 @@
+/*
+ * This file is in the public domain.
+ *
+ * Feel free to use it as you wish.
+ */
+
+/*
+ * This example program reads an archive from stdin (which can be in
+ * any format recognized by libarchive) and writes certain entries to
+ * an uncompressed ustar archive on stdout. This is a template for
+ * many kinds of archive manipulation: converting formats, resetting
+ * ownership, inserting entries, removing entries, etc.
+ *
+ * To compile:
+ * gcc -Wall -o tarfilter tarfilter.c -larchive -lz -lbz2
+ */
+
+#include <sys/stat.h>
+#include <archive.h>
+#include <archive_entry.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+die(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char buff[8192];
+ ssize_t len;
+ int r;
+ mode_t m;
+ struct archive *ina;
+ struct archive *outa;
+ struct archive_entry *entry;
+
+ /* Read an archive from stdin, with automatic format detection. */
+ ina = archive_read_new();
+ if (ina == NULL)
+ die("Couldn't create archive reader.");
+ if (archive_read_support_compression_all(ina) != ARCHIVE_OK)
+ die("Couldn't enable decompression");
+ if (archive_read_support_format_all(ina) != ARCHIVE_OK)
+ die("Couldn't enable read formats");
+ if (archive_read_open_fd(ina, 0, 10240) != ARCHIVE_OK)
+ die("Couldn't open input archive");
+
+ /* Write an uncompressed ustar archive to stdout. */
+ outa = archive_write_new();
+ if (outa == NULL)
+ die("Couldn't create archive writer.");
+ if (archive_write_set_compression_none(outa) != ARCHIVE_OK)
+ die("Couldn't enable compression");
+ if (archive_write_set_format_ustar(outa) != ARCHIVE_OK)
+ die("Couldn't set output format");
+ if (archive_write_open_fd(outa, 1) != ARCHIVE_OK)
+ die("Couldn't open output archive");
+
+ /* Examine each entry in the input archive. */
+ while ((r = archive_read_next_header(ina, &entry)) == ARCHIVE_OK) {
+ fprintf(stderr, "%s: ", archive_entry_pathname(entry));
+
+ /* Skip anything that isn't a regular file. */
+ if (!S_ISREG(archive_entry_mode(entry))) {
+ fprintf(stderr, "skipped\n");
+ continue;
+ }
+
+ /* Make everything owned by root/wheel. */
+ archive_entry_set_uid(entry, 0);
+ archive_entry_set_uname(entry, "root");
+ archive_entry_set_gid(entry, 0);
+ archive_entry_set_gname(entry, "wheel");
+
+ /* Make everything permission 0744, strip SUID, etc. */
+ m = archive_entry_mode(entry);
+ archive_entry_set_mode(entry, (m & ~07777) | 0744);
+
+ /* Copy input entries to output archive. */
+ if (archive_write_header(outa, entry) != ARCHIVE_OK)
+ die("Error writing output archive");
+ if (archive_entry_size(entry) > 0) {
+ len = archive_read_data(ina, buff, sizeof(buff));
+ while (len > 0) {
+ if (archive_write_data(outa, buff, len) != len)
+ die("Error writing output archive");
+ len = archive_read_data(ina, buff, sizeof(buff));
+ }
+ if (len < 0)
+ die("Error reading input archive");
+ }
+ fprintf(stderr, "copied\n");
+ }
+ if (r != ARCHIVE_EOF)
+ die("Error reading archive");
+ /* Close the archives. */
+ if (archive_read_finish(ina) != ARCHIVE_OK)
+ die("Error closing input archive");
+ if (archive_write_finish(outa) != ARCHIVE_OK)
+ die("Error closing output archive");
+ return (0);
+}
diff --git a/examples/untar.c b/examples/untar.c
new file mode 100644
index 00000000..88f6dc26
--- /dev/null
+++ b/examples/untar.c
@@ -0,0 +1,211 @@
+/*
+ * This file is in the public domain.
+ * Use it as you wish.
+ */
+
+/*
+ * This is a compact tar extraction program whose primary goal is
+ * small size. Statically linked, it can be under 64k, depending on
+ * how cleanly factored your system libraries are. Note that this
+ * uses the standard libarchive, without any special recompilation.
+ * The only functional concession is that this program uses the
+ * uid/gid from the archive instead of doing uname/gname lookups.
+ * (Call archive_write_disk_set_standard_lookup() to enable
+ * uname/gname lookups, but be aware that this can add 500k or more to
+ * a static executable, depending on the system libraries.)
+ *
+ * To build:
+ * gcc -static -Wall -o untar untar.c -larchive
+ * strip untar
+ *
+ * For fun, statically compile the following simple hello.c program
+ * and compare the size. (On my system, the result is 89k, untar is
+ * 69k.)
+ *
+ * #include <stdio.h>
+ * int main(int argc, char **argv) {
+ * printf("hello, world\n");
+ * return(0);
+ * }
+ */
+
+#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>
+
+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) {
+ case 'f':
+ if (*p != '\0')
+ filename = p;
+ else
+ filename = *++argv;
+ p += strlen(p);
+ break;
+ 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;
+ default:
+ usage();
+ }
+ }
+ }
+
+ switch (mode) {
+ case 't':
+ extract(filename, 0, flags);
+ break;
+ case 'x':
+ extract(filename, 1, flags);
+ break;
+ }
+
+ return (0);
+}
+
+
+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);
+ /*
+ * Note: archive_write_disk_set_standard_lookup() is useful
+ * here, but it requires library routines that can add 500k or
+ * more to a static executable.
+ */
+ archive_read_support_format_tar(a);
+ /*
+ * On my system, enabling other archive formats adds 20k-30k
+ * each. Enabling gzip decompression adds about 20k.
+ * Enabling bzip2 is more expensive because the libbz2 library
+ * isn't very well factored.
+ */
+ 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)
+{
+ const char *m = "Usage: untar [-tvx] [-f file] [file]\n";
+ errmsg(m);
+ exit(1);
+}