mirror of
https://git.freebsd.org/src.git
synced 2026-01-12 06:54:03 +00:00
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
This commit is contained in:
parent
609720ed97
commit
deeebfdeca
8 changed files with 410 additions and 42 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 .
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#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 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
|
||||
{
|
||||
DIR *dirp;
|
||||
int fd;
|
||||
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().
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
118
lib/libc/tests/gen/scandir_blocks_test.c
Normal file
118
lib/libc/tests/gen/scandir_blocks_test.c
Normal file
|
|
@ -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());
|
||||
}
|
||||
112
lib/libc/tests/gen/scandir_test.c
Normal file
112
lib/libc/tests/gen/scandir_test.c
Normal file
|
|
@ -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());
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue