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
This commit is contained in:
Bojan Novković
2025-05-23 15:26:04 +02:00
parent 61d77e6c00
commit 1e0743f54d
5 changed files with 128 additions and 26 deletions
+14 -2
View File
@@ -50,8 +50,15 @@ typedef struct {
size_t gl_offs; /* Reserved at beginning of gl_pathv. */ size_t gl_offs; /* Reserved at beginning of gl_pathv. */
int gl_flags; /* Copy of flags parameter to glob. */ int gl_flags; /* Copy of flags parameter to glob. */
char **gl_pathv; /* List of paths matching pattern. */ char **gl_pathv; /* List of paths matching pattern. */
/* Copy of errfunc parameter to glob. */ /* Copy of error callback parameter to glob. */
int (*gl_errfunc)(const char *, int); 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 * Alternate filesystem access methods for glob; replacement
@@ -90,6 +97,7 @@ typedef struct {
#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ #define GLOB_QUOTE 0x0400 /* Quote special chars with \. */
#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ #define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */
#define GLOB_LIMIT 0x1000 /* limit number of returned paths */ #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 */ /* source compatibility, these are the old names */
#define GLOB_MAXPATH GLOB_LIMIT #define GLOB_MAXPATH GLOB_LIMIT
@@ -99,6 +107,10 @@ typedef struct {
__BEGIN_DECLS __BEGIN_DECLS
int glob(const char * __restrict, int, int glob(const char * __restrict, int,
int (*)(const char *, int), glob_t * __restrict); 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 *); void globfree(glob_t *);
__END_DECLS __END_DECLS
+1
View File
@@ -172,6 +172,7 @@ SRCS+= \
.if ${COMPILER_FEATURES:Mblocks} .if ${COMPILER_FEATURES:Mblocks}
CFLAGS.fts.c= -fblocks CFLAGS.fts.c= -fblocks
CFLAGS.glob.c= -fblocks
.endif .endif
CFLAGS.arc4random.c= -I${SRCTOP}/sys -I${SRCTOP}/sys/crypto/chacha20 CFLAGS.arc4random.c= -I${SRCTOP}/sys -I${SRCTOP}/sys/crypto/chacha20
+1
View File
@@ -459,6 +459,7 @@ FBSD_1.8 {
aio_write2; aio_write2;
execvpe; execvpe;
fts_open_b; fts_open_b;
glob_b;
psiginfo; psiginfo;
rtld_get_var; rtld_get_var;
rtld_set_var; rtld_set_var;
+57 -9
View File
@@ -27,11 +27,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd December 20, 2011 .Dd June 02, 2025
.Dt GLOB 3 .Dt GLOB 3
.Os .Os
.Sh NAME .Sh NAME
.Nm glob , .Nm glob ,
.Nm glob_b,
.Nm globfree .Nm globfree
.Nd generate pathnames matching a pattern .Nd generate pathnames matching a pattern
.Sh LIBRARY .Sh LIBRARY
@@ -39,7 +40,9 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.In glob.h .In glob.h
.Ft int .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 .Ft void
.Fn globfree "glob_t *pglob" .Fn globfree "glob_t *pglob"
.Sh DESCRIPTION .Sh DESCRIPTION
@@ -272,10 +275,24 @@ is
.Pf non- Dv NULL , .Pf non- Dv NULL ,
.Fn glob .Fn glob
calls 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 however, the
.Dv GLOB_ERR .Dv GLOB_ERR
flag will cause an immediate flag will still cause an immediate
return when this happens. return when this happens.
.Pp .Pp
If If
@@ -307,16 +324,27 @@ or
returns zero, the error is ignored. returns zero, the error is ignored.
.Pp .Pp
The 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 .Fn globfree
function frees any space associated with function frees any space associated with
.Fa pglob .Fa pglob
from a previous call(s) to from a previous call(s) to
.Fn glob . .Fn glob
or
.Fn glob_b .
.Sh RETURN VALUES .Sh RETURN VALUES
On successful completion, On successful completion,
.Fn glob .Fn glob
returns zero. and
In addition the fields of .Fn glob_b
return zero.
In addition, the fields of
.Fa pglob .Fa pglob
contain the values described below: contain the values described below:
.Bl -tag -width GLOB_NOCHECK .Bl -tag -width GLOB_NOCHECK
@@ -324,12 +352,16 @@ contain the values described below:
contains the total number of matched pathnames so far. contains the total number of matched pathnames so far.
This includes other matches from previous invocations of This includes other matches from previous invocations of
.Fn glob .Fn glob
or
.Fn glob_b .
if if
.Dv GLOB_APPEND .Dv GLOB_APPEND
was specified. was specified.
.It Fa gl_matchc .It Fa gl_matchc
contains the number of matched pathnames in the current invocation of contains the number of matched pathnames in the current invocation of
.Fn glob . .Fn glob
or
.Fn glob_b .
.It Fa gl_flags .It Fa gl_flags
contains a copy of the contains a copy of the
.Fa flags .Fa flags
@@ -352,6 +384,8 @@ are undefined.
.Pp .Pp
If If
.Fn glob .Fn glob
or
.Fn glob_b
terminates due to an error, it sets errno and returns one of the terminates due to an error, it sets errno and returns one of the
following non-zero constants, which are defined in the include following non-zero constants, which are defined in the include
file file
@@ -397,6 +431,14 @@ g.gl_pathv[0] = "ls";
g.gl_pathv[1] = "-l"; g.gl_pathv[1] = "-l";
execvp("ls", g.gl_pathv); execvp("ls", g.gl_pathv);
.Ed .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 .Sh SEE ALSO
.Xr sh 1 , .Xr sh 1 ,
.Xr fnmatch 3 , .Xr fnmatch 3 ,
@@ -435,6 +477,10 @@ and
.Fn globfree .Fn globfree
functions first appeared in functions first appeared in
.Bx 4.4 . .Bx 4.4 .
The
.Fn glob_b
function first appeared in
.Fx 15.0 .
.Sh BUGS .Sh BUGS
Patterns longer than Patterns longer than
.Dv MAXPATHLEN .Dv MAXPATHLEN
@@ -442,7 +488,9 @@ may cause unchecked errors.
.Pp .Pp
The The
.Fn glob .Fn glob
argument and
.Fn glob_b
functions
may fail and set errno for any of the errors specified for the may fail and set errno for any of the errors specified for the
library routines library routines
.Xr stat 2 , .Xr stat 2 ,
+55 -15
View File
@@ -88,8 +88,11 @@
#include <unistd.h> #include <unistd.h>
#include <wchar.h> #include <wchar.h>
#include "block_abi.h"
#include "collate.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 * 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 * 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 *); static void qprintf(const char *, Char *);
#endif #endif
int static int
glob(const char * __restrict pattern, int flags, __glob(const char *pattern, glob_t *pglob)
int (*errfunc)(const char *, int), glob_t * __restrict pglob)
{ {
struct glob_limit limit = { 0, 0, 0, 0, 0 }; struct glob_limit limit = { 0, 0, 0, 0, 0 };
const char *patnext; const char *patnext;
@@ -192,25 +194,23 @@ glob(const char * __restrict pattern, int flags,
int too_long; int too_long;
patnext = pattern; patnext = pattern;
if (!(flags & GLOB_APPEND)) { if (!(pglob->gl_flags & GLOB_APPEND)) {
pglob->gl_pathc = 0; pglob->gl_pathc = 0;
pglob->gl_pathv = NULL; pglob->gl_pathv = NULL;
if (!(flags & GLOB_DOOFFS)) if (!(pglob->gl_flags & GLOB_DOOFFS))
pglob->gl_offs = 0; pglob->gl_offs = 0;
} }
if (flags & GLOB_LIMIT) { if (pglob->gl_flags & GLOB_LIMIT) {
limit.l_path_lim = pglob->gl_matchc; limit.l_path_lim = pglob->gl_matchc;
if (limit.l_path_lim == 0) if (limit.l_path_lim == 0)
limit.l_path_lim = GLOB_LIMIT_PATH; limit.l_path_lim = GLOB_LIMIT_PATH;
} }
pglob->gl_flags = flags & ~GLOB_MAGCHAR;
pglob->gl_errfunc = errfunc;
pglob->gl_matchc = 0; pglob->gl_matchc = 0;
bufnext = patbuf; bufnext = patbuf;
bufend = bufnext + MAXPATHLEN - 1; bufend = bufnext + MAXPATHLEN - 1;
too_long = 1; too_long = 1;
if (flags & GLOB_NOESCAPE) { if (pglob->gl_flags & GLOB_NOESCAPE) {
memset(&mbs, 0, sizeof(mbs)); memset(&mbs, 0, sizeof(mbs));
while (bufnext <= bufend) { while (bufnext <= bufend) {
clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); 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)); return (err_nomatch(pglob, &limit, pattern));
*bufnext = EOS; *bufnext = EOS;
if (flags & GLOB_BRACE) if (pglob->gl_flags & GLOB_BRACE)
return (globexp0(patbuf, pglob, &limit, pattern)); return (globexp0(patbuf, pglob, &limit, pattern));
else else
return (glob0(patbuf, pglob, &limit, pattern)); 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 static int
globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit,
const char *origpat) { const char *origpat)
{
int rv; int rv;
size_t oldpathc; size_t oldpathc;
@@ -724,7 +754,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
return (GLOB_NOSPACE); return (GLOB_NOSPACE);
} }
*pathend = EOS; *pathend = EOS;
if (pglob->gl_errfunc != NULL && if ((pglob->gl_errfunc != NULL || pglob->gl_errblk != NULL) &&
g_Ctoc(pathbuf, buf, sizeof(buf))) { g_Ctoc(pathbuf, buf, sizeof(buf))) {
errno = E2BIG; errno = E2BIG;
return (GLOB_NOSPACE); return (GLOB_NOSPACE);
@@ -1085,10 +1115,20 @@ err_nomatch(glob_t *pglob, struct glob_limit *limit, const char *origpat) {
} }
static int static int
err_aborted(glob_t *pglob, int err, char *buf) { err_aborted(glob_t *pglob, int err, char *buf)
if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) || {
(pglob->gl_flags & GLOB_ERR)) 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 (GLOB_ABORTED);
}
return (0); return (0);
} }