Augment the -T handling:

* Add --null option (sort #defines here)
 * Add process_lines function to util.c that reads newline-terminated
   or null-terminated lines (with self-sizing buffers, etc) and iteratively
   invokes a provided function.  Use this to dramatically simplify:
   -T handling for -c, --exclude-from-file, and --include-from-file.
 * Add -T handling to -x (via include_from_file)

Hopefully, this will fix the openoffice port and a couple of
others that rely on -T and --null.
This commit is contained in:
Tim Kientzle 2004-06-27 06:29:03 +00:00
parent 30d140332e
commit d3d1a208cb
6 changed files with 161 additions and 98 deletions

View File

@ -91,11 +91,12 @@ const char *tar_opts = "+Bb:C:cF:f:HhjkLlmnOoPprtT:UuvW:wX:xyZz";
/* Fake short equivalents for long options that otherwise lack them. */
#define OPTION_EXCLUDE 1
#define OPTION_FAST_READ 2
#define OPTION_NODUMP 3
#define OPTION_HELP 4
#define OPTION_INCLUDE 5
#define OPTION_ONE_FILE_SYSTEM 6
#define OPTION_NO_SAME_PERMISSIONS 7
#define OPTION_HELP 3
#define OPTION_INCLUDE 4
#define OPTION_NODUMP 5
#define OPTION_NO_SAME_PERMISSIONS 6
#define OPTION_NULL 7
#define OPTION_ONE_FILE_SYSTEM 8
const struct option tar_longopts[] = {
{ "absolute-paths", no_argument, NULL, 'P' },
@ -127,6 +128,7 @@ const struct option tar_longopts[] = {
{ "norecurse", no_argument, NULL, 'n' },
{ "no-same-owner", no_argument, NULL, 'o' },
{ "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS },
{ "null", no_argument, NULL, OPTION_NULL },
{ "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM },
{ "preserve-permissions", no_argument, NULL, 'p' },
{ "read-full-blocks", no_argument, NULL, 'B' },
@ -199,10 +201,7 @@ main(int argc, char **argv)
while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) {
switch (opt) {
case 'B': /* GNU tar */
/*
* bsdtar is stream-based internally, so this
* option has no effect. Just ignore it.
*/
/* libarchive doesn't need this; just ignore it. */
break;
case 'b': /* SUSv2 */
bsdtar->bytes_per_block = 512 * atoi(optarg);
@ -280,6 +279,9 @@ main(int argc, char **argv)
* command-line option as a no-op.
*/
break;
case OPTION_NULL: /* GNU tar */
bsdtar->option_null++;
break;
case 'O': /* GNU tar */
bsdtar->option_stdout = 1;
break;
@ -314,6 +316,9 @@ main(int argc, char **argv)
opt, mode);
mode = opt;
break;
case 'T': /* GNU tar */
bsdtar->names_from_file = optarg;
break;
case 't': /* SUSv2 */
if (mode != '\0')
bsdtar_errc(bsdtar, 1, 0,
@ -322,9 +327,6 @@ main(int argc, char **argv)
mode = opt;
bsdtar->verbose++;
break;
case 'T': /* GNU tar */
bsdtar->names_from_file = optarg;
break;
case 'U': /* GNU tar */
bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
bsdtar->option_unlink_first = 1;
@ -366,7 +368,7 @@ main(int argc, char **argv)
bsdtar->create_compression);
bsdtar->create_compression = opt;
break;
case 'z': /* GNU tar, star */
case 'z': /* GNU tar, star, many others */
if (bsdtar->create_compression != '\0')
bsdtar_errc(bsdtar, 1, 0,
"Can't specify both -%c and -%c", opt,
@ -418,10 +420,8 @@ main(int argc, char **argv)
}
if (bsdtar->create_format != NULL)
only_mode(bsdtar, mode, "-F", "c");
if (bsdtar->names_from_file != NULL)
only_mode(bsdtar, mode, "-T", "cru");
if (bsdtar->symlink_mode != '\0') {
strcpy(buff, "-X");
strcpy(buff, "-?");
buff[1] = bsdtar->symlink_mode;
only_mode(bsdtar, mode, buff, "cru");
}

View File

@ -59,6 +59,7 @@ struct bsdtar {
char option_honor_nodump; /* --nodump */
char option_interactive; /* -w */
char option_no_subdirs; /* -d */
char option_null; /* --null */
char option_stdout; /* -p */
char option_unlink_first; /* -U */
char option_warn_links; /* -l */
@ -67,6 +68,7 @@ struct bsdtar {
int fd;
/* Miscellaneous state information */
struct archive *archive;
const char *progname;
int argc;
char **argv;
@ -76,6 +78,7 @@ struct bsdtar {
uid_t user_uid; /* UID running this program */
int return_value; /* Value returned by main() */
char warned_lead_slash; /* Already displayed warning */
char next_line_is_dir; /* Used for -C parsing in -cT */
/*
* Data for various subsystems. Full definitions are located in
@ -94,10 +97,13 @@ void bsdtar_errc(struct bsdtar *, int _eval, int _code,
void bsdtar_strmode(struct archive_entry *entry, char *bp);
void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...);
void cleanup_exclusions(struct bsdtar *);
void exclude(struct bsdtar *, const char *pattern);
void exclude_from_file(struct bsdtar *, const char *pathname);
int exclude(struct bsdtar *, const char *pattern);
int exclude_from_file(struct bsdtar *, const char *pathname);
int excluded(struct bsdtar *, const char *pathname);
void include(struct bsdtar *, const char *pattern);
int include(struct bsdtar *, const char *pattern);
int include_from_file(struct bsdtar *, const char *pathname);
int process_lines(struct bsdtar *bsdtar, const char *pathname,
int (*process)(struct bsdtar *, const char *));
void safe_fprintf(FILE *, const char *fmt, ...);
void tar_mode_c(struct bsdtar *bsdtar);
void tar_mode_r(struct bsdtar *bsdtar);

View File

@ -56,17 +56,17 @@ static int match_exclusion(struct match *, const char *pathname);
static int match_inclusion(struct match *, const char *pathname);
/*
* The matching logic here needs to be re-thought. I started
* out to try to mimic gtar's matching logic, but found it wasn't
* really consistent. In particular 'tar -t' and 'tar -x' interpret
* patterns on the command line as anchored, but --exclude doesn't.
* The matching logic here needs to be re-thought. I started out to
* try to mimic gtar's matching logic, but it's not entirely
* consistent. In particular 'tar -t' and 'tar -x' interpret patterns
* on the command line as anchored, but --exclude doesn't.
*/
/*
* Utility functions to manage exclusion/inclusion patterns
*/
void
int
exclude(struct bsdtar *bsdtar, const char *pattern)
{
struct matching *matching;
@ -76,47 +76,16 @@ exclude(struct bsdtar *bsdtar, const char *pattern)
matching = bsdtar->matching;
add_pattern(bsdtar, &(matching->exclusions), pattern);
matching->exclusions_count++;
return (0);
}
/*
* Read lines from file and exclude() each one. This uses
* a self-sizing buffer to handle arbitrarily-long lines.
*/
void
int
exclude_from_file(struct bsdtar *bsdtar, const char *pathname)
{
FILE *f;
char *buff;
size_t buff_length, line_length;
f = fopen(pathname, "r");
if (f == NULL)
bsdtar_errc(bsdtar, 1, errno, "Couldn't open %s", pathname);
buff_length = 256;
buff = malloc(buff_length);
if (buff == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read %s", pathname);
while(fgets(buff, buff_length, f) != NULL) {
line_length = strlen(buff);
while (buff[line_length - 1] != '\n') {
buff = realloc(buff, buff_length *= 2);
if (buff == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM,
"Line too long in %s", pathname);
if (fgets(buff + line_length,
buff_length - line_length, f) == NULL)
bsdtar_errc(bsdtar, 1, 0,
"Bad input line in %s", pathname);
line_length = strlen(buff);
}
buff[line_length - 1] = '\0';
exclude(bsdtar, buff);
}
free(buff);
fclose(f);
return (process_lines(bsdtar, pathname, &exclude));
}
void
int
include(struct bsdtar *bsdtar, const char *pattern)
{
struct matching *matching;
@ -127,6 +96,13 @@ include(struct bsdtar *bsdtar, const char *pattern)
add_pattern(bsdtar, &(matching->inclusions), pattern);
matching->inclusions_count++;
matching->inclusions_unmatched_count++;
return (0);
}
int
include_from_file(struct bsdtar *bsdtar, const char *pathname)
{
return (process_lines(bsdtar, pathname, &include));
}
static void

View File

@ -77,6 +77,9 @@ read_archive(struct bsdtar *bsdtar, char mode)
bsdtar->argv++;
}
if (bsdtar->names_from_file != NULL)
include_from_file(bsdtar, bsdtar->names_from_file);
a = archive_read_new();
archive_read_support_compression_all(a);
archive_read_support_format_all(a);

View File

@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h> /* Linux doesn't define mode_t, etc. in sys/stat.h. */
#include <archive_entry.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -234,3 +235,88 @@ bsdtar_strmode(struct archive_entry *entry, char *bp)
if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
bp[10] = '+';
}
/*
* Read lines from file and do something with each one. If option_null
* is set, lines are terminated with zero bytes; otherwise, they're
* terminated with newlines.
*
* This uses a self-sizing buffer to handle arbitrarily-long lines.
* If the "process" function returns non-zero for any line, this
* function will return non-zero after attempting to process all
* remaining lines.
*/
int
process_lines(struct bsdtar *bsdtar, const char *pathname,
int (*process)(struct bsdtar *, const char *))
{
FILE *f;
char *buff, *buff_end, *line_start, *line_end, *p;
size_t buff_length, bytes_read, bytes_wanted;
int separator;
int ret;
separator = bsdtar->option_null ? '\0' : '\n';
ret = 0;
if (strcmp(pathname, "-") == 0)
f = stdin;
else
f = fopen(pathname, "r");
if (f == NULL)
bsdtar_errc(bsdtar, 1, errno, "Couldn't open %s", pathname);
buff_length = 8192;
buff = malloc(buff_length);
if (buff == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read %s", pathname);
line_start = line_end = buff_end = buff;
for (;;) {
/* Get some more data into the buffer. */
bytes_wanted = buff + buff_length - buff_end;
bytes_read = fread(buff_end, 1, bytes_wanted, f);
buff_end += bytes_read;
/* Process all complete lines in the buffer. */
while (line_end < buff_end) {
if (*line_end == separator) {
*line_end = '\0';
if ((*process)(bsdtar, line_start) != 0)
ret = -1;
line_start = line_end + 1;
line_end = line_start;
} else
line_end++;
}
if (feof(f))
break;
if (ferror(f))
bsdtar_errc(bsdtar, 1, errno,
"Can't read %s", pathname);
if (line_start > buff) {
/* Move a leftover fractional line to the beginning. */
memmove(buff, line_start, buff_end - line_start);
buff_end -= line_start - buff;
line_end -= line_start - buff;
line_start = buff;
} else {
/* Line is too big; enlarge the buffer. */
p = realloc(buff, buff_length *= 2);
if (buff == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM,
"Line too long in %s", pathname);
buff_end = p + (buff_end - buff);
line_end = p + (line_end - buff);
line_start = buff = p;
}
}
/* At end-of-file, handle the final line. */
if (line_end > line_start) {
*line_end = '\0';
if ((*process)(bsdtar, line_start) != 0)
ret = -1;
}
free(buff);
if (f != stdin)
fclose(f);
return (ret);
}

View File

@ -103,6 +103,8 @@ static int append_archive(struct bsdtar *, struct archive *,
const char *fname);
static void archive_names_from_file(struct bsdtar *bsdtar,
struct archive *a);
static int archive_names_from_file_helper(struct bsdtar *bsdtar,
const char *line);
static void create_cleanup(struct bsdtar *);
static void free_cache(struct name_cache *cache);
static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid);
@ -447,50 +449,40 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
create_cleanup(bsdtar);
}
/* Archive names specified in file. */
/*
* Archive names specified in file.
*
* Unless --null was specified, a line containing exactly "-C" will
* cause the next line to be a directory to pass to chdir(). If
* --null is specified, then a line "-C" is just another filename.
*/
void
archive_names_from_file(struct bsdtar *bsdtar, struct archive *a)
{
FILE *f;
char buff[1024];
int l;
bsdtar->archive = a;
if (strcmp(bsdtar->names_from_file, "-") == 0)
f = stdin;
bsdtar->next_line_is_dir = 0;
process_lines(bsdtar, bsdtar->names_from_file,
archive_names_from_file_helper);
if (bsdtar->next_line_is_dir)
bsdtar_errc(bsdtar, 1, errno,
"Unexpected end of filename list; "
"directory expected after -C");
}
static int
archive_names_from_file_helper(struct bsdtar *bsdtar, const char *line)
{
if (bsdtar->next_line_is_dir) {
if (chdir(line) != 0)
bsdtar_errc(bsdtar, 1, errno,
"chdir(%s) failed", line);
bsdtar->next_line_is_dir = 0;
} else if (!bsdtar->option_null && strcmp(line, "-C") == 0)
bsdtar->next_line_is_dir = 1;
else
f = fopen(bsdtar->names_from_file, "r");
while (fgets(buff, sizeof(buff), f) != NULL) {
l = strlen(buff);
if (buff[l-1] == '\n')
buff[l-1] = '\0';
/*
* This -C processing hack is really quite ugly, but
* gtar does it this way and pkg_create depends on it,
* so I guess I'm stuck having to implement it.
*
* Someday, pkg_create will just use libarchive directly
* and this will just be a bad memory.
*/
if (strcmp(buff, "-C") == 0) {
if (fgets(buff, sizeof(buff), f) == NULL)
bsdtar_errc(bsdtar, 1, errno,
"Unexpected end of filename list; "
"directory expected after -C");
l = strlen(buff);
if (buff[l-1] == '\n')
buff[l-1] = '\0';
if (chdir(buff))
bsdtar_errc(bsdtar, 1, errno,
"chdir(%s) failed", buff);
} else {
write_heirarchy(bsdtar, a, buff);
}
}
if (strcmp(bsdtar->names_from_file, "-") != 0)
fclose(f);
write_heirarchy(bsdtar, bsdtar->archive, line);
return (0);
}
/*