1170754Sdelphij/* exclude.c -- exclude file names
2170754Sdelphij
3170754Sdelphij   Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003 Free
4170754Sdelphij   Software Foundation, Inc.
5170754Sdelphij
6170754Sdelphij   This program is free software; you can redistribute it and/or modify
7170754Sdelphij   it under the terms of the GNU General Public License as published by
8170754Sdelphij   the Free Software Foundation; either version 2, or (at your option)
9170754Sdelphij   any later version.
10170754Sdelphij
11170754Sdelphij   This program is distributed in the hope that it will be useful,
12170754Sdelphij   but WITHOUT ANY WARRANTY; without even the implied warranty of
13170754Sdelphij   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14170754Sdelphij   GNU General Public License for more details.
15170754Sdelphij
16170754Sdelphij   You should have received a copy of the GNU General Public License
17170754Sdelphij   along with this program; see the file COPYING.
18170754Sdelphij   If not, write to the Free Software Foundation,
19170754Sdelphij   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20170754Sdelphij
21170754Sdelphij/* Written by Paul Eggert <eggert@twinsun.com>  */
22170754Sdelphij
23170754Sdelphij#if HAVE_CONFIG_H
24170754Sdelphij# include <config.h>
25170754Sdelphij#endif
26170754Sdelphij
27170754Sdelphij#include <stdbool.h>
28170754Sdelphij
29170754Sdelphij#include <ctype.h>
30170754Sdelphij#include <errno.h>
31170754Sdelphij#ifndef errno
32170754Sdelphijextern int errno;
33170754Sdelphij#endif
34170754Sdelphij#include <stddef.h>
35170754Sdelphij#include <stdio.h>
36170754Sdelphij#include <stdlib.h>
37170754Sdelphij#include <string.h>
38170754Sdelphij
39170754Sdelphij#include "exclude.h"
40170754Sdelphij#include "fnmatch.h"
41170754Sdelphij#include "unlocked-io.h"
42170754Sdelphij#include "xalloc.h"
43170754Sdelphij
44170754Sdelphij#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
45170754Sdelphij# define IN_CTYPE_DOMAIN(c) true
46170754Sdelphij#else
47170754Sdelphij# define IN_CTYPE_DOMAIN(c) isascii (c)
48170754Sdelphij#endif
49170754Sdelphij
50170754Sdelphijstatic inline bool
51170754Sdelphijis_space (unsigned char c)
52170754Sdelphij{
53170754Sdelphij  return IN_CTYPE_DOMAIN (c) && isspace (c);
54170754Sdelphij}
55170754Sdelphij
56170754Sdelphij/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
57170754Sdelphij#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
58170754Sdelphij
59170754Sdelphij/* Non-GNU systems lack these options, so we don't need to check them.  */
60170754Sdelphij#ifndef FNM_CASEFOLD
61170754Sdelphij# define FNM_CASEFOLD 0
62170754Sdelphij#endif
63170754Sdelphij#ifndef FNM_LEADING_DIR
64170754Sdelphij# define FNM_LEADING_DIR 0
65170754Sdelphij#endif
66170754Sdelphij
67170754Sdelphijverify (EXCLUDE_macros_do_not_collide_with_FNM_macros,
68170754Sdelphij	(((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
69170754Sdelphij	  & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
70170754Sdelphij	     | FNM_CASEFOLD))
71170754Sdelphij	 == 0));
72170754Sdelphij
73170754Sdelphij/* An exclude pattern-options pair.  The options are fnmatch options
74170754Sdelphij   ORed with EXCLUDE_* options.  */
75170754Sdelphij
76170754Sdelphijstruct patopts
77170754Sdelphij  {
78170754Sdelphij    char const *pattern;
79170754Sdelphij    int options;
80170754Sdelphij  };
81170754Sdelphij
82170754Sdelphij/* An exclude list, of pattern-options pairs.  */
83170754Sdelphij
84170754Sdelphijstruct exclude
85170754Sdelphij  {
86170754Sdelphij    struct patopts *exclude;
87170754Sdelphij    size_t exclude_alloc;
88170754Sdelphij    size_t exclude_count;
89170754Sdelphij  };
90170754Sdelphij
91170754Sdelphij/* Return a newly allocated and empty exclude list.  */
92170754Sdelphij
93170754Sdelphijstruct exclude *
94170754Sdelphijnew_exclude (void)
95170754Sdelphij{
96170754Sdelphij  return xzalloc (sizeof *new_exclude ());
97170754Sdelphij}
98170754Sdelphij
99170754Sdelphij/* Free the storage associated with an exclude list.  */
100170754Sdelphij
101170754Sdelphijvoid
102170754Sdelphijfree_exclude (struct exclude *ex)
103170754Sdelphij{
104170754Sdelphij  free (ex->exclude);
105170754Sdelphij  free (ex);
106170754Sdelphij}
107170754Sdelphij
108170754Sdelphij/* Return zero if PATTERN matches F, obeying OPTIONS, except that
109170754Sdelphij   (unlike fnmatch) wildcards are disabled in PATTERN.  */
110170754Sdelphij
111170754Sdelphijstatic int
112170754Sdelphijfnmatch_no_wildcards (char const *pattern, char const *f, int options)
113170754Sdelphij{
114170754Sdelphij  if (! (options & FNM_LEADING_DIR))
115170754Sdelphij    return ((options & FNM_CASEFOLD)
116170754Sdelphij	    ? strcasecmp (pattern, f)
117170754Sdelphij	    : strcmp (pattern, f));
118170754Sdelphij  else
119170754Sdelphij    {
120170754Sdelphij      size_t patlen = strlen (pattern);
121170754Sdelphij      int r = ((options & FNM_CASEFOLD)
122170754Sdelphij		? strncasecmp (pattern, f, patlen)
123170754Sdelphij		: strncmp (pattern, f, patlen));
124170754Sdelphij      if (! r)
125170754Sdelphij	{
126170754Sdelphij	  r = f[patlen];
127170754Sdelphij	  if (r == '/')
128170754Sdelphij	    r = 0;
129170754Sdelphij	}
130170754Sdelphij      return r;
131170754Sdelphij    }
132170754Sdelphij}
133170754Sdelphij
134170754Sdelphij/* Return true if EX excludes F.  */
135170754Sdelphij
136170754Sdelphijbool
137170754Sdelphijexcluded_filename (struct exclude const *ex, char const *f)
138170754Sdelphij{
139170754Sdelphij  size_t exclude_count = ex->exclude_count;
140170754Sdelphij
141170754Sdelphij  /* If no options are given, the default is to include.  */
142170754Sdelphij  if (exclude_count == 0)
143170754Sdelphij    return false;
144170754Sdelphij  else
145170754Sdelphij    {
146170754Sdelphij      struct patopts const *exclude = ex->exclude;
147170754Sdelphij      size_t i;
148170754Sdelphij
149170754Sdelphij      /* Otherwise, the default is the opposite of the first option.  */
150170754Sdelphij      bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
151170754Sdelphij
152170754Sdelphij      /* Scan through the options, seeing whether they change F from
153170754Sdelphij	 excluded to included or vice versa.  */
154170754Sdelphij      for (i = 0;  i < exclude_count;  i++)
155170754Sdelphij	{
156170754Sdelphij	  char const *pattern = exclude[i].pattern;
157170754Sdelphij	  int options = exclude[i].options;
158170754Sdelphij	  if (excluded == !! (options & EXCLUDE_INCLUDE))
159170754Sdelphij	    {
160170754Sdelphij	      int (*matcher) (char const *, char const *, int) =
161170754Sdelphij		(options & EXCLUDE_WILDCARDS
162170754Sdelphij		 ? fnmatch
163170754Sdelphij		 : fnmatch_no_wildcards);
164170754Sdelphij	      bool matched = ((*matcher) (pattern, f, options) == 0);
165170754Sdelphij	      char const *p;
166170754Sdelphij
167170754Sdelphij	      if (! (options & EXCLUDE_ANCHORED))
168170754Sdelphij		for (p = f; *p && ! matched; p++)
169170754Sdelphij		  if (*p == '/' && p[1] != '/')
170170754Sdelphij		    matched = ((*matcher) (pattern, p + 1, options) == 0);
171170754Sdelphij
172170754Sdelphij	      excluded ^= matched;
173170754Sdelphij	    }
174170754Sdelphij	}
175170754Sdelphij
176170754Sdelphij      return excluded;
177170754Sdelphij    }
178170754Sdelphij}
179170754Sdelphij
180170754Sdelphij/* Append to EX the exclusion PATTERN with OPTIONS.  */
181170754Sdelphij
182170754Sdelphijvoid
183170754Sdelphijadd_exclude (struct exclude *ex, char const *pattern, int options)
184170754Sdelphij{
185170754Sdelphij  struct patopts *patopts;
186170754Sdelphij
187170754Sdelphij  if (ex->exclude_count == ex->exclude_alloc)
188170754Sdelphij    ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc,
189170754Sdelphij			      sizeof *ex->exclude);
190170754Sdelphij
191170754Sdelphij  patopts = &ex->exclude[ex->exclude_count++];
192170754Sdelphij  patopts->pattern = pattern;
193170754Sdelphij  patopts->options = options;
194170754Sdelphij}
195170754Sdelphij
196170754Sdelphij/* Use ADD_FUNC to append to EX the patterns in FILENAME, each with
197170754Sdelphij   OPTIONS.  LINE_END terminates each pattern in the file.  If
198170754Sdelphij   LINE_END is a space character, ignore trailing spaces and empty
199170754Sdelphij   lines in FILE.  Return -1 on failure, 0 on success.  */
200170754Sdelphij
201170754Sdelphijint
202170754Sdelphijadd_exclude_file (void (*add_func) (struct exclude *, char const *, int),
203170754Sdelphij		  struct exclude *ex, char const *filename, int options,
204170754Sdelphij		  char line_end)
205170754Sdelphij{
206170754Sdelphij  bool use_stdin = filename[0] == '-' && !filename[1];
207170754Sdelphij  FILE *in;
208170754Sdelphij  char *buf = NULL;
209170754Sdelphij  char *p;
210170754Sdelphij  char const *pattern;
211170754Sdelphij  char const *lim;
212170754Sdelphij  size_t buf_alloc = 0;
213170754Sdelphij  size_t buf_count = 0;
214170754Sdelphij  int c;
215170754Sdelphij  int e = 0;
216170754Sdelphij
217170754Sdelphij  if (use_stdin)
218170754Sdelphij    in = stdin;
219170754Sdelphij  else if (! (in = fopen (filename, "r")))
220170754Sdelphij    return -1;
221170754Sdelphij
222170754Sdelphij  while ((c = getc (in)) != EOF)
223170754Sdelphij    {
224170754Sdelphij      if (buf_count == buf_alloc)
225170754Sdelphij	buf = x2realloc (buf, &buf_alloc);
226170754Sdelphij      buf[buf_count++] = c;
227170754Sdelphij    }
228170754Sdelphij
229170754Sdelphij  if (ferror (in))
230170754Sdelphij    e = errno;
231170754Sdelphij
232170754Sdelphij  if (!use_stdin && fclose (in) != 0)
233170754Sdelphij    e = errno;
234170754Sdelphij
235170754Sdelphij  buf = xrealloc (buf, buf_count + 1);
236170754Sdelphij  buf[buf_count] = line_end;
237170754Sdelphij  lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
238170754Sdelphij  pattern = buf;
239170754Sdelphij
240170754Sdelphij  for (p = buf; p < lim; p++)
241170754Sdelphij    if (*p == line_end)
242170754Sdelphij      {
243170754Sdelphij	char *pattern_end = p;
244170754Sdelphij
245170754Sdelphij	if (is_space (line_end))
246170754Sdelphij	  {
247170754Sdelphij	    for (; ; pattern_end--)
248170754Sdelphij	      if (pattern_end == pattern)
249170754Sdelphij		goto next_pattern;
250170754Sdelphij	      else if (! is_space (pattern_end[-1]))
251170754Sdelphij		break;
252170754Sdelphij	  }
253170754Sdelphij
254170754Sdelphij	*pattern_end = '\0';
255170754Sdelphij	(*add_func) (ex, pattern, options);
256170754Sdelphij
257170754Sdelphij      next_pattern:
258170754Sdelphij	pattern = p + 1;
259170754Sdelphij      }
260170754Sdelphij
261170754Sdelphij  errno = e;
262170754Sdelphij  return e ? -1 : 0;
263170754Sdelphij}
264