On 2026-08-17 01:48, Sergey Poznyakoff wrote:
I'm OK with it either way, the
most important thing is being able to tell how excluded_file_name
took the decision: using a matching entry or because nothing matched
the file name.
OK, thanks, I installed the attached into Gnulib; if this won't do please let
us know.
From a50726598270147223685f91ac9761ce8a052a0e Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Tue, 18 Aug 2026 01:20:04 -0700
Subject: [PATCH] exclude: new function excluded_file_name_status
From a suggestion by Sergey Poznyakoff in:
https://lists.gnu.org/r/bug-gnulib/2026-08/msg00217.html
* lib/exclude.c (EXCLUDE_INLINE): New macro.
(excluded_file_name_status): New function, extending
excluded_file_name which is now in exclude.h.
* lib/exclude.h (EXCLUDE_INLINE, EXCLUDED_EXCLUDED, EXCLUDED_MATCHED):
New macros.
(excluded_file_name): New function.
* tests/test-exclude.c (main): Test new function too.
All test scripts changed.
---
ChangeLog | 14 ++++++++++++++
lib/exclude.c | 24 +++++++++++++-----------
lib/exclude.h | 22 +++++++++++++++++++++-
tests/test-exclude.c | 6 ++++--
tests/test-exclude1.sh | 12 ++++++------
tests/test-exclude2.sh | 12 ++++++------
tests/test-exclude3.sh | 12 ++++++------
tests/test-exclude4.sh | 2 +-
tests/test-exclude5.sh | 8 ++++----
tests/test-exclude6.sh | 4 ++--
tests/test-exclude7.sh | 4 ++--
tests/test-exclude8.sh | 6 +++---
12 files changed, 82 insertions(+), 44 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 752464e397..30ad4d78e8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2026-08-18 Paul Eggert <[email protected]>
+
+ exclude: new function excluded_file_name_status
+ From a suggestion by Sergey Poznyakoff in:
+ https://lists.gnu.org/r/bug-gnulib/2026-08/msg00217.html
+ * lib/exclude.c (EXCLUDE_INLINE): New macro.
+ (excluded_file_name_status): New function, extending
+ excluded_file_name which is now in exclude.h.
+ * lib/exclude.h (EXCLUDE_INLINE, EXCLUDED_EXCLUDED, EXCLUDED_MATCHED):
+ New macros.
+ (excluded_file_name): New function.
+ * tests/test-exclude.c (main): Test new function too.
+ All test scripts changed.
+
2026-08-17 Bruno Haible <[email protected]>
Depend on 'bool' for use of 'false' or 'true'.
diff --git a/lib/exclude.c b/lib/exclude.c
index 6a8604ec1b..0519ffec8e 100644
--- a/lib/exclude.c
+++ b/lib/exclude.c
@@ -21,6 +21,7 @@
Thanks to Phil Proudman <[email protected]>
for improvement suggestions. */
+#define EXCLUDE_INLINE _GL_EXTERN_INLINE
#include <config.h>
#include <ctype.h>
@@ -455,17 +456,18 @@ file_name_matches (struct exclude_segment const *seg, char const *f,
return false;
}
-/* Return true if EX excludes F. */
+/* Return the status of whether EX excludes F. */
-bool
-excluded_file_name (struct exclude const *ex, char const *f)
+int
+excluded_file_name_status (struct exclude const *ex, char const *f)
{
/* If no patterns are given, the default is to include. */
if (!ex->head)
- return false;
+ return 0;
bool invert = false;
char *filename = nullptr;
+ bool found = false;
/* Scan through the segments, reporting the status of the first match.
The segments are in reverse order, so this reports the status of
@@ -477,14 +479,13 @@ excluded_file_name (struct exclude const *ex, char const *f)
{
if (!filename)
filename = xmalloc (strlen (f) + 1);
- if (file_name_matches (seg, f, filename))
- break;
+ found = file_name_matches (seg, f, filename);
}
else
- {
- if (file_pattern_matches (seg, f))
- break;
- }
+ found = file_pattern_matches (seg, f);
+
+ if (found)
+ break;
if (! seg->next)
{
@@ -500,7 +501,8 @@ excluded_file_name (struct exclude const *ex, char const *f)
}
free (filename);
- return invert ^ ! (seg->options & EXCLUDE_INCLUDE);
+ return ((invert ^ ! (seg->options & EXCLUDE_INCLUDE) ? EXCLUDED_EXCLUDED : 0)
+ | (found ? EXCLUDED_MATCHED : 0));
}
/* Append to EX the exclusion PATTERN with OPTIONS. */
diff --git a/lib/exclude.h b/lib/exclude.h
index c217a1e4e6..3c77493f75 100644
--- a/lib/exclude.h
+++ b/lib/exclude.h
@@ -30,6 +30,11 @@
#include <stdio.h>
+_GL_INLINE_HEADER_BEGIN
+#ifndef EXCLUDE_INLINE
+# define EXCLUDE_INLINE _GL_INLINE
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -54,6 +59,12 @@ extern "C" {
/* Allocate storage for the pattern */
#define EXCLUDE_ALLOC (1 << 26)
+
+/* Return status flags for excluded_file_name_status. */
+#define EXCLUDED_EXCLUDED (1 << 0) /* The file name was excluded. */
+#define EXCLUDED_MATCHED (1 << 1) /* The file name was matched. */
+
+
struct exclude;
bool fnmatch_pattern_has_wildcards (const char *, int) _GL_ATTRIBUTE_PURE;
@@ -68,13 +79,22 @@ int add_exclude_file (void (*) (struct exclude *, char const *, int),
struct exclude *, char const *, int, char);
int add_exclude_fp (void (*) (struct exclude *, char const *, int, void *),
struct exclude *, FILE *, int, char, void *);
-bool excluded_file_name (struct exclude const *, char const *);
+int excluded_file_name_status (struct exclude const *, char const *);
void exclude_add_pattern_buffer (struct exclude *ex, char *buf);
bool exclude_fnmatch (char const *, char const *, int);
+/* Return true if EX excludes F. */
+EXCLUDE_INLINE bool
+excluded_file_name (struct exclude const *ex, char const *f)
+{
+ return excluded_file_name_status (ex, f) & EXCLUDED_EXCLUDED;
+}
+
#ifdef __cplusplus
}
#endif
+_GL_INLINE_HEADER_END
+
#endif /* _GL_EXCLUDE_H */
diff --git a/tests/test-exclude.c b/tests/test-exclude.c
index 17c631c02a..e57b34d232 100644
--- a/tests/test-exclude.c
+++ b/tests/test-exclude.c
@@ -118,8 +118,10 @@ main (int argc, char **argv)
for (; argc; --argc)
{
char *word = *++argv;
-
- printf ("%s: %d\n", word, excluded_file_name (exclude, word));
+ bool excluded = excluded_file_name (exclude, word);
+ int status = excluded_file_name_status (exclude, word);
+ printf ("%s: %d %d%s\n", word, excluded, !!(status & EXCLUDED_MATCHED),
+ excluded != !!(status & EXCLUDED_EXCLUDED) ? " !" : "");
}
free_exclude (exclude);
diff --git a/tests/test-exclude1.sh b/tests/test-exclude1.sh
index 0e2058915c..890da3ba43 100755
--- a/tests/test-exclude1.sh
+++ b/tests/test-exclude1.sh
@@ -28,12 +28,12 @@ Baz
EOT
cat > expected <<EOT
-foo: 0
-foo*: 1
-bar: 1
-foobar: 0
-baz: 0
-bar/qux: 0
+foo: 0 0
+foo*: 1 1
+bar: 1 1
+foobar: 0 0
+baz: 0 0
+bar/qux: 0 0
EOT
${CHECKER} test-exclude in -- foo 'foo*' bar foobar baz bar/qux > out || exit $?
diff --git a/tests/test-exclude2.sh b/tests/test-exclude2.sh
index 25b69f4b90..c67a5d7473 100755
--- a/tests/test-exclude2.sh
+++ b/tests/test-exclude2.sh
@@ -28,12 +28,12 @@ EOT
# Test case-insensitive literal matches
cat > expected <<EOT
-foo: 0
-foo*: 1
-bar: 1
-foobar: 0
-baz: 1
-bar/qux: 0
+foo: 0 0
+foo*: 1 1
+bar: 1 1
+foobar: 0 0
+baz: 1 1
+bar/qux: 0 0
EOT
${CHECKER} test-exclude -casefold in -- foo 'foo*' bar foobar baz bar/qux > out || exit $?
diff --git a/tests/test-exclude3.sh b/tests/test-exclude3.sh
index bfd168c21b..e3f35d8ce2 100755
--- a/tests/test-exclude3.sh
+++ b/tests/test-exclude3.sh
@@ -28,12 +28,12 @@ Baz
EOT
cat > expected <<EOT
-foo: 1
-foo*: 0
-bar: 0
-foobar: 1
-baz: 1
-bar/qux: 1
+foo: 1 0
+foo*: 0 1
+bar: 0 1
+foobar: 1 0
+baz: 1 0
+bar/qux: 1 0
EOT
${CHECKER} test-exclude -include in -- foo 'foo*' bar foobar baz bar/qux > out || exit $?
diff --git a/tests/test-exclude4.sh b/tests/test-exclude4.sh
index 55ce8cfb86..4f250db3a8 100755
--- a/tests/test-exclude4.sh
+++ b/tests/test-exclude4.sh
@@ -28,7 +28,7 @@ Baz
EOT
cat > expected <<EOT
-foobar: 1
+foobar: 1 1
EOT
${CHECKER} test-exclude -wildcards in -- foobar > out || exit $?
diff --git a/tests/test-exclude5.sh b/tests/test-exclude5.sh
index 6978ba4cd6..8372d9463b 100755
--- a/tests/test-exclude5.sh
+++ b/tests/test-exclude5.sh
@@ -28,10 +28,10 @@ Baz
EOT
cat > expected <<EOT
-bar: 1
-bar/qux: 1
-barz: 0
-foo/bar: 1
+bar: 1 1
+bar/qux: 1 1
+barz: 0 0
+foo/bar: 1 1
EOT
${CHECKER} test-exclude -leading_dir in -- bar bar/qux barz foo/bar > out || exit $?
diff --git a/tests/test-exclude6.sh b/tests/test-exclude6.sh
index 240115a4f6..8b882f4e53 100755
--- a/tests/test-exclude6.sh
+++ b/tests/test-exclude6.sh
@@ -28,8 +28,8 @@ Baz
EOT
cat > expected <<EOT
-bar: 1
-foo/bar: 0
+bar: 1 1
+foo/bar: 0 0
EOT
${CHECKER} test-exclude -anchored in -- bar foo/bar > out || exit $?
diff --git a/tests/test-exclude7.sh b/tests/test-exclude7.sh
index cb701d2c86..a8953c9d7b 100755
--- a/tests/test-exclude7.sh
+++ b/tests/test-exclude7.sh
@@ -28,8 +28,8 @@ Baz
EOT
cat > expected <<EOT
-bar: 0
-bar: 1
+bar: 0 1
+bar: 1 1
EOT
${CHECKER} test-exclude in -include in -- bar > out || exit $?
diff --git a/tests/test-exclude8.sh b/tests/test-exclude8.sh
index 02524d6e89..e7a304ab65 100755
--- a/tests/test-exclude8.sh
+++ b/tests/test-exclude8.sh
@@ -27,9 +27,9 @@ b[a\*]r
EOT
cat > expected <<'EOT'
-f*e: 1
-file: 0
-bar: 1
+f*e: 1 1
+file: 0 0
+bar: 1 1
EOT
${CHECKER} test-exclude -wildcards in -- 'f*e' 'file' 'bar' > out || exit $?
--
2.55.0