xref: /freebsd-src/contrib/diff/lib/exclude.c (revision 18fd37a72c3a7549d2d4f6c6ea00bdcd2bdaca01)
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