xref: /dragonfly/contrib/grep/lib/exclude.c (revision 09d4459f)
195b7b453SJohn Marino /* exclude.c -- exclude file names
295b7b453SJohn Marino 
3*09d4459fSDaniel Fojt    Copyright (C) 1992-1994, 1997, 1999-2007, 2009-2020 Free Software
4200fbe8dSJohn Marino    Foundation, Inc.
595b7b453SJohn Marino 
695b7b453SJohn Marino    This program is free software: you can redistribute it and/or modify
795b7b453SJohn Marino    it under the terms of the GNU General Public License as published by
895b7b453SJohn Marino    the Free Software Foundation; either version 3 of the License, or
995b7b453SJohn Marino    (at your option) any later version.
1095b7b453SJohn Marino 
1195b7b453SJohn Marino    This program is distributed in the hope that it will be useful,
1295b7b453SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
1395b7b453SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1495b7b453SJohn Marino    GNU General Public License for more details.
1595b7b453SJohn Marino 
1695b7b453SJohn Marino    You should have received a copy of the GNU General Public License
17*09d4459fSDaniel Fojt    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
1895b7b453SJohn Marino 
1995b7b453SJohn Marino /* Written by Paul Eggert <eggert@twinsun.com>
2095b7b453SJohn Marino    and Sergey Poznyakoff <gray@gnu.org>.
2195b7b453SJohn Marino    Thanks to Phil Proudman <phil@proudman51.freeserve.co.uk>
2295b7b453SJohn Marino    for improvement suggestions. */
2395b7b453SJohn Marino 
2495b7b453SJohn Marino #include <config.h>
2595b7b453SJohn Marino 
2695b7b453SJohn Marino #include <stdbool.h>
2795b7b453SJohn Marino 
2895b7b453SJohn Marino #include <ctype.h>
2995b7b453SJohn Marino #include <errno.h>
3095b7b453SJohn Marino #include <stddef.h>
3195b7b453SJohn Marino #include <stdio.h>
3295b7b453SJohn Marino #include <stdlib.h>
3395b7b453SJohn Marino #include <string.h>
3495b7b453SJohn Marino #include <wctype.h>
35680a9cb8SJohn Marino #include <regex.h>
3695b7b453SJohn Marino 
3795b7b453SJohn Marino #include "exclude.h"
3895b7b453SJohn Marino #include "hash.h"
3995b7b453SJohn Marino #include "mbuiter.h"
4095b7b453SJohn Marino #include "fnmatch.h"
4195b7b453SJohn Marino #include "xalloc.h"
4295b7b453SJohn Marino #include "verify.h"
43680a9cb8SJohn Marino #include "filename.h"
4495b7b453SJohn Marino 
4595b7b453SJohn Marino #if USE_UNLOCKED_IO
4695b7b453SJohn Marino # include "unlocked-io.h"
4795b7b453SJohn Marino #endif
4895b7b453SJohn Marino 
4995b7b453SJohn Marino /* Non-GNU systems lack these options, so we don't need to check them.  */
5095b7b453SJohn Marino #ifndef FNM_CASEFOLD
5195b7b453SJohn Marino # define FNM_CASEFOLD 0
5295b7b453SJohn Marino #endif
5395b7b453SJohn Marino #ifndef FNM_EXTMATCH
5495b7b453SJohn Marino # define FNM_EXTMATCH 0
5595b7b453SJohn Marino #endif
5695b7b453SJohn Marino #ifndef FNM_LEADING_DIR
5795b7b453SJohn Marino # define FNM_LEADING_DIR 0
5895b7b453SJohn Marino #endif
5995b7b453SJohn Marino 
6095b7b453SJohn Marino verify (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
6195b7b453SJohn Marino          & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
6295b7b453SJohn Marino             | FNM_CASEFOLD | FNM_EXTMATCH))
6395b7b453SJohn Marino         == 0);
6495b7b453SJohn Marino 
6595b7b453SJohn Marino 
6695b7b453SJohn Marino /* Exclusion patterns are grouped into a singly-linked list of
6795b7b453SJohn Marino    "exclusion segments".  Each segment represents a set of patterns
6895b7b453SJohn Marino    that can be matches using the same algorithm.  Non-wildcard
6995b7b453SJohn Marino    patterns are kept in hash tables, to speed up searches.  Wildcard
7095b7b453SJohn Marino    patterns are stored as arrays of patterns. */
7195b7b453SJohn Marino 
7295b7b453SJohn Marino 
7395b7b453SJohn Marino /* An exclude pattern-options pair.  The options are fnmatch options
7495b7b453SJohn Marino    ORed with EXCLUDE_* options.  */
7595b7b453SJohn Marino 
7695b7b453SJohn Marino struct patopts
7795b7b453SJohn Marino   {
7895b7b453SJohn Marino     int options;
79680a9cb8SJohn Marino     union
80680a9cb8SJohn Marino     {
81680a9cb8SJohn Marino       char const *pattern;
82680a9cb8SJohn Marino       regex_t re;
83680a9cb8SJohn Marino     } v;
8495b7b453SJohn Marino   };
8595b7b453SJohn Marino 
8695b7b453SJohn Marino /* An array of pattern-options pairs.  */
8795b7b453SJohn Marino 
8895b7b453SJohn Marino struct exclude_pattern
8995b7b453SJohn Marino   {
9095b7b453SJohn Marino     struct patopts *exclude;
9195b7b453SJohn Marino     size_t exclude_alloc;
9295b7b453SJohn Marino     size_t exclude_count;
9395b7b453SJohn Marino   };
9495b7b453SJohn Marino 
9595b7b453SJohn Marino enum exclude_type
9695b7b453SJohn Marino   {
9795b7b453SJohn Marino     exclude_hash,                    /* a hash table of excluded names */
9895b7b453SJohn Marino     exclude_pattern                  /* an array of exclude patterns */
9995b7b453SJohn Marino   };
10095b7b453SJohn Marino 
10195b7b453SJohn Marino struct exclude_segment
10295b7b453SJohn Marino   {
10395b7b453SJohn Marino     struct exclude_segment *next;    /* next segment in list */
10495b7b453SJohn Marino     enum exclude_type type;          /* type of this segment */
10595b7b453SJohn Marino     int options;                     /* common options for this segment */
10695b7b453SJohn Marino     union
10795b7b453SJohn Marino     {
10895b7b453SJohn Marino       Hash_table *table;             /* for type == exclude_hash */
10995b7b453SJohn Marino       struct exclude_pattern pat;    /* for type == exclude_pattern */
11095b7b453SJohn Marino     } v;
11195b7b453SJohn Marino   };
11295b7b453SJohn Marino 
113680a9cb8SJohn Marino struct pattern_buffer
114680a9cb8SJohn Marino   {
115680a9cb8SJohn Marino     struct pattern_buffer *next;
116680a9cb8SJohn Marino     char *base;
117680a9cb8SJohn Marino   };
118680a9cb8SJohn Marino 
119a8597f6cSJohn Marino /* The exclude structure keeps a singly-linked list of exclude segments,
120a8597f6cSJohn Marino    maintained in reverse order.  */
12195b7b453SJohn Marino struct exclude
12295b7b453SJohn Marino   {
123a8597f6cSJohn Marino     struct exclude_segment *head;
124680a9cb8SJohn Marino     struct pattern_buffer *patbuf;
12595b7b453SJohn Marino   };
12695b7b453SJohn Marino 
127680a9cb8SJohn Marino /* Register BUF in the pattern buffer list of EX.  ADD_FUNC (see
128680a9cb8SJohn Marino    add_exclude_file and add_exclude_fp below) can use this function
129680a9cb8SJohn Marino    if it modifies the pattern, to ensure the allocated memory will be
130680a9cb8SJohn Marino    properly reclaimed upon calling free_exclude. */
131680a9cb8SJohn Marino void
exclude_add_pattern_buffer(struct exclude * ex,char * buf)132680a9cb8SJohn Marino exclude_add_pattern_buffer (struct exclude *ex, char *buf)
133680a9cb8SJohn Marino {
134680a9cb8SJohn Marino   struct pattern_buffer *pbuf = xmalloc (sizeof *pbuf);
135680a9cb8SJohn Marino   pbuf->base = buf;
136680a9cb8SJohn Marino   pbuf->next = ex->patbuf;
137680a9cb8SJohn Marino   ex->patbuf = pbuf;
138680a9cb8SJohn Marino }
139680a9cb8SJohn Marino 
140a8597f6cSJohn Marino /* Return true if STR has or may have wildcards, when matched with OPTIONS.
141a8597f6cSJohn Marino    Return false if STR definitely does not have wildcards.  */
14295b7b453SJohn Marino bool
fnmatch_pattern_has_wildcards(const char * str,int options)14395b7b453SJohn Marino fnmatch_pattern_has_wildcards (const char *str, int options)
14495b7b453SJohn Marino {
145a8597f6cSJohn Marino   while (1)
14695b7b453SJohn Marino     {
147a8597f6cSJohn Marino       switch (*str++)
148a8597f6cSJohn Marino         {
149680a9cb8SJohn Marino         case '.':
150680a9cb8SJohn Marino         case '{':
151680a9cb8SJohn Marino         case '}':
152680a9cb8SJohn Marino         case '(':
153680a9cb8SJohn Marino         case ')':
154680a9cb8SJohn Marino           if (options & EXCLUDE_REGEX)
155680a9cb8SJohn Marino             return true;
156680a9cb8SJohn Marino           break;
157680a9cb8SJohn Marino 
158a8597f6cSJohn Marino         case '\\':
159680a9cb8SJohn Marino           if (options & EXCLUDE_REGEX)
160680a9cb8SJohn Marino             continue;
161680a9cb8SJohn Marino           else
162a8597f6cSJohn Marino             str += ! (options & FNM_NOESCAPE) && *str;
16395b7b453SJohn Marino           break;
164a8597f6cSJohn Marino 
165a8597f6cSJohn Marino         case '+': case '@': case '!':
166a8597f6cSJohn Marino           if (options & FNM_EXTMATCH && *str == '(')
16795b7b453SJohn Marino             return true;
168a8597f6cSJohn Marino           break;
169a8597f6cSJohn Marino 
170a8597f6cSJohn Marino         case '?': case '*': case '[':
171a8597f6cSJohn Marino           return true;
172a8597f6cSJohn Marino 
173a8597f6cSJohn Marino         case '\0':
17495b7b453SJohn Marino           return false;
17595b7b453SJohn Marino         }
176a8597f6cSJohn Marino     }
177a8597f6cSJohn Marino }
17895b7b453SJohn Marino 
17995b7b453SJohn Marino static void
unescape_pattern(char * str)18095b7b453SJohn Marino unescape_pattern (char *str)
18195b7b453SJohn Marino {
182a8597f6cSJohn Marino   char const *q = str;
18395b7b453SJohn Marino   do
184a8597f6cSJohn Marino     q += *q == '\\' && q[1];
18595b7b453SJohn Marino   while ((*str++ = *q++));
18695b7b453SJohn Marino }
18795b7b453SJohn Marino 
18895b7b453SJohn Marino /* Return a newly allocated and empty exclude list.  */
18995b7b453SJohn Marino 
19095b7b453SJohn Marino struct exclude *
new_exclude(void)19195b7b453SJohn Marino new_exclude (void)
19295b7b453SJohn Marino {
19395b7b453SJohn Marino   return xzalloc (sizeof *new_exclude ());
19495b7b453SJohn Marino }
19595b7b453SJohn Marino 
19695b7b453SJohn Marino /* Calculate the hash of string.  */
19795b7b453SJohn Marino static size_t
string_hasher(void const * data,size_t n_buckets)19895b7b453SJohn Marino string_hasher (void const *data, size_t n_buckets)
19995b7b453SJohn Marino {
20095b7b453SJohn Marino   char const *p = data;
20195b7b453SJohn Marino   return hash_string (p, n_buckets);
20295b7b453SJohn Marino }
20395b7b453SJohn Marino 
20495b7b453SJohn Marino /* Ditto, for case-insensitive hashes */
20595b7b453SJohn Marino static size_t
string_hasher_ci(void const * data,size_t n_buckets)20695b7b453SJohn Marino string_hasher_ci (void const *data, size_t n_buckets)
20795b7b453SJohn Marino {
20895b7b453SJohn Marino   char const *p = data;
20995b7b453SJohn Marino   mbui_iterator_t iter;
21095b7b453SJohn Marino   size_t value = 0;
21195b7b453SJohn Marino 
21295b7b453SJohn Marino   for (mbui_init (iter, p); mbui_avail (iter); mbui_advance (iter))
21395b7b453SJohn Marino     {
21495b7b453SJohn Marino       mbchar_t m = mbui_cur (iter);
21595b7b453SJohn Marino       wchar_t wc;
21695b7b453SJohn Marino 
21795b7b453SJohn Marino       if (m.wc_valid)
21895b7b453SJohn Marino         wc = towlower (m.wc);
21995b7b453SJohn Marino       else
22095b7b453SJohn Marino         wc = *m.ptr;
22195b7b453SJohn Marino 
22295b7b453SJohn Marino       value = (value * 31 + wc) % n_buckets;
22395b7b453SJohn Marino     }
22495b7b453SJohn Marino 
22595b7b453SJohn Marino   return value;
22695b7b453SJohn Marino }
22795b7b453SJohn Marino 
22895b7b453SJohn Marino /* compare two strings for equality */
22995b7b453SJohn Marino static bool
string_compare(void const * data1,void const * data2)23095b7b453SJohn Marino string_compare (void const *data1, void const *data2)
23195b7b453SJohn Marino {
23295b7b453SJohn Marino   char const *p1 = data1;
23395b7b453SJohn Marino   char const *p2 = data2;
23495b7b453SJohn Marino   return strcmp (p1, p2) == 0;
23595b7b453SJohn Marino }
23695b7b453SJohn Marino 
23795b7b453SJohn Marino /* compare two strings for equality, case-insensitive */
23895b7b453SJohn Marino static bool
string_compare_ci(void const * data1,void const * data2)23995b7b453SJohn Marino string_compare_ci (void const *data1, void const *data2)
24095b7b453SJohn Marino {
24195b7b453SJohn Marino   char const *p1 = data1;
24295b7b453SJohn Marino   char const *p2 = data2;
24395b7b453SJohn Marino   return mbscasecmp (p1, p2) == 0;
24495b7b453SJohn Marino }
24595b7b453SJohn Marino 
24695b7b453SJohn Marino static void
string_free(void * data)24795b7b453SJohn Marino string_free (void *data)
24895b7b453SJohn Marino {
24995b7b453SJohn Marino   free (data);
25095b7b453SJohn Marino }
25195b7b453SJohn Marino 
25295b7b453SJohn Marino /* Create new exclude segment of given TYPE and OPTIONS, and attach it
253a8597f6cSJohn Marino    to the head of EX.  */
254a8597f6cSJohn Marino static void
new_exclude_segment(struct exclude * ex,enum exclude_type type,int options)25595b7b453SJohn Marino new_exclude_segment (struct exclude *ex, enum exclude_type type, int options)
25695b7b453SJohn Marino {
25795b7b453SJohn Marino   struct exclude_segment *sp = xzalloc (sizeof (struct exclude_segment));
25895b7b453SJohn Marino   sp->type = type;
25995b7b453SJohn Marino   sp->options = options;
26095b7b453SJohn Marino   switch (type)
26195b7b453SJohn Marino     {
26295b7b453SJohn Marino     case exclude_pattern:
26395b7b453SJohn Marino       break;
26495b7b453SJohn Marino 
26595b7b453SJohn Marino     case exclude_hash:
26695b7b453SJohn Marino       sp->v.table = hash_initialize (0, NULL,
26795b7b453SJohn Marino                                      (options & FNM_CASEFOLD) ?
26895b7b453SJohn Marino                                        string_hasher_ci
26995b7b453SJohn Marino                                        : string_hasher,
27095b7b453SJohn Marino                                      (options & FNM_CASEFOLD) ?
27195b7b453SJohn Marino                                        string_compare_ci
27295b7b453SJohn Marino                                        : string_compare,
27395b7b453SJohn Marino                                      string_free);
27495b7b453SJohn Marino       break;
27595b7b453SJohn Marino     }
276a8597f6cSJohn Marino   sp->next = ex->head;
27795b7b453SJohn Marino   ex->head = sp;
27895b7b453SJohn Marino }
27995b7b453SJohn Marino 
28095b7b453SJohn Marino /* Free a single exclude segment */
28195b7b453SJohn Marino static void
free_exclude_segment(struct exclude_segment * seg)28295b7b453SJohn Marino free_exclude_segment (struct exclude_segment *seg)
28395b7b453SJohn Marino {
284680a9cb8SJohn Marino   size_t i;
285680a9cb8SJohn Marino 
28695b7b453SJohn Marino   switch (seg->type)
28795b7b453SJohn Marino     {
28895b7b453SJohn Marino     case exclude_pattern:
289680a9cb8SJohn Marino       for (i = 0; i < seg->v.pat.exclude_count; i++)
290680a9cb8SJohn Marino         {
291680a9cb8SJohn Marino           if (seg->v.pat.exclude[i].options & EXCLUDE_REGEX)
292680a9cb8SJohn Marino             regfree (&seg->v.pat.exclude[i].v.re);
293680a9cb8SJohn Marino         }
29495b7b453SJohn Marino       free (seg->v.pat.exclude);
29595b7b453SJohn Marino       break;
29695b7b453SJohn Marino 
29795b7b453SJohn Marino     case exclude_hash:
29895b7b453SJohn Marino       hash_free (seg->v.table);
29995b7b453SJohn Marino       break;
30095b7b453SJohn Marino     }
30195b7b453SJohn Marino   free (seg);
30295b7b453SJohn Marino }
30395b7b453SJohn Marino 
30495b7b453SJohn Marino /* Free the storage associated with an exclude list.  */
30595b7b453SJohn Marino void
free_exclude(struct exclude * ex)30695b7b453SJohn Marino free_exclude (struct exclude *ex)
30795b7b453SJohn Marino {
30895b7b453SJohn Marino   struct exclude_segment *seg;
309680a9cb8SJohn Marino   struct pattern_buffer *pbuf;
310680a9cb8SJohn Marino 
31195b7b453SJohn Marino   for (seg = ex->head; seg; )
31295b7b453SJohn Marino     {
31395b7b453SJohn Marino       struct exclude_segment *next = seg->next;
31495b7b453SJohn Marino       free_exclude_segment (seg);
31595b7b453SJohn Marino       seg = next;
31695b7b453SJohn Marino     }
317680a9cb8SJohn Marino 
318680a9cb8SJohn Marino   for (pbuf = ex->patbuf; pbuf; )
319680a9cb8SJohn Marino     {
320680a9cb8SJohn Marino       struct pattern_buffer *next = pbuf->next;
321680a9cb8SJohn Marino       free (pbuf->base);
322680a9cb8SJohn Marino       free (pbuf);
323680a9cb8SJohn Marino       pbuf = next;
324680a9cb8SJohn Marino     }
325680a9cb8SJohn Marino 
32695b7b453SJohn Marino   free (ex);
32795b7b453SJohn Marino }
32895b7b453SJohn Marino 
32995b7b453SJohn Marino /* Return zero if PATTERN matches F, obeying OPTIONS, except that
33095b7b453SJohn Marino    (unlike fnmatch) wildcards are disabled in PATTERN.  */
33195b7b453SJohn Marino 
33295b7b453SJohn Marino static int
fnmatch_no_wildcards(char const * pattern,char const * f,int options)33395b7b453SJohn Marino fnmatch_no_wildcards (char const *pattern, char const *f, int options)
33495b7b453SJohn Marino {
33595b7b453SJohn Marino   if (! (options & FNM_LEADING_DIR))
33695b7b453SJohn Marino     return ((options & FNM_CASEFOLD)
33795b7b453SJohn Marino             ? mbscasecmp (pattern, f)
33895b7b453SJohn Marino             : strcmp (pattern, f));
33995b7b453SJohn Marino   else if (! (options & FNM_CASEFOLD))
34095b7b453SJohn Marino     {
34195b7b453SJohn Marino       size_t patlen = strlen (pattern);
34295b7b453SJohn Marino       int r = strncmp (pattern, f, patlen);
34395b7b453SJohn Marino       if (! r)
34495b7b453SJohn Marino         {
34595b7b453SJohn Marino           r = f[patlen];
34695b7b453SJohn Marino           if (r == '/')
34795b7b453SJohn Marino             r = 0;
34895b7b453SJohn Marino         }
34995b7b453SJohn Marino       return r;
35095b7b453SJohn Marino     }
35195b7b453SJohn Marino   else
35295b7b453SJohn Marino     {
35395b7b453SJohn Marino       /* Walk through a copy of F, seeing whether P matches any prefix
35495b7b453SJohn Marino          of F.
35595b7b453SJohn Marino 
35695b7b453SJohn Marino          FIXME: This is an O(N**2) algorithm; it should be O(N).
35795b7b453SJohn Marino          Also, the copy should not be necessary.  However, fixing this
35895b7b453SJohn Marino          will probably involve a change to the mbs* API.  */
35995b7b453SJohn Marino 
36095b7b453SJohn Marino       char *fcopy = xstrdup (f);
36195b7b453SJohn Marino       char *p;
36295b7b453SJohn Marino       int r;
36395b7b453SJohn Marino       for (p = fcopy; ; *p++ = '/')
36495b7b453SJohn Marino         {
36595b7b453SJohn Marino           p = strchr (p, '/');
36695b7b453SJohn Marino           if (p)
36795b7b453SJohn Marino             *p = '\0';
36895b7b453SJohn Marino           r = mbscasecmp (pattern, fcopy);
36995b7b453SJohn Marino           if (!p || r <= 0)
37095b7b453SJohn Marino             break;
37195b7b453SJohn Marino         }
37295b7b453SJohn Marino       free (fcopy);
37395b7b453SJohn Marino       return r;
37495b7b453SJohn Marino     }
37595b7b453SJohn Marino }
37695b7b453SJohn Marino 
37795b7b453SJohn Marino bool
exclude_fnmatch(char const * pattern,char const * f,int options)37895b7b453SJohn Marino exclude_fnmatch (char const *pattern, char const *f, int options)
37995b7b453SJohn Marino {
38095b7b453SJohn Marino   int (*matcher) (char const *, char const *, int) =
38195b7b453SJohn Marino     (options & EXCLUDE_WILDCARDS
38295b7b453SJohn Marino      ? fnmatch
38395b7b453SJohn Marino      : fnmatch_no_wildcards);
38495b7b453SJohn Marino   bool matched = ((*matcher) (pattern, f, options) == 0);
38595b7b453SJohn Marino   char const *p;
38695b7b453SJohn Marino 
38795b7b453SJohn Marino   if (! (options & EXCLUDE_ANCHORED))
38895b7b453SJohn Marino     for (p = f; *p && ! matched; p++)
38995b7b453SJohn Marino       if (*p == '/' && p[1] != '/')
39095b7b453SJohn Marino         matched = ((*matcher) (pattern, p + 1, options) == 0);
39195b7b453SJohn Marino 
39295b7b453SJohn Marino   return matched;
39395b7b453SJohn Marino }
39495b7b453SJohn Marino 
395dc7c36e4SJohn Marino static bool
exclude_patopts(struct patopts const * opts,char const * f)396680a9cb8SJohn Marino exclude_patopts (struct patopts const *opts, char const *f)
397680a9cb8SJohn Marino {
398680a9cb8SJohn Marino   int options = opts->options;
399680a9cb8SJohn Marino 
400680a9cb8SJohn Marino   return (options & EXCLUDE_REGEX)
401680a9cb8SJohn Marino           ? regexec (&opts->v.re, f, 0, NULL, 0) == 0
402680a9cb8SJohn Marino           : exclude_fnmatch (opts->v.pattern, f, options);
403680a9cb8SJohn Marino }
404680a9cb8SJohn Marino 
405a8597f6cSJohn Marino /* Return true if the exclude_pattern segment SEG matches F.  */
40695b7b453SJohn Marino 
40795b7b453SJohn Marino static bool
file_pattern_matches(struct exclude_segment const * seg,char const * f)408a8597f6cSJohn Marino file_pattern_matches (struct exclude_segment const *seg, char const *f)
40995b7b453SJohn Marino {
41095b7b453SJohn Marino   size_t exclude_count = seg->v.pat.exclude_count;
41195b7b453SJohn Marino   struct patopts const *exclude = seg->v.pat.exclude;
41295b7b453SJohn Marino   size_t i;
41395b7b453SJohn Marino 
41495b7b453SJohn Marino   for (i = 0; i < exclude_count; i++)
41595b7b453SJohn Marino     {
416680a9cb8SJohn Marino       if (exclude_patopts (exclude + i, f))
417a8597f6cSJohn Marino         return true;
41895b7b453SJohn Marino     }
419a8597f6cSJohn Marino   return false;
42095b7b453SJohn Marino }
42195b7b453SJohn Marino 
422a8597f6cSJohn Marino /* Return true if the exclude_hash segment SEG matches F.
42395b7b453SJohn Marino    BUFFER is an auxiliary storage of the same length as F (with nul
42495b7b453SJohn Marino    terminator included) */
42595b7b453SJohn Marino static bool
file_name_matches(struct exclude_segment const * seg,char const * f,char * buffer)426a8597f6cSJohn Marino file_name_matches (struct exclude_segment const *seg, char const *f,
42795b7b453SJohn Marino                    char *buffer)
42895b7b453SJohn Marino {
42995b7b453SJohn Marino   int options = seg->options;
43095b7b453SJohn Marino   Hash_table *table = seg->v.table;
43195b7b453SJohn Marino 
43295b7b453SJohn Marino   do
43395b7b453SJohn Marino     {
43495b7b453SJohn Marino       /* initialize the pattern */
43595b7b453SJohn Marino       strcpy (buffer, f);
43695b7b453SJohn Marino 
43795b7b453SJohn Marino       while (1)
43895b7b453SJohn Marino         {
43995b7b453SJohn Marino           if (hash_lookup (table, buffer))
440a8597f6cSJohn Marino             return true;
44195b7b453SJohn Marino           if (options & FNM_LEADING_DIR)
44295b7b453SJohn Marino             {
44395b7b453SJohn Marino               char *p = strrchr (buffer, '/');
44495b7b453SJohn Marino               if (p)
44595b7b453SJohn Marino                 {
44695b7b453SJohn Marino                   *p = 0;
44795b7b453SJohn Marino                   continue;
44895b7b453SJohn Marino                 }
44995b7b453SJohn Marino             }
45095b7b453SJohn Marino           break;
45195b7b453SJohn Marino         }
45295b7b453SJohn Marino 
45395b7b453SJohn Marino       if (!(options & EXCLUDE_ANCHORED))
45495b7b453SJohn Marino         {
45595b7b453SJohn Marino           f = strchr (f, '/');
45695b7b453SJohn Marino           if (f)
45795b7b453SJohn Marino             f++;
45895b7b453SJohn Marino         }
45995b7b453SJohn Marino       else
46095b7b453SJohn Marino         break;
46195b7b453SJohn Marino     }
46295b7b453SJohn Marino   while (f);
463a8597f6cSJohn Marino 
464a8597f6cSJohn Marino   return false;
46595b7b453SJohn Marino }
46695b7b453SJohn Marino 
46795b7b453SJohn Marino /* Return true if EX excludes F.  */
46895b7b453SJohn Marino 
46995b7b453SJohn Marino bool
excluded_file_name(struct exclude const * ex,char const * f)47095b7b453SJohn Marino excluded_file_name (struct exclude const *ex, char const *f)
47195b7b453SJohn Marino {
47295b7b453SJohn Marino   struct exclude_segment *seg;
473a8597f6cSJohn Marino   bool invert = false;
47495b7b453SJohn Marino   char *filename = NULL;
47595b7b453SJohn Marino 
47695b7b453SJohn Marino   /* If no patterns are given, the default is to include.  */
47795b7b453SJohn Marino   if (!ex->head)
47895b7b453SJohn Marino     return false;
47995b7b453SJohn Marino 
480a8597f6cSJohn Marino   /* Scan through the segments, reporting the status of the first match.
481a8597f6cSJohn Marino      The segments are in reverse order, so this reports the status of
482a8597f6cSJohn Marino      the last match in the original option list.  */
483a8597f6cSJohn Marino   for (seg = ex->head; ; seg = seg->next)
48495b7b453SJohn Marino     {
485a8597f6cSJohn Marino       if (seg->type == exclude_hash)
48695b7b453SJohn Marino         {
48795b7b453SJohn Marino           if (!filename)
48895b7b453SJohn Marino             filename = xmalloc (strlen (f) + 1);
489a8597f6cSJohn Marino           if (file_name_matches (seg, f, filename))
49095b7b453SJohn Marino             break;
49195b7b453SJohn Marino         }
492a8597f6cSJohn Marino       else
49395b7b453SJohn Marino         {
494a8597f6cSJohn Marino           if (file_pattern_matches (seg, f))
495a8597f6cSJohn Marino             break;
496a8597f6cSJohn Marino         }
497a8597f6cSJohn Marino 
498a8597f6cSJohn Marino       if (! seg->next)
499a8597f6cSJohn Marino         {
500a8597f6cSJohn Marino           /* If patterns are given but none match, the default is the
501a8597f6cSJohn Marino              opposite of the last segment (i.e., the first in the
502a8597f6cSJohn Marino              original option list).  For example, in the command
503a8597f6cSJohn Marino              'grep -r --exclude="a*" --include="*b" pat dir', the
504a8597f6cSJohn Marino              first option is --exclude so any file name matching
505a8597f6cSJohn Marino              neither a* nor *b is included.  */
506a8597f6cSJohn Marino           invert = true;
50795b7b453SJohn Marino           break;
50895b7b453SJohn Marino         }
50995b7b453SJohn Marino     }
510a8597f6cSJohn Marino 
51195b7b453SJohn Marino   free (filename);
512a8597f6cSJohn Marino   return invert ^ ! (seg->options & EXCLUDE_INCLUDE);
51395b7b453SJohn Marino }
51495b7b453SJohn Marino 
51595b7b453SJohn Marino /* Append to EX the exclusion PATTERN with OPTIONS.  */
51695b7b453SJohn Marino 
51795b7b453SJohn Marino void
add_exclude(struct exclude * ex,char const * pattern,int options)51895b7b453SJohn Marino add_exclude (struct exclude *ex, char const *pattern, int options)
51995b7b453SJohn Marino {
52095b7b453SJohn Marino   struct exclude_segment *seg;
52195b7b453SJohn Marino   struct exclude_pattern *pat;
52295b7b453SJohn Marino   struct patopts *patopts;
52395b7b453SJohn Marino 
524680a9cb8SJohn Marino   if ((options & (EXCLUDE_REGEX|EXCLUDE_WILDCARDS))
525680a9cb8SJohn Marino       && fnmatch_pattern_has_wildcards (pattern, options))
526680a9cb8SJohn Marino     {
527a8597f6cSJohn Marino       if (! (ex->head && ex->head->type == exclude_pattern
528a8597f6cSJohn Marino              && ((ex->head->options & EXCLUDE_INCLUDE)
529a8597f6cSJohn Marino                  == (options & EXCLUDE_INCLUDE))))
530a8597f6cSJohn Marino         new_exclude_segment (ex, exclude_pattern, options);
531680a9cb8SJohn Marino 
532a8597f6cSJohn Marino       seg = ex->head;
53395b7b453SJohn Marino 
53495b7b453SJohn Marino       pat = &seg->v.pat;
53595b7b453SJohn Marino       if (pat->exclude_count == pat->exclude_alloc)
53695b7b453SJohn Marino         pat->exclude = x2nrealloc (pat->exclude, &pat->exclude_alloc,
53795b7b453SJohn Marino                                    sizeof *pat->exclude);
53895b7b453SJohn Marino       patopts = &pat->exclude[pat->exclude_count++];
539680a9cb8SJohn Marino 
54095b7b453SJohn Marino       patopts->options = options;
541680a9cb8SJohn Marino       if (options & EXCLUDE_REGEX)
542680a9cb8SJohn Marino         {
543680a9cb8SJohn Marino           int rc;
544680a9cb8SJohn Marino           int cflags = REG_NOSUB|REG_EXTENDED|
545680a9cb8SJohn Marino                        ((options & FNM_CASEFOLD) ? REG_ICASE : 0);
546680a9cb8SJohn Marino 
547680a9cb8SJohn Marino           if (options & FNM_LEADING_DIR)
548680a9cb8SJohn Marino             {
549680a9cb8SJohn Marino               char *tmp;
550680a9cb8SJohn Marino               size_t len = strlen (pattern);
551680a9cb8SJohn Marino 
552680a9cb8SJohn Marino               while (len > 0 && ISSLASH (pattern[len-1]))
553680a9cb8SJohn Marino                 --len;
554680a9cb8SJohn Marino 
555680a9cb8SJohn Marino               if (len == 0)
556680a9cb8SJohn Marino                 rc = 1;
557680a9cb8SJohn Marino               else
558680a9cb8SJohn Marino                 {
559680a9cb8SJohn Marino                   tmp = xmalloc (len + 7);
560680a9cb8SJohn Marino                   memcpy (tmp, pattern, len);
561680a9cb8SJohn Marino                   strcpy (tmp + len, "(/.*)?");
562680a9cb8SJohn Marino                   rc = regcomp (&patopts->v.re, tmp, cflags);
563680a9cb8SJohn Marino                   free (tmp);
564680a9cb8SJohn Marino                 }
565680a9cb8SJohn Marino             }
566680a9cb8SJohn Marino           else
567680a9cb8SJohn Marino             rc = regcomp (&patopts->v.re, pattern, cflags);
568680a9cb8SJohn Marino 
569680a9cb8SJohn Marino           if (rc)
570680a9cb8SJohn Marino             {
571680a9cb8SJohn Marino               pat->exclude_count--;
572680a9cb8SJohn Marino               return;
573680a9cb8SJohn Marino             }
574680a9cb8SJohn Marino         }
575680a9cb8SJohn Marino       else
576680a9cb8SJohn Marino         {
577680a9cb8SJohn Marino           if (options & EXCLUDE_ALLOC)
578680a9cb8SJohn Marino             {
579680a9cb8SJohn Marino               pattern = xstrdup (pattern);
580680a9cb8SJohn Marino               exclude_add_pattern_buffer (ex, (char*) pattern);
581680a9cb8SJohn Marino             }
582680a9cb8SJohn Marino           patopts->v.pattern = pattern;
583680a9cb8SJohn Marino         }
58495b7b453SJohn Marino     }
58595b7b453SJohn Marino   else
58695b7b453SJohn Marino     {
58795b7b453SJohn Marino       char *str, *p;
588a8597f6cSJohn Marino       int exclude_hash_flags = (EXCLUDE_INCLUDE | EXCLUDE_ANCHORED
589a8597f6cSJohn Marino                                 | FNM_LEADING_DIR | FNM_CASEFOLD);
590a8597f6cSJohn Marino       if (! (ex->head && ex->head->type == exclude_hash
591a8597f6cSJohn Marino              && ((ex->head->options & exclude_hash_flags)
592a8597f6cSJohn Marino                  == (options & exclude_hash_flags))))
593a8597f6cSJohn Marino         new_exclude_segment (ex, exclude_hash, options);
594a8597f6cSJohn Marino       seg = ex->head;
59595b7b453SJohn Marino 
59695b7b453SJohn Marino       str = xstrdup (pattern);
597a8597f6cSJohn Marino       if ((options & (EXCLUDE_WILDCARDS | FNM_NOESCAPE)) == EXCLUDE_WILDCARDS)
59895b7b453SJohn Marino         unescape_pattern (str);
59995b7b453SJohn Marino       p = hash_insert (seg->v.table, str);
60095b7b453SJohn Marino       if (p != str)
60195b7b453SJohn Marino         free (str);
60295b7b453SJohn Marino     }
60395b7b453SJohn Marino }
60495b7b453SJohn Marino 
60595b7b453SJohn Marino /* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with
60695b7b453SJohn Marino    OPTIONS.  LINE_END terminates each pattern in the file.  If
60795b7b453SJohn Marino    LINE_END is a space character, ignore trailing spaces and empty
608680a9cb8SJohn Marino    lines in FP.  Return -1 on failure, 0 on success.  */
60995b7b453SJohn Marino 
61095b7b453SJohn Marino 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)611680a9cb8SJohn Marino add_exclude_fp (void (*add_func) (struct exclude *, char const *, int, void *),
612680a9cb8SJohn Marino                 struct exclude *ex, FILE *fp, int options,
613680a9cb8SJohn Marino                 char line_end,
614680a9cb8SJohn Marino                 void *data)
61595b7b453SJohn Marino {
61695b7b453SJohn Marino   char *buf = NULL;
61795b7b453SJohn Marino   char *p;
618680a9cb8SJohn Marino   char *pattern;
61995b7b453SJohn Marino   char const *lim;
62095b7b453SJohn Marino   size_t buf_alloc = 0;
62195b7b453SJohn Marino   size_t buf_count = 0;
62295b7b453SJohn Marino   int c;
62395b7b453SJohn Marino   int e = 0;
62495b7b453SJohn Marino 
625680a9cb8SJohn Marino   while ((c = getc (fp)) != EOF)
62695b7b453SJohn Marino     {
62795b7b453SJohn Marino       if (buf_count == buf_alloc)
62895b7b453SJohn Marino         buf = x2realloc (buf, &buf_alloc);
62995b7b453SJohn Marino       buf[buf_count++] = c;
63095b7b453SJohn Marino     }
63195b7b453SJohn Marino 
632680a9cb8SJohn Marino   if (ferror (fp))
63395b7b453SJohn Marino     e = errno;
63495b7b453SJohn Marino 
63595b7b453SJohn Marino   buf = xrealloc (buf, buf_count + 1);
63695b7b453SJohn Marino   buf[buf_count] = line_end;
63795b7b453SJohn Marino   lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
638680a9cb8SJohn Marino 
639680a9cb8SJohn Marino   exclude_add_pattern_buffer (ex, buf);
640680a9cb8SJohn Marino 
64195b7b453SJohn Marino   pattern = buf;
64295b7b453SJohn Marino 
64395b7b453SJohn Marino   for (p = buf; p < lim; p++)
64495b7b453SJohn Marino     if (*p == line_end)
64595b7b453SJohn Marino       {
64695b7b453SJohn Marino         char *pattern_end = p;
64795b7b453SJohn Marino 
64895b7b453SJohn Marino         if (isspace ((unsigned char) line_end))
64995b7b453SJohn Marino           {
65095b7b453SJohn Marino             for (; ; pattern_end--)
65195b7b453SJohn Marino               if (pattern_end == pattern)
65295b7b453SJohn Marino                 goto next_pattern;
65395b7b453SJohn Marino               else if (! isspace ((unsigned char) pattern_end[-1]))
65495b7b453SJohn Marino                 break;
65595b7b453SJohn Marino           }
65695b7b453SJohn Marino 
65795b7b453SJohn Marino         *pattern_end = '\0';
658680a9cb8SJohn Marino         (*add_func) (ex, pattern, options, data);
65995b7b453SJohn Marino 
66095b7b453SJohn Marino       next_pattern:
66195b7b453SJohn Marino         pattern = p + 1;
66295b7b453SJohn Marino       }
66395b7b453SJohn Marino 
66495b7b453SJohn Marino   errno = e;
66595b7b453SJohn Marino   return e ? -1 : 0;
66695b7b453SJohn Marino }
667680a9cb8SJohn Marino 
668680a9cb8SJohn Marino static void
call_addfn(struct exclude * ex,char const * pattern,int options,void * data)669680a9cb8SJohn Marino call_addfn (struct exclude *ex, char const *pattern, int options, void *data)
670680a9cb8SJohn Marino {
671680a9cb8SJohn Marino   void (**addfnptr) (struct exclude *, char const *, int) = data;
672680a9cb8SJohn Marino   (*addfnptr) (ex, pattern, options);
673680a9cb8SJohn Marino }
674680a9cb8SJohn Marino 
675680a9cb8SJohn Marino int
add_exclude_file(void (* add_func)(struct exclude *,char const *,int),struct exclude * ex,char const * file_name,int options,char line_end)676680a9cb8SJohn Marino add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
677680a9cb8SJohn Marino                   struct exclude *ex, char const *file_name, int options,
678680a9cb8SJohn Marino                   char line_end)
679680a9cb8SJohn Marino {
680680a9cb8SJohn Marino   bool use_stdin = file_name[0] == '-' && !file_name[1];
681680a9cb8SJohn Marino   FILE *in;
682680a9cb8SJohn Marino   int rc = 0;
683680a9cb8SJohn Marino 
684680a9cb8SJohn Marino   if (use_stdin)
685680a9cb8SJohn Marino     in = stdin;
686680a9cb8SJohn Marino   else if (! (in = fopen (file_name, "r")))
687680a9cb8SJohn Marino     return -1;
688680a9cb8SJohn Marino 
689680a9cb8SJohn Marino   rc = add_exclude_fp (call_addfn, ex, in, options, line_end, &add_func);
690680a9cb8SJohn Marino 
691680a9cb8SJohn Marino   if (!use_stdin && fclose (in) != 0)
692680a9cb8SJohn Marino     rc = -1;
693680a9cb8SJohn Marino 
694680a9cb8SJohn Marino   return rc;
695680a9cb8SJohn Marino }
696