Fix --update --wildcards

* src/common.h (name): New field: is_wildcard.
(name_scan): Change protoype.
* src/delete.c: Update calls to name_scan.
* src/names.c (addname, add_starting_file): Initialize is_wildcard.
(namelist_match): Take two arguments.  If second one is true, return
only exact matches.
(name_scan): Likewise.  All callers updated.
(name_from_list): Skip patterns.
* src/update.c (remove_exact_name): New function.
(update_archive): Do not remove matching name, if it is a pattern.
Instead, add a new entry with the matching file name.

* tests/update04.at: New test.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Include new test.

* NEWS: Update.
This commit is contained in:
Sergey Poznyakoff
2023-07-10 21:09:57 +03:00
parent b3a71dbdb9
commit 10954cf163
8 changed files with 121 additions and 20 deletions

View File

@@ -376,6 +376,7 @@ struct name
char *name; /* File name or globbing pattern */
size_t length; /* cached strlen (name) */
int matching_flags; /* wildcard flags if name is a pattern */
bool is_wildcard; /* true if this is a wildcard pattern */
bool cmdline; /* true if this name was given in the
command line */
@@ -783,7 +784,7 @@ bool name_match (const char *name);
void names_notfound (void);
void label_notfound (void);
void collect_and_sort_names (void);
struct name *name_scan (const char *name);
struct name *name_scan (const char *name, bool exact);
struct name const *name_from_list (void);
void blank_name_list (void);
char *make_file_name (const char *dir_name, const char *name);

View File

@@ -184,7 +184,7 @@ delete_archive_members (void)
abort ();
case HEADER_SUCCESS:
if ((name = name_scan (current_stat_info.file_name)) == NULL)
if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
{
skim_member (acting_as_filter);
break;
@@ -279,7 +279,7 @@ delete_archive_members (void)
/* Found another header. */
xheader_decode (&current_stat_info);
if ((name = name_scan (current_stat_info.file_name)) != NULL)
if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
{
name->found_count++;
if (ISFOUND (name))

View File

@@ -1153,6 +1153,13 @@ name_next (int change_dirs)
return nelt ? nelt->v.name : NULL;
}
static bool
name_is_wildcard (struct name const *name)
{
return (name->matching_flags & EXCLUDE_WILDCARDS) &&
fnmatch_pattern_has_wildcards (name->name, name->matching_flags);
}
/* Gather names in a list for scanning. Could hash them later if we
really care.
@@ -1189,6 +1196,7 @@ name_gather (void)
buffer->directory = NULL;
buffer->parent = NULL;
buffer->cmdline = true;
buffer->is_wildcard = name_is_wildcard (buffer);
namelist = nametail = buffer;
}
@@ -1232,6 +1240,7 @@ addname (char const *string, int change_dir, bool cmdline, struct name *parent)
name->directory = NULL;
name->parent = parent;
name->cmdline = cmdline;
name->is_wildcard = name_is_wildcard (name);
if (nametail)
nametail->next = name;
@@ -1265,19 +1274,22 @@ add_starting_file (char const *file_name)
name->directory = NULL;
name->parent = NULL;
name->cmdline = true;
name->is_wildcard = name_is_wildcard (name);
starting_file_option = true;
}
/* Find a match for FILE_NAME in the name list. */
/* Find a match for FILE_NAME in the name list. If EXPECT is true,
look for exact match (no wildcards). */
static struct name *
namelist_match (char const *file_name)
namelist_match (char const *file_name, bool exact)
{
struct name *p;
for (p = namelist; p; p = p->next)
{
if (p->name[0]
&& (exact ? !p->is_wildcard : true)
&& exclude_fnmatch (p->name, file_name, p->matching_flags))
return p;
}
@@ -1321,7 +1333,7 @@ name_match (const char *file_name)
return true;
}
cursor = namelist_match (file_name);
cursor = namelist_match (file_name, false);
if (starting_file_option)
{
/* If starting_file_option is set, the head of the list is the name
@@ -1870,13 +1882,14 @@ collect_and_sort_names (void)
1. It returns a pointer to the name it matched, and doesn't set FOUND
in structure. The caller will have to do that if it wants to.
2. If the namelist is empty, it returns null, unlike name_match, which
returns TRUE. */
returns TRUE.
3. If EXPECT is true, it looks for exact matches only (no wildcards). */
struct name *
name_scan (const char *file_name)
name_scan (const char *file_name, bool exact)
{
while (1)
{
struct name *cursor = namelist_match (file_name);
struct name *cursor = namelist_match (file_name, exact);
if (cursor)
return cursor;
@@ -1896,9 +1909,10 @@ name_scan (const char *file_name)
}
}
/* This returns a name from the namelist which doesn't have ->found
set. It sets ->found before returning, so successive calls will
find and return all the non-found names in the namelist. */
/* This returns a name from the namelist which is an exact match (i.e.
not a pattern) and doesn't have ->found set. It sets ->found before
returning, so successive calls will find and return all the non-found
names in the namelist. */
struct name *gnu_list_name;
struct name const *
@@ -1907,11 +1921,13 @@ name_from_list (void)
if (!gnu_list_name)
gnu_list_name = namelist;
while (gnu_list_name
&& (gnu_list_name->found_count || gnu_list_name->name[0] == 0))
&& (gnu_list_name->is_wildcard ||
gnu_list_name->found_count || gnu_list_name->name[0] == 0))
gnu_list_name = gnu_list_name->next;
if (gnu_list_name)
{
gnu_list_name->found_count++;
if (!gnu_list_name->is_wildcard)
gnu_list_name->found_count++;
chdir_do (gnu_list_name->change_dir);
return gnu_list_name;
}

View File

@@ -76,6 +76,25 @@ append_file (char *file_name)
close_error (file_name);
}
/* If NAME is not a pattern, remove it from the namelist. Otherwise,
remove the FILE_NAME that matched it. Take care to look for exact
match when removing it. */
static void
remove_exact_name (struct name *name, char const *file_name)
{
if (name->is_wildcard)
{
struct name *match = name_scan (file_name, true);
name->found_count++;
if (match)
name = match;
else
return;
}
remname (name);
}
/* Implement the 'r' (add files to end of archive), and 'u' (add files
to end of archive if they aren't there, or are more up to date than
the version in the archive) commands. */
@@ -113,7 +132,7 @@ update_archive (void)
archive_format = current_format;
if (subcommand_option == UPDATE_SUBCOMMAND
&& (name = name_scan (current_stat_info.file_name)) != NULL)
&& (name = name_scan (current_stat_info.file_name, false)) != NULL)
{
struct stat s;
@@ -122,10 +141,10 @@ update_archive (void)
{
if (S_ISDIR (s.st_mode))
{
char *p, *dirp = tar_savedir (name->name, 1);
char *p, *dirp = tar_savedir (current_stat_info.file_name, 1);
if (dirp)
{
namebuf_t nbuf = namebuf_create (name->name);
namebuf_t nbuf = namebuf_create (current_stat_info.file_name);
for (p = dirp; *p; p += strlen (p) + 1)
addname (namebuf_name (nbuf, p),
@@ -134,13 +153,18 @@ update_archive (void)
namebuf_free (nbuf);
free (dirp);
remname (name);
remove_exact_name (name, current_stat_info.file_name);
}
}
else if (tar_timespec_cmp (get_stat_mtime (&s),
current_stat_info.mtime)
<= 0)
remname (name);
{
remove_exact_name (name, current_stat_info.file_name);
}
else if (name->is_wildcard)
addname (current_stat_info.file_name,
name->change_dir, false, NULL);
}
}