Tizen 2.0 Release
[external/tizen-coreutils.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, 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 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 "xalloc.h"
37 #include "verify.h"
38
39 #if USE_UNLOCKED_IO
40 # include "unlocked-io.h"
41 #endif
42
43 /* Non-GNU systems lack these options, so we don't need to check them.  */
44 #ifndef FNM_CASEFOLD
45 # define FNM_CASEFOLD 0
46 #endif
47 #ifndef FNM_EXTMATCH
48 # define FNM_EXTMATCH 0
49 #endif
50 #ifndef FNM_LEADING_DIR
51 # define FNM_LEADING_DIR 0
52 #endif
53
54 verify (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
55          & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
56             | FNM_CASEFOLD | FNM_EXTMATCH))
57         == 0);
58
59 /* An exclude pattern-options pair.  The options are fnmatch options
60    ORed with EXCLUDE_* options.  */
61
62 struct patopts
63   {
64     char const *pattern;
65     int options;
66   };
67
68 /* An exclude list, of pattern-options pairs.  */
69
70 struct exclude
71   {
72     struct patopts *exclude;
73     size_t exclude_alloc;
74     size_t exclude_count;
75   };
76
77 /* Return a newly allocated and empty exclude list.  */
78
79 struct exclude *
80 new_exclude (void)
81 {
82   return xzalloc (sizeof *new_exclude ());
83 }
84
85 /* Free the storage associated with an exclude list.  */
86
87 void
88 free_exclude (struct exclude *ex)
89 {
90   free (ex->exclude);
91   free (ex);
92 }
93
94 /* Return zero if PATTERN matches F, obeying OPTIONS, except that
95    (unlike fnmatch) wildcards are disabled in PATTERN.  */
96
97 static int
98 fnmatch_no_wildcards (char const *pattern, char const *f, int options)
99 {
100   if (! (options & FNM_LEADING_DIR))
101     return ((options & FNM_CASEFOLD)
102             ? mbscasecmp (pattern, f)
103             : strcmp (pattern, f));
104   else if (! (options & FNM_CASEFOLD))
105     {
106       size_t patlen = strlen (pattern);
107       int r = strncmp (pattern, f, patlen);
108       if (! r)
109         {
110           r = f[patlen];
111           if (r == '/')
112             r = 0;
113         }
114       return r;
115     }
116   else
117     {
118       /* Walk through a copy of F, seeing whether P matches any prefix
119          of F.
120
121          FIXME: This is an O(N**2) algorithm; it should be O(N).
122          Also, the copy should not be necessary.  However, fixing this
123          will probably involve a change to the mbs* API.  */
124
125       char *fcopy = xstrdup (f);
126       char *p;
127       int r;
128       for (p = fcopy; ; *p++ = '/')
129         {
130           p = strchr (p, '/');
131           if (p)
132             *p = '\0';
133           r = mbscasecmp (pattern, fcopy);
134           if (!p || r <= 0)
135             break;
136         }
137       free (fcopy);
138       return r;
139     }
140 }
141
142 bool
143 exclude_fnmatch (char const *pattern, char const *f, int options)
144 {
145   int (*matcher) (char const *, char const *, int) =
146     (options & EXCLUDE_WILDCARDS
147      ? fnmatch
148      : fnmatch_no_wildcards);
149   bool matched = ((*matcher) (pattern, f, options) == 0);
150   char const *p;
151
152   if (! (options & EXCLUDE_ANCHORED))
153     for (p = f; *p && ! matched; p++)
154       if (*p == '/' && p[1] != '/')
155         matched = ((*matcher) (pattern, p + 1, options) == 0);
156
157   return matched;
158 }
159
160 /* Return true if EX excludes F.  */
161
162 bool
163 excluded_file_name (struct exclude const *ex, char const *f)
164 {
165   size_t exclude_count = ex->exclude_count;
166
167   /* If no options are given, the default is to include.  */
168   if (exclude_count == 0)
169     return false;
170   else
171     {
172       struct patopts const *exclude = ex->exclude;
173       size_t i;
174
175       /* Otherwise, the default is the opposite of the first option.  */
176       bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
177
178       /* Scan through the options, seeing whether they change F from
179          excluded to included or vice versa.  */
180       for (i = 0;  i < exclude_count;  i++)
181         {
182           char const *pattern = exclude[i].pattern;
183           int options = exclude[i].options;
184           if (excluded == !! (options & EXCLUDE_INCLUDE))
185             excluded ^= exclude_fnmatch (pattern, f, options);
186         }
187
188       return excluded;
189     }
190 }
191
192 /* Append to EX the exclusion PATTERN with OPTIONS.  */
193
194 void
195 add_exclude (struct exclude *ex, char const *pattern, int options)
196 {
197   struct patopts *patopts;
198
199   if (ex->exclude_count == ex->exclude_alloc)
200     ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc,
201                               sizeof *ex->exclude);
202
203   patopts = &ex->exclude[ex->exclude_count++];
204   patopts->pattern = pattern;
205   patopts->options = options;
206 }
207
208 /* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with
209    OPTIONS.  LINE_END terminates each pattern in the file.  If
210    LINE_END is a space character, ignore trailing spaces and empty
211    lines in FILE.  Return -1 on failure, 0 on success.  */
212
213 int
214 add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
215                   struct exclude *ex, char const *file_name, int options,
216                   char line_end)
217 {
218   bool use_stdin = file_name[0] == '-' && !file_name[1];
219   FILE *in;
220   char *buf = NULL;
221   char *p;
222   char const *pattern;
223   char const *lim;
224   size_t buf_alloc = 0;
225   size_t buf_count = 0;
226   int c;
227   int e = 0;
228
229   if (use_stdin)
230     in = stdin;
231   else if (! (in = fopen (file_name, "r")))
232     return -1;
233
234   while ((c = getc (in)) != EOF)
235     {
236       if (buf_count == buf_alloc)
237         buf = x2realloc (buf, &buf_alloc);
238       buf[buf_count++] = c;
239     }
240
241   if (ferror (in))
242     e = errno;
243
244   if (!use_stdin && fclose (in) != 0)
245     e = errno;
246
247   buf = xrealloc (buf, buf_count + 1);
248   buf[buf_count] = line_end;
249   lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
250   pattern = buf;
251
252   for (p = buf; p < lim; p++)
253     if (*p == line_end)
254       {
255         char *pattern_end = p;
256
257         if (isspace ((unsigned char) line_end))
258           {
259             for (; ; pattern_end--)
260               if (pattern_end == pattern)
261                 goto next_pattern;
262               else if (! isspace ((unsigned char) pattern_end[-1]))
263                 break;
264           }
265
266         *pattern_end = '\0';
267         (*add_func) (ex, pattern, options);
268
269       next_pattern:
270         pattern = p + 1;
271       }
272
273   errno = e;
274   return e ? -1 : 0;
275 }