93 lines
3.0 KiB
C
93 lines
3.0 KiB
C
/*
|
|
* POSIX directory-stream wrappers.
|
|
*
|
|
* opendir(3), fdopendir(3), readdir(3), closedir(3), and rewinddir(3) expose
|
|
* three different failure conventions between them. These wrappers turn all
|
|
* three into error contexts and make end-of-directory an explicit AKERR_EOF.
|
|
*/
|
|
#include <akstdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include "aksl_internal.h"
|
|
|
|
/*
|
|
* opendir(3) returns NULL for failure. Clear *dest first so a failed open
|
|
* cannot leave the caller holding a stale directory stream.
|
|
*/
|
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_opendir(const char *pathname, DIR **dest)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "pathname=%p, dest=%p",
|
|
(void *)pathname, (void *)dest);
|
|
*dest = NULL;
|
|
FAIL_ZERO_RETURN(e, pathname, AKERR_NULLPOINTER, "pathname=%p, dest=%p",
|
|
(void *)pathname, (void *)dest);
|
|
errno = 0;
|
|
*dest = opendir(pathname);
|
|
FAIL_ZERO_RETURN(e, *dest, AKSL_ERRNO_OR(AKERR_IO), "pathname=%s", pathname);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
/*
|
|
* fdopendir(3) takes ownership of fd only when it succeeds. On success the
|
|
* matching aksl_closedir call closes both the stream and its descriptor.
|
|
*/
|
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fdopendir(int fd, DIR **dest)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "fd=%d, dest=%p", fd, (void *)dest);
|
|
*dest = NULL;
|
|
errno = 0;
|
|
*dest = fdopendir(fd);
|
|
FAIL_ZERO_RETURN(e, *dest, AKSL_ERRNO_OR(AKERR_IO), "fd=%d", fd);
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
/*
|
|
* readdir(3) owns and may reuse its returned storage. Copy the entry into the
|
|
* caller's destination, and use errno to distinguish failure from exhaustion.
|
|
*/
|
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_readdir(DIR *dirp, struct dirent *dest)
|
|
{
|
|
struct dirent *entry = NULL;
|
|
PREPARE_ERROR(e);
|
|
FAIL_ZERO_RETURN(e, dirp, AKERR_NULLPOINTER, "dirp=%p, dest=%p",
|
|
(void *)dirp, (void *)dest);
|
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dirp=%p, dest=%p",
|
|
(void *)dirp, (void *)dest);
|
|
|
|
/* readdir uses errno to distinguish failure from end-of-directory. */
|
|
errno = 0;
|
|
entry = readdir(dirp);
|
|
if ( entry == NULL ) {
|
|
FAIL_NONZERO_RETURN(e, errno, AKSL_ERRNO_OR(AKERR_IO), "readdir failed");
|
|
FAIL_RETURN(e, AKERR_EOF, "end of directory");
|
|
}
|
|
*dest = *entry;
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
/* closedir(3) reports its failure directly and invalidates dirp on success. */
|
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_closedir(DIR *dirp)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
FAIL_ZERO_RETURN(e, dirp, AKERR_NULLPOINTER, "dirp=%p", (void *)dirp);
|
|
errno = 0;
|
|
FAIL_NONZERO_RETURN(e, closedir(dirp), AKSL_ERRNO_OR(AKERR_IO),
|
|
"closedir failed");
|
|
SUCCEED_RETURN(e);
|
|
}
|
|
|
|
/*
|
|
* rewinddir(3) has no failure return. Preserve that contract after rejecting
|
|
* a NULL stream, which would otherwise be undefined behaviour.
|
|
*/
|
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_rewinddir(DIR *dirp)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
FAIL_ZERO_RETURN(e, dirp, AKERR_NULLPOINTER, "dirp=%p", (void *)dirp);
|
|
rewinddir(dirp);
|
|
SUCCEED_RETURN(e);
|
|
}
|