/* A tar (tape archiver) program.
- Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
- 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright 1988, 1992-1997, 1999-2001, 2003-2007, 2012-2013 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
Public License for more details.
You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <system.h>
#include <fnmatch.h>
-#include <getline.h>
#include <argp.h>
#include <argp-namefrob.h>
#include <argp-fmtstream.h>
+#include <argp-version-etc.h>
#include <signal.h>
#if ! defined SIGCHLD && defined SIGCLD
#include <closeout.h>
#include <configmake.h>
#include <exitfail.h>
-#include <getdate.h>
+#include <parse-datetime.h>
#include <rmt.h>
#include <rmt-command.h>
#include <prepargs.h>
#include <version-etc.h>
#include <xstrtol.h>
#include <stdopen.h>
+#include <priv-set.h>
/* Local declarations. */
# define DEFAULT_BLOCKING 20
#endif
+/* Print a message if not all links are dumped */
+static int check_links_option;
+
+/* Number of allocated tape drive names. */
+static size_t allocated_archive_names;
+
\f
/* Miscellaneous. */
request_stdin (const char *option)
{
if (stdin_used_by)
- USAGE_ERROR ((0, 0, _("Options `-%s' and `-%s' both want standard input"),
+ USAGE_ERROR ((0, 0, _("Options '-%s' and '-%s' both want standard input"),
stdin_used_by, option));
stdin_used_by = option;
case UPDATE_SUBCOMMAND:
return "-u";
- default:
- abort ();
+ case TEST_LABEL_SUBCOMMAND:
+ return "--test-label";
}
+ abort ();
}
-void
-tar_list_quoting_styles (argp_fmtstream_t fs, char *prefix)
+static void
+tar_list_quoting_styles (struct obstack *stk, char const *prefix)
{
int i;
+ size_t prefixlen = strlen (prefix);
for (i = 0; quoting_style_args[i]; i++)
- argp_fmtstream_printf (fs, "%s%s\n", prefix, quoting_style_args[i]);
+ {
+ obstack_grow (stk, prefix, prefixlen);
+ obstack_grow (stk, quoting_style_args[i],
+ strlen (quoting_style_args[i]));
+ obstack_1grow (stk, '\n');
+ }
}
-void
+static void
tar_set_quoting_style (char *arg)
{
int i;
return;
}
FATAL_ERROR ((0, 0,
- _("Unknown quoting style `%s'. Try `%s --quoting-style=help' to get a list."), arg, program_invocation_short_name));
+ _("Unknown quoting style '%s'. Try '%s --quoting-style=help' to get a list."), arg, program_invocation_short_name));
}
\f
enum
{
- ANCHORED_OPTION = CHAR_MAX + 1,
+ ACLS_OPTION = CHAR_MAX + 1,
+ ANCHORED_OPTION,
ATIME_PRESERVE_OPTION,
BACKUP_OPTION,
+ CHECK_DEVICE_OPTION,
CHECKPOINT_OPTION,
+ CHECKPOINT_ACTION_OPTION,
DELAY_DIRECTORY_RESTORE_OPTION,
+ HARD_DEREFERENCE_OPTION,
DELETE_OPTION,
+ EXCLUDE_BACKUPS_OPTION,
EXCLUDE_CACHES_OPTION,
EXCLUDE_CACHES_UNDER_OPTION,
EXCLUDE_CACHES_ALL_OPTION,
EXCLUDE_TAG_OPTION,
EXCLUDE_TAG_UNDER_OPTION,
EXCLUDE_TAG_ALL_OPTION,
+ EXCLUDE_VCS_OPTION,
FORCE_LOCAL_OPTION,
+ FULL_TIME_OPTION,
GROUP_OPTION,
- HANG_OPTION,
IGNORE_CASE_OPTION,
IGNORE_COMMAND_ERROR_OPTION,
IGNORE_FAILED_READ_OPTION,
INDEX_FILE_OPTION,
KEEP_NEWER_FILES_OPTION,
+ LEVEL_OPTION,
+ LZIP_OPTION,
+ LZMA_OPTION,
+ LZOP_OPTION,
MODE_OPTION,
MTIME_OPTION,
NEWER_MTIME_OPTION,
+ NO_ACLS_OPTION,
NO_ANCHORED_OPTION,
+ NO_AUTO_COMPRESS_OPTION,
+ NO_CHECK_DEVICE_OPTION,
NO_DELAY_DIRECTORY_RESTORE_OPTION,
NO_IGNORE_CASE_OPTION,
NO_IGNORE_COMMAND_ERROR_OPTION,
+ NO_NULL_OPTION,
NO_OVERWRITE_DIR_OPTION,
NO_QUOTE_CHARS_OPTION,
NO_RECURSION_OPTION,
NO_SAME_OWNER_OPTION,
NO_SAME_PERMISSIONS_OPTION,
+ NO_SEEK_OPTION,
+ NO_SELINUX_CONTEXT_OPTION,
NO_UNQUOTE_OPTION,
NO_WILDCARDS_MATCH_SLASH_OPTION,
NO_WILDCARDS_OPTION,
+ NO_XATTR_OPTION,
NULL_OPTION,
NUMERIC_OWNER_OPTION,
OCCURRENCE_OPTION,
RMT_COMMAND_OPTION,
RSH_COMMAND_OPTION,
SAME_OWNER_OPTION,
+ SELINUX_CONTEXT_OPTION,
SHOW_DEFAULTS_OPTION,
SHOW_OMITTED_DIRS_OPTION,
+ SHOW_SNAPSHOT_FIELD_RANGES_OPTION,
SHOW_TRANSFORMED_NAMES_OPTION,
+ SKIP_OLD_FILES_OPTION,
SPARSE_VERSION_OPTION,
STRIP_COMPONENTS_OPTION,
SUFFIX_OPTION,
TO_COMMAND_OPTION,
TRANSFORM_OPTION,
UNQUOTE_OPTION,
- USAGE_OPTION,
- USE_COMPRESS_PROGRAM_OPTION,
UTC_OPTION,
- VERSION_OPTION,
VOLNO_FILE_OPTION,
+ WARNING_OPTION,
WILDCARDS_MATCH_SLASH_OPTION,
- WILDCARDS_OPTION
+ WILDCARDS_OPTION,
+ XATTR_OPTION,
+ XATTR_EXCLUDE,
+ XATTR_INCLUDE
};
const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
static char const doc[] = N_("\
-GNU `tar' saves many files together into a single tape or disk archive, \
+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 -tvf archive.tar # List all files in archive.tar verbosely.\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\
+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\
/* NOTE:
- Available option letters are DEIJQY and aeqy. Consider the following
+ Available option letters are DEQY and eqy. Consider the following
assignments:
[For Solaris tar compatibility =/= Is it important at all?]
E use extended headers (--format=posix)
[q alias for --occurrence=1 =/= this would better be used for quiet?]
- [I same as T =/= will harm star compatibility]
y per-file gzip compression
- Y per-block gzip compression */
+ Y per-block gzip compression.
+
+ Additionally, the 'n' letter is assigned for option --seek, which
+ is probably not needed and should be marked as deprecated, so that
+ -n may become available in the future.
+*/
static struct argp_option options[] = {
#define GRID 10
N_("handle old GNU-format incremental backup"), GRID+1 },
{"listed-incremental", 'g', N_("FILE"), 0,
N_("handle new GNU-format incremental backup"), GRID+1 },
+ {"level", LEVEL_OPTION, N_("NUMBER"), 0,
+ N_("dump level for created listed-incremental archive"), GRID+1 },
{"ignore-failed-read", IGNORE_FAILED_READ_OPTION, 0, 0,
N_("do not exit with nonzero on unreadable files"), GRID+1 },
{"occurrence", OCCURRENCE_OPTION, N_("NUMBER"), OPTION_ARG_OPTIONAL,
" NUMBER defaults to 1"), GRID+1 },
{"seek", 'n', NULL, 0,
N_("archive is seekable"), GRID+1 },
+ {"no-seek", NO_SEEK_OPTION, NULL, 0,
+ N_("archive is not seekable"), GRID+1 },
+ {"no-check-device", NO_CHECK_DEVICE_OPTION, NULL, 0,
+ N_("do not check device numbers when creating incremental archives"),
+ GRID+1 },
+ {"check-device", CHECK_DEVICE_OPTION, NULL, 0,
+ N_("check device numbers when creating incremental archives (default)"),
+ GRID+1 },
#undef GRID
#define GRID 30
{"remove-files", REMOVE_FILES_OPTION, 0, 0,
N_("remove files after adding them to the archive"), GRID+1 },
{"keep-old-files", 'k', 0, 0,
- N_("don't replace existing files when extracting"), GRID+1 },
+ N_("don't replace existing files when extracting, "
+ "treat them as errors"), GRID+1 },
+ {"skip-old-files", SKIP_OLD_FILES_OPTION, 0, 0,
+ N_("don't replace existing files when extracting, silently skip over them"),
+ GRID+1 },
{"keep-newer-files", KEEP_NEWER_FILES_OPTION, 0, 0,
N_("don't replace existing files that are newer than their archive copies"), GRID+1 },
{"overwrite", OVERWRITE_OPTION, 0, 0,
{"touch", 'm', 0, 0,
N_("don't extract file modified time"), GRID+1 },
{"same-owner", SAME_OWNER_OPTION, 0, 0,
- N_("try extracting files with the same ownership"), GRID+1 },
+ N_("try extracting files with the same ownership as exists in the archive (default for superuser)"), GRID+1 },
{"no-same-owner", NO_SAME_OWNER_OPTION, 0, 0,
- N_("extract files as yourself"), GRID+1 },
+ N_("extract files as yourself (default for ordinary users)"), GRID+1 },
{"numeric-owner", NUMERIC_OWNER_OPTION, 0, 0,
N_("always use numbers for user/group names"), GRID+1 },
{"preserve-permissions", 'p', 0, 0,
N_("cancel the effect of --delay-directory-restore option"), GRID+1 },
#undef GRID
+#define GRID 55
+ {NULL, 0, NULL, 0,
+ N_("Handling of extended file attributes:"), GRID },
+
+ {"xattrs", XATTR_OPTION, 0, 0,
+ N_("Enable extended attributes support"), GRID+1 },
+ {"no-xattrs", NO_XATTR_OPTION, 0, 0,
+ N_("Disable extended attributes support"), GRID+1 },
+ {"xattrs-include", XATTR_INCLUDE, N_("MASK"), 0,
+ N_("specify the include pattern for xattr keys"), GRID+1 },
+ {"xattrs-exclude", XATTR_EXCLUDE, N_("MASK"), 0,
+ N_("specify the exclude pattern for xattr keys"), GRID+1 },
+ {"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
+ N_("Enable the SELinux context support"), GRID+1 },
+ {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
+ N_("Disable the SELinux context support"), GRID+1 },
+ {"acls", ACLS_OPTION, 0, 0,
+ N_("Enable the POSIX ACLs support"), GRID+1 },
+ {"no-acls", NO_ACLS_OPTION, 0, 0,
+ N_("Disable the POSIX ACLs support"), GRID+1 },
+#undef GRID
+
#define GRID 60
{NULL, 0, NULL, 0,
N_("Device selection and switching:"), GRID },
{"rsh-command", RSH_COMMAND_OPTION, N_("COMMAND"), 0,
N_("use remote COMMAND instead of rsh"), GRID+1 },
#ifdef DEVICE_PREFIX
- {"-[0-7][lmh]", 0, NULL, OPTION_DOC, /* It is OK, since `name' will never be
+ {"-[0-7][lmh]", 0, NULL, OPTION_DOC, /* It is OK, since 'name' will never be
translated */
N_("specify drive and density"), GRID+1 },
#endif
N_("control pax keywords"), GRID+8 },
{"label", 'V', N_("TEXT"), 0,
N_("create archive with volume name TEXT; at list/extract time, use TEXT as a globbing pattern for volume name"), GRID+8 },
- {"bzip2", 'j', 0, 0,
- N_("filter the archive through bzip2"), GRID+8 },
- {"gzip", 'z', 0, 0,
- N_("filter the archive through gzip"), GRID+8 },
- {"gunzip", 0, 0, OPTION_ALIAS, NULL, GRID+8 },
- {"ungzip", 0, 0, OPTION_ALIAS, NULL, GRID+8 },
- {"compress", 'Z', 0, 0,
- N_("filter the archive through compress"), GRID+8 },
- {"uncompress", 0, 0, OPTION_ALIAS, NULL, GRID+8 },
- {"use-compress-program", USE_COMPRESS_PROGRAM_OPTION, N_("PROG"), 0,
- N_("filter through PROG (must accept -d)"), GRID+8 },
#undef GRID
#define GRID 90
+ {NULL, 0, NULL, 0,
+ N_("Compression options:"), GRID },
+ {"auto-compress", 'a', 0, 0,
+ N_("use archive suffix to determine the compression program"), GRID+1 },
+ {"no-auto-compress", NO_AUTO_COMPRESS_OPTION, 0, 0,
+ N_("do not use archive suffix to determine the compression program"),
+ GRID+1 },
+ {"use-compress-program", 'I', N_("PROG"), 0,
+ N_("filter through PROG (must accept -d)"), GRID+1 },
+ /* Note: docstrings for the options below are generated by tar_help_filter */
+ {"bzip2", 'j', 0, 0, NULL, GRID+1 },
+ {"gzip", 'z', 0, 0, NULL, GRID+1 },
+ {"gunzip", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
+ {"ungzip", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
+ {"compress", 'Z', 0, 0, NULL, GRID+1 },
+ {"uncompress", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
+ {"lzip", LZIP_OPTION, 0, 0, NULL, GRID+1 },
+ {"lzma", LZMA_OPTION, 0, 0, NULL, GRID+1 },
+ {"lzop", LZOP_OPTION, 0, 0, NULL, GRID+1 },
+ {"xz", 'J', 0, 0, NULL, GRID+1 },
+#undef GRID
+
+#define GRID 100
{NULL, 0, NULL, 0,
N_("Local file selection:"), GRID },
N_("get names to extract or create from FILE"), GRID+1 },
{"null", NULL_OPTION, 0, 0,
N_("-T reads null-terminated names, disable -C"), GRID+1 },
+ {"no-null", NO_NULL_OPTION, 0, 0,
+ N_("disable the effect of the previous --null option"), GRID+1 },
{"unquote", UNQUOTE_OPTION, 0, 0,
N_("unquote filenames read with -T (default)"), GRID+1 },
{"no-unquote", NO_UNQUOTE_OPTION, 0, 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 },
+ {"exclude-vcs", EXCLUDE_VCS_OPTION, NULL, 0,
+ N_("exclude version control system directories"), GRID+1 },
+ {"exclude-backups", EXCLUDE_BACKUPS_OPTION, NULL, 0,
+ N_("exclude backup and lock files"), 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,
{"recursion", RECURSION_OPTION, 0, 0,
N_("recurse into directories (default)"), GRID+1 },
{"absolute-names", 'P', 0, 0,
- N_("don't strip leading `/'s from file names"), GRID+1 },
+ N_("don't strip leading '/'s from file names"), GRID+1 },
{"dereference", 'h', 0, 0,
N_("follow symlinks; archive and dump the files they point to"), GRID+1 },
+ {"hard-dereference", HARD_DEREFERENCE_OPTION, 0, 0,
+ N_("follow hard links; archive and dump the files they refer to"), GRID+1 },
{"starting-file", 'K', N_("MEMBER-NAME"), 0,
N_("begin at member MEMBER-NAME in the archive"), GRID+1 },
{"newer", 'N', N_("DATE-OR-FILE"), 0,
N_("backup before removal, override usual suffix ('~' unless overridden by environment variable SIMPLE_BACKUP_SUFFIX)"), GRID+1 },
#undef GRID
-#define GRID 92
+#define GRID 110
{NULL, 0, NULL, 0,
N_("File name transformations:"), GRID },
{"strip-components", STRIP_COMPONENTS_OPTION, N_("NUMBER"), 0,
GRID+1 },
{"transform", TRANSFORM_OPTION, N_("EXPRESSION"), 0,
N_("use sed replace EXPRESSION to transform file names"), GRID+1 },
+ {"xform", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
#undef GRID
-#define GRID 95
+#define GRID 120
{NULL, 0, NULL, 0,
N_("File name matching options (affect both exclude and include patterns):"),
GRID },
{"anchored", ANCHORED_OPTION, 0, 0,
N_("patterns match file name start"), GRID+1 },
{"no-anchored", NO_ANCHORED_OPTION, 0, 0,
- N_("patterns match after any `/' (default for exclusion)"), GRID+1 },
+ N_("patterns match after any '/' (default for exclusion)"), GRID+1 },
{"no-ignore-case", NO_IGNORE_CASE_OPTION, 0, 0,
N_("case sensitive matching (default)"), GRID+1 },
{"wildcards", WILDCARDS_OPTION, 0, 0,
{"no-wildcards", NO_WILDCARDS_OPTION, 0, 0,
N_("verbatim string matching"), GRID+1 },
{"no-wildcards-match-slash", NO_WILDCARDS_MATCH_SLASH_OPTION, 0, 0,
- N_("wildcards do not match `/'"), GRID+1 },
+ N_("wildcards do not match '/'"), GRID+1 },
{"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0,
- N_("wildcards match `/' (default for exclusion)"), GRID+1 },
+ N_("wildcards match '/' (default for exclusion)"), GRID+1 },
#undef GRID
-#define GRID 100
+#define GRID 130
{NULL, 0, NULL, 0,
N_("Informative output:"), GRID },
{"verbose", 'v', 0, 0,
N_("verbosely list files processed"), GRID+1 },
- {"checkpoint", CHECKPOINT_OPTION, N_("[.]NUMBER"), OPTION_ARG_OPTIONAL,
+ {"warning", WARNING_OPTION, N_("KEYWORD"), 0,
+ N_("warning control"), GRID+1 },
+ {"checkpoint", CHECKPOINT_OPTION, N_("NUMBER"), OPTION_ARG_OPTIONAL,
N_("display progress messages every NUMBERth record (default 10)"),
GRID+1 },
+ {"checkpoint-action", CHECKPOINT_ACTION_OPTION, N_("ACTION"), 0,
+ N_("execute ACTION on each checkpoint"),
+ GRID+1 },
{"check-links", 'l', 0, 0,
N_("print a message if not all links are dumped"), GRID+1 },
{"totals", TOTALS_OPTION, N_("SIGNAL"), OPTION_ARG_OPTIONAL,
"Allowed signals are: SIGHUP, SIGQUIT, SIGINT, SIGUSR1 and SIGUSR2; "
"the names without SIG prefix are also accepted"), GRID+1 },
{"utc", UTC_OPTION, 0, 0,
- N_("print file modification dates in UTC"), GRID+1 },
+ N_("print file modification times in UTC"), GRID+1 },
+ {"full-time", FULL_TIME_OPTION, 0, 0,
+ N_("print file time to its full resolution"), GRID+1 },
{"index-file", INDEX_FILE_OPTION, N_("FILE"), 0,
N_("send verbose output to FILE"), GRID+1 },
{"block-number", 'R', 0, 0,
{"confirmation", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
{"show-defaults", SHOW_DEFAULTS_OPTION, 0, 0,
N_("show tar defaults"), GRID+1 },
+ {"show-snapshot-field-ranges", SHOW_SNAPSHOT_FIELD_RANGES_OPTION, 0, 0,
+ N_("show valid ranges for snapshot-file fields"), GRID+1 },
{"show-omitted-dirs", SHOW_OMITTED_DIRS_OPTION, 0, 0,
N_("when listing or extracting, list each directory that does not match search criteria"), GRID+1 },
{"show-transformed-names", SHOW_TRANSFORMED_NAMES_OPTION, 0, 0,
N_("disable quoting for characters from STRING"), GRID+1 },
#undef GRID
-#define GRID 110
+#define GRID 140
{NULL, 0, NULL, 0,
N_("Compatibility options:"), GRID },
N_("when creating, same as --old-archive; when extracting, same as --no-same-owner"), GRID+1 },
#undef GRID
-#define GRID 120
+#define GRID 150
{NULL, 0, NULL, 0,
N_("Other options:"), GRID },
{"restrict", RESTRICT_OPTION, 0, 0,
N_("disable use of some potentially harmful options"), -1 },
-
- {"help", '?', 0, 0, N_("give this help list"), -1},
- {"usage", USAGE_OPTION, 0, 0, N_("give a short usage message"), -1},
- {"version", VERSION_OPTION, 0, 0, N_("print program version"), -1},
- /* FIXME -V (--label) conflicts with the default short option for
- --version */
- {"HANG", HANG_OPTION, "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN,
- N_("hang for SECS seconds (default 3600)"), 0},
#undef GRID
{0, 0, 0, 0, 0, 0}
char const *backup_suffix_string; /* --suffix option argument */
char const *version_control_string; /* --backup option argument */
bool input_files; /* True if some input files where given */
+ int compress_autodetect; /* True if compression autodetection should
+ be attempted when creating archives */
};
+\f
#define MAKE_EXCL_OPTIONS(args) \
((((args)->wildcards != disable_wildcards) ? EXCLUDE_WILDCARDS : 0) \
| (args)->matching_flags \
| (args)->matching_flags \
| recursion_option)
-#ifdef REMOTE_SHELL
-# define DECL_SHOW_DEFAULT_SETTINGS(stream, printer) \
-{ \
- printer (stream, \
- "--format=%s -f%s -b%d --quoting-style=%s --rmt-command=%s", \
- archive_format_string (DEFAULT_ARCHIVE_FORMAT), \
- DEFAULT_ARCHIVE, DEFAULT_BLOCKING, \
- quoting_style_args[DEFAULT_QUOTING_STYLE], \
- DEFAULT_RMT_COMMAND); \
- printer (stream, " --rsh-command=%s", REMOTE_SHELL); \
- printer (stream, "\n"); \
-}
-#else
-# define DECL_SHOW_DEFAULT_SETTINGS(stream, printer) \
-{ \
- printer (stream, \
- "--format=%s -f%s -b%d --quoting-style=%s --rmt-command=%s", \
- archive_format_string (DEFAULT_ARCHIVE_FORMAT), \
- DEFAULT_ARCHIVE, DEFAULT_BLOCKING, \
- quoting_style_args[DEFAULT_QUOTING_STYLE], \
- DEFAULT_RMT_COMMAND); \
- printer (stream, "\n"); \
-}
-#endif
+static char const * const vcs_file_table[] = {
+ /* CVS: */
+ "CVS",
+ ".cvsignore",
+ /* RCS: */
+ "RCS",
+ /* SCCS: */
+ "SCCS",
+ /* SVN: */
+ ".svn",
+ /* git: */
+ ".git",
+ ".gitignore",
+ /* Arch: */
+ ".arch-ids",
+ "{arch}",
+ "=RELEASE-ID",
+ "=meta-update",
+ "=update",
+ /* Bazaar */
+ ".bzr",
+ ".bzrignore",
+ ".bzrtags",
+ /* Mercurial */
+ ".hg",
+ ".hgignore",
+ ".hgtags",
+ /* darcs */
+ "_darcs",
+ NULL
+};
-static void
-show_default_settings (FILE *fp)
- DECL_SHOW_DEFAULT_SETTINGS(fp, fprintf)
+static char const * const backup_file_table[] = {
+ ".#*",
+ "*~",
+ "#*#",
+ NULL
+};
static void
-show_default_settings_fs (argp_fmtstream_t fs)
- DECL_SHOW_DEFAULT_SETTINGS(fs, argp_fmtstream_printf)
+add_exclude_array (char const * const * fv, int opts)
+{
+ int i;
+
+ for (i = 0; fv[i]; i++)
+ add_exclude (excluded, fv[i], opts);
+}
+
+\f
+static char *
+format_default_settings (void)
+{
+ return xasprintf (
+ "--format=%s -f%s -b%d --quoting-style=%s --rmt-command=%s"
+#ifdef REMOTE_SHELL
+ " --rsh-command=%s"
+#endif
+ ,
+ archive_format_string (DEFAULT_ARCHIVE_FORMAT),
+ DEFAULT_ARCHIVE, DEFAULT_BLOCKING,
+ quoting_style_args[DEFAULT_QUOTING_STYLE],
+ DEFAULT_RMT_COMMAND
+#ifdef REMOTE_SHELL
+ , REMOTE_SHELL
+#endif
+ );
+}
+\f
static void
set_subcommand_option (enum subcommand subcommand)
{
if (subcommand_option != UNKNOWN_SUBCOMMAND
&& subcommand_option != subcommand)
USAGE_ERROR ((0, 0,
- _("You may not specify more than one `-Acdtrux' option")));
+ _("You may not specify more than one '-Acdtrux' or '--test-label' option")));
subcommand_option = subcommand;
}
stat_on_signal (int signo)
{
#ifdef HAVE_SIGACTION
+# ifndef SA_RESTART
+# define SA_RESTART 0
+# endif
struct sigaction act;
act.sa_handler = sigstat;
sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
+ act.sa_flags = SA_RESTART;
sigaction (signo, &act, NULL);
#else
signal (signo, sigstat);
#endif
}
-void
+static void
set_stat_signal (const char *name)
{
static struct sigtab
{
- char *name;
+ char const *name;
int signo;
- } sigtab[] = {
+ } const sigtab[] = {
{ "SIGUSR1", SIGUSR1 },
{ "USR1", SIGUSR1 },
{ "SIGUSR2", SIGUSR2 },
{ "SIGQUIT", SIGQUIT },
{ "QUIT", SIGQUIT }
};
- struct sigtab *p;
+ struct sigtab const *p;
for (p = sigtab; p < sigtab + sizeof (sigtab) / sizeof (sigtab[0]); p++)
if (strcmp (p->name, name) == 0)
struct textual_date
{
struct textual_date *next;
- struct timespec *ts;
+ struct timespec ts;
const char *option;
- const char *date;
+ char *date;
};
-static void
+static int
get_date_or_file (struct tar_args *args, const char *option,
const char *str, struct timespec *ts)
{
|| *str == '.')
{
struct stat st;
- if (deref_stat (dereference_option, str, &st) != 0)
+ if (stat (str, &st) != 0)
{
stat_error (str);
USAGE_ERROR ((0, 0, _("Date sample file not found")));
}
else
{
- if (! get_date (ts, str, NULL))
+ if (! parse_datetime (ts, str, NULL))
{
WARN ((0, 0, _("Substituting %s for unknown date format %s"),
tartime (*ts, false), quote (str)));
ts->tv_nsec = 0;
+ return 1;
}
else
{
struct textual_date *p = xmalloc (sizeof (*p));
- p->ts = ts;
+ p->ts = *ts;
p->option = option;
- p->date = str;
+ p->date = xstrdup (str);
p->next = args->textual_date;
args->textual_date = p;
}
}
+ return 0;
}
static void
for (p = args->textual_date; p; )
{
struct textual_date *next = p->next;
- char const *treated_as = tartime (*p->ts, true);
- if (strcmp (p->date, treated_as) != 0)
- WARN ((0, 0, _("Option %s: Treating date `%s' as %s"),
- p->option, p->date, treated_as));
+ if (verbose_option)
+ {
+ char const *treated_as = tartime (p->ts, true);
+ if (strcmp (p->date, treated_as) != 0)
+ WARN ((0, 0, _("Option %s: Treating date '%s' as %s"),
+ p->option, p->date, treated_as));
+ }
+ free (p->date);
free (p);
p = next;
}
}
\f
-static volatile int _argp_hang;
+static bool files_from_option; /* When set, tar will not refuse to create
+ empty archives */
-enum read_file_list_state /* Result of reading file name from the list file */
- {
- 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_skip /* Empty (zero-length) entry encountered, skip it */
- };
+/* Default density numbers for [0-9][lmh] device specifications */
+
+#if defined DEVICE_PREFIX && !defined DENSITY_LETTER
+# ifndef LOW_DENSITY_NUM
+# define LOW_DENSITY_NUM 0
+# endif
-/* Read from FP a sequence of characters up to FILENAME_TERMINATOR and put them
- into STK.
- */
-static enum read_file_list_state
-read_name_from_file (FILE *fp, struct obstack *stk)
+# ifndef MID_DENSITY_NUM
+# define MID_DENSITY_NUM 8
+# endif
+
+# ifndef HIGH_DENSITY_NUM
+# define HIGH_DENSITY_NUM 16
+# endif
+#endif
+
+\f
+static char *
+tar_help_filter (int key, const char *text, void *input)
{
- int c;
- size_t counter = 0;
+ struct obstack stk;
+ char *s;
- for (c = getc (fp); c != EOF && c != filename_terminator; c = getc (fp))
+ switch (key)
{
- if (c == 0)
- {
- /* We have read a zero separator. The file possibly is
- zero-separated */
- return file_list_zero;
- }
- obstack_1grow (stk, c);
- counter++;
- }
-
- if (counter == 0 && c != EOF)
- return file_list_skip;
+ default:
+ s = (char*) text;
+ break;
- obstack_1grow (stk, 0);
+ case 'j':
+ s = xasprintf (_("filter the archive through %s"), BZIP2_PROGRAM);
+ break;
- return (counter == 0 && c == EOF) ? file_list_end : file_list_success;
-}
+ case 'z':
+ s = xasprintf (_("filter the archive through %s"), GZIP_PROGRAM);
+ break;
-\f
-static bool files_from_option; /* When set, tar will not refuse to create
- empty archives */
-static struct obstack argv_stk; /* Storage for additional command line options
- read using -T option */
+ case 'Z':
+ s = xasprintf (_("filter the archive through %s"), COMPRESS_PROGRAM);
+ break;
-/* Prevent recursive inclusion of the same file */
-struct file_id_list
-{
- struct file_id_list *next;
- ino_t ino;
- dev_t dev;
-};
+ case LZIP_OPTION:
+ s = xasprintf (_("filter the archive through %s"), LZIP_PROGRAM);
+ break;
-static struct file_id_list *file_id_list;
+ case LZMA_OPTION:
+ s = xasprintf (_("filter the archive through %s"), LZMA_PROGRAM);
+ break;
-static void
-add_file_id (const char *filename)
-{
- struct file_id_list *p;
- struct stat st;
+ case 'J':
+ s = xasprintf (_("filter the archive through %s"), XZ_PROGRAM);
+ break;
- if (stat (filename, &st))
- stat_fatal (filename);
- for (p = file_id_list; p; p = p->next)
- if (p->ino == st.st_ino && p->dev == st.st_dev)
+ case ARGP_KEY_HELP_EXTRA:
{
- FATAL_ERROR ((0, 0, _("%s: file list already read"),
- quotearg_colon (filename)));
+ const char *tstr;
+
+ obstack_init (&stk);
+ tstr = _("Valid arguments for the --quoting-style option are:");
+ obstack_grow (&stk, tstr, strlen (tstr));
+ obstack_grow (&stk, "\n\n", 2);
+ tar_list_quoting_styles (&stk, " ");
+ tstr = _("\n*This* tar defaults to:\n");
+ obstack_grow (&stk, tstr, strlen (tstr));
+ s = format_default_settings ();
+ obstack_grow (&stk, s, strlen (s));
+ obstack_1grow (&stk, '\n');
+ obstack_1grow (&stk, 0);
+ s = xstrdup (obstack_finish (&stk));
+ obstack_free (&stk, NULL);
}
- p = xmalloc (sizeof *p);
- p->next = file_id_list;
- p->ino = st.st_ino;
- p->dev = st.st_dev;
- file_id_list = p;
+ }
+ return s;
}
+\f
+static char *
+expand_pax_option (struct tar_args *targs, const char *arg)
+{
+ struct obstack stk;
+ char *res;
-/* Default density numbers for [0-9][lmh] device specifications */
-
-#ifndef LOW_DENSITY_NUM
-# define LOW_DENSITY_NUM 0
-#endif
-
-#ifndef MID_DENSITY_NUM
-# define MID_DENSITY_NUM 8
-#endif
+ obstack_init (&stk);
+ while (*arg)
+ {
+ size_t seglen = strcspn (arg, ",");
+ char *p = memchr (arg, '=', seglen);
+ if (p)
+ {
+ size_t len = p - arg + 1;
+ obstack_grow (&stk, arg, len);
+ len = seglen - len;
+ for (++p; *p && isspace ((unsigned char) *p); p++)
+ len--;
+ if (*p == '{' && p[len-1] == '}')
+ {
+ struct timespec ts;
+ char *tmp = xmalloc (len);
+ memcpy (tmp, p + 1, len-2);
+ tmp[len-2] = 0;
+ if (get_date_or_file (targs, "--pax-option", tmp, &ts) == 0)
+ {
+ char buf[TIMESPEC_STRSIZE_BOUND];
+ char const *s = code_timespec (ts, buf);
+ obstack_grow (&stk, s, strlen (s));
+ }
+ else
+ obstack_grow (&stk, p, len);
+ free (tmp);
+ }
+ else
+ obstack_grow (&stk, p, len);
+ }
+ else
+ obstack_grow (&stk, arg, seglen);
-#ifndef HIGH_DENSITY_NUM
-# define HIGH_DENSITY_NUM 16
-#endif
+ arg += seglen;
+ if (*arg)
+ {
+ obstack_1grow (&stk, *arg);
+ arg++;
+ }
+ }
+ obstack_1grow (&stk, 0);
+ res = xstrdup (obstack_finish (&stk));
+ obstack_free (&stk, NULL);
+ return res;
+}
-static void
-update_argv (const char *filename, struct argp_state *state)
+\f
+static uintmax_t
+parse_owner_group (char *arg, uintmax_t field_max, char const **name_option)
{
- FILE *fp;
- size_t count = 0, i;
- char *start, *p;
- char **new_argv;
- size_t new_argc;
- bool is_stdin = false;
- enum read_file_list_state read_state;
-
- if (!strcmp (filename, "-"))
+ uintmax_t u = UINTMAX_MAX;
+ char *end;
+ char const *name = 0;
+ char const *invalid_num = 0;
+ char *colon = strchr (arg, ':');
+
+ if (colon)
{
- is_stdin = true;
- request_stdin ("-T");
- fp = stdin;
+ char const *num = colon + 1;
+ *colon = '\0';
+ if (*arg)
+ name = arg;
+ if (num && (! (xstrtoumax (num, &end, 10, &u, "") == LONGINT_OK
+ && u <= field_max)))
+ invalid_num = num;
}
else
{
- add_file_id (filename);
- if ((fp = fopen (filename, "r")) == NULL)
- open_fatal (filename);
- }
-
- while ((read_state = read_name_from_file (fp, &argv_stk)) != file_list_end)
- {
- switch (read_state)
+ uintmax_t u1;
+ switch ('0' <= *arg && *arg <= '9'
+ ? xstrtoumax (arg, &end, 10, &u1, "")
+ : LONGINT_INVALID)
{
- case file_list_success:
- count++;
- break;
-
- case file_list_end: /* won't happen, just to pacify gcc */
+ default:
+ name = arg;
break;
- 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:
+ case LONGINT_OK:
+ if (u1 <= field_max)
+ {
+ u = u1;
+ break;
+ }
+ /* Fall through. */
+ case LONGINT_OVERFLOW:
+ invalid_num = arg;
break;
}
}
- if (!is_stdin)
- fclose (fp);
-
- if (count == 0)
- return;
-
- start = obstack_finish (&argv_stk);
-
- if (filename_terminator == 0)
- for (p = start; *p; p += strlen (p) + 1)
- if (p[0] == '-')
- count++;
-
- new_argc = state->argc + count;
- new_argv = xmalloc (sizeof (state->argv[0]) * (new_argc + 1));
- memcpy (new_argv, state->argv, sizeof (state->argv[0]) * (state->argc + 1));
- state->argv = new_argv;
- memmove (&state->argv[state->next + count], &state->argv[state->next],
- (state->argc - state->next + 1) * sizeof (state->argv[0]));
+ if (invalid_num)
+ FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (invalid_num),
+ _("Invalid owner or group ID")));
+ if (name)
+ *name_option = name;
+ return u;
+}
- state->argc = new_argc;
+#define TAR_SIZE_SUFFIXES "bBcGgkKMmPTtw"
- for (i = state->next, p = start; *p; p += strlen (p) + 1, i++)
- {
- if (filename_terminator == 0 && p[0] == '-')
- state->argv[i++] = "--add-file";
- state->argv[i] = p;
- }
-}
+/* Either NL or NUL, as decided by the --null option. */
+static char filename_terminator;
-\f
-static void
-tar_help (struct argp_state *state)
-{
- argp_fmtstream_t fs;
- state->flags |= ARGP_NO_EXIT;
- argp_state_help (state, state->out_stream,
- 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');
- argp_fmtstream_printf (fs, _("Report bugs to %s.\n"),
- argp_program_bug_address);
- argp_fmtstream_free (fs);
-}
-\f
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
set_subcommand_option (CAT_SUBCOMMAND);
break;
+ case 'a':
+ args->compress_autodetect = true;
+ break;
+
+ case NO_AUTO_COMPRESS_OPTION:
+ args->compress_autodetect = false;
+ break;
+
case 'b':
{
uintmax_t u;
multi_volume_option = true;
break;
+ case FULL_TIME_OPTION:
+ full_time_option = true;
+ break;
+
case 'g':
listed_incremental_option = arg;
after_date_option = true;
dereference_option = true;
break;
+ case HARD_DEREFERENCE_OPTION:
+ hard_dereference_option = true;
+ break;
+
case 'i':
/* Ignore zero blocks (eofs). This can't be the default,
because Unix tar writes two blocks of zeros, then pads out
ignore_zeros_option = true;
break;
- case 'I':
- USAGE_ERROR ((0, 0,
- _("Warning: the -I option is not supported;"
- " perhaps you meant -j or -T?")));
+ case 'j':
+ set_use_compress_program_option (BZIP2_PROGRAM);
break;
- case 'j':
- set_use_compress_program_option ("bzip2");
+ case 'J':
+ set_use_compress_program_option (XZ_PROGRAM);
break;
case 'k':
case 'K':
starting_file_option = true;
- addname (arg, 0);
+ addname (arg, 0, true, NULL);
break;
case ONE_FILE_SYSTEM_OPTION:
case 'L':
{
uintmax_t u;
- if (xstrtoumax (arg, 0, 10, &u, "") != LONGINT_OK)
+ char *p;
+
+ if (xstrtoumax (arg, &p, 10, &u, TAR_SIZE_SUFFIXES) != LONGINT_OK)
USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
_("Invalid tape length")));
- tape_length_option = 1024 * (tarlong) u;
+ if (p > arg && !strchr (TAR_SIZE_SUFFIXES, p[-1]))
+ tape_length_option = 1024 * (tarlong) u;
+ else
+ tape_length_option = (tarlong) u;
multi_volume_option = true;
}
break;
+ case LEVEL_OPTION:
+ {
+ char *p;
+ incremental_level = strtoul (arg, &p, 10);
+ if (*p)
+ USAGE_ERROR ((0, 0, _("Invalid incremental level value")));
+ }
+ break;
+
+ case LZIP_OPTION:
+ set_use_compress_program_option (LZIP_PROGRAM);
+ break;
+
+ case LZMA_OPTION:
+ set_use_compress_program_option (LZMA_PROGRAM);
+ break;
+
+ case LZOP_OPTION:
+ set_use_compress_program_option (LZOP_PROGRAM);
+ break;
+
case 'm':
touch_option = true;
break;
break;
case 'n':
- seekable_archive = true;
+ seek_option = 1;
+ break;
+
+ case NO_SEEK_OPTION:
+ seek_option = 0;
break;
case 'N':
USAGE_ERROR ((0, 0, _("More than one threshold date")));
get_date_or_file (args,
key == NEWER_MTIME_OPTION ? "--newer-mtime"
- : "--after-date", arg, &newer_mtime_option);
+ : "--after-date", arg, &newer_mtime_option);
break;
case 'o':
sparse_option = true;
break;
+ case SKIP_OLD_FILES_OPTION:
+ old_files_option = SKIP_OLD_FILES;
+ break;
+
case SPARSE_VERSION_OPTION:
sparse_option = true;
{
break;
case TEST_LABEL_OPTION:
- set_subcommand_option (LIST_SUBCOMMAND);
- test_label_option = true;
+ set_subcommand_option (TEST_LABEL_SUBCOMMAND);
break;
case 'T':
- update_argv (arg, state);
+ name_add_file (arg, filename_terminator);
/* Indicate we've been given -T option. This is for backward
compatibility only, so that `tar cfT archive /dev/null will
succeed */
case 'v':
verbose_option++;
+ warning_option |= WARN_VERBOSE_WARNINGS;
break;
case 'V':
break;
case 'z':
- set_use_compress_program_option ("gzip");
+ set_use_compress_program_option (GZIP_PROGRAM);
break;
case 'Z':
- set_use_compress_program_option ("compress");
+ set_use_compress_program_option (COMPRESS_PROGRAM);
break;
case ANCHORED_OPTION:
" on this platform")));
break;
+ case CHECK_DEVICE_OPTION:
+ check_device_option = true;
+ break;
+
+ case NO_CHECK_DEVICE_OPTION:
+ check_device_option = false;
+ break;
+
case CHECKPOINT_OPTION:
if (arg)
{
if (*arg == '.')
{
- checkpoint_style = checkpoint_dot;
+ checkpoint_compile_action (".");
arg++;
}
checkpoint_option = strtoul (arg, &p, 0);
_("--checkpoint value is not an integer")));
}
else
- checkpoint_option = 10;
+ checkpoint_option = DEFAULT_CHECKPOINT;
+ break;
+
+ case CHECKPOINT_ACTION_OPTION:
+ checkpoint_compile_action (arg);
break;
case BACKUP_OPTION:
set_subcommand_option (DELETE_SUBCOMMAND);
break;
+ case EXCLUDE_BACKUPS_OPTION:
+ add_exclude_array (backup_file_table, EXCLUDE_WILDCARDS);
+ break;
+
case EXCLUDE_OPTION:
add_exclude (excluded, arg, MAKE_EXCL_OPTIONS (args));
break;
add_exclusion_tag (arg, exclusion_tag_all, NULL);
break;
+ case EXCLUDE_VCS_OPTION:
+ add_exclude_array (vcs_file_table, 0);
+ break;
+
case FORCE_LOCAL_OPTION:
force_local_option = true;
break;
break;
case GROUP_OPTION:
- if (! (strlen (arg) < GNAME_FIELD_SIZE
- && gname_to_gid (arg, &group_option)))
- {
- uintmax_t g;
- if (xstrtoumax (arg, 0, 10, &g, "") == LONGINT_OK
- && g == (gid_t) g)
- group_option = g;
- else
- FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
- _("%s: Invalid group")));
- }
+ {
+ uintmax_t u = parse_owner_group (arg, TYPE_MAXIMUM (gid_t),
+ &group_name_option);
+ if (u == UINTMAX_MAX)
+ {
+ group_option = -1;
+ if (group_name_option)
+ gname_to_gid (group_name_option, &group_option);
+ }
+ else
+ group_option = u;
+ }
break;
case MODE_OPTION:
filename_terminator = '\0';
break;
+ case NO_NULL_OPTION:
+ filename_terminator = '\n';
+ break;
+
case NUMERIC_OWNER_OPTION:
numeric_owner_option = true;
break;
}
break;
+ case OLD_ARCHIVE_OPTION:
+ set_archive_format ("v7");
+ break;
+
case OVERWRITE_DIR_OPTION:
old_files_option = DEFAULT_OLD_FILES;
break;
break;
case OWNER_OPTION:
- if (! (strlen (arg) < UNAME_FIELD_SIZE
- && uname_to_uid (arg, &owner_option)))
- {
- uintmax_t u;
- if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
- && u == (uid_t) u)
- owner_option = u;
- else
- FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
- _("Invalid owner")));
- }
+ {
+ uintmax_t u = parse_owner_group (arg, TYPE_MAXIMUM (uid_t),
+ &owner_name_option);
+ if (u == UINTMAX_MAX)
+ {
+ owner_option = -1;
+ if (owner_name_option)
+ uname_to_uid (owner_name_option, &owner_option);
+ }
+ else
+ owner_option = u;
+ }
break;
case QUOTE_CHARS_OPTION:
break;
case PAX_OPTION:
- args->pax_option = true;
- xheader_set_option (arg);
+ {
+ char *tmp = expand_pax_option (args, arg);
+ args->pax_option = true;
+ xheader_set_option (tmp);
+ free (tmp);
+ }
break;
case POSIX_OPTION:
/* FIXME: What it is good for? */
same_permissions_option = true;
same_order_option = true;
+ WARN ((0, 0, _("The --preserve option is deprecated, "
+ "use --preserve-permissions --preserve-order instead")));
break;
case RECORD_SIZE_OPTION:
{
uintmax_t u;
- if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
+
+ if (! (xstrtoumax (arg, NULL, 10, &u, TAR_SIZE_SUFFIXES) == LONGINT_OK
&& u == (size_t) u))
USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
_("Invalid record size")));
break;
case SHOW_DEFAULTS_OPTION:
- show_default_settings (stdout);
+ {
+ char *s = format_default_settings ();
+ printf ("%s\n", s);
+ close_stdout ();
+ free (s);
+ exit (0);
+ }
+
+ case SHOW_SNAPSHOT_FIELD_RANGES_OPTION:
+ show_snapshot_field_ranges ();
close_stdout ();
exit (0);
set_transform_expr (arg);
break;
- case USE_COMPRESS_PROGRAM_OPTION:
+ case 'I':
set_use_compress_program_option (arg);
break;
same_permissions_option = -1;
break;
+ case ACLS_OPTION:
+ set_archive_format ("posix");
+ acls_option = 1;
+ break;
+
+ case NO_ACLS_OPTION:
+ acls_option = -1;
+ break;
+
+ case SELINUX_CONTEXT_OPTION:
+ set_archive_format ("posix");
+ selinux_context_option = 1;
+ break;
+
+ case NO_SELINUX_CONTEXT_OPTION:
+ selinux_context_option = -1;
+ break;
+
+ case XATTR_OPTION:
+ set_archive_format ("posix");
+ xattrs_option = 1;
+ break;
+
+ case NO_XATTR_OPTION:
+ xattrs_option = -1;
+ break;
+
+ case XATTR_INCLUDE:
+ case XATTR_EXCLUDE:
+ xattrs_mask_add (arg, (key == XATTR_INCLUDE));
+ break;
+
case RECURSION_OPTION:
recursion_option = FNM_LEADING_DIR;
break;
unquote_option = false;
break;
+ case WARNING_OPTION:
+ set_warning_option (arg);
+ break;
+
case '0':
case '1':
case '2':
break;
default:
- argp_error (state, _("Unknown density: `%c'"), arg[0]);
+ argp_error (state, _("Unknown density: '%c'"), arg[0]);
}
sprintf (cursor, "%d", device);
#else /* not DEVICE_PREFIX */
argp_error (state,
- _("Options `-[0-7][lmh]' not supported by *this* tar"));
+ _("Options '-[0-7][lmh]' not supported by *this* tar"));
#endif /* not DEVICE_PREFIX */
- case '?':
- tar_help (state);
- close_stdout ();
- exit (0);
-
- case USAGE_OPTION:
- argp_state_help (state, state->out_stream, ARGP_HELP_USAGE);
- close_stdout ();
- exit (0);
-
- case VERSION_OPTION:
- version_etc (state->out_stream, "tar", PACKAGE_NAME, VERSION,
- "John Gilmore", "Jay Fenlason", (char *) NULL);
- close_stdout ();
- exit (0);
-
- case HANG_OPTION:
- _argp_hang = atoi (arg ? arg : "3600");
- while (_argp_hang-- > 0)
- sleep (1);
- break;
-
default:
return ARGP_ERR_UNKNOWN;
}
N_("[FILE]..."),
doc,
NULL,
- NULL,
+ tar_help_filter,
NULL
};
/* 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 const char *tar_authors[] = {
+ "John Gilmore",
+ "Jay Fenlason",
+ NULL
+};
+\f
+/* Subcommand classes */
+#define SUBCL_READ 0x01 /* subcommand reads from the archive */
+#define SUBCL_WRITE 0x02 /* subcommand writes to the archive */
+#define SUBCL_UPDATE 0x04 /* subcommand updates existing archive */
+#define SUBCL_TEST 0x08 /* subcommand tests archive header or meta-info */
+#define SUBCL_OCCUR 0x10 /* subcommand allows the use of the occurrence
+ option */
+
+static int subcommand_class[] = {
+ /* UNKNOWN_SUBCOMMAND */ 0,
+ /* APPEND_SUBCOMMAND */ SUBCL_WRITE|SUBCL_UPDATE,
+ /* CAT_SUBCOMMAND */ SUBCL_WRITE,
+ /* CREATE_SUBCOMMAND */ SUBCL_WRITE,
+ /* DELETE_SUBCOMMAND */ SUBCL_WRITE|SUBCL_UPDATE|SUBCL_OCCUR,
+ /* DIFF_SUBCOMMAND */ SUBCL_READ|SUBCL_OCCUR,
+ /* EXTRACT_SUBCOMMAND */ SUBCL_READ|SUBCL_OCCUR,
+ /* LIST_SUBCOMMAND */ SUBCL_READ|SUBCL_OCCUR,
+ /* UPDATE_SUBCOMMAND */ SUBCL_WRITE|SUBCL_UPDATE,
+ /* TEST_LABEL_SUBCOMMAND */ SUBCL_TEST
+};
+
+/* Return t if the subcommand_option is in class(es) f */
+#define IS_SUBCOMMAND_CLASS(f) (subcommand_class[subcommand_option] & (f))
+
+static struct tar_args args;
+
static void
decode_options (int argc, char **argv)
{
- int index;
- struct tar_args args;
+ int idx;
+
+ argp_version_setup ("tar", tar_authors);
/* Set some default option values. */
args.textual_date = NULL;
args.backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
args.version_control_string = 0;
args.input_files = false;
+ args.compress_autodetect = false;
subcommand_option = UNKNOWN_SUBCOMMAND;
archive_format = DEFAULT_FORMAT;
tar_sparse_major = 1;
tar_sparse_minor = 0;
- owner_option = -1;
- group_option = -1;
+ owner_option = -1; owner_name_option = NULL;
+ group_option = -1; group_name_option = NULL;
+
+ check_device_option = true;
+
+ incremental_level = -1;
+
+ seek_option = -1;
/* Convert old-style tar call by exploding option element and rearranging
options accordingly. */
if (in < argv + argc)
*out++ = *in++;
else
- USAGE_ERROR ((0, 0, _("Old option `%c' requires an argument."),
+ USAGE_ERROR ((0, 0, _("Old option '%c' requires an argument."),
*letter));
}
}
prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv);
- if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_HELP,
- &index, &args))
+ if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &idx, &args))
exit (TAREXIT_FAILURE);
-
/* Special handling for 'o' option:
GNU tar used to say "output old format".
}
/* 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;
}
if (!args.input_files)
USAGE_ERROR ((0, 0,
_("--occurrence is meaningless without a file list")));
- if (subcommand_option != DELETE_SUBCOMMAND
- && subcommand_option != DIFF_SUBCOMMAND
- && subcommand_option != EXTRACT_SUBCOMMAND
- && subcommand_option != LIST_SUBCOMMAND)
- USAGE_ERROR ((0, 0,
- _("--occurrence cannot be used in the requested operation mode")));
- }
-
- if (seekable_archive && subcommand_option == DELETE_SUBCOMMAND)
- {
- /* The current code in delete.c is based on the assumption that
- skip_member() reads all data from the archive. So, we should
- make sure it won't use seeks. On the other hand, the same code
- depends on the ability to backspace a record in the archive,
- so setting seekable_archive to false is technically incorrect.
- However, it is tested only in skip_member(), so it's not a
- problem. */
- seekable_archive = false;
+ if (!IS_SUBCOMMAND_CLASS (SUBCL_OCCUR))
+ USAGE_ERROR ((0, 0,
+ _("--occurrence cannot be used with %s"),
+ subcommand_string (subcommand_option)));
}
if (archive_names == 0)
archive_name_array[0] = DEFAULT_ARCHIVE;
}
- /* Allow multiple archives only with `-M'. */
+ /* Allow multiple archives only with '-M'. */
if (archive_names > 1 && !multi_volume_option)
USAGE_ERROR ((0, 0,
- _("Multiple archive files require `-M' option")));
+ _("Multiple archive files require '-M' option")));
if (listed_incremental_option
&& NEWER_OPTION_INITIALIZED (newer_mtime_option))
USAGE_ERROR ((0, 0,
_("Cannot combine --listed-incremental with --newer")));
+ if (incremental_level != -1 && !listed_incremental_option)
+ WARN ((0, 0,
+ _("--level is meaningless without --listed-incremental")));
if (volume_label_option)
{
USAGE_ERROR ((0, 0, _("Cannot verify multi-volume archives")));
if (use_compress_program_option)
USAGE_ERROR ((0, 0, _("Cannot verify compressed archives")));
+ if (!IS_SUBCOMMAND_CLASS (SUBCL_WRITE))
+ USAGE_ERROR ((0, 0, _("--verify cannot be used with %s"),
+ subcommand_string (subcommand_option)));
}
if (use_compress_program_option)
{
if (multi_volume_option)
USAGE_ERROR ((0, 0, _("Cannot use multi-volume compressed archives")));
- if (subcommand_option == UPDATE_SUBCOMMAND
- || subcommand_option == APPEND_SUBCOMMAND
- || subcommand_option == DELETE_SUBCOMMAND)
+ if (IS_SUBCOMMAND_CLASS (SUBCL_UPDATE))
USAGE_ERROR ((0, 0, _("Cannot update compressed archives")));
if (subcommand_option == CAT_SUBCOMMAND)
USAGE_ERROR ((0, 0, _("Cannot concatenate compressed archives")));
--gray */
if (args.pax_option
&& archive_format != POSIX_FORMAT
- && (subcommand_option != EXTRACT_SUBCOMMAND
- || subcommand_option != DIFF_SUBCOMMAND
- || subcommand_option != LIST_SUBCOMMAND))
+ && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
+ /* star creates non-POSIX typed archives with xattr support, so allow the
+ extra headers when reading */
+ if ((acls_option > 0)
+ && archive_format != POSIX_FORMAT
+ && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+ USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
+
+ if ((selinux_context_option > 0)
+ && archive_format != POSIX_FORMAT
+ && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+ USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
+
+ if ((xattrs_option > 0)
+ && archive_format != POSIX_FORMAT
+ && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+ USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives")));
+
/* If ready to unlink hierarchies, so we are for simpler files. */
if (recursive_unlink_option)
old_files_option = UNLINK_FIRST_OLD_FILES;
-
- if (test_label_option)
+ /* Flags for accessing files to be read from or copied into. POSIX says
+ O_NONBLOCK has unspecified effect on most types of files, but in
+ practice it never harms and sometimes helps. */
+ {
+ int base_open_flags =
+ (O_BINARY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK
+ | (dereference_option ? 0 : O_NOFOLLOW)
+ | (atime_preserve_option == system_atime_preserve ? O_NOATIME : 0));
+ open_read_flags = O_RDONLY | base_open_flags;
+ open_searchdir_flags = O_SEARCH | O_DIRECTORY | base_open_flags;
+ }
+ fstatat_flags = dereference_option ? 0 : AT_SYMLINK_NOFOLLOW;
+
+ if (subcommand_option == TEST_LABEL_SUBCOMMAND)
{
/* --test-label is silent if the user has specified the label name to
compare against. */
else if (utc_option)
verbose_option = 2;
- /* Forbid using -c with no input files whatsoever. Check that `-f -',
+ if (tape_length_option && tape_length_option < record_size)
+ USAGE_ERROR ((0, 0, _("Volume length cannot be less than record size")));
+
+ if (same_order_option && listed_incremental_option)
+ USAGE_ERROR ((0, 0, _("--preserve-order is not compatible with "
+ "--listed-incremental")));
+
+ /* Forbid using -c with no input files whatsoever. Check that '-f -',
explicit or implied, is used correctly. */
switch (subcommand_option)
if (!args.input_files && !files_from_option)
USAGE_ERROR ((0, 0,
_("Cowardly refusing to create an empty archive")));
+ if (args.compress_autodetect && archive_names
+ && strcmp (archive_name_array[0], "-"))
+ set_compression_program_by_suffix (archive_name_array[0],
+ use_compress_program_option);
break;
case EXTRACT_SUBCOMMAND:
case LIST_SUBCOMMAND:
case DIFF_SUBCOMMAND:
+ case TEST_LABEL_SUBCOMMAND:
for (archive_name_cursor = archive_name_array;
archive_name_cursor < archive_name_array + archive_names;
archive_name_cursor++)
archive_name_cursor++)
if (!strcmp (*archive_name_cursor, "-"))
USAGE_ERROR ((0, 0,
- _("Options `-Aru' are incompatible with `-f -'")));
+ _("Options '-Aru' are incompatible with '-f -'")));
default:
break;
{
stdlis = fopen (index_file_name, "w");
if (! stdlis)
- open_error (index_file_name);
+ open_fatal (index_file_name);
}
else
stdlis = to_stdout_option ? stderr : stdout;
backup_option = false;
}
- if (verbose_option)
- report_textual_dates (&args);
+ checkpoint_finish_compile ();
+
+ report_textual_dates (&args);
}
+void
+more_options (int argc, char **argv)
+{
+ int idx;
+ if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER,
+ &idx, &args))
+ exit (TAREXIT_FAILURE);
+}
\f
/* Tar proper. */
main (int argc, char **argv)
{
set_start_time ();
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
xmalloc (sizeof (const char *) * allocated_archive_names);
archive_names = 0;
- obstack_init (&argv_stk);
-
-#ifdef SIGCHLD
/* System V fork+wait does not work if SIGCHLD is ignored. */
signal (SIGCHLD, SIG_DFL);
-#endif
+
+ /* Try to disable the ability to unlink a directory. */
+ priv_set_remove_linkdir ();
/* Decode options. */
{
case UNKNOWN_SUBCOMMAND:
USAGE_ERROR ((0, 0,
- _("You must specify one of the `-Acdtrux' options")));
+ _("You must specify one of the '-Acdtrux', '--delete' or '--test-label' options")));
case CAT_SUBCOMMAND:
case UPDATE_SUBCOMMAND:
diff_init ();
read_and (diff_archive);
break;
+
+ case TEST_LABEL_SUBCOMMAND:
+ test_archive_label ();
}
if (totals_option)
/* Dispose of allocated memory, and return. */
free (archive_name_array);
+ xattrs_clear_setup ();
name_term ();
if (exit_status == TAREXIT_FAILURE)
- error (0, 0, _("Error exit delayed from previous errors"));
+ error (0, 0, _("Exiting with failure status due to previous errors"));
if (stdlis == stdout)
close_stdout ();
else if (ferror (stderr) || fclose (stderr) != 0)
- exit_status = TAREXIT_FAILURE;
+ set_exit_status (TAREXIT_FAILURE);
return exit_status;
}
memset (st, 0, sizeof (*st));
}
+/* Close the stream or file descriptor associated with ST, and remove
+ all traces of it from ST. Return true if successful, false (with a
+ diagnostic) otherwise. */
+bool
+tar_stat_close (struct tar_stat_info *st)
+{
+ int status = (st->dirstream ? closedir (st->dirstream)
+ : 0 < st->fd ? close (st->fd)
+ : 0);
+ st->dirstream = 0;
+ st->fd = 0;
+
+ if (status == 0)
+ return true;
+ else
+ {
+ close_diag (st->orig_file_name);
+ return false;
+ }
+}
+
void
tar_stat_destroy (struct tar_stat_info *st)
{
+ tar_stat_close (st);
+ xheader_xattr_free (st->xattr_map, st->xattr_map_size);
free (st->orig_file_name);
free (st->file_name);
free (st->link_name);
free (st->uname);
free (st->gname);
+ free (st->cntx_name);
+ free (st->acls_a_ptr);
+ free (st->acls_d_ptr);
free (st->sparse_map);
free (st->dumpdir);
xheader_destroy (&st->xhdr);
a.tv_nsec = b.tv_nsec = 0;
return timespec_cmp (a, b);
}
+
+/* Set tar exit status to VAL, unless it is already indicating
+ a more serious condition. This relies on the fact that the
+ values of TAREXIT_ constants are ranged by severity. */
+void
+set_exit_status (int val)
+{
+ if (val > exit_status)
+ exit_status = val;
+}