xref: /dragonfly/contrib/diffutils/lib/exclude.c (revision 6ea1f93e)
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