Compare commits
4 Commits
d5e5e95c61
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
4b22913c57
|
|||
|
b879a94119
|
|||
|
83ff77608f
|
|||
|
a95ed6d13a
|
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>
|
#include <akstdlib.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "aksl_internal.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)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_opendir(const char *pathname, DIR **dest)
|
||||||
{
|
{
|
||||||
PREPARE_ERROR(e);
|
PREPARE_ERROR(e);
|
||||||
@@ -19,6 +29,10 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_opendir(const char *pathname, DIR **dest
|
|||||||
SUCCEED_RETURN(e);
|
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)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fdopendir(int fd, DIR **dest)
|
||||||
{
|
{
|
||||||
PREPARE_ERROR(e);
|
PREPARE_ERROR(e);
|
||||||
@@ -30,6 +44,10 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fdopendir(int fd, DIR **dest)
|
|||||||
SUCCEED_RETURN(e);
|
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)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_readdir(DIR *dirp, struct dirent *dest)
|
||||||
{
|
{
|
||||||
struct dirent *entry = NULL;
|
struct dirent *entry = NULL;
|
||||||
@@ -50,6 +68,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_readdir(DIR *dirp, struct dirent *dest)
|
|||||||
SUCCEED_RETURN(e);
|
SUCCEED_RETURN(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* closedir(3) reports its failure directly and invalidates dirp on success. */
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_closedir(DIR *dirp)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_closedir(DIR *dirp)
|
||||||
{
|
{
|
||||||
PREPARE_ERROR(e);
|
PREPARE_ERROR(e);
|
||||||
@@ -60,6 +79,10 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_closedir(DIR *dirp)
|
|||||||
SUCCEED_RETURN(e);
|
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)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_rewinddir(DIR *dirp)
|
||||||
{
|
{
|
||||||
PREPARE_ERROR(e);
|
PREPARE_ERROR(e);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Numeric conversion, complete. The cases that used to live in
|
* Numeric conversion, complete. The cases that used to live in
|
||||||
* tests/test_convert_strict.c -- registered as a known failure because the ato*
|
* tests/test_convert_strict.c -- registered as a known failure because the ato*
|
||||||
* family had no error channel at all (2.1.5) -- are folded back in here now that
|
* family had no error channel at all -- are folded back in here now that
|
||||||
* they pass: non-numeric input, empty input, trailing junk and overflow are all
|
* they pass: non-numeric input, empty input, trailing junk and overflow are all
|
||||||
* errors rather than silent wrong answers.
|
* errors rather than silent wrong answers.
|
||||||
*
|
*
|
||||||
@@ -54,7 +54,7 @@ static int test_atoi_rejects_null_arguments(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The whole reason 2.1.5 mattered. atoi("not a number") returned success and 0,
|
* The whole reason strict conversion matters. atoi("not a number") returned success and 0,
|
||||||
* so a caller could not tell a parse failure from a legitimate zero -- and
|
* so a caller could not tell a parse failure from a legitimate zero -- and
|
||||||
* akbasic had to write ~60 lines of its own strtoll/strtod wrapper to avoid
|
* akbasic had to write ~60 lines of its own strtoll/strtod wrapper to avoid
|
||||||
* turning four diagnosable BASIC errors into four wrong answers.
|
* turning four diagnosable BASIC errors into four wrong answers.
|
||||||
|
|||||||
@@ -23,14 +23,21 @@ static int test_open_errors_and_nulls(void)
|
|||||||
DIR *dirp = (DIR *)1;
|
DIR *dirp = (DIR *)1;
|
||||||
struct dirent entry;
|
struct dirent entry;
|
||||||
|
|
||||||
|
/* opendir propagates missing-path and non-directory failures. */
|
||||||
AKSL_CHECK_STATUS(aksl_opendir("/nonexistent/aksl/dir", &dirp), ENOENT);
|
AKSL_CHECK_STATUS(aksl_opendir("/nonexistent/aksl/dir", &dirp), ENOENT);
|
||||||
AKSL_CHECK(dirp == NULL);
|
AKSL_CHECK(dirp == NULL);
|
||||||
AKSL_CHECK(aksl_temp_file(file, sizeof(file)) == 0);
|
AKSL_CHECK(aksl_temp_file(file, sizeof(file)) == 0);
|
||||||
AKSL_CHECK_STATUS(aksl_opendir(file, &dirp), ENOTDIR);
|
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, &dirp), AKERR_NULLPOINTER);
|
||||||
AKSL_CHECK_STATUS(aksl_opendir(".", NULL), 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(-1, &dirp), EBADF);
|
||||||
AKSL_CHECK_STATUS(aksl_fdopendir(0, NULL), AKERR_NULLPOINTER);
|
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_STATUS(aksl_readdir(NULL, &entry), AKERR_NULLPOINTER);
|
||||||
AKSL_CHECK_OK(aksl_opendir(".", &dirp));
|
AKSL_CHECK_OK(aksl_opendir(".", &dirp));
|
||||||
AKSL_CHECK_STATUS(aksl_readdir(dirp, NULL), AKERR_NULLPOINTER);
|
AKSL_CHECK_STATUS(aksl_readdir(dirp, NULL), AKERR_NULLPOINTER);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
* The two confirmed list defects are fixed, so the tests that used to live in
|
* The two confirmed list defects are fixed, so the tests that used to live in
|
||||||
* tests/test_list_append_chain.c and tests/test_list_iterate_head.c are folded
|
* tests/test_list_append_chain.c and tests/test_list_iterate_head.c are folded
|
||||||
* back in here: aksl_list_append builds the whole chain rather than truncating
|
* back in here: aksl_list_append builds the whole chain rather than truncating
|
||||||
* it at the midpoint (2.1.1), and aksl_list_iterate starts at the head rather
|
* it at the midpoint, and aksl_list_iterate starts at the head rather than at
|
||||||
* than at whatever node Floyd's slow pointer happened to stop on (2.1.2).
|
* whatever node Floyd's slow pointer happened to stop on.
|
||||||
*
|
*
|
||||||
* The chain assertions build their lists with aksl_list_append now, which is the
|
* The chain assertions build their lists with aksl_list_append now, which is the
|
||||||
* point -- they could not, while append was the thing under suspicion.
|
* point -- they could not, while append was the thing under suspicion.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Memory wrappers, complete, plus the additions from
|
* Memory wrappers, complete.
|
||||||
* section 3.1.
|
|
||||||
*
|
*
|
||||||
* The three cases the wrapper plan left open are all pinned here: malloc(0) is AKERR_VALUE
|
* The three cases the wrapper plan left open are all pinned here: malloc(0) is AKERR_VALUE
|
||||||
* rather than whatever errno happened to hold when the platform's malloc(0)
|
* rather than whatever errno happened to hold when the platform's malloc(0)
|
||||||
|
|||||||
Reference in New Issue
Block a user