]> Dogcows Code - chaz/tar/commitdiff
Support exclusion patterns from various VCS ignore lists.
authorSergey Poznyakoff <gray@gnu.org.ua>
Fri, 21 Feb 2014 15:57:26 +0000 (17:57 +0200)
committerSergey Poznyakoff <gray@gnu.org.ua>
Sat, 22 Feb 2014 06:13:30 +0000 (08:13 +0200)
* src/Makefile.am (tar_SOURCES): Add exclist.c
* src/common.h (EXCL_DEFAULT, EXCL_RECURSIVE)
(EXCL_NON_RECURSIVE): New flags.
(excfile_add, info_attach_exclist)
(info_cleanup_exclist,info_free_exclist)
(exclude_vcs_ignores): New prototypes.
* src/create.c (dump_dir0): Call info_attach_exclist.
* src/exclist.c: New file.
* src/incremen.c (scan_directory): Call info_attach_exclist.
* src/names.c (excluded_name): Moved to exclist.c. Change signature.
All uses updated.
* src/tar.c: New options: --exclude-ignore, --exclude-ignore-recursive
and --exclude-vcs-ignores.
(tar_stat_destroy): Free exclist.
* src/tar.h (tar_stat_info): New member exclude_list.

* NEWS: Document new exclusion options.
* doc/tar.texi: Likewise.
* doc/tar.1: Likewise.

13 files changed:
NEWS
doc/tar.1
doc/tar.texi
src/Makefile.am
src/common.h
src/create.c
src/exclist.c [new file with mode: 0644]
src/incremen.c
src/list.c
src/names.c
src/tar.c
src/tar.h
src/update.c

diff --git a/NEWS b/NEWS
index ec9c8a75fb5c994a020e8ff8728d44031269a536..fa241425be910da0966db3aa1426cf969cf1b24f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU tar NEWS - User visible changes. 2014-02-14
+GNU tar NEWS - User visible changes. 2014-02-21
 Please send GNU tar bug reports to <bug-tar@gnu.org>
 
 \f
 Please send GNU tar bug reports to <bug-tar@gnu.org>
 
 \f
@@ -48,6 +48,20 @@ is uniform and reproducible.  Using --sort=inode reduces the number
 of disk seeks made when creating the archive and thus can considerably
 speed up archivation.
 
 of disk seeks made when creating the archive and thus can considerably
 speed up archivation.
 
+* New exclusion options
+
+  --exclude-ignore=FILE   Before dumping a directory check if it
+                          contains FILE, and if so read exclude
+                          patterns for this directory from FILE.
+  --exclude-ignore-recursive=FILE
+                          Same as above, but the exclusion patterns
+                          read from FILE remain in effect for any
+                         subdirectory, recursively.
+  --exclude-vcs-ignores   Read exclude tags from VCS ignore files,
+                          where such files exist.  Supported VCS's
+                          are: CVS, Git, Bazaar, Mercurial.
+                         
+
 * Manpages
 
 This release includes official tar(1) and rmt(8) manpages.
 * Manpages
 
 This release includes official tar(1) and rmt(8) manpages.
index b33f55b9060e27887fdf6e94c25c60023ddcd347..cd133d9ed858d8e904651d0dc4b3b72749df0d00 100644 (file)
--- a/doc/tar.1
+++ b/doc/tar.1
@@ -13,7 +13,7 @@
 .\"
 .\" You should have received a copy of the GNU General Public License
 .\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
 .\"
 .\" You should have received a copy of the GNU General Public License
 .\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
-.TH TAR 1 "February 14, 2014" "TAR" "GNU TAR Manual"
+.TH TAR 1 "February 22, 2014" "TAR" "GNU TAR Manual"
 .SH NAME
 tar \- an archiving utility
 .SH SYNOPSIS
 .SH NAME
 tar \- an archiving utility
 .SH SYNOPSIS
