Compare commits
3 Commits
af5a4b861a
...
10
| Author | SHA1 | Date | |
|---|---|---|---|
|
0620370dd9
|
|||
|
83ff77608f
|
|||
|
d5e5e95c61
|
@@ -14,6 +14,7 @@ reasoning. The implementation is split by domain:
|
|||||||
| `src/stdlib.c` | memory, formatted output, string-to-number, realpath, djb2, and the list/tree traversal entry points |
|
| `src/stdlib.c` | memory, formatted output, string-to-number, realpath, djb2, and the list/tree traversal entry points |
|
||||||
| `src/string.c` | the `string.h` surface |
|
| `src/string.c` | the `string.h` surface |
|
||||||
| `src/stream.c` | `stdio.h` beyond open/read/write/close |
|
| `src/stream.c` | `stdio.h` beyond open/read/write/close |
|
||||||
|
| `src/dir.c` | directory stream open/read/rewind/close |
|
||||||
| `src/collections.c` | list and tree operations, hash map, string buffer, FNV-1a |
|
| `src/collections.c` | list and tree operations, hash map, string buffer, FNV-1a |
|
||||||
| `src/aksl_internal.h` | shared internals; not installed, not public |
|
| `src/aksl_internal.h` | shared internals; not installed, not public |
|
||||||
|
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ add_library(akstdlib SHARED
|
|||||||
src/string.c
|
src/string.c
|
||||||
src/stream.c
|
src/stream.c
|
||||||
src/stat.c
|
src/stat.c
|
||||||
|
src/dir.c
|
||||||
src/collections.c
|
src/collections.c
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -308,6 +309,7 @@ install(FILES
|
|||||||
set(AKSL_TESTS
|
set(AKSL_TESTS
|
||||||
collections
|
collections
|
||||||
convert
|
convert
|
||||||
|
dir
|
||||||
format
|
format
|
||||||
hashmap
|
hashmap
|
||||||
linkedlist
|
linkedlist
|
||||||
|
|||||||
@@ -83,11 +83,10 @@ having visited nothing.
|
|||||||
/* before */
|
/* before */
|
||||||
aksl_sprintf(&count, buf, "%s=%d", key, value);
|
aksl_sprintf(&count, buf, "%s=%d", key, value);
|
||||||
/* after */
|
/* after */
|
||||||
aksl_snprintf(&count, buf, sizeof(buf), "%s=%d", key, value);
|
aksl_snprintf(buf, sizeof(buf), "%s=%d", key, value);
|
||||||
```
|
```
|
||||||
|
|
||||||
Truncation is `AKERR_OUTOFBOUNDS` rather than a short success, and `*count` is
|
Truncation is `AKERR_OUTOFBOUNDS` rather than a short success.
|
||||||
`0` on any failure rather than `vsprintf`'s `-1`.
|
|
||||||
|
|
||||||
**`aksl_realpath` takes the destination's length.**
|
**`aksl_realpath` takes the destination's length.**
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* What this header needs in its own declarations, and no more:
|
* What this header needs in its own declarations, and no more:
|
||||||
|
* dirent.h DIR, struct dirent
|
||||||
* stdio.h FILE
|
* stdio.h FILE
|
||||||
* stddef.h size_t
|
* stddef.h size_t
|
||||||
* stdint.h uint32_t
|
* stdint.h uint32_t
|
||||||
@@ -72,11 +73,12 @@
|
|||||||
* which every consumer then got whether it wanted them or not. stddef.h in place
|
* which every consumer then got whether it wanted them or not. stddef.h in place
|
||||||
* of stdlib.h is the same size_t at a fraction of the namespace.
|
* of stdlib.h is the same size_t at a fraction of the namespace.
|
||||||
*/
|
*/
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
/* off_t, for the aksl_fseeko/aksl_ftello pair. POSIX, like aksl_realpath. */
|
/* off_t, for the aksl_fseeko/aksl_ftello pair. POSIX, like aksl_realpath. */
|
||||||
@@ -442,9 +444,8 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_memchr(const void *s, int c, size_t n, v
|
|||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/** @name Formatted output
|
/** @name Formatted output
|
||||||
*
|
*
|
||||||
* `*count` is the byte count written excluding the terminating NUL, and is 0 on
|
* Bounded output is checked for truncation and reports an error when the result
|
||||||
* every failure path -- never vsnprintf's -1, and never the length the output
|
* does not fit.
|
||||||
* *would* have been.
|
|
||||||
*
|
*
|
||||||
* There is no aksl_sprintf. It wrapped vsprintf, which cannot be bounded, and an
|
* There is no aksl_sprintf. It wrapped vsprintf, which cannot be bounded, and an
|
||||||
* error-handling wrapper around an unbounded write is the sharp edge this
|
* error-handling wrapper around an unbounded write is the sharp edge this
|
||||||
@@ -481,16 +482,15 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict strea
|
|||||||
* written, leaving the caller to notice by comparing that against the buffer
|
* written, leaving the caller to notice by comparing that against the buffer
|
||||||
* size -- the check this library exists to stop people forgetting.
|
* size -- the check this library exists to stop people forgetting.
|
||||||
*
|
*
|
||||||
* @param[out] count Bytes written excluding the NUL; 0 on failure. Required.
|
|
||||||
* @param[out] str Destination buffer. Required.
|
* @param[out] str Destination buffer. Required.
|
||||||
* @param[in] size Size of `str` including the terminator. Must be non-zero.
|
* @param[in] size Size of `str` including the terminator. Must be non-zero.
|
||||||
* @param[in] format printf format string. Required. Checked at compile time.
|
* @param[in] format printf format string. Required. Checked at compile time.
|
||||||
* @throws AKERR_NULLPOINTER If any pointer is NULL.
|
* @throws AKERR_NULLPOINTER If str or format is NULL.
|
||||||
* @throws AKERR_VALUE If size is 0.
|
* @throws AKERR_VALUE If size is 0.
|
||||||
* @throws AKERR_OUTOFBOUNDS If the output does not fit, naming both lengths.
|
* @throws AKERR_OUTOFBOUNDS If the output does not fit, naming both lengths.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_snprintf(int *count, char *restrict str, size_t size, const char *restrict format, ...) AKSL_PRINTF_FORMAT(4, 5);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_snprintf(char *restrict str, size_t size, const char *restrict format, ...) AKSL_PRINTF_FORMAT(3, 4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Format into a freshly allocated string.
|
* @brief Format into a freshly allocated string.
|
||||||
@@ -549,7 +549,6 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_vfprintf(int *count, FILE *restrict stre
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief vsnprintf(3) into a bounded buffer. The va_list form of aksl_snprintf.
|
* @brief vsnprintf(3) into a bounded buffer. The va_list form of aksl_snprintf.
|
||||||
* @param[out] count Bytes written excluding the NUL; 0 on failure. Required.
|
|
||||||
* @param[out] str Destination buffer. Required.
|
* @param[out] str Destination buffer. Required.
|
||||||
* @param[in] size Size of `str` including the terminator. Must be non-zero.
|
* @param[in] size Size of `str` including the terminator. Must be non-zero.
|
||||||
* @param[in] format printf format string. Required.
|
* @param[in] format printf format string. Required.
|
||||||
@@ -559,7 +558,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_vfprintf(int *count, FILE *restrict stre
|
|||||||
* @throws AKERR_OUTOFBOUNDS If the output does not fit.
|
* @throws AKERR_OUTOFBOUNDS If the output does not fit.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_vsnprintf(int *count, char *restrict str, size_t size, const char *restrict format, va_list args);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list args);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
@@ -892,6 +891,69 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_statvfs(const char *path, struct statvfs
|
|||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatvfs(int fd, struct statvfs *dest);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatvfs(int fd, struct statvfs *dest);
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
/* ====================================================================== */
|
||||||
|
/** @name Directories
|
||||||
|
*
|
||||||
|
* Directory entries are copied into caller-owned storage. `d_type` may be
|
||||||
|
* `DT_UNKNOWN`; callers that require a type must fall back to aksl_stat or
|
||||||
|
* aksl_fstatat.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* ====================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open a directory stream by path.
|
||||||
|
* @param[in] pathname Directory path. Required.
|
||||||
|
* @param[out] dest Open directory stream, or NULL on failure. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If pathname or dest is NULL.
|
||||||
|
* @throws AKERR_IO If opendir(3) fails without setting errno.
|
||||||
|
* @throws (errno) The errno opendir(3) set, reported directly as the status.
|
||||||
|
* @return NULL on success, an error context otherwise.
|
||||||
|
*/
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_opendir(const char *pathname, DIR **dest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open a directory stream from a file descriptor.
|
||||||
|
* @param[in] fd Open directory descriptor. Ownership transfers on success.
|
||||||
|
* @param[out] dest Open directory stream, or NULL on failure. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If dest is NULL.
|
||||||
|
* @throws AKERR_IO If fdopendir(3) fails without setting errno.
|
||||||
|
* @throws (errno) The errno fdopendir(3) set, reported directly as the status.
|
||||||
|
* @return NULL on success, an error context otherwise.
|
||||||
|
*/
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fdopendir(int fd, DIR **dest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read and copy the next directory entry.
|
||||||
|
* @param[in] dirp Open directory stream. Required.
|
||||||
|
* @param[out] dest Caller-owned storage for the copied entry. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If dirp or dest is NULL.
|
||||||
|
* @throws AKERR_EOF At the end of the directory stream.
|
||||||
|
* @throws AKERR_IO If readdir(3) fails without setting errno.
|
||||||
|
* @throws (errno) The errno readdir(3) set, reported directly as the status.
|
||||||
|
* @return NULL on success, an error context otherwise.
|
||||||
|
*/
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_readdir(DIR *dirp, struct dirent *dest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close a directory stream.
|
||||||
|
* @param[in] dirp Open directory stream. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If dirp is NULL.
|
||||||
|
* @throws AKERR_IO If closedir(3) fails without setting errno.
|
||||||
|
* @throws (errno) The errno closedir(3) set, reported directly as the status.
|
||||||
|
* @return NULL on success, an error context otherwise.
|
||||||
|
*/
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_closedir(DIR *dirp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset a directory stream to its beginning.
|
||||||
|
* @param[in] dirp Open directory stream. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If dirp is NULL.
|
||||||
|
* @return NULL on success, an error context otherwise.
|
||||||
|
*/
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_rewinddir(DIR *dirp);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/** @name Streams: open, read, write, close
|
/** @name Streams: open, read, write, close
|
||||||
|
|||||||
92
src/dir.c
Normal file
92
src/dir.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
21
src/stdlib.c
21
src/stdlib.c
@@ -416,10 +416,6 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fclose(FILE *stream)
|
|||||||
* register-save state on some ABIs. akbasic's text sink ran this UB on every
|
* register-save state on some ABIs. akbasic's text sink ran this UB on every
|
||||||
* line of program output without anything visibly misbehaving, which is
|
* line of program output without anything visibly misbehaving, which is
|
||||||
* exactly what made it worth fixing before something did.
|
* exactly what made it worth fixing before something did.
|
||||||
* - *count is written on every path. It used to be left holding vprintf's -1
|
|
||||||
* after a failure, so a caller who read the length rather than the status got
|
|
||||||
* a negative byte count out of a function that had already failed. It is now
|
|
||||||
* 0 whenever an error is raised.
|
|
||||||
* - errno is cleared before the call and read back through AKSL_ERRNO_OR, so a
|
* - errno is cleared before the call and read back through AKSL_ERRNO_OR, so a
|
||||||
* failure can never be reported with a stale -- or with a zero -- status.
|
* failure can never be reported with a stale -- or with a zero -- status.
|
||||||
*
|
*
|
||||||
@@ -485,34 +481,31 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict strea
|
|||||||
* *would* have written and silently drops the rest, which is the single most
|
* *would* have written and silently drops the rest, which is the single most
|
||||||
* common way a bounded write goes wrong unnoticed; a caller who wanted to know
|
* common way a bounded write goes wrong unnoticed; a caller who wanted to know
|
||||||
* would have had to compare the return against the buffer size by hand, which is
|
* would have had to compare the return against the buffer size by hand, which is
|
||||||
* the check this library exists to stop people forgetting. *count is the number
|
* the check this library exists to stop people forgetting. The bounded wrapper
|
||||||
* of bytes written excluding the terminating NUL, and is 0 on any failure.
|
* reports truncation instead.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_vsnprintf(int *count, char *restrict str, size_t size, const char *restrict format, va_list args)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list args)
|
||||||
{
|
{
|
||||||
int needed = 0;
|
int needed = 0;
|
||||||
PREPARE_ERROR(e);
|
PREPARE_ERROR(e);
|
||||||
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
FAIL_ZERO_RETURN(e, str, AKERR_NULLPOINTER, "str=%p, format=%p", (void *)str, (void *)format);
|
||||||
*count = 0;
|
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "str=%p, format=%p", (void *)str, (void *)format);
|
||||||
FAIL_ZERO_RETURN(e, str, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
|
||||||
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
|
||||||
FAIL_ZERO_RETURN(e, size, AKERR_VALUE, "size=0 leaves no room even for the terminating NUL");
|
FAIL_ZERO_RETURN(e, size, AKERR_VALUE, "size=0 leaves no room even for the terminating NUL");
|
||||||
errno = 0;
|
errno = 0;
|
||||||
needed = vsnprintf(str, size, format, args);
|
needed = vsnprintf(str, size, format, args);
|
||||||
FAIL_NONZERO_RETURN(e, (needed < 0), AKSL_ERRNO_OR(AKERR_IO), "Output error");
|
FAIL_NONZERO_RETURN(e, (needed < 0), AKSL_ERRNO_OR(AKERR_IO), "Output error");
|
||||||
FAIL_NONZERO_RETURN(e, ((size_t)needed >= size), AKERR_OUTOFBOUNDS,
|
FAIL_NONZERO_RETURN(e, ((size_t)needed >= size), AKERR_OUTOFBOUNDS,
|
||||||
"output truncated: %d bytes needed, %zu available", needed, size);
|
"output truncated: %d bytes needed, %zu available", needed, size);
|
||||||
*count = needed;
|
|
||||||
SUCCEED_RETURN(e);
|
SUCCEED_RETURN(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_snprintf(int *count, char *restrict str, size_t size, const char *restrict format, ...)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_snprintf(char *restrict str, size_t size, const char *restrict format, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
akerr_ErrorContext *raised = NULL;
|
akerr_ErrorContext *raised = NULL;
|
||||||
|
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
raised = aksl_vsnprintf(count, str, size, format, args);
|
raised = aksl_vsnprintf(str, size, format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return raised;
|
return raised;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,10 @@
|
|||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
char buf[64];
|
char buf[64];
|
||||||
int count = 0;
|
|
||||||
akerr_ErrorContext *raised = NULL;
|
akerr_ErrorContext *raised = NULL;
|
||||||
|
|
||||||
/* %d against a string. This is the line that must not build. */
|
/* %d against a string. This is the line that must not build. */
|
||||||
raised = aksl_snprintf(&count, buf, sizeof(buf), "%d", "not an int");
|
raised = aksl_snprintf(buf, sizeof(buf), "%d", "not an int");
|
||||||
|
|
||||||
return raised == NULL ? 0 : 1;
|
return raised == NULL ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|||||||
154
tests/test_dir.c
Normal file
154
tests/test_dir.c
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#include "aksl_capture.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static int make_directory(char *path, size_t size)
|
||||||
|
{
|
||||||
|
const char *tmp = getenv("TMPDIR");
|
||||||
|
if ( tmp == NULL || tmp[0] == '\0' ) {
|
||||||
|
tmp = "/tmp";
|
||||||
|
}
|
||||||
|
if ( (size_t)snprintf(path, size, "%s/aksl_dir_XXXXXX", tmp) >= size ) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return mkdtemp(path) == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_open_errors_and_nulls(void)
|
||||||
|
{
|
||||||
|
char file[AKSL_TMP_MAX];
|
||||||
|
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);
|
||||||
|
AKSL_CHECK_OK(aksl_closedir(dirp));
|
||||||
|
AKSL_CHECK_STATUS(aksl_closedir(NULL), AKERR_NULLPOINTER);
|
||||||
|
AKSL_CHECK_STATUS(aksl_rewinddir(NULL), AKERR_NULLPOINTER);
|
||||||
|
AKSL_CHECK(unlink(file) == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_copy_eof_rewind_and_fdopendir(void)
|
||||||
|
{
|
||||||
|
char path[AKSL_TMP_MAX], first_path[AKSL_TMP_MAX], second_path[AKSL_TMP_MAX];
|
||||||
|
struct dirent entry, saved;
|
||||||
|
char first_read[sizeof(entry.d_name)];
|
||||||
|
DIR *dirp = NULL;
|
||||||
|
int fd = -1, seen_first = 0, seen_second = 0;
|
||||||
|
|
||||||
|
AKSL_CHECK(make_directory(path, sizeof(path)) == 0);
|
||||||
|
AKSL_CHECK(snprintf(first_path, sizeof(first_path), "%s/first", path) < (int)sizeof(first_path));
|
||||||
|
AKSL_CHECK(snprintf(second_path, sizeof(second_path), "%s/second", path) < (int)sizeof(second_path));
|
||||||
|
fd = open(first_path, O_CREAT | O_WRONLY, 0600);
|
||||||
|
AKSL_CHECK(fd >= 0);
|
||||||
|
AKSL_CHECK(close(fd) == 0);
|
||||||
|
fd = open(second_path, O_CREAT | O_WRONLY, 0600);
|
||||||
|
AKSL_CHECK(fd >= 0);
|
||||||
|
AKSL_CHECK(close(fd) == 0);
|
||||||
|
|
||||||
|
AKSL_CHECK_OK(aksl_opendir(path, &dirp));
|
||||||
|
do {
|
||||||
|
akerr_ErrorContext *error = aksl_readdir(dirp, &entry);
|
||||||
|
if ( error != NULL ) {
|
||||||
|
int status = error->status;
|
||||||
|
RELEASE_ERROR(error);
|
||||||
|
AKSL_CHECK(status == AKERR_EOF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( strcmp(entry.d_name, "first") == 0 ) { saved = entry; seen_first++; }
|
||||||
|
if ( strcmp(entry.d_name, "second") == 0 ) seen_second++;
|
||||||
|
} while ( 1 );
|
||||||
|
AKSL_CHECK(seen_first == 1 && seen_second == 1);
|
||||||
|
AKSL_CHECK(strcmp(saved.d_name, "first") == 0);
|
||||||
|
AKSL_CHECK_OK(aksl_rewinddir(dirp));
|
||||||
|
AKSL_CHECK_OK(aksl_readdir(dirp, &entry));
|
||||||
|
AKSL_CHECK(snprintf(first_read, sizeof(first_read), "%s", entry.d_name) < (int)sizeof(first_read));
|
||||||
|
AKSL_CHECK_OK(aksl_readdir(dirp, &entry));
|
||||||
|
AKSL_CHECK_OK(aksl_rewinddir(dirp));
|
||||||
|
AKSL_CHECK_OK(aksl_readdir(dirp, &entry));
|
||||||
|
AKSL_CHECK(strcmp(entry.d_name, first_read) == 0);
|
||||||
|
AKSL_CHECK_OK(aksl_closedir(dirp));
|
||||||
|
|
||||||
|
fd = open(path, O_RDONLY | O_DIRECTORY);
|
||||||
|
AKSL_CHECK(fd >= 0);
|
||||||
|
AKSL_CHECK_OK(aksl_fdopendir(fd, &dirp));
|
||||||
|
AKSL_CHECK_OK(aksl_readdir(dirp, &entry));
|
||||||
|
AKSL_CHECK_OK(aksl_closedir(dirp));
|
||||||
|
AKSL_CHECK(unlink(first_path) == 0);
|
||||||
|
AKSL_CHECK(unlink(second_path) == 0);
|
||||||
|
AKSL_CHECK(rmdir(path) == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_empty_directory_reaches_eof_after_dot_entries(void)
|
||||||
|
{
|
||||||
|
char path[AKSL_TMP_MAX];
|
||||||
|
struct dirent entry;
|
||||||
|
DIR *dirp = NULL;
|
||||||
|
int count = 0, saw_dot = 0, saw_dotdot = 0;
|
||||||
|
|
||||||
|
AKSL_CHECK(make_directory(path, sizeof(path)) == 0);
|
||||||
|
AKSL_CHECK_OK(aksl_opendir(path, &dirp));
|
||||||
|
for ( ;; ) {
|
||||||
|
akerr_ErrorContext *error = aksl_readdir(dirp, &entry);
|
||||||
|
if ( error != NULL ) {
|
||||||
|
int status = error->status;
|
||||||
|
RELEASE_ERROR(error);
|
||||||
|
AKSL_CHECK(status == AKERR_EOF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
if ( strcmp(entry.d_name, ".") == 0 ) saw_dot++;
|
||||||
|
if ( strcmp(entry.d_name, "..") == 0 ) saw_dotdot++;
|
||||||
|
}
|
||||||
|
AKSL_CHECK(count == 2 && saw_dot == 1 && saw_dotdot == 1);
|
||||||
|
AKSL_CHECK_OK(aksl_closedir(dirp));
|
||||||
|
AKSL_CHECK(rmdir(path) == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_permission_denied(void)
|
||||||
|
{
|
||||||
|
char path[AKSL_TMP_MAX];
|
||||||
|
DIR *dirp = NULL;
|
||||||
|
AKSL_CHECK(make_directory(path, sizeof(path)) == 0);
|
||||||
|
AKSL_CHECK(chmod(path, 0000) == 0);
|
||||||
|
if ( geteuid() == 0 ) {
|
||||||
|
fprintf(stderr, " (skipped: running as root, chmod 000 denies nothing)\n");
|
||||||
|
} else {
|
||||||
|
AKSL_CHECK_STATUS(aksl_opendir(path, &dirp), EACCES);
|
||||||
|
}
|
||||||
|
AKSL_CHECK(chmod(path, 0700) == 0);
|
||||||
|
AKSL_CHECK(rmdir(path) == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int failures = 0;
|
||||||
|
AKSL_RUN(failures, test_open_errors_and_nulls);
|
||||||
|
AKSL_RUN(failures, test_copy_eof_rewind_and_fdopendir);
|
||||||
|
AKSL_RUN(failures, test_empty_directory_reaches_eof_after_dot_entries);
|
||||||
|
AKSL_RUN(failures, test_permission_denied);
|
||||||
|
AKSL_REPORT(failures);
|
||||||
|
}
|
||||||
@@ -2,10 +2,8 @@
|
|||||||
* Formatted-output wrappers: aksl_printf / aksl_fprintf / aksl_snprintf and
|
* Formatted-output wrappers: aksl_printf / aksl_fprintf / aksl_snprintf and
|
||||||
* their va_list forms.
|
* their va_list forms.
|
||||||
*
|
*
|
||||||
* Formatted output, complete. Each happy path asserts both halves of the
|
* Formatted output, complete. Each happy path asserts the text that actually
|
||||||
* contract -- the byte count handed back through *count and the text that
|
* landed somewhere, and every pointer argument is checked for its NULL guard.
|
||||||
* actually landed somewhere -- and every pointer argument is checked for its
|
|
||||||
* NULL guard.
|
|
||||||
*
|
*
|
||||||
* Readback goes through plain libc rather than aksl_fread so that a failure here
|
* Readback goes through plain libc rather than aksl_fread so that a failure here
|
||||||
* points at the formatted-output wrapper under test and not at the stream
|
* points at the formatted-output wrapper under test and not at the stream
|
||||||
@@ -39,14 +37,12 @@ static long read_file(const char *path, char *buf, size_t n)
|
|||||||
return (long)got;
|
return (long)got;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_snprintf_writes_text_and_count(void)
|
static int test_snprintf_writes_text(void)
|
||||||
{
|
{
|
||||||
char buf[64];
|
char buf[64];
|
||||||
int count = -1;
|
|
||||||
|
|
||||||
memset(buf, 0x00, sizeof(buf));
|
memset(buf, 0x00, sizeof(buf));
|
||||||
AKSL_CHECK_OK(aksl_snprintf(&count, buf, sizeof(buf), "%s=%d", "x", 7));
|
AKSL_CHECK_OK(aksl_snprintf(buf, sizeof(buf), "%s=%d", "x", 7));
|
||||||
AKSL_CHECK(count == 3);
|
|
||||||
AKSL_CHECK(strcmp(buf, "x=7") == 0);
|
AKSL_CHECK(strcmp(buf, "x=7") == 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -54,10 +50,8 @@ static int test_snprintf_writes_text_and_count(void)
|
|||||||
static int test_snprintf_empty_format_writes_nothing(void)
|
static int test_snprintf_empty_format_writes_nothing(void)
|
||||||
{
|
{
|
||||||
char buf[8] = { 'z', 'z', 'z', 'z', 'z', 'z', 'z', 'z' };
|
char buf[8] = { 'z', 'z', 'z', 'z', 'z', 'z', 'z', 'z' };
|
||||||
int count = -1;
|
|
||||||
|
|
||||||
AKSL_CHECK_OK(aksl_snprintf(&count, buf, sizeof(buf), "%s", ""));
|
AKSL_CHECK_OK(aksl_snprintf(buf, sizeof(buf), "%s", ""));
|
||||||
AKSL_CHECK(count == 0);
|
|
||||||
AKSL_CHECK(buf[0] == '\0');
|
AKSL_CHECK(buf[0] == '\0');
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -66,18 +60,17 @@ static int test_snprintf_empty_format_writes_nothing(void)
|
|||||||
* The case that could not be written while the wrapper was aksl_sprintf: output
|
* The case that could not be written while the wrapper was aksl_sprintf: output
|
||||||
* longer than the destination. snprintf(3) would truncate, NUL-terminate and
|
* longer than the destination. snprintf(3) would truncate, NUL-terminate and
|
||||||
* report the length it *would* have written, leaving the caller to notice; here
|
* report the length it *would* have written, leaving the caller to notice; here
|
||||||
* it is an error, and *count is 0 rather than the would-have-been length.
|
* it is an error.
|
||||||
*/
|
*/
|
||||||
static int test_snprintf_truncation_is_an_error(void)
|
static int test_snprintf_truncation_is_an_error(void)
|
||||||
{
|
{
|
||||||
char buf[8];
|
char buf[8];
|
||||||
int count = -1;
|
|
||||||
|
|
||||||
memset(buf, 0x00, sizeof(buf));
|
memset(buf, 0x00, sizeof(buf));
|
||||||
AKSL_CHECK_STATUS_MSG_CONTAINS(
|
AKSL_CHECK_STATUS_MSG_CONTAINS(
|
||||||
aksl_snprintf(&count, buf, sizeof(buf), "%s", "far too long for eight bytes"),
|
aksl_snprintf(buf, sizeof(buf), "%s", "far too long for eight bytes"),
|
||||||
AKERR_OUTOFBOUNDS, "truncated");
|
AKERR_OUTOFBOUNDS, "truncated");
|
||||||
AKSL_CHECK(count == 0);
|
AKSL_CHECK(strcmp(buf, "far too") == 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,34 +78,27 @@ static int test_snprintf_truncation_is_an_error(void)
|
|||||||
static int test_snprintf_boundary_is_exact(void)
|
static int test_snprintf_boundary_is_exact(void)
|
||||||
{
|
{
|
||||||
char buf[8];
|
char buf[8];
|
||||||
int count = -1;
|
|
||||||
|
|
||||||
/* 7 characters plus the NUL is exactly sizeof(buf). */
|
/* 7 characters plus the NUL is exactly sizeof(buf). */
|
||||||
AKSL_CHECK_OK(aksl_snprintf(&count, buf, sizeof(buf), "%s", "1234567"));
|
AKSL_CHECK_OK(aksl_snprintf(buf, sizeof(buf), "%s", "1234567"));
|
||||||
AKSL_CHECK(count == 7);
|
|
||||||
AKSL_CHECK(strcmp(buf, "1234567") == 0);
|
AKSL_CHECK(strcmp(buf, "1234567") == 0);
|
||||||
|
|
||||||
count = -1;
|
AKSL_CHECK_STATUS(aksl_snprintf(buf, sizeof(buf), "%s", "12345678"),
|
||||||
AKSL_CHECK_STATUS(aksl_snprintf(&count, buf, sizeof(buf), "%s", "12345678"),
|
|
||||||
AKERR_OUTOFBOUNDS);
|
AKERR_OUTOFBOUNDS);
|
||||||
AKSL_CHECK(count == 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_snprintf_rejects_null_arguments_and_zero_size(void)
|
static int test_snprintf_rejects_null_arguments_and_zero_size(void)
|
||||||
{
|
{
|
||||||
char buf[8];
|
char buf[8];
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
memset(buf, 0x00, sizeof(buf));
|
memset(buf, 0x00, sizeof(buf));
|
||||||
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(NULL, buf, sizeof(buf), "x"),
|
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(NULL, 8, "x"),
|
||||||
AKERR_NULLPOINTER, "count=");
|
|
||||||
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(&count, NULL, 8, "x"),
|
|
||||||
AKERR_NULLPOINTER, "str=");
|
AKERR_NULLPOINTER, "str=");
|
||||||
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(&count, buf, sizeof(buf), NULL),
|
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(buf, sizeof(buf), NULL),
|
||||||
AKERR_NULLPOINTER, "format=");
|
AKERR_NULLPOINTER, "format=");
|
||||||
/* size 0 leaves no room even for the terminator, so there is nothing to do. */
|
/* size 0 leaves no room even for the terminator, so there is nothing to do. */
|
||||||
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(&count, buf, 0, "x"),
|
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_snprintf(buf, 0, "x"),
|
||||||
AKERR_VALUE, "size=0");
|
AKERR_VALUE, "size=0");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -274,14 +260,14 @@ static int test_asprintf_allocates_to_fit(void)
|
|||||||
* wanted them exposed so consumers can write their own variadic wrappers. This
|
* wanted them exposed so consumers can write their own variadic wrappers. This
|
||||||
* is a consumer doing exactly that.
|
* is a consumer doing exactly that.
|
||||||
*/
|
*/
|
||||||
static akerr_ErrorContext AKERR_NOIGNORE *consumer_wrapper(int *count, char *buf, size_t n,
|
static akerr_ErrorContext AKERR_NOIGNORE *consumer_wrapper(char *buf, size_t n,
|
||||||
const char *fmt, ...)
|
const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
akerr_ErrorContext *raised = NULL;
|
akerr_ErrorContext *raised = NULL;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
raised = aksl_vsnprintf(count, buf, n, fmt, args);
|
raised = aksl_vsnprintf(buf, n, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return raised;
|
return raised;
|
||||||
}
|
}
|
||||||
@@ -289,17 +275,14 @@ static akerr_ErrorContext AKERR_NOIGNORE *consumer_wrapper(int *count, char *buf
|
|||||||
static int test_va_list_forms_are_usable_from_outside(void)
|
static int test_va_list_forms_are_usable_from_outside(void)
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int count = -1;
|
|
||||||
|
|
||||||
memset(buf, 0x00, sizeof(buf));
|
memset(buf, 0x00, sizeof(buf));
|
||||||
AKSL_CHECK_OK(consumer_wrapper(&count, buf, sizeof(buf), "%s/%d", "via", 3));
|
AKSL_CHECK_OK(consumer_wrapper(buf, sizeof(buf), "%s/%d", "via", 3));
|
||||||
AKSL_CHECK(count == 5);
|
|
||||||
AKSL_CHECK(strcmp(buf, "via/3") == 0);
|
AKSL_CHECK(strcmp(buf, "via/3") == 0);
|
||||||
|
|
||||||
/* The error contract survives the extra layer intact. */
|
/* The error contract survives the extra layer intact. */
|
||||||
AKSL_CHECK_STATUS(consumer_wrapper(&count, buf, 4, "%s", "too long"),
|
AKSL_CHECK_STATUS(consumer_wrapper(buf, 4, "%s", "too long"),
|
||||||
AKERR_OUTOFBOUNDS);
|
AKERR_OUTOFBOUNDS);
|
||||||
AKSL_CHECK(count == 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,14 +295,12 @@ static int test_va_list_forms_are_usable_from_outside(void)
|
|||||||
static int test_variadic_wrappers_survive_repeated_calls(void)
|
static int test_variadic_wrappers_survive_repeated_calls(void)
|
||||||
{
|
{
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int count = 0;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for ( i = 0; i < 512; i++ ) {
|
for ( i = 0; i < 512; i++ ) {
|
||||||
AKSL_CHECK_OK(aksl_snprintf(&count, buf, sizeof(buf), "%d %s %ld %c %f",
|
AKSL_CHECK_OK(aksl_snprintf(buf, sizeof(buf), "%d %s %ld %c %f",
|
||||||
i, "iteration", (long)i, 'x', (double)i));
|
i, "iteration", (long)i, 'x', (double)i));
|
||||||
AKSL_CHECK(count > 0);
|
AKSL_CHECK(strlen(buf) > 0);
|
||||||
AKSL_CHECK((size_t)count == strlen(buf));
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -330,7 +311,7 @@ int main(void)
|
|||||||
|
|
||||||
akerr_init();
|
akerr_init();
|
||||||
|
|
||||||
AKSL_RUN(failures, test_snprintf_writes_text_and_count);
|
AKSL_RUN(failures, test_snprintf_writes_text);
|
||||||
AKSL_RUN(failures, test_snprintf_empty_format_writes_nothing);
|
AKSL_RUN(failures, test_snprintf_empty_format_writes_nothing);
|
||||||
AKSL_RUN(failures, test_snprintf_truncation_is_an_error);
|
AKSL_RUN(failures, test_snprintf_truncation_is_an_error);
|
||||||
AKSL_RUN(failures, test_snprintf_boundary_is_exact);
|
AKSL_RUN(failures, test_snprintf_boundary_is_exact);
|
||||||
|
|||||||
@@ -109,14 +109,13 @@ static int test_stream_wrappers_do_not_leak_slots(void)
|
|||||||
static int test_format_wrappers_do_not_leak_slots(void)
|
static int test_format_wrappers_do_not_leak_slots(void)
|
||||||
{
|
{
|
||||||
char buf[16];
|
char buf[16];
|
||||||
int count = 0;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for ( i = 0; i < ROUNDS; i++ ) {
|
for ( i = 0; i < ROUNDS; i++ ) {
|
||||||
AKSL_CHECK_STATUS(aksl_printf(NULL, "x"), AKERR_NULLPOINTER);
|
AKSL_CHECK_STATUS(aksl_printf(NULL, "x"), AKERR_NULLPOINTER);
|
||||||
AKSL_CHECK_STATUS(aksl_fprintf(NULL, stdout, "x"), AKERR_NULLPOINTER);
|
AKSL_CHECK_STATUS(aksl_fprintf(NULL, stdout, "x"), AKERR_NULLPOINTER);
|
||||||
AKSL_CHECK_STATUS(aksl_snprintf(NULL, buf, sizeof(buf), "x"), AKERR_NULLPOINTER);
|
AKSL_CHECK_OK(aksl_snprintf(buf, sizeof(buf), "x"));
|
||||||
AKSL_CHECK_STATUS(aksl_snprintf(&count, buf, 4, "%s", "far too long"),
|
AKSL_CHECK_STATUS(aksl_snprintf(buf, 4, "%s", "far too long"),
|
||||||
AKERR_OUTOFBOUNDS);
|
AKERR_OUTOFBOUNDS);
|
||||||
AKSL_CHECK(aksl_slots_in_use() == 0);
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
||||||
}
|
}
|
||||||
@@ -266,7 +265,6 @@ static int test_traversal_failures_do_not_leak_slots(void)
|
|||||||
static int test_errors_name_their_origin_in_stdlib(void)
|
static int test_errors_name_their_origin_in_stdlib(void)
|
||||||
{
|
{
|
||||||
void *ptr = NULL;
|
void *ptr = NULL;
|
||||||
int count = 0;
|
|
||||||
char resolved[PATH_MAX];
|
char resolved[PATH_MAX];
|
||||||
uint32_t h = 0;
|
uint32_t h = 0;
|
||||||
aksl_ListNode node;
|
aksl_ListNode node;
|
||||||
@@ -295,7 +293,7 @@ static int test_errors_name_their_origin_in_stdlib(void)
|
|||||||
AKSL_CHECK_STATUS(aksl_printf(NULL, "x"), AKERR_NULLPOINTER);
|
AKSL_CHECK_STATUS(aksl_printf(NULL, "x"), AKERR_NULLPOINTER);
|
||||||
AKSL_CHECK(came_from("aksl_vprintf", "src/stdlib.c") == 0);
|
AKSL_CHECK(came_from("aksl_vprintf", "src/stdlib.c") == 0);
|
||||||
|
|
||||||
AKSL_CHECK_STATUS(aksl_snprintf(&count, NULL, 8, "x"), AKERR_NULLPOINTER);
|
AKSL_CHECK_STATUS(aksl_snprintf(NULL, 8, "x"), AKERR_NULLPOINTER);
|
||||||
AKSL_CHECK(came_from("aksl_vsnprintf", "src/stdlib.c") == 0);
|
AKSL_CHECK(came_from("aksl_vsnprintf", "src/stdlib.c") == 0);
|
||||||
|
|
||||||
/* Likewise, the ato* forms are calls into the strto* ones. */
|
/* Likewise, the ato* forms are calls into the strto* ones. */
|
||||||
|
|||||||
Reference in New Issue
Block a user