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:
+14
-2
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user