@@ -788,6 +788,15 @@ Exclude directories containing file \fBCACHEDIR.TAG\fR and the file itself.
 \fB\-\-exclude\-caches\-under\fR
 Exclude everything under directories containing \fBCACHEDIR.TAG\fR
 .TP
 \fB\-\-exclude\-caches\-under\fR
 Exclude everything under directories containing \fBCACHEDIR.TAG\fR
 .TP
+\fB\-\-exclude\-ignore=\fIFILE\fR
+Before dumping a directory, see if it contains \fIFILE\fR.
+If so, read exclusion patterns from this file.  The patterns affect
+only the directory itself.
+.TP
+\fB\-\-exclude\-ignore\-recursive=\fIFILE\fR
+Same as \fB\-\-exclude\-ignore\fR, except that patterns from
+\fIFILE\fR affect both the directory and all its subdirectories.
+.TP
 \fB\-\-exclude\-tag\fR=\fIFILE\fR
 Exclude contents of directories containing \fIFILE\fR, except for
 \fIFILE\fR itself.
 \fB\-\-exclude\-tag\fR=\fIFILE\fR
 Exclude contents of directories containing \fIFILE\fR, except for
 \fIFILE\fR itself.
@@ -801,6 +810,14 @@ Exclude everything under directories containing \fIFILE\fR.
 \fB\-\-exclude\-vcs\fR
 Exclude version control system directories.
 .TP
 \fB\-\-exclude\-vcs\fR
 Exclude version control system directories.
 .TP
+\fB\-\-exclude\-vcs\-ignores\fR
+Exclude files that match patterns read from VCS-specific ignore
+files.  Supported files are:
+.BR .cvsignore ,
+.BR .gitignore ,
+.BR .bzrignore ", and"
+.BR .hgignore .
+.TP
 \fB\-h\fR, \fB\-\-dereference\fR
 Follow symlinks; archive and dump the files they point to.
 .TP
 \fB\-h\fR, \fB\-\-dereference\fR
 Follow symlinks; archive and dump the files they point to.
 .TP
index 9bb5a833c8c4c0d09d6f8012c13c44ab52dfc1d4..e3df0c98b238cfd2326452888048598a91de80ad 100644 (file)
@@ -2608,6 +2608,19 @@ tag file, but still dump the directory node itself.
 Exclude from dump any directory containing a valid cache directory
 tag file.  @xref{exclude}.
 
 Exclude from dump any directory containing a valid cache directory
 tag file.  @xref{exclude}.
 
+@opsummary{exclude-ignore}
+@item --exclude-ignore=@var{file}
+Before dumping a directory, @command{tar} checks if it contains
+@var{file}.  If so, exclusion patterns are read from this file.
+The patterns affect only the directory itself.  @xref{exclude}.
+
+@opsummary{exclude-ignore-recursive}
+@item --exclude-ignore-recursive=@var{file}
+Before dumping a directory, @command{tar} checks if it contains
+@var{file}.  If so, exclusion patterns are read from this file.
+The patterns affect the directory and all itssubdirectories.
+@xref{exclude}.
+
 @opsummary{exclude-tag}
 @item --exclude-tag=@var{file}
 
 @opsummary{exclude-tag}
 @item --exclude-tag=@var{file}
 
@@ -2633,7 +2646,16 @@ Exclude from dump any directory containing file named @var{file}.
 Exclude from dump directories and files, that are internal for some
 widely used version control systems.
 
 Exclude from dump directories and files, that are internal for some
 widely used version control systems.
 
-@xref{exclude,,exclude-vcs}.
+@xref{exclude-vcs}.
+
+@opsummary{exclude-vcs-ignores}
+@item --exclude-vcs-ignores
+Exclude files that match patterns read from VCS-specific ignore
+files.  Supported files are: @file{.cvsignore}, @file{.gitignore},
+@file{.bzrignore}, and @file{.hgignore}.  The semantics of each file
+is the same as for the corresponding VCS, e.g. patterns read from
+@file{.gitignore} affect the directory and all its subdirectories.
+@xref{exclude-vcs-ignores}.
 
 @opsummary{file}
 @item --file=@var{archive}
 
 @opsummary{file}
 @item --file=@var{archive}
