From 4cf2af45007b5288dae1037baffa681d37c0b1c2 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Fri, 11 Dec 2015 00:37:17 +0200 Subject: [PATCH] Fix handling of filename-selection options. Filename-selection options are --wildcards, --recursive, etc. (see names.c for a complete list). These options are position-sensitive, i.e. each such option affects all filenames and filename-selection options that appear after it until eventually cancelled by a corresponding counterpart option. These options can appear in "file-from" file lists, which means that they cannot be handled right away, but instead should be put on the "name_elt" list and processed sequentionally, as file name arguments are. * src/common.h (warn_regex_usage): Remove. (name_add_name): Change signature. (name_add_dir, name_add_file): Remove prototypes. * src/names.c (name_add_option, name_add_dir) (name_add_file): Static functions. (names_options, is_file_selection_option, names_parse_opt): Static functions. (names_argp_children): New variable. (NELT_NAME, NELT_CHDIR) (NELT_FILE, NELT_NOOP): Redefine as enum nelt_type. (NELT_FMASK): Remove. (NELT_OPTION): New constant. (name_elt) : Change type. : New member. (name_elt_alloc_matflags): Remove. (name_add_name): Take one argument. (name_add_option): New static function. (name_add_file): Take one argument. (read_next_name): Use filename_terminator and verbatim_files_from_option to initialize file.term and file.verbatim. * src/tar.c: Move handling of filename-selection options to names.c * tests/T-dir00.at: Fix typo. * tests/T-recurse.at: Remove expected failure. --- src/common.h | 10 +- src/names.c | 475 +++++++++++++++++++++++++++++++++++++++++---- src/tar.c | 378 +++--------------------------------- tests/T-dir00.at | 2 +- tests/T-recurse.at | 1 - 5 files changed, 466 insertions(+), 400 deletions(-) diff --git a/src/common.h b/src/common.h index 02ef1795..7534748e 100644 --- a/src/common.h +++ b/src/common.h @@ -408,9 +408,8 @@ GLOBAL bool show_transformed_names_option; set for incremental archives. */ GLOBAL bool delay_directory_restore_option; -/* Warn about implicit use of the wildcards in command line arguments. - (Default for tar prior to 1.15.91, but changed afterwards */ -GLOBAL bool warn_regex_usage; +/* When set, tar will not refuse to create empty archives */ +GLOBAL bool files_from_option; /* Declarations for each module. */ @@ -732,10 +731,7 @@ void uid_to_uname (uid_t uid, char **uname); int uname_to_uid (char const *uname, uid_t *puid); void name_init (void); -void name_add_name (const char *name, int matching_flags); -void name_add_dir (const char *name); -void name_add_file (const char *name, int term, bool verbatim, - int matching_flags); +void name_add_name (const char *name); void name_term (void); const char *name_next (int change_dirs); void name_gather (void); diff --git a/src/names.c b/src/names.c index 6e4616d6..a30b0fbe 100644 --- a/src/names.c +++ b/src/names.c @@ -26,6 +26,396 @@ #include "common.h" +static void name_add_option (int option, const char *arg); +static void name_add_dir (const char *name); +static void name_add_file (const char *name); + +enum + { + EXCLUDE_BACKUPS_OPTION = 256, + EXCLUDE_CACHES_OPTION, + EXCLUDE_CACHES_UNDER_OPTION, + EXCLUDE_CACHES_ALL_OPTION, + EXCLUDE_OPTION, + EXCLUDE_IGNORE_OPTION, + EXCLUDE_IGNORE_RECURSIVE_OPTION, + EXCLUDE_TAG_OPTION, + EXCLUDE_TAG_UNDER_OPTION, + EXCLUDE_TAG_ALL_OPTION, + EXCLUDE_VCS_OPTION, + EXCLUDE_VCS_IGNORES_OPTION, + IGNORE_CASE_OPTION, + NO_IGNORE_CASE_OPTION, + ANCHORED_OPTION, + NO_ANCHORED_OPTION, + RECURSION_OPTION, + NO_RECURSION_OPTION, + UNQUOTE_OPTION, + NO_UNQUOTE_OPTION, + NO_VERBATIM_FILES_FROM_OPTION, + NO_WILDCARDS_MATCH_SLASH_OPTION, + NO_WILDCARDS_OPTION, + NULL_OPTION, + NO_NULL_OPTION, + VERBATIM_FILES_FROM_OPTION, + WILDCARDS_MATCH_SLASH_OPTION, + WILDCARDS_OPTION + }; + +static struct argp_option names_options[] = { +#define GRID 100 + {NULL, 0, NULL, 0, + N_("Local file name selection:"), GRID }, + + {"add-file", ARGP_KEY_ARG, N_("FILE"), 0, + N_("add given FILE to the archive (useful if its name starts with a dash)"), GRID+1 }, + {"directory", 'C', N_("DIR"), 0, + N_("change to directory DIR"), GRID+1 }, + {"files-from", 'T', N_("FILE"), 0, + N_("get names to extract or create from FILE"), GRID+1 }, + {"null", NULL_OPTION, 0, 0, + N_("-T reads null-terminated names; implies --verbatim-files-from"), + 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 input file or member names (default)"), GRID+1 }, + {"no-unquote", NO_UNQUOTE_OPTION, 0, 0, + N_("do not unquote input file or member names"), GRID+1 }, + {"verbatim-files-from", VERBATIM_FILES_FROM_OPTION, 0, 0, + N_("-T reads file names verbatim (no option handling)"), GRID+1 }, + {"no-verbatim-files-from", NO_VERBATIM_FILES_FROM_OPTION, 0, 0, + N_("-T treats file names starting with dash as options (default)"), + GRID+1 }, + {"exclude", EXCLUDE_OPTION, N_("PATTERN"), 0, + N_("exclude files, given as a PATTERN"), 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 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-ignore", EXCLUDE_IGNORE_OPTION, N_("FILE"), 0, + N_("read exclude patterns for each directory from FILE, if it exists"), + GRID+1 }, + {"exclude-ignore-recursive", EXCLUDE_IGNORE_RECURSIVE_OPTION, N_("FILE"), 0, + N_("read exclude patterns for each directory and its subdirectories " + "from FILE, if it exists"), 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 }, + {"exclude-vcs", EXCLUDE_VCS_OPTION, NULL, 0, + N_("exclude version control system directories"), GRID+1 }, + {"exclude-vcs-ignores", EXCLUDE_VCS_IGNORES_OPTION, NULL, 0, + N_("read exclude patterns from the VCS ignore files"), GRID+1 }, + {"exclude-backups", EXCLUDE_BACKUPS_OPTION, NULL, 0, + N_("exclude backup and lock files"), GRID+1 }, + {"recursion", RECURSION_OPTION, 0, 0, + N_("recurse into directories (default)"), GRID+1 }, + {"no-recursion", NO_RECURSION_OPTION, 0, 0, + N_("avoid descending automatically in directories"), GRID+1 }, +#undef GRID + +#define GRID 120 + {NULL, 0, NULL, 0, + N_("File name matching options (affect both exclude and include patterns):"), + GRID }, + {"ignore-case", IGNORE_CASE_OPTION, 0, 0, + N_("ignore case"), GRID+1 }, + {"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 }, + {"no-ignore-case", NO_IGNORE_CASE_OPTION, 0, 0, + N_("case sensitive matching (default)"), GRID+1 }, + {"wildcards", WILDCARDS_OPTION, 0, 0, + N_("use wildcards (default for exclusion)"), GRID+1 }, + {"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 }, + {"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0, + N_("wildcards match '/' (default for exclusion)"), GRID+1 }, +#undef GRID + + {NULL} +}; + +static bool +is_file_selection_option (int key) +{ + struct argp_option *p; + + for (p = names_options; + !(p->name == NULL && p->key == 0 && p->doc == NULL); p++) + if (p->key == key) + return true; + return false; +} + +/* Either NL or NUL, as decided by the --null option. */ +static char filename_terminator = '\n'; +/* Treat file names read from -T input verbatim */ +static bool verbatim_files_from_option; + +static error_t +names_parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'C': + name_add_dir (arg); + break; + + case 'T': + name_add_file (arg); + /* Indicate we've been given -T option. This is for backward + compatibility only, so that `tar cfT archive /dev/null will + succeed */ + files_from_option = true; + break; + + default: + if (is_file_selection_option (key)) + name_add_option (key, arg); + else + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* Wildcard matching settings */ +enum wildcards + { + default_wildcards, /* For exclusion == enable_wildcards, + for inclusion == disable_wildcards */ + disable_wildcards, + enable_wildcards + }; + +static enum wildcards wildcards = default_wildcards; + /* Wildcard settings (--wildcards/--no-wildcards) */ +static int matching_flags = 0; + /* exclude_fnmatch options */ +static int include_anchored = EXCLUDE_ANCHORED; + /* Pattern anchoring options used for file inclusion */ + +#define EXCLUDE_OPTIONS \ + (((wildcards != disable_wildcards) ? EXCLUDE_WILDCARDS : 0) \ + | matching_flags \ + | recursion_option) + +#define INCLUDE_OPTIONS \ + (((wildcards == enable_wildcards) ? EXCLUDE_WILDCARDS : 0) \ + | include_anchored \ + | matching_flags \ + | recursion_option) + +static char const * const vcs_file_table[] = { + /* CVS: */ + "CVS", + ".cvsignore", + /* RCS: */ + "RCS", + /* SCCS: */ + "SCCS", + /* SVN: */ + ".svn", + /* git: */ + ".git", + ".gitignore", + ".gitattributes", + ".gitmodules", + /* Arch: */ + ".arch-ids", + "{arch}", + "=RELEASE-ID", + "=meta-update", + "=update", + /* Bazaar */ + ".bzr", + ".bzrignore", + ".bzrtags", + /* Mercurial */ + ".hg", + ".hgignore", + ".hgtags", + /* darcs */ + "_darcs", + NULL +}; + +static char const * const backup_file_table[] = { + ".#*", + "*~", + "#*#", + NULL +}; + +static void +add_exclude_array (char const * const * fv, int opts) +{ + int i; + + for (i = 0; fv[i]; i++) + add_exclude (excluded, fv[i], opts); +} + +static void +handle_file_selection_option (int key, const char *arg) +{ + switch (key) + { + case EXCLUDE_BACKUPS_OPTION: + add_exclude_array (backup_file_table, EXCLUDE_WILDCARDS); + break; + + case EXCLUDE_OPTION: + add_exclude (excluded, arg, EXCLUDE_OPTIONS); + break; + + case EXCLUDE_CACHES_OPTION: + 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_IGNORE_OPTION: + excfile_add (arg, EXCL_NON_RECURSIVE); + break; + + case EXCLUDE_IGNORE_RECURSIVE_OPTION: + excfile_add (arg, EXCL_RECURSIVE); + 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 EXCLUDE_VCS_OPTION: + add_exclude_array (vcs_file_table, 0); + break; + + case EXCLUDE_VCS_IGNORES_OPTION: + exclude_vcs_ignores (); + break; + + case RECURSION_OPTION: + recursion_option = FNM_LEADING_DIR; + break; + + case NO_RECURSION_OPTION: + recursion_option = 0; + break; + + case UNQUOTE_OPTION: + unquote_option = true; + break; + + case NO_UNQUOTE_OPTION: + unquote_option = false; + break; + + case NULL_OPTION: + filename_terminator = '\0'; + verbatim_files_from_option = true; + break; + + case NO_NULL_OPTION: + filename_terminator = '\n'; + verbatim_files_from_option = false; + break; + + case 'X': + if (add_exclude_file (add_exclude, excluded, arg, EXCLUDE_OPTIONS, '\n') + != 0) + { + int e = errno; + FATAL_ERROR ((0, e, "%s", quotearg_colon (arg))); + } + break; + + case ANCHORED_OPTION: + matching_flags |= EXCLUDE_ANCHORED; + break; + + case NO_ANCHORED_OPTION: + include_anchored = 0; /* Clear the default for comman line args */ + matching_flags &= ~ EXCLUDE_ANCHORED; + break; + + case IGNORE_CASE_OPTION: + matching_flags |= FNM_CASEFOLD; + break; + + case NO_IGNORE_CASE_OPTION: + matching_flags &= ~ FNM_CASEFOLD; + break; + + case WILDCARDS_OPTION: + wildcards = enable_wildcards; + break; + + case NO_WILDCARDS_OPTION: + wildcards = disable_wildcards; + break; + + case WILDCARDS_MATCH_SLASH_OPTION: + matching_flags &= ~ FNM_FILE_NAME; + break; + + case NO_WILDCARDS_MATCH_SLASH_OPTION: + matching_flags |= FNM_FILE_NAME; + break; + + case VERBATIM_FILES_FROM_OPTION: + verbatim_files_from_option = true; + break; + + case NO_VERBATIM_FILES_FROM_OPTION: + verbatim_files_from_option = false; + break; + + default: + FATAL_ERROR ((0, 0, "unhandled positional option %d", key)); + } +} + +static struct argp names_argp = { + names_options, + names_parse_opt, +}; + +struct argp_child names_argp_children[] = { + { &names_argp, 0, "" }, + { NULL } +}; + /* User and group names. */ /* Make sure you link with the proper libraries if you are running the @@ -210,20 +600,22 @@ static struct name *nametail; /* end of name list */ /* A name_list element contains entries of three types: */ -#define NELT_NAME 0 /* File name */ -#define NELT_CHDIR 1 /* Change directory request */ -#define NELT_FMASK 2 /* Change fnmatch options request */ -#define NELT_FILE 3 /* Read file names from that file */ -#define NELT_NOOP 4 /* No operation */ +enum nelt_type + { + NELT_NAME, /* File name */ + NELT_CHDIR, /* Change directory request */ + NELT_FILE, /* Read file names from that file */ + NELT_NOOP, /* No operation */ + NELT_OPTION /* Filename-selection option */ + }; struct name_elt /* A name_array element. */ { struct name_elt *next, *prev; - char type; /* Element type, see NELT_* constants above */ + enum nelt_type type; /* Element type, see NELT_* constants above */ union { const char *name; /* File or directory name */ - int matching_flags;/* fnmatch options if type == NELT_FMASK */ struct /* File, if type == NELT_FILE */ { const char *name;/* File name */ @@ -233,6 +625,11 @@ struct name_elt /* A name_array element. */ trimming, no option processing */ FILE *fp; } file; + struct + { + int option; + char const *arg; + } opt; /* NELT_OPTION */ } v; }; @@ -261,21 +658,6 @@ name_elt_alloc (void) return elt; } -static struct name_elt * -name_elt_alloc_matflags (int matflags) -{ - static int prev_flags = 0; /* FIXME: Or EXCLUDE_ANCHORED? */ - struct name_elt *ep = name_elt_alloc (); - if (prev_flags != matflags) - { - ep->type = NELT_FMASK; - ep->v.matching_flags = matflags; - prev_flags = matflags; - ep = name_elt_alloc (); - } - return ep; -} - static void name_list_adjust (void) { @@ -297,17 +679,26 @@ name_list_advance (void) /* Add to name_array the file NAME with fnmatch options MATFLAGS */ void -name_add_name (const char *name, int matflags) +name_add_name (const char *name) { - struct name_elt *ep = name_elt_alloc_matflags (matflags); + struct name_elt *ep = name_elt_alloc (); ep->type = NELT_NAME; ep->v.name = name; name_count++; } +static void +name_add_option (int option, const char *arg) +{ + struct name_elt *elt = name_elt_alloc (); + elt->type = NELT_OPTION; + elt->v.opt.option = option; + elt->v.opt.arg = arg; +} + /* Add to name_array a chdir request for the directory NAME */ -void +static void name_add_dir (const char *name) { struct name_elt *ep = name_elt_alloc (); @@ -315,16 +706,14 @@ name_add_dir (const char *name) ep->v.name = name; } -void -name_add_file (const char *name, int term, bool verbatim, int matflags) +static void +name_add_file (const char *name) { - struct name_elt *ep = name_elt_alloc_matflags (matflags); + struct name_elt *ep = name_elt_alloc (); ep->type = NELT_FILE; ep->v.file.name = name; ep->v.file.line = 0; - ep->v.file.term = term; - ep->v.file.verbatim = verbatim; ep->v.file.fp = NULL; } @@ -504,6 +893,8 @@ read_next_name (struct name_elt *ent, struct name_elt *ret) if ((ent->v.file.fp = fopen (ent->v.file.name, "r")) == NULL) open_fatal (ent->v.file.name); } + ent->v.file.term = filename_terminator; + ent->v.file.verbatim = verbatim_files_from_option; } while (1) @@ -567,16 +958,12 @@ copy_name (struct name_elt *ep) } -static int matching_flags; /* exclude_fnmatch options */ - /* Get the next NELT_NAME element from name_array. Result is in static storage and can't be relied upon across two calls. If CHANGE_DIRS is true, treat any entries of type NELT_CHDIR as the request to change to the given directory. - Entries of type NELT_FMASK cause updates of the matching_flags - value. */ static struct name_elt * name_next_elt (int change_dirs) @@ -592,12 +979,6 @@ name_next_elt (int change_dirs) name_list_advance (); break; - case NELT_FMASK: - matching_flags = ep->v.matching_flags; - recursion_option = matching_flags & FNM_LEADING_DIR; - name_list_advance (); - continue; - case NELT_FILE: if (read_next_name (ep, &entry) == 0) return &entry; @@ -619,6 +1000,11 @@ name_next_elt (int change_dirs) entry.v.name = name_buffer; name_list_advance (); return &entry; + + case NELT_OPTION: + handle_file_selection_option (ep->v.opt.option, ep->v.opt.arg); + name_list_advance (); + continue; } } @@ -664,7 +1050,7 @@ name_gather (void) buffer->change_dir = change_dir; buffer->next = 0; buffer->found_count = 0; - buffer->matching_flags = matching_flags; + buffer->matching_flags = INCLUDE_OPTIONS; buffer->directory = NULL; buffer->parent = NULL; buffer->cmdline = true; @@ -706,7 +1092,7 @@ addname (char const *string, int change_dir, bool cmdline, struct name *parent) name->prev = nametail; name->next = NULL; name->found_count = 0; - name->matching_flags = matching_flags; + name->matching_flags = INCLUDE_OPTIONS; name->change_dir = change_dir; name->directory = NULL; name->parent = parent; @@ -841,7 +1227,10 @@ regex_usage_warning (const char *name) { static int warned_once = 0; - if (warn_regex_usage && fnmatch_pattern_has_wildcards (name, 0)) + /* Warn about implicit use of the wildcards in command line arguments. + (Default for tar prior to 1.15.91, but changed afterwards) */ + if (wildcards == default_wildcards + && fnmatch_pattern_has_wildcards (name, 0)) { warned_once = 1; WARN ((0, 0, diff --git a/src/tar.c b/src/tar.c index 6be62e05..bacb142d 100644 --- a/src/tar.c +++ b/src/tar.c @@ -74,9 +74,6 @@ static int check_links_option; /* Number of allocated tape drive names. */ static size_t allocated_archive_names; -/* Treat file names read from -T input verbatim */ -static bool verbatim_files_from_option; - /* Miscellaneous. */ @@ -274,7 +271,6 @@ tar_set_quoting_style (char *arg) enum { ACLS_OPTION = CHAR_MAX + 1, - ANCHORED_OPTION, ATIME_PRESERVE_OPTION, BACKUP_OPTION, CHECK_DEVICE_OPTION, @@ -283,23 +279,10 @@ enum DELAY_DIRECTORY_RESTORE_OPTION, HARD_DEREFERENCE_OPTION, DELETE_OPTION, - EXCLUDE_BACKUPS_OPTION, - EXCLUDE_CACHES_OPTION, - EXCLUDE_CACHES_UNDER_OPTION, - EXCLUDE_CACHES_ALL_OPTION, - EXCLUDE_OPTION, - EXCLUDE_IGNORE_OPTION, - EXCLUDE_IGNORE_RECURSIVE_OPTION, - EXCLUDE_TAG_OPTION, - EXCLUDE_TAG_UNDER_OPTION, - EXCLUDE_TAG_ALL_OPTION, - EXCLUDE_VCS_OPTION, - EXCLUDE_VCS_IGNORES_OPTION, FORCE_LOCAL_OPTION, FULL_TIME_OPTION, GROUP_OPTION, GROUP_MAP_OPTION, - IGNORE_CASE_OPTION, IGNORE_COMMAND_ERROR_OPTION, IGNORE_FAILED_READ_OPTION, INDEX_FILE_OPTION, @@ -313,26 +296,17 @@ enum 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_VERBATIM_FILES_FROM_OPTION, - NO_WILDCARDS_MATCH_SLASH_OPTION, - NO_WILDCARDS_OPTION, NO_XATTR_OPTION, - NULL_OPTION, NUMERIC_OWNER_OPTION, OCCURRENCE_OPTION, OLD_ARCHIVE_OPTION, @@ -348,7 +322,6 @@ enum QUOTE_CHARS_OPTION, QUOTING_STYLE_OPTION, RECORD_SIZE_OPTION, - RECURSION_OPTION, RECURSIVE_UNLINK_OPTION, REMOVE_FILES_OPTION, RESTRICT_OPTION, @@ -370,13 +343,9 @@ enum TOTALS_OPTION, TO_COMMAND_OPTION, TRANSFORM_OPTION, - UNQUOTE_OPTION, UTC_OPTION, - VERBATIM_FILES_FROM_OPTION, VOLNO_FILE_OPTION, WARNING_OPTION, - WILDCARDS_MATCH_SLASH_OPTION, - WILDCARDS_OPTION, XATTR_OPTION, XATTR_EXCLUDE, XATTR_INCLUDE @@ -718,64 +687,8 @@ static struct argp_option options[] = { #define GRID 100 {NULL, 0, NULL, 0, N_("Local file selection:"), GRID }, - - {"add-file", ARGP_KEY_ARG, N_("FILE"), 0, - N_("add given FILE to the archive (useful if its name starts with a dash)"), GRID+1 }, - {"directory", 'C', N_("DIR"), 0, - N_("change to directory DIR"), GRID+1 }, - {"files-from", 'T', N_("FILE"), 0, - N_("get names to extract or create from FILE"), GRID+1 }, - {"null", NULL_OPTION, 0, 0, - N_("-T reads null-terminated names; implies --verbatim-files-from"), - 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 input file or member names (default)"), GRID+1 }, - {"no-unquote", NO_UNQUOTE_OPTION, 0, 0, - N_("do not unquote input file or member names"), GRID+1 }, - {"verbatim-files-from", VERBATIM_FILES_FROM_OPTION, 0, 0, - N_("-T reads file names verbatim (no option handling)"), GRID+1 }, - {"no-verbatim-files-from", NO_VERBATIM_FILES_FROM_OPTION, 0, 0, - N_("-T treats file names starting with dash as options (default)"), - GRID+1 }, - {"exclude", EXCLUDE_OPTION, N_("PATTERN"), 0, - N_("exclude files, given as a PATTERN"), 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 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-ignore", EXCLUDE_IGNORE_OPTION, N_("FILE"), 0, - N_("read exclude patterns for each directory from FILE, if it exists"), - GRID+1 }, - {"exclude-ignore-recursive", EXCLUDE_IGNORE_RECURSIVE_OPTION, N_("FILE"), 0, - N_("read exclude patterns for each directory and its subdirectories " - "from FILE, if it exists"), 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 }, - {"exclude-vcs", EXCLUDE_VCS_OPTION, NULL, 0, - N_("exclude version control system directories"), GRID+1 }, - {"exclude-vcs-ignores", EXCLUDE_VCS_IGNORES_OPTION, NULL, 0, - N_("read exclude patterns from the VCS ignore files"), 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, N_("stay in local file system when creating archive"), GRID+1 }, - {"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 }, {"dereference", 'h', 0, 0, @@ -806,28 +719,6 @@ static struct argp_option options[] = { {"xform", 0, 0, OPTION_ALIAS, NULL, GRID+1 }, #undef GRID -#define GRID 120 - {NULL, 0, NULL, 0, - N_("File name matching options (affect both exclude and include patterns):"), - GRID }, - {"ignore-case", IGNORE_CASE_OPTION, 0, 0, - N_("ignore case"), GRID+1 }, - {"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 }, - {"no-ignore-case", NO_IGNORE_CASE_OPTION, 0, 0, - N_("case sensitive matching (default)"), GRID+1 }, - {"wildcards", WILDCARDS_OPTION, 0, 0, - N_("use wildcards (default for exclusion)"), GRID+1 }, - {"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 }, - {"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0, - N_("wildcards match '/' (default for exclusion)"), GRID+1 }, -#undef GRID - #define GRID 130 {NULL, 0, NULL, 0, N_("Informative output:"), GRID }, @@ -911,15 +802,6 @@ static enum atime_preserve const atime_preserve_types[] = (minus 1 for NULL guard) */ ARGMATCH_VERIFY (atime_preserve_args, atime_preserve_types); -/* Wildcard matching settings */ -enum wildcards - { - default_wildcards, /* For exclusion == enable_wildcards, - for inclusion == disable_wildcards */ - disable_wildcards, - enable_wildcards - }; - struct tar_args /* Variables used during option parsing */ { struct option_locus *loc; @@ -927,11 +809,6 @@ struct tar_args /* Variables used during option parsing */ struct textual_date *textual_date; /* Keeps the arguments to --newer-mtime and/or --date option if they are textual dates */ - enum wildcards wildcards; /* Wildcard settings (--wildcards/ - --no-wildcards) */ - int matching_flags; /* exclude_fnmatch options */ - int include_anchored; /* Pattern anchoring options used for - file inclusion */ bool o_option; /* True if -o option was given */ bool pax_option; /* True if --pax-option was given */ char const *backup_suffix_string; /* --suffix option argument */ @@ -941,68 +818,6 @@ struct tar_args /* Variables used during option parsing */ be attempted when creating archives */ }; - -#define MAKE_EXCL_OPTIONS(args) \ - ((((args)->wildcards != disable_wildcards) ? EXCLUDE_WILDCARDS : 0) \ - | (args)->matching_flags \ - | recursion_option) - -#define MAKE_INCL_OPTIONS(args) \ - ((((args)->wildcards == enable_wildcards) ? EXCLUDE_WILDCARDS : 0) \ - | (args)->include_anchored \ - | (args)->matching_flags \ - | recursion_option) - -static char const * const vcs_file_table[] = { - /* CVS: */ - "CVS", - ".cvsignore", - /* RCS: */ - "RCS", - /* SCCS: */ - "SCCS", - /* SVN: */ - ".svn", - /* git: */ - ".git", - ".gitignore", - ".gitattributes", - ".gitmodules", - /* Arch: */ - ".arch-ids", - "{arch}", - "=RELEASE-ID", - "=meta-update", - "=update", - /* Bazaar */ - ".bzr", - ".bzrignore", - ".bzrtags", - /* Mercurial */ - ".hg", - ".hgignore", - ".hgtags", - /* darcs */ - "_darcs", - NULL -}; - -static char const * const backup_file_table[] = { - ".#*", - "*~", - "#*#", - NULL -}; - -static void -add_exclude_array (char const * const * fv, int opts) -{ - int i; - - for (i = 0; fv[i]; i++) - add_exclude (excluded, fv[i], opts); -} - static char * format_default_settings (void) @@ -1258,9 +1073,6 @@ report_textual_dates (struct tar_args *args) } -static bool files_from_option; /* When set, tar will not refuse to create - empty archives */ - /* Default density numbers for [0-9][lmh] device specifications */ #if defined DEVICE_PREFIX && !defined DENSITY_LETTER @@ -1446,9 +1258,6 @@ parse_owner_group (char *arg, uintmax_t field_max, char const **name_option) #define TAR_SIZE_SUFFIXES "bBcGgkKMmPTtw" -/* Either NL or NUL, as decided by the --null option. */ -static char filename_terminator; - static char const *const sort_mode_arg[] = { "none", "name", @@ -1502,6 +1311,7 @@ set_old_files_option (int code, struct option_locus *loc) old_files_option = code; } + static error_t parse_opt (int key, char *arg, struct argp_state *state) { @@ -1511,7 +1321,7 @@ parse_opt (int key, char *arg, struct argp_state *state) { case ARGP_KEY_ARG: /* File name or non-parsed option, because of ARGP_IN_ORDER */ - name_add_name (arg, MAKE_INCL_OPTIONS (args)); + name_add_name (arg); args->input_files = true; break; @@ -1554,10 +1364,6 @@ parse_opt (int key, char *arg, struct argp_state *state) set_subcommand_option (CREATE_SUBCOMMAND); break; - case 'C': - name_add_dir (arg); - break; - case 'd': set_subcommand_option (DIFF_SUBCOMMAND); break; @@ -1800,15 +1606,6 @@ parse_opt (int key, char *arg, struct argp_state *state) set_subcommand_option (TEST_LABEL_SUBCOMMAND); break; - case 'T': - name_add_file (arg, filename_terminator, verbatim_files_from_option, - MAKE_INCL_OPTIONS (args)); - /* Indicate we've been given -T option. This is for backward - compatibility only, so that `tar cfT archive /dev/null will - succeed */ - files_from_option = true; - break; - case 'u': set_subcommand_option (UPDATE_SUBCOMMAND); break; @@ -1843,16 +1640,6 @@ parse_opt (int key, char *arg, struct argp_state *state) set_subcommand_option (EXTRACT_SUBCOMMAND); break; - case 'X': - if (add_exclude_file (add_exclude, excluded, arg, - MAKE_EXCL_OPTIONS (args), '\n') - != 0) - { - int e = errno; - FATAL_ERROR ((0, e, "%s", quotearg_colon (arg))); - } - break; - case 'z': set_use_compress_program_option (GZIP_PROGRAM, args->loc); break; @@ -1861,10 +1648,6 @@ parse_opt (int key, char *arg, struct argp_state *state) set_use_compress_program_option (COMPRESS_PROGRAM, args->loc); break; - case ANCHORED_OPTION: - args->matching_flags |= EXCLUDE_ANCHORED; - break; - case ATIME_PRESERVE_OPTION: atime_preserve_option = (arg @@ -1926,57 +1709,6 @@ parse_opt (int key, char *arg, struct argp_state *state) 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; - - case EXCLUDE_CACHES_OPTION: - 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_IGNORE_OPTION: - excfile_add (arg, EXCL_NON_RECURSIVE); - break; - - case EXCLUDE_IGNORE_RECURSIVE_OPTION: - excfile_add (arg, EXCL_RECURSIVE); - 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 EXCLUDE_VCS_OPTION: - add_exclude_array (vcs_file_table, 0); - break; - - case EXCLUDE_VCS_IGNORES_OPTION: - exclude_vcs_ignores (); - break; - case FORCE_LOCAL_OPTION: force_local_option = true; break; @@ -1989,10 +1721,6 @@ parse_opt (int key, char *arg, struct argp_state *state) index_file_name = arg; break; - case IGNORE_CASE_OPTION: - args->matching_flags |= FNM_CASEFOLD; - break; - case IGNORE_COMMAND_ERROR_OPTION: ignore_command_error_option = true; break; @@ -2036,15 +1764,6 @@ parse_opt (int key, char *arg, struct argp_state *state) umask (initial_umask); break; - case NO_ANCHORED_OPTION: - args->include_anchored = 0; /* Clear the default for comman line args */ - args->matching_flags &= ~ EXCLUDE_ANCHORED; - break; - - case NO_IGNORE_CASE_OPTION: - args->matching_flags &= ~ FNM_CASEFOLD; - break; - case NO_IGNORE_COMMAND_ERROR_OPTION: ignore_command_error_option = false; break; @@ -2058,24 +1777,6 @@ parse_opt (int key, char *arg, struct argp_state *state) set_char_quoting (NULL, *arg, 0); break; - case NO_WILDCARDS_OPTION: - args->wildcards = disable_wildcards; - break; - - case NO_WILDCARDS_MATCH_SLASH_OPTION: - args->matching_flags |= FNM_FILE_NAME; - break; - - case NULL_OPTION: - filename_terminator = '\0'; - verbatim_files_from_option = true; - break; - - case NO_NULL_OPTION: - filename_terminator = '\n'; - verbatim_files_from_option = false; - break; - case NUMERIC_OWNER_OPTION: numeric_owner_option = true; break; @@ -2261,18 +1962,6 @@ parse_opt (int key, char *arg, struct argp_state *state) volno_file_option = arg; break; - case WILDCARDS_OPTION: - args->wildcards = enable_wildcards; - break; - - case WILDCARDS_MATCH_SLASH_OPTION: - args->matching_flags &= ~ FNM_FILE_NAME; - break; - - case NO_RECURSION_OPTION: - recursion_option = 0; - break; - case NO_SAME_OWNER_OPTION: same_owner_option = -1; break; @@ -2313,30 +2002,10 @@ parse_opt (int key, char *arg, struct argp_state *state) xattrs_mask_add (arg, (key == XATTR_INCLUDE)); break; - case RECURSION_OPTION: - recursion_option = FNM_LEADING_DIR; - break; - case SAME_OWNER_OPTION: same_owner_option = 1; break; - case UNQUOTE_OPTION: - unquote_option = true; - break; - - case NO_UNQUOTE_OPTION: - unquote_option = false; - break; - - case VERBATIM_FILES_FROM_OPTION: - verbatim_files_from_option = true; - break; - - case NO_VERBATIM_FILES_FROM_OPTION: - verbatim_files_from_option = false; - break; - case WARNING_OPTION: set_warning_option (arg); break; @@ -2419,12 +2088,14 @@ parse_opt (int key, char *arg, struct argp_state *state) return 0; } +extern struct argp_child names_argp_children[]; + static struct argp argp = { options, parse_opt, N_("[FILE]..."), doc, - NULL, + names_argp_children, tar_help_filter, NULL }; @@ -2439,8 +2110,8 @@ usage (int status) /* Parse the options for tar. */ -static struct argp_option * -find_argp_option (struct argp_option *o, int letter) +static struct argp_option const * +find_argp_option_key (struct argp_option const *o, int key) { for (; !(o->name == NULL @@ -2448,11 +2119,30 @@ find_argp_option (struct argp_option *o, int letter) && o->arg == 0 && o->flags == 0 && o->doc == NULL); o++) - if (o->key == letter) + if (o->key == key) return o; return NULL; } +static struct argp_option const * +find_argp_option (struct argp *ap, int key) +{ + struct argp_option const *p = NULL; + struct argp_child const *child; + + p = find_argp_option_key (ap->options, key); + if (!p && ap->children) + { + for (child = ap->children; child; child++) + { + p = find_argp_option_key (child->argp->options, key); + if (p) + break; + } + } + return p; +} + static const char *tar_authors[] = { "John Gilmore", "Jay Fenlason", @@ -2530,9 +2220,6 @@ decode_options (int argc, char **argv) /* Set some default option values. */ args.textual_date = NULL; - args.wildcards = default_wildcards; - args.matching_flags = 0; - args.include_anchored = EXCLUDE_ANCHORED; args.o_option = false; args.pax_option = false; args.backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); @@ -2595,11 +2282,11 @@ decode_options (int argc, char **argv) for (letter = *in++; *letter; letter++) { - struct argp_option *opt; + struct argp_option const *opt; buffer[1] = *letter; *out++ = xstrdup (buffer); - opt = find_argp_option (options, *letter); + opt = find_argp_option (&argp, *letter); if (opt && opt->arg) { if (in < argv + argc) @@ -2655,14 +2342,10 @@ decode_options (int argc, char **argv) /* Handle operands after any "--" argument. */ for (; idx < argc; idx++) { - name_add_name (argv[idx], MAKE_INCL_OPTIONS (&args)); + name_add_name (argv[idx]); 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) @@ -2992,7 +2675,6 @@ main (int argc, char **argv) exit_status = TAREXIT_SUCCESS; error_hook = checkpoint_flush_actions; - filename_terminator = '\n'; set_quoting_style (0, DEFAULT_QUOTING_STYLE); /* Make sure we have first three descriptors available */ diff --git a/tests/T-dir00.at b/tests/T-dir00.at index 8ff6ba8a..d2b48f79 100644 --- a/tests/T-dir00.at +++ b/tests/T-dir00.at @@ -18,7 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Tar 1.27 and 1.28 did not extract files under directory memberes listed +# Tar 1.27 and 1.28 did not extract files under directory members listed # in the file read by --file-from. # # Reported-by: Jean-Louis Martineau diff --git a/tests/T-recurse.at b/tests/T-recurse.at index 7d5efe8f..2a4359bf 100644 --- a/tests/T-recurse.at +++ b/tests/T-recurse.at @@ -85,7 +85,6 @@ AT_DATA([F2],[directory2/ tar cf archive -T F1 --recursion -T F2 tar tf archive -AT_XFAIL_IF([true]) ], [0], [directory1/ -- 2.47.2