Add directory stream wrappers #40
25
src/dir.c
25
src/dir.c
@@ -1,10 +1,20 @@
|
||||
/* POSIX directory-stream wrappers. */
|
||||
/*
|
||||
* 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>
|
||||
|
andrew marked this conversation as resolved
|
||||
|
||||
#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);
|
||||
@@ -19,6 +29,10 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_opendir(const char *pathname, DIR **dest
|
||||
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);
|
||||
@@ -30,6 +44,10 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fdopendir(int fd, DIR **dest)
|
||||
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;
|
||||
@@ -50,6 +68,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_readdir(DIR *dirp, struct dirent *dest)
|
||||
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);
|
||||
@@ -60,6 +79,10 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_closedir(DIR *dirp)
|
||||
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);
|
||||
|
||||
@@ -23,14 +23,21 @@ static int test_open_errors_and_nulls(void)
|
||||
DIR *dirp = (DIR *)1;
|
||||
struct dirent entry;
|
||||
|
||||
/* opendir propagates missing-path and non-directory failures. */
|
||||
AKSL_CHECK_STATUS(aksl_opendir("/nonexistent/aksl/dir", &dirp), ENOENT);
|
||||
AKSL_CHECK(dirp == NULL);
|
||||
AKSL_CHECK(aksl_temp_file(file, sizeof(file)) == 0);
|
||||
AKSL_CHECK_STATUS(aksl_opendir(file, &dirp), ENOTDIR);
|
||||
|
||||
/* Every pointer required by the wrapped operation rejects NULL. */
|
||||
AKSL_CHECK_STATUS(aksl_opendir(NULL, &dirp), AKERR_NULLPOINTER);
|
||||
AKSL_CHECK_STATUS(aksl_opendir(".", NULL), AKERR_NULLPOINTER);
|
||||
|
||||
/* fdopendir propagates an invalid descriptor and validates its out-param. */
|
||||
AKSL_CHECK_STATUS(aksl_fdopendir(-1, &dirp), EBADF);
|
||||
AKSL_CHECK_STATUS(aksl_fdopendir(0, NULL), AKERR_NULLPOINTER);
|
||||
|
||||
/* The remaining wrappers reject NULL streams and destinations. */
|
||||
AKSL_CHECK_STATUS(aksl_readdir(NULL, &entry), AKERR_NULLPOINTER);
|
||||
AKSL_CHECK_OK(aksl_opendir(".", &dirp));
|
||||
AKSL_CHECK_STATUS(aksl_readdir(dirp, NULL), AKERR_NULLPOINTER);
|
||||
|
||||
Reference in New Issue
Block a user
None of these functions have
manstyle comment blocks for context on their operations, the same way other sources in this library do.