From d0f12416e9a371fa107a6a6a3780976ea93dd5b9 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Sat, 6 Dec 2025 12:00:27 +0200 Subject: [PATCH 1/2] Made a cast from `floating_point_t` to `int_fast64_t` explicit --- src/printf/printf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/printf/printf.c b/src/printf/printf.c index 837081a..dee5bc6 100644 --- a/src/printf/printf.c +++ b/src/printf/printf.c @@ -1094,7 +1094,7 @@ static void print_exponential_number(output_gadget_t* output, floating_point_t n /* Redo some work :-) */ floored_exp10 = original_floored_exp10; decimal_part_components = get_components(SIGN(negative, abs_number), precision); - if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) { + if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == (int_fast64_t) powers_of_10[floored_exp10 + 1]) { floored_exp10++; /* Not strictly necessary, since floored_exp10 is no longer really used */ if (precision > 0U) { precision--; } /* ... and it should already be the case that decimal_part_components.fractional == 0 */ From e1dbaef13cc87e1e78c617c18ac6b1936de33592 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Sun, 23 Nov 2025 21:22:48 +0200 Subject: [PATCH 2/2] Fixes #203: Added a CMake parameter and preprocessor definition: `PROVIDE_PLAIN_PRINTF` (which gets a `PRINTF_` prefix in the source files). This controls whether or not we expose, and define, the functions `putchar_()`, `printf()` and `vprintf()`. --- CMakeLists.txt | 2 ++ README.md | 3 ++- printf_config.h.in | 1 + src/printf/printf.c | 8 ++++++++ src/printf/printf.h | 7 +++++++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eeb7e5..b2f3985 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ option(BUILD_SHARED_LIBS "Build the library as a shared (dynamically-linked) # Boolean options which go into config.h +option(PROVIDE_PLAIN_PRINTF "Provide the plain printf() and vprintf() functions, requiring a putchar_() primitive" ON) option(SUPPORT_DECIMAL_SPECIFIERS "Support decimal notation floating-point conversion specifiers (%f,%F)" ON) option(SUPPORT_EXPONENTIAL_SPECIFIERS "Support exponential floating point format conversion specifiers (%e,%E,%g,%G)" ON) option(SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS "Support the I + bit size integer specifiers (%I8, %I16, %I32, %I64) as in Microsoft Visual C++" ON) @@ -34,6 +35,7 @@ if (NOT ${ALIAS_STANDARD_FUNCTION_NAMES} STREQUAL NONE) endif() foreach(opt + PROVIDE_PLAIN_PRINTF SUPPORT_DECIMAL_SPECIFIERS SUPPORT_EXPONENTIAL_SPECIFIERS SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS diff --git a/README.md b/README.md index 70d4bfc..73d2126 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Whichever way you choose to use the library: * You can have this library stand-in for the C standard library's `printf()` family of functions, e.g. provide `snprintf()` instead of `snprintf_()`, by setting an appropriate [preprocessor definition](#cmake-options-and-preprocessor-definitions) during compilation and use. * Speaking of the [preprocessor definitions](#cmake-options-and-preprocessor-definitions) which affect the library's behavior - you have to be consistent in their choice when building and when using the library. (The easiest way to do that is just not to change any of them and accept the reasonable defaults.) -* Two of the functions --- `printf_()` and `vprintf_()` --- will only be usable if you implement a `putchar_(char c)` function to underlie them. +* The two 'plain' functions - `printf_()` and `vprintf_()` - will require you to define a `putchar_(char c)` function to back them - or the library will fail to link (!); if you don't intend to use them, and are only interested in the other functions, there's an option to build without them (see below) * **Avoid `sprintf()` in favor of `snprintf()` for safety and security** - and that goes for the standard C library `sprintf()` as well:. `sprintf()` is unaware of the amount of memory allocated for the string it writes into, and will "happily" overflow your buffer; instead of calling it, pass your buffer size to `snprintf()` - and avoid overflow. Finally, if you've started using the library in a publicly-available (FOSS or commercial) project, please consider emailing [@eyalroz](https://github.com/eyalroz), or open an [issue](https://github.com/eyalroz/printf/issues/), to announce this. @@ -91,6 +91,7 @@ Options used both in CMake and in the library source code via a preprocessor def | Option name | Default | Description | |----------------------------------------|---------|--------------| +| PROVIDE_PLAIN_PRINTF | YES | Provide the plain `printf()` and `vprintf()` functions, requiring a `putchar_()` primitive to link | | ALIAS_STANDARD_FUNCTION_NAMES | NONE | Alias the standard library function names (`printf()`, `sprintf()` etc.) to the library's functions.
The possible values are `NONE`, `SOFT` and `HARD`. With Soft aliasing, the library's object files contain symbols which do not clash with the standard library's: `printf_`, `sprintd_` etc; and a macro in `printf.h` replaces usages of `printf()`, `sprintf()` etc. with the underscored versions. With Hard aliasing, no such macro is used, and the library's object files contain `printf`, `sprintf` etc. - and thus cannot be linked together with a full-fledged standard library. **Note:** The preprocessort definitions `#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT` and `#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD` should be defined to have the same values when using the library as when having compiled the list. | | INTEGER_BUFFER_SIZE | 32 | ntoa (integer) conversion buffer size. This must be big enough to hold one converted numeric number _including_ leading zeros, normally 32 is a sufficient value. Created on the stack. | | DECIMAL_BUFFER_SIZE | 32 | ftoa (float) conversion buffer size. This must be big enough to hold one converted float number _including_ leading zeros, normally 32 is a sufficient value. Created on the stack. | diff --git a/printf_config.h.in b/printf_config.h.in index ab47f76..10423b4 100644 --- a/printf_config.h.in +++ b/printf_config.h.in @@ -1,6 +1,7 @@ #ifndef PRINTF_CONFIG_H_ #define PRINTF_CONFIG_H_ +#define PRINTF_PROVIDE_PLAIN_PRINTF @PRINTF_PROVIDE_PLAIN_PRINTF@ #define PRINTF_SUPPORT_DECIMAL_SPECIFIERS @PRINTF_SUPPORT_DECIMAL_SPECIFIERS@ #define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS @PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS@ #define PRINTF_SUPPORT_WRITEBACK_SPECIFIER @PRINTF_SUPPORT_WRITEBACK_SPECIFIER@ diff --git a/src/printf/printf.c b/src/printf/printf.c index dee5bc6..deb3aaa 100644 --- a/src/printf/printf.c +++ b/src/printf/printf.c @@ -428,6 +428,7 @@ static inline void append_termination_with_gadget(output_gadget_t* gadget) gadget->buffer[null_char_pos] = '\0'; } +#if PRINTF_PROVIDE_PLAIN_PRINTF /* * We can't use putchar_ as is, since our output gadget * only takes pointers to functions with an extra argument @@ -437,6 +438,7 @@ static inline void putchar_wrapper(char c, void* unused) (void) unused; putchar_(c); } +#endif /* PRINTF_PROVIDE_PLAIN_PRINTF */ static inline output_gadget_t discarding_gadget(void) { @@ -470,10 +472,12 @@ static inline output_gadget_t function_gadget(void (*function)(char, void*), voi return result; } +#if PRINTF_PROVIDE_PLAIN_PRINTF static inline output_gadget_t extern_putchar_gadget(void) { return function_gadget(putchar_wrapper, NULL); } +#endif /* PRINTF_PROVIDE_PLAIN_PRINTF */ /* * internal secure strlen @@ -1592,11 +1596,13 @@ static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list a /*===========================================================================*/ +#if PRINTF_PROVIDE_PLAIN_PRINTF int vprintf_(const char* format, va_list arg) { output_gadget_t gadget = extern_putchar_gadget(); return vsnprintf_impl(&gadget, format, arg); } +#endif /* PRINTF_PROVIDE_PLAIN_PRINTF */ int vsnprintf_(char* s, size_t n, const char* format, va_list arg) { @@ -1617,6 +1623,7 @@ int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char return vsnprintf_impl(&gadget, format, arg); } +#if PRINTF_PROVIDE_PLAIN_PRINTF int printf_(const char* format, ...) { int ret; @@ -1626,6 +1633,7 @@ int printf_(const char* format, ...) va_end(args); return ret; } +#endif /* PRINTF_PROVIDE_PLAIN_PRINTF */ int sprintf_(char* s, const char* format, ...) { diff --git a/src/printf/printf.h b/src/printf/printf.h index 4796198..74cfed4 100644 --- a/src/printf/printf.h +++ b/src/printf/printf.h @@ -76,6 +76,11 @@ ATTR_PRINTF((one_based_format_index), 0) # define vprintf_ vprintf #endif +/* Provide the basic printf() and vprintf() functions */ +#ifndef PRINTF_PROVIDE_PLAIN_PRINTF +#define PRINTF_PROVIDE_PLAIN_PRINTF 1 +#endif + /* * If you want to include this implementation file directly rather than * link against it, this will let you control the functions' visibility, @@ -86,6 +91,7 @@ ATTR_PRINTF((one_based_format_index), 0) #define PRINTF_VISIBILITY #endif +#if PRINTF_PROVIDE_PLAIN_PRINTF /** * Prints/send a single character to some opaque output entity * @@ -133,6 +139,7 @@ int printf_(const char* format, ...) ATTR_PRINTF(1, 2); PRINTF_VISIBILITY int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1); /* @} */ +#endif /* PRINTF_PROVIDE_PLAIN_PRINTF */ /**