Files
tar/src/exclist.c
Paul Eggert 238250f19e Prefer streq/memeq when they will do
Gnulib’s new streq and memeq functions make code a bit more
readable and, we hope, a bit more reliable and easy to maintain.
* gnulib.modules: Add stringeq.
* lib/wordsplit.c (wordsplit_find_env):
* src/buffer.c (check_compressed_archive, check_tty)
(_open_archive, new_volume, try_new_volume)
(drop_volume_label_suffix):
* src/checkpoint.c (checkpoint_compile_action):
* src/compare.c (process_rawdata, diff_symlink):
* src/create.c (cachedir_file_p):
* src/delete.c (delete_archive_members):
* src/exclist.c (hg_addfn, get_vcs_ignore_file):
* src/extract.c (ds_compare, remove_delayed_set_stat)
(fixup_delayed_set_stat, apply_nonancestor_delayed_set_stat)
(extract_link):
* src/incremen.c (nfs_file_stat, compare_directory_canonical_names)
(procdir):
* src/list.c (read_header, decode_header):
* src/misc.c (replace_prefix):
* src/names.c (uname_to_uid, gname_to_gid, read_next_name)
(name_compare):
* src/sparse.c (check_data_region):
* src/suffix.c (find_compression_suffix):
* src/system.c (sys_detect_dev_null_output)
(sys_child_open_for_compress, sys_child_open_for_uncompress):
* src/tar.c (set_archive_format, tar_set_quoting_style)
(optloc_eq, set_use_compress_program_option, decode_signal)
(report_textual_dates, decode_options):
* src/update.c (update_archive):
* src/warning.c (set_warning_option):
* src/xattrs.c (xattrs_xattrs_set):
* src/xheader.c (xheader_keyword_override_p)
(xheader_set_keyword_equal, locate_handler)
(xheader_protected_keyword_p):
Prefer memeq/streq to memcmp/strcmp when either will do.
2025-11-15 15:10:48 -08:00

