1 /* Per-directory exclusion files for tar.
3 Copyright 2014 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 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 EXCLUDE_WILDCARDS
|EXCLUDE_ANCHORED
, '\n',
111 FATAL_ERROR ((0, e
, "%s", quotearg_colon (file
->name
)));
115 ent
= xmalloc (sizeof (*ent
));
117 ent
->flags
= file
->flags
== EXCL_DEFAULT
118 ? file
->flags
: vcsfile
->flags
;
129 dir
->exclude_list
= head
;
133 info_cleanup_exclist (struct tar_stat_info
*dir
)
135 struct exclist
*ep
= dir
->exclude_list
;
139 struct exclist
*next
= ep
->next
;
141 if (ep
->flags
& EXCL_NON_RECURSIVE
)
144 /* Remove the entry */
146 ep
->prev
->next
= ep
->next
;
148 dir
->exclude_list
= ep
->next
;
151 ep
->next
->prev
= ep
->prev
;
153 free_exclude (ep
->excluded
);
161 info_free_exclist (struct tar_stat_info
*dir
)
163 struct exclist
*ep
= dir
->exclude_list
;
167 struct exclist
*next
= ep
->next
;
168 free_exclude (ep
->excluded
);
173 dir
->exclude_list
= NULL
;
177 /* Return nonzero if file NAME is excluded. */
179 excluded_name (char const *name
, struct tar_stat_info
*st
)
182 const char *rname
= NULL
;
187 name
+= FILE_SYSTEM_PREFIX_LEN (name
);
189 /* Try global exclusion list first */
190 if (excluded_file_name (excluded
, name
))
196 for (result
= false; st
&& !result
; st
= st
->parent
, nr
= EXCL_NON_RECURSIVE
)
198 for (ep
= st
->exclude_list
; ep
; ep
= ep
->next
)
202 if ((result
= excluded_file_name (ep
->excluded
, name
)))
208 /* Skip leading ./ */
209 while (*rname
== '.' && ISSLASH (rname
[1]))
212 if ((result
= excluded_file_name (ep
->excluded
, rname
)))
216 bname
= base_name (name
);
217 if ((result
= excluded_file_name (ep
->excluded
, bname
)))
228 cvs_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
233 if (wordsplit (pattern
, &ws
,
234 WRDSF_NOVAR
| WRDSF_NOCMD
| WRDSF_SQUEEZE_DELIMS
))
236 for (i
= 0; i
< ws
.ws_wordc
; i
++)
237 add_exclude (ex
, ws
.ws_wordv
[i
], options
);
238 wordsplit_free (&ws
);
242 git_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
244 while (isspace (*pattern
))
246 if (*pattern
== 0 || *pattern
== '#')
248 if (*pattern
== '\\' && pattern
[1] == '#')
250 add_exclude (ex
, pattern
, options
);
254 bzr_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
256 while (isspace (*pattern
))
258 if (*pattern
== 0 || *pattern
== '#')
262 if (*++pattern
== '!')
265 options
|= EXCLUDE_INCLUDE
;
267 /* FIXME: According to the docs, globbing patterns are rsync-style,
268 and regexps are perl-style. */
269 if (strncmp (pattern
, "RE:", 3) == 0)
272 options
&= ~EXCLUDE_WILDCARDS
;
273 options
|= EXCLUDE_REGEX
;
275 add_exclude (ex
, pattern
, options
);
279 hg_initfn (void *data
)
282 static int hg_options
;
287 *hgopt
= EXCLUDE_REGEX
;
292 hg_addfn (struct exclude
*ex
, char const *pattern
, int options
, void *data
)
297 while (isspace (*pattern
))
299 if (*pattern
== 0 || *pattern
== '#')
301 if (strncmp (pattern
, "syntax:", 7) == 0)
303 for (pattern
+= 7; isspace (*pattern
); ++pattern
)
305 if (strcmp (pattern
, "regexp") == 0)
306 /* FIXME: Regexps must be perl-style */
307 *hgopt
= EXCLUDE_REGEX
;
308 else if (strcmp (pattern
, "glob") == 0)
309 *hgopt
= EXCLUDE_WILDCARDS
;
310 /* Ignore unknown syntax */
314 len
= strlen(pattern
);
315 if (pattern
[len
-1] == '/')
321 memcpy (p
, pattern
, len
);
324 exclude_add_pattern_buffer (ex
, p
);
325 options
|= FNM_LEADING_DIR
|EXCLUDE_ALLOC
;
328 add_exclude (ex
, pattern
,
329 ((*hgopt
== EXCLUDE_REGEX
)
330 ? (options
& ~EXCLUDE_WILDCARDS
)
331 : (options
& ~EXCLUDE_REGEX
)) | *hgopt
);
334 struct vcs_ignore_file vcs_ignore_files
[] = {
335 { ".cvsignore", EXCL_NON_RECURSIVE
, cvs_addfn
, NULL
, NULL
},
336 { ".gitignore", 0, git_addfn
, NULL
, NULL
},
337 { ".bzrignore", 0, bzr_addfn
, NULL
, NULL
},
338 { ".hgignore", 0, hg_addfn
, hg_initfn
, NULL
},
339 { NULL
, 0, git_addfn
, NULL
, NULL
}
342 static struct vcs_ignore_file
*
343 get_vcs_ignore_file (const char *name
)
345 struct vcs_ignore_file
*p
;
347 for (p
= vcs_ignore_files
; p
->filename
; p
++)
348 if (strcmp (p
->filename
, name
) == 0)
355 exclude_vcs_ignores (void)
357 struct vcs_ignore_file
*p
;
359 for (p
= vcs_ignore_files
; p
->filename
; p
++)
360 excfile_add (p
->filename
, EXCL_DEFAULT
);