git: 1e0743f54d2d - main - glob: Add blocks support
Date: Mon, 02 Jun 2025 09:33:14 UTC
The branch main has been updated by bnovkov:
URL: https://cgit.FreeBSD.org/src/commit/?id=1e0743f54d2d3624cd4de2167d373aa38597778e
commit 1e0743f54d2d3624cd4de2167d373aa38597778e
Author: Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2025-05-23 13:26:04 +0000
Commit: Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2025-06-02 09:32:50 +0000
glob: Add blocks support
This change introduces the `glob_b` function which takes a block instead
of a function pointer.
Relnotes: yes
Sponsored by: Klara, Inc.
Inspired by: https://github.com/apple-oss-distributions/Libc
Differential Revision: https://reviews.freebsd.org/D50485
---
include/glob.h | 16 +++++++++--
lib/libc/gen/Makefile.inc | 1 +
lib/libc/gen/Symbol.map | 1 +
lib/libc/gen/glob.3 | 66 ++++++++++++++++++++++++++++++++++++++------
lib/libc/gen/glob.c | 70 +++++++++++++++++++++++++++++++++++++----------
5 files changed, 128 insertions(+), 26 deletions(-)
diff --git a/include/glob.h b/include/glob.h
index dc86cdf99929..cbe99bfef6ed 100644
--- a/include/glob.h
+++ b/include/glob.h
@@ -50,8 +50,15 @@ typedef struct {
size_t gl_offs; /* Reserved at beginning of gl_pathv. */
int gl_flags; /* Copy of flags parameter to glob. */
char **gl_pathv; /* List of paths matching pattern. */
- /* Copy of errfunc parameter to glob. */
- int (*gl_errfunc)(const char *, int);
+ /* Copy of error callback parameter to glob. */
+ union {
+ int (*gl_errfunc)(const char *, int);
+#ifdef __BLOCKS__
+ int (^gl_errblk)(const char *, int);
+#else
+ void *gl_errblk;
+#endif
+ };
/*
* Alternate filesystem access methods for glob; replacement
@@ -90,6 +97,7 @@ typedef struct {
#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */
#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */
#define GLOB_LIMIT 0x1000 /* limit number of returned paths */
+#define _GLOB_ERR_BLOCK 0x08000000 /* (internal) error callback is a block */
/* source compatibility, these are the old names */
#define GLOB_MAXPATH GLOB_LIMIT
@@ -99,6 +107,10 @@ typedef struct {
__BEGIN_DECLS
int glob(const char * __restrict, int,
int (*)(const char *, int), glob_t * __restrict);
+#ifdef __BLOCKS__
+int glob_b(const char * __restrict, int,
+ int (^)(const char *, int), glob_t * __restrict);
+#endif
void globfree(glob_t *);
__END_DECLS
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index 302fd3a004d1..74b18b44e575 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -172,6 +172,7 @@ SRCS+= \
.if ${COMPILER_FEATURES:Mblocks}
CFLAGS.fts.c= -fblocks
+CFLAGS.glob.c= -fblocks
.endif
CFLAGS.arc4random.c= -I${SRCTOP}/sys -I${SRCTOP}/sys/crypto/chacha20
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
index 2ddca1f20e00..afc277822787 100644
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -459,6 +459,7 @@ FBSD_1.8 {
aio_write2;
execvpe;
fts_open_b;
+ glob_b;
psiginfo;
rtld_get_var;
rtld_set_var;
diff --git a/lib/libc/gen/glob.3 b/lib/libc/gen/glob.3
index 006e8decb3db..9f15b2edb63a 100644
--- a/lib/libc/gen/glob.3
+++ b/lib/libc/gen/glob.3
@@ -27,11 +27,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 20, 2011
+.Dd June 02, 2025
.Dt GLOB 3
.Os
.Sh NAME
.Nm glob ,
+.Nm glob_b,
.Nm globfree
.Nd generate pathnames matching a pattern
.Sh LIBRARY
@@ -39,7 +40,9 @@
.Sh SYNOPSIS
.In glob.h
.Ft int
-.Fn glob "const char * restrict pattern" "int flags" "int (*errfunc)(const char *, int)" "glob_t * restrict pglob"
+.Fn glob "const char * restrict pattern" "int flags" "int (*errfunc)(const char *epath, int errno)" "glob_t * restrict pglob"
+.Ft int
+.Fn glob_b "const char * restrict pattern" "int flags" "int (^errblk)(const char *epath, int errno)" "glob_t * restrict pglob"
.Ft void
.Fn globfree "glob_t *pglob"
.Sh DESCRIPTION
@@ -272,10 +275,24 @@ is
.Pf non- Dv NULL ,
.Fn glob
calls
-.Fa \*(lp*errfunc\*(rp Ns ( Fa path , errno ) ,
+.Fa \*(lp*errfunc\*(rp Ns ( Fa path , errno ) .
+This may be unintuitive: a pattern like
+.Ql */Makefile
+will try to
+.Xr stat 2
+.Ql foo/Makefile
+even if
+.Ql foo
+is not a directory, resulting in a
+call to
+.Fa errfunc .
+The error routine can suppress this action by testing for
+.Er ENOENT
+and
+.Er ENOTDIR ;
however, the
.Dv GLOB_ERR
-flag will cause an immediate
+flag will still cause an immediate
return when this happens.
.Pp
If
@@ -307,16 +324,27 @@ or
returns zero, the error is ignored.
.Pp
The
+.Fn glob_b
+function is like
+.Fn glob
+except that the error callback is a block pointer instead of a function
+pointer.
+.Pp
+The
.Fn globfree
function frees any space associated with
.Fa pglob
from a previous call(s) to
-.Fn glob .
+.Fn glob
+or
+.Fn glob_b .
.Sh RETURN VALUES
On successful completion,
.Fn glob
-returns zero.
-In addition the fields of
+and
+.Fn glob_b
+return zero.
+In addition, the fields of
.Fa pglob
contain the values described below:
.Bl -tag -width GLOB_NOCHECK
@@ -324,12 +352,16 @@ contain the values described below:
contains the total number of matched pathnames so far.
This includes other matches from previous invocations of
.Fn glob
+or
+.Fn glob_b .
if
.Dv GLOB_APPEND
was specified.
.It Fa gl_matchc
contains the number of matched pathnames in the current invocation of
-.Fn glob .
+.Fn glob
+or
+.Fn glob_b .
.It Fa gl_flags
contains a copy of the
.Fa flags
@@ -352,6 +384,8 @@ are undefined.
.Pp
If
.Fn glob
+or
+.Fn glob_b
terminates due to an error, it sets errno and returns one of the
following non-zero constants, which are defined in the include
file
@@ -397,6 +431,14 @@ g.gl_pathv[0] = "ls";
g.gl_pathv[1] = "-l";
execvp("ls", g.gl_pathv);
.Ed
+.Sh CAVEATS
+The
+.Fn glob
+and
+.Fn glob_b
+functions
+will not match filenames that begin with a period
+unless this is specifically requested (e.g., by ".*").
.Sh SEE ALSO
.Xr sh 1 ,
.Xr fnmatch 3 ,
@@ -435,6 +477,10 @@ and
.Fn globfree
functions first appeared in
.Bx 4.4 .
+The
+.Fn glob_b
+function first appeared in
+.Fx 15.0 .
.Sh BUGS
Patterns longer than
.Dv MAXPATHLEN
@@ -442,7 +488,9 @@ may cause unchecked errors.
.Pp
The
.Fn glob
-argument
+and
+.Fn glob_b
+functions
may fail and set errno for any of the errors specified for the
library routines
.Xr stat 2 ,
diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c
index 7a988196549a..1ac919edaa12 100644
--- a/lib/libc/gen/glob.c
+++ b/lib/libc/gen/glob.c
@@ -88,8 +88,11 @@
#include <unistd.h>
#include <wchar.h>
+#include "block_abi.h"
#include "collate.h"
+typedef DECLARE_BLOCK(int, glob_b_block, const char*, int);
+
/*
* glob(3) expansion limits. Stop the expansion if any of these limits
* is reached. This caps the runtime in the face of DoS attacks. See
@@ -179,9 +182,8 @@ static int err_aborted(glob_t *, int, char *);
static void qprintf(const char *, Char *);
#endif
-int
-glob(const char * __restrict pattern, int flags,
- int (*errfunc)(const char *, int), glob_t * __restrict pglob)
+static int
+__glob(const char *pattern, glob_t *pglob)
{
struct glob_limit limit = { 0, 0, 0, 0, 0 };
const char *patnext;
@@ -192,25 +194,23 @@ glob(const char * __restrict pattern, int flags,
int too_long;
patnext = pattern;
- if (!(flags & GLOB_APPEND)) {
+ if (!(pglob->gl_flags & GLOB_APPEND)) {
pglob->gl_pathc = 0;
pglob->gl_pathv = NULL;
- if (!(flags & GLOB_DOOFFS))
+ if (!(pglob->gl_flags & GLOB_DOOFFS))
pglob->gl_offs = 0;
}
- if (flags & GLOB_LIMIT) {
+ if (pglob->gl_flags & GLOB_LIMIT) {
limit.l_path_lim = pglob->gl_matchc;
if (limit.l_path_lim == 0)
limit.l_path_lim = GLOB_LIMIT_PATH;
}
- pglob->gl_flags = flags & ~GLOB_MAGCHAR;
- pglob->gl_errfunc = errfunc;
pglob->gl_matchc = 0;
bufnext = patbuf;
bufend = bufnext + MAXPATHLEN - 1;
too_long = 1;
- if (flags & GLOB_NOESCAPE) {
+ if (pglob->gl_flags & GLOB_NOESCAPE) {
memset(&mbs, 0, sizeof(mbs));
while (bufnext <= bufend) {
clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs);
@@ -250,15 +250,45 @@ glob(const char * __restrict pattern, int flags,
return (err_nomatch(pglob, &limit, pattern));
*bufnext = EOS;
- if (flags & GLOB_BRACE)
+ if (pglob->gl_flags & GLOB_BRACE)
return (globexp0(patbuf, pglob, &limit, pattern));
else
return (glob0(patbuf, pglob, &limit, pattern));
}
+int
+glob(const char * __restrict pattern, int flags,
+ int (*errfunc)(const char *, int), glob_t * __restrict pglob)
+{
+ int rv;
+
+ pglob->gl_flags = flags & ~(GLOB_MAGCHAR | _GLOB_ERR_BLOCK);
+ pglob->gl_errfunc = errfunc;
+ rv = __glob(pattern, pglob);
+ pglob->gl_errfunc = NULL;
+
+ return (rv);
+}
+
+int
+glob_b(const char * __restrict pattern, int flags,
+ glob_b_block block, glob_t * __restrict pglob)
+{
+ int rv;
+
+ pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+ pglob->gl_flags |= _GLOB_ERR_BLOCK;
+ pglob->gl_errblk = block;
+ rv = __glob(pattern, pglob);
+ pglob->gl_errblk = NULL;
+
+ return (rv);
+}
+
static int
globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit,
- const char *origpat) {
+ const char *origpat)
+{
int rv;
size_t oldpathc;
@@ -724,7 +754,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
return (GLOB_NOSPACE);
}
*pathend = EOS;
- if (pglob->gl_errfunc != NULL &&
+ if ((pglob->gl_errfunc != NULL || pglob->gl_errblk != NULL) &&
g_Ctoc(pathbuf, buf, sizeof(buf))) {
errno = E2BIG;
return (GLOB_NOSPACE);
@@ -1085,10 +1115,20 @@ err_nomatch(glob_t *pglob, struct glob_limit *limit, const char *origpat) {
}
static int
-err_aborted(glob_t *pglob, int err, char *buf) {
- if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) ||
- (pglob->gl_flags & GLOB_ERR))
+err_aborted(glob_t *pglob, int err, char *buf)
+{
+ int rv = 0;
+
+ if ((pglob->gl_flags & _GLOB_ERR_BLOCK) != 0) {
+ if (pglob->gl_errblk != NULL)
+ rv = CALL_BLOCK(pglob->gl_errblk, buf, errno);
+ } else if (pglob->gl_errfunc != NULL) {
+ rv = pglob->gl_errfunc(buf, errno);
+ }
+ /* GLOB_ERR is allowed to override the error callback function. */
+ if (rv != 0 || pglob->gl_flags & GLOB_ERR) {
return (GLOB_ABORTED);
+ }
return (0);
}