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