1855caec6SPeter Avalos /* exclude.c -- exclude file names
2855caec6SPeter Avalos
3*6ea1f93eSDaniel Fojt Copyright (C) 1992-1994, 1997, 1999-2007, 2009-2018 Free Software
4008e37b6SJohn Marino Foundation, Inc.
5855caec6SPeter Avalos
644b87433SJohn Marino This program is free software: you can redistribute it and/or modify
7855caec6SPeter Avalos it under the terms of the GNU General Public License as published by
844b87433SJohn Marino the Free Software Foundation; either version 3 of the License, or
944b87433SJohn Marino (at your option) any later version.
10855caec6SPeter Avalos
11855caec6SPeter Avalos This program is distributed in the hope that it will be useful,
12855caec6SPeter Avalos but WITHOUT ANY WARRANTY; without even the implied warranty of
13855caec6SPeter Avalos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14855caec6SPeter Avalos GNU General Public License for more details.
15855caec6SPeter Avalos
16855caec6SPeter Avalos You should have received a copy of the GNU General Public License
17*6ea1f93eSDaniel Fojt along with this program. If not, see <https://www.gnu.org/licenses/>. */
18855caec6SPeter Avalos
1944b87433SJohn Marino /* Written by Paul Eggert <eggert@twinsun.com>
2044b87433SJohn Marino and Sergey Poznyakoff <gray@gnu.org>.
2144b87433SJohn Marino Thanks to Phil Proudman <phil@proudman51.freeserve.co.uk>
2244b87433SJohn Marino for improvement suggestions. */
23855caec6SPeter Avalos
24855caec6SPeter Avalos #include <config.h>
25855caec6SPeter Avalos
26855caec6SPeter Avalos #include <stdbool.h>
27855caec6SPeter Avalos
28855caec6SPeter Avalos #include <ctype.h>
29855caec6SPeter Avalos #include <errno.h>
30855caec6SPeter Avalos #include <stddef.h>
31855caec6SPeter Avalos #include <stdio.h>
32855caec6SPeter Avalos #include <stdlib.h>
33855caec6SPeter Avalos #include <string.h>
3444b87433SJohn Marino #include <wctype.h>
35*6ea1f93eSDaniel Fojt #include <regex.h>
36855caec6SPeter Avalos
37855caec6SPeter Avalos #include "exclude.h"
3844b87433SJohn Marino #include "hash.h"
3944b87433SJohn Marino #include "mbuiter.h"
40855caec6SPeter Avalos #include "fnmatch.h"
41855caec6SPeter Avalos #include "xalloc.h"
4244b87433SJohn Marino #include "verify.h"
43*6ea1f93eSDaniel Fojt #include "filename.h"
44855caec6SPeter Avalos
4544b87433SJohn Marino #if USE_UNLOCKED_IO
4644b87433SJohn Marino # include "unlocked-io.h"
47855caec6SPeter Avalos #endif
48855caec6SPeter Avalos
49855caec6SPeter Avalos /* Non-GNU systems lack these options, so we don't need to check them. */
50855caec6SPeter Avalos #ifndef FNM_CASEFOLD
51855caec6SPeter Avalos # define FNM_CASEFOLD 0
52855caec6SPeter Avalos #endif
5344b87433SJohn Marino #ifndef FNM_EXTMATCH
5444b87433SJohn Marino # define FNM_EXTMATCH 0
5544b87433SJohn Marino #endif
56855caec6SPeter Avalos #ifndef FNM_LEADING_DIR
57855caec6SPeter Avalos # define FNM_LEADING_DIR 0
58855caec6SPeter Avalos #endif
59855caec6SPeter Avalos
6044b87433SJohn Marino verify (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
61855caec6SPeter Avalos & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
6244b87433SJohn Marino | FNM_CASEFOLD | FNM_EXTMATCH))
6344b87433SJohn Marino == 0);
6444b87433SJohn Marino
6544b87433SJohn Marino
6644b87433SJohn Marino /* Exclusion patterns are grouped into a singly-linked list of
6744b87433SJohn Marino "exclusion segments". Each segment represents a set of patterns
6844b87433SJohn Marino that can be matches using the same algorithm. Non-wildcard
6944b87433SJohn Marino patterns are kept in hash tables, to speed up searches. Wildcard
7044b87433SJohn Marino patterns are stored as arrays of patterns. */
7144b87433SJohn Marino
72855caec6SPeter Avalos
73855caec6SPeter Avalos /* An exclude pattern-options pair. The options are fnmatch options
74855caec6SPeter Avalos ORed with EXCLUDE_* options. */
75855caec6SPeter Avalos
76855caec6SPeter Avalos struct patopts
77855caec6SPeter Avalos {
78855caec6SPeter Avalos int options;
79*6ea1f93eSDaniel Fojt union
80*6ea1f93eSDaniel Fojt {
81*6ea1f93eSDaniel Fojt char const *pattern;
82*6ea1f93eSDaniel Fojt regex_t re;
83*6ea1f93eSDaniel Fojt } v;
84855caec6SPeter Avalos };
85855caec6SPeter Avalos
8644b87433SJohn Marino /* An array of pattern-options pairs. */
87855caec6SPeter Avalos
8844b87433SJohn Marino struct exclude_pattern
89855caec6SPeter Avalos {
90855caec6SPeter Avalos struct patopts *exclude;
91855caec6SPeter Avalos size_t exclude_alloc;
92855caec6SPeter Avalos size_t exclude_count;
93855caec6SPeter Avalos };
94855caec6SPeter Avalos
9544b87433SJohn Marino enum exclude_type
9644b87433SJohn Marino {
9744b87433SJohn Marino exclude_hash, /* a hash table of excluded names */
9844b87433SJohn Marino exclude_pattern /* an array of exclude patterns */
9944b87433SJohn Marino };
10044b87433SJohn Marino
10144b87433SJohn Marino struct exclude_segment
10244b87433SJohn Marino {
10344b87433SJohn Marino struct exclude_segment *next; /* next segment in list */
10444b87433SJohn Marino enum exclude_type type; /* type of this segment */
10544b87433SJohn Marino int options; /* common options for this segment */
10644b87433SJohn Marino union
10744b87433SJohn Marino {
10844b87433SJohn Marino Hash_table *table; /* for type == exclude_hash */
10944b87433SJohn Marino struct exclude_pattern pat; /* for type == exclude_pattern */
11044b87433SJohn Marino } v;
11144b87433SJohn Marino };
11244b87433SJohn Marino
113*6ea1f93eSDaniel Fojt struct pattern_buffer
114*6ea1f93eSDaniel Fojt {
115*6ea1f93eSDaniel Fojt struct pattern_buffer *next;
116*6ea1f93eSDaniel Fojt char *base;
117*6ea1f93eSDaniel Fojt };
118*6ea1f93eSDaniel Fojt
1194536c563SJohn Marino /* The exclude structure keeps a singly-linked list of exclude segments,
1204536c563SJohn Marino maintained in reverse order. */
12144b87433SJohn Marino struct exclude
12244b87433SJohn Marino {
1234536c563SJohn Marino struct exclude_segment *head;
124*6ea1f93eSDaniel Fojt struct pattern_buffer *patbuf;
12544b87433SJohn Marino };
12644b87433SJohn Marino
127*6ea1f93eSDaniel Fojt /* Register BUF in the pattern buffer list of EX. ADD_FUNC (see
128*6ea1f93eSDaniel Fojt add_exclude_file and add_exclude_fp below) can use this function
129*6ea1f93eSDaniel Fojt if it modifies the pattern, to ensure the allocated memory will be
130*6ea1f93eSDaniel Fojt properly reclaimed upon calling free_exclude. */
131*6ea1f93eSDaniel Fojt void
exclude_add_pattern_buffer(struct exclude * ex,char * buf)132*6ea1f93eSDaniel Fojt exclude_add_pattern_buffer (struct exclude *ex, char *buf)
133*6ea1f93eSDaniel Fojt {
134*6ea1f93eSDaniel Fojt struct pattern_buffer *pbuf = xmalloc (sizeof *pbuf);
135*6ea1f93eSDaniel Fojt pbuf->base = buf;
136*6ea1f93eSDaniel Fojt pbuf->next = ex->patbuf;
137*6ea1f93eSDaniel Fojt ex->patbuf = pbuf;
138*6ea1f93eSDaniel Fojt }
139*6ea1f93eSDaniel Fojt
1404536c563SJohn Marino /* Return true if STR has or may have wildcards, when matched with OPTIONS.
1414536c563SJohn Marino Return false if STR definitely does not have wildcards. */
14244b87433SJohn Marino bool
fnmatch_pattern_has_wildcards(const char * str,int options)14344b87433SJohn Marino fnmatch_pattern_has_wildcards (const char *str, int options)
14444b87433SJohn Marino {
1454536c563SJohn Marino while (1)
14644b87433SJohn Marino {
1474536c563SJohn Marino switch (*str++)
1484536c563SJohn Marino {
149*6ea1f93eSDaniel Fojt case '.':
150*6ea1f93eSDaniel Fojt case '{':
151*6ea1f93eSDaniel Fojt case '}':
152*6ea1f93eSDaniel Fojt case '(':
153*6ea1f93eSDaniel Fojt case ')':
154*6ea1f93eSDaniel Fojt if (options & EXCLUDE_REGEX)
155*6ea1f93eSDaniel Fojt return true;
156*6ea1f93eSDaniel Fojt break;
157*6ea1f93eSDaniel Fojt
1584536c563SJohn Marino case '\\':
159*6ea1f93eSDaniel Fojt if (options & EXCLUDE_REGEX)
160*6ea1f93eSDaniel Fojt continue;
161*6ea1f93eSDaniel Fojt else
1624536c563SJohn Marino str += ! (options & FNM_NOESCAPE) && *str;
16344b87433SJohn Marino break;
1644536c563SJohn Marino
1654536c563SJohn Marino case '+': case '@': case '!':
1664536c563SJohn Marino if (options & FNM_EXTMATCH && *str == '(')
16744b87433SJohn Marino return true;
1684536c563SJohn Marino break;
1694536c563SJohn Marino
1704536c563SJohn Marino case '?': case '*': case '[':
1714536c563SJohn Marino return true;
1724536c563SJohn Marino
1734536c563SJohn Marino case '\0':
17444b87433SJohn Marino return false;
17544b87433SJohn Marino }
1764536c563SJohn Marino }
1774536c563SJohn Marino }
17844b87433SJohn Marino
179008e37b6SJohn Marino static void
unescape_pattern(char * str)180008e37b6SJohn Marino unescape_pattern (char *str)
181008e37b6SJohn Marino {
1824536c563SJohn Marino char const *q = str;
183008e37b6SJohn Marino do
1844536c563SJohn Marino q += *q == '\\' && q[1];
185008e37b6SJohn Marino while ((*str++ = *q++));
186008e37b6SJohn Marino }
187008e37b6SJohn Marino
188855caec6SPeter Avalos /* Return a newly allocated and empty exclude list. */
189855caec6SPeter Avalos
190855caec6SPeter Avalos struct exclude *
new_exclude(void)191855caec6SPeter Avalos new_exclude (void)
192855caec6SPeter Avalos {
193855caec6SPeter Avalos return xzalloc (sizeof *new_exclude ());
194855caec6SPeter Avalos }
195855caec6SPeter Avalos
19644b87433SJohn Marino /* Calculate the hash of string. */
19744b87433SJohn Marino static size_t
string_hasher(void const * data,size_t n_buckets)19844b87433SJohn Marino string_hasher (void const *data, size_t n_buckets)
19944b87433SJohn Marino {
20044b87433SJohn Marino char const *p = data;
20144b87433SJohn Marino return hash_string (p, n_buckets);
20244b87433SJohn Marino }
203855caec6SPeter Avalos
20444b87433SJohn Marino /* Ditto, for case-insensitive hashes */
20544b87433SJohn Marino static size_t
string_hasher_ci(void const * data,size_t n_buckets)20644b87433SJohn Marino string_hasher_ci (void const *data, size_t n_buckets)
20744b87433SJohn Marino {
20844b87433SJohn Marino char const *p = data;
20944b87433SJohn Marino mbui_iterator_t iter;
21044b87433SJohn Marino size_t value = 0;
21144b87433SJohn Marino
21244b87433SJohn Marino for (mbui_init (iter, p); mbui_avail (iter); mbui_advance (iter))
21344b87433SJohn Marino {
21444b87433SJohn Marino mbchar_t m = mbui_cur (iter);
21544b87433SJohn Marino wchar_t wc;
21644b87433SJohn Marino
21744b87433SJohn Marino if (m.wc_valid)
21844b87433SJohn Marino wc = towlower (m.wc);
21944b87433SJohn Marino else
22044b87433SJohn Marino wc = *m.ptr;
22144b87433SJohn Marino
22244b87433SJohn Marino value = (value * 31 + wc) % n_buckets;
22344b87433SJohn Marino }
22444b87433SJohn Marino
22544b87433SJohn Marino return value;
22644b87433SJohn Marino }
22744b87433SJohn Marino
22844b87433SJohn Marino /* compare two strings for equality */
22944b87433SJohn Marino static bool
string_compare(void const * data1,void const * data2)23044b87433SJohn Marino string_compare (void const *data1, void const *data2)
23144b87433SJohn Marino {
23244b87433SJohn Marino char const *p1 = data1;
23344b87433SJohn Marino char const *p2 = data2;
23444b87433SJohn Marino return strcmp (p1, p2) == 0;
23544b87433SJohn Marino }
23644b87433SJohn Marino
23744b87433SJohn Marino /* compare two strings for equality, case-insensitive */
23844b87433SJohn Marino static bool
string_compare_ci(void const * data1,void const * data2)23944b87433SJohn Marino string_compare_ci (void const *data1, void const *data2)
24044b87433SJohn Marino {
24144b87433SJohn Marino char const *p1 = data1;
24244b87433SJohn Marino char const *p2 = data2;
24344b87433SJohn Marino return mbscasecmp (p1, p2) == 0;
24444b87433SJohn Marino }
24544b87433SJohn Marino
24644b87433SJohn Marino static void
string_free(void * data)24744b87433SJohn Marino string_free (void *data)
24844b87433SJohn Marino {
24944b87433SJohn Marino free (data);
25044b87433SJohn Marino }
25144b87433SJohn Marino
25244b87433SJohn Marino /* Create new exclude segment of given TYPE and OPTIONS, and attach it
2534536c563SJohn Marino to the head of EX. */
2544536c563SJohn Marino static void
new_exclude_segment(struct exclude * ex,enum exclude_type type,int options)25544b87433SJohn Marino new_exclude_segment (struct exclude *ex, enum exclude_type type, int options)
25644b87433SJohn Marino {
25744b87433SJohn Marino struct exclude_segment *sp = xzalloc (sizeof (struct exclude_segment));
25844b87433SJohn Marino sp->type = type;
25944b87433SJohn Marino sp->options = options;
26044b87433SJohn Marino switch (type)
26144b87433SJohn Marino {
26244b87433SJohn Marino case exclude_pattern:
26344b87433SJohn Marino break;
26444b87433SJohn Marino
26544b87433SJohn Marino case exclude_hash:
26644b87433SJohn Marino sp->v.table = hash_initialize (0, NULL,
26744b87433SJohn Marino (options & FNM_CASEFOLD) ?
26844b87433SJohn Marino string_hasher_ci
26944b87433SJohn Marino : string_hasher,
27044b87433SJohn Marino (options & FNM_CASEFOLD) ?
27144b87433SJohn Marino string_compare_ci
27244b87433SJohn Marino : string_compare,
27344b87433SJohn Marino string_free);
27444b87433SJohn Marino break;
27544b87433SJohn Marino }
2764536c563SJohn Marino sp->next = ex->head;
27744b87433SJohn Marino ex->head = sp;
27844b87433SJohn Marino }
27944b87433SJohn Marino
28044b87433SJohn Marino /* Free a single exclude segment */
28144b87433SJohn Marino static void
free_exclude_segment(struct exclude_segment * seg)28244b87433SJohn Marino free_exclude_segment (struct exclude_segment *seg)
28344b87433SJohn Marino {
284*6ea1f93eSDaniel Fojt size_t i;
285*6ea1f93eSDaniel Fojt
28644b87433SJohn Marino switch (seg->type)
28744b87433SJohn Marino {
28844b87433SJohn Marino case exclude_pattern:
289*6ea1f93eSDaniel Fojt for (i = 0; i < seg->v.pat.exclude_count; i++)
290*6ea1f93eSDaniel Fojt {
291*6ea1f93eSDaniel Fojt if (seg->v.pat.exclude[i].options & EXCLUDE_REGEX)
292*6ea1f93eSDaniel Fojt regfree (&seg->v.pat.exclude[i].v.re);
293*6ea1f93eSDaniel Fojt }
29444b87433SJohn Marino free (seg->v.pat.exclude);
29544b87433SJohn Marino break;
29644b87433SJohn Marino
29744b87433SJohn Marino case exclude_hash:
29844b87433SJohn Marino hash_free (seg->v.table);
29944b87433SJohn Marino break;
30044b87433SJohn Marino }
30144b87433SJohn Marino free (seg);
30244b87433SJohn Marino }
30344b87433SJohn Marino
30444b87433SJohn Marino /* Free the storage associated with an exclude list. */
305855caec6SPeter Avalos void
free_exclude(struct exclude * ex)306855caec6SPeter Avalos free_exclude (struct exclude *ex)
307855caec6SPeter Avalos {
30844b87433SJohn Marino struct exclude_segment *seg;
309*6ea1f93eSDaniel Fojt struct pattern_buffer *pbuf;
310*6ea1f93eSDaniel Fojt
31144b87433SJohn Marino for (seg = ex->head; seg; )
31244b87433SJohn Marino {
31344b87433SJohn Marino struct exclude_segment *next = seg->next;
31444b87433SJohn Marino free_exclude_segment (seg);
31544b87433SJohn Marino seg = next;
31644b87433SJohn Marino }
317*6ea1f93eSDaniel Fojt
318*6ea1f93eSDaniel Fojt for (pbuf = ex->patbuf; pbuf; )
319*6ea1f93eSDaniel Fojt {
320*6ea1f93eSDaniel Fojt struct pattern_buffer *next = pbuf->next;
321*6ea1f93eSDaniel Fojt free (pbuf->base);
322*6ea1f93eSDaniel Fojt free (pbuf);
323*6ea1f93eSDaniel Fojt pbuf = next;
324*6ea1f93eSDaniel Fojt }
325*6ea1f93eSDaniel Fojt
326855caec6SPeter Avalos free (ex);
327855caec6SPeter Avalos }
328855caec6SPeter Avalos
329855caec6SPeter Avalos /* Return zero if PATTERN matches F, obeying OPTIONS, except that
330855caec6SPeter Avalos (unlike fnmatch) wildcards are disabled in PATTERN. */
331855caec6SPeter Avalos
332855caec6SPeter Avalos static int
fnmatch_no_wildcards(char const * pattern,char const * f,int options)333855caec6SPeter Avalos fnmatch_no_wildcards (char const *pattern, char const *f, int options)
334855caec6SPeter Avalos {
335855caec6SPeter Avalos if (! (options & FNM_LEADING_DIR))
336855caec6SPeter Avalos return ((options & FNM_CASEFOLD)
33744b87433SJohn Marino ? mbscasecmp (pattern, f)
338855caec6SPeter Avalos : strcmp (pattern, f));
33944b87433SJohn Marino else if (! (options & FNM_CASEFOLD))
340855caec6SPeter Avalos {
341855caec6SPeter Avalos size_t patlen = strlen (pattern);
34244b87433SJohn Marino int r = strncmp (pattern, f, patlen);
343855caec6SPeter Avalos if (! r)
344855caec6SPeter Avalos {
345855caec6SPeter Avalos r = f[patlen];
346855caec6SPeter Avalos if (r == '/')
347855caec6SPeter Avalos r = 0;
348855caec6SPeter Avalos }
349855caec6SPeter Avalos return r;
350855caec6SPeter Avalos }
351855caec6SPeter Avalos else
352855caec6SPeter Avalos {
35344b87433SJohn Marino /* Walk through a copy of F, seeing whether P matches any prefix
35444b87433SJohn Marino of F.
355855caec6SPeter Avalos
35644b87433SJohn Marino FIXME: This is an O(N**2) algorithm; it should be O(N).
35744b87433SJohn Marino Also, the copy should not be necessary. However, fixing this
35844b87433SJohn Marino will probably involve a change to the mbs* API. */
359855caec6SPeter Avalos
36044b87433SJohn Marino char *fcopy = xstrdup (f);
36144b87433SJohn Marino char *p;
36244b87433SJohn Marino int r;
36344b87433SJohn Marino for (p = fcopy; ; *p++ = '/')
364855caec6SPeter Avalos {
36544b87433SJohn Marino p = strchr (p, '/');
36644b87433SJohn Marino if (p)
36744b87433SJohn Marino *p = '\0';
36844b87433SJohn Marino r = mbscasecmp (pattern, fcopy);
36944b87433SJohn Marino if (!p || r <= 0)
37044b87433SJohn Marino break;
37144b87433SJohn Marino }
37244b87433SJohn Marino free (fcopy);
37344b87433SJohn Marino return r;
37444b87433SJohn Marino }
37544b87433SJohn Marino }
37644b87433SJohn Marino
37744b87433SJohn Marino bool
exclude_fnmatch(char const * pattern,char const * f,int options)37844b87433SJohn Marino exclude_fnmatch (char const *pattern, char const *f, int options)
379855caec6SPeter Avalos {
380855caec6SPeter Avalos int (*matcher) (char const *, char const *, int) =
381855caec6SPeter Avalos (options & EXCLUDE_WILDCARDS
382855caec6SPeter Avalos ? fnmatch
383855caec6SPeter Avalos : fnmatch_no_wildcards);
384855caec6SPeter Avalos bool matched = ((*matcher) (pattern, f, options) == 0);
385855caec6SPeter Avalos char const *p;
386855caec6SPeter Avalos
387855caec6SPeter Avalos if (! (options & EXCLUDE_ANCHORED))
388855caec6SPeter Avalos for (p = f; *p && ! matched; p++)
389855caec6SPeter Avalos if (*p == '/' && p[1] != '/')
390855caec6SPeter Avalos matched = ((*matcher) (pattern, p + 1, options) == 0);
391855caec6SPeter Avalos
39244b87433SJohn Marino return matched;
393855caec6SPeter Avalos }
394855caec6SPeter Avalos
395*6ea1f93eSDaniel Fojt static bool
exclude_patopts(struct patopts const * opts,char const * f)396*6ea1f93eSDaniel Fojt exclude_patopts (struct patopts const *opts, char const *f)
397*6ea1f93eSDaniel Fojt {
398*6ea1f93eSDaniel Fojt int options = opts->options;
399*6ea1f93eSDaniel Fojt
400*6ea1f93eSDaniel Fojt return (options & EXCLUDE_REGEX)
401*6ea1f93eSDaniel Fojt ? regexec (&opts->v.re, f, 0, NULL, 0) == 0
402*6ea1f93eSDaniel Fojt : exclude_fnmatch (opts->v.pattern, f, options);
403*6ea1f93eSDaniel Fojt }
404*6ea1f93eSDaniel Fojt
4054536c563SJohn Marino /* Return true if the exclude_pattern segment SEG matches F. */
40644b87433SJohn Marino
40744b87433SJohn Marino static bool
file_pattern_matches(struct exclude_segment const * seg,char const * f)4084536c563SJohn Marino file_pattern_matches (struct exclude_segment const *seg, char const *f)
40944b87433SJohn Marino {
41044b87433SJohn Marino size_t exclude_count = seg->v.pat.exclude_count;
41144b87433SJohn Marino struct patopts const *exclude = seg->v.pat.exclude;
41244b87433SJohn Marino size_t i;
41344b87433SJohn Marino
41444b87433SJohn Marino for (i = 0; i < exclude_count; i++)
41544b87433SJohn Marino {
416*6ea1f93eSDaniel Fojt if (exclude_patopts (exclude + i, f))
4174536c563SJohn Marino return true;
41844b87433SJohn Marino }
4194536c563SJohn Marino return false;
420855caec6SPeter Avalos }
42144b87433SJohn Marino
4224536c563SJohn Marino /* Return true if the exclude_hash segment SEG matches F.
42344b87433SJohn Marino BUFFER is an auxiliary storage of the same length as F (with nul
42444b87433SJohn Marino terminator included) */
42544b87433SJohn Marino static bool
file_name_matches(struct exclude_segment const * seg,char const * f,char * buffer)4264536c563SJohn Marino file_name_matches (struct exclude_segment const *seg, char const *f,
42744b87433SJohn Marino char *buffer)
42844b87433SJohn Marino {
42944b87433SJohn Marino int options = seg->options;
43044b87433SJohn Marino Hash_table *table = seg->v.table;
43144b87433SJohn Marino
43244b87433SJohn Marino do
43344b87433SJohn Marino {
43444b87433SJohn Marino /* initialize the pattern */
43544b87433SJohn Marino strcpy (buffer, f);
43644b87433SJohn Marino
43744b87433SJohn Marino while (1)
43844b87433SJohn Marino {
43944b87433SJohn Marino if (hash_lookup (table, buffer))
4404536c563SJohn Marino return true;
44144b87433SJohn Marino if (options & FNM_LEADING_DIR)
44244b87433SJohn Marino {
44344b87433SJohn Marino char *p = strrchr (buffer, '/');
44444b87433SJohn Marino if (p)
44544b87433SJohn Marino {
44644b87433SJohn Marino *p = 0;
44744b87433SJohn Marino continue;
44844b87433SJohn Marino }
44944b87433SJohn Marino }
45044b87433SJohn Marino break;
45144b87433SJohn Marino }
45244b87433SJohn Marino
45344b87433SJohn Marino if (!(options & EXCLUDE_ANCHORED))
45444b87433SJohn Marino {
45544b87433SJohn Marino f = strchr (f, '/');
45644b87433SJohn Marino if (f)
45744b87433SJohn Marino f++;
45844b87433SJohn Marino }
45944b87433SJohn Marino else
46044b87433SJohn Marino break;
46144b87433SJohn Marino }
46244b87433SJohn Marino while (f);
4634536c563SJohn Marino
4644536c563SJohn Marino return false;
46544b87433SJohn Marino }
46644b87433SJohn Marino
46744b87433SJohn Marino /* Return true if EX excludes F. */
46844b87433SJohn Marino
46944b87433SJohn Marino bool
excluded_file_name(struct exclude const * ex,char const * f)47044b87433SJohn Marino excluded_file_name (struct exclude const *ex, char const *f)
47144b87433SJohn Marino {
47244b87433SJohn Marino struct exclude_segment *seg;
4734536c563SJohn Marino bool invert = false;
47444b87433SJohn Marino char *filename = NULL;
47544b87433SJohn Marino
47644b87433SJohn Marino /* If no patterns are given, the default is to include. */
47744b87433SJohn Marino if (!ex->head)
47844b87433SJohn Marino return false;
47944b87433SJohn Marino
4804536c563SJohn Marino /* Scan through the segments, reporting the status of the first match.
4814536c563SJohn Marino The segments are in reverse order, so this reports the status of
4824536c563SJohn Marino the last match in the original option list. */
4834536c563SJohn Marino for (seg = ex->head; ; seg = seg->next)
48444b87433SJohn Marino {
4854536c563SJohn Marino if (seg->type == exclude_hash)
48644b87433SJohn Marino {
48744b87433SJohn Marino if (!filename)
48844b87433SJohn Marino filename = xmalloc (strlen (f) + 1);
4894536c563SJohn Marino if (file_name_matches (seg, f, filename))
49044b87433SJohn Marino break;
49144b87433SJohn Marino }
4924536c563SJohn Marino else
49344b87433SJohn Marino {
4944536c563SJohn Marino if (file_pattern_matches (seg, f))
4954536c563SJohn Marino break;
4964536c563SJohn Marino }
4974536c563SJohn Marino
4984536c563SJohn Marino if (! seg->next)
4994536c563SJohn Marino {
5004536c563SJohn Marino /* If patterns are given but none match, the default is the
5014536c563SJohn Marino opposite of the last segment (i.e., the first in the
5024536c563SJohn Marino original option list). For example, in the command
5034536c563SJohn Marino 'grep -r --exclude="a*" --include="*b" pat dir', the
5044536c563SJohn Marino first option is --exclude so any file name matching
5054536c563SJohn Marino neither a* nor *b is included. */
5064536c563SJohn Marino invert = true;
50744b87433SJohn Marino break;
50844b87433SJohn Marino }
50944b87433SJohn Marino }
5104536c563SJohn Marino
51144b87433SJohn Marino free (filename);
5124536c563SJohn Marino return invert ^ ! (seg->options & EXCLUDE_INCLUDE);
513855caec6SPeter Avalos }
514855caec6SPeter Avalos
515855caec6SPeter Avalos /* Append to EX the exclusion PATTERN with OPTIONS. */
516855caec6SPeter Avalos
517855caec6SPeter Avalos void
add_exclude(struct exclude * ex,char const * pattern,int options)518855caec6SPeter Avalos add_exclude (struct exclude *ex, char const *pattern, int options)
519855caec6SPeter Avalos {
52044b87433SJohn Marino struct exclude_segment *seg;
52144b87433SJohn Marino struct exclude_pattern *pat;
522855caec6SPeter Avalos struct patopts *patopts;
523855caec6SPeter Avalos
524*6ea1f93eSDaniel Fojt if ((options & (EXCLUDE_REGEX|EXCLUDE_WILDCARDS))
525*6ea1f93eSDaniel Fojt && fnmatch_pattern_has_wildcards (pattern, options))
526*6ea1f93eSDaniel Fojt {
5274536c563SJohn Marino if (! (ex->head && ex->head->type == exclude_pattern
5284536c563SJohn Marino && ((ex->head->options & EXCLUDE_INCLUDE)
5294536c563SJohn Marino == (options & EXCLUDE_INCLUDE))))
5304536c563SJohn Marino new_exclude_segment (ex, exclude_pattern, options);
531*6ea1f93eSDaniel Fojt
5324536c563SJohn Marino seg = ex->head;
533855caec6SPeter Avalos
53444b87433SJohn Marino pat = &seg->v.pat;
53544b87433SJohn Marino if (pat->exclude_count == pat->exclude_alloc)
53644b87433SJohn Marino pat->exclude = x2nrealloc (pat->exclude, &pat->exclude_alloc,
53744b87433SJohn Marino sizeof *pat->exclude);
53844b87433SJohn Marino patopts = &pat->exclude[pat->exclude_count++];
539*6ea1f93eSDaniel Fojt
540855caec6SPeter Avalos patopts->options = options;
541*6ea1f93eSDaniel Fojt if (options & EXCLUDE_REGEX)
542*6ea1f93eSDaniel Fojt {
543*6ea1f93eSDaniel Fojt int rc;
544*6ea1f93eSDaniel Fojt int cflags = REG_NOSUB|REG_EXTENDED|
545*6ea1f93eSDaniel Fojt ((options & FNM_CASEFOLD) ? REG_ICASE : 0);
546*6ea1f93eSDaniel Fojt
547*6ea1f93eSDaniel Fojt if (options & FNM_LEADING_DIR)
548*6ea1f93eSDaniel Fojt {
549*6ea1f93eSDaniel Fojt char *tmp;
550*6ea1f93eSDaniel Fojt size_t len = strlen (pattern);
551*6ea1f93eSDaniel Fojt
552*6ea1f93eSDaniel Fojt while (len > 0 && ISSLASH (pattern[len-1]))
553*6ea1f93eSDaniel Fojt --len;
554*6ea1f93eSDaniel Fojt
555*6ea1f93eSDaniel Fojt if (len == 0)
556*6ea1f93eSDaniel Fojt rc = 1;
557*6ea1f93eSDaniel Fojt else
558*6ea1f93eSDaniel Fojt {
559*6ea1f93eSDaniel Fojt tmp = xmalloc (len + 7);
560*6ea1f93eSDaniel Fojt memcpy (tmp, pattern, len);
561*6ea1f93eSDaniel Fojt strcpy (tmp + len, "(/.*)?");
562*6ea1f93eSDaniel Fojt rc = regcomp (&patopts->v.re, tmp, cflags);
563*6ea1f93eSDaniel Fojt free (tmp);
564*6ea1f93eSDaniel Fojt }
565*6ea1f93eSDaniel Fojt }
566*6ea1f93eSDaniel Fojt else
567*6ea1f93eSDaniel Fojt rc = regcomp (&patopts->v.re, pattern, cflags);
568*6ea1f93eSDaniel Fojt
569*6ea1f93eSDaniel Fojt if (rc)
570*6ea1f93eSDaniel Fojt {
571*6ea1f93eSDaniel Fojt pat->exclude_count--;
572*6ea1f93eSDaniel Fojt return;
573*6ea1f93eSDaniel Fojt }
574*6ea1f93eSDaniel Fojt }
575*6ea1f93eSDaniel Fojt else
576*6ea1f93eSDaniel Fojt {
577*6ea1f93eSDaniel Fojt if (options & EXCLUDE_ALLOC)
578*6ea1f93eSDaniel Fojt {
579*6ea1f93eSDaniel Fojt pattern = xstrdup (pattern);
580*6ea1f93eSDaniel Fojt exclude_add_pattern_buffer (ex, (char*) pattern);
581*6ea1f93eSDaniel Fojt }
582*6ea1f93eSDaniel Fojt patopts->v.pattern = pattern;
583*6ea1f93eSDaniel Fojt }
584855caec6SPeter Avalos }
58544b87433SJohn Marino else
58644b87433SJohn Marino {
58744b87433SJohn Marino char *str, *p;
5884536c563SJohn Marino int exclude_hash_flags = (EXCLUDE_INCLUDE | EXCLUDE_ANCHORED
5894536c563SJohn Marino | FNM_LEADING_DIR | FNM_CASEFOLD);
5904536c563SJohn Marino if (! (ex->head && ex->head->type == exclude_hash
5914536c563SJohn Marino && ((ex->head->options & exclude_hash_flags)
5924536c563SJohn Marino == (options & exclude_hash_flags))))
5934536c563SJohn Marino new_exclude_segment (ex, exclude_hash, options);
5944536c563SJohn Marino seg = ex->head;
595855caec6SPeter Avalos
59644b87433SJohn Marino str = xstrdup (pattern);
5974536c563SJohn Marino if ((options & (EXCLUDE_WILDCARDS | FNM_NOESCAPE)) == EXCLUDE_WILDCARDS)
598008e37b6SJohn Marino unescape_pattern (str);
59944b87433SJohn Marino p = hash_insert (seg->v.table, str);
60044b87433SJohn Marino if (p != str)
60144b87433SJohn Marino free (str);
60244b87433SJohn Marino }
60344b87433SJohn Marino }
60444b87433SJohn Marino
60544b87433SJohn Marino /* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with
606855caec6SPeter Avalos OPTIONS. LINE_END terminates each pattern in the file. If
607855caec6SPeter Avalos LINE_END is a space character, ignore trailing spaces and empty
608*6ea1f93eSDaniel Fojt lines in FP. Return -1 on failure, 0 on success. */
609855caec6SPeter Avalos
610855caec6SPeter Avalos int
add_exclude_fp(void (* add_func)(struct exclude *,char const *,int,void *),struct exclude * ex,FILE * fp,int options,char line_end,void * data)611*6ea1f93eSDaniel Fojt add_exclude_fp (void (*add_func) (struct exclude *, char const *, int, void *),
612*6ea1f93eSDaniel Fojt struct exclude *ex, FILE *fp, int options,
613*6ea1f93eSDaniel Fojt char line_end,
614*6ea1f93eSDaniel Fojt void *data)
615855caec6SPeter Avalos {
616855caec6SPeter Avalos char *buf = NULL;
617855caec6SPeter Avalos char *p;
618*6ea1f93eSDaniel Fojt char *pattern;
619855caec6SPeter Avalos char const *lim;
620855caec6SPeter Avalos size_t buf_alloc = 0;
621855caec6SPeter Avalos size_t buf_count = 0;
622855caec6SPeter Avalos int c;
623855caec6SPeter Avalos int e = 0;
624855caec6SPeter Avalos
625*6ea1f93eSDaniel Fojt while ((c = getc (fp)) != EOF)
626855caec6SPeter Avalos {
627855caec6SPeter Avalos if (buf_count == buf_alloc)
628855caec6SPeter Avalos buf = x2realloc (buf, &buf_alloc);
629855caec6SPeter Avalos buf[buf_count++] = c;
630855caec6SPeter Avalos }
631855caec6SPeter Avalos
632*6ea1f93eSDaniel Fojt if (ferror (fp))
633855caec6SPeter Avalos e = errno;
634855caec6SPeter Avalos
635855caec6SPeter Avalos buf = xrealloc (buf, buf_count + 1);
636855caec6SPeter Avalos buf[buf_count] = line_end;
637855caec6SPeter Avalos lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
638*6ea1f93eSDaniel Fojt
639*6ea1f93eSDaniel Fojt exclude_add_pattern_buffer (ex, buf);
640*6ea1f93eSDaniel Fojt
641855caec6SPeter Avalos pattern = buf;
642855caec6SPeter Avalos
643855caec6SPeter Avalos for (p = buf; p < lim; p++)
644855caec6SPeter Avalos if (*p == line_end)
645855caec6SPeter Avalos {
646855caec6SPeter Avalos char *pattern_end = p;
647855caec6SPeter Avalos
64844b87433SJohn Marino if (isspace ((unsigned char) line_end))
649855caec6SPeter Avalos {
650855caec6SPeter Avalos for (; ; pattern_end--)
651855caec6SPeter Avalos if (pattern_end == pattern)
652855caec6SPeter Avalos goto next_pattern;
65344b87433SJohn Marino else if (! isspace ((unsigned char) pattern_end[-1]))
654855caec6SPeter Avalos break;
655855caec6SPeter Avalos }
656855caec6SPeter Avalos
657855caec6SPeter Avalos *pattern_end = '\0';
658*6ea1f93eSDaniel Fojt (*add_func) (ex, pattern, options, data);
659855caec6SPeter Avalos
660855caec6SPeter Avalos next_pattern:
661855caec6SPeter Avalos pattern = p + 1;
662855caec6SPeter Avalos }
663855caec6SPeter Avalos
664855caec6SPeter Avalos errno = e;
665855caec6SPeter Avalos return e ? -1 : 0;
666855caec6SPeter Avalos }
667*6ea1f93eSDaniel Fojt
668*6ea1f93eSDaniel Fojt static void
call_addfn(struct exclude * ex,char const * pattern,int options,void * data)669*6ea1f93eSDaniel Fojt call_addfn (struct exclude *ex, char const *pattern, int options, void *data)
670*6ea1f93eSDaniel Fojt {
671*6ea1f93eSDaniel Fojt void (**addfnptr) (struct exclude *, char const *, int) = data;
672*6ea1f93eSDaniel Fojt (*addfnptr) (ex, pattern, options);
673*6ea1f93eSDaniel Fojt }
674*6ea1f93eSDaniel Fojt
675*6ea1f93eSDaniel Fojt int
add_exclude_file(void (* add_func)(struct exclude *,char const *,int),struct exclude * ex,char const * file_name,int options,char line_end)676*6ea1f93eSDaniel Fojt add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
677*6ea1f93eSDaniel Fojt struct exclude *ex, char const *file_name, int options,
678*6ea1f93eSDaniel Fojt char line_end)
679*6ea1f93eSDaniel Fojt {
680*6ea1f93eSDaniel Fojt bool use_stdin = file_name[0] == '-' && !file_name[1];
681*6ea1f93eSDaniel Fojt FILE *in;
682*6ea1f93eSDaniel Fojt int rc = 0;
683*6ea1f93eSDaniel Fojt
684*6ea1f93eSDaniel Fojt if (use_stdin)
685*6ea1f93eSDaniel Fojt in = stdin;
686*6ea1f93eSDaniel Fojt else if (! (in = fopen (file_name, "r")))
687*6ea1f93eSDaniel Fojt return -1;
688*6ea1f93eSDaniel Fojt
689*6ea1f93eSDaniel Fojt rc = add_exclude_fp (call_addfn, ex, in, options, line_end, &add_func);
690*6ea1f93eSDaniel Fojt
691*6ea1f93eSDaniel Fojt if (!use_stdin && fclose (in) != 0)
692*6ea1f93eSDaniel Fojt rc = -1;
693*6ea1f93eSDaniel Fojt
694*6ea1f93eSDaniel Fojt return rc;
695*6ea1f93eSDaniel Fojt }
696