Line data Source code
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 68 : new_exclude (void)
79 : {
80 68 : return xzalloc (sizeof *new_exclude ());
81 : }
82 :
83 : /* Free the storage associated with an exclude list. */
84 :
85 : void
86 0 : free_exclude (struct exclude *ex)
87 : {
88 0 : free (ex->exclude);
89 0 : free (ex);
90 0 : }
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 0 : fnmatch_no_wildcards (char const *pattern, char const *f, int options)
97 : {
98 0 : if (! (options & FNM_LEADING_DIR))
99 0 : return ((options & FNM_CASEFOLD)
100 : ? mbscasecmp (pattern, f)
101 0 : : strcmp (pattern, f));
102 0 : else if (! (options & FNM_CASEFOLD))
103 : {
104 0 : size_t patlen = strlen (pattern);
105 0 : int r = strncmp (pattern, f, patlen);
106 0 : if (! r)
107 : {
108 0 : r = f[patlen];
109 0 : if (r == '/')
110 0 : r = 0;
111 : }
112 0 : 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 0 : char *fcopy = xstrdup (f);
124 : char *p;
125 : int r;
126 0 : for (p = fcopy; ; *p++ = '/')
127 : {
128 0 : p = strchr (p, '/');
129 0 : if (p)
130 0 : *p = '\0';
131 0 : r = mbscasecmp (pattern, fcopy);
132 0 : if (!p || r <= 0)
133 : break;
134 : }
135 0 : free (fcopy);
136 0 : return r;
137 : }
138 : }
139 :
140 : bool
141 0 : exclude_fnmatch (char const *pattern, char const *f, int options)
142 : {
143 0 : int (*matcher) (char const *, char const *, int) =
144 0 : (options & EXCLUDE_WILDCARDS
145 : ? fnmatch
146 0 : : fnmatch_no_wildcards);
147 0 : bool matched = ((*matcher) (pattern, f, options) == 0);
148 : char const *p;
149 :
150 0 : if (! (options & EXCLUDE_ANCHORED))
151 0 : for (p = f; *p && ! matched; p++)
152 0 : if (*p == '/' && p[1] != '/')
153 0 : matched = ((*matcher) (pattern, p + 1, options) == 0);
154 :
155 0 : return matched;
156 : }
157 :
158 : /* Return true if EX excludes F. */
159 :
160 : bool
161 449528 : excluded_file_name (struct exclude const *ex, char const *f)
162 : {
163 449528 : size_t exclude_count = ex->exclude_count;
164 :
165 : /* If no options are given, the default is to include. */
166 449528 : if (exclude_count == 0)
167 449528 : return false;
168 : else
169 : {
170 0 : struct patopts const *exclude = ex->exclude;
171 : size_t i;
172 :
173 : /* Otherwise, the default is the opposite of the first option. */
174 0 : 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 0 : for (i = 0; i < exclude_count; i++)
179 : {
180 0 : char const *pattern = exclude[i].pattern;
181 0 : int options = exclude[i].options;
182 0 : if (excluded == !! (options & EXCLUDE_INCLUDE))
183 0 : excluded ^= exclude_fnmatch (pattern, f, options);
184 : }
185 :
186 0 : return excluded;
187 : }
188 : }
189 :
190 : /* Append to EX the exclusion PATTERN with OPTIONS. */
191 :
192 : void
193 0 : add_exclude (struct exclude *ex, char const *pattern, int options)
194 : {
195 : struct patopts *patopts;
196 :
197 0 : if (ex->exclude_count == ex->exclude_alloc)
198 0 : ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc,
199 : sizeof *ex->exclude);
200 :
201 0 : patopts = &ex->exclude[ex->exclude_count++];
202 0 : patopts->pattern = pattern;
203 0 : patopts->options = options;
204 0 : }
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 0 : 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 0 : bool use_stdin = file_name[0] == '-' && !file_name[1];
217 : FILE *in;
218 0 : char *buf = NULL;
219 : char *p;
220 : char const *pattern;
221 : char const *lim;
222 0 : size_t buf_alloc = 0;
223 0 : size_t buf_count = 0;
224 : int c;
225 0 : int e = 0;
226 :
227 0 : if (use_stdin)
228 0 : in = stdin;
229 0 : else if (! (in = fopen (file_name, "r")))
230 0 : return -1;
231 :
232 0 : while ((c = getc (in)) != EOF)
233 : {
234 0 : if (buf_count == buf_alloc)
235 0 : buf = x2realloc (buf, &buf_alloc);
236 0 : buf[buf_count++] = c;
237 : }
238 :
239 0 : if (ferror (in))
240 0 : e = errno;
241 :
242 0 : if (!use_stdin && fclose (in) != 0)
243 0 : e = errno;
244 :
245 0 : buf = xrealloc (buf, buf_count + 1);
246 0 : buf[buf_count] = line_end;
247 0 : lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
248 0 : pattern = buf;
249 :
250 0 : for (p = buf; p < lim; p++)
251 0 : if (*p == line_end)
252 : {
253 0 : char *pattern_end = p;
254 :
255 0 : if (isspace ((unsigned char) line_end))
256 : {
257 0 : for (; ; pattern_end--)
258 0 : if (pattern_end == pattern)
259 0 : goto next_pattern;
260 0 : else if (! isspace ((unsigned char) pattern_end[-1]))
261 0 : break;
262 : }
263 :
264 0 : *pattern_end = '\0';
265 0 : (*add_func) (ex, pattern, options);
266 :
267 0 : next_pattern:
268 0 : pattern = p + 1;
269 : }
270 :
271 0 : errno = e;
272 0 : return e ? -1 : 0;
273 : }
|