1 /* Per-directory exclusion files for tar.
3 Copyright 2014-2020 Free Software Foundation, Inc.
5 This file is part of GNU tar.
7 GNU tar is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 GNU tar is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <wordsplit.h>
26 typedef void (*add_fn
) (struct exclude
*, char const *, int, void *);
28 struct vcs_ignore_file
33 void *(*initfn
) (void *);
37 static struct vcs_ignore_file
*get_vcs_ignore_file (const char *name
);
46 static struct excfile
*excfile_head
, *excfile_tail
;
49 excfile_add (const char *name
, int flags
)
51 struct excfile
*p
= xmalloc (sizeof (*p
) + strlen (name
));
54 strcpy (p
->name
, name
);
56 excfile_tail
->next
= p
;
64 struct exclist
*next
, *prev
;
66 struct exclude
*excluded
;
70 info_attach_exclist (struct tar_stat_info
*dir
)
73 struct exclist
*head
= NULL
, *tail
= NULL
, *ent
;
74 struct vcs_ignore_file
*vcsfile
;
76 if (dir
->exclude_list
)
78 for (file
= excfile_head
; file
; file
= file
->next
)
80 if (faccessat (dir
? dir
->fd
: chdir_fd
, file
->name
, F_OK
, 0) == 0)
83 struct exclude
*ex
= NULL
;
84 int fd
= subfile_open (dir
, file
->name
, O_RDONLY
);
87 open_error (file
->name
);
90 fp
= fdopen (fd
, "r");
93 ERROR ((0, errno
, _("%s: fdopen failed"), file
->name
));
101 vcsfile
= get_vcs_ignore_file (file
->name
);
104 vcsfile
->data
= vcsfile
->initfn (vcsfile
->data
);
106 if (add_exclude_fp (vcsfile
->addfn
, ex
, fp
,
107 FNM_FILE_NAME
|EXCLUDE_WILDCARDS
|EXCLUDE_ANCHORED
,
112 FATAL_ERROR ((0, e
, "%s", quotearg_colon (file
->name
)));
116 ent
= xmalloc (sizeof (*ent
));
118 ent
->flags
= file
->flags
== EXCL_DEFAULT
119 ? file
->flags
: vcsfile
->flags
;
130 dir
->exclude_list
= head
;
134 info_free_exclist (struct tar_stat_info
*dir
)
136 struct exclist
*ep
= dir
->exclude_list
;
140 struct exclist
*next
= ep
->next
;
141 free_exclude (ep
->excluded
);
146 dir
->exclude_list
= NULL
;
150 /* Return nonzero if file NAME is excluded. */
152 excluded_name (char const *name
, struct tar_stat_info
*st
)
155 const char *rname
= NULL
;
160 name
+= FILE_SYSTEM_PREFIX_LEN (name
);
162 /* Try global exclusion list first */
163 if (excluded_file_name (excluded
, name
))
169 for (result
= false; st
&& !result
; st
= st
->parent
, nr
= EXCL_NON_RECURSIVE
)
171 for (ep
= st
->exclude_list
; ep
; ep
= ep
->next
)
175 if ((result
= excluded_file_name (ep
->excluded
, name
)))
181 /* Skip leading ./ */
182 while (*rname
== '.' && ISSLASH (rname
[1]))
185 if ((result
= excluded_file_name (ep
->excluded
, rname
)))
189 bname
= base_name (name
);
190 if ((result
= excluded_file_name (ep
->excluded
, bname
)))
201 cvs_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
206 if (wordsplit (pattern
, &ws
,
207 WRDSF_NOVAR
| WRDSF_NOCMD
| WRDSF_SQUEEZE_DELIMS
))
209 for (i
= 0; i
< ws
.ws_wordc
; i
++)
210 add_exclude (ex
, ws
.ws_wordv
[i
], options
);
211 wordsplit_free (&ws
);
215 git_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
217 while (isspace (*pattern
))
219 if (*pattern
== 0 || *pattern
== '#')
221 if (*pattern
== '\\' && pattern
[1] == '#')
223 add_exclude (ex
, pattern
, options
);
227 bzr_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
229 while (isspace (*pattern
))
231 if (*pattern
== 0 || *pattern
== '#')
235 if (*++pattern
== '!')
238 options
|= EXCLUDE_INCLUDE
;
240 /* FIXME: According to the docs, globbing patterns are rsync-style,
241 and regexps are perl-style. */
242 if (strncmp (pattern
, "RE:", 3) == 0)
245 options
&= ~EXCLUDE_WILDCARDS
;
246 options
|= EXCLUDE_REGEX
;
248 add_exclude (ex
, pattern
, options
);
252 hg_initfn (void *data
)
254 static int hg_options
;
255 int *hgopt
= data
? data
: &hg_options
;
256 *hgopt
= EXCLUDE_REGEX
;
261 hg_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
266 while (isspace (*pattern
))
268 if (*pattern
== 0 || *pattern
== '#')
270 if (strncmp (pattern
, "syntax:", 7) == 0)
272 for (pattern
+= 7; isspace (*pattern
); ++pattern
)
274 if (strcmp (pattern
, "regexp") == 0)
275 /* FIXME: Regexps must be perl-style */
276 *hgopt
= EXCLUDE_REGEX
;
277 else if (strcmp (pattern
, "glob") == 0)
278 *hgopt
= EXCLUDE_WILDCARDS
;
279 /* Ignore unknown syntax */
283 len
= strlen(pattern
);
284 if (pattern
[len
-1] == '/')
290 memcpy (p
, pattern
, len
);
293 exclude_add_pattern_buffer (ex
, p
);
294 options
|= FNM_LEADING_DIR
|EXCLUDE_ALLOC
;
297 add_exclude (ex
, pattern
,
298 ((*hgopt
== EXCLUDE_REGEX
)
299 ? (options
& ~EXCLUDE_WILDCARDS
)
300 : (options
& ~EXCLUDE_REGEX
)) | *hgopt
);
303 static struct vcs_ignore_file vcs_ignore_files
[] = {
304 { ".cvsignore", EXCL_NON_RECURSIVE
, cvs_addfn
, NULL
, NULL
},
305 { ".gitignore", 0, git_addfn
, NULL
, NULL
},
306 { ".bzrignore", 0, bzr_addfn
, NULL
, NULL
},
307 { ".hgignore", 0, hg_addfn
, hg_initfn
, NULL
},
308 { NULL
, 0, git_addfn
, NULL
, NULL
}
311 static struct vcs_ignore_file
*
312 get_vcs_ignore_file (const char *name
)
314 struct vcs_ignore_file
*p
;
316 for (p
= vcs_ignore_files
; p
->filename
; p
++)
317 if (strcmp (p
->filename
, name
) == 0)
324 exclude_vcs_ignores (void)
326 struct vcs_ignore_file
*p
;
328 for (p
= vcs_ignore_files
; p
->filename
; p
++)
329 excfile_add (p
->filename
, EXCL_DEFAULT
);