File: | src/exclist.c |
Warning: | line 287, column 3 Dereference of undefined pointer value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
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((void*)0); | |||
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((void*)0), *tail = NULL((void*)0), *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_OK0, 0) == 0) | |||
81 | { | |||
82 | FILE *fp; | |||
83 | struct exclude *ex = NULL((void*)0); | |||
84 | int fd = subfile_open (dir, file->name, O_RDONLY00); | |||
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))do { if (error_hook) error_hook (); error (0, (*__errno_location ()), dcgettext (((void*)0), "%s: fdopen failed", 5), file-> name); exit_status = 2; } while (0); | |||
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(1 << 28)|EXCLUDE_ANCHORED(1 << 30), '\n', | |||
108 | vcsfile->data)) | |||
109 | { | |||
110 | int e = errno(*__errno_location ()); | |||
111 | FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)))do { if (error_hook) error_hook (); error (0, e, "%s", quotearg_colon (file->name)); fatal_exit (); } while (0); | |||
112 | } | |||
113 | fclose (fp); | |||
114 | ||||
115 | ent = xmalloc (sizeof (*ent)); | |||
116 | ent->excluded = ex; | |||
117 | ent->flags = file->flags == EXCL_DEFAULT0x00 | |||
118 | ? file->flags : vcsfile->flags; | |||
119 | ent->prev = tail; | |||
120 | ent->next = NULL((void*)0); | |||
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_RECURSIVE0x02) | |||
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((void*)0); | |||
174 | } | |||
175 | ||||
176 | ||||
177 | /* Return nonzero if file NAME is excluded. */ | |||
178 | bool_Bool | |||
179 | excluded_name (char const *name, struct tar_stat_info *st) | |||
180 | { | |||
181 | struct exclist *ep; | |||
182 | const char *rname = NULL((void*)0); | |||
183 | char *bname = NULL((void*)0); | |||
184 | bool_Bool result; | |||
185 | int nr = 0; | |||
186 | ||||
187 | name += FILE_SYSTEM_PREFIX_LEN (name)0; | |||
188 | ||||
189 | /* Try global exclusion list first */ | |||
190 | if (excluded_file_name (excluded, name)) | |||
191 | return true1; | |||
192 | ||||
193 | if (!st) | |||
194 | return false0; | |||
195 | ||||
196 | for (result = false0; st && !result; st = st->parent, nr = EXCL_NON_RECURSIVE0x02) | |||
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])((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 | ||||
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_NOVAR0x00000040 | WRDSF_NOCMD0x00000004 | WRDSF_SQUEEZE_DELIMS0x00000800)) | |||
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)((*__ctype_b_loc ())[(int) ((*pattern))] & (unsigned short int) _ISspace)) | |||
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)((*__ctype_b_loc ())[(int) ((*pattern))] & (unsigned short int) _ISspace)) | |||
257 | ++pattern; | |||
258 | if (*pattern == 0 || *pattern == '#') | |||
259 | return; | |||
260 | if (*pattern == '!') | |||
261 | { | |||
262 | if (*++pattern == '!') | |||
263 | ++pattern; | |||
264 | else | |||
265 | options |= EXCLUDE_INCLUDE(1 << 29); | |||
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(1 << 28); | |||
273 | options |= EXCLUDE_REGEX(1 << 27); | |||
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(1 << 27); | |||
| ||||
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)((*__ctype_b_loc ())[(int) ((*pattern))] & (unsigned short int) _ISspace)) | |||
298 | ++pattern; | |||
299 | if (*pattern == 0 || *pattern == '#') | |||
300 | return; | |||
301 | if (strncmp (pattern, "syntax:", 7) == 0) | |||
302 | { | |||
303 | for (pattern += 7; isspace (*pattern)((*__ctype_b_loc ())[(int) ((*pattern))] & (unsigned short int) _ISspace); ++pattern) | |||
304 | ; | |||
305 | if (strcmp (pattern, "regexp") == 0) | |||
306 | /* FIXME: Regexps must be perl-style */ | |||
307 | *hgopt = EXCLUDE_REGEX(1 << 27); | |||
308 | else if (strcmp (pattern, "glob") == 0) | |||
309 | *hgopt = EXCLUDE_WILDCARDS(1 << 28); | |||
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(1 << 3)|EXCLUDE_ALLOC(1 << 26); | |||
326 | } | |||
327 | ||||
328 | add_exclude (ex, pattern, | |||
329 | ((*hgopt == EXCLUDE_REGEX(1 << 27)) | |||
330 | ? (options & ~EXCLUDE_WILDCARDS(1 << 28)) | |||
331 | : (options & ~EXCLUDE_REGEX(1 << 27))) | *hgopt); | |||
332 | } | |||
333 | ||||
334 | struct vcs_ignore_file vcs_ignore_files[] = { | |||
335 | { ".cvsignore", EXCL_NON_RECURSIVE0x02, cvs_addfn, NULL((void*)0), NULL((void*)0) }, | |||
336 | { ".gitignore", 0, git_addfn, NULL((void*)0), NULL((void*)0) }, | |||
337 | { ".bzrignore", 0, bzr_addfn, NULL((void*)0), NULL((void*)0) }, | |||
338 | { ".hgignore", 0, hg_addfn, hg_initfn , NULL((void*)0) }, | |||
339 | { NULL((void*)0), 0, git_addfn, NULL((void*)0), NULL((void*)0) } | |||
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 | ||||
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_DEFAULT0x00); | |||
361 | } |