1 | /* exclude.c -- exclude file names
|
---|
2 |
|
---|
3 | Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003,
|
---|
4 | 2004, 2005, 2006 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 2, or (at your option)
|
---|
9 | 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; see the file COPYING.
|
---|
18 | If not, write to the Free Software Foundation,
|
---|
19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
---|
20 |
|
---|
21 | /* Written by Paul Eggert <eggert@twinsun.com> */
|
---|
22 |
|
---|
23 | #include <config.h>
|
---|
24 |
|
---|
25 | #include <stdbool.h>
|
---|
26 |
|
---|
27 | #include <ctype.h>
|
---|
28 | #include <errno.h>
|
---|
29 | #include <stddef.h>
|
---|
30 | #include <stdio.h>
|
---|
31 | #include <stdlib.h>
|
---|
32 | #include <string.h>
|
---|
33 |
|
---|
34 | #include "exclude.h"
|
---|
35 | #include "fnmatch.h"
|
---|
36 | #include "strcase.h"
|
---|
37 | #include "xalloc.h"
|
---|
38 | #include "verify.h"
|
---|
39 |
|
---|
40 | #if USE_UNLOCKED_IO
|
---|
41 | # include "unlocked-io.h"
|
---|
42 | #endif
|
---|
43 |
|
---|
44 | /* Non-GNU systems lack these options, so we don't need to check them. */
|
---|
45 | #ifndef FNM_CASEFOLD
|
---|
46 | # define FNM_CASEFOLD 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))
|
---|
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 | ? strcasecmp (pattern, f)
|
---|
101 | : strcmp (pattern, f));
|
---|
102 | else
|
---|
103 | {
|
---|
104 | size_t patlen = strlen (pattern);
|
---|
105 | int r = ((options & FNM_CASEFOLD)
|
---|
106 | ? strncasecmp (pattern, f, patlen)
|
---|
107 | : strncmp (pattern, f, patlen));
|
---|
108 | if (! r)
|
---|
109 | {
|
---|
110 | r = f[patlen];
|
---|
111 | if (r == '/')
|
---|
112 | r = 0;
|
---|
113 | }
|
---|
114 | return r;
|
---|
115 | }
|
---|
116 | }
|
---|
117 |
|
---|
118 | bool
|
---|
119 | exclude_fnmatch (char const *pattern, char const *f, int options)
|
---|
120 | {
|
---|
121 | int (*matcher) (char const *, char const *, int) =
|
---|
122 | (options & EXCLUDE_WILDCARDS
|
---|
123 | ? fnmatch
|
---|
124 | : fnmatch_no_wildcards);
|
---|
125 | bool matched = ((*matcher) (pattern, f, options) == 0);
|
---|
126 | char const *p;
|
---|
127 |
|
---|
128 | if (! (options & EXCLUDE_ANCHORED))
|
---|
129 | for (p = f; *p && ! matched; p++)
|
---|
130 | if (*p == '/' && p[1] != '/')
|
---|
131 | matched = ((*matcher) (pattern, p + 1, options) == 0);
|
---|
132 |
|
---|
133 | return matched;
|
---|
134 | }
|
---|
135 |
|
---|
136 | /* Return true if EX excludes F. */
|
---|
137 |
|
---|
138 | bool
|
---|
139 | excluded_file_name (struct exclude const *ex, char const *f)
|
---|
140 | {
|
---|
141 | size_t exclude_count = ex->exclude_count;
|
---|
142 |
|
---|
143 | /* If no options are given, the default is to include. */
|
---|
144 | if (exclude_count == 0)
|
---|
145 | return false;
|
---|
146 | else
|
---|
147 | {
|
---|
148 | struct patopts const *exclude = ex->exclude;
|
---|
149 | size_t i;
|
---|
150 |
|
---|
151 | /* Otherwise, the default is the opposite of the first option. */
|
---|
152 | bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
|
---|
153 |
|
---|
154 | /* Scan through the options, seeing whether they change F from
|
---|
155 | excluded to included or vice versa. */
|
---|
156 | for (i = 0; i < exclude_count; i++)
|
---|
157 | {
|
---|
158 | char const *pattern = exclude[i].pattern;
|
---|
159 | int options = exclude[i].options;
|
---|
160 | if (excluded == !! (options & EXCLUDE_INCLUDE))
|
---|
161 | excluded ^= exclude_fnmatch (pattern, f, options);
|
---|
162 | }
|
---|
163 |
|
---|
164 | return excluded;
|
---|
165 | }
|
---|
166 | }
|
---|
167 |
|
---|
168 | /* Append to EX the exclusion PATTERN with OPTIONS. */
|
---|
169 |
|
---|
170 | void
|
---|
171 | add_exclude (struct exclude *ex, char const *pattern, int options)
|
---|
172 | {
|
---|
173 | struct patopts *patopts;
|
---|
174 |
|
---|
175 | if (ex->exclude_count == ex->exclude_alloc)
|
---|
176 | ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc,
|
---|
177 | sizeof *ex->exclude);
|
---|
178 |
|
---|
179 | patopts = &ex->exclude[ex->exclude_count++];
|
---|
180 | patopts->pattern = pattern;
|
---|
181 | patopts->options = options;
|
---|
182 | }
|
---|
183 |
|
---|
184 | /* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with
|
---|
185 | OPTIONS. LINE_END terminates each pattern in the file. If
|
---|
186 | LINE_END is a space character, ignore trailing spaces and empty
|
---|
187 | lines in FILE. Return -1 on failure, 0 on success. */
|
---|
188 |
|
---|
189 | int
|
---|
190 | add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
|
---|
191 | struct exclude *ex, char const *file_name, int options,
|
---|
192 | char line_end)
|
---|
193 | {
|
---|
194 | bool use_stdin = file_name[0] == '-' && !file_name[1];
|
---|
195 | FILE *in;
|
---|
196 | char *buf = NULL;
|
---|
197 | char *p;
|
---|
198 | char const *pattern;
|
---|
199 | char const *lim;
|
---|
200 | size_t buf_alloc = 0;
|
---|
201 | size_t buf_count = 0;
|
---|
202 | int c;
|
---|
203 | int e = 0;
|
---|
204 |
|
---|
205 | if (use_stdin)
|
---|
206 | in = stdin;
|
---|
207 | else if (! (in = fopen (file_name, "r")))
|
---|
208 | return -1;
|
---|
209 |
|
---|
210 | while ((c = getc (in)) != EOF)
|
---|
211 | {
|
---|
212 | if (buf_count == buf_alloc)
|
---|
213 | buf = x2realloc (buf, &buf_alloc);
|
---|
214 | buf[buf_count++] = c;
|
---|
215 | }
|
---|
216 |
|
---|
217 | if (ferror (in))
|
---|
218 | e = errno;
|
---|
219 |
|
---|
220 | if (!use_stdin && fclose (in) != 0)
|
---|
221 | e = errno;
|
---|
222 |
|
---|
223 | buf = xrealloc (buf, buf_count + 1);
|
---|
224 | buf[buf_count] = line_end;
|
---|
225 | lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
|
---|
226 | pattern = buf;
|
---|
227 |
|
---|
228 | for (p = buf; p < lim; p++)
|
---|
229 | if (*p == line_end)
|
---|
230 | {
|
---|
231 | char *pattern_end = p;
|
---|
232 |
|
---|
233 | if (isspace ((unsigned char) line_end))
|
---|
234 | {
|
---|
235 | for (; ; pattern_end--)
|
---|
236 | if (pattern_end == pattern)
|
---|
237 | goto next_pattern;
|
---|
238 | else if (! isspace ((unsigned char) pattern_end[-1]))
|
---|
239 | break;
|
---|
240 | }
|
---|
241 |
|
---|
242 | *pattern_end = '\0';
|
---|
243 | (*add_func) (ex, pattern, options);
|
---|
244 |
|
---|
245 | next_pattern:
|
---|
246 | pattern = p + 1;
|
---|
247 | }
|
---|
248 |
|
---|
249 | errno = e;
|
---|
250 | return e ? -1 : 0;
|
---|
251 | }
|
---|