* clean-temp.h (close_stream_temp): New declaration.
[gnulib.git] / 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 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 }