Add directory stream wrappers #40

Merged
andrew merged 2 commits from 10 into main 2026-08-03 15:26:00 -04:00
2 changed files with 31 additions and 1 deletions
Showing only changes of commit 83ff77608f - Show all commits

View File

@@ -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
Review

None of these functions have man style comment blocks for context on their operations, the same way other sources in this library do.

None of these functions have `man` style comment blocks for context on their operations, the same way other sources in this library do.
#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);

View File

@@ -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);