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