/* * 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 #include #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); }