/* A tar (tape archiver) program.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
- 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
Written by John Gilmore, starting 1985-08-25.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any later
+ Free Software Foundation; either version 3, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but
#include <system.h>
#include <fnmatch.h>
-#include <getline.h>
#include <argp.h>
#include <argp-namefrob.h>
#include <argp-fmtstream.h>
#include <argmatch.h>
#include <closeout.h>
+#include <configmake.h>
#include <exitfail.h>
#include <getdate.h>
-#include <localedir.h>
#include <rmt.h>
+#include <rmt-command.h>
#include <prepargs.h>
#include <quotearg.h>
#include <version-etc.h>
DELAY_DIRECTORY_RESTORE_OPTION,
DELETE_OPTION,
EXCLUDE_CACHES_OPTION,
+ EXCLUDE_CACHES_UNDER_OPTION,
+ EXCLUDE_CACHES_ALL_OPTION,
EXCLUDE_OPTION,
+ EXCLUDE_TAG_OPTION,
+ EXCLUDE_TAG_UNDER_OPTION,
+ EXCLUDE_TAG_ALL_OPTION,
FORCE_LOCAL_OPTION,
GROUP_OPTION,
HANG_OPTION,
const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
-static char doc[] = N_("GNU `tar' saves many files together into a single tape or disk archive, and can restore individual files from the archive.\n\
+static char const doc[] = N_("\
+GNU `tar' saves many files together into a single tape or disk archive, \
+and can restore individual files from the archive.\n\
\n\
Examples:\n\
tar -cf archive.tar foo bar # Create archive.tar from files foo and bar.\n\
tar -tvf archive.tar # List all files in archive.tar verbosely.\n\
- tar -xf archive.tar # Extract all files from archive.tar.\n\
-\vThe backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+ tar -xf archive.tar # Extract all files from archive.tar.\n")
+"\v"
+N_("The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control may be set with --backup or VERSION_CONTROL, values are:\n\n\
none, off never make backups\n\
t, numbered make numbered backups\n\
#define GRID 30
{NULL, 0, NULL, 0,
- N_("Overwrite control:"), GRID+1 },
+ N_("Overwrite control:"), GRID },
{"verify", 'W', 0, 0,
N_("attempt to verify the archive after writing it"), GRID+1 },
#define GRID 60
{NULL, 0, NULL, 0,
- N_("Device selection and switching:\n"), GRID+1 },
+ N_("Device selection and switching:"), GRID },
{"file", 'f', N_("ARCHIVE"), 0,
N_("use archive file or device ARCHIVE"), GRID+1 },
#define GRID 70
{NULL, 0, NULL, 0,
- N_("Device blocking:"), GRID+1 },
+ N_("Device blocking:"), GRID },
{"blocking-factor", 'b', N_("BLOCKS"), 0,
N_("BLOCKS x 512 bytes per record"), GRID+1 },
{"exclude-from", 'X', N_("FILE"), 0,
N_("exclude patterns listed in FILE"), GRID+1 },
{"exclude-caches", EXCLUDE_CACHES_OPTION, 0, 0,
- N_("exclude directories containing a cache tag"), GRID+1 },
+ N_("exclude contents of directories containing CACHEDIR.TAG, "
+ "except for the tag file itself"), GRID+1 },
+ {"exclude-caches-under", EXCLUDE_CACHES_UNDER_OPTION, 0, 0,
+ N_("exclude everything under directories containing CACHEDIR.TAG"),
+ GRID+1 },
+ {"exclude-caches-all", EXCLUDE_CACHES_ALL_OPTION, 0, 0,
+ N_("exclude directories containing CACHEDIR.TAG"), GRID+1 },
+ {"exclude-tag", EXCLUDE_TAG_OPTION, N_("FILE"), 0,
+ N_("exclude contents of directories containing FILE, except"
+ " for FILE itself"), GRID+1 },
+ {"exclude-tag-under", EXCLUDE_TAG_UNDER_OPTION, N_("FILE"), 0,
+ N_("exclude everything under directories containing FILE"), GRID+1 },
+ {"exclude-tag-all", EXCLUDE_TAG_ALL_OPTION, N_("FILE"), 0,
+ N_("exclude directories containing FILE"), GRID+1 },
{"no-recursion", NO_RECURSION_OPTION, 0, 0,
N_("avoid descending automatically in directories"), GRID+1 },
{"one-file-system", ONE_FILE_SYSTEM_OPTION, 0, 0,
N_("use sed replace EXPRESSION to transform file names"), GRID+1 },
#undef GRID
-#define GRID 95
+#define GRID 95
{NULL, 0, NULL, 0,
N_("File name matching options (affect both exclude and include patterns):"),
GRID },
{"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0,
N_("wildcards match `/' (default for exclusion)"), GRID+1 },
#undef GRID
-
+
#define GRID 100
{NULL, 0, NULL, 0,
N_("Informative output:"), GRID },
| (args)->matching_flags \
| recursion_option)
-#ifdef REMOTE_SHELL
+#ifdef REMOTE_SHELL
# define DECL_SHOW_DEFAULT_SETTINGS(stream, printer) \
{ \
printer (stream, \
static void
show_default_settings (FILE *fp)
DECL_SHOW_DEFAULT_SETTINGS(fp, fprintf)
-
+
static void
show_default_settings_fs (argp_fmtstream_t fs)
DECL_SHOW_DEFAULT_SETTINGS(fs, argp_fmtstream_printf)
{ "QUIT", SIGQUIT }
};
struct sigtab *p;
-
+
for (p = sigtab; p < sigtab + sizeof (sigtab) / sizeof (sigtab[0]); p++)
if (strcmp (p->name, name) == 0)
{
{
file_list_success, /* OK, name read successfully */
file_list_end, /* End of list file */
- file_list_zero /* Zero separator encountered where it should not */
+ file_list_zero, /* Zero separator encountered where it should not */
+ file_list_skip /* Empty (zero-length) entry encountered, skip it */
};
/* Read from FP a sequence of characters up to FILENAME_TERMINATOR and put them
{
/* We have read a zero separator. The file possibly is
zero-separated */
- /* FATAL_ERROR((0, 0, N_("file name contains null character"))); */
return file_list_zero;
}
obstack_1grow (stk, c);
counter++;
}
+ if (counter == 0 && c != EOF)
+ return file_list_skip;
+
obstack_1grow (stk, 0);
return (counter == 0 && c == EOF) ? file_list_end : file_list_success;
open_fatal (filename);
}
- while ((read_state = read_name_from_file (fp, &argv_stk)) == file_list_success)
- count++;
-
- if (read_state == file_list_zero)
+ while ((read_state = read_name_from_file (fp, &argv_stk)) != file_list_end)
{
- size_t size;
+ switch (read_state)
+ {
+ case file_list_success:
+ count++;
+ break;
- WARN ((0, 0, N_("%s: file name read contains nul character"),
- quotearg_colon (filename)));
+ case file_list_end: /* won't happen, just to pacify gcc */
+ break;
- /* Prepare new stack contents */
- size = obstack_object_size (&argv_stk);
- p = obstack_finish (&argv_stk);
- for (; size > 0; size--, p++)
- if (*p)
- obstack_1grow (&argv_stk, *p);
- else
- obstack_1grow (&argv_stk, '\n');
- obstack_1grow (&argv_stk, 0);
- count = 1;
-
- /* Read rest of files using new filename terminator */
- filename_terminator = 0;
- while (read_name_from_file (fp, &argv_stk) == file_list_success)
- count++;
+ case file_list_zero:
+ {
+ size_t size;
+
+ WARN ((0, 0, N_("%s: file name read contains nul character"),
+ quotearg_colon (filename)));
+
+ /* Prepare new stack contents */
+ size = obstack_object_size (&argv_stk);
+ p = obstack_finish (&argv_stk);
+ for (; size > 0; size--, p++)
+ if (*p)
+ obstack_1grow (&argv_stk, *p);
+ else
+ obstack_1grow (&argv_stk, '\n');
+ obstack_1grow (&argv_stk, 0);
+ count = 1;
+ /* Read rest of files using new filename terminator */
+ filename_terminator = 0;
+ break;
+ }
+
+ case file_list_skip:
+ break;
+ }
}
if (!is_stdin)
ARGP_HELP_STD_HELP & ~ARGP_HELP_BUG_ADDR);
/* FIXME: use struct uparams.rmargin (from argp-help.c) instead of 79 */
fs = argp_make_fmtstream (state->out_stream, 0, 79, 0);
-
+
argp_fmtstream_printf (fs, "\n%s\n\n",
_("Valid arguments for --quoting-style options are:"));
tar_list_quoting_styles (fs, " ");
-
+
argp_fmtstream_puts (fs, _("\n*This* tar defaults to:\n"));
show_default_settings_fs (fs);
argp_fmtstream_putc (fs, '\n');
switch (key)
{
- case ARGP_KEY_ARG:
- /* File name or non-parsed option, because of ARGP_IN_ORDER */
- name_add_name (arg, MAKE_INCL_OPTIONS (args));
- args->input_files = true;
- break;
+ case ARGP_KEY_ARG:
+ /* File name or non-parsed option, because of ARGP_IN_ORDER */
+ name_add_name (arg, MAKE_INCL_OPTIONS (args));
+ args->input_files = true;
+ break;
case 'A':
set_subcommand_option (CAT_SUBCOMMAND);
get_date_or_file (args, "--mtime", arg, &mtime_option);
set_mtime_option = true;
break;
-
+
case 'n':
seekable_archive = true;
break;
}
}
break;
-
+
case 't':
set_subcommand_option (LIST_SUBCOMMAND);
verbose_option++;
break;
case EXCLUDE_CACHES_OPTION:
- exclude_caches_option = true;
+ add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_contents,
+ cachedir_file_p);
+ break;
+
+ case EXCLUDE_CACHES_UNDER_OPTION:
+ add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_under,
+ cachedir_file_p);
+ break;
+
+ case EXCLUDE_CACHES_ALL_OPTION:
+ add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_all,
+ cachedir_file_p);
+ break;
+
+ case EXCLUDE_TAG_OPTION:
+ add_exclusion_tag (arg, exclusion_tag_contents, NULL);
+ break;
+
+ case EXCLUDE_TAG_UNDER_OPTION:
+ add_exclusion_tag (arg, exclusion_tag_under, NULL);
+ break;
+
+ case EXCLUDE_TAG_ALL_OPTION:
+ add_exclusion_tag (arg, exclusion_tag_all, NULL);
break;
case FORCE_LOCAL_OPTION:
case TRANSFORM_OPTION:
set_transform_expr (arg);
break;
-
+
case USE_COMPRESS_PROGRAM_OPTION:
set_use_compress_program_option (arg);
break;
/* Parse the options for tar. */
static struct argp_option *
-find_argp_option (struct argp_option *options, int letter)
+find_argp_option (struct argp_option *o, int letter)
{
for (;
- !(options->name == NULL
- && options->key == 0
- && options->arg == 0
- && options->flags == 0
- && options->doc == NULL); options++)
- if (options->key == letter)
- return options;
+ !(o->name == NULL
+ && o->key == 0
+ && o->arg == 0
+ && o->flags == 0
+ && o->doc == NULL); o++)
+ if (o->key == letter)
+ return o;
return NULL;
}
static void
decode_options (int argc, char **argv)
{
- int index;
+ int idx;
struct tar_args args;
/* Set some default option values. */
args.backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
args.version_control_string = 0;
args.input_files = false;
-
+
subcommand_option = UNKNOWN_SUBCOMMAND;
archive_format = DEFAULT_FORMAT;
blocking_factor = DEFAULT_BLOCKING;
unquote_option = true;
tar_sparse_major = 1;
tar_sparse_minor = 0;
-
+
owner_option = -1;
group_option = -1;
prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv);
if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_HELP,
- &index, &args))
+ &idx, &args))
exit (TAREXIT_FAILURE);
}
/* Handle operands after any "--" argument. */
- for (; index < argc; index++)
+ for (; idx < argc; idx++)
{
- name_add_name (argv[index], MAKE_INCL_OPTIONS (&args));
+ name_add_name (argv[idx], MAKE_INCL_OPTIONS (&args));
args.input_files = true;
}
/* Warn about implicit use of the wildcards in command line arguments.
See TODO */
warn_regex_usage = args.wildcards == default_wildcards;
-
+
/* Derive option values and check option consistency. */
if (archive_format == DEFAULT_FORMAT)
| FORMAT_MASK (GNU_FORMAT)
| FORMAT_MASK (POSIX_FORMAT));
- if (multi_volume_option
- && archive_format == POSIX_FORMAT
- && subcommand_option == CREATE_SUBCOMMAND
- && !tape_length_option)
- USAGE_ERROR ((0, 0,
- _("creating multi-volume archives in posix format requires using --tape-length (-L) option")));
-
if (occurrence_option)
{
if (!args.input_files)
/* Make sure we have first three descriptors available */
stdopen ();
- /* Close all inherited open descriptors, except for the first three */
- closeopen ();
-
/* Pre-allocate a few structures. */
allocated_archive_names = 10;
/* Decode options. */
decode_options (argc, argv);
+
name_init ();
/* Main command execution. */
free (archive_name_array);
name_term ();
- if (stdlis == stdout)
- close_stdout ();
-
if (exit_status == TAREXIT_FAILURE)
error (0, 0, _("Error exit delayed from previous errors"));
- if (ferror (stderr) || fclose (stderr) != 0)
+
+ if (stdlis == stdout)
+ close_stdout ();
+ else if (ferror (stderr) || fclose (stderr) != 0)
exit_status = TAREXIT_FAILURE;
+
return exit_status;
}
free (st->gname);
free (st->sparse_map);
free (st->dumpdir);
+ xheader_destroy (&st->xhdr);
memset (st, 0, sizeof (*st));
}