]> git.cworth.org Git - tar/blob - lib/exclude.c
Imported Upstream version 1.20
[tar] / lib / exclude.c
1 /* exclude.c -- exclude file names
2
3    Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003,
4    2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* Written by Paul Eggert <eggert@twinsun.com>  */
20
21 #include <config.h>
22
23 #include <stdbool.h>
24
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "exclude.h"
33 #include "fnmatch.h"
34 #include "xalloc.h"
35 #include "verify.h"
36
37 #if USE_UNLOCKED_IO
38 # include "unlocked-io.h"
39 #endif
40
41 /* Non-GNU systems lack these options, so we don't need to check them.  */
42 #ifndef FNM_CASEFOLD
43 # define FNM_CASEFOLD 0
44 #endif
45 #ifndef FNM_EXTMATCH
46 # define FNM_EXTMATCH 0
47 #endif
48 #ifndef FNM_LEADING_DIR
49 # define FNM_LEADING_DIR 0
50 #endif
51
52 verify (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
53          & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
54             | FNM_CASEFOLD | FNM_EXTMATCH))
55         == 0);
56
57 /* An exclude pattern-options pair.  The options are fnmatch options
58    ORed with EXCLUDE_* options.  */
59
60 struct patopts
61   {
62     char const *pattern;
63     int options;
64   };
65
66 /* An exclude list, of pattern-options pairs.  */
67
68 struct exclude
69   {
70     struct patopts *exclude;
71     size_t exclude_alloc;
72     size_t exclude_count;
73   };
74
75 /* Return a newly allocated and empty exclude list.  */
76
77 struct exclude *
78 new_exclude (void)
79 {
80   return xzalloc (sizeof *new_exclude ());
81 }
82
83 /* Free the storage associated with an exclude list.  */
84
85 void
86 free_exclude (struct exclude *ex)
87 {
88   free (ex->exclude);
89   free (ex);
90 }
91
92 /* Return zero if PATTERN matches F, obeying OPTIONS, except that
93    (unlike fnmatch) wildcards are disabled in PATTERN.  */
94
95 static int
96 fnmatch_no_wildcards (char const *pattern, char const *f, int options)
97 {
98   if (! (options & FNM_LEADING_DIR))
99     return ((options & FNM_CASEFOLD)
100             ? mbscasecmp (pattern, f)
101             : strcmp (pattern, f));
102   else if (! (options & FNM_CASEFOLD))
103     {
104       size_t patlen = strlen (pattern);
105       int r = strncmp (pattern, f, patlen);
106       if (! r)
107         {
108           r = f[patlen];
109           if (r == '/')
110             r = 0;
111         }
112       return r;
113     }
114   else
115     {
116       /* Walk through a copy of F, seeing whether P matches any prefix
117          of F.
118
119          FIXME: This is an O(N**2) algorithm; it should be O(N).
120          Also, the copy should not be necessary.  However, fixing this
121          will probably involve a change to the mbs* API.  */
122
123       char *fcopy = xstrdup (f);
124       char *p;
125       int r;
126       for (p = fcopy; ; *p++ = '/')
127         {
128           p = strchr (p, '/');
129           if (p)
130             *p = '\0';
131           r = mbscasecmp (pattern, fcopy);
132           if (!p || r <= 0)
133             break;
134         }
135       free (fcopy);
136       return r;
137     }
138 }
139
140 bool
141 exclude_fnmatch (char const *pattern, char const *f, int options)
142 {
143   int (*matcher) (char const *, char const *, int) =
144     (options & EXCLUDE_WILDCARDS
145      ? fnmatch
146      : fnmatch_no_wildcards);
147   bool matched = ((*matcher) (pattern, f, options) == 0);
148   char const *p;
149
150   if (! (options & EXCLUDE_ANCHORED))
151     for (p = f; *p && ! matched; p++)
152       if (*p == '/' && p[1] != '/')
153         matched = ((*matcher) (pattern, p + 1, options) == 0);
154
155   return matched;
156 }
157
158 /* Return true if EX excludes F.  */
159
160 bool
161 excluded_file_name (struct exclude const *ex, char const *f)
162 {
163   size_t exclude_count = ex->exclude_count;
164
165   /* If no options are given, the default is to include.  */
166   if (exclude_count == 0)
167     return false;
168   else
169     {
170       struct patopts const *exclude = ex->exclude;
171       size_t i;
172
173       /* Otherwise, the default is the opposite of the first option.  */
174       bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
175
176       /* Scan through the options, seeing whether they change F from
177          excluded to included or vice versa.  */
178       for (i = 0;  i < exclude_count;  i++)
179         {
180           char const *pattern = exclude[i].pattern;
181           int options = exclude[i].options;
182           if (excluded == !! (options & EXCLUDE_INCLUDE))
183             excluded ^= exclude_fnmatch (pattern, f, options);
184         }
185
186       return excluded;
187     }
188 }
189
190 /* Append to EX the exclusion PATTERN with OPTIONS.  */
191
192 void
193 add_exclude (struct exclude *ex, char const *pattern, int options)
194 {
195   struct patopts *patopts;
196
197   if (ex->exclude_count == ex->exclude_alloc)
198     ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc,
199                               sizeof *ex->exclude);
200
201   patopts = &ex->exclude[ex->exclude_count++];
202   patopts->pattern = pattern;
203   patopts->options = options;
204 }
205
206 /* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with
207    OPTIONS.  LINE_END terminates each pattern in the file.  If
208    LINE_END is a space character, ignore trailing spaces and empty
209    lines in FILE.  Return -1 on failure, 0 on success.  */
210
211 int
212 add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
213                   struct exclude *ex, char const *file_name, int options,
214                   char line_end)
215 {
216   bool use_stdin = file_name[0] == '-' && !file_name[1];
217   FILE *in;
218   char *buf = NULL;
219   char *p;
220   char const *pattern;
221   char const *lim;
222   size_t buf_alloc = 0;
223   size_t buf_count = 0;
224   int c;
225   int e = 0;
226
227   if (use_stdin)
228     in = stdin;
229   else if (! (in = fopen (file_name, "r")))
230     return -1;
231
232   while ((c = getc (in)) != EOF)
233     {
234       if (buf_count == buf_alloc)
235         buf = x2realloc (buf, &buf_alloc);
236       buf[buf_count++] = c;
237     }
238
239   if (ferror (in))
240     e = errno;
241
242   if (!use_stdin && fclose (in) != 0)
243     e = errno;
244
245   buf = xrealloc (buf, buf_count + 1);
246   buf[buf_count] = line_end;
247   lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
248   pattern = buf;
249
250   for (p = buf; p < lim; p++)
251     if (*p == line_end)
252       {
253         char *pattern_end = p;
254
255         if (isspace ((unsigned char) line_end))
256           {
257             for (; ; pattern_end--)
258               if (pattern_end == pattern)
259                 goto next_pattern;
260               else if (! isspace ((unsigned char) pattern_end[-1]))
261                 break;
262           }
263
264         *pattern_end = '\0';
265         (*add_func) (ex, pattern, options);
266
267       next_pattern:
268         pattern = p + 1;
269       }
270
271   errno = e;
272   return e ? -1 : 0;
273 }