@@ -7381,6 +7403,77 @@ which is difficult to catch using text editors.
 
 However, empty lines are OK.
 
 
 However, empty lines are OK.
 
+@cindex VCS, excluding patterns from ignore files
+@cindex VCS, ignore files
+@cindex CVS, ignore files
+@cindex Git, ignore files
+@cindex Bazaar, ignore files
+@cindex Mercurial, ignore files
+When archiving directories that are under some version control system (VCS), 
+it is often convenient to read exclusion patterns from this VCS'
+ignore files (e.g. @file{.cvsignore}, @file{.gitignore}, etc.)  The
+following options provide such possibilty:
+
+@table @option
+@anchor{exclude-vcs-ignores}
+@opindex exclude-vcs-ignores
+@item --exclude-vcs-ignores
+Before archiving a directory, see if it contains any of the following
+files: @file{cvsignore}, @file{.gitignore}, @file{.bzrignore}, or
+@file{.hgignore}.  If so, read ignore patterns from these files.
+
+The patterns are treated much as the corresponding VCS would treat
+them, i.e.:
+
+@table @file
+@findex .cvsignore
+@item .cvsignore
+Contains shell-style globbing patterns that apply only to the
+directory where this file resides.  No comments are allowed in the
+file.  Empty lines are ignored.
+
+@findex .gitignore
+@item .gitignore
+Contains shell-style globbing patterns.  Applies to the directory
+where @file{.gitfile} is located and all its subdirectories.
+
+Any line beginning with a @samp{#} is a comment.  Backslash escapes
+the comment character.
+
+@findex .bzrignore
+@item .bzrignore
+Contains shell globbing-patterns and regular expressions (if prefixed
+with @samp{RE:}@footnote{According to the Bazaar docs,
+globbing-patterns are Korn-shell style and regular expressions are
+perl-style.  As of @GNUTAR{} version @value{VERSION}, these are
+treated as shell-style globs and posix extended regexps.  This will be
+fixed in future releases.}.  Patterns affect the directory and all its
+subdirectories.
+
+Any line beginning with a @samp{#} is a comment.
+
+@findex .hgignore
+@item .hgignore
+Contains posix regular expressions@footnote{Support for perl-style
+regexps will appear in future releases.}.  The line @samp{syntax:
+glob} switches to shell globbing patterns.  The line @samp{syntax:
+regexp} switches back.  Comments begin with a @samp{#}.  Patterns
+affect the directory and all its subdirectories.
+@end table
+
+@opindex exclude-ignore
+@item --exclude-ignore=@var{file}
+Before dumping a directory, @command{tar} checks if it contains
+@var{file}.  If so, exclusion patterns are read from this file.
+The patterns affect only the directory itself.
+
+@opindex exclude-ignore-recursive
+@item --exclude-ignore-recursive=@var{file}
+Same as @option{--exclude-ignore}, except that the patterns read
+affect both the directory where @var{file} resides and all its
+subdirectories.
+@end table
+
 @table @option
 @cindex version control system, excluding files
 @cindex VCS, excluding files
 @table @option
 @cindex version control system, excluding files
 @cindex VCS, excluding files
@@ -7393,6 +7486,7 @@ However, empty lines are OK.
 @cindex Arch, excluding files
 @cindex Mercurial, excluding files
 @cindex Darcs, excluding files
 @cindex Arch, excluding files
 @cindex Mercurial, excluding files
 @cindex Darcs, excluding files
+@anchor{exclude-vcs}
 @opindex exclude-vcs
 @item --exclude-vcs
 Exclude files and directories used by following version control
 @opindex exclude-vcs
 @item --exclude-vcs
 Exclude files and directories used by following version control
index a9d25efa4ba997bda1aa7473b1af3ecee0007d75..82b2d4628510754d0283141a3c2ffc186ae2f000 100644 (file)
@@ -28,6 +28,7 @@ tar_SOURCES = \
  create.c\
  delete.c\
  exit.c\
  create.c\
  delete.c\
  exit.c\
+ exclist.c\
  extract.c\
  xheader.c\
  incremen.c\
  extract.c\
  xheader.c\
  incremen.c\
index 4d2c3994181051b6bfa95b537ee3219dfb80e2fe..edf787c8316f98c3a4e9c696b8b26d26bff8baf9 100644 (file)
@@ -743,8 +743,6 @@ char *new_name (const char *dir_name, const char *name);
 size_t stripped_prefix_len (char const *file_name, size_t num);
 bool all_names_found (struct tar_stat_info *st);
 
 size_t stripped_prefix_len (char const *file_name, size_t num);
 bool all_names_found (struct tar_stat_info *st);
 
-bool excluded_name (char const *name);
-
 void add_avoided_name (char const *name);
 bool is_avoided_name (char const *name);
 
 void add_avoided_name (char const *name);
 bool is_avoided_name (char const *name);
 
@@ -921,4 +919,16 @@ void finish_deferred_unlinks (void);
 /* Module exit.c */
 extern void (*fatal_exit_hook) (void);
 
 /* Module exit.c */
 extern void (*fatal_exit_hook) (void);
 
+/* Module exclist.c */
+#define EXCL_DEFAULT       0x00
+#define EXCL_RECURSIVE     0x01
+#define EXCL_NON_RECURSIVE 0x02
+
+void excfile_add (const char *name, int flags);
+void info_attach_exclist (struct tar_stat_info *dir);
+void info_cleanup_exclist (struct tar_stat_info *dir);
+void info_free_exclist (struct tar_stat_info *dir);
+bool excluded_name (char const *name, struct tar_stat_info *st);
+void exclude_vcs_ignores (void);
+
 _GL_INLINE_HEADER_END
 _GL_INLINE_HEADER_END
index 3455bddc60c74016bcad238e0e416ee1f2ad5895..e2f4ede6b8d12c1200c931f9f425c9537a72906b 100644 (file)
@@ -1113,6 +1113,8 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
   if (!blk)
     return;
 
   if (!blk)
     return;
 
+  info_attach_exclist (st);
+  
   if (incremental_option && archive_format != POSIX_FORMAT)
     blk->header.typeflag = GNUTYPE_DUMPDIR;
   else /* if (standard_option) */
   if (incremental_option && archive_format != POSIX_FORMAT)
     blk->header.typeflag = GNUTYPE_DUMPDIR;
   else /* if (standard_option) */
@@ -1196,7 +1198,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
            char const *entry;
            size_t entry_len;
            size_t name_len;
            char const *entry;
            size_t entry_len;
            size_t name_len;
-
+           
            name_buf = xstrdup (st->orig_file_name);
            name_size = name_len = strlen (name_buf);
 
            name_buf = xstrdup (st->orig_file_name);
            name_size = name_len = strlen (name_buf);
 
@@ -1210,7 +1212,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
                    name_buf = xrealloc (name_buf, name_size + 1);
                  }
                strcpy (name_buf + name_len, entry);
                    name_buf = xrealloc (name_buf, name_size + 1);
                  }
                strcpy (name_buf + name_len, entry);
-               if (!excluded_name (name_buf))
+               if (!excluded_name (name_buf, st))
                  dump_file (st, entry, name_buf);
              }
 
                  dump_file (st, entry, name_buf);
              }
 
