Include 'examples' and 'contrib' dirs from libarchive portable distro.
SVN-Revision: 8
This commit is contained in:
1 parent
1d718c3b69
commit
6d04d9655f
11 files changed
+1791
No files matched your search
@@ -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 *~
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 *);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user