+bool
+exclude_fnmatch (char const *pattern, char const *f, int options)
+{
+ int (*matcher) (char const *, char const *, int) =
+ (options & EXCLUDE_WILDCARDS
+ ? fnmatch
+ : fnmatch_no_wildcards);
+ bool matched = ((*matcher) (pattern, f, options) == 0);
+ char const *p;
+
+ if (! (options & EXCLUDE_ANCHORED))
+ for (p = f; *p && ! matched; p++)
+ if (*p == '/' && p[1] != '/')
+ matched = ((*matcher) (pattern, p + 1, options) == 0);
+
+ return matched;
+}
+
+/* Return true if the exclude_pattern segment SEG matches F. */
+
+static bool
+file_pattern_matches (struct exclude_segment const *seg, char const *f)
+{
+ size_t exclude_count = seg->v.pat.exclude_count;
+ struct patopts const *exclude = seg->v.pat.exclude;
+ size_t i;
+
+ for (i = 0; i < exclude_count; i++)
+ {
+ char const *pattern = exclude[i].pattern;
+ int options = exclude[i].options;
+ if (exclude_fnmatch (pattern, f, options))
+ return true;
+ }
+ return false;
+}
+
+/* Return true if the exclude_hash segment SEG matches F.
+ BUFFER is an auxiliary storage of the same length as F (with nul
+ terminator included) */
+static bool
+file_name_matches (struct exclude_segment const *seg, char const *f,
+ char *buffer)
+{
+ int options = seg->options;
+ Hash_table *table = seg->v.table;
+
+ do
+ {
+ /* initialize the pattern */
+ strcpy (buffer, f);
+
+ while (1)
+ {
+ if (hash_lookup (table, buffer))
+ return true;
+ if (options & FNM_LEADING_DIR)
+ {
+ char *p = strrchr (buffer, '/');
+ if (p)
+ {
+ *p = 0;
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (!(options & EXCLUDE_ANCHORED))
+ {
+ f = strchr (f, '/');
+ if (f)
+ f++;
+ }
+ else
+ break;
+ }
+ while (f);
+
+ return false;
+}
+