Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 20 Jun 2025 11:10:50 GMT
From:      Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= <des@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: deeebfdecab5 - main - libc: Add fscandir(), fscandir_b(), scandirat_b().
Message-ID:  <202506201110.55KBAo1Q051229@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=deeebfdecab56729fa898271ae53d01c8e156302

commit deeebfdecab56729fa898271ae53d01c8e156302
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-06-20 11:10:23 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-06-20 11:10:23 +0000

    libc: Add fscandir(), fscandir_b(), scandirat_b().
    
    While here, clean up scandir() a bit and improve the documentation.
    
    MFC after:      never
    Sponsored by:   Klara, Inc.
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D50935
---
 include/dirent.h                         |  13 ++++
 lib/libc/gen/Makefile.inc                |   5 +-
 lib/libc/gen/Symbol.map                  |   3 +
 lib/libc/gen/scandir.3                   |  94 ++++++++++++++++++++++--
 lib/libc/gen/scandir.c                   | 101 +++++++++++++++++---------
 lib/libc/tests/gen/Makefile              |   6 +-
 lib/libc/tests/gen/scandir_blocks_test.c | 118 +++++++++++++++++++++++++++++++
 lib/libc/tests/gen/scandir_test.c        | 112 +++++++++++++++++++++++++++++
 8 files changed, 410 insertions(+), 42 deletions(-)

