Import upstream version 1.28
[debian/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 }