From d97f069c63147c3ee25fba1fa161e8f9aec0b673 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Sat, 22 Jan 2022 14:09:04 +0200 Subject: [PATCH] Fixes #89, #90: Parameter rename to match the C standard and significant doxygen comment edits. --- README.md | 14 ++-- src/printf/printf.c | 164 ++++++++++++++++++++++---------------------- src/printf/printf.h | 124 ++++++++++++++++++++------------- 3 files changed, 167 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 8e7e09f6..aac32761 100644 --- a/README.md +++ b/README.md @@ -112,18 +112,18 @@ Note: The preprocessor definitions are taken into account when compiling `printf The library offers the following, with the same signatures as in the standard C library (plus an extra underscore): ``` int printf_(const char* format, ...); -int sprintf_(char* buffer, const char* format, ...); -int vsprintf_(char* buffer, const char* format, va_list va); -int snprintf_(char* buffer, size_t count, const char* format, ...); -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); -int vprintf_(const char* format, va_list va); +int sprintf_(char* s, const char* format, ...); +int vsprintf_(char* s, const char* format, va_list arg); +int snprintf_(char* s, size_t n, const char* format, ...); +int vsnprintf_(char* s, size_t n, const char* format, va_list arg); +int vprintf_(const char* format, va_list arg); ``` Note that `printf()` and `vprintf()` don't actually write anything on their own: In addition to their parameters, you must provide them with a lower-level `putchar_()` function which they can call for actual printing. This is part of this library's independence: It is isolated from dealing with console/serial output, files etc. Two additional functions are provided beyond those available in the standard library: ``` -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va); +int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...); +int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg); ``` These higher-order functions allow for better flexibility of use: You can decide to do different things with the individual output characters: Encode them, compress them, filter them, append them to a buffer or a file, or just discard them. This is achieved by you passing a pointer to your own state information - through `(v)fctprintf()` and all the way to your own `out()` function. diff --git a/src/printf/printf.c b/src/printf/printf.c index dd1d9fd9..99395137 100644 --- a/src/printf/printf.c +++ b/src/printf/printf.c @@ -289,49 +289,49 @@ static inline int get_exp2(double_with_bit_access x) #define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) )) // output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); +typedef void (*out_fct_type)(char c, void* buffer, size_t idx, size_t maxlen); // wrapper (used as buffer) for output function type typedef struct { - void (*fct)(char character, void* arg); + void (*fct)(char c, void* arg); void* arg; } out_function_wrapper_type; // internal buffer output -static inline void out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +static inline void out_buffer(char c, void* buffer, size_t idx, size_t maxlen) { if (idx < maxlen) { - ((char*)buffer)[idx] = character; + ((char*)buffer)[idx] = c; } } // internal null output -static inline void out_discard(char character, void* buffer, size_t idx, size_t maxlen) +static inline void out_discard(char c, void* buffer, size_t idx, size_t maxlen) { - (void)character; (void)buffer; (void)idx; (void)maxlen; + (void)c; (void)buffer; (void)idx; (void)maxlen; } // internal putchar_ wrapper -static inline void out_putchar(char character, void* buffer, size_t idx, size_t maxlen) +static inline void out_putchar(char c, void* buffer, size_t idx, size_t maxlen) { (void)buffer; (void)idx; (void)maxlen; - if (character) { - putchar_(character); + if (c) { + putchar_(c); } } // internal output function wrapper -static inline void out_wrapped_function(char character, void* wrapped_function, size_t idx, size_t maxlen) +static inline void out_wrapped_function(char c, void* wrapped_function, size_t idx, size_t maxlen) { (void)idx; (void)maxlen; - if (character) { + if (c) { // buffer is the output fct pointer - ((out_function_wrapper_type*)wrapped_function)->fct(character, ((out_function_wrapper_type*)wrapped_function)->arg); + ((out_function_wrapper_type*)wrapped_function)->fct(c, ((out_function_wrapper_type*)wrapped_function)->arg); } } @@ -891,9 +891,10 @@ static size_t print_floating_point(out_fct_type out, char* buffer, size_t idx, s #endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -// internal vsnprintf -static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, const char* format, va_list va) +// internal vsnprintf - used for implementing _all library functions +// Note: We don't like the C standard's parameter names, so using more informative parameter names +// here instead. +static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t buffer_size, const char* format, va_list args) { printf_flags_t flags; printf_size_t width, precision, n; @@ -909,7 +910,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons // format specifier? %[flags][width][.precision][length] if (*format != '%') { // no - out(*format, buffer, idx++, maxlen); + out(*format, buffer, idx++, buffer_size); format++; continue; } @@ -937,7 +938,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons width = (printf_size_t) atou_(&format); } else if (*format == '*') { - const int w = va_arg(va, int); + const int w = va_arg(args, int); if (w < 0) { flags |= FLAGS_LEFT; // reverse padding width = (printf_size_t)-w; @@ -957,7 +958,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons precision = (printf_size_t) atou_(&format); } else if (*format == '*') { - const int precision_ = va_arg(va, int); + const int precision_ = va_arg(args, int); precision = precision_ > 0 ? (printf_size_t) precision_ : 0U; format++; } @@ -1066,13 +1067,13 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons if (flags & FLAGS_LONG_LONG) { #if PRINTF_SUPPORT_LONG_LONG - const long long value = va_arg(va, long long); - idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + const long long value = va_arg(args, long long); + idx = print_integer(out, buffer, idx, buffer_size, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); #endif } else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + const long value = va_arg(args, long); + idx = print_integer(out, buffer, idx, buffer_size, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); } else { // We never try to interpret the argument as something potentially-smaller than int, @@ -1080,10 +1081,10 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons // etc. - these will come in after promotion, as int's (or unsigned for the case of // short unsigned when it has the same size as int) const int value = - (flags & FLAGS_CHAR) ? (signed char) va_arg(va, int) : - (flags & FLAGS_SHORT) ? (short int) va_arg(va, int) : - va_arg(va, int); - idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + (flags & FLAGS_CHAR) ? (signed char) va_arg(args, int) : + (flags & FLAGS_SHORT) ? (short int) va_arg(args, int) : + va_arg(args, int); + idx = print_integer(out, buffer, idx, buffer_size, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); } } else { @@ -1093,18 +1094,18 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons if (flags & FLAGS_LONG_LONG) { #if PRINTF_SUPPORT_LONG_LONG - idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) va_arg(va, unsigned long long), false, base, precision, width, flags); + idx = print_integer(out, buffer, idx, buffer_size, (printf_unsigned_value_t) va_arg(args, unsigned long long), false, base, precision, width, flags); #endif } else if (flags & FLAGS_LONG) { - idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) va_arg(va, unsigned long), false, base, precision, width, flags); + idx = print_integer(out, buffer, idx, buffer_size, (printf_unsigned_value_t) va_arg(args, unsigned long), false, base, precision, width, flags); } else { const unsigned int value = - (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : - (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : - va_arg(va, unsigned int); - idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) value, false, base, precision, width, flags); + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : + (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) : + va_arg(args, unsigned int); + idx = print_integer(out, buffer, idx, buffer_size, (printf_unsigned_value_t) value, false, base, precision, width, flags); } } break; @@ -1113,7 +1114,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons case 'f' : case 'F' : if (*format == 'F') flags |= FLAGS_UPPERCASE; - idx = print_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_DECIMAL); + idx = print_floating_point(out, buffer, idx, buffer_size, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL); format++; break; #endif @@ -1124,7 +1125,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons case 'G': if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - idx = print_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + idx = print_floating_point(out, buffer, idx, buffer_size, va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); format++; break; #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS @@ -1133,15 +1134,15 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons // pre padding if (!(flags & FLAGS_LEFT)) { while (l++ < width) { - out(' ', buffer, idx++, maxlen); + out(' ', buffer, idx++, buffer_size); } } // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); + out((char)va_arg(args, int), buffer, idx++, buffer_size); // post padding if (flags & FLAGS_LEFT) { while (l++ < width) { - out(' ', buffer, idx++, maxlen); + out(' ', buffer, idx++, buffer_size); } } format++; @@ -1149,9 +1150,9 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons } case 's' : { - const char* p = va_arg(va, char*); + const char* p = va_arg(args, char*); if (p == NULL) { - idx = out_rev_(out, buffer, idx, maxlen, ")llun(", 6, width, flags); + idx = out_rev_(out, buffer, idx, buffer_size, ")llun(", 6, width, flags); } else { printf_size_t l = strnlen_s_(p, precision ? precision : (printf_size_t) -1); @@ -1161,18 +1162,18 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons } if (!(flags & FLAGS_LEFT)) { while (l++ < width) { - out(' ', buffer, idx++, maxlen); + out(' ', buffer, idx++, buffer_size); } } // string output while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { - out(*(p++), buffer, idx++, maxlen); + out(*(p++), buffer, idx++, buffer_size); --precision; } // post padding if (flags & FLAGS_LEFT) { while (l++ < width) { - out(' ', buffer, idx++, maxlen); + out(' ', buffer, idx++, buffer_size); } } } @@ -1183,16 +1184,16 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons case 'p' : { width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix flags |= FLAGS_ZEROPAD | FLAGS_POINTER; - uintptr_t value = (uintptr_t)va_arg(va, void*); + uintptr_t value = (uintptr_t)va_arg(args, void*); idx = (value == (uintptr_t) NULL) ? - out_rev_(out, buffer, idx, maxlen, ")lin(", 5, width, flags) : - print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); + out_rev_(out, buffer, idx, buffer_size, ")lin(", 5, width, flags) : + print_integer(out, buffer, idx, buffer_size, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); format++; break; } case '%' : - out('%', buffer, idx++, maxlen); + out('%', buffer, idx++, buffer_size); format++; break; @@ -1201,27 +1202,27 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons // effectively controls - which could be a security concern in some cases. #if PRINTF_SUPPORT_WRITEBACK_SPECIFIER case 'n' : { - if (flags & FLAGS_CHAR) *(va_arg(va, char*)) = (char) idx; - else if (flags & FLAGS_SHORT) *(va_arg(va, short*)) = (short) idx; - else if (flags & FLAGS_LONG) *(va_arg(va, long*)) = (long) idx; + if (flags & FLAGS_CHAR) *(va_arg(args, char*)) = (char) idx; + else if (flags & FLAGS_SHORT) *(va_arg(args, short*)) = (short) idx; + else if (flags & FLAGS_LONG) *(va_arg(args, long*)) = (long) idx; #if PRINTF_SUPPORT_LONG_LONG - else if (flags & FLAGS_LONG_LONG) *(va_arg(va, long long*)) = (long long int) idx; + else if (flags & FLAGS_LONG_LONG) *(va_arg(args, long long*)) = (long long int) idx; #endif // PRINTF_SUPPORT_LONG_LONG - else *(va_arg(va, int*)) = (int) idx; + else *(va_arg(args, int*)) = (int) idx; format++; break; } #endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER default : - out(*format, buffer, idx++, maxlen); + out(*format, buffer, idx++, buffer_size); format++; break; } } // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + out((char)0, buffer, idx < buffer_size ? idx : buffer_size - 1U, buffer_size); // return written chars without terminating \0 return (int)idx; @@ -1230,62 +1231,61 @@ static int _vsnprintf(out_fct_type out, char* buffer, printf_size_t maxlen, cons /////////////////////////////////////////////////////////////////////////////// -int vprintf_(const char* format, va_list va) +int vprintf_(const char* format, va_list arg) { char buffer[1]; - return _vsnprintf(&out_putchar, buffer, (printf_size_t)-1, format, va); + return _vsnprintf(&out_putchar, buffer, (printf_size_t)-1, format, arg); } -int vsprintf_(char* buffer, const char* format, va_list va) +int vsprintf_(char* s, const char* format, va_list arg) { - return _vsnprintf(out_buffer, buffer, (printf_size_t)-1, format, va); + return _vsnprintf(out_buffer, s, (printf_size_t)-1, format, arg); } -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +int vsnprintf_(char* s, size_t n, const char* format, va_list arg) { - return _vsnprintf(out_buffer, buffer, count, format, va); + return _vsnprintf(out_buffer, s, n, format, arg); } -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) +int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) { - const out_function_wrapper_type out_fct_wrap = { out, arg }; - return _vsnprintf(out_wrapped_function, (char*)(uintptr_t)&out_fct_wrap, (printf_size_t)-1, format, va); + const out_function_wrapper_type out_fct_wrap = { out, extra_arg }; + return _vsnprintf(out_wrapped_function, (char*)(uintptr_t)&out_fct_wrap, (printf_size_t)-1, format, arg); } - int printf_(const char* format, ...) { - va_list va; - va_start(va, format); - const int ret = vprintf_(format, va); - va_end(va); + va_list args; + va_start(args, format); + const int ret = vprintf_(format, args); + va_end(args); return ret; } -int sprintf_(char* buffer, const char* format, ...) +int sprintf_(char* s, const char* format, ...) { - va_list va; - va_start(va, format); - const int ret = vsprintf_(buffer, format, va); - va_end(va); + va_list args; + va_start(args, format); + const int ret = vsprintf_(s, format, args); + va_end(args); return ret; } -int snprintf_(char* buffer, size_t count, const char* format, ...) +int snprintf_(char* s, size_t n, const char* format, ...) { - va_list va; - va_start(va, format); - const int ret = vsnprintf_(buffer, count, format, va); - va_end(va); + va_list args; + va_start(args, format); + const int ret = vsnprintf_(s, n, format, args); + va_end(args); return ret; } -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) { - va_list va; - va_start(va, format); - const int ret = vfctprintf(out, arg, format, va); - va_end(va); + va_list args; + va_start(args, format); + const int ret = vfctprintf(out, extra_arg, format, args); + va_end(args); return ret; } diff --git a/src/printf/printf.h b/src/printf/printf.h index bc65387e..fb689b44 100644 --- a/src/printf/printf.h +++ b/src/printf/printf.h @@ -79,79 +79,111 @@ __attribute__((format(__printf__, (one_based_format_index), (first_arg)))) #endif /** - * Output a character to a custom device like UART, used by the printf() function - * This function is declared here only. You have to write your custom implementation somewhere - * @param character Character to output + * Prints/send a single character to some opaque output entity + * + * @note This function is not implemented by the library, only declared; you must provide an + * implementation if you wish to use the @ref printf / @ref vprintf function (and possibly + * for linking against the library, if your toolchain does not support discarding unused functions) + * + * @note The output could be as simple as a wrapper for the `write()` system call on a Unix-like + * system, or even libc's @ref putchar , for replicating actual functionality of libc's @ref printf + * function; but on an embedded system it may involve interaction with a special output device, + * like a UART, etc. + * + * @note in libc's @ref putchar, the parameter type is an int; this was intended to support the + * representation of either a proper character or EOF in a variable - but this is really not + * meaningful to pass into @ref putchar and is discouraged today. See further discussion in: + * @link https://stackoverflow.com/q/17452847/1593077 + * + * @param c the single character to print */ PRINTF_VISIBILITY -void putchar_(char character); +void putchar_(char c); /** - * Tiny printf implementation - * You have to implement putchar_ if you use printf() - * To avoid conflicts with the regular printf() API it is overridden by macro defines - * and internal underscore-appended functions like printf_() are used - * @param format A string that specifies the format of the output - * @return The number of characters that are written into the array, not counting the terminating null character + * An implementation of the C standard's printf/vprintf + * + * @note you must implement a @ref putchar_ function for using this function - it invokes @ref putchar_ + * rather than directly performing any I/O (which insulates it from any dependence on the operating system + * and external libraries). + * + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each %-specifier in @p format string + * @return The number of characters written into @p s, not counting the terminating null character */ + ///@{ PRINTF_VISIBILITY int printf_(const char* format, ...) ATTR_PRINTF(1, 2); +PRINTF_VISIBILITY +int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1); +///@} /** - * Tiny sprintf/vsprintf implementation - * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! - * @param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + * An implementation of the C standard's sprintf/vsprintf + * + * @note For security considerations (the potential for exceeding the buffer bounds), please consider using + * the size-constrained variant, @ref snprintf / @ref vsnprintf , instead. + * + * @param s An array in which to store the formatted string. It must be large enough to fit the formatted + * output! + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each specifier in @p format + * @return The number of characters written into @p s, not counting the terminating null character */ +///@{ PRINTF_VISIBILITY -int sprintf_(char* buffer, const char* format, ...) ATTR_PRINTF(2, 3); +int sprintf_(char* s, const char* format, ...) ATTR_PRINTF(2, 3); PRINTF_VISIBILITY -int vsprintf_(char* buffer, const char* format, va_list va) ATTR_VPRINTF(2); +int vsprintf_(char* s, const char* format, va_list arg) ATTR_VPRINTF(2); +///@} /** - * Tiny snprintf/vsnprintf implementation - * @param buffer A pointer to the buffer where to store the formatted string - * @param count The maximum number of characters to store in the buffer, including a terminating null character - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that COULD have been written into the buffer, not counting the terminating - * null character. A value equal or larger than count indicates truncation. Only when the returned value - * is non-negative and less than count, the string has been completely written. + * An implementation of the C standard's snprintf/vsnprintf + * + * @param s An array in which to store the formatted string. It must be large enough to fit either the + * entire formatted output, or at least @p n characters. Alternatively, it can be NULL, in which case + * nothing will be printed, and only the number of characters which _could_ have been printed is + * tallied and returned. + * @param n The maximum number of characters to write to the array, including a terminating null character + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each specifier in @p format + * @return The number of characters that COULD have been written into @p s, not counting the terminating + * null character. A value equal or larger than @p n indicates truncation. Only when the returned value + * is non-negative and less than @p n, the null-terminated string has been fully and successfully printed. */ +///@{ PRINTF_VISIBILITY -int snprintf_(char* buffer, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); +int snprintf_(char* s, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); PRINTF_VISIBILITY -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) ATTR_VPRINTF(3); - +int vsnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3); +///@} -/** - * Tiny vprintf implementation - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that are WRITTEN into the buffer, not counting the terminating null character - */ -PRINTF_VISIBILITY -int vprintf_(const char* format, va_list va) ATTR_VPRINTF(1); /** - * printf/vprintf with output function - * You may use this as dynamic alternative to printf() with its fixed putchar_() output - * @param out An output function which takes one character and an argument pointer - * @param arg An argument pointer for user data passed to output function - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that are sent to the output function, not counting the terminating null character + * printf/vprintf with user-specified output function + * + * An alternative to @ref printf_, in which the output function is specified dynamically + * (rather than @ref putchar_ being used) + * + * @param out An output function which takes one character and a type-erased additional parameters + * @param extra_arg The type-erased argument to pass to the output function @p out with each call + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each specifier in @p format + * @return The number of characters for which the output f unction was invoked, not counting the terminating null character + * */ PRINTF_VISIBILITY -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) ATTR_PRINTF(3, 4); +int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) ATTR_PRINTF(3, 4); PRINTF_VISIBILITY -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) ATTR_VPRINTF(3); +int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3); #ifdef __cplusplus } // extern "C"