diff --git a/include/dirent.h b/include/dirent.h
index 460be40c1064..00319c0a8bd0 100644
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -130,9 +130,22 @@ int	 scandir_b(const char *, struct dirent ***,
 #endif
 #endif
 #if __BSD_VISIBLE
+int	 fscandir(int, struct dirent ***,
+	    int (*)(const struct dirent *), int (*)(const struct dirent **,
+	    const struct dirent **));
+#ifdef __BLOCKS__
+int	 fscandir_b(int, struct dirent ***,
+	    int (^)(const struct dirent *),
+	    int (^)(const struct dirent **, const struct dirent **));
+#endif
 int	 scandirat(int, const char *, struct dirent ***,
 	    int (*)(const struct dirent *), int (*)(const struct dirent **,
 	    const struct dirent **));
+#ifdef __BLOCKS__
+int	 scandirat_b(int, const char *, struct dirent ***,
+	    int (^)(const struct dirent *),
+	    int (^)(const struct dirent **, const struct dirent **));
+#endif
 #endif
 #if __XSI_VISIBLE
 void	 seekdir(DIR *, long);
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index 1ab3b026ac07..f2f5afbb24d8 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -499,8 +499,11 @@ MLINKS+=rand48.3 _rand48.3 \
 MLINKS+=rtld_get_var.3 \
 	rtld_set_var.3
 MLINKS+=scandir.3 alphasort.3 \
-	scandir.3 scandirat.3 \
+	scandir.3 fscandir.3 \
+	scandir.3 fscandir_b.3 \
 	scandir.3 scandir_b.3 \
+	scandir.3 scandirat.3 \
+	scandir.3 scandirat_b.3 \
 	scandir.3 versionsort.3
 MLINKS+=sem_open.3 sem_close.3 \
 	sem_open.3 sem_unlink.3
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
index 765db07019b5..e7483d3e6ec2 100644
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -458,11 +458,14 @@ FBSD_1.8 {
 	aio_read2;
 	aio_write2;
 	execvpe;
+	fscandir;
+	fscandir_b;
 	fts_open_b;
 	glob_b;
 	psiginfo;
 	rtld_get_var;
 	rtld_set_var;
+	scandirat_b;
 	uexterr_gettext;
 	sig2str;
 	str2sig;
diff --git a/lib/libc/gen/scandir.3 b/lib/libc/gen/scandir.3
index 6656842c251f..f74bd1f23613 100644
--- a/lib/libc/gen/scandir.3
+++ b/lib/libc/gen/scandir.3
@@ -25,13 +25,16 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd August 31, 2023
+.Dd June 19, 2025
 .Dt SCANDIR 3
 .Os
 .Sh NAME
 .Nm scandir ,
+.Nm fscandir ,
 .Nm scandirat ,
 .Nm scandir_b ,
+.Nm fscandir_b ,
+.Nm fscandirat_b ,
 .Nm alphasort ,
 .Nm versionsort
 .Nd scan a directory
@@ -47,6 +50,13 @@
 .Fa "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
 .Fc
 .Ft int
+.Fo fscandir
+.Fa "int dirfd"
+.Fa "struct dirent ***namelist"
+.Fa "int \*(lp*select\*(rp\*(lpconst struct dirent *\*(rp"
+.Fa "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
+.Fc
+.Ft int
 .Fo scandirat
 .Fa "int dirfd"
 .Fa "const char *dirname"
@@ -62,6 +72,21 @@
 .Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
 .Fc
 .Ft int
+.Fo fscandir_b
+.Fa "int dirfd"
+.Fa "struct dirent ***namelist"
+.Fa "int \*(lp^select\*(rp\*(lpconst struct dirent *\*(rp"
+.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
+.Fc
+.Ft int
+.Fo scandirat_b
+.Fa "int dirfd"
+.Fa "const char *dirname"
+.Fa "struct dirent ***namelist"
+.Fa "int \*(lp^select\*(rp\*(lpconst struct dirent *\*(rp"
+.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
+.Fc
+.Ft int
 .Fn alphasort "const struct dirent **d1" "const struct dirent **d2"
 .Ft int
 .Fn versionsort "const struct dirent **d1" "const struct dirent **d2"
@@ -118,6 +143,13 @@ The memory allocated for the array can be deallocated with
 by freeing each pointer in the array and then the array itself.
 .Pp
 The
+.Fn fscandir
+function is similar to
+.Fn scandir ,
+but takes a file descriptor referencing a directory instead of a path.
+The file descriptor is left open on return, regardless of outcome.
+.Pp
+The
 .Fn scandirat
 function is similar to
 .Fn scandir ,
@@ -151,17 +183,37 @@ See
 for additional details.
 .Pp
 The
-.Fn scandir_b
-function behaves in the same way as
+.Fn scandir_b ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions behave in the same way as
 .Fn scandir ,
-but takes blocks as arguments instead of function pointers and calls
+.Fn fscandir ,
+and
+.Fn scandirat ,
+respectively,
+but take blocks as arguments instead of function pointers and call
 .Fn qsort_b
 rather than
 .Fn qsort .
 .Sh DIAGNOSTICS
-Returns \-1 if the directory cannot be opened for reading or if
+The
+.Fn scandir ,
+.Fn fscandir ,
+.Fn scandirat ,
+.Fn scandir_b ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions return the number of directory entries found on succes.
+If the directory cannot be opened for reading, an error occurs
+while reading the directory, or
 .Xr malloc 3
-cannot allocate enough memory to hold all the data structures.
+cannot allocate enough memory to hold all the directory entries,
+they return \-1 and set
+.Va errno
+to an appropriate value.
 .Sh SEE ALSO
 .Xr openat 2 ,
 .Xr directory 3 ,
@@ -172,8 +224,25 @@ cannot allocate enough memory to hold all the data structures.
 .Xr dir 5
 .Sh STANDARDS
 The
+.Fn alphasort
+and
+.Fn scandir
+functions are expected to conform to
+.St -p1003.1-2008 .
+The
+.Fn scandirat
+and
 .Fn versionsort
-function is a GNU extension and conforms to no standard.
+functions are GNU extensions and conform to no standard.
+The
+.Fn fscandir ,
+.Fn scandir_b ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions are
+.Fx
+extensions.
 .Sh HISTORY
 The
 .Fn scandir
@@ -182,8 +251,19 @@ and
 functions appeared in
 .Bx 4.2 .
 The
+.Fn scandir_b
+function was added in
+.Fx 11.0 .
+The
 .Fn scandirat
 and
 .Fn versionsort
 functions were added in
 .Fx 13.2 .
+The
+.Fn fscandir ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions were added in
+.Fx 15.0 .
diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c
index f59f57047278..172937392ddc 100644
--- a/lib/libc/gen/scandir.c
+++ b/lib/libc/gen/scandir.c
@@ -38,6 +38,7 @@
 
 #include "namespace.h"
 #include <dirent.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
@@ -64,7 +65,7 @@ static int scandir_thunk_cmp(const void *p1, const void *p2, void *thunk);
 
 static int
 #ifdef I_AM_SCANDIR_B
-scandir_b_dirp(DIR *dirp, struct dirent ***namelist, select_block select,
+scandir_dirp_b(DIR *dirp, struct dirent ***namelist, select_block select,
     dcomp_block dcomp)
 #else
 scandir_dirp(DIR *dirp, struct dirent ***namelist,
@@ -72,14 +73,9 @@ scandir_dirp(DIR *dirp, struct dirent ***namelist,
     const struct dirent **))
 #endif
 {
-	struct dirent *d, *p, **names = NULL;
-	size_t arraysz, numitems;
-
-	numitems = 0;
-	arraysz = 32;	/* initial estimate of the array size */
-	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
-	if (names == NULL)
-		goto fail;
+	struct dirent *d, *p = NULL, **names = NULL, **names2;
+	size_t arraysz = 0, numitems = 0;
+	int serrno;
 
 	while ((d = readdir(dirp)) != NULL) {
 		if (select != NULL && !SELECT(d))
@@ -87,33 +83,27 @@ scandir_dirp(DIR *dirp, struct dirent ***namelist,
 		/*
 		 * Make a minimum size copy of the data
 		 */
-		p = (struct dirent *)malloc(_GENERIC_DIRSIZ(d));
+		p = malloc(_GENERIC_DIRSIZ(d));
 		if (p == NULL)
 			goto fail;
 		p->d_fileno = d->d_fileno;
 		p->d_type = d->d_type;
 		p->d_reclen = d->d_reclen;
 		p->d_namlen = d->d_namlen;
-		bcopy(d->d_name, p->d_name, p->d_namlen + 1);
+		memcpy(p->d_name, d->d_name, p->d_namlen + 1);
 		/*
 		 * Check to make sure the array has space left and
 		 * realloc the maximum size.
 		 */
 		if (numitems >= arraysz) {
-			struct dirent **names2;
-
-			names2 = reallocarray(names, arraysz,
-			    2 * sizeof(struct dirent *));
-			if (names2 == NULL) {
-				free(p);
+			arraysz = arraysz ? arraysz * 2 : 32;
+			names2 = reallocarray(names, arraysz, sizeof(*names));
+			if (names2 == NULL)
 				goto fail;
-			}
 			names = names2;
-			arraysz *= 2;
 		}
 		names[numitems++] = p;
 	}
-	closedir(dirp);
 	if (numitems && dcomp != NULL)
 #ifdef I_AM_SCANDIR_B
 		qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp);
@@ -125,10 +115,12 @@ scandir_dirp(DIR *dirp, struct dirent ***namelist,
 	return (numitems);
 
 fail:
+	serrno = errno;
+	free(p);
 	while (numitems > 0)
 		free(names[--numitems]);
 	free(names);
-	closedir(dirp);
+	errno = serrno;
 	return (-1);
 }
 
@@ -143,39 +135,82 @@ scandir(const char *dirname, struct dirent ***namelist,
 #endif
 {
 	DIR *dirp;
+	int ret, serrno;
 
 	dirp = opendir(dirname);
 	if (dirp == NULL)
 		return (-1);
-	return (
+	ret =
 #ifdef I_AM_SCANDIR_B
-	    scandir_b_dirp
+	    scandir_dirp_b
 #else
 	    scandir_dirp
 #endif
-	    (dirp, namelist, select, dcomp));
+	    (dirp, namelist, select, dcomp);
+	serrno = errno;
+	closedir(dirp);
+	errno = serrno;
+	return (ret);
 }
 
-#ifndef I_AM_SCANDIR_B
 int
-scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
+#ifdef I_AM_SCANDIR_B
+fscandir_b(int dirfd, struct dirent ***namelist, select_block select,
+    dcomp_block dcomp)
+#else
+fscandir(int dirfd, struct dirent ***namelist,
     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
     const struct dirent **))
+#endif
 {
 	DIR *dirp;
-	int fd;
+	int ret, serrno;
+
+	dirp = fdopendir(dirfd);
+	if (dirp == NULL)
+		return (-1);
+	ret =
+#ifdef I_AM_SCANDIR_B
+	    scandir_dirp_b
+#else
+	    scandir_dirp
+#endif
+	    (dirp, namelist, select, dcomp);
+	serrno = errno;
+	fdclosedir(dirp);
+	errno = serrno;
+	return (ret);
+}
+
+int
+#ifdef I_AM_SCANDIR_B
+scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist,
+    select_block select, dcomp_block dcomp)
+#else
+scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
+    int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
+    const struct dirent **))
+#endif
+{
+	int fd, ret, serrno;
 
 	fd = _openat(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
 	if (fd == -1)
 		return (-1);
-	dirp = fdopendir(fd);
-	if (dirp == NULL) {
-		_close(fd);
-		return (-1);
-	}
-	return (scandir_dirp(dirp, namelist, select, dcomp));
+	ret =
+#ifdef I_AM_SCANDIR_B
+	    fscandir_b
+#else
+	    fscandir
+#endif
+	    (fd, namelist, select, dcomp);
+	serrno = errno;
+	_close(fd);
+	errno = serrno;
+	return (ret);
 }
 
+#ifndef I_AM_SCANDIR_B
 /*
  * Alphabetic order comparison routine for those who want it.
  * POSIX 2008 requires that alphasort() uses strcoll().
diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile
index 4776dc4c774d..b7df4b1d037b 100644
--- a/lib/libc/tests/gen/Makefile
+++ b/lib/libc/tests/gen/Makefile
@@ -22,6 +22,10 @@ ATF_TESTS_C+=		makecontext_test
 ATF_TESTS_C+=		popen_test
 ATF_TESTS_C+=		posix_spawn_test
 ATF_TESTS_C+=		realpath2_test
+ATF_TESTS_C+=		scandir_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+=		scandir_blocks_test
+.endif
 ATF_TESTS_C+=		sig2str_test
 ATF_TESTS_C+=		sigsetops_test
 ATF_TESTS_C+=		wordexp_test
@@ -101,7 +105,7 @@ TESTS_SUBDIRS=	execve
 TESTS_SUBDIRS+=	posix_spawn
 
 # Tests that require blocks support
-.for t in fts_blocks_test glob_blocks_test
+.for t in fts_blocks_test glob_blocks_test scandir_blocks_test
 CFLAGS.${t}.c+=		-fblocks
 LIBADD.${t}+=		BlocksRuntime
 .endfor
diff --git a/lib/libc/tests/gen/scandir_blocks_test.c b/lib/libc/tests/gen/scandir_blocks_test.c
new file mode 100644
index 000000000000..28aeef4e7d4c
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_blocks_test.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+scandir_blocks_prepare(const struct atf_tc *tc)
+{
+	ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+	ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
+	ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+	ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
+	ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
+}
+
+static void
+scandir_blocks_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
+{
+	ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
+	ATF_CHECK_STREQ("link", namelist[0]->d_name);
+	ATF_CHECK_STREQ("file", namelist[1]->d_name);
+	ATF_CHECK_STREQ("dir", namelist[2]->d_name);
+	ATF_CHECK_STREQ("..", namelist[3]->d_name);
+	ATF_CHECK_STREQ(".", namelist[4]->d_name);
+}
+
+ATF_TC(scandir_b_test);
+ATF_TC_HEAD(scandir_b_test, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Test scandir_b()");
+}
+ATF_TC_BODY(scandir_b_test, tc)
+{
+	struct dirent **namelist = NULL;
+	int i, ret;
+
+	scandir_blocks_prepare(tc);
+	ret = scandir_b("dir", &namelist,
+	    ^(const struct dirent *ent) {
+		    return (strcmp(ent->d_name, "skip") != 0);
+	    },
+	    ^(const struct dirent **a, const struct dirent **b) {
+		    return (strcmp((*b)->d_name, (*a)->d_name));
+	    });
+	scandir_blocks_verify(tc, ret, namelist);
+	for (i = 0; i < ret; i++)
+		free(namelist[i]);
+	free(namelist);
+}
+
+ATF_TC(fscandir_b_test);
+ATF_TC_HEAD(fscandir_b_test, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Test fscandir_b()");
+}
+ATF_TC_BODY(fscandir_b_test, tc)
+{
+	struct dirent **namelist = NULL;
+	int fd, i, ret;
+
+	scandir_blocks_prepare(tc);
+	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+	ret = fscandir_b(fd, &namelist,
+	    ^(const struct dirent *ent) {
+		    return (strcmp(ent->d_name, "skip") != 0);
+	    },
+	    ^(const struct dirent **a, const struct dirent **b) {
+		    return (strcmp((*b)->d_name, (*a)->d_name));
+	    });
+	scandir_blocks_verify(tc, ret, namelist);
+	for (i = 0; i < ret; i++)
+		free(namelist[i]);
+	free(namelist);
+	ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TC(scandirat_b_test);
+ATF_TC_HEAD(scandirat_b_test, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Test scandirat_b()");
+}
+ATF_TC_BODY(scandirat_b_test, tc)
+{
+	struct dirent **namelist = NULL;
+	int fd, i, ret;
+
+	scandir_blocks_prepare(tc);
+	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
+	ret = scandirat_b(fd, ".", &namelist,
+	    ^(const struct dirent *ent) {
+		    return (strcmp(ent->d_name, "skip") != 0);
+	    },
+	    ^(const struct dirent **a, const struct dirent **b) {
+		    return (strcmp((*b)->d_name, (*a)->d_name));
+	    });
+	scandir_blocks_verify(tc, ret, namelist);
+	for (i = 0; i < ret; i++)
+		free(namelist[i]);
+	free(namelist);
+	ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, scandir_b_test);
+	ATF_TP_ADD_TC(tp, fscandir_b_test);
+	ATF_TP_ADD_TC(tp, scandirat_b_test);
+	return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/scandir_test.c b/lib/libc/tests/gen/scandir_test.c
new file mode 100644
index 000000000000..54848c0572ca
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_test.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+scandir_prepare(const struct atf_tc *tc)
+{
+	ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+	ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
+	ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+	ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
+	ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
+}
+
+static void
+scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
+{
+	ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
+	ATF_CHECK_STREQ("link", namelist[0]->d_name);
+	ATF_CHECK_STREQ("file", namelist[1]->d_name);
+	ATF_CHECK_STREQ("dir", namelist[2]->d_name);
+	ATF_CHECK_STREQ("..", namelist[3]->d_name);
+	ATF_CHECK_STREQ(".", namelist[4]->d_name);
+}
+
+static int
+scandir_select(const struct dirent *ent)
+{
+	return (strcmp(ent->d_name, "skip") != 0);
+}
+
+static int
+scandir_compare(const struct dirent **a, const struct dirent **b)
+{
+	return (strcmp((*b)->d_name, (*a)->d_name));
+}
+
+ATF_TC(scandir_test);
+ATF_TC_HEAD(scandir_test, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Test scandir()");
+}
+ATF_TC_BODY(scandir_test, tc)
+{
+	struct dirent **namelist = NULL;
+	int i, ret;
+
+	scandir_prepare(tc);
+	ret = scandir("dir", &namelist, scandir_select, scandir_compare);
+	scandir_verify(tc, ret, namelist);
+	for (i = 0; i < ret; i++)
+		free(namelist[i]);
+	free(namelist);
+}
+
+ATF_TC(fscandir_test);
+ATF_TC_HEAD(fscandir_test, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Test fscandir()");
+}
+ATF_TC_BODY(fscandir_test, tc)
+{
+	struct dirent **namelist = NULL;
+	int fd, i, ret;
+
+	scandir_prepare(tc);
+	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+	ret = fscandir(fd, &namelist, scandir_select, scandir_compare);
+	scandir_verify(tc, ret, namelist);
+	for (i = 0; i < ret; i++)
+		free(namelist[i]);
+	free(namelist);
+	ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TC(scandirat_test);
+ATF_TC_HEAD(scandirat_test, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Test scandirat()");
+}
+ATF_TC_BODY(scandirat_test, tc)
+{
+	struct dirent **namelist = NULL;
+	int fd, i, ret;
+
+	scandir_prepare(tc);
+	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
+	ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare);
+	scandir_verify(tc, ret, namelist);
+	for (i = 0; i < ret; i++)
+		free(namelist[i]);
+	free(namelist);
+	ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, scandir_test);
+	ATF_TP_ADD_TC(tp, fscandir_test);
+	ATF_TP_ADD_TC(tp, scandirat_test);
+	return (atf_no_error());
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202506201110.55KBAo1Q051229>