Skip to content
Draft
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
10 changes: 8 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ option(SUPPORT_WRITEBACK_SPECIFIER "Support the length write-back spe
option(SUPPORT_LONG_LONG "Support long long integral types (allows for the ll length modifier and affects %p)" ON)
option(USE_DOUBLE_INTERNALLY "Use the C `double` type - typically 64-bit in size - for internal floating-point arithmetic " ON)
option(CHECK_FOR_NUL_IN_FORMAT_SPECIFIER "Be defensive in the undefined-behavior case of a format specifier not ending before the string ends" ON)
option(PROVIDE_PLAIN_PRINTF "Provide the plain printf() and vprintf() functions, requiring a putchar_() primitive" ON)

set(ALIASING_MODES NONE HARD SOFT)
set(ALIAS_STANDARD_FUNCTION_NAMES NONE CACHE STRING "Alias the standard library function names (printf, sprintf etc.) to the library's functions - concretely, via a macro, or not at all")
Expand All @@ -43,6 +44,7 @@ foreach(opt
ALIAS_STANDARD_FUNCTION_NAMES_SOFT
ALIAS_STANDARD_FUNCTION_NAMES_HARD
CHECK_FOR_NUL_IN_FORMAT_SPECIFIER
PROVIDE_PLAIN_PRINTF
)
if (${${opt}})
set("PRINTF_${opt}" 1)
Expand Down Expand Up @@ -83,7 +85,10 @@ add_library("printf::printf" ALIAS printf)

set(GENERATED_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
configure_file("printf_config.h.in" "${GENERATED_INCLUDE_DIR}/printf_config.h" @ONLY)
target_sources(printf PRIVATE src/printf/printf.c "${GENERATED_INCLUDE_DIR}/printf_config.h" src/printf/printf.h)
target_sources(printf PRIVATE src/printf/printf_core.c "${GENERATED_INCLUDE_DIR}/printf_config.h" src/printf/internal.h src/printf/printf.h)
if(PROVIDE_PLAIN_PRINTF)
target_sources(printf PRIVATE src/printf/printf.c)
endif()
target_compile_definitions(printf PRIVATE PRINTF_INCLUDE_CONFIG_H)
target_include_directories(printf PRIVATE "$<BUILD_INTERFACE:${GENERATED_INCLUDE_DIR}>")

Expand Down Expand Up @@ -162,7 +167,7 @@ if (UNIX)
COMMAND objdump --disassemble --line-numbers -S "$<TARGET_FILE:printf>" > printf.list
DEPENDS printf
BYPRODUCTS printf.lst
COMMENT Dissassembles the compiled library into an .lst file)
COMMENT Disassembles the compiled library into an .lst file)

endif()

Expand All @@ -186,6 +191,7 @@ install(

install(
FILES "src/printf/printf.h"
FILES "${GENERATED_INCLUDE_DIR}/printf_config.h"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/printf"
)

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Options used both in CMake and in the library source code via a preprocessor def
| PRINTF_SUPPORT_WRITEBACK_SPECIFIER | YES | Support the length write-back specifier (%n) |
| PRINTF_SUPPORT_LONG_LONG | YES | Support long long integral types (allows for the ll length modifier and affects %p) |
| PRINTF_USE_DOUBLE_INTERNALLY | YES | Use the `double` for internal floating-point calculations (rather than using the single-precision `float` type |
| PRINTF_PROVIDE_PLAIN_PRINTF | YES | Provide the plain printf() and vprintf() functions, requiring a putchar_() primitive |

Within CMake, these options lack the `PRINTF_` prefix.

Expand All @@ -115,7 +116,7 @@ Source-only options:

| Option name | Default | Description |
|----------------------------------------|---------|--------------|
| PRINTF_INCLUDE_CONFIG_H | NO | Triggers inclusing by `printf.c` of a "printf_config.h" file, which in turn contains the values of all of the CMake-and-preprocessor options above. A CMake build of the library uses this mechanism to apply the user's choice of options, so it can't have the mechanism itself as an option. |
| PRINTF_INCLUDE_CONFIG_H | NO | Triggers including by `printf.c` of a "printf_config.h" file, which in turn contains the values of all of the CMake-and-preprocessor options above. A CMake build of the library uses this mechanism to apply the user's choice of options, so it can't have the mechanism itself as an option. |

Note: The preprocessor definitions are taken into account mostly when compiling `printf.c`.

Expand Down
1 change: 1 addition & 0 deletions printf_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL @MAX_INTEGRAL_DIGITS_FOR_DECIMAL@
#define PRINTF_LOG10_TAYLOR_TERMS @LOG10_TAYLOR_TERMS@
#define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER @PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER@
#define PRINTF_PROVIDE_PLAIN_PRINTF @PRINTF_PROVIDE_PLAIN_PRINTF@

#endif /* PRINTF_CONFIG_H_ */

71 changes: 71 additions & 0 deletions src/printf/internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#ifndef PRINTF_INTERNAL_H_
#define PRINTF_INTERNAL_H_

#include <printf/printf.h>
#include <limits.h>

#if !(defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L))
/* C90 */
#if defined(_MSC_VER)
#define inline __inline
#else
#define inline __inline__
#endif /* defined(_MSC_VER) */
#endif /* !(defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)) */

/*
* The printf()-family functions return an `int`; it is therefore
* unnecessary/inappropriate to use size_t - often larger than int
* in practice - for non-negative related values, such as widths,
* precisions, offsets into buffers used for printing and the sizes
* of these buffers. instead, we use:
*/
typedef unsigned int printf_size_t;
#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX
/*
* If we were to nitpick, this would actually be INT_MAX + 1,
* since INT_MAX is the maximum return value, which excludes the
* trailing '\0'.
*/

/*
* wrapper (used as buffer) for output function type
*
* One of the following must hold:
* 1. max_chars is 0
* 2. buffer is non-null
* 3. function is non-null
*
* ... otherwise bad things will happen.
*/
typedef struct {
void (*function)(char c, void* extra_arg);
void* extra_function_arg;
char* buffer;
printf_size_t pos;
printf_size_t max_chars;
} output_gadget_t;

static inline output_gadget_t discarding_gadget(void)
{
output_gadget_t gadget;
gadget.function = NULL;
gadget.extra_function_arg = NULL;
gadget.buffer = NULL;
gadget.pos = 0;
gadget.max_chars = 0;
return gadget;
}

static inline output_gadget_t function_gadget(void (*function)(char, void*), void* extra_arg)
{
output_gadget_t result = discarding_gadget();
result.function = function;
result.extra_function_arg = extra_arg;
result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE;
return result;
}

int eyalroz_vsnprintf_impl(output_gadget_t* output, const char* format, va_list args);

#endif /* PRINTF_INTERNAL_H_ */
Loading