Preserve required snprintf length on truncation
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 2m55s
libakstdlib CI Build / sanitizers (push) Successful in 2m57s
libakstdlib CI Build / coverage (push) Successful in 2m46s
libakstdlib CI Build / mutation_test (push) Successful in 18m29s

This commit is contained in:
2026-08-03 18:25:43 -04:00
parent 0620370dd9
commit acb47a0d56
7 changed files with 64 additions and 40 deletions

View File

@@ -445,7 +445,8 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_memchr(const void *s, int c, size_t n, v
/** @name Formatted output
*
* Bounded output is checked for truncation and reports an error when the result
* does not fit.
* does not fit. `*count` receives the number of bytes written, or the complete
* output length when truncation occurs.
*
* 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
@@ -456,7 +457,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_memchr(const void *s, int c, size_t n, v
/**
* @brief printf(3) to stdout.
* @param[out] count Bytes written; 0 on failure. Required.
* @param[out] count Bytes written. Required.
* @param[in] format printf format string. Required. Checked at compile time.
* @throws AKERR_NULLPOINTER If count or format is NULL.
* @throws AKERR_IO Or the errno the C library saw, if the write fails.
@@ -466,7 +467,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_printf(int *count, const char *restrict
/**
* @brief fprintf(3) to a stream.
* @param[out] count Bytes written; 0 on failure. Required.
* @param[out] count Bytes written. Required.
* @param[in] stream Destination stream. Required.
* @param[in] format printf format string. Required. Checked at compile time.
* @throws AKERR_NULLPOINTER If any pointer is NULL.
@@ -482,6 +483,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict strea
* written, leaving the caller to notice by comparing that against the buffer
* size -- the check this library exists to stop people forgetting.
*
* @param[out] count Bytes written, or the required length on truncation. Required.
* @param[out] str Destination buffer. Required.
* @param[in] size Size of `str` including the terminator. Must be non-zero.
* @param[in] format printf format string. Required. Checked at compile time.
@@ -490,7 +492,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict strea
* @throws AKERR_OUTOFBOUNDS If the output does not fit, naming both lengths.
* @return NULL on success, an error context otherwise.
*/
akerr_ErrorContext AKERR_NOIGNORE *aksl_snprintf(char *restrict str, size_t size, const char *restrict format, ...) AKSL_PRINTF_FORMAT(3, 4);
akerr_ErrorContext AKERR_NOIGNORE *aksl_snprintf(int *count, char *restrict str, size_t size, const char *restrict format, ...) AKSL_PRINTF_FORMAT(4, 5);
/**
* @brief Format into a freshly allocated string.
@@ -551,6 +553,7 @@ 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.
* @param[out] str Destination buffer. Required.
* @param[in] size Size of `str` including the terminator. Must be non-zero.
* @param[out] count Bytes written, or the required length on truncation. Required.
* @param[in] format printf format string. Required.
* @param[in] args Arguments. The caller owns it and must va_end it.
* @throws AKERR_NULLPOINTER If any pointer is NULL.
@@ -558,7 +561,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_vfprintf(int *count, FILE *restrict stre
* @throws AKERR_OUTOFBOUNDS If the output does not fit.
* @return NULL on success, an error context otherwise.
*/
akerr_ErrorContext AKERR_NOIGNORE *aksl_vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list args);
akerr_ErrorContext AKERR_NOIGNORE *aksl_vsnprintf(int *count, char *restrict str, size_t size, const char *restrict format, va_list args);
/** @} */