This is the code that **Toby Speight** suggested in his answer: ```c /* * int sbprintf(char buff[restrict], int *restrict written, * const char *restrict format, ...); */ #define sbprintf(buff, written, format, ...) ( \ { \ int len_; \ \ alx_static_assert_array(buff); \ \ len_ = snprintf((buff), sizeof(buff), format, ##__VA_ARGS__);\ if (written != NULL) \ *written = len_; \ len_ >= (int)sizeof(buff); \ } \ ) ``` _________________________________________________ I have tested it and works as expected: - If `written` is `NULL` it doesn't write into it. - The return value is `true` when the string is truncated and `false` otherwise. - It doesn't compile if `buff` is not an array. - It accepts a variable number of arguments after `format`, including no arguments. The comment above the macro is the prototype that a user should see, to better understand the usage, although a real function with that prototype wouldn't work because of the array decaying to a pointer. ____________________________________ Comments about the style: I tried to follow the Linux Kernel Coding Style, but there have been exceptions: `if (written != NULL)` is used instead of `if (written)` to avoid the compiler complaining: ``` main.c:22:23: error: the address of ‘w1’ will always evaluate as ‘true’ [-Werror=address] if (alx_sbprintf(b1, &w1, test)) ^ .../libalx/base/stdio/sbprintf.h:36:6: note: in definition of macro ‘alx_sbprintf’ if (written) \ ^~~~~~~ ``` _____________ **EDIT:** Given that `snprintf` sets `errno` (at least in [POSIX][1]), it would be good to set `errno` to `ENOMEM` on truncation. __________________________________________ **EDIT:** This version includes the improvements suggested by **Toby Speight** and **chux**, as well as setting `errno` on error. First I added code to the macro, but it proved to have [some problems][2] (at least it didn't compile, so no dangerous bugs). Now it's an `extern` function enclosed in a simple macro. Now the code relies less on GCC extensions. Properties: - If `written` is `NULL` it doesn't write into it. - It doesn't compile if `buff` is not an array. - It accepts a variable number of arguments after `format`, including no arguments. - Sets `errno` on any error. - If there's a `snprintf` internal error, the error code is negative (`-errno`), and `written` is also negative. - If the string is truncated, the error code is positive (`ENOMEM`). - If the error code is negative, the string should not be trusted; if it's positive, it's been truncated, but it's a valid string. Code: `sbprintf.h`: ```c #ifndef ALX_STDIO_PRINTF_SBPRINTF_H #define ALX_STDIO_PRINTF_SBPRINTF_H #include <stddef.h> #include "libalx/base/assert/assert.h" /* * int alx_sbprintf__(char buff[restrict], int *restrict written, * const char *restrict format, ...); */ #define alx_sbprintf__(buff, written, fmt, ...) ( \ { \ \ alx_static_assert_array(buff); \ alx_sbprintf(buff, written, sizeof(buff), fmt, ##__VA_ARGS__); \ } \ ) int alx_sbprintf(char buff[restrict], int *written, size_t nmemb, char *restrict format, ...); #endif /* libalx/base/stdio/printf/sbprintf.h */ ``` `sbprintf.c`: ```c #include "libalx/base/stdio/printf/sbprintf.h" #include <errno.h> #include <stdarg.h> #include <stddef.h> #include <stdio.h> int alx_sbprintf(char buff[restrict], int *written, size_t nmemb, char *restrict format, ...) { va_list ap; int len; va_start(ap, format); len = vsnprintf(buff, nmemb, format, ap); va_end(ap); if (written != NULL) *written = len; if (len < 0) goto err; if ((unsigned)len >= nmemb) goto trunc; return 0; err: return -errno; trunc: if (written) *written = nmemb - 1; errno = ENOMEM; return ENOMEM; } ``` [1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html [2]: https://stackoverflow.com/q/56825271/6872717