@@ -1339,12 +1341,12 @@ create_archive (void)
       collect_and_sort_names ();
 
       while ((p = name_from_list ()) != NULL)
       collect_and_sort_names ();
 
       while ((p = name_from_list ()) != NULL)
-       if (!excluded_name (p->name))
+       if (!excluded_name (p->name, NULL))
          dump_file (0, p->name, p->name);
 
       blank_name_list ();
       while ((p = name_from_list ()) != NULL)
          dump_file (0, p->name, p->name);
 
       blank_name_list ();
       while ((p = name_from_list ()) != NULL)
-       if (!excluded_name (p->name))
+       if (!excluded_name (p->name, NULL))
          {
            struct tar_stat_info st;
            size_t plen = strlen (p->name);
          {
            struct tar_stat_info st;
            size_t plen = strlen (p->name);
@@ -1358,7 +1360,7 @@ create_archive (void)
            if (! ISSLASH (buffer[plen - 1]))
              buffer[plen++] = DIRECTORY_SEPARATOR;
            tar_stat_init (&st);
            if (! ISSLASH (buffer[plen - 1]))
              buffer[plen++] = DIRECTORY_SEPARATOR;
            tar_stat_init (&st);
-           q = directory_contents (gnu_list_name->directory);
+           q = directory_contents (p->directory);
            if (q)
              while (*q)
                {
            if (q)
              while (*q)
                {
@@ -1401,7 +1403,7 @@ create_archive (void)
     {
       const char *name;
       while ((name = name_next (1)) != NULL)
     {
       const char *name;
       while ((name = name_next (1)) != NULL)
-       if (!excluded_name (name))
+       if (!excluded_name (name, NULL))
          dump_file (0, name, name);
     }
 
          dump_file (0, name, name);
     }
 
diff --git a/src/exclist.c b/src/exclist.c
new file mode 100644 (file)
index 0000000..7cccc74
--- /dev/null
@@ -0,0 +1,361 @@
+/* Per-directory exclusion files for tar.
+
+   Copyright 2014 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 <quotearg.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);
+\f
+struct excfile
+{
+  struct excfile *next;
+  int flags;
+  char name[1];
+};
+
+struct excfile *excfile_head, *excfile_tail;
+
+void
+excfile_add (const char *name, int flags)
+{
+  struct excfile *p = xmalloc (sizeof (*p) + strlen (name));
+  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 excfile *file;
+  struct exclist *head = NULL, *tail = NULL, *ent;
+  struct vcs_ignore_file *vcsfile;
+    
+  if (dir->exclude_list)
+    return;
+  for (file = excfile_head; file; file = file->next)
+    {
+      if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0)
+       {
+         FILE *fp;
+         struct exclude *ex = NULL;
+         int fd = subfile_open (dir, file->name, O_RDONLY);
+         if (fd == -1)
+           {
+             open_error (file->name);
+             continue;
+           }
+         fp = fdopen (fd, "r");
+         if (!fp)
+           {
+             ERROR ((0, errno, _("%s: fdopen failed"), file->name));
+             close (fd);
+             continue;
+           }
+
+         if (!ex)
+           ex = new_exclude ();
+
+         vcsfile = get_vcs_ignore_file (file->name);
+
+         if (vcsfile->initfn)
+           vcsfile->data = vcsfile->initfn (vcsfile->data);
+         
+         if (add_exclude_fp (vcsfile->addfn, ex, fp,
+                             EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n',
+                             vcsfile->data))
+           {
+             int e = errno;
+             FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)));
+           }
+         fclose (fp);
+         
+         ent = xmalloc (sizeof (*ent));
+         ent->excluded = ex;
+         ent->flags = file->flags == EXCL_DEFAULT
+                      ? file->flags : vcsfile->flags;
+         ent->prev = tail;
+         ent->next = NULL;
+
+         if (tail)
+           tail->next = ent;
+         else
+           head = ent;
+         tail = ent;
+       }
+    }
+  dir->exclude_list = head;
+}
+
+void
+info_cleanup_exclist (struct tar_stat_info *dir)
+{
+  struct exclist *ep = dir->exclude_list;
+
+  while (ep)
+    {
+      struct exclist *next = ep->next;
+      
+      if (ep->flags & EXCL_NON_RECURSIVE)
+       {
+         
+         /* Remove the entry */
+         if (ep->prev)
+           ep->prev->next = ep->next;
+         else
+           dir->exclude_list = ep->next;
+
+         if (ep->next)
+           ep->next->prev = ep->prev;
+
+         free_exclude (ep->excluded);
+         free (ep);
+       }
+      ep = next;
+    }
+}
+
+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;
+             /* Skip leading ./ */
+             while (*rname == '.' && ISSLASH (rname[1]))
+               rname += 2;
+           }
+         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;
+}
+\f
+static void
+cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data)
+{
+  struct wordsplit ws;
+  size_t i;
+    
+  if (wordsplit (pattern, &ws, 
+                WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS))
+    return;
+  for (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, void *data)
+{
+  while (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, void *data)
+{
+  while (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)
+{
+  int *hgopt;
+  static int hg_options;
+  
+  if (!data)
+    hgopt = &hg_options;
+
+  *hgopt = EXCLUDE_REGEX;
+  return hgopt;
+}
+  
+static void
+hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
+{
+  int *hgopt = data;
+  size_t len;
+  
+  while (isspace (*pattern))
+    ++pattern;
+  if (*pattern == 0 || *pattern == '#')
+    return;
+  if (strncmp (pattern, "syntax:", 7) == 0)
+    {
+      for (pattern += 7; isspace (*pattern); ++pattern)
+       ;
+      if (strcmp (pattern, "regexp") == 0)
+       /* FIXME: Regexps must be perl-style */
+       *hgopt = EXCLUDE_REGEX;
+      else if (strcmp (pattern, "glob") == 0)
+       *hgopt = EXCLUDE_WILDCARDS;
+      /* Ignore unknown syntax */
+      return;
+    }
+
+  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);
+}
+\f
+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 (strcmp (p->filename, name) == 0)
+      break;
+
+  return p;
+}
+\f
+void
+exclude_vcs_ignores (void)
+{
+  struct vcs_ignore_file *p;
+
+  for (p = vcs_ignore_files; p->filename; p++)
+    excfile_add (p->filename, EXCL_DEFAULT);
+}
index 884d2fa2865831ad3a8ffec73aa617872324fa33..b1b70ba41764b04049853d12834cf520d12f0254 100644 (file)
@@ -734,6 +734,8 @@ scan_directory (struct tar_stat_info *st)
   if (! dirp)
     savedir_error (dir);
 
   if (! dirp)
     savedir_error (dir);
 
+  info_attach_exclist (st);
+  
   tmp = xstrdup (dir);
   zap_slashes (tmp);
 
   tmp = xstrdup (dir);
   zap_slashes (tmp);
 
@@ -762,7 +764,7 @@ scan_directory (struct tar_stat_info *st)
 
              if (*entry == 'I') /* Ignored entry */
                *entry = 'N';
 
              if (*entry == 'I') /* Ignored entry */
                *entry = 'N';
-             else if (excluded_name (full_name))
+             else if (excluded_name (full_name, st))
                *entry = 'N';
              else
                {
                *entry = 'N';
              else
                {
index d46be651cb3752b0f1fe2d0fc29d79a8a256feb3..b4277e028e59f9672fbb5b247574fcad5c8e41c9 100644 (file)
@@ -203,7 +203,8 @@ read_and (void (*do_something) (void))
                      mtime.tv_nsec = 0,
                      current_stat_info.mtime = mtime,
                      OLDER_TAR_STAT_TIME (current_stat_info, m)))
                      mtime.tv_nsec = 0,
                      current_stat_info.mtime = mtime,
                      OLDER_TAR_STAT_TIME (current_stat_info, m)))