323 lines
7.1 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Per-directory exclusion files for tar.
Copyright 2014-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
GNU tar 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 3 of the License, or
(at your option) any later version.
GNU tar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <system.h>
#include <c-ctype.h>
#include <quotearg.h>
#include <flexmember.h>
#include <fnmatch.h>
#include <wordsplit.h>
#include "common.h"
typedef void (*add_fn) (struct exclude *, char const *, int, void *);
struct vcs_ignore_file
{
char const *filename;
int flags;
add_fn addfn;
void *(*initfn) (void *);
void *data;
};
static struct vcs_ignore_file *get_vcs_ignore_file (const char *name);
struct excfile
{
struct excfile *next;
int flags;
char name[FLEXIBLE_ARRAY_MEMBER];
};
static struct excfile *excfile_head, *excfile_tail;
void
excfile_add (const char *name, int flags)
{
struct excfile *p = xmalloc (FLEXNSIZEOF (struct excfile, name,
strlen (name) + 1));
p->next = NULL;
p->flags = flags;
strcpy (p->name, name);
if (excfile_tail)
excfile_tail->next = p;
else
excfile_head = p;
excfile_tail = p;
}
struct exclist
{
struct exclist *next, *prev;
int flags;
struct exclude *excluded;
};
void
info_attach_exclist (struct tar_stat_info *dir)
{
struct exclist *head = NULL, *tail = NULL;
if (dir->exclude_list)
return;
for (struct excfile *file = excfile_head; file; file = file->next)
{
if (faccessat (dir->fd, file->name, F_OK, 0) == 0)
{
int fd = subfile_open (dir, file->name, O_RDONLY);
if (fd < 0)
{
open_error (file->name);
continue;
}
FILE *fp = fdopen (fd, "r");
if (!fp)
{
paxerror (errno, _("%s: fdopen failed"), file->name);
close (fd);
continue;
}
struct exclude *ex = new_exclude ();
struct vcs_ignore_file *vcsfile = get_vcs_ignore_file (file->name);
if (vcsfile->initfn)
vcsfile->data = vcsfile->initfn (vcsfile->data);
if (add_exclude_fp (vcsfile->addfn, ex, fp,
FNM_FILE_NAME|EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED,
'\n',
vcsfile->data))
paxfatal (errno, "%s", quotearg_colon (file->name));
fclose (fp);
struct exclist *ent = xmalloc (sizeof *ent);
ent->excluded = ex;
ent->flags = file->flags;
ent->prev = tail;
ent->next = NULL;
if (tail)
tail->next = ent;
else
head = ent;
tail = ent;
}
}
dir->exclude_list = head;
}
void
info_free_exclist (struct tar_stat_info *dir)
{
struct exclist *ep = dir->exclude_list;
while (ep)
{
struct exclist *next = ep->next;
free_exclude (ep->excluded);
free (ep);
ep = next;
}
dir->exclude_list = NULL;
}
/* Return nonzero if file NAME is excluded. */
bool
excluded_name (char const *name, struct tar_stat_info *st)
{
struct exclist *ep;
const char *rname = NULL;
char *bname = NULL;
bool result;
int nr = 0;
name += FILE_SYSTEM_PREFIX_LEN (name);
/* Try global exclusion list first */
if (excluded_file_name (excluded, name))
return true;
if (!st)
return false;
for (result = false; st && !result; st = st->parent, nr = EXCL_NON_RECURSIVE)
{
for (ep = st->exclude_list; ep; ep = ep->next)
{
if (ep->flags & nr)
continue;
if ((result = excluded_file_name (ep->excluded, name)))
break;
if (!rname)
rname = name + dotslashlen (name);
if ((result = excluded_file_name (ep->excluded, rname)))
break;
if (!bname)
bname = base_name (name);
if ((result = excluded_file_name (ep->excluded, bname)))
break;
}
}
free (bname);
return result;
}
static void
cvs_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
struct wordsplit ws;
options |= EXCLUDE_ALLOC;
if (wordsplit (pattern, &ws,
WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS)
!= WRDSE_OK)
return;
for (idx_t i = 0; i < ws.ws_wordc; i++)
add_exclude (ex, ws.ws_wordv[i], options);
wordsplit_free (&ws);
}
static void
git_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
while (c_isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
if (*pattern == '\\' && pattern[1] == '#')
++pattern;
add_exclude (ex, pattern, options);
}
static void
bzr_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
while (c_isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
if (*pattern == '!')
{
if (*++pattern == '!')
++pattern;
else
options |= EXCLUDE_INCLUDE;
}
/* FIXME: According to the docs, globbing patterns are rsync-style,
and regexps are perl-style. */
if (strncmp (pattern, "RE:", 3) == 0)
{
pattern += 3;
options &= ~EXCLUDE_WILDCARDS;
options |= EXCLUDE_REGEX;
}
add_exclude (ex, pattern, options);
}
static void *
hg_initfn (void *data)
{
static int hg_options;
int *hgopt = data ? data : &hg_options;
*hgopt = EXCLUDE_REGEX;
return hgopt;
}
static void
hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
{
int *hgopt = data;
while (c_isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
if (strncmp (pattern, "syntax:", 7) == 0)
{
for (pattern += 7; c_isspace (*pattern); ++pattern)
;
if (streq (pattern, "regexp"))
/* FIXME: Regexps must be perl-style */
*hgopt = EXCLUDE_REGEX;
else if (streq (pattern, "glob"))
*hgopt = EXCLUDE_WILDCARDS;
/* Ignore unknown syntax */
return;
}
idx_t len = strlen (pattern);
if (pattern[len-1] == '/')
{
char *p;
--len;
p = xmalloc (len+1);
memcpy (p, pattern, len);
p[len] = 0;
pattern = p;
exclude_add_pattern_buffer (ex, p);
options |= FNM_LEADING_DIR|EXCLUDE_ALLOC;
}
add_exclude (ex, pattern,
((*hgopt == EXCLUDE_REGEX)
? (options & ~EXCLUDE_WILDCARDS)
: (options & ~EXCLUDE_REGEX)) | *hgopt);
}
static struct vcs_ignore_file vcs_ignore_files[] = {
{ ".cvsignore", EXCL_NON_RECURSIVE, cvs_addfn, NULL, NULL },
{ ".gitignore", 0, git_addfn, NULL, NULL },
{ ".bzrignore", 0, bzr_addfn, NULL, NULL },
{ ".hgignore", 0, hg_addfn, hg_initfn, NULL },
{ NULL, 0, git_addfn, NULL, NULL }
};
static struct vcs_ignore_file *
get_vcs_ignore_file (const char *name)
{
struct vcs_ignore_file *p;
for (p = vcs_ignore_files; p->filename; p++)
if (streq (p->filename, name))
break;
return p;
}
void
exclude_vcs_ignores (void)
{
struct vcs_ignore_file *p;
for (p = vcs_ignore_files; p->filename; p++)
excfile_add (p->filename, p->flags);
}