+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 excludes F. */
+
+static bool
+excluded_file_pattern_p (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;
+ bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
+
+ /* Scan through the options, until they change excluded */
+ 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 !excluded;
+ }
+ return excluded;
+}
+
+/* Return true if the exclude_hash segment SEG excludes F.
+ BUFFER is an auxiliary storage of the same length as F (with nul
+ terminator included) */
+static bool
+excluded_file_name_p (struct exclude_segment const *seg, char const *f,
+ char *buffer)
+{
+ int options = seg->options;
+ bool excluded = !! (options & EXCLUDE_INCLUDE);
+ Hash_table *table = seg->v.table;
+
+ do
+ {
+ /* initialize the pattern */
+ strcpy (buffer, f);
+
+ while (1)
+ {
+ if (hash_lookup (table, buffer))
+ return !excluded;
+ 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 excluded;
+}
+