xref: /freebsd/contrib/diff/lib/exclude.c (revision 18fd37a72c3a7549d2d4f6c6ea00bdcd2bdaca01)
118fd37a7SXin LI /* exclude.c -- exclude file names
218fd37a7SXin LI 
318fd37a7SXin LI    Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003 Free
418fd37a7SXin LI    Software Foundation, Inc.
518fd37a7SXin LI 
618fd37a7SXin LI    This program is free software; you can redistribute it and/or modify
718fd37a7SXin LI    it under the terms of the GNU General Public License as published by
818fd37a7SXin LI    the Free Software Foundation; either version 2, or (at your option)
918fd37a7SXin LI    any later version.
1018fd37a7SXin LI 
1118fd37a7SXin LI    This program is distributed in the hope that it will be useful,
1218fd37a7SXin LI    but WITHOUT ANY WARRANTY; without even the implied warranty of
1318fd37a7SXin LI    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1418fd37a7SXin LI    GNU General Public License for more details.
1518fd37a7SXin LI 
1618fd37a7SXin LI    You should have received a copy of the GNU General Public License
1718fd37a7SXin LI    along with this program; see the file COPYING.
1818fd37a7SXin LI    If not, write to the Free Software Foundation,
1918fd37a7SXin LI    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2018fd37a7SXin LI 
2118fd37a7SXin LI /* Written by Paul Eggert <eggert@twinsun.com>  */
2218fd37a7SXin LI 
2318fd37a7SXin LI #if HAVE_CONFIG_H
2418fd37a7SXin LI # include <config.h>
2518fd37a7SXin LI #endif
2618fd37a7SXin LI 
2718fd37a7SXin LI #include <stdbool.h>
2818fd37a7SXin LI 
2918fd37a7SXin LI #include <ctype.h>
3018fd37a7SXin LI #include <errno.h>
3118fd37a7SXin LI #ifndef errno
3218fd37a7SXin LI extern int errno;
3318fd37a7SXin LI #endif
3418fd37a7SXin LI #include <stddef.h>
3518fd37a7SXin LI #include <stdio.h>
3618fd37a7SXin LI #include <stdlib.h>
3718fd37a7SXin LI #include <string.h>
3818fd37a7SXin LI 
3918fd37a7SXin LI #include "exclude.h"
4018fd37a7SXin LI #include "fnmatch.h"
4118fd37a7SXin LI #include "unlocked-io.h"
4218fd37a7SXin LI #include "xalloc.h"
4318fd37a7SXin LI 
4418fd37a7SXin LI #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
4518fd37a7SXin LI # define IN_CTYPE_DOMAIN(c) true
4618fd37a7SXin LI #else
4718fd37a7SXin LI # define IN_CTYPE_DOMAIN(c) isascii (c)
4818fd37a7SXin LI #endif
4918fd37a7SXin LI 
5018fd37a7SXin LI static inline bool
is_space(unsigned char c)5118fd37a7SXin LI is_space (unsigned char c)
5218fd37a7SXin LI {
5318fd37a7SXin LI   return IN_CTYPE_DOMAIN (c) && isspace (c);
5418fd37a7SXin LI }
5518fd37a7SXin LI 
5618fd37a7SXin LI /* Verify a requirement at compile-time (unlike assert, which is runtime).  */
5718fd37a7SXin LI #define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
5818fd37a7SXin LI 
5918fd37a7SXin LI /* Non-GNU systems lack these options, so we don't need to check them.  */
6018fd37a7SXin LI #ifndef FNM_CASEFOLD
6118fd37a7SXin LI # define FNM_CASEFOLD 0
6218fd37a7SXin LI #endif
6318fd37a7SXin LI #ifndef FNM_LEADING_DIR
6418fd37a7SXin LI # define FNM_LEADING_DIR 0
6518fd37a7SXin LI #endif
6618fd37a7SXin LI 
6718fd37a7SXin LI verify (EXCLUDE_macros_do_not_collide_with_FNM_macros,
6818fd37a7SXin LI 	(((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
6918fd37a7SXin LI 	  & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
7018fd37a7SXin LI 	     | FNM_CASEFOLD))
7118fd37a7SXin LI 	 == 0));
7218fd37a7SXin LI 
7318fd37a7SXin LI /* An exclude pattern-options pair.  The options are fnmatch options
7418fd37a7SXin LI    ORed with EXCLUDE_* options.  */
7518fd37a7SXin LI 
7618fd37a7SXin LI struct patopts
7718fd37a7SXin LI   {
7818fd37a7SXin LI     char const *pattern;
7918fd37a7SXin LI     int options;
8018fd37a7SXin LI   };
8118fd37a7SXin LI 
8218fd37a7SXin LI /* An exclude list, of pattern-options pairs.  */
8318fd37a7SXin LI 
8418fd37a7SXin LI struct exclude
8518fd37a7SXin LI   {
8618fd37a7SXin LI     struct patopts *exclude;
8718fd37a7SXin LI     size_t exclude_alloc;
8818fd37a7SXin LI     size_t exclude_count;
8918fd37a7SXin LI   };
9018fd37a7SXin LI 
9118fd37a7SXin LI /* Return a newly allocated and empty exclude list.  */
9218fd37a7SXin LI 
9318fd37a7SXin LI struct exclude *
new_exclude(void)9418fd37a7SXin LI new_exclude (void)
9518fd37a7SXin LI {
9618fd37a7SXin LI   return xzalloc (sizeof *new_exclude ());
9718fd37a7SXin LI }
9818fd37a7SXin LI 
9918fd37a7SXin LI /* Free the storage associated with an exclude list.  */
10018fd37a7SXin LI 
10118fd37a7SXin LI void
free_exclude(struct exclude * ex)10218fd37a7SXin LI free_exclude (struct exclude *ex)
10318fd37a7SXin LI {
10418fd37a7SXin LI   free (ex->exclude);
10518fd37a7SXin LI   free (ex);
10618fd37a7SXin LI }
10718fd37a7SXin LI 
10818fd37a7SXin LI /* Return zero if PATTERN matches F, obeying OPTIONS, except that
10918fd37a7SXin LI    (unlike fnmatch) wildcards are disabled in PATTERN.  */
11018fd37a7SXin LI 
11118fd37a7SXin LI static int
fnmatch_no_wildcards(char const * pattern,char const * f,int options)11218fd37a7SXin LI fnmatch_no_wildcards (char const *pattern, char const *f, int options)
11318fd37a7SXin LI {
11418fd37a7SXin LI   if (! (options & FNM_LEADING_DIR))
11518fd37a7SXin LI     return ((options & FNM_CASEFOLD)
11618fd37a7SXin LI 	    ? strcasecmp (pattern, f)
11718fd37a7SXin LI 	    : strcmp (pattern, f));
11818fd37a7SXin LI   else
11918fd37a7SXin LI     {
12018fd37a7SXin LI       size_t patlen = strlen (pattern);
12118fd37a7SXin LI       int r = ((options & FNM_CASEFOLD)
12218fd37a7SXin LI 		? strncasecmp (pattern, f, patlen)
12318fd37a7SXin LI 		: strncmp (pattern, f, patlen));
12418fd37a7SXin LI       if (! r)
12518fd37a7SXin LI 	{
12618fd37a7SXin LI 	  r = f[patlen];
12718fd37a7SXin LI 	  if (r == '/')
12818fd37a7SXin LI 	    r = 0;
12918fd37a7SXin LI 	}
13018fd37a7SXin LI       return r;
13118fd37a7SXin LI     }
13218fd37a7SXin LI }
13318fd37a7SXin LI 
13418fd37a7SXin LI /* Return true if EX excludes F.  */
13518fd37a7SXin LI 
13618fd37a7SXin LI bool
excluded_filename(struct exclude const * ex,char const * f)13718fd37a7SXin LI excluded_filename (struct exclude const *ex, char const *f)
13818fd37a7SXin LI {
13918fd37a7SXin LI   size_t exclude_count = ex->exclude_count;
14018fd37a7SXin LI 
14118fd37a7SXin LI   /* If no options are given, the default is to include.  */
14218fd37a7SXin LI   if (exclude_count == 0)
14318fd37a7SXin LI     return false;
14418fd37a7SXin LI   else
14518fd37a7SXin LI     {
14618fd37a7SXin LI       struct patopts const *exclude = ex->exclude;
14718fd37a7SXin LI       size_t i;
14818fd37a7SXin LI 
14918fd37a7SXin LI       /* Otherwise, the default is the opposite of the first option.  */
15018fd37a7SXin LI       bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
15118fd37a7SXin LI 
15218fd37a7SXin LI       /* Scan through the options, seeing whether they change F from
15318fd37a7SXin LI 	 excluded to included or vice versa.  */
15418fd37a7SXin LI       for (i = 0;  i < exclude_count;  i++)
15518fd37a7SXin LI 	{
15618fd37a7SXin LI 	  char const *pattern = exclude[i].pattern;
15718fd37a7SXin LI 	  int options = exclude[i].options;
15818fd37a7SXin LI 	  if (excluded == !! (options & EXCLUDE_INCLUDE))
15918fd37a7SXin LI 	    {
16018fd37a7SXin LI 	      int (*matcher) (char const *, char const *, int) =
16118fd37a7SXin LI 		(options & EXCLUDE_WILDCARDS
16218fd37a7SXin LI 		 ? fnmatch
16318fd37a7SXin LI 		 : fnmatch_no_wildcards);
16418fd37a7SXin LI 	      bool matched = ((*matcher) (pattern, f, options) == 0);
16518fd37a7SXin LI 	      char const *p;
16618fd37a7SXin LI 
16718fd37a7SXin LI 	      if (! (options & EXCLUDE_ANCHORED))
16818fd37a7SXin LI 		for (p = f; *p && ! matched; p++)
16918fd37a7SXin LI 		  if (*p == '/' && p[1] != '/')
17018fd37a7SXin LI 		    matched = ((*matcher) (pattern, p + 1, options) == 0);
17118fd37a7SXin LI 
17218fd37a7SXin LI 	      excluded ^= matched;
17318fd37a7SXin LI 	    }
17418fd37a7SXin LI 	}
17518fd37a7SXin LI 
17618fd37a7SXin LI       return excluded;
17718fd37a7SXin LI     }
17818fd37a7SXin LI }
17918fd37a7SXin LI 
18018fd37a7SXin LI /* Append to EX the exclusion PATTERN with OPTIONS.  */
18118fd37a7SXin LI 
18218fd37a7SXin LI void
add_exclude(struct exclude * ex,char const * pattern,int options)18318fd37a7SXin LI add_exclude (struct exclude *ex, char const *pattern, int options)
18418fd37a7SXin LI {
18518fd37a7SXin LI   struct patopts *patopts;
18618fd37a7SXin LI 
18718fd37a7SXin LI   if (ex->exclude_count == ex->exclude_alloc)
18818fd37a7SXin LI     ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc,
18918fd37a7SXin LI 			      sizeof *ex->exclude);
19018fd37a7SXin LI 
19118fd37a7SXin LI   patopts = &ex->exclude[ex->exclude_count++];
19218fd37a7SXin LI   patopts->pattern = pattern;
19318fd37a7SXin LI   patopts->options = options;
19418fd37a7SXin LI }
19518fd37a7SXin LI 
19618fd37a7SXin LI /* Use ADD_FUNC to append to EX the patterns in FILENAME, each with
19718fd37a7SXin LI    OPTIONS.  LINE_END terminates each pattern in the file.  If
19818fd37a7SXin LI    LINE_END is a space character, ignore trailing spaces and empty
19918fd37a7SXin LI    lines in FILE.  Return -1 on failure, 0 on success.  */
20018fd37a7SXin LI 
20118fd37a7SXin LI int
add_exclude_file(void (* add_func)(struct exclude *,char const *,int),struct exclude * ex,char const * filename,int options,char line_end)20218fd37a7SXin LI add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
20318fd37a7SXin LI 		  struct exclude *ex, char const *filename, int options,
20418fd37a7SXin LI 		  char line_end)
20518fd37a7SXin LI {
20618fd37a7SXin LI   bool use_stdin = filename[0] == '-' && !filename[1];
20718fd37a7SXin LI   FILE *in;
20818fd37a7SXin LI   char *buf = NULL;
20918fd37a7SXin LI   char *p;
21018fd37a7SXin LI   char const *pattern;
21118fd37a7SXin LI   char const *lim;
21218fd37a7SXin LI   size_t buf_alloc = 0;
21318fd37a7SXin LI   size_t buf_count = 0;
21418fd37a7SXin LI   int c;
21518fd37a7SXin LI   int e = 0;
21618fd37a7SXin LI 
21718fd37a7SXin LI   if (use_stdin)
21818fd37a7SXin LI     in = stdin;
21918fd37a7SXin LI   else if (! (in = fopen (filename, "r")))
22018fd37a7SXin LI     return -1;
22118fd37a7SXin LI 
22218fd37a7SXin LI   while ((c = getc (in)) != EOF)
22318fd37a7SXin LI     {
22418fd37a7SXin LI       if (buf_count == buf_alloc)
22518fd37a7SXin LI 	buf = x2realloc (buf, &buf_alloc);
22618fd37a7SXin LI       buf[buf_count++] = c;
22718fd37a7SXin LI     }
22818fd37a7SXin LI 
22918fd37a7SXin LI   if (ferror (in))
23018fd37a7SXin LI     e = errno;
23118fd37a7SXin LI 
23218fd37a7SXin LI   if (!use_stdin && fclose (in) != 0)
23318fd37a7SXin LI     e = errno;
23418fd37a7SXin LI 
23518fd37a7SXin LI   buf = xrealloc (buf, buf_count + 1);
23618fd37a7SXin LI   buf[buf_count] = line_end;
23718fd37a7SXin LI   lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
23818fd37a7SXin LI   pattern = buf;
23918fd37a7SXin LI 
24018fd37a7SXin LI   for (p = buf; p < lim; p++)
24118fd37a7SXin LI     if (*p == line_end)
24218fd37a7SXin LI       {
24318fd37a7SXin LI 	char *pattern_end = p;
24418fd37a7SXin LI 
24518fd37a7SXin LI 	if (is_space (line_end))
24618fd37a7SXin LI 	  {
24718fd37a7SXin LI 	    for (; ; pattern_end--)
24818fd37a7SXin LI 	      if (pattern_end == pattern)
24918fd37a7SXin LI 		goto next_pattern;
25018fd37a7SXin LI 	      else if (! is_space (pattern_end[-1]))
25118fd37a7SXin LI 		break;
25218fd37a7SXin LI 	  }
25318fd37a7SXin LI 
25418fd37a7SXin LI 	*pattern_end = '\0';
25518fd37a7SXin LI 	(*add_func) (ex, pattern, options);
25618fd37a7SXin LI 
25718fd37a7SXin LI       next_pattern:
25818fd37a7SXin LI 	pattern = p + 1;
25918fd37a7SXin LI       }
26018fd37a7SXin LI 
26118fd37a7SXin LI   errno = e;
26218fd37a7SXin LI   return e ? -1 : 0;
26318fd37a7SXin LI }
264