-             || excluded_name (current_stat_info.file_name))
+             || excluded_name (current_stat_info.file_name,
+                               current_stat_info.parent))
            {
              switch (current_header->header.typeflag)
                {
            {
              switch (current_header->header.typeflag)
                {
index 60a5f700c3acaa3412e14b4da7cf6c3b900c96a7..9fc0ad5888ccc0f75e5c4d8da6925d807d4b1f66 100644 (file)
@@ -1373,12 +1373,6 @@ new_name (const char *file_name, const char *name)
   return buffer;
 }
 
   return buffer;
 }
 
-/* Return nonzero if file NAME is excluded.  */
-bool
-excluded_name (char const *name)
-{
-  return excluded_file_name (excluded, name + FILE_SYSTEM_PREFIX_LEN (name));
-}
 \f
 
 /* Return the size of the prefix of FILE_NAME that is removed after
 \f
 
 /* Return the size of the prefix of FILE_NAME that is removed after
index 08f334f554f50f0376576baaf51aa2199549f9de..ac32f97c02ea78bd1b396887d912109a84754914 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -284,10 +284,13 @@ enum
   EXCLUDE_CACHES_UNDER_OPTION,
   EXCLUDE_CACHES_ALL_OPTION,
   EXCLUDE_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_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,
   FORCE_LOCAL_OPTION,
   FULL_TIME_OPTION,
   GROUP_OPTION,
@@ -732,12 +735,20 @@ static struct argp_option options[] = {
   {"exclude-tag", EXCLUDE_TAG_OPTION, N_("FILE"), 0,
    N_("exclude contents of directories containing FILE, except"
       " for FILE itself"), 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-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,
   {"exclude-backups", EXCLUDE_BACKUPS_OPTION, NULL, 0,
    N_("exclude backup and lock files"), GRID+1 },
   {"no-recursion", NO_RECURSION_OPTION, 0, 0,
@@ -1776,6 +1787,14 @@ parse_opt (int key, char *arg, struct argp_state *state)
                         cachedir_file_p);
       break;
 
                         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_OPTION:
       add_exclusion_tag (arg, exclusion_tag_contents, NULL);
       break;
@@ -1792,6 +1811,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       add_exclude_array (vcs_file_table, 0);
       break;
 
       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;
     case FORCE_LOCAL_OPTION:
       force_local_option = true;
       break;
@@ -2304,6 +2327,7 @@ decode_options (int argc, char **argv)
   blocking_factor = DEFAULT_BLOCKING;
   record_size = DEFAULT_BLOCKING * BLOCKSIZE;
   excluded = new_exclude ();
   blocking_factor = DEFAULT_BLOCKING;
   record_size = DEFAULT_BLOCKING * BLOCKSIZE;
   excluded = new_exclude ();
+  
   newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
   newer_mtime_option.tv_nsec = -1;
   recursion_option = FNM_LEADING_DIR;
   newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
   newer_mtime_option.tv_nsec = -1;
   recursion_option = FNM_LEADING_DIR;
@@ -2849,6 +2873,7 @@ tar_stat_destroy (struct tar_stat_info *st)
   free (st->sparse_map);
   free (st->dumpdir);
   xheader_destroy (&st->xhdr);
   free (st->sparse_map);
   free (st->dumpdir);
   xheader_destroy (&st->xhdr);
+  info_free_exclist (st);
   memset (st, 0, sizeof (*st));
 }
 
   memset (st, 0, sizeof (*st));
 }
 
index cb7d70cf62534c268b5e1b04f85d985ae6e78f31..3d6939948ba5ae10b10816a53d72b652d1ef6fad 100644 (file)
--- a/src/tar.h
+++ b/src/tar.h
@@ -358,6 +358,9 @@ struct tar_stat_info
      It is negative if it could not be reopened after it was closed.
      Negate it to find out what errno was when the reopen failed.  */
   int fd;
      It is negative if it could not be reopened after it was closed.
      Negate it to find out what errno was when the reopen failed.  */
   int fd;
+
+  /* Exclusion list */
+  struct exclist *exclude_list;
 };
 
 union block
 };
 
 union block
index e9fad3f2ae48ab6a196c9b6aa85d4a76da9192ad..c8fca0c5a7c79da702d1bac53dff862d187fc2ff 100644 (file)
@@ -216,7 +216,7 @@ update_archive (void)
     while ((p = name_from_list ()) != NULL)
       {
        char *file_name = p->name;
     while ((p = name_from_list ()) != NULL)
       {
        char *file_name = p->name;
-       if (excluded_name (file_name))
+       if (excluded_name (file_name, NULL))
          continue;
        if (interactive_option && !confirm ("add", file_name))
          continue;
          continue;
        if (interactive_option && !confirm ("add", file_name))
          continue;
This page took 0.041603 seconds and 4 git commands to generate.