]> Dogcows Code - chaz/tar/blob - src/exclist.c
Support exclusion patterns from various VCS ignore lists.
[chaz/tar] / src / exclist.c
1 /* Per-directory exclusion files for tar.
2
3 Copyright 2014 Free Software Foundation, Inc.
4
5 This file is part of GNU tar.
6
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.
11
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.
16
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/>.
19 */
20 #include <system.h>
21 #include <quotearg.h>
22 #include <fnmatch.h>
23 #include <wordsplit.h>
24 #include "common.h"
25
26 typedef void (*add_fn) (struct exclude *, char const *, int, void *);
27
28 struct vcs_ignore_file
29 {
30 char const *filename;
31 int flags;
32 add_fn addfn;
33 void *(*initfn) (void *);
34 void *data;
35 };
36
37 static struct vcs_ignore_file *get_vcs_ignore_file (const char *name);
38 \f
39 struct excfile
40 {
41 struct excfile *next;
42 int flags;
43 char name[1];
44 };
45
46 struct excfile *excfile_head, *excfile_tail;
47
48 void
49 excfile_add (const char *name, int flags)
50 {
51 struct excfile *p = xmalloc (sizeof (*p) + strlen (name));
52 p->next = NULL;
53 p->flags = flags;
54 strcpy (p->name, name);
55 if (excfile_tail)
56 excfile_tail->next = p;
57 else
58 excfile_head = p;
59 excfile_tail = p;
60 }
61
62 struct exclist
63 {
64 struct exclist *next, *prev;
65 int flags;
66 struct exclude *excluded;
67 };
68
69 void
70 info_attach_exclist (struct tar_stat_info *dir)
71 {
72 struct excfile *file;
73 struct exclist *head = NULL, *tail = NULL, *ent;
74 struct vcs_ignore_file *vcsfile;
75
76 if (dir->exclude_list)
77 return;
78 for (file = excfile_head; file; file = file->next)
79 {
80 if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0)
81 {
82 FILE *fp;
83 struct exclude *ex = NULL;
84 int fd = subfile_open (dir, file->name, O_RDONLY);
85 if (fd == -1)
86 {
87 open_error (file->name);
88 continue;
89 }
90 fp = fdopen (fd, "r");
91 if (!fp)
92 {
93 ERROR ((0, errno, _("%s: fdopen failed"), file->name));
94 close (fd);
95 continue;
96 }
97
98 if (!ex)
99 ex = new_exclude ();
100
101 vcsfile = get_vcs_ignore_file (file->name);
102
103 if (vcsfile->initfn)
104 vcsfile->data = vcsfile->initfn (vcsfile->data);
105
106 if (add_exclude_fp (vcsfile->addfn, ex, fp,
107 EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n',
108 vcsfile->data))
109 {
110 int e = errno;
111 FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)));
112 }
113 fclose (fp);
114
115 ent = xmalloc (sizeof (*ent));
116 ent->excluded = ex;
117 ent->flags = file->flags == EXCL_DEFAULT
118 ? file->flags : vcsfile->flags;
119 ent->prev = tail;
120 ent->next = NULL;
121
122 if (tail)
123 tail->next = ent;
124 else
125 head = ent;
126 tail = ent;
127 }
128 }
129 dir->exclude_list = head;
130 }
131
132 void
133 info_cleanup_exclist (struct tar_stat_info *dir)
134 {
135 struct exclist *ep = dir->exclude_list;
136
137 while (ep)
138 {
139 struct exclist *next = ep->next;
140
141 if (ep->flags & EXCL_NON_RECURSIVE)
142 {
143
144 /* Remove the entry */
145 if (ep->prev)
146 ep->prev->next = ep->next;
147 else
148 dir->exclude_list = ep->next;
149
150 if (ep->next)
151 ep->next->prev = ep->prev;
152
153 free_exclude (ep->excluded);
154 free (ep);
155 }
156 ep = next;
157 }
158 }
159
160 void
161 info_free_exclist (struct tar_stat_info *dir)
162 {
163 struct exclist *ep = dir->exclude_list;
164
165 while (ep)
166 {
167 struct exclist *next = ep->next;
168 free_exclude (ep->excluded);
169 free (ep);
170 ep = next;
171 }
172
173 dir->exclude_list = NULL;
174 }
175
176
177 /* Return nonzero if file NAME is excluded. */
178 bool
179 excluded_name (char const *name, struct tar_stat_info *st)
180 {
181 struct exclist *ep;
182 const char *rname = NULL;
183 char *bname = NULL;
184 bool result;
185 int nr = 0;
186
187 name += FILE_SYSTEM_PREFIX_LEN (name);
188
189 /* Try global exclusion list first */
190 if (excluded_file_name (excluded, name))
191 return true;
192
193 if (!st)
194 return false;
195
196 for (result = false; st && !result; st = st->parent, nr = EXCL_NON_RECURSIVE)
197 {
198 for (ep = st->exclude_list; ep; ep = ep->next)
199 {
200 if (ep->flags & nr)
201 continue;
202 if ((result = excluded_file_name (ep->excluded, name)))
203 break;
204
205 if (!rname)
206 {
207 rname = name;
208 /* Skip leading ./ */
209 while (*rname == '.' && ISSLASH (rname[1]))
210 rname += 2;
211 }
212 if ((result = excluded_file_name (ep->excluded, rname)))
213 break;
214
215 if (!bname)
216 bname = base_name (name);
217 if ((result = excluded_file_name (ep->excluded, bname)))
218 break;
219 }
220 }
221
222 free (bname);
223
224 return result;
225 }
226 \f
227 static void
228 cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data)
229 {
230 struct wordsplit ws;
231 size_t i;
232
233 if (wordsplit (pattern, &ws,
234 WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS))
235 return;
236 for (i = 0; i < ws.ws_wordc; i++)
237 add_exclude (ex, ws.ws_wordv[i], options);
238 wordsplit_free (&ws);
239 }
240
241 static void
242 git_addfn (struct exclude *ex, char const *pattern, int options, void *data)
243 {
244 while (isspace (*pattern))
245 ++pattern;
246 if (*pattern == 0 || *pattern == '#')
247 return;
248 if (*pattern == '\\' && pattern[1] == '#')
249 ++pattern;
250 add_exclude (ex, pattern, options);
251 }
252
253 static void
254 bzr_addfn (struct exclude *ex, char const *pattern, int options, void *data)
255 {
256 while (isspace (*pattern))
257 ++pattern;
258 if (*pattern == 0 || *pattern == '#')
259 return;
260 if (*pattern == '!')
261 {
262 if (*++pattern == '!')
263 ++pattern;
264 else
265 options |= EXCLUDE_INCLUDE;
266 }
267 /* FIXME: According to the docs, globbing patterns are rsync-style,
268 and regexps are perl-style. */
269 if (strncmp (pattern, "RE:", 3) == 0)
270 {
271 pattern += 3;
272 options &= ~EXCLUDE_WILDCARDS;
273 options |= EXCLUDE_REGEX;
274 }
275 add_exclude (ex, pattern, options);
276 }
277
278 static void *
279 hg_initfn (void *data)
280 {
281 int *hgopt;
282 static int hg_options;
283
284 if (!data)
285 hgopt = &hg_options;
286
287 *hgopt = EXCLUDE_REGEX;
288 return hgopt;
289 }
290
291 static void
292 hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
293 {
294 int *hgopt = data;
295 size_t len;
296
297 while (isspace (*pattern))
298 ++pattern;
299 if (*pattern == 0 || *pattern == '#')
300 return;
301 if (strncmp (pattern, "syntax:", 7) == 0)
302 {
303 for (pattern += 7; isspace (*pattern); ++pattern)
304 ;
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 */
311 return;
312 }
313
314 len = strlen(pattern);
315 if (pattern[len-1] == '/')
316 {
317 char *p;
318
319 --len;
320 p = xmalloc (len+1);
321 memcpy (p, pattern, len);
322 p[len] = 0;
323 pattern = p;
324 exclude_add_pattern_buffer (ex, p);
325 options |= FNM_LEADING_DIR|EXCLUDE_ALLOC;
326 }
327
328 add_exclude (ex, pattern,
329 ((*hgopt == EXCLUDE_REGEX)
330 ? (options & ~EXCLUDE_WILDCARDS)
331 : (options & ~EXCLUDE_REGEX)) | *hgopt);
332 }
333 \f
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 }
340 };
341
342 static struct vcs_ignore_file *
343 get_vcs_ignore_file (const char *name)
344 {
345 struct vcs_ignore_file *p;
346
347 for (p = vcs_ignore_files; p->filename; p++)
348 if (strcmp (p->filename, name) == 0)
349 break;
350
351 return p;
352 }
353 \f
354 void
355 exclude_vcs_ignores (void)
356 {
357 struct vcs_ignore_file *p;
358
359 for (p = vcs_ignore_files; p->filename; p++)
360 excfile_add (p->filename, EXCL_DEFAULT);
361 }
This page took 0.048531 seconds and 4 git commands to generate.