Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.<br>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. |
Expand Down
1 change: 1 addition & 0 deletions printf_config.h.in
Original file line number Diff line number Diff line change
@@ -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@
Expand Down
10 changes: 9 additions & 1 deletion src/printf/printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1094,7 +1098,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 */
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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, ...)
{
Expand Down
7 changes: 7 additions & 0 deletions src/printf/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
*
Expand Down Expand Up @@ -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 */


/**
Expand Down
Loading