From 6344c0433f2db7abaaba77ecdf509311ee881c53 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Thu, 1 Jul 2021 17:51:43 +0300 Subject: [PATCH 01/31] Line breaks CRLF->LF again... Some files must have slipped back during some of the merges. --- README.md | 414 ++++++------ printf.c | 1880 ++++++++++++++++++++++++++--------------------------- printf.h | 282 ++++---- 3 files changed, 1288 insertions(+), 1288 deletions(-) diff --git a/README.md b/README.md index ff0dff66..7bb2ade6 100644 --- a/README.md +++ b/README.md @@ -1,207 +1,207 @@ -# printf / sprintf Implementation for Embedded Systems - -[![Build Status](https://travis-ci.com/eyalroz/printf.svg?branch=master)](https://travis-ci.com/eyalroz/printf) -[![Github Releases](https://img.shields.io/github/release/mpaland/printf.svg)](https://github.com/mpaland/printf/releases) -[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/eyalroz/printf/master/LICENSE) -[![Github Issues](https://img.shields.io/github/issues/eyalroz/printf.svg)](http://github.com/eyalroz/printf/issues) -Parent repo: [![Github Issues (original repo)](https://img.shields.io/github/issues/mpaland/printf.svg)](http://github.com/mpaland/printf/issues) - -(This is a fork of the [mpaland/printf](https://github.com/mpaland/printf) repository, with bug fixes and a few more features applied.) - -This is a tiny but **fully loaded** printf, sprintf and (v)snprintf implementation. -Primarily designed for usage in embedded systems, where printf is not available due to memory issues or in avoidance of linking against libc. -Using the standard libc printf may pull **a lot** of unwanted library stuff and can bloat code size about 20k or is not 100% thread safe. In this cases the following implementation can be used. -Absolutely **NO dependencies** are required, *printf.c* brings all necessary routines, even its own fast `ftoa` (floating point), `ntoa` (decimal) conversion. - -If memory footprint is really a critical issue, floating point, exponential and 'long long' support and can be turned off via the `PRINTF_DISABLE_SUPPORT_FLOAT`, `PRINTF_DISABLE_SUPPORT_EXPONENTIAL` and `PRINTF_DISABLE_SUPPORT_LONG_LONG` compiler switches. -When using printf (instead of sprintf/snprintf) you have to provide your own `_putchar()` low level function as console/serial output. - -## Highlights and Design Goals - -There is a boatload of so called 'tiny' printf implementations around. So why this one? -I've tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing most of the test suite. -Therefore I decided to write an own, final implementation which meets the following items: - - - Very small implementation (around 600 code lines) - - NO dependencies, no libs, just one module file - - Support of all important flags, width and precision sub-specifiers (see below) - - Support of decimal/floating number representation (with an own fast itoa/ftoa) - - Reentrant and thread-safe, malloc free, no static vars/buffers - - LINT and compiler L4 warning free, mature, coverity clean, automotive ready - - Extensive test suite (> 400 test cases) passing - - Simply the best *printf* around the net - - MIT license - - -## Usage - -Add/link *printf.c* to your project and include *printf.h*. That's it. -Implement your low level output function needed for `printf()`: -```C -void _putchar(char character) -{ - // send char to console etc. -} -``` - -Usage is 1:1 like the according stdio.h library version: -```C -int printf(const char* format, ...); -int sprintf(char* buffer, const char* format, ...); -int snprintf(char* buffer, size_t count, const char* format, ...); -int vsnprintf(char* buffer, size_t count, const char* format, va_list va); - -// use output function (instead of buffer) for streamlike interface -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); -``` - -**Due to general security reasons it is highly recommended to prefer and use `snprintf` (with the max buffer size as `count` parameter) instead of `sprintf`.** -`sprintf` has no buffer limitation, so when needed - use it really with care! - -### Streamlike Usage -Besides the regular standard `printf()` functions, this module also provides `fctprintf()`, which takes an output function as first parameter to build a streamlike output like `fprintf()`: -```C -// define the output function -void my_stream_output(char character, void* arg) -{ - // opt. evaluate the argument and send the char somewhere -} - -{ - // in your code - void* arg = (void*)100; // this argument is passed to the output function - fctprintf(&my_stream_output, arg, "This is a test: %X", 0xAA); - fctprintf(&my_stream_output, nullptr, "Send to null dev"); -} -``` - -## Format Specifiers - -A format specifier follows this prototype: `%[flags][width][.precision][length]type` -The following format specifiers are supported: - - -### Supported Types - -| Type | Output | -|--------|--------| -| d or i | Signed decimal integer | -| u | Unsigned decimal integer | -| b | Unsigned binary | -| o | Unsigned octal | -| x | Unsigned hexadecimal integer (lowercase) | -| X | Unsigned hexadecimal integer (uppercase) | -| f or F | Decimal floating point | -| e or E | Scientific-notation (exponential) floating point | -| g or G | Scientific or decimal floating point | -| c | Single character | -| s | String of characters | -| p | Pointer address | -| % | A % followed by another % character will write a single % | - - -### Supported Flags - -| Flags | Description | -|-------|-------------| -| - | Left-justify within the given field width; Right justification is the default. | -| + | Forces to precede the result with a plus or minus sign (+ or -) even for positive numbers.
By default, only negative numbers are preceded with a - sign. | -| (space) | If no sign is going to be written, a blank space is inserted before the value. | -| # | Used with o, b, x or X specifiers the value is preceded with 0, 0b, 0x or 0X respectively for values different than zero.
Used with f, F it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written. | -| 0 | Left-pads the number with zeros (0) instead of spaces when padding is specified (see width sub-specifier). | - - -### Supported Width - -| Width | Description | -|----------|-------------| -| (number) | Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. The value is not truncated even if the result is larger. | -| * | The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | - - -### Supported Precision - -| Precision | Description | -|-----------|-------------| -| .number | For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.
For f and F specifiers: this is the number of digits to be printed after the decimal point. **By default, this is 6, maximum is 9**.
For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.
If the period is specified without an explicit value for precision, 0 is assumed. | -| .* | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | - - -### Supported Length - -The length sub-specifier modifies the length of the data type. - -| Length | d i | u o x X | -|--------|------|---------| -| (none) | int | unsigned int | -| hh | signed char | unsigned char | -| h | short int | unsigned short int | -| l | long int | unsigned long int | -| ll | long long int | unsigned long long int (if PRINTF_SUPPORT_LONG_LONG is defined) | -| j | intmax_t | uintmax_t | -| z | size_t | size_t | -| t | ptrdiff_t | ptrdiff_t (if PRINTF_SUPPORT_PTRDIFF_T is defined) | - - -### Return Value - -Upon successful return, all functions return the number of characters written, _excluding_ the terminating null character used to end the string. -Functions `snprintf()` and `vsnprintf()` don't write more than `count` bytes, _including_ the terminating null byte ('\0'). -Anyway, if the output was truncated due to this limit, the return value is the number of characters that _could_ have been written. -Notice that a value equal or larger than `count` indicates a truncation. Only when the returned value is non-negative and less than `count`, -the string has been completely written. -If any error is encountered, `-1` is returned. - -If `buffer` is set to `NULL` (`nullptr`) nothing is written and just the formatted length is returned. -```C -int length = sprintf(NULL, "Hello, world"); // length is set to 12 -``` - - -## Compiler Switches/Defines - -| Name | Default value | Description | -|------|---------------|-------------| -| PRINTF_INCLUDE_CONFIG_H | undefined | Define this as compiler switch (e.g. `gcc -DPRINTF_INCLUDE_CONFIG_H`) to include a "printf_config.h" definition file | -| PRINTF_NTOA_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 | -| PRINTF_FTOA_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 | -| PRINTF_DEFAULT_FLOAT_PRECISION | 6 | Define the default floating point precision | -| PRINTF_MAX_FLOAT | 1e9 | Define the largest suitable value to be printed with %f, before using exponential representation. Note that this must be set to a value which fits in a 64-bit signed integer (i.e. less than 2^63) | -| PRINTF_DISABLE_SUPPORT_FLOAT | undefined | Define this to disable floating point (%f) support | -| PRINTF_DISABLE_SUPPORT_EXPONENTIAL | undefined | Define this to disable exponential floating point (%e) support | -| PRINTF_DISABLE_SUPPORT_LONG_LONG | undefined | Define this to disable long long (%ll) support | -| PRINTF_DISABLE_SUPPORT_PTRDIFF_T | undefined | Define this to disable ptrdiff_t (%t) support | - - -## Caveats -None anymore (finally). - - -## Test Suite -For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/catchorg/Catch2) framework for unit-tests, which is auto-adding main(). -Running with the `--wait-for-keypress exit` option waits for the enter key after test end. - - -## Projects Using printf -- [turnkeyboard](https://github.com/mpaland/turnkeyboard) uses printf as log and generic tty (formatting) output. -- printf is part of [embeddedartistry/libc](https://github.com/embeddedartistry/libc), a libc targeted for embedded systems usage. -- The [Hatchling Platform]( https://github.com/adrian3git/HatchlingPlatform) uses printf. - -(Just send me a mail/issue/PR to get *your* project listed here) - - -## Contributing - -0. Give this project a :star: -1. Create an issue and describe your idea -2. [Fork it](https://github.com/mpaland/printf/fork) -3. Create your feature branch (`git checkout -b my-new-feature`) -4. Commit your changes (`git commit -am 'Add some feature'`) -5. Publish the branch (`git push origin my-new-feature`) -6. Create a new pull request -7. Profit! :heavy_check_mark: - - -## License -printf is written under the [MIT license](http://www.opensource.org/licenses/MIT). +# printf / sprintf Implementation for Embedded Systems + +[![Build Status](https://travis-ci.com/eyalroz/printf.svg?branch=master)](https://travis-ci.com/eyalroz/printf) +[![Github Releases](https://img.shields.io/github/release/mpaland/printf.svg)](https://github.com/mpaland/printf/releases) +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/eyalroz/printf/master/LICENSE) +[![Github Issues](https://img.shields.io/github/issues/eyalroz/printf.svg)](http://github.com/eyalroz/printf/issues) +Parent repo: [![Github Issues (original repo)](https://img.shields.io/github/issues/mpaland/printf.svg)](http://github.com/mpaland/printf/issues) + +(This is a fork of the [mpaland/printf](https://github.com/mpaland/printf) repository, with bug fixes and a few more features applied.) + +This is a tiny but **fully loaded** printf, sprintf and (v)snprintf implementation. +Primarily designed for usage in embedded systems, where printf is not available due to memory issues or in avoidance of linking against libc. +Using the standard libc printf may pull **a lot** of unwanted library stuff and can bloat code size about 20k or is not 100% thread safe. In this cases the following implementation can be used. +Absolutely **NO dependencies** are required, *printf.c* brings all necessary routines, even its own fast `ftoa` (floating point), `ntoa` (decimal) conversion. + +If memory footprint is really a critical issue, floating point, exponential and 'long long' support and can be turned off via the `PRINTF_DISABLE_SUPPORT_FLOAT`, `PRINTF_DISABLE_SUPPORT_EXPONENTIAL` and `PRINTF_DISABLE_SUPPORT_LONG_LONG` compiler switches. +When using printf (instead of sprintf/snprintf) you have to provide your own `_putchar()` low level function as console/serial output. + +## Highlights and Design Goals + +There is a boatload of so called 'tiny' printf implementations around. So why this one? +I've tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing most of the test suite. +Therefore I decided to write an own, final implementation which meets the following items: + + - Very small implementation (around 600 code lines) + - NO dependencies, no libs, just one module file + - Support of all important flags, width and precision sub-specifiers (see below) + - Support of decimal/floating number representation (with an own fast itoa/ftoa) + - Reentrant and thread-safe, malloc free, no static vars/buffers + - LINT and compiler L4 warning free, mature, coverity clean, automotive ready + - Extensive test suite (> 400 test cases) passing + - Simply the best *printf* around the net + - MIT license + + +## Usage + +Add/link *printf.c* to your project and include *printf.h*. That's it. +Implement your low level output function needed for `printf()`: +```C +void _putchar(char character) +{ + // send char to console etc. +} +``` + +Usage is 1:1 like the according stdio.h library version: +```C +int printf(const char* format, ...); +int sprintf(char* buffer, const char* format, ...); +int snprintf(char* buffer, size_t count, const char* format, ...); +int vsnprintf(char* buffer, size_t count, const char* format, va_list va); + +// use output function (instead of buffer) for streamlike interface +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); +``` + +**Due to general security reasons it is highly recommended to prefer and use `snprintf` (with the max buffer size as `count` parameter) instead of `sprintf`.** +`sprintf` has no buffer limitation, so when needed - use it really with care! + +### Streamlike Usage +Besides the regular standard `printf()` functions, this module also provides `fctprintf()`, which takes an output function as first parameter to build a streamlike output like `fprintf()`: +```C +// define the output function +void my_stream_output(char character, void* arg) +{ + // opt. evaluate the argument and send the char somewhere +} + +{ + // in your code + void* arg = (void*)100; // this argument is passed to the output function + fctprintf(&my_stream_output, arg, "This is a test: %X", 0xAA); + fctprintf(&my_stream_output, nullptr, "Send to null dev"); +} +``` + +## Format Specifiers + +A format specifier follows this prototype: `%[flags][width][.precision][length]type` +The following format specifiers are supported: + + +### Supported Types + +| Type | Output | +|--------|--------| +| d or i | Signed decimal integer | +| u | Unsigned decimal integer | +| b | Unsigned binary | +| o | Unsigned octal | +| x | Unsigned hexadecimal integer (lowercase) | +| X | Unsigned hexadecimal integer (uppercase) | +| f or F | Decimal floating point | +| e or E | Scientific-notation (exponential) floating point | +| g or G | Scientific or decimal floating point | +| c | Single character | +| s | String of characters | +| p | Pointer address | +| % | A % followed by another % character will write a single % | + + +### Supported Flags + +| Flags | Description | +|-------|-------------| +| - | Left-justify within the given field width; Right justification is the default. | +| + | Forces to precede the result with a plus or minus sign (+ or -) even for positive numbers.
By default, only negative numbers are preceded with a - sign. | +| (space) | If no sign is going to be written, a blank space is inserted before the value. | +| # | Used with o, b, x or X specifiers the value is preceded with 0, 0b, 0x or 0X respectively for values different than zero.
Used with f, F it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written. | +| 0 | Left-pads the number with zeros (0) instead of spaces when padding is specified (see width sub-specifier). | + + +### Supported Width + +| Width | Description | +|----------|-------------| +| (number) | Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. The value is not truncated even if the result is larger. | +| * | The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | + + +### Supported Precision + +| Precision | Description | +|-----------|-------------| +| .number | For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.
For f and F specifiers: this is the number of digits to be printed after the decimal point. **By default, this is 6, maximum is 9**.
For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.
If the period is specified without an explicit value for precision, 0 is assumed. | +| .* | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | + + +### Supported Length + +The length sub-specifier modifies the length of the data type. + +| Length | d i | u o x X | +|--------|------|---------| +| (none) | int | unsigned int | +| hh | signed char | unsigned char | +| h | short int | unsigned short int | +| l | long int | unsigned long int | +| ll | long long int | unsigned long long int (if PRINTF_SUPPORT_LONG_LONG is defined) | +| j | intmax_t | uintmax_t | +| z | size_t | size_t | +| t | ptrdiff_t | ptrdiff_t (if PRINTF_SUPPORT_PTRDIFF_T is defined) | + + +### Return Value + +Upon successful return, all functions return the number of characters written, _excluding_ the terminating null character used to end the string. +Functions `snprintf()` and `vsnprintf()` don't write more than `count` bytes, _including_ the terminating null byte ('\0'). +Anyway, if the output was truncated due to this limit, the return value is the number of characters that _could_ have been written. +Notice that a value equal or larger than `count` indicates a truncation. Only when the returned value is non-negative and less than `count`, +the string has been completely written. +If any error is encountered, `-1` is returned. + +If `buffer` is set to `NULL` (`nullptr`) nothing is written and just the formatted length is returned. +```C +int length = sprintf(NULL, "Hello, world"); // length is set to 12 +``` + + +## Compiler Switches/Defines + +| Name | Default value | Description | +|------|---------------|-------------| +| PRINTF_INCLUDE_CONFIG_H | undefined | Define this as compiler switch (e.g. `gcc -DPRINTF_INCLUDE_CONFIG_H`) to include a "printf_config.h" definition file | +| PRINTF_NTOA_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 | +| PRINTF_FTOA_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 | +| PRINTF_DEFAULT_FLOAT_PRECISION | 6 | Define the default floating point precision | +| PRINTF_MAX_FLOAT | 1e9 | Define the largest suitable value to be printed with %f, before using exponential representation. Note that this must be set to a value which fits in a 64-bit signed integer (i.e. less than 2^63) | +| PRINTF_DISABLE_SUPPORT_FLOAT | undefined | Define this to disable floating point (%f) support | +| PRINTF_DISABLE_SUPPORT_EXPONENTIAL | undefined | Define this to disable exponential floating point (%e) support | +| PRINTF_DISABLE_SUPPORT_LONG_LONG | undefined | Define this to disable long long (%ll) support | +| PRINTF_DISABLE_SUPPORT_PTRDIFF_T | undefined | Define this to disable ptrdiff_t (%t) support | + + +## Caveats +None anymore (finally). + + +## Test Suite +For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/catchorg/Catch2) framework for unit-tests, which is auto-adding main(). +Running with the `--wait-for-keypress exit` option waits for the enter key after test end. + + +## Projects Using printf +- [turnkeyboard](https://github.com/mpaland/turnkeyboard) uses printf as log and generic tty (formatting) output. +- printf is part of [embeddedartistry/libc](https://github.com/embeddedartistry/libc), a libc targeted for embedded systems usage. +- The [Hatchling Platform]( https://github.com/adrian3git/HatchlingPlatform) uses printf. + +(Just send me a mail/issue/PR to get *your* project listed here) + + +## Contributing + +0. Give this project a :star: +1. Create an issue and describe your idea +2. [Fork it](https://github.com/mpaland/printf/fork) +3. Create your feature branch (`git checkout -b my-new-feature`) +4. Commit your changes (`git commit -am 'Add some feature'`) +5. Publish the branch (`git push origin my-new-feature`) +6. Create a new pull request +7. Profit! :heavy_check_mark: + + +## License +printf is written under the [MIT license](http://www.opensource.org/licenses/MIT). diff --git a/printf.c b/printf.c index b77a1df5..17856c4e 100644 --- a/printf.c +++ b/printf.c @@ -1,940 +1,940 @@ -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2019, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. These routines are thread -// safe and reentrant! -// Use this instead of the bloated standard/newlib printf cause these use -// malloc for printf (and may not be thread safe). -// -/////////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include "printf.h" - - -// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the -// printf_config.h header file -// default: undefined -#ifdef PRINTF_INCLUDE_CONFIG_H -#include "printf_config.h" -#endif - - -// 'ntoa' conversion buffer size, this must be big enough to hold one converted -// numeric number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_NTOA_BUFFER_SIZE -#define PRINTF_NTOA_BUFFER_SIZE 32U -#endif - -// 'ftoa' conversion buffer size, this must be big enough to hold one converted -// float number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U -#endif - -// support for the floating point type (%f) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_FLOAT -#define PRINTF_SUPPORT_FLOAT -#endif - -// support for exponential floating point notation (%e/%g) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL -#define PRINTF_SUPPORT_EXPONENTIAL -#endif - -// define the default floating point precision -// default: 6 digits -#ifndef PRINTF_DEFAULT_FLOAT_PRECISION -#define PRINTF_DEFAULT_FLOAT_PRECISION 6U -#endif - -// define the largest float suitable to print with %f -// default: 1e9 -#ifndef PRINTF_MAX_FLOAT -#define PRINTF_MAX_FLOAT 1e9 -#endif - -// support for the long long types (%llu or %p) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG -#define PRINTF_SUPPORT_LONG_LONG -#endif - -// support for the ptrdiff_t type (%t) -// ptrdiff_t is normally defined in as long or long long type -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T -#define PRINTF_SUPPORT_PTRDIFF_T -#endif - -/////////////////////////////////////////////////////////////////////////////// - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) -#define FLAGS_PRECISION (1U << 10U) -#define FLAGS_ADAPT_EXP (1U << 11U) -#define FLAGS_POINTER (1U << 12U) - // Note: Similar, but not identical, effect as FLAGS_HASH - - -// import float.h for DBL_MAX -#if defined(PRINTF_SUPPORT_FLOAT) -#include -#endif - - -// output function type -typedef void (*out_fct_type)(char character, 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* arg; -} out_fct_wrap_type; - - -// internal buffer output -static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) -{ - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } -} - - -// internal null output -static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)character; (void)buffer; (void)idx; (void)maxlen; -} - - -// internal _putchar wrapper -static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)buffer; (void)idx; (void)maxlen; - if (character) { - _putchar(character); - } -} - - -// internal output function wrapper -static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)idx; (void)maxlen; - if (character) { - // buffer is the output fct pointer - ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); - } -} - - -// internal secure strlen -// \return The length of the string (excluding the terminating 0) limited by 'maxsize' -static inline unsigned int _strnlen_s(const char* str, size_t maxsize) -{ - const char* s; - for (s = str; *s && maxsize--; ++s); - return (unsigned int)(s - str); -} - - -// internal test if char is a digit (0-9) -// \return true if char is a digit -static inline bool _is_digit(char ch) -{ - return (ch >= '0') && (ch <= '9'); -} - - -// internal ASCII string to unsigned int conversion -static unsigned int _atoi(const char** str) -{ - unsigned int i = 0U; - while (_is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - - -// output the specified string in reverse, taking care of any zero-padding -static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) -{ - const size_t start_idx = idx; - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - while (len) { - out(buf[--len], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} - - -// internal itoa format -static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) -{ - // pad leading zeros - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - // handle hash - if (flags & (FLAGS_HASH | FLAGS_POINTER)) { - if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { - len--; - if (len && (base == 16U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} - - -// internal itoa for 'long' type -static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); -} - - -// internal itoa for 'long long' type -#if defined(PRINTF_SUPPORT_LONG_LONG) -static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); -} -#endif // PRINTF_SUPPORT_LONG_LONG - - -#if defined(PRINTF_SUPPORT_FLOAT) - -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); -#endif - - -// internal ftoa for fixed decimal floating point -static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - double diff = 0.0; - - // powers of 10 - static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - - // test for special values - if (value != value) - return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - - // test for very large values - // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { -#if defined(PRINTF_SUPPORT_EXPONENTIAL) - return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); -#else - return 0U; -#endif - } - - // test for negative - bool negative = false; - if (value < 0) { - negative = true; - value = 0 - value; - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { - buf[len++] = '0'; - prec--; - } - - int_fast64_t whole = (int_fast64_t)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - (double)frac; - - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if ((double)frac >= pow10[prec]) { - frac = 0; - ++whole; - } - } - else if (diff < 0.5) { - } - else if ((frac == 0U) || (frac & 1U)) { - // if halfway, round up if odd OR if last digit is 0 - ++frac; - } - - if (prec == 0U) { - diff = value - (double)whole; - if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } - else { - unsigned int count = prec; - // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - // add decimal - buf[len++] = '.'; - } - } - - // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { - break; - } - } - - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} - - -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) -{ - // check for NaN and special values - if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { - return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); - } - - // determine the sign - const bool negative = value < 0; - if (negative) { - value = -value; - } - - // default precision - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // determine the decimal exponent - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - union { - uint64_t U; - double F; - } conv; - - conv.F = value; - int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 - conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) - // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); - // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval * 3.321928094887362 + 0.5); - const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; - // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (value < conv.F) { - expval--; - conv.F /= 10; - } - - // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters - unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; - - // in "%g" mode, "prec" is the number of *significant figures* not decimals - if (flags & FLAGS_ADAPT_EXP) { - // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { - if ((int)prec > expval) { - prec = (unsigned)((int)prec - expval - 1); - } - else { - prec = 0; - } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision - // no characters in exponent - minwidth = 0U; - expval = 0; - } - else { - // we use one sigfig for the whole part - if ((prec > 0) && (flags & FLAGS_PRECISION)) { - --prec; - } - } - } - - // will everything fit? - unsigned int fwidth = width; - if (width > minwidth) { - // we didn't fall-back so subtract the characters required for the exponent - fwidth -= minwidth; - } else { - // not enough characters, so go back to default sizing - fwidth = 0U; - } - if ((flags & FLAGS_LEFT) && minwidth) { - // if we're padding on the right, DON'T pad the floating part - fwidth = 0U; - } - - // rescale the float value - if (expval) { - value /= conv.F; - } - - // output the floating part - const size_t start_idx = idx; - idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); - - // output the exponent part - if (minwidth) { - // output the exponential symbol - out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); - // output the exponent value - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned long)((expval < 0) ? -expval : expval), - expval < 0, 10, 0, minwidth-1, - FLAGS_ZEROPAD | FLAGS_PLUS); - // might need to right-pad spaces - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); - } - } - return idx; -} -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - - -// internal vsnprintf -static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) -{ - unsigned int flags, width, precision, n; - size_t idx = 0U; - - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) - { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } - else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; - case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; - case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; - case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; - case '#': flags |= FLAGS_HASH; format++; n = 1U; break; - default : n = 0U; break; - } - } while (n); - - // evaluate width field - width = 0U; - if (_is_digit(*format)) { - width = _atoi(&format); - } - else if (*format == '*') { - const int w = va_arg(va, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } - else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (_is_digit(*format)) { - precision = _atoi(&format); - } - else if (*format == '*') { - const int prec = (int)va_arg(va, int); - precision = prec > 0 ? (unsigned int)prec : 0U; - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l' : - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h' : - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) - case 't' : - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; -#endif - case 'j' : - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z' : - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default : - break; - } - - // evaluate specifier - switch (*format) { - case 'd' : - case 'i' : - case 'u' : - case 'x' : - case 'X' : - case 'o' : - case 'b' : { - // set the base - unsigned int base; - if (*format == 'x' || *format == 'X') { - base = 16U; - } - else if (*format == 'o') { - base = 8U; - } - else if (*format == 'b') { - base = 2U; - } - else { - base = 10U; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - const long long value = va_arg(va, long long); - idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); -#endif - } - else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); - } - else { - 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 = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); - } - } - else { - // unsigned - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); -#endif - } - else if (flags & FLAGS_LONG) { - idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, 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 = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); - } - } - format++; - break; - } -#if defined(PRINTF_SUPPORT_FLOAT) - case 'f' : - case 'F' : - if (*format == 'F') flags |= FLAGS_UPPERCASE; - idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); - format++; - break; -#if defined(PRINTF_SUPPORT_EXPONENTIAL) - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); - format++; - break; -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - case 'c' : { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's' : { - const char* p = va_arg(va, char*); - if (p == NULL) { - idx = _out_rev(out, buffer, idx, maxlen, ")llun(", 6, width, flags); - } - else { - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - } - format++; - break; - } - - 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*); - - if (value == (uintptr_t) NULL) { - idx = _out_rev(out, buffer, idx, maxlen, ")lin(", 5, width, flags); - } - else { -#if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - idx = _ntoa_long_long(out, buffer, idx, maxlen, value, false, 16U, precision, width, flags); - } - else { -#endif - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) - } -#endif - } - format++; - break; - } - - case '%' : - out('%', buffer, idx++, maxlen); - format++; - break; - - default : - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx; -} - - -/////////////////////////////////////////////////////////////////////////////// - -int printf_(const char* format, ...) -{ - va_list va; - va_start(va, format); - char buffer[1]; - const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - - -int sprintf_(char* buffer, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - - -int snprintf_(char* buffer, size_t count, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); - va_end(va); - return ret; -} - - -int vprintf_(const char* format, va_list va) -{ - char buffer[1]; - return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); -} - -int vsprintf_(char* buffer, const char* format, va_list va) -{ - return _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); -} - -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) -{ - return _vsnprintf(_out_buffer, buffer, count, format, va); -} - - -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = vfctprintf(out, arg, format, va); - va_end(va); - return ret; -} - -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) -{ - const out_fct_wrap_type out_fct_wrap = { out, arg }; - return _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); -} +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf.h" + + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) +#define FLAGS_POINTER (1U << 12U) + // Note: Similar, but not identical, effect as FLAGS_HASH + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, 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* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)buffer; (void)idx; (void)maxlen; + if (character) { + _putchar(character); + } +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & (FLAGS_HASH | FLAGS_POINTER)) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int_fast64_t whole = (int_fast64_t)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - (double)frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if ((double)frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, + (unsigned long)((expval < 0) ? -expval : expval), + expval < 0, 10, 0, minwidth-1, + FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + 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 = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, 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 = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + if (p == NULL) { + idx = _out_rev(out, buffer, idx, maxlen, ")llun(", 6, width, flags); + } + else { + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + } + format++; + break; + } + + 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*); + + if (value == (uintptr_t) NULL) { + idx = _out_rev(out, buffer, idx, maxlen, ")lin(", 5, width, flags); + } + else { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, value, false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + } + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int sprintf_(char* buffer, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vprintf_(const char* format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsprintf_(char* buffer, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = vfctprintf(out, arg, format, va); + va_end(va); + return ret; +} + +int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) +{ + const out_fct_wrap_type out_fct_wrap = { out, arg }; + return _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); +} diff --git a/printf.h b/printf.h index 4118929b..7adb1b2c 100644 --- a/printf.h +++ b/printf.h @@ -1,141 +1,141 @@ -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2019, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. -// Use this instead of bloated standard/newlib printf. -// These routines are thread safe and reentrant. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _PRINTF_H_ -#define _PRINTF_H_ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ -# define ATTR_PRINTF(one_based_format_index, first_arg) \ -__attribute__((format(__printf__, (one_based_format_index), (first_arg)))) -# define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF(one_based_format_index, 0) -#else -# define ATTR_PRINTF(one_based_format_index, first_arg) -# define ATTR_VPRINTF(one_based_format_index) -#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 - */ -void _putchar(char character); - - -/** - * 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 - */ -#define printf printf_ -int printf_(const char* format, ...) ATTR_PRINTF(1, 2); - - -/** - * 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 - */ -#define sprintf sprintf_ -#define vsprintf vsprintf_ -int sprintf_(char* buffer, const char* format, ...) ATTR_PRINTF(2, 3); -int vsprintf_(char* buffer, const char* format, va_list va) 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. - */ -#define snprintf snprintf_ -#define vsnprintf vsnprintf_ -int snprintf_(char* buffer, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) 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 - */ -#define vprintf vprintf_ -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 - */ -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) ATTR_PRINTF(3, 4); -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) ATTR_VPRINTF(3); - -/** - * vprintf with output function - * You may use this as dynamic alternative to vprintf() 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 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 - */ -int fctvprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va); - - -#ifdef __cplusplus -} -#endif - - -#endif // _PRINTF_H_ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +# define ATTR_PRINTF(one_based_format_index, first_arg) \ +__attribute__((format(__printf__, (one_based_format_index), (first_arg)))) +# define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF(one_based_format_index, 0) +#else +# define ATTR_PRINTF(one_based_format_index, first_arg) +# define ATTR_VPRINTF(one_based_format_index) +#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 + */ +void _putchar(char character); + + +/** + * 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 + */ +#define printf printf_ +int printf_(const char* format, ...) ATTR_PRINTF(1, 2); + + +/** + * 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 + */ +#define sprintf sprintf_ +#define vsprintf vsprintf_ +int sprintf_(char* buffer, const char* format, ...) ATTR_PRINTF(2, 3); +int vsprintf_(char* buffer, const char* format, va_list va) 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. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) 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 + */ +#define vprintf vprintf_ +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 + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) ATTR_PRINTF(3, 4); +int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) ATTR_VPRINTF(3); + +/** + * vprintf with output function + * You may use this as dynamic alternative to vprintf() 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 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 + */ +int fctvprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va); + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ From 0cb6c66fbeef4cc4384940df1c33f72dbc425f98 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Sun, 11 Jul 2021 19:10:33 +0300 Subject: [PATCH 02/31] Added an `enable_testing` command to `CMakeLists.txt` when building tests, so that `make test` and `ctest` would work. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69295299..d725cfdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR endif() if (BUILD_TESTS) + enable_testing() add_subdirectory(test) endif() From 5daeb48ff25d311b5e401ac33e44b68559980478 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Wed, 14 Jul 2021 22:32:24 +0300 Subject: [PATCH 03/31] Had `STATIC` and `SHARED` mixed up in `CMakeLists.txt` --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d725cfdb..9a337ee7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,9 +67,9 @@ option(BUILD_STATIC_LIBRARY "Build the library as static rather than shared" OFF if (BUILD_STATIC_LIBRARY) - add_library(mpaland-printf SHARED printf.c) -else() add_library(mpaland-printf STATIC printf.c) +else() + add_library(mpaland-printf SHARED printf.c) endif() set_property(TARGET mpaland-printf PROPERTY C_STANDARD 99) From 29ee8bb2e40ca3529f2e45d44c8a84a8ab510915 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Thu, 15 Jul 2021 00:05:58 +0300 Subject: [PATCH 04/31] Exposing bugs mpaland/printf#75, mpaland/printf#99, mpaland/printf#109, mpaland/printf#110 via the test suite. --- printf.c | 6 +++--- test/test_suite.cpp | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/printf.c b/printf.c index 17856c4e..da81d526 100644 --- a/printf.c +++ b/printf.c @@ -226,14 +226,14 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen // internal itoa format -static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int precision, unsigned int width, unsigned int flags) { // pad leading zeros if (!(flags & FLAGS_LEFT)) { if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { width--; } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + while ((len < precision) && (len < PRINTF_NTOA_BUFFER_SIZE)) { buf[len++] = '0'; } while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { @@ -243,7 +243,7 @@ static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t ma // handle hash if (flags & (FLAGS_HASH | FLAGS_POINTER)) { - if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { len--; if (len && (base == 16U)) { len--; diff --git a/test/test_suite.cpp b/test/test_suite.cpp index ca4d02bc..8f83134f 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -485,10 +485,37 @@ TEST_CASE("- flag - non-standard format", "[]" ) { TEST_CASE("# flag", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%#.0x", 0); - REQUIRE(!strcmp(buffer, "")); - test::sprintf(buffer, "%#.1x", 0); + test::sprintf(buffer, "%#o", 0); + REQUIRE(!strcmp(buffer, "0")); + test::sprintf(buffer, "%#0o", 0); + REQUIRE(!strcmp(buffer, "0")); + test::sprintf(buffer, "%#.0o", 0); REQUIRE(!strcmp(buffer, "0")); + test::sprintf(buffer, "%#.1o", 0); + REQUIRE(!strcmp(buffer, "0")); + test::sprintf(buffer, "%#4o", 0); + REQUIRE(!strcmp(buffer, " 0")); + test::sprintf(buffer, "%#.4o", 0); + REQUIRE(!strcmp(buffer, "0000")); + + test::sprintf(buffer, "%#o", 1); + REQUIRE(!strcmp(buffer, "01")); + test::sprintf(buffer, "%#0o", 1); + REQUIRE(!strcmp(buffer, "01")); + test::sprintf(buffer, "%#.0o", 1); + REQUIRE(!strcmp(buffer, "01")); + test::sprintf(buffer, "%#.1o", 1); + REQUIRE(!strcmp(buffer, "01")); + test::sprintf(buffer, "%#4o", 1); + REQUIRE(!strcmp(buffer, " 01")); + test::sprintf(buffer, "%#.4o", 1); + REQUIRE(!strcmp(buffer, "0001")); + + test::sprintf(buffer, "%#04x", 0x1001); + REQUIRE(!strcmp(buffer, "0x1001")); + test::sprintf(buffer, "%#04o", 01001); + REQUIRE(!strcmp(buffer, "01001")); + test::sprintf(buffer, "%#.0llx", (long long)0); REQUIRE(!strcmp(buffer, "")); test::sprintf(buffer, "%#.8x", 0x614e); @@ -1341,6 +1368,9 @@ TEST_CASE("float", "[]" ) { REQUIRE(!strcmp(buffer, "a0.5 end")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL + test::sprintf(buffer, "%.4g", 1.0); + REQUIRE(!strcmp(buffer, "1")); + test::sprintf(buffer, "%G", 12345.678); REQUIRE(!strcmp(buffer, "12345.7")); From 82b5978fe0ac04732b5e811f3f4fd9d1f39c9c22 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Thu, 15 Jul 2021 00:47:26 +0300 Subject: [PATCH 05/31] In the test suite, replaced all `REQUIRE`'s with `CHECK`'s, so we can see all the failures; and because we can't get "rolling failures" - each `sprintf()` (and other functions) call starts at the beginning of the buffer; so - this is safe. --- test/test_suite.cpp | 886 ++++++++++++++++++++++---------------------- 1 file changed, 443 insertions(+), 443 deletions(-) diff --git a/test/test_suite.cpp b/test/test_suite.cpp index 8f83134f..3fe270e3 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -95,10 +95,10 @@ void _out_fct(char character, void* arg) TEST_CASE("printf", "[]" ) { printf_idx = 0U; memset(printf_buffer, 0xCC, 100U); - REQUIRE(test::printf("% d", 4232) == 5); - REQUIRE(printf_buffer[5] == (char)0xCC); + CHECK(test::printf("% d", 4232) == 5); + CHECK(printf_buffer[5] == (char)0xCC); printf_buffer[5] = 0; - REQUIRE(!strcmp(printf_buffer, " 4232")); + CHECK(!strcmp(printf_buffer, " 4232")); } @@ -106,8 +106,8 @@ TEST_CASE("fctprintf", "[]" ) { printf_idx = 0U; memset(printf_buffer, 0xCC, 100U); test::fctprintf(&_out_fct, nullptr, "This is a test of %X", 0x12EFU); - REQUIRE(!strncmp(printf_buffer, "This is a test of 12EF", 22U)); - REQUIRE(printf_buffer[22] == (char)0xCC); + CHECK(!strncmp(printf_buffer, "This is a test of 12EF", 22U)); + CHECK(printf_buffer[22] == (char)0xCC); } // output function type @@ -126,18 +126,18 @@ TEST_CASE("vfctprintf", "[]" ) { printf_idx = 0U; memset(printf_buffer, 0xCC, 100U); vfctprintf_builder_1(&_out_fct, nullptr, 0x12EFU); - REQUIRE(!strncmp(printf_buffer, "This is a test of 12EF", 22U)); - REQUIRE(printf_buffer[22] == (char)0xCC); + CHECK(!strncmp(printf_buffer, "This is a test of 12EF", 22U)); + CHECK(printf_buffer[22] == (char)0xCC); } TEST_CASE("snprintf", "[]" ) { char buffer[100]; test::snprintf(buffer, 100U, "%d", -1000); - REQUIRE(!strcmp(buffer, "-1000")); + CHECK(!strcmp(buffer, "-1000")); test::snprintf(buffer, 3U, "%d", -1000); - REQUIRE(!strcmp(buffer, "-1")); + CHECK(!strcmp(buffer, "-1")); } static void vprintf_builder_1(char* buffer, ...) @@ -186,9 +186,9 @@ TEST_CASE("vprintf", "[]" ) { printf_idx = 0U; memset(printf_buffer, 0xCC, 100U); vprintf_builder_1(buffer, 2345); - REQUIRE(printf_buffer[4] == (char)0xCC); + CHECK(printf_buffer[4] == (char)0xCC); printf_buffer[4] = 0; - REQUIRE(!strcmp(printf_buffer, "2345")); + CHECK(!strcmp(printf_buffer, "2345")); } @@ -196,10 +196,10 @@ TEST_CASE("vsprintf", "[]" ) { char buffer[100]; vsprintf_builder_1(buffer, -1); - REQUIRE(!strcmp(buffer, "-1")); + CHECK(!strcmp(buffer, "-1")); vsprintf_builder_3(buffer, 3, -1000, "test"); - REQUIRE(!strcmp(buffer, "3 -1000 test")); + CHECK(!strcmp(buffer, "3 -1000 test")); } @@ -207,10 +207,10 @@ TEST_CASE("vsnprintf", "[]" ) { char buffer[100]; vsnprintf_builder_1(buffer, -1); - REQUIRE(!strcmp(buffer, "-1")); + CHECK(!strcmp(buffer, "-1")); vsnprintf_builder_3(buffer, 3, -1000, "test"); - REQUIRE(!strcmp(buffer, "3 -1000 test")); + CHECK(!strcmp(buffer, "3 -1000 test")); } @@ -218,43 +218,43 @@ TEST_CASE("space flag", "[]" ) { char buffer[100]; test::sprintf(buffer, "% d", 42); - REQUIRE(!strcmp(buffer, " 42")); + CHECK(!strcmp(buffer, " 42")); test::sprintf(buffer, "% d", -42); - REQUIRE(!strcmp(buffer, "-42")); + CHECK(!strcmp(buffer, "-42")); test::sprintf(buffer, "% 5d", 42); - REQUIRE(!strcmp(buffer, " 42")); + CHECK(!strcmp(buffer, " 42")); test::sprintf(buffer, "% 5d", -42); - REQUIRE(!strcmp(buffer, " -42")); + CHECK(!strcmp(buffer, " -42")); test::sprintf(buffer, "% 15d", 42); - REQUIRE(!strcmp(buffer, " 42")); + CHECK(!strcmp(buffer, " 42")); test::sprintf(buffer, "% 15d", -42); - REQUIRE(!strcmp(buffer, " -42")); + CHECK(!strcmp(buffer, " -42")); test::sprintf(buffer, "% 15d", -42); - REQUIRE(!strcmp(buffer, " -42")); + CHECK(!strcmp(buffer, " -42")); test::sprintf(buffer, "% 15.3f", -42.987); - REQUIRE(!strcmp(buffer, " -42.987")); + CHECK(!strcmp(buffer, " -42.987")); test::sprintf(buffer, "% 15.3f", 42.987); - REQUIRE(!strcmp(buffer, " 42.987")); + CHECK(!strcmp(buffer, " 42.987")); test::sprintf(buffer, "% d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "% d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "% i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "% i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -262,34 +262,34 @@ TEST_CASE("space flag - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "% s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); + CHECK(!strcmp(buffer, "Hello testing")); test::sprintf(buffer, "% u", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "% u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); + CHECK(!strcmp(buffer, "4294966272")); test::sprintf(buffer, "% o", 511); - REQUIRE(!strcmp(buffer, "777")); + CHECK(!strcmp(buffer, "777")); test::sprintf(buffer, "% o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); + CHECK(!strcmp(buffer, "37777777001")); test::sprintf(buffer, "% x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); + CHECK(!strcmp(buffer, "1234abcd")); test::sprintf(buffer, "% x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); + CHECK(!strcmp(buffer, "edcb5433")); test::sprintf(buffer, "% X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); + CHECK(!strcmp(buffer, "1234ABCD")); test::sprintf(buffer, "% X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); + CHECK(!strcmp(buffer, "EDCB5433")); test::sprintf(buffer, "% c", 'x'); - REQUIRE(!strcmp(buffer, "x")); + CHECK(!strcmp(buffer, "x")); } #endif @@ -298,37 +298,37 @@ TEST_CASE("+ flag", "[]" ) { char buffer[100]; test::sprintf(buffer, "%+d", 42); - REQUIRE(!strcmp(buffer, "+42")); + CHECK(!strcmp(buffer, "+42")); test::sprintf(buffer, "%+d", -42); - REQUIRE(!strcmp(buffer, "-42")); + CHECK(!strcmp(buffer, "-42")); test::sprintf(buffer, "%+5d", 42); - REQUIRE(!strcmp(buffer, " +42")); + CHECK(!strcmp(buffer, " +42")); test::sprintf(buffer, "%+5d", -42); - REQUIRE(!strcmp(buffer, " -42")); + CHECK(!strcmp(buffer, " -42")); test::sprintf(buffer, "%+15d", 42); - REQUIRE(!strcmp(buffer, " +42")); + CHECK(!strcmp(buffer, " +42")); test::sprintf(buffer, "%+15d", -42); - REQUIRE(!strcmp(buffer, " -42")); + CHECK(!strcmp(buffer, " -42")); test::sprintf(buffer, "%+d", 1024); - REQUIRE(!strcmp(buffer, "+1024")); + CHECK(!strcmp(buffer, "+1024")); test::sprintf(buffer, "%+d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "%+i", 1024); - REQUIRE(!strcmp(buffer, "+1024")); + CHECK(!strcmp(buffer, "+1024")); test::sprintf(buffer, "%+i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "%+.0d", 0); - REQUIRE(!strcmp(buffer, "+")); + CHECK(!strcmp(buffer, "+")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -336,34 +336,34 @@ TEST_CASE("+ flag - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "%+s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); + CHECK(!strcmp(buffer, "Hello testing")); test::sprintf(buffer, "%+u", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%+u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); + CHECK(!strcmp(buffer, "4294966272")); test::sprintf(buffer, "%+o", 511); - REQUIRE(!strcmp(buffer, "777")); + CHECK(!strcmp(buffer, "777")); test::sprintf(buffer, "%+o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); + CHECK(!strcmp(buffer, "37777777001")); test::sprintf(buffer, "%+x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); + CHECK(!strcmp(buffer, "1234abcd")); test::sprintf(buffer, "%+x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); + CHECK(!strcmp(buffer, "edcb5433")); test::sprintf(buffer, "%+X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); + CHECK(!strcmp(buffer, "1234ABCD")); test::sprintf(buffer, "%+X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); + CHECK(!strcmp(buffer, "EDCB5433")); test::sprintf(buffer, "%+c", 'x'); - REQUIRE(!strcmp(buffer, "x")); + CHECK(!strcmp(buffer, "x")); } #endif @@ -372,34 +372,34 @@ TEST_CASE("0 flag", "[]" ) { char buffer[100]; test::sprintf(buffer, "%0d", 42); - REQUIRE(!strcmp(buffer, "42")); + CHECK(!strcmp(buffer, "42")); test::sprintf(buffer, "%0ld", 42L); - REQUIRE(!strcmp(buffer, "42")); + CHECK(!strcmp(buffer, "42")); test::sprintf(buffer, "%0d", -42); - REQUIRE(!strcmp(buffer, "-42")); + CHECK(!strcmp(buffer, "-42")); test::sprintf(buffer, "%05d", 42); - REQUIRE(!strcmp(buffer, "00042")); + CHECK(!strcmp(buffer, "00042")); test::sprintf(buffer, "%05d", -42); - REQUIRE(!strcmp(buffer, "-0042")); + CHECK(!strcmp(buffer, "-0042")); test::sprintf(buffer, "%015d", 42); - REQUIRE(!strcmp(buffer, "000000000000042")); + CHECK(!strcmp(buffer, "000000000000042")); test::sprintf(buffer, "%015d", -42); - REQUIRE(!strcmp(buffer, "-00000000000042")); + CHECK(!strcmp(buffer, "-00000000000042")); test::sprintf(buffer, "%015.2f", 42.1234); - REQUIRE(!strcmp(buffer, "000000000042.12")); + CHECK(!strcmp(buffer, "000000000042.12")); test::sprintf(buffer, "%015.3f", 42.9876); - REQUIRE(!strcmp(buffer, "00000000042.988")); + CHECK(!strcmp(buffer, "00000000042.988")); test::sprintf(buffer, "%015.5f", -42.9876); - REQUIRE(!strcmp(buffer, "-00000042.98760")); + CHECK(!strcmp(buffer, "-00000042.98760")); } @@ -407,22 +407,22 @@ TEST_CASE("- flag", "[]" ) { char buffer[100]; test::sprintf(buffer, "%-d", 42); - REQUIRE(!strcmp(buffer, "42")); + CHECK(!strcmp(buffer, "42")); test::sprintf(buffer, "%-d", -42); - REQUIRE(!strcmp(buffer, "-42")); + CHECK(!strcmp(buffer, "-42")); test::sprintf(buffer, "%-5d", 42); - REQUIRE(!strcmp(buffer, "42 ")); + CHECK(!strcmp(buffer, "42 ")); test::sprintf(buffer, "%-5d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); + CHECK(!strcmp(buffer, "-42 ")); test::sprintf(buffer, "%-15d", 42); - REQUIRE(!strcmp(buffer, "42 ")); + CHECK(!strcmp(buffer, "42 ")); test::sprintf(buffer, "%-15d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); + CHECK(!strcmp(buffer, "-42 ")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -430,53 +430,53 @@ TEST_CASE("- flag - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "%-0d", 42); - REQUIRE(!strcmp(buffer, "42")); + CHECK(!strcmp(buffer, "42")); test::sprintf(buffer, "%-0d", -42); - REQUIRE(!strcmp(buffer, "-42")); + CHECK(!strcmp(buffer, "-42")); test::sprintf(buffer, "%-05d", 42); - REQUIRE(!strcmp(buffer, "42 ")); + CHECK(!strcmp(buffer, "42 ")); test::sprintf(buffer, "%-05d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); + CHECK(!strcmp(buffer, "-42 ")); test::sprintf(buffer, "%-015d", 42); - REQUIRE(!strcmp(buffer, "42 ")); + CHECK(!strcmp(buffer, "42 ")); test::sprintf(buffer, "%-015d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); + CHECK(!strcmp(buffer, "-42 ")); test::sprintf(buffer, "%0-d", 42); - REQUIRE(!strcmp(buffer, "42")); + CHECK(!strcmp(buffer, "42")); test::sprintf(buffer, "%0-d", -42); - REQUIRE(!strcmp(buffer, "-42")); + CHECK(!strcmp(buffer, "-42")); test::sprintf(buffer, "%0-5d", 42); - REQUIRE(!strcmp(buffer, "42 ")); + CHECK(!strcmp(buffer, "42 ")); test::sprintf(buffer, "%0-5d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); + CHECK(!strcmp(buffer, "-42 ")); test::sprintf(buffer, "%0-15d", 42); - REQUIRE(!strcmp(buffer, "42 ")); + CHECK(!strcmp(buffer, "42 ")); test::sprintf(buffer, "%0-15d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); + CHECK(!strcmp(buffer, "-42 ")); test::sprintf(buffer, "%0-15.3e", -42.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - REQUIRE(!strcmp(buffer, "-4.200e+01 ")); + CHECK(!strcmp(buffer, "-4.200e+01 ")); #else - REQUIRE(!strcmp(buffer, "e")); + CHECK(!strcmp(buffer, "e")); #endif test::sprintf(buffer, "%0-15.3g", -42.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - REQUIRE(!strcmp(buffer, "-42.0 ")); + CHECK(!strcmp(buffer, "-42.0 ")); #else - REQUIRE(!strcmp(buffer, "g")); + CHECK(!strcmp(buffer, "g")); #endif } #endif @@ -486,40 +486,40 @@ TEST_CASE("# flag", "[]" ) { char buffer[100]; test::sprintf(buffer, "%#o", 0); - REQUIRE(!strcmp(buffer, "0")); + CHECK(!strcmp(buffer, "0")); test::sprintf(buffer, "%#0o", 0); - REQUIRE(!strcmp(buffer, "0")); + CHECK(!strcmp(buffer, "0")); test::sprintf(buffer, "%#.0o", 0); - REQUIRE(!strcmp(buffer, "0")); + CHECK(!strcmp(buffer, "0")); test::sprintf(buffer, "%#.1o", 0); - REQUIRE(!strcmp(buffer, "0")); + CHECK(!strcmp(buffer, "0")); test::sprintf(buffer, "%#4o", 0); - REQUIRE(!strcmp(buffer, " 0")); + CHECK(!strcmp(buffer, " 0")); test::sprintf(buffer, "%#.4o", 0); - REQUIRE(!strcmp(buffer, "0000")); + CHECK(!strcmp(buffer, "0000")); test::sprintf(buffer, "%#o", 1); - REQUIRE(!strcmp(buffer, "01")); + CHECK(!strcmp(buffer, "01")); test::sprintf(buffer, "%#0o", 1); - REQUIRE(!strcmp(buffer, "01")); + CHECK(!strcmp(buffer, "01")); test::sprintf(buffer, "%#.0o", 1); - REQUIRE(!strcmp(buffer, "01")); + CHECK(!strcmp(buffer, "01")); test::sprintf(buffer, "%#.1o", 1); - REQUIRE(!strcmp(buffer, "01")); + CHECK(!strcmp(buffer, "01")); test::sprintf(buffer, "%#4o", 1); - REQUIRE(!strcmp(buffer, " 01")); + CHECK(!strcmp(buffer, " 01")); test::sprintf(buffer, "%#.4o", 1); - REQUIRE(!strcmp(buffer, "0001")); + CHECK(!strcmp(buffer, "0001")); test::sprintf(buffer, "%#04x", 0x1001); - REQUIRE(!strcmp(buffer, "0x1001")); + CHECK(!strcmp(buffer, "0x1001")); test::sprintf(buffer, "%#04o", 01001); - REQUIRE(!strcmp(buffer, "01001")); + CHECK(!strcmp(buffer, "01001")); test::sprintf(buffer, "%#.0llx", (long long)0); - REQUIRE(!strcmp(buffer, "")); + CHECK(!strcmp(buffer, "")); test::sprintf(buffer, "%#.8x", 0x614e); - REQUIRE(!strcmp(buffer, "0x0000614e")); + CHECK(!strcmp(buffer, "0x0000614e")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -527,7 +527,7 @@ TEST_CASE("# flag - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer,"%#b", 6); - REQUIRE(!strcmp(buffer, "0b110")); + CHECK(!strcmp(buffer, "0b110")); } #endif @@ -535,55 +535,55 @@ TEST_CASE("specifier", "[]" ) { char buffer[100]; test::sprintf(buffer, "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); + CHECK(!strcmp(buffer, "Hello testing")); test::sprintf(buffer, "%s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); + CHECK(!strcmp(buffer, "Hello testing")); DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW test::sprintf(buffer, "%s", NULL); DISABLE_WARNING_POP - REQUIRE(!strcmp(buffer, "(null)")); + CHECK(!strcmp(buffer, "(null)")); test::sprintf(buffer, "%d", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "%i", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "%u", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); + CHECK(!strcmp(buffer, "4294966272")); test::sprintf(buffer, "%o", 511); - REQUIRE(!strcmp(buffer, "777")); + CHECK(!strcmp(buffer, "777")); test::sprintf(buffer, "%o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); + CHECK(!strcmp(buffer, "37777777001")); test::sprintf(buffer, "%x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); + CHECK(!strcmp(buffer, "1234abcd")); test::sprintf(buffer, "%x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); + CHECK(!strcmp(buffer, "edcb5433")); test::sprintf(buffer, "%X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); + CHECK(!strcmp(buffer, "1234ABCD")); test::sprintf(buffer, "%X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); + CHECK(!strcmp(buffer, "EDCB5433")); test::sprintf(buffer, "%%"); - REQUIRE(!strcmp(buffer, "%")); + CHECK(!strcmp(buffer, "%")); } @@ -591,46 +591,46 @@ TEST_CASE("width", "[]" ) { char buffer[100]; test::sprintf(buffer, "%1s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); + CHECK(!strcmp(buffer, "Hello testing")); test::sprintf(buffer, "%1d", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%1d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "%1i", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%1i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); + CHECK(!strcmp(buffer, "-1024")); test::sprintf(buffer, "%1u", 1024); - REQUIRE(!strcmp(buffer, "1024")); + CHECK(!strcmp(buffer, "1024")); test::sprintf(buffer, "%1u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); + CHECK(!strcmp(buffer, "4294966272")); test::sprintf(buffer, "%1o", 511); - REQUIRE(!strcmp(buffer, "777")); + CHECK(!strcmp(buffer, "777")); test::sprintf(buffer, "%1o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); + CHECK(!strcmp(buffer, "37777777001")); test::sprintf(buffer, "%1x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); + CHECK(!strcmp(buffer, "1234abcd")); test::sprintf(buffer, "%1x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); + CHECK(!strcmp(buffer, "edcb5433")); test::sprintf(buffer, "%1X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); + CHECK(!strcmp(buffer, "1234ABCD")); test::sprintf(buffer, "%1X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); + CHECK(!strcmp(buffer, "EDCB5433")); test::sprintf(buffer, "%1c", 'x'); - REQUIRE(!strcmp(buffer, "x")); + CHECK(!strcmp(buffer, "x")); } @@ -638,46 +638,46 @@ TEST_CASE("width 20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%20s", "Hello"); - REQUIRE(!strcmp(buffer, " Hello")); + CHECK(!strcmp(buffer, " Hello")); test::sprintf(buffer, "%20d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%20d", -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%20i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%20i", -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%20u", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%20u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); + CHECK(!strcmp(buffer, " 4294966272")); test::sprintf(buffer, "%20o", 511); - REQUIRE(!strcmp(buffer, " 777")); + CHECK(!strcmp(buffer, " 777")); test::sprintf(buffer, "%20o", 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); + CHECK(!strcmp(buffer, " 37777777001")); test::sprintf(buffer, "%20x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); + CHECK(!strcmp(buffer, " 1234abcd")); test::sprintf(buffer, "%20x", 3989525555U); - REQUIRE(!strcmp(buffer, " edcb5433")); + CHECK(!strcmp(buffer, " edcb5433")); test::sprintf(buffer, "%20X", 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); + CHECK(!strcmp(buffer, " 1234ABCD")); test::sprintf(buffer, "%20X", 3989525555U); - REQUIRE(!strcmp(buffer, " EDCB5433")); + CHECK(!strcmp(buffer, " EDCB5433")); test::sprintf(buffer, "%20c", 'x'); - REQUIRE(!strcmp(buffer, " x")); + CHECK(!strcmp(buffer, " x")); } @@ -685,46 +685,46 @@ TEST_CASE("width *20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%*s", 20, "Hello"); - REQUIRE(!strcmp(buffer, " Hello")); + CHECK(!strcmp(buffer, " Hello")); test::sprintf(buffer, "%*d", 20, 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%*d", 20, -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%*i", 20, 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%*i", 20, -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%*u", 20, 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%*u", 20, 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); + CHECK(!strcmp(buffer, " 4294966272")); test::sprintf(buffer, "%*o", 20, 511); - REQUIRE(!strcmp(buffer, " 777")); + CHECK(!strcmp(buffer, " 777")); test::sprintf(buffer, "%*o", 20, 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); + CHECK(!strcmp(buffer, " 37777777001")); test::sprintf(buffer, "%*x", 20, 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); + CHECK(!strcmp(buffer, " 1234abcd")); test::sprintf(buffer, "%*x", 20, 3989525555U); - REQUIRE(!strcmp(buffer, " edcb5433")); + CHECK(!strcmp(buffer, " edcb5433")); test::sprintf(buffer, "%*X", 20, 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); + CHECK(!strcmp(buffer, " 1234ABCD")); test::sprintf(buffer, "%*X", 20, 3989525555U); - REQUIRE(!strcmp(buffer, " EDCB5433")); + CHECK(!strcmp(buffer, " EDCB5433")); test::sprintf(buffer, "%*c", 20,'x'); - REQUIRE(!strcmp(buffer, " x")); + CHECK(!strcmp(buffer, " x")); } @@ -732,61 +732,61 @@ TEST_CASE("width -20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%-20s", "Hello"); - REQUIRE(!strcmp(buffer, "Hello ")); + CHECK(!strcmp(buffer, "Hello ")); test::sprintf(buffer, "%-20d", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); + CHECK(!strcmp(buffer, "1024 ")); test::sprintf(buffer, "%-20d", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); + CHECK(!strcmp(buffer, "-1024 ")); test::sprintf(buffer, "%-20i", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); + CHECK(!strcmp(buffer, "1024 ")); test::sprintf(buffer, "%-20i", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); + CHECK(!strcmp(buffer, "-1024 ")); test::sprintf(buffer, "%-20u", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); + CHECK(!strcmp(buffer, "1024 ")); test::sprintf(buffer, "%-20.4f", 1024.1234); - REQUIRE(!strcmp(buffer, "1024.1234 ")); + CHECK(!strcmp(buffer, "1024.1234 ")); test::sprintf(buffer, "%-20u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272 ")); + CHECK(!strcmp(buffer, "4294966272 ")); test::sprintf(buffer, "%-20o", 511); - REQUIRE(!strcmp(buffer, "777 ")); + CHECK(!strcmp(buffer, "777 ")); test::sprintf(buffer, "%-20o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001 ")); + CHECK(!strcmp(buffer, "37777777001 ")); test::sprintf(buffer, "%-20x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd ")); + CHECK(!strcmp(buffer, "1234abcd ")); test::sprintf(buffer, "%-20x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433 ")); + CHECK(!strcmp(buffer, "edcb5433 ")); test::sprintf(buffer, "%-20X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD ")); + CHECK(!strcmp(buffer, "1234ABCD ")); test::sprintf(buffer, "%-20X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433 ")); + CHECK(!strcmp(buffer, "EDCB5433 ")); test::sprintf(buffer, "%-20c", 'x'); - REQUIRE(!strcmp(buffer, "x ")); + CHECK(!strcmp(buffer, "x ")); test::sprintf(buffer, "|%5d| |%-2d| |%5d|", 9, 9, 9); - REQUIRE(!strcmp(buffer, "| 9| |9 | | 9|")); + CHECK(!strcmp(buffer, "| 9| |9 | | 9|")); test::sprintf(buffer, "|%5d| |%-2d| |%5d|", 10, 10, 10); - REQUIRE(!strcmp(buffer, "| 10| |10| | 10|")); + CHECK(!strcmp(buffer, "| 10| |10| | 10|")); test::sprintf(buffer, "|%5d| |%-12d| |%5d|", 9, 9, 9); - REQUIRE(!strcmp(buffer, "| 9| |9 | | 9|")); + CHECK(!strcmp(buffer, "| 9| |9 | | 9|")); test::sprintf(buffer, "|%5d| |%-12d| |%5d|", 10, 10, 10); - REQUIRE(!strcmp(buffer, "| 10| |10 | | 10|")); + CHECK(!strcmp(buffer, "| 10| |10 | | 10|")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -794,46 +794,46 @@ TEST_CASE("width 0-20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%0-20s", "Hello"); - REQUIRE(!strcmp(buffer, "Hello ")); + CHECK(!strcmp(buffer, "Hello ")); test::sprintf(buffer, "%0-20d", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); + CHECK(!strcmp(buffer, "1024 ")); test::sprintf(buffer, "%0-20d", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); + CHECK(!strcmp(buffer, "-1024 ")); test::sprintf(buffer, "%0-20i", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); + CHECK(!strcmp(buffer, "1024 ")); test::sprintf(buffer, "%0-20i", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); + CHECK(!strcmp(buffer, "-1024 ")); test::sprintf(buffer, "%0-20u", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); + CHECK(!strcmp(buffer, "1024 ")); test::sprintf(buffer, "%0-20u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272 ")); + CHECK(!strcmp(buffer, "4294966272 ")); test::sprintf(buffer, "%0-20o", 511); - REQUIRE(!strcmp(buffer, "777 ")); + CHECK(!strcmp(buffer, "777 ")); test::sprintf(buffer, "%0-20o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001 ")); + CHECK(!strcmp(buffer, "37777777001 ")); test::sprintf(buffer, "%0-20x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd ")); + CHECK(!strcmp(buffer, "1234abcd ")); test::sprintf(buffer, "%0-20x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433 ")); + CHECK(!strcmp(buffer, "edcb5433 ")); test::sprintf(buffer, "%0-20X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD ")); + CHECK(!strcmp(buffer, "1234ABCD ")); test::sprintf(buffer, "%0-20X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433 ")); + CHECK(!strcmp(buffer, "EDCB5433 ")); test::sprintf(buffer, "%0-20c", 'x'); - REQUIRE(!strcmp(buffer, "x ")); + CHECK(!strcmp(buffer, "x ")); } #endif @@ -841,40 +841,40 @@ TEST_CASE("padding 20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%020d", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%020d", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); + CHECK(!strcmp(buffer, "-0000000000000001024")); test::sprintf(buffer, "%020i", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%020i", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); + CHECK(!strcmp(buffer, "-0000000000000001024")); test::sprintf(buffer, "%020u", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%020u", 4294966272U); - REQUIRE(!strcmp(buffer, "00000000004294966272")); + CHECK(!strcmp(buffer, "00000000004294966272")); test::sprintf(buffer, "%020o", 511); - REQUIRE(!strcmp(buffer, "00000000000000000777")); + CHECK(!strcmp(buffer, "00000000000000000777")); test::sprintf(buffer, "%020o", 4294966785U); - REQUIRE(!strcmp(buffer, "00000000037777777001")); + CHECK(!strcmp(buffer, "00000000037777777001")); test::sprintf(buffer, "%020x", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234abcd")); + CHECK(!strcmp(buffer, "0000000000001234abcd")); test::sprintf(buffer, "%020x", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000edcb5433")); + CHECK(!strcmp(buffer, "000000000000edcb5433")); test::sprintf(buffer, "%020X", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234ABCD")); + CHECK(!strcmp(buffer, "0000000000001234ABCD")); test::sprintf(buffer, "%020X", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000EDCB5433")); + CHECK(!strcmp(buffer, "000000000000EDCB5433")); } @@ -882,40 +882,40 @@ TEST_CASE("padding .20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%.20d", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%.20d", -1024); - REQUIRE(!strcmp(buffer, "-00000000000000001024")); + CHECK(!strcmp(buffer, "-00000000000000001024")); test::sprintf(buffer, "%.20i", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%.20i", -1024); - REQUIRE(!strcmp(buffer, "-00000000000000001024")); + CHECK(!strcmp(buffer, "-00000000000000001024")); test::sprintf(buffer, "%.20u", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%.20u", 4294966272U); - REQUIRE(!strcmp(buffer, "00000000004294966272")); + CHECK(!strcmp(buffer, "00000000004294966272")); test::sprintf(buffer, "%.20o", 511); - REQUIRE(!strcmp(buffer, "00000000000000000777")); + CHECK(!strcmp(buffer, "00000000000000000777")); test::sprintf(buffer, "%.20o", 4294966785U); - REQUIRE(!strcmp(buffer, "00000000037777777001")); + CHECK(!strcmp(buffer, "00000000037777777001")); test::sprintf(buffer, "%.20x", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234abcd")); + CHECK(!strcmp(buffer, "0000000000001234abcd")); test::sprintf(buffer, "%.20x", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000edcb5433")); + CHECK(!strcmp(buffer, "000000000000edcb5433")); test::sprintf(buffer, "%.20X", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234ABCD")); + CHECK(!strcmp(buffer, "0000000000001234ABCD")); test::sprintf(buffer, "%.20X", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000EDCB5433")); + CHECK(!strcmp(buffer, "000000000000EDCB5433")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -923,44 +923,44 @@ TEST_CASE("padding #020 - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "%#020d", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%#020d", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); + CHECK(!strcmp(buffer, "-0000000000000001024")); test::sprintf(buffer, "%#020i", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%#020i", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); + CHECK(!strcmp(buffer, "-0000000000000001024")); test::sprintf(buffer, "%#020u", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); + CHECK(!strcmp(buffer, "00000000000000001024")); test::sprintf(buffer, "%#020u", 4294966272U); - REQUIRE(!strcmp(buffer, "00000000004294966272")); + CHECK(!strcmp(buffer, "00000000004294966272")); } #endif TEST_CASE("padding #020", "[]" ) { char buffer[100]; test::sprintf(buffer, "%#020o", 511); - REQUIRE(!strcmp(buffer, "00000000000000000777")); + CHECK(!strcmp(buffer, "00000000000000000777")); test::sprintf(buffer, "%#020o", 4294966785U); - REQUIRE(!strcmp(buffer, "00000000037777777001")); + CHECK(!strcmp(buffer, "00000000037777777001")); test::sprintf(buffer, "%#020x", 305441741); - REQUIRE(!strcmp(buffer, "0x00000000001234abcd")); + CHECK(!strcmp(buffer, "0x00000000001234abcd")); test::sprintf(buffer, "%#020x", 3989525555U); - REQUIRE(!strcmp(buffer, "0x0000000000edcb5433")); + CHECK(!strcmp(buffer, "0x0000000000edcb5433")); test::sprintf(buffer, "%#020X", 305441741); - REQUIRE(!strcmp(buffer, "0X00000000001234ABCD")); + CHECK(!strcmp(buffer, "0X00000000001234ABCD")); test::sprintf(buffer, "%#020X", 3989525555U); - REQUIRE(!strcmp(buffer, "0X0000000000EDCB5433")); + CHECK(!strcmp(buffer, "0X0000000000EDCB5433")); } @@ -969,22 +969,22 @@ TEST_CASE("padding #20 - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "%#20d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%#20d", -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%#20i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%#20i", -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%#20u", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%#20u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); + CHECK(!strcmp(buffer, " 4294966272")); } #endif @@ -992,22 +992,22 @@ TEST_CASE("padding #20", "[]" ) { char buffer[100]; test::sprintf(buffer, "%#20o", 511); - REQUIRE(!strcmp(buffer, " 0777")); + CHECK(!strcmp(buffer, " 0777")); test::sprintf(buffer, "%#20o", 4294966785U); - REQUIRE(!strcmp(buffer, " 037777777001")); + CHECK(!strcmp(buffer, " 037777777001")); test::sprintf(buffer, "%#20x", 305441741); - REQUIRE(!strcmp(buffer, " 0x1234abcd")); + CHECK(!strcmp(buffer, " 0x1234abcd")); test::sprintf(buffer, "%#20x", 3989525555U); - REQUIRE(!strcmp(buffer, " 0xedcb5433")); + CHECK(!strcmp(buffer, " 0xedcb5433")); test::sprintf(buffer, "%#20X", 305441741); - REQUIRE(!strcmp(buffer, " 0X1234ABCD")); + CHECK(!strcmp(buffer, " 0X1234ABCD")); test::sprintf(buffer, "%#20X", 3989525555U); - REQUIRE(!strcmp(buffer, " 0XEDCB5433")); + CHECK(!strcmp(buffer, " 0XEDCB5433")); } @@ -1015,40 +1015,40 @@ TEST_CASE("padding 20.5", "[]" ) { char buffer[100]; test::sprintf(buffer, "%20.5d", 1024); - REQUIRE(!strcmp(buffer, " 01024")); + CHECK(!strcmp(buffer, " 01024")); test::sprintf(buffer, "%20.5d", -1024); - REQUIRE(!strcmp(buffer, " -01024")); + CHECK(!strcmp(buffer, " -01024")); test::sprintf(buffer, "%20.5i", 1024); - REQUIRE(!strcmp(buffer, " 01024")); + CHECK(!strcmp(buffer, " 01024")); test::sprintf(buffer, "%20.5i", -1024); - REQUIRE(!strcmp(buffer, " -01024")); + CHECK(!strcmp(buffer, " -01024")); test::sprintf(buffer, "%20.5u", 1024); - REQUIRE(!strcmp(buffer, " 01024")); + CHECK(!strcmp(buffer, " 01024")); test::sprintf(buffer, "%20.5u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); + CHECK(!strcmp(buffer, " 4294966272")); test::sprintf(buffer, "%20.5o", 511); - REQUIRE(!strcmp(buffer, " 00777")); + CHECK(!strcmp(buffer, " 00777")); test::sprintf(buffer, "%20.5o", 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); + CHECK(!strcmp(buffer, " 37777777001")); test::sprintf(buffer, "%20.5x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); + CHECK(!strcmp(buffer, " 1234abcd")); test::sprintf(buffer, "%20.10x", 3989525555U); - REQUIRE(!strcmp(buffer, " 00edcb5433")); + CHECK(!strcmp(buffer, " 00edcb5433")); test::sprintf(buffer, "%20.5X", 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); + CHECK(!strcmp(buffer, " 1234ABCD")); test::sprintf(buffer, "%20.10X", 3989525555U); - REQUIRE(!strcmp(buffer, " 00EDCB5433")); + CHECK(!strcmp(buffer, " 00EDCB5433")); } @@ -1057,29 +1057,29 @@ TEST_CASE("padding neg numbers", "[]" ) { // space padding test::sprintf(buffer, "% 1d", -5); - REQUIRE(!strcmp(buffer, "-5")); + CHECK(!strcmp(buffer, "-5")); test::sprintf(buffer, "% 2d", -5); - REQUIRE(!strcmp(buffer, "-5")); + CHECK(!strcmp(buffer, "-5")); test::sprintf(buffer, "% 3d", -5); - REQUIRE(!strcmp(buffer, " -5")); + CHECK(!strcmp(buffer, " -5")); test::sprintf(buffer, "% 4d", -5); - REQUIRE(!strcmp(buffer, " -5")); + CHECK(!strcmp(buffer, " -5")); // zero padding test::sprintf(buffer, "%01d", -5); - REQUIRE(!strcmp(buffer, "-5")); + CHECK(!strcmp(buffer, "-5")); test::sprintf(buffer, "%02d", -5); - REQUIRE(!strcmp(buffer, "-5")); + CHECK(!strcmp(buffer, "-5")); test::sprintf(buffer, "%03d", -5); - REQUIRE(!strcmp(buffer, "-05")); + CHECK(!strcmp(buffer, "-05")); test::sprintf(buffer, "%04d", -5); - REQUIRE(!strcmp(buffer, "-005")); + CHECK(!strcmp(buffer, "-005")); } @@ -1088,54 +1088,54 @@ TEST_CASE("float padding neg numbers", "[]" ) { // space padding test::sprintf(buffer, "% 3.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); + CHECK(!strcmp(buffer, "-5.0")); test::sprintf(buffer, "% 4.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); + CHECK(!strcmp(buffer, "-5.0")); test::sprintf(buffer, "% 5.1f", -5.); - REQUIRE(!strcmp(buffer, " -5.0")); + CHECK(!strcmp(buffer, " -5.0")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL test::sprintf(buffer, "% 6.1g", -5.); - REQUIRE(!strcmp(buffer, " -5")); + CHECK(!strcmp(buffer, " -5")); test::sprintf(buffer, "% 6.1e", -5.); - REQUIRE(!strcmp(buffer, "-5.0e+00")); + CHECK(!strcmp(buffer, "-5.0e+00")); test::sprintf(buffer, "% 10.1e", -5.); - REQUIRE(!strcmp(buffer, " -5.0e+00")); + CHECK(!strcmp(buffer, " -5.0e+00")); #endif // zero padding test::sprintf(buffer, "%03.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); + CHECK(!strcmp(buffer, "-5.0")); test::sprintf(buffer, "%04.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); + CHECK(!strcmp(buffer, "-5.0")); test::sprintf(buffer, "%05.1f", -5.); - REQUIRE(!strcmp(buffer, "-05.0")); + CHECK(!strcmp(buffer, "-05.0")); // zero padding no decimal point test::sprintf(buffer, "%01.0f", -5.); - REQUIRE(!strcmp(buffer, "-5")); + CHECK(!strcmp(buffer, "-5")); test::sprintf(buffer, "%02.0f", -5.); - REQUIRE(!strcmp(buffer, "-5")); + CHECK(!strcmp(buffer, "-5")); test::sprintf(buffer, "%03.0f", -5.); - REQUIRE(!strcmp(buffer, "-05")); + CHECK(!strcmp(buffer, "-05")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL test::sprintf(buffer, "%010.1e", -5.); - REQUIRE(!strcmp(buffer, "-005.0e+00")); + CHECK(!strcmp(buffer, "-005.0e+00")); test::sprintf(buffer, "%07.0E", -5.); - REQUIRE(!strcmp(buffer, "-05E+00")); + CHECK(!strcmp(buffer, "-05E+00")); test::sprintf(buffer, "%03.0g", -5.); - REQUIRE(!strcmp(buffer, "-05")); + CHECK(!strcmp(buffer, "-05")); #endif } @@ -1143,76 +1143,76 @@ TEST_CASE("length", "[]" ) { char buffer[100]; test::sprintf(buffer, "%.0s", "Hello testing"); - REQUIRE(!strcmp(buffer, "")); + CHECK(!strcmp(buffer, "")); test::sprintf(buffer, "%20.0s", "Hello testing"); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%.s", "Hello testing"); - REQUIRE(!strcmp(buffer, "")); + CHECK(!strcmp(buffer, "")); test::sprintf(buffer, "%20.s", "Hello testing"); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%20.0d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%20.0d", -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%20.d", 0); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%20.0i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%20.i", -1024); - REQUIRE(!strcmp(buffer, " -1024")); + CHECK(!strcmp(buffer, " -1024")); test::sprintf(buffer, "%20.i", 0); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%20.u", 1024); - REQUIRE(!strcmp(buffer, " 1024")); + CHECK(!strcmp(buffer, " 1024")); test::sprintf(buffer, "%20.0u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); + CHECK(!strcmp(buffer, " 4294966272")); test::sprintf(buffer, "%20.u", 0U); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%20.o", 511); - REQUIRE(!strcmp(buffer, " 777")); + CHECK(!strcmp(buffer, " 777")); test::sprintf(buffer, "%20.0o", 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); + CHECK(!strcmp(buffer, " 37777777001")); test::sprintf(buffer, "%20.o", 0U); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%20.x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); + CHECK(!strcmp(buffer, " 1234abcd")); test::sprintf(buffer, "%50.x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); + CHECK(!strcmp(buffer, " 1234abcd")); test::sprintf(buffer, "%50.x%10.u", 305441741, 12345); - REQUIRE(!strcmp(buffer, " 1234abcd 12345")); + CHECK(!strcmp(buffer, " 1234abcd 12345")); test::sprintf(buffer, "%20.0x", 3989525555U); - REQUIRE(!strcmp(buffer, " edcb5433")); + CHECK(!strcmp(buffer, " edcb5433")); test::sprintf(buffer, "%20.x", 0U); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%20.X", 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); + CHECK(!strcmp(buffer, " 1234ABCD")); test::sprintf(buffer, "%20.0X", 3989525555U); - REQUIRE(!strcmp(buffer, " EDCB5433")); + CHECK(!strcmp(buffer, " EDCB5433")); test::sprintf(buffer, "%20.X", 0U); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -1220,10 +1220,10 @@ TEST_CASE("length - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "%02.0u", 0U); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%02.0d", 0); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); } #endif @@ -1233,181 +1233,181 @@ TEST_CASE("float", "[]" ) { // test special-case floats using math.h macros test::sprintf(buffer, "%8f", (double) NAN); - REQUIRE(!strcmp(buffer, " nan")); + CHECK(!strcmp(buffer, " nan")); test::sprintf(buffer, "%8f", (double) INFINITY); - REQUIRE(!strcmp(buffer, " inf")); + CHECK(!strcmp(buffer, " inf")); test::sprintf(buffer, "%-8f", (double) -INFINITY); - REQUIRE(!strcmp(buffer, "-inf ")); + CHECK(!strcmp(buffer, "-inf ")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL test::sprintf(buffer, "%+8e", (double) INFINITY); - REQUIRE(!strcmp(buffer, " +inf")); + CHECK(!strcmp(buffer, " +inf")); #endif test::sprintf(buffer, "%.4f", 3.1415354); - REQUIRE(!strcmp(buffer, "3.1415")); + CHECK(!strcmp(buffer, "3.1415")); test::sprintf(buffer, "%.3f", 30343.1415354); - REQUIRE(!strcmp(buffer, "30343.142")); + CHECK(!strcmp(buffer, "30343.142")); // switch from decimal to exponential representation // test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 ) ); if (PRINTF_MAX_FLOAT < 10e+2) { - REQUIRE(!strcmp(buffer, "10e+2")); + CHECK(!strcmp(buffer, "10e+2")); } else { - REQUIRE(!strcmp(buffer, "1000")); + CHECK(!strcmp(buffer, "1000")); } test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 ) ); if (PRINTF_MAX_FLOAT < 10e+5) { - REQUIRE(!strcmp(buffer, "10e+5")); + CHECK(!strcmp(buffer, "10e+5")); } else { - REQUIRE(!strcmp(buffer, "1000000")); + CHECK(!strcmp(buffer, "1000000")); } test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 ) ); if (PRINTF_MAX_FLOAT < 10e+8) { - REQUIRE(!strcmp(buffer, "10e+8")); + CHECK(!strcmp(buffer, "10e+8")); } else { - REQUIRE(!strcmp(buffer, "1000000000")); + CHECK(!strcmp(buffer, "1000000000")); } test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000) ); if (PRINTF_MAX_FLOAT < 10e+11) { - REQUIRE(!strcmp(buffer, "10e+11")); + CHECK(!strcmp(buffer, "10e+11")); } else { - REQUIRE(!strcmp(buffer, "1000000000000")); + CHECK(!strcmp(buffer, "1000000000000")); } test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000 * 1000) ); if (PRINTF_MAX_FLOAT < 10e+14) { - REQUIRE(!strcmp(buffer, "10e+14")); + CHECK(!strcmp(buffer, "10e+14")); } else { - REQUIRE(!strcmp(buffer, "1000000000000000")); + CHECK(!strcmp(buffer, "1000000000000000")); } test::sprintf(buffer, "%.0f", 34.1415354); - REQUIRE(!strcmp(buffer, "34")); + CHECK(!strcmp(buffer, "34")); test::sprintf(buffer, "%.0f", 1.3); - REQUIRE(!strcmp(buffer, "1")); + CHECK(!strcmp(buffer, "1")); test::sprintf(buffer, "%.0f", 1.55); - REQUIRE(!strcmp(buffer, "2")); + CHECK(!strcmp(buffer, "2")); test::sprintf(buffer, "%.1f", 1.64); - REQUIRE(!strcmp(buffer, "1.6")); + CHECK(!strcmp(buffer, "1.6")); test::sprintf(buffer, "%.2f", 42.8952); - REQUIRE(!strcmp(buffer, "42.90")); + CHECK(!strcmp(buffer, "42.90")); test::sprintf(buffer, "%.9f", 42.8952); - REQUIRE(!strcmp(buffer, "42.895200000")); + CHECK(!strcmp(buffer, "42.895200000")); test::sprintf(buffer, "%.10f", 42.895223); - REQUIRE(!strcmp(buffer, "42.8952230000")); + CHECK(!strcmp(buffer, "42.8952230000")); // this testcase checks, that the precision is truncated to 9 digits. // a perfect working float should return the whole number test::sprintf(buffer, "%.12f", 42.89522312345678); - REQUIRE(!strcmp(buffer, "42.895223123000")); + CHECK(!strcmp(buffer, "42.895223123000")); // this testcase checks, that the precision is truncated AND rounded to 9 digits. // a perfect working float should return the whole number test::sprintf(buffer, "%.12f", 42.89522387654321); - REQUIRE(!strcmp(buffer, "42.895223877000")); + CHECK(!strcmp(buffer, "42.895223877000")); test::sprintf(buffer, "%6.2f", 42.8952); - REQUIRE(!strcmp(buffer, " 42.90")); + CHECK(!strcmp(buffer, " 42.90")); test::sprintf(buffer, "%+6.2f", 42.8952); - REQUIRE(!strcmp(buffer, "+42.90")); + CHECK(!strcmp(buffer, "+42.90")); test::sprintf(buffer, "%+5.1f", 42.9252); - REQUIRE(!strcmp(buffer, "+42.9")); + CHECK(!strcmp(buffer, "+42.9")); test::sprintf(buffer, "%f", 42.5); - REQUIRE(!strcmp(buffer, "42.500000")); + CHECK(!strcmp(buffer, "42.500000")); test::sprintf(buffer, "%.1f", 42.5); - REQUIRE(!strcmp(buffer, "42.5")); + CHECK(!strcmp(buffer, "42.5")); test::sprintf(buffer, "%f", 42167.0); - REQUIRE(!strcmp(buffer, "42167.000000")); + CHECK(!strcmp(buffer, "42167.000000")); test::sprintf(buffer, "%.9f", -12345.987654321); - REQUIRE(!strcmp(buffer, "-12345.987654321")); + CHECK(!strcmp(buffer, "-12345.987654321")); test::sprintf(buffer, "%.1f", 3.999); - REQUIRE(!strcmp(buffer, "4.0")); + CHECK(!strcmp(buffer, "4.0")); test::sprintf(buffer, "%.0f", 3.5); - REQUIRE(!strcmp(buffer, "4")); + CHECK(!strcmp(buffer, "4")); test::sprintf(buffer, "%.0f", 4.5); - REQUIRE(!strcmp(buffer, "4")); + CHECK(!strcmp(buffer, "4")); test::sprintf(buffer, "%.0f", 3.49); - REQUIRE(!strcmp(buffer, "3")); + CHECK(!strcmp(buffer, "3")); test::sprintf(buffer, "%.1f", 3.49); - REQUIRE(!strcmp(buffer, "3.5")); + CHECK(!strcmp(buffer, "3.5")); test::sprintf(buffer, "a%-5.1f", 0.5); - REQUIRE(!strcmp(buffer, "a0.5 ")); + CHECK(!strcmp(buffer, "a0.5 ")); test::sprintf(buffer, "a%-5.1fend", 0.5); - REQUIRE(!strcmp(buffer, "a0.5 end")); + CHECK(!strcmp(buffer, "a0.5 end")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL test::sprintf(buffer, "%.4g", 1.0); - REQUIRE(!strcmp(buffer, "1")); + CHECK(!strcmp(buffer, "1")); test::sprintf(buffer, "%G", 12345.678); - REQUIRE(!strcmp(buffer, "12345.7")); + CHECK(!strcmp(buffer, "12345.7")); test::sprintf(buffer, "%.7G", 12345.678); - REQUIRE(!strcmp(buffer, "12345.68")); + CHECK(!strcmp(buffer, "12345.68")); test::sprintf(buffer, "%.5G", 123456789.); - REQUIRE(!strcmp(buffer, "1.2346E+08")); + CHECK(!strcmp(buffer, "1.2346E+08")); test::sprintf(buffer, "%.6G", 12345.); - REQUIRE(!strcmp(buffer, "12345.0")); + CHECK(!strcmp(buffer, "12345.0")); test::sprintf(buffer, "%+12.4g", 123456789.); - REQUIRE(!strcmp(buffer, " +1.235e+08")); + CHECK(!strcmp(buffer, " +1.235e+08")); test::sprintf(buffer, "%.2G", 0.001234); - REQUIRE(!strcmp(buffer, "0.0012")); + CHECK(!strcmp(buffer, "0.0012")); test::sprintf(buffer, "%+10.4G", 0.001234); - REQUIRE(!strcmp(buffer, " +0.001234")); + CHECK(!strcmp(buffer, " +0.001234")); test::sprintf(buffer, "%+012.4g", 0.00001234); - REQUIRE(!strcmp(buffer, "+001.234e-05")); + CHECK(!strcmp(buffer, "+001.234e-05")); test::sprintf(buffer, "%.3g", -1.2345e-308); - REQUIRE(!strcmp(buffer, "-1.23e-308")); + CHECK(!strcmp(buffer, "-1.23e-308")); test::sprintf(buffer, "%+.3E", 1.23e+308); - REQUIRE(!strcmp(buffer, "+1.230E+308")); + CHECK(!strcmp(buffer, "+1.230E+308")); #endif // out of range for float: should switch to exp notation if supported, else empty test::sprintf(buffer, "%.1f", 1E20); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - REQUIRE(!strcmp(buffer, "1.0e+20")); + CHECK(!strcmp(buffer, "1.0e+20")); #else - REQUIRE(!strcmp(buffer, "")); + CHECK(!strcmp(buffer, "")); #endif // brute force float @@ -1420,7 +1420,7 @@ TEST_CASE("float", "[]" ) { str << std::fixed << i / 10000; fail = fail || !!strcmp(buffer, str.str().c_str()); } - REQUIRE(!fail); + CHECK(!fail); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL @@ -1432,7 +1432,7 @@ TEST_CASE("float", "[]" ) { str << i; fail = fail || !!strcmp(buffer, str.str().c_str()); } - REQUIRE(!fail); + CHECK(!fail); #endif } @@ -1441,97 +1441,97 @@ TEST_CASE("types", "[]" ) { char buffer[100]; test::sprintf(buffer, "%i", 0); - REQUIRE(!strcmp(buffer, "0")); + CHECK(!strcmp(buffer, "0")); test::sprintf(buffer, "%i", 1234); - REQUIRE(!strcmp(buffer, "1234")); + CHECK(!strcmp(buffer, "1234")); test::sprintf(buffer, "%i", 32767); - REQUIRE(!strcmp(buffer, "32767")); + CHECK(!strcmp(buffer, "32767")); test::sprintf(buffer, "%i", -32767); - REQUIRE(!strcmp(buffer, "-32767")); + CHECK(!strcmp(buffer, "-32767")); test::sprintf(buffer, "%li", 30L); - REQUIRE(!strcmp(buffer, "30")); + CHECK(!strcmp(buffer, "30")); test::sprintf(buffer, "%li", -2147483647L); - REQUIRE(!strcmp(buffer, "-2147483647")); + CHECK(!strcmp(buffer, "-2147483647")); test::sprintf(buffer, "%li", 2147483647L); - REQUIRE(!strcmp(buffer, "2147483647")); + CHECK(!strcmp(buffer, "2147483647")); test::sprintf(buffer, "%lli", 30LL); - REQUIRE(!strcmp(buffer, "30")); + CHECK(!strcmp(buffer, "30")); test::sprintf(buffer, "%lli", -9223372036854775807LL); - REQUIRE(!strcmp(buffer, "-9223372036854775807")); + CHECK(!strcmp(buffer, "-9223372036854775807")); test::sprintf(buffer, "%lli", 9223372036854775807LL); - REQUIRE(!strcmp(buffer, "9223372036854775807")); + CHECK(!strcmp(buffer, "9223372036854775807")); test::sprintf(buffer, "%lu", 100000L); - REQUIRE(!strcmp(buffer, "100000")); + CHECK(!strcmp(buffer, "100000")); test::sprintf(buffer, "%lu", 0xFFFFFFFFL); - REQUIRE(!strcmp(buffer, "4294967295")); + CHECK(!strcmp(buffer, "4294967295")); test::sprintf(buffer, "%llu", 281474976710656LLU); - REQUIRE(!strcmp(buffer, "281474976710656")); + CHECK(!strcmp(buffer, "281474976710656")); test::sprintf(buffer, "%llu", 18446744073709551615LLU); - REQUIRE(!strcmp(buffer, "18446744073709551615")); + CHECK(!strcmp(buffer, "18446744073709551615")); test::sprintf(buffer, "%zu", (size_t)2147483647UL); - REQUIRE(!strcmp(buffer, "2147483647")); + CHECK(!strcmp(buffer, "2147483647")); test::sprintf(buffer, "%zd", (size_t)2147483647UL); - REQUIRE(!strcmp(buffer, "2147483647")); + CHECK(!strcmp(buffer, "2147483647")); test::sprintf(buffer, "%zi", (ssize_t)-2147483647L); - REQUIRE(!strcmp(buffer, "-2147483647")); + CHECK(!strcmp(buffer, "-2147483647")); test::sprintf(buffer, "%o", 60000); - REQUIRE(!strcmp(buffer, "165140")); + CHECK(!strcmp(buffer, "165140")); test::sprintf(buffer, "%lo", 12345678L); - REQUIRE(!strcmp(buffer, "57060516")); + CHECK(!strcmp(buffer, "57060516")); test::sprintf(buffer, "%lx", 0x12345678L); - REQUIRE(!strcmp(buffer, "12345678")); + CHECK(!strcmp(buffer, "12345678")); test::sprintf(buffer, "%llx", 0x1234567891234567LLU); - REQUIRE(!strcmp(buffer, "1234567891234567")); + CHECK(!strcmp(buffer, "1234567891234567")); test::sprintf(buffer, "%lx", 0xabcdefabL); - REQUIRE(!strcmp(buffer, "abcdefab")); + CHECK(!strcmp(buffer, "abcdefab")); test::sprintf(buffer, "%lX", 0xabcdefabL); - REQUIRE(!strcmp(buffer, "ABCDEFAB")); + CHECK(!strcmp(buffer, "ABCDEFAB")); test::sprintf(buffer, "%c", 'v'); - REQUIRE(!strcmp(buffer, "v")); + CHECK(!strcmp(buffer, "v")); test::sprintf(buffer, "%cv", 'w'); - REQUIRE(!strcmp(buffer, "wv")); + CHECK(!strcmp(buffer, "wv")); test::sprintf(buffer, "%s", "A Test"); - REQUIRE(!strcmp(buffer, "A Test")); + CHECK(!strcmp(buffer, "A Test")); test::sprintf(buffer, "%hhu", (unsigned char) 0xFFU); - REQUIRE(!strcmp(buffer, "255")); + CHECK(!strcmp(buffer, "255")); test::sprintf(buffer, "%hu", (unsigned short) 0x1234u); - REQUIRE(!strcmp(buffer, "4660")); + CHECK(!strcmp(buffer, "4660")); test::sprintf(buffer, "%s%hhi %hu", "Test", (char) 100, (unsigned short) 0xFFFF); - REQUIRE(!strcmp(buffer, "Test100 65535")); + CHECK(!strcmp(buffer, "Test100 65535")); test::sprintf(buffer, "%tx", &buffer[10] - &buffer[0]); - REQUIRE(!strcmp(buffer, "a")); + CHECK(!strcmp(buffer, "a")); test::sprintf(buffer, "%ji", (intmax_t)-2147483647L); - REQUIRE(!strcmp(buffer, "-2147483647")); + CHECK(!strcmp(buffer, "-2147483647")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -1539,10 +1539,10 @@ TEST_CASE("types - non-standard format", "[]" ) { char buffer[100]; test::sprintf(buffer, "%b", 60000); - REQUIRE(!strcmp(buffer, "1110101001100000")); + CHECK(!strcmp(buffer, "1110101001100000")); test::sprintf(buffer, "%lb", 12345678L); - REQUIRE(!strcmp(buffer, "101111000110000101001110")); + CHECK(!strcmp(buffer, "101111000110000101001110")); } #endif @@ -1551,39 +1551,39 @@ TEST_CASE("pointer", "[]" ) { test::sprintf(buffer, "%p", (void*)0x1234U); if (sizeof(void*) == 4U) { - REQUIRE(!strcmp(buffer, "0x00001234")); + CHECK(!strcmp(buffer, "0x00001234")); } else { - REQUIRE(!strcmp(buffer, "0x0000000000001234")); + CHECK(!strcmp(buffer, "0x0000000000001234")); } test::sprintf(buffer, "%p", (void*)0x12345678U); if (sizeof(void*) == 4U) { - REQUIRE(!strcmp(buffer, "0x12345678")); + CHECK(!strcmp(buffer, "0x12345678")); } else { - REQUIRE(!strcmp(buffer, "0x0000000012345678")); + CHECK(!strcmp(buffer, "0x0000000012345678")); } test::sprintf(buffer, "%p-%p", (void*)0x12345678U, (void*)0x7EDCBA98U); if (sizeof(void*) == 4U) { - REQUIRE(!strcmp(buffer, "0x12345678-0x7edcba98")); + CHECK(!strcmp(buffer, "0x12345678-0x7edcba98")); } else { - REQUIRE(!strcmp(buffer, "0x0000000012345678-0x000000007edcba98")); + CHECK(!strcmp(buffer, "0x0000000012345678-0x000000007edcba98")); } if (sizeof(uintptr_t) == sizeof(uint64_t)) { test::sprintf(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); - REQUIRE(!strcmp(buffer, "0x00000000ffffffff")); + CHECK(!strcmp(buffer, "0x00000000ffffffff")); } else { test::sprintf(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); - REQUIRE(!strcmp(buffer, "0xffffffff")); + CHECK(!strcmp(buffer, "0xffffffff")); } test::sprintf(buffer, "%p", NULL); - REQUIRE(!strcmp(buffer, "(nil)")); + CHECK(!strcmp(buffer, "(nil)")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -1591,7 +1591,7 @@ TEST_CASE("unknown flag (non-standard format)", "[]" ) { char buffer[100]; test::sprintf(buffer, "%kmarco", 42, 37); - REQUIRE(!strcmp(buffer, "kmarco")); + CHECK(!strcmp(buffer, "kmarco")); } #endif @@ -1599,28 +1599,28 @@ TEST_CASE("string length", "[]" ) { char buffer[100]; test::sprintf(buffer, "%.4s", "This is a test"); - REQUIRE(!strcmp(buffer, "This")); + CHECK(!strcmp(buffer, "This")); test::sprintf(buffer, "%.4s", "test"); - REQUIRE(!strcmp(buffer, "test")); + CHECK(!strcmp(buffer, "test")); test::sprintf(buffer, "%.7s", "123"); - REQUIRE(!strcmp(buffer, "123")); + CHECK(!strcmp(buffer, "123")); test::sprintf(buffer, "%.7s", ""); - REQUIRE(!strcmp(buffer, "")); + CHECK(!strcmp(buffer, "")); test::sprintf(buffer, "%.4s%.2s", "123456", "abcdef"); - REQUIRE(!strcmp(buffer, "1234ab")); + CHECK(!strcmp(buffer, "1234ab")); test::sprintf(buffer, "%.*s", 3, "123456"); - REQUIRE(!strcmp(buffer, "123")); + CHECK(!strcmp(buffer, "123")); DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW test::sprintf(buffer, "%.*s", 3, NULL); DISABLE_WARNING_POP - REQUIRE(!strcmp(buffer, "(null)")); + CHECK(!strcmp(buffer, "(null)")); } #ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS @@ -1628,7 +1628,7 @@ TEST_CASE("string length (non-standard format)", "[]" ) { char buffer[100]; test::sprintf(buffer, "%.4.2s", "123456"); - REQUIRE(!strcmp(buffer, ".2s")); + CHECK(!strcmp(buffer, ".2s")); } #endif @@ -1638,27 +1638,27 @@ TEST_CASE("buffer length", "[]" ) { int ret; ret = test::snprintf(nullptr, 10, "%s", "Test"); - REQUIRE(ret == 4); + CHECK(ret == 4); ret = test::snprintf(nullptr, 0, "%s", "Test"); - REQUIRE(ret == 4); + CHECK(ret == 4); buffer[0] = (char)0xA5; ret = test::snprintf(buffer, 0, "%s", "Test"); - REQUIRE(buffer[0] == (char)0xA5); - REQUIRE(ret == 4); + CHECK(buffer[0] == (char)0xA5); + CHECK(ret == 4); buffer[0] = (char)0xCC; test::snprintf(buffer, 1, "%s", "Test"); - REQUIRE(buffer[0] == '\0'); + CHECK(buffer[0] == '\0'); test::snprintf(buffer, 2, "%s", "Hello"); - REQUIRE(!strcmp(buffer, "H")); + CHECK(!strcmp(buffer, "H")); DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW test::snprintf(buffer, 2, "%s", NULL); DISABLE_WARNING_POP - REQUIRE(!strcmp(buffer, "(")); + CHECK(!strcmp(buffer, "(")); } @@ -1667,33 +1667,33 @@ TEST_CASE("ret value", "[]" ) { int ret; ret = test::snprintf(buffer, 6, "0%s", "1234"); - REQUIRE(!strcmp(buffer, "01234")); - REQUIRE(ret == 5); + CHECK(!strcmp(buffer, "01234")); + CHECK(ret == 5); ret = test::snprintf(buffer, 6, "0%s", "12345"); - REQUIRE(!strcmp(buffer, "01234")); - REQUIRE(ret == 6); // "5" is truncated + CHECK(!strcmp(buffer, "01234")); + CHECK(ret == 6); // "5" is truncated ret = test::snprintf(buffer, 6, "0%s", "1234567"); - REQUIRE(!strcmp(buffer, "01234")); - REQUIRE(ret == 8); // "567" are truncated + CHECK(!strcmp(buffer, "01234")); + CHECK(ret == 8); // "567" are truncated DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW ret = test::snprintf(buffer, 6, "0%s", NULL); DISABLE_WARNING_POP - REQUIRE(!strcmp(buffer, "0(nul")); - REQUIRE(ret == 7); // "l)" is truncated + CHECK(!strcmp(buffer, "0(nul")); + CHECK(ret == 7); // "l)" is truncated ret = test::snprintf(buffer, 10, "hello, world"); - REQUIRE(ret == 12); + CHECK(ret == 12); ret = test::snprintf(buffer, 3, "%d", 10000); - REQUIRE(ret == 5); - REQUIRE(strlen(buffer) == 2U); - REQUIRE(buffer[0] == '1'); - REQUIRE(buffer[1] == '0'); - REQUIRE(buffer[2] == '\0'); + CHECK(ret == 5); + CHECK(strlen(buffer) == 2U); + CHECK(buffer[0] == '1'); + CHECK(buffer[1] == '0'); + CHECK(buffer[2] == '\0'); } @@ -1701,32 +1701,32 @@ TEST_CASE("misc", "[]" ) { char buffer[100]; test::sprintf(buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"); - REQUIRE(!strcmp(buffer, "53000atest-20 bit")); + CHECK(!strcmp(buffer, "53000atest-20 bit")); test::sprintf(buffer, "%.*f", 2, 0.33333333); - REQUIRE(!strcmp(buffer, "0.33")); + CHECK(!strcmp(buffer, "0.33")); test::sprintf(buffer, "%.*d", -1, 1); - REQUIRE(!strcmp(buffer, "1")); + CHECK(!strcmp(buffer, "1")); test::sprintf(buffer, "%.3s", "foobar"); - REQUIRE(!strcmp(buffer, "foo")); + CHECK(!strcmp(buffer, "foo")); test::sprintf(buffer, "% .0d", 0); - REQUIRE(!strcmp(buffer, " ")); + CHECK(!strcmp(buffer, " ")); test::sprintf(buffer, "%10.5d", 4); - REQUIRE(!strcmp(buffer, " 00004")); + CHECK(!strcmp(buffer, " 00004")); test::sprintf(buffer, "%*sx", -3, "hi"); - REQUIRE(!strcmp(buffer, "hi x")); + CHECK(!strcmp(buffer, "hi x")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL test::sprintf(buffer, "%.*g", 2, 0.33333333); - REQUIRE(!strcmp(buffer, "0.33")); + CHECK(!strcmp(buffer, "0.33")); test::sprintf(buffer, "%.*e", 2, 0.33333333); - REQUIRE(!strcmp(buffer, "3.33e-01")); + CHECK(!strcmp(buffer, "3.33e-01")); #endif } From 60f9f2e56d43eb13d8eabe93627f1e6c9c01c981 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Fri, 9 Jul 2021 13:15:23 +0300 Subject: [PATCH 06/31] Fixes #14: We now have a CMake build option, translated into a defined value, controlling whether or not to alias `printf()`, `sprintf()` etc. --- CMakeLists.txt | 10 + printf.h | 15 +- test/test_suite.cpp | 804 ++++++++++++++++++++++---------------------- 3 files changed, 421 insertions(+), 408 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a337ee7..bb24436a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,16 @@ if (NOT SUPPORT_PTRDIFF_ARGUMENTS) add_definitions(-DPRINTF_DISABLE_SUPPORT_PTRDIFF_T) endif() +option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the relevant standard library function (printf, sprintf etc.) to the library's implementations" ON) +if (NOT ALIAS_STANDARD_FUNCTION_NAMES) + add_definitions(-DALIAS_STANDARD_FUNCTION_NAMES) +endif() + +option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the relevant standard library function (printf, sprintf etc.) to the library's implementations" ON) +if (NOT ALIAS_STANDARD_FUNCTION_NAMES) + add_definitions(-DALIAS_STANDARD_FUNCTION_NAMES) +endif() + # numeric defines set(INTEGER_TO_STRING_BUFFER_SIZE "32" CACHE STRING "Integer to string (ntoa) conversion buffer size") diff --git a/printf.h b/printf.h index 7adb1b2c..1b9b68e6 100644 --- a/printf.h +++ b/printf.h @@ -49,6 +49,15 @@ __attribute__((format(__printf__, (one_based_format_index), (first_arg)))) # define ATTR_VPRINTF(one_based_format_index) #endif +#ifdef ALIAS_STANDARD_FUNCTION_NAMES +# define printf printf_ +# define sprintf sprintf_ +# define vsprintf vsprintf_ +# define snprintf snprintf_ +# define vsnprintf vsnprintf_ +# define vprintf vprintf_ +#endif + /** * Output a character to a custom device like UART, used by the printf() function @@ -66,7 +75,6 @@ void _putchar(char character); * \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 */ -#define printf printf_ int printf_(const char* format, ...) ATTR_PRINTF(1, 2); @@ -78,8 +86,6 @@ int printf_(const char* format, ...) ATTR_PRINTF(1, 2); * \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 */ -#define sprintf sprintf_ -#define vsprintf vsprintf_ int sprintf_(char* buffer, const char* format, ...) ATTR_PRINTF(2, 3); int vsprintf_(char* buffer, const char* format, va_list va) ATTR_VPRINTF(2); @@ -94,8 +100,6 @@ int vsprintf_(char* buffer, const char* format, va_list va) ATTR_VPRINTF(2); * 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. */ -#define snprintf snprintf_ -#define vsnprintf vsnprintf_ int snprintf_(char* buffer, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) ATTR_VPRINTF(3); @@ -106,7 +110,6 @@ int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) ATTR_ * \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 */ -#define vprintf vprintf_ int vprintf_(const char* format, va_list va) ATTR_VPRINTF(1); diff --git a/test/test_suite.cpp b/test/test_suite.cpp index 3fe270e3..6acbada9 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -95,7 +95,7 @@ void _out_fct(char character, void* arg) TEST_CASE("printf", "[]" ) { printf_idx = 0U; memset(printf_buffer, 0xCC, 100U); - CHECK(test::printf("% d", 4232) == 5); + CHECK(test::printf_("% d", 4232) == 5); CHECK(printf_buffer[5] == (char)0xCC); printf_buffer[5] = 0; CHECK(!strcmp(printf_buffer, " 4232")); @@ -130,13 +130,13 @@ TEST_CASE("vfctprintf", "[]" ) { CHECK(printf_buffer[22] == (char)0xCC); } -TEST_CASE("snprintf", "[]" ) { +TEST_CASE("snprintf_", "[]" ) { char buffer[100]; - test::snprintf(buffer, 100U, "%d", -1000); + test::snprintf_(buffer, 100U, "%d", -1000); CHECK(!strcmp(buffer, "-1000")); - test::snprintf(buffer, 3U, "%d", -1000); + test::snprintf_(buffer, 3U, "%d", -1000); CHECK(!strcmp(buffer, "-1")); } @@ -144,7 +144,7 @@ static void vprintf_builder_1(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vprintf("%d", args); + test::vprintf_("%d", args); va_end(args); } @@ -152,7 +152,7 @@ static void vsprintf_builder_1(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vsprintf(buffer, "%d", args); + test::vsprintf_(buffer, "%d", args); va_end(args); } @@ -160,7 +160,7 @@ static void vsnprintf_builder_1(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vsnprintf(buffer, 100U, "%d", args); + test::vsnprintf_(buffer, 100U, "%d", args); va_end(args); } @@ -168,7 +168,7 @@ static void vsprintf_builder_3(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vsprintf(buffer, "%d %d %s", args); + test::vsprintf_(buffer, "%d %d %s", args); va_end(args); } @@ -176,7 +176,7 @@ static void vsnprintf_builder_3(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vsnprintf(buffer, 100U, "%d %d %s", args); + test::vsnprintf_(buffer, 100U, "%d %d %s", args); va_end(args); } @@ -203,7 +203,7 @@ TEST_CASE("vsprintf", "[]" ) { } -TEST_CASE("vsnprintf", "[]" ) { +TEST_CASE("vsnprintf_", "[]" ) { char buffer[100]; vsnprintf_builder_1(buffer, -1); @@ -217,43 +217,43 @@ TEST_CASE("vsnprintf", "[]" ) { TEST_CASE("space flag", "[]" ) { char buffer[100]; - test::sprintf(buffer, "% d", 42); + test::sprintf_(buffer, "% d", 42); CHECK(!strcmp(buffer, " 42")); - test::sprintf(buffer, "% d", -42); + test::sprintf_(buffer, "% d", -42); CHECK(!strcmp(buffer, "-42")); - test::sprintf(buffer, "% 5d", 42); + test::sprintf_(buffer, "% 5d", 42); CHECK(!strcmp(buffer, " 42")); - test::sprintf(buffer, "% 5d", -42); + test::sprintf_(buffer, "% 5d", -42); CHECK(!strcmp(buffer, " -42")); - test::sprintf(buffer, "% 15d", 42); + test::sprintf_(buffer, "% 15d", 42); CHECK(!strcmp(buffer, " 42")); - test::sprintf(buffer, "% 15d", -42); + test::sprintf_(buffer, "% 15d", -42); CHECK(!strcmp(buffer, " -42")); - test::sprintf(buffer, "% 15d", -42); + test::sprintf_(buffer, "% 15d", -42); CHECK(!strcmp(buffer, " -42")); - test::sprintf(buffer, "% 15.3f", -42.987); + test::sprintf_(buffer, "% 15.3f", -42.987); CHECK(!strcmp(buffer, " -42.987")); - test::sprintf(buffer, "% 15.3f", 42.987); + test::sprintf_(buffer, "% 15.3f", 42.987); CHECK(!strcmp(buffer, " 42.987")); - test::sprintf(buffer, "% d", 1024); + test::sprintf_(buffer, "% d", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "% d", -1024); + test::sprintf_(buffer, "% d", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "% i", 1024); + test::sprintf_(buffer, "% i", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "% i", -1024); + test::sprintf_(buffer, "% i", -1024); CHECK(!strcmp(buffer, "-1024")); } @@ -261,34 +261,34 @@ TEST_CASE("space flag", "[]" ) { TEST_CASE("space flag - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "% s", "Hello testing"); + test::sprintf_(buffer, "% s", "Hello testing"); CHECK(!strcmp(buffer, "Hello testing")); - test::sprintf(buffer, "% u", 1024); + test::sprintf_(buffer, "% u", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "% u", 4294966272U); + test::sprintf_(buffer, "% u", 4294966272U); CHECK(!strcmp(buffer, "4294966272")); - test::sprintf(buffer, "% o", 511); + test::sprintf_(buffer, "% o", 511); CHECK(!strcmp(buffer, "777")); - test::sprintf(buffer, "% o", 4294966785U); + test::sprintf_(buffer, "% o", 4294966785U); CHECK(!strcmp(buffer, "37777777001")); - test::sprintf(buffer, "% x", 305441741); + test::sprintf_(buffer, "% x", 305441741); CHECK(!strcmp(buffer, "1234abcd")); - test::sprintf(buffer, "% x", 3989525555U); + test::sprintf_(buffer, "% x", 3989525555U); CHECK(!strcmp(buffer, "edcb5433")); - test::sprintf(buffer, "% X", 305441741); + test::sprintf_(buffer, "% X", 305441741); CHECK(!strcmp(buffer, "1234ABCD")); - test::sprintf(buffer, "% X", 3989525555U); + test::sprintf_(buffer, "% X", 3989525555U); CHECK(!strcmp(buffer, "EDCB5433")); - test::sprintf(buffer, "% c", 'x'); + test::sprintf_(buffer, "% c", 'x'); CHECK(!strcmp(buffer, "x")); } #endif @@ -297,37 +297,37 @@ TEST_CASE("space flag - non-standard format", "[]" ) { TEST_CASE("+ flag", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%+d", 42); + test::sprintf_(buffer, "%+d", 42); CHECK(!strcmp(buffer, "+42")); - test::sprintf(buffer, "%+d", -42); + test::sprintf_(buffer, "%+d", -42); CHECK(!strcmp(buffer, "-42")); - test::sprintf(buffer, "%+5d", 42); + test::sprintf_(buffer, "%+5d", 42); CHECK(!strcmp(buffer, " +42")); - test::sprintf(buffer, "%+5d", -42); + test::sprintf_(buffer, "%+5d", -42); CHECK(!strcmp(buffer, " -42")); - test::sprintf(buffer, "%+15d", 42); + test::sprintf_(buffer, "%+15d", 42); CHECK(!strcmp(buffer, " +42")); - test::sprintf(buffer, "%+15d", -42); + test::sprintf_(buffer, "%+15d", -42); CHECK(!strcmp(buffer, " -42")); - test::sprintf(buffer, "%+d", 1024); + test::sprintf_(buffer, "%+d", 1024); CHECK(!strcmp(buffer, "+1024")); - test::sprintf(buffer, "%+d", -1024); + test::sprintf_(buffer, "%+d", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "%+i", 1024); + test::sprintf_(buffer, "%+i", 1024); CHECK(!strcmp(buffer, "+1024")); - test::sprintf(buffer, "%+i", -1024); + test::sprintf_(buffer, "%+i", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "%+.0d", 0); + test::sprintf_(buffer, "%+.0d", 0); CHECK(!strcmp(buffer, "+")); } @@ -335,34 +335,34 @@ TEST_CASE("+ flag", "[]" ) { TEST_CASE("+ flag - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%+s", "Hello testing"); + test::sprintf_(buffer, "%+s", "Hello testing"); CHECK(!strcmp(buffer, "Hello testing")); - test::sprintf(buffer, "%+u", 1024); + test::sprintf_(buffer, "%+u", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%+u", 4294966272U); + test::sprintf_(buffer, "%+u", 4294966272U); CHECK(!strcmp(buffer, "4294966272")); - test::sprintf(buffer, "%+o", 511); + test::sprintf_(buffer, "%+o", 511); CHECK(!strcmp(buffer, "777")); - test::sprintf(buffer, "%+o", 4294966785U); + test::sprintf_(buffer, "%+o", 4294966785U); CHECK(!strcmp(buffer, "37777777001")); - test::sprintf(buffer, "%+x", 305441741); + test::sprintf_(buffer, "%+x", 305441741); CHECK(!strcmp(buffer, "1234abcd")); - test::sprintf(buffer, "%+x", 3989525555U); + test::sprintf_(buffer, "%+x", 3989525555U); CHECK(!strcmp(buffer, "edcb5433")); - test::sprintf(buffer, "%+X", 305441741); + test::sprintf_(buffer, "%+X", 305441741); CHECK(!strcmp(buffer, "1234ABCD")); - test::sprintf(buffer, "%+X", 3989525555U); + test::sprintf_(buffer, "%+X", 3989525555U); CHECK(!strcmp(buffer, "EDCB5433")); - test::sprintf(buffer, "%+c", 'x'); + test::sprintf_(buffer, "%+c", 'x'); CHECK(!strcmp(buffer, "x")); } #endif @@ -371,34 +371,34 @@ TEST_CASE("+ flag - non-standard format", "[]" ) { TEST_CASE("0 flag", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%0d", 42); + test::sprintf_(buffer, "%0d", 42); CHECK(!strcmp(buffer, "42")); - test::sprintf(buffer, "%0ld", 42L); + test::sprintf_(buffer, "%0ld", 42L); CHECK(!strcmp(buffer, "42")); - test::sprintf(buffer, "%0d", -42); + test::sprintf_(buffer, "%0d", -42); CHECK(!strcmp(buffer, "-42")); - test::sprintf(buffer, "%05d", 42); + test::sprintf_(buffer, "%05d", 42); CHECK(!strcmp(buffer, "00042")); - test::sprintf(buffer, "%05d", -42); + test::sprintf_(buffer, "%05d", -42); CHECK(!strcmp(buffer, "-0042")); - test::sprintf(buffer, "%015d", 42); + test::sprintf_(buffer, "%015d", 42); CHECK(!strcmp(buffer, "000000000000042")); - test::sprintf(buffer, "%015d", -42); + test::sprintf_(buffer, "%015d", -42); CHECK(!strcmp(buffer, "-00000000000042")); - test::sprintf(buffer, "%015.2f", 42.1234); + test::sprintf_(buffer, "%015.2f", 42.1234); CHECK(!strcmp(buffer, "000000000042.12")); - test::sprintf(buffer, "%015.3f", 42.9876); + test::sprintf_(buffer, "%015.3f", 42.9876); CHECK(!strcmp(buffer, "00000000042.988")); - test::sprintf(buffer, "%015.5f", -42.9876); + test::sprintf_(buffer, "%015.5f", -42.9876); CHECK(!strcmp(buffer, "-00000042.98760")); } @@ -406,22 +406,22 @@ TEST_CASE("0 flag", "[]" ) { TEST_CASE("- flag", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%-d", 42); + test::sprintf_(buffer, "%-d", 42); CHECK(!strcmp(buffer, "42")); - test::sprintf(buffer, "%-d", -42); + test::sprintf_(buffer, "%-d", -42); CHECK(!strcmp(buffer, "-42")); - test::sprintf(buffer, "%-5d", 42); + test::sprintf_(buffer, "%-5d", 42); CHECK(!strcmp(buffer, "42 ")); - test::sprintf(buffer, "%-5d", -42); + test::sprintf_(buffer, "%-5d", -42); CHECK(!strcmp(buffer, "-42 ")); - test::sprintf(buffer, "%-15d", 42); + test::sprintf_(buffer, "%-15d", 42); CHECK(!strcmp(buffer, "42 ")); - test::sprintf(buffer, "%-15d", -42); + test::sprintf_(buffer, "%-15d", -42); CHECK(!strcmp(buffer, "-42 ")); } @@ -429,50 +429,50 @@ TEST_CASE("- flag", "[]" ) { TEST_CASE("- flag - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%-0d", 42); + test::sprintf_(buffer, "%-0d", 42); CHECK(!strcmp(buffer, "42")); - test::sprintf(buffer, "%-0d", -42); + test::sprintf_(buffer, "%-0d", -42); CHECK(!strcmp(buffer, "-42")); - test::sprintf(buffer, "%-05d", 42); + test::sprintf_(buffer, "%-05d", 42); CHECK(!strcmp(buffer, "42 ")); - test::sprintf(buffer, "%-05d", -42); + test::sprintf_(buffer, "%-05d", -42); CHECK(!strcmp(buffer, "-42 ")); - test::sprintf(buffer, "%-015d", 42); + test::sprintf_(buffer, "%-015d", 42); CHECK(!strcmp(buffer, "42 ")); - test::sprintf(buffer, "%-015d", -42); + test::sprintf_(buffer, "%-015d", -42); CHECK(!strcmp(buffer, "-42 ")); - test::sprintf(buffer, "%0-d", 42); + test::sprintf_(buffer, "%0-d", 42); CHECK(!strcmp(buffer, "42")); - test::sprintf(buffer, "%0-d", -42); + test::sprintf_(buffer, "%0-d", -42); CHECK(!strcmp(buffer, "-42")); - test::sprintf(buffer, "%0-5d", 42); + test::sprintf_(buffer, "%0-5d", 42); CHECK(!strcmp(buffer, "42 ")); - test::sprintf(buffer, "%0-5d", -42); + test::sprintf_(buffer, "%0-5d", -42); CHECK(!strcmp(buffer, "-42 ")); - test::sprintf(buffer, "%0-15d", 42); + test::sprintf_(buffer, "%0-15d", 42); CHECK(!strcmp(buffer, "42 ")); - test::sprintf(buffer, "%0-15d", -42); + test::sprintf_(buffer, "%0-15d", -42); CHECK(!strcmp(buffer, "-42 ")); - test::sprintf(buffer, "%0-15.3e", -42.); + test::sprintf_(buffer, "%0-15.3e", -42.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL CHECK(!strcmp(buffer, "-4.200e+01 ")); #else CHECK(!strcmp(buffer, "e")); #endif - test::sprintf(buffer, "%0-15.3g", -42.); + test::sprintf_(buffer, "%0-15.3g", -42.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL CHECK(!strcmp(buffer, "-42.0 ")); #else @@ -518,7 +518,7 @@ TEST_CASE("# flag", "[]" ) { test::sprintf(buffer, "%#.0llx", (long long)0); CHECK(!strcmp(buffer, "")); - test::sprintf(buffer, "%#.8x", 0x614e); + test::sprintf_(buffer, "%#.8x", 0x614e); CHECK(!strcmp(buffer, "0x0000614e")); } @@ -526,7 +526,7 @@ TEST_CASE("# flag", "[]" ) { TEST_CASE("# flag - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer,"%#b", 6); + test::sprintf_(buffer,"%#b", 6); CHECK(!strcmp(buffer, "0b110")); } #endif @@ -534,55 +534,55 @@ TEST_CASE("# flag - non-standard format", "[]" ) { TEST_CASE("specifier", "[]" ) { char buffer[100]; - test::sprintf(buffer, "Hello testing"); + test::sprintf_(buffer, "Hello testing"); CHECK(!strcmp(buffer, "Hello testing")); - test::sprintf(buffer, "%s", "Hello testing"); + test::sprintf_(buffer, "%s", "Hello testing"); CHECK(!strcmp(buffer, "Hello testing")); DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW - test::sprintf(buffer, "%s", NULL); + test::sprintf_(buffer, "%s", NULL); DISABLE_WARNING_POP CHECK(!strcmp(buffer, "(null)")); - test::sprintf(buffer, "%d", 1024); + test::sprintf_(buffer, "%d", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%d", -1024); + test::sprintf_(buffer, "%d", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "%i", 1024); + test::sprintf_(buffer, "%i", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%i", -1024); + test::sprintf_(buffer, "%i", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "%u", 1024); + test::sprintf_(buffer, "%u", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%u", 4294966272U); + test::sprintf_(buffer, "%u", 4294966272U); CHECK(!strcmp(buffer, "4294966272")); - test::sprintf(buffer, "%o", 511); + test::sprintf_(buffer, "%o", 511); CHECK(!strcmp(buffer, "777")); - test::sprintf(buffer, "%o", 4294966785U); + test::sprintf_(buffer, "%o", 4294966785U); CHECK(!strcmp(buffer, "37777777001")); - test::sprintf(buffer, "%x", 305441741); + test::sprintf_(buffer, "%x", 305441741); CHECK(!strcmp(buffer, "1234abcd")); - test::sprintf(buffer, "%x", 3989525555U); + test::sprintf_(buffer, "%x", 3989525555U); CHECK(!strcmp(buffer, "edcb5433")); - test::sprintf(buffer, "%X", 305441741); + test::sprintf_(buffer, "%X", 305441741); CHECK(!strcmp(buffer, "1234ABCD")); - test::sprintf(buffer, "%X", 3989525555U); + test::sprintf_(buffer, "%X", 3989525555U); CHECK(!strcmp(buffer, "EDCB5433")); - test::sprintf(buffer, "%%"); + test::sprintf_(buffer, "%%"); CHECK(!strcmp(buffer, "%")); } @@ -590,46 +590,46 @@ DISABLE_WARNING_POP TEST_CASE("width", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%1s", "Hello testing"); + test::sprintf_(buffer, "%1s", "Hello testing"); CHECK(!strcmp(buffer, "Hello testing")); - test::sprintf(buffer, "%1d", 1024); + test::sprintf_(buffer, "%1d", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%1d", -1024); + test::sprintf_(buffer, "%1d", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "%1i", 1024); + test::sprintf_(buffer, "%1i", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%1i", -1024); + test::sprintf_(buffer, "%1i", -1024); CHECK(!strcmp(buffer, "-1024")); - test::sprintf(buffer, "%1u", 1024); + test::sprintf_(buffer, "%1u", 1024); CHECK(!strcmp(buffer, "1024")); - test::sprintf(buffer, "%1u", 4294966272U); + test::sprintf_(buffer, "%1u", 4294966272U); CHECK(!strcmp(buffer, "4294966272")); - test::sprintf(buffer, "%1o", 511); + test::sprintf_(buffer, "%1o", 511); CHECK(!strcmp(buffer, "777")); - test::sprintf(buffer, "%1o", 4294966785U); + test::sprintf_(buffer, "%1o", 4294966785U); CHECK(!strcmp(buffer, "37777777001")); - test::sprintf(buffer, "%1x", 305441741); + test::sprintf_(buffer, "%1x", 305441741); CHECK(!strcmp(buffer, "1234abcd")); - test::sprintf(buffer, "%1x", 3989525555U); + test::sprintf_(buffer, "%1x", 3989525555U); CHECK(!strcmp(buffer, "edcb5433")); - test::sprintf(buffer, "%1X", 305441741); + test::sprintf_(buffer, "%1X", 305441741); CHECK(!strcmp(buffer, "1234ABCD")); - test::sprintf(buffer, "%1X", 3989525555U); + test::sprintf_(buffer, "%1X", 3989525555U); CHECK(!strcmp(buffer, "EDCB5433")); - test::sprintf(buffer, "%1c", 'x'); + test::sprintf_(buffer, "%1c", 'x'); CHECK(!strcmp(buffer, "x")); } @@ -637,46 +637,46 @@ TEST_CASE("width", "[]" ) { TEST_CASE("width 20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%20s", "Hello"); + test::sprintf_(buffer, "%20s", "Hello"); CHECK(!strcmp(buffer, " Hello")); - test::sprintf(buffer, "%20d", 1024); + test::sprintf_(buffer, "%20d", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%20d", -1024); + test::sprintf_(buffer, "%20d", -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%20i", 1024); + test::sprintf_(buffer, "%20i", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%20i", -1024); + test::sprintf_(buffer, "%20i", -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%20u", 1024); + test::sprintf_(buffer, "%20u", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%20u", 4294966272U); + test::sprintf_(buffer, "%20u", 4294966272U); CHECK(!strcmp(buffer, " 4294966272")); - test::sprintf(buffer, "%20o", 511); + test::sprintf_(buffer, "%20o", 511); CHECK(!strcmp(buffer, " 777")); - test::sprintf(buffer, "%20o", 4294966785U); + test::sprintf_(buffer, "%20o", 4294966785U); CHECK(!strcmp(buffer, " 37777777001")); - test::sprintf(buffer, "%20x", 305441741); + test::sprintf_(buffer, "%20x", 305441741); CHECK(!strcmp(buffer, " 1234abcd")); - test::sprintf(buffer, "%20x", 3989525555U); + test::sprintf_(buffer, "%20x", 3989525555U); CHECK(!strcmp(buffer, " edcb5433")); - test::sprintf(buffer, "%20X", 305441741); + test::sprintf_(buffer, "%20X", 305441741); CHECK(!strcmp(buffer, " 1234ABCD")); - test::sprintf(buffer, "%20X", 3989525555U); + test::sprintf_(buffer, "%20X", 3989525555U); CHECK(!strcmp(buffer, " EDCB5433")); - test::sprintf(buffer, "%20c", 'x'); + test::sprintf_(buffer, "%20c", 'x'); CHECK(!strcmp(buffer, " x")); } @@ -684,46 +684,46 @@ TEST_CASE("width 20", "[]" ) { TEST_CASE("width *20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%*s", 20, "Hello"); + test::sprintf_(buffer, "%*s", 20, "Hello"); CHECK(!strcmp(buffer, " Hello")); - test::sprintf(buffer, "%*d", 20, 1024); + test::sprintf_(buffer, "%*d", 20, 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%*d", 20, -1024); + test::sprintf_(buffer, "%*d", 20, -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%*i", 20, 1024); + test::sprintf_(buffer, "%*i", 20, 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%*i", 20, -1024); + test::sprintf_(buffer, "%*i", 20, -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%*u", 20, 1024); + test::sprintf_(buffer, "%*u", 20, 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%*u", 20, 4294966272U); + test::sprintf_(buffer, "%*u", 20, 4294966272U); CHECK(!strcmp(buffer, " 4294966272")); - test::sprintf(buffer, "%*o", 20, 511); + test::sprintf_(buffer, "%*o", 20, 511); CHECK(!strcmp(buffer, " 777")); - test::sprintf(buffer, "%*o", 20, 4294966785U); + test::sprintf_(buffer, "%*o", 20, 4294966785U); CHECK(!strcmp(buffer, " 37777777001")); - test::sprintf(buffer, "%*x", 20, 305441741); + test::sprintf_(buffer, "%*x", 20, 305441741); CHECK(!strcmp(buffer, " 1234abcd")); - test::sprintf(buffer, "%*x", 20, 3989525555U); + test::sprintf_(buffer, "%*x", 20, 3989525555U); CHECK(!strcmp(buffer, " edcb5433")); - test::sprintf(buffer, "%*X", 20, 305441741); + test::sprintf_(buffer, "%*X", 20, 305441741); CHECK(!strcmp(buffer, " 1234ABCD")); - test::sprintf(buffer, "%*X", 20, 3989525555U); + test::sprintf_(buffer, "%*X", 20, 3989525555U); CHECK(!strcmp(buffer, " EDCB5433")); - test::sprintf(buffer, "%*c", 20,'x'); + test::sprintf_(buffer, "%*c", 20,'x'); CHECK(!strcmp(buffer, " x")); } @@ -731,61 +731,61 @@ TEST_CASE("width *20", "[]" ) { TEST_CASE("width -20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%-20s", "Hello"); + test::sprintf_(buffer, "%-20s", "Hello"); CHECK(!strcmp(buffer, "Hello ")); - test::sprintf(buffer, "%-20d", 1024); + test::sprintf_(buffer, "%-20d", 1024); CHECK(!strcmp(buffer, "1024 ")); - test::sprintf(buffer, "%-20d", -1024); + test::sprintf_(buffer, "%-20d", -1024); CHECK(!strcmp(buffer, "-1024 ")); - test::sprintf(buffer, "%-20i", 1024); + test::sprintf_(buffer, "%-20i", 1024); CHECK(!strcmp(buffer, "1024 ")); - test::sprintf(buffer, "%-20i", -1024); + test::sprintf_(buffer, "%-20i", -1024); CHECK(!strcmp(buffer, "-1024 ")); - test::sprintf(buffer, "%-20u", 1024); + test::sprintf_(buffer, "%-20u", 1024); CHECK(!strcmp(buffer, "1024 ")); - test::sprintf(buffer, "%-20.4f", 1024.1234); + test::sprintf_(buffer, "%-20.4f", 1024.1234); CHECK(!strcmp(buffer, "1024.1234 ")); - test::sprintf(buffer, "%-20u", 4294966272U); + test::sprintf_(buffer, "%-20u", 4294966272U); CHECK(!strcmp(buffer, "4294966272 ")); - test::sprintf(buffer, "%-20o", 511); + test::sprintf_(buffer, "%-20o", 511); CHECK(!strcmp(buffer, "777 ")); - test::sprintf(buffer, "%-20o", 4294966785U); + test::sprintf_(buffer, "%-20o", 4294966785U); CHECK(!strcmp(buffer, "37777777001 ")); - test::sprintf(buffer, "%-20x", 305441741); + test::sprintf_(buffer, "%-20x", 305441741); CHECK(!strcmp(buffer, "1234abcd ")); - test::sprintf(buffer, "%-20x", 3989525555U); + test::sprintf_(buffer, "%-20x", 3989525555U); CHECK(!strcmp(buffer, "edcb5433 ")); - test::sprintf(buffer, "%-20X", 305441741); + test::sprintf_(buffer, "%-20X", 305441741); CHECK(!strcmp(buffer, "1234ABCD ")); - test::sprintf(buffer, "%-20X", 3989525555U); + test::sprintf_(buffer, "%-20X", 3989525555U); CHECK(!strcmp(buffer, "EDCB5433 ")); - test::sprintf(buffer, "%-20c", 'x'); + test::sprintf_(buffer, "%-20c", 'x'); CHECK(!strcmp(buffer, "x ")); - test::sprintf(buffer, "|%5d| |%-2d| |%5d|", 9, 9, 9); + test::sprintf_(buffer, "|%5d| |%-2d| |%5d|", 9, 9, 9); CHECK(!strcmp(buffer, "| 9| |9 | | 9|")); - test::sprintf(buffer, "|%5d| |%-2d| |%5d|", 10, 10, 10); + test::sprintf_(buffer, "|%5d| |%-2d| |%5d|", 10, 10, 10); CHECK(!strcmp(buffer, "| 10| |10| | 10|")); - test::sprintf(buffer, "|%5d| |%-12d| |%5d|", 9, 9, 9); + test::sprintf_(buffer, "|%5d| |%-12d| |%5d|", 9, 9, 9); CHECK(!strcmp(buffer, "| 9| |9 | | 9|")); - test::sprintf(buffer, "|%5d| |%-12d| |%5d|", 10, 10, 10); + test::sprintf_(buffer, "|%5d| |%-12d| |%5d|", 10, 10, 10); CHECK(!strcmp(buffer, "| 10| |10 | | 10|")); } @@ -793,46 +793,46 @@ TEST_CASE("width -20", "[]" ) { TEST_CASE("width 0-20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%0-20s", "Hello"); + test::sprintf_(buffer, "%0-20s", "Hello"); CHECK(!strcmp(buffer, "Hello ")); - test::sprintf(buffer, "%0-20d", 1024); + test::sprintf_(buffer, "%0-20d", 1024); CHECK(!strcmp(buffer, "1024 ")); - test::sprintf(buffer, "%0-20d", -1024); + test::sprintf_(buffer, "%0-20d", -1024); CHECK(!strcmp(buffer, "-1024 ")); - test::sprintf(buffer, "%0-20i", 1024); + test::sprintf_(buffer, "%0-20i", 1024); CHECK(!strcmp(buffer, "1024 ")); - test::sprintf(buffer, "%0-20i", -1024); + test::sprintf_(buffer, "%0-20i", -1024); CHECK(!strcmp(buffer, "-1024 ")); - test::sprintf(buffer, "%0-20u", 1024); + test::sprintf_(buffer, "%0-20u", 1024); CHECK(!strcmp(buffer, "1024 ")); - test::sprintf(buffer, "%0-20u", 4294966272U); + test::sprintf_(buffer, "%0-20u", 4294966272U); CHECK(!strcmp(buffer, "4294966272 ")); - test::sprintf(buffer, "%0-20o", 511); + test::sprintf_(buffer, "%0-20o", 511); CHECK(!strcmp(buffer, "777 ")); - test::sprintf(buffer, "%0-20o", 4294966785U); + test::sprintf_(buffer, "%0-20o", 4294966785U); CHECK(!strcmp(buffer, "37777777001 ")); - test::sprintf(buffer, "%0-20x", 305441741); + test::sprintf_(buffer, "%0-20x", 305441741); CHECK(!strcmp(buffer, "1234abcd ")); - test::sprintf(buffer, "%0-20x", 3989525555U); + test::sprintf_(buffer, "%0-20x", 3989525555U); CHECK(!strcmp(buffer, "edcb5433 ")); - test::sprintf(buffer, "%0-20X", 305441741); + test::sprintf_(buffer, "%0-20X", 305441741); CHECK(!strcmp(buffer, "1234ABCD ")); - test::sprintf(buffer, "%0-20X", 3989525555U); + test::sprintf_(buffer, "%0-20X", 3989525555U); CHECK(!strcmp(buffer, "EDCB5433 ")); - test::sprintf(buffer, "%0-20c", 'x'); + test::sprintf_(buffer, "%0-20c", 'x'); CHECK(!strcmp(buffer, "x ")); } #endif @@ -840,40 +840,40 @@ TEST_CASE("width 0-20", "[]" ) { TEST_CASE("padding 20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%020d", 1024); + test::sprintf_(buffer, "%020d", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%020d", -1024); + test::sprintf_(buffer, "%020d", -1024); CHECK(!strcmp(buffer, "-0000000000000001024")); - test::sprintf(buffer, "%020i", 1024); + test::sprintf_(buffer, "%020i", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%020i", -1024); + test::sprintf_(buffer, "%020i", -1024); CHECK(!strcmp(buffer, "-0000000000000001024")); - test::sprintf(buffer, "%020u", 1024); + test::sprintf_(buffer, "%020u", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%020u", 4294966272U); + test::sprintf_(buffer, "%020u", 4294966272U); CHECK(!strcmp(buffer, "00000000004294966272")); - test::sprintf(buffer, "%020o", 511); + test::sprintf_(buffer, "%020o", 511); CHECK(!strcmp(buffer, "00000000000000000777")); - test::sprintf(buffer, "%020o", 4294966785U); + test::sprintf_(buffer, "%020o", 4294966785U); CHECK(!strcmp(buffer, "00000000037777777001")); - test::sprintf(buffer, "%020x", 305441741); + test::sprintf_(buffer, "%020x", 305441741); CHECK(!strcmp(buffer, "0000000000001234abcd")); - test::sprintf(buffer, "%020x", 3989525555U); + test::sprintf_(buffer, "%020x", 3989525555U); CHECK(!strcmp(buffer, "000000000000edcb5433")); - test::sprintf(buffer, "%020X", 305441741); + test::sprintf_(buffer, "%020X", 305441741); CHECK(!strcmp(buffer, "0000000000001234ABCD")); - test::sprintf(buffer, "%020X", 3989525555U); + test::sprintf_(buffer, "%020X", 3989525555U); CHECK(!strcmp(buffer, "000000000000EDCB5433")); } @@ -881,40 +881,40 @@ TEST_CASE("padding 20", "[]" ) { TEST_CASE("padding .20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%.20d", 1024); + test::sprintf_(buffer, "%.20d", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%.20d", -1024); + test::sprintf_(buffer, "%.20d", -1024); CHECK(!strcmp(buffer, "-00000000000000001024")); - test::sprintf(buffer, "%.20i", 1024); + test::sprintf_(buffer, "%.20i", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%.20i", -1024); + test::sprintf_(buffer, "%.20i", -1024); CHECK(!strcmp(buffer, "-00000000000000001024")); - test::sprintf(buffer, "%.20u", 1024); + test::sprintf_(buffer, "%.20u", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%.20u", 4294966272U); + test::sprintf_(buffer, "%.20u", 4294966272U); CHECK(!strcmp(buffer, "00000000004294966272")); - test::sprintf(buffer, "%.20o", 511); + test::sprintf_(buffer, "%.20o", 511); CHECK(!strcmp(buffer, "00000000000000000777")); - test::sprintf(buffer, "%.20o", 4294966785U); + test::sprintf_(buffer, "%.20o", 4294966785U); CHECK(!strcmp(buffer, "00000000037777777001")); - test::sprintf(buffer, "%.20x", 305441741); + test::sprintf_(buffer, "%.20x", 305441741); CHECK(!strcmp(buffer, "0000000000001234abcd")); - test::sprintf(buffer, "%.20x", 3989525555U); + test::sprintf_(buffer, "%.20x", 3989525555U); CHECK(!strcmp(buffer, "000000000000edcb5433")); - test::sprintf(buffer, "%.20X", 305441741); + test::sprintf_(buffer, "%.20X", 305441741); CHECK(!strcmp(buffer, "0000000000001234ABCD")); - test::sprintf(buffer, "%.20X", 3989525555U); + test::sprintf_(buffer, "%.20X", 3989525555U); CHECK(!strcmp(buffer, "000000000000EDCB5433")); } @@ -922,44 +922,44 @@ TEST_CASE("padding .20", "[]" ) { TEST_CASE("padding #020 - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%#020d", 1024); + test::sprintf_(buffer, "%#020d", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%#020d", -1024); + test::sprintf_(buffer, "%#020d", -1024); CHECK(!strcmp(buffer, "-0000000000000001024")); - test::sprintf(buffer, "%#020i", 1024); + test::sprintf_(buffer, "%#020i", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%#020i", -1024); + test::sprintf_(buffer, "%#020i", -1024); CHECK(!strcmp(buffer, "-0000000000000001024")); - test::sprintf(buffer, "%#020u", 1024); + test::sprintf_(buffer, "%#020u", 1024); CHECK(!strcmp(buffer, "00000000000000001024")); - test::sprintf(buffer, "%#020u", 4294966272U); + test::sprintf_(buffer, "%#020u", 4294966272U); CHECK(!strcmp(buffer, "00000000004294966272")); } #endif TEST_CASE("padding #020", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%#020o", 511); + test::sprintf_(buffer, "%#020o", 511); CHECK(!strcmp(buffer, "00000000000000000777")); - test::sprintf(buffer, "%#020o", 4294966785U); + test::sprintf_(buffer, "%#020o", 4294966785U); CHECK(!strcmp(buffer, "00000000037777777001")); - test::sprintf(buffer, "%#020x", 305441741); + test::sprintf_(buffer, "%#020x", 305441741); CHECK(!strcmp(buffer, "0x00000000001234abcd")); - test::sprintf(buffer, "%#020x", 3989525555U); + test::sprintf_(buffer, "%#020x", 3989525555U); CHECK(!strcmp(buffer, "0x0000000000edcb5433")); - test::sprintf(buffer, "%#020X", 305441741); + test::sprintf_(buffer, "%#020X", 305441741); CHECK(!strcmp(buffer, "0X00000000001234ABCD")); - test::sprintf(buffer, "%#020X", 3989525555U); + test::sprintf_(buffer, "%#020X", 3989525555U); CHECK(!strcmp(buffer, "0X0000000000EDCB5433")); } @@ -968,22 +968,22 @@ TEST_CASE("padding #020", "[]" ) { TEST_CASE("padding #20 - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%#20d", 1024); + test::sprintf_(buffer, "%#20d", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%#20d", -1024); + test::sprintf_(buffer, "%#20d", -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%#20i", 1024); + test::sprintf_(buffer, "%#20i", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%#20i", -1024); + test::sprintf_(buffer, "%#20i", -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%#20u", 1024); + test::sprintf_(buffer, "%#20u", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%#20u", 4294966272U); + test::sprintf_(buffer, "%#20u", 4294966272U); CHECK(!strcmp(buffer, " 4294966272")); } #endif @@ -991,22 +991,22 @@ TEST_CASE("padding #20 - non-standard format", "[]" ) { TEST_CASE("padding #20", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%#20o", 511); + test::sprintf_(buffer, "%#20o", 511); CHECK(!strcmp(buffer, " 0777")); - test::sprintf(buffer, "%#20o", 4294966785U); + test::sprintf_(buffer, "%#20o", 4294966785U); CHECK(!strcmp(buffer, " 037777777001")); - test::sprintf(buffer, "%#20x", 305441741); + test::sprintf_(buffer, "%#20x", 305441741); CHECK(!strcmp(buffer, " 0x1234abcd")); - test::sprintf(buffer, "%#20x", 3989525555U); + test::sprintf_(buffer, "%#20x", 3989525555U); CHECK(!strcmp(buffer, " 0xedcb5433")); - test::sprintf(buffer, "%#20X", 305441741); + test::sprintf_(buffer, "%#20X", 305441741); CHECK(!strcmp(buffer, " 0X1234ABCD")); - test::sprintf(buffer, "%#20X", 3989525555U); + test::sprintf_(buffer, "%#20X", 3989525555U); CHECK(!strcmp(buffer, " 0XEDCB5433")); } @@ -1014,40 +1014,40 @@ TEST_CASE("padding #20", "[]" ) { TEST_CASE("padding 20.5", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%20.5d", 1024); + test::sprintf_(buffer, "%20.5d", 1024); CHECK(!strcmp(buffer, " 01024")); - test::sprintf(buffer, "%20.5d", -1024); + test::sprintf_(buffer, "%20.5d", -1024); CHECK(!strcmp(buffer, " -01024")); - test::sprintf(buffer, "%20.5i", 1024); + test::sprintf_(buffer, "%20.5i", 1024); CHECK(!strcmp(buffer, " 01024")); - test::sprintf(buffer, "%20.5i", -1024); + test::sprintf_(buffer, "%20.5i", -1024); CHECK(!strcmp(buffer, " -01024")); - test::sprintf(buffer, "%20.5u", 1024); + test::sprintf_(buffer, "%20.5u", 1024); CHECK(!strcmp(buffer, " 01024")); - test::sprintf(buffer, "%20.5u", 4294966272U); + test::sprintf_(buffer, "%20.5u", 4294966272U); CHECK(!strcmp(buffer, " 4294966272")); - test::sprintf(buffer, "%20.5o", 511); + test::sprintf_(buffer, "%20.5o", 511); CHECK(!strcmp(buffer, " 00777")); - test::sprintf(buffer, "%20.5o", 4294966785U); + test::sprintf_(buffer, "%20.5o", 4294966785U); CHECK(!strcmp(buffer, " 37777777001")); - test::sprintf(buffer, "%20.5x", 305441741); + test::sprintf_(buffer, "%20.5x", 305441741); CHECK(!strcmp(buffer, " 1234abcd")); - test::sprintf(buffer, "%20.10x", 3989525555U); + test::sprintf_(buffer, "%20.10x", 3989525555U); CHECK(!strcmp(buffer, " 00edcb5433")); - test::sprintf(buffer, "%20.5X", 305441741); + test::sprintf_(buffer, "%20.5X", 305441741); CHECK(!strcmp(buffer, " 1234ABCD")); - test::sprintf(buffer, "%20.10X", 3989525555U); + test::sprintf_(buffer, "%20.10X", 3989525555U); CHECK(!strcmp(buffer, " 00EDCB5433")); } @@ -1056,29 +1056,29 @@ TEST_CASE("padding neg numbers", "[]" ) { char buffer[100]; // space padding - test::sprintf(buffer, "% 1d", -5); + test::sprintf_(buffer, "% 1d", -5); CHECK(!strcmp(buffer, "-5")); - test::sprintf(buffer, "% 2d", -5); + test::sprintf_(buffer, "% 2d", -5); CHECK(!strcmp(buffer, "-5")); - test::sprintf(buffer, "% 3d", -5); + test::sprintf_(buffer, "% 3d", -5); CHECK(!strcmp(buffer, " -5")); - test::sprintf(buffer, "% 4d", -5); + test::sprintf_(buffer, "% 4d", -5); CHECK(!strcmp(buffer, " -5")); // zero padding - test::sprintf(buffer, "%01d", -5); + test::sprintf_(buffer, "%01d", -5); CHECK(!strcmp(buffer, "-5")); - test::sprintf(buffer, "%02d", -5); + test::sprintf_(buffer, "%02d", -5); CHECK(!strcmp(buffer, "-5")); - test::sprintf(buffer, "%03d", -5); + test::sprintf_(buffer, "%03d", -5); CHECK(!strcmp(buffer, "-05")); - test::sprintf(buffer, "%04d", -5); + test::sprintf_(buffer, "%04d", -5); CHECK(!strcmp(buffer, "-005")); } @@ -1087,54 +1087,54 @@ TEST_CASE("float padding neg numbers", "[]" ) { char buffer[100]; // space padding - test::sprintf(buffer, "% 3.1f", -5.); + test::sprintf_(buffer, "% 3.1f", -5.); CHECK(!strcmp(buffer, "-5.0")); - test::sprintf(buffer, "% 4.1f", -5.); + test::sprintf_(buffer, "% 4.1f", -5.); CHECK(!strcmp(buffer, "-5.0")); - test::sprintf(buffer, "% 5.1f", -5.); + test::sprintf_(buffer, "% 5.1f", -5.); CHECK(!strcmp(buffer, " -5.0")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "% 6.1g", -5.); + test::sprintf_(buffer, "% 6.1g", -5.); CHECK(!strcmp(buffer, " -5")); - test::sprintf(buffer, "% 6.1e", -5.); + test::sprintf_(buffer, "% 6.1e", -5.); CHECK(!strcmp(buffer, "-5.0e+00")); - test::sprintf(buffer, "% 10.1e", -5.); + test::sprintf_(buffer, "% 10.1e", -5.); CHECK(!strcmp(buffer, " -5.0e+00")); #endif // zero padding - test::sprintf(buffer, "%03.1f", -5.); + test::sprintf_(buffer, "%03.1f", -5.); CHECK(!strcmp(buffer, "-5.0")); - test::sprintf(buffer, "%04.1f", -5.); + test::sprintf_(buffer, "%04.1f", -5.); CHECK(!strcmp(buffer, "-5.0")); - test::sprintf(buffer, "%05.1f", -5.); + test::sprintf_(buffer, "%05.1f", -5.); CHECK(!strcmp(buffer, "-05.0")); // zero padding no decimal point - test::sprintf(buffer, "%01.0f", -5.); + test::sprintf_(buffer, "%01.0f", -5.); CHECK(!strcmp(buffer, "-5")); - test::sprintf(buffer, "%02.0f", -5.); + test::sprintf_(buffer, "%02.0f", -5.); CHECK(!strcmp(buffer, "-5")); - test::sprintf(buffer, "%03.0f", -5.); + test::sprintf_(buffer, "%03.0f", -5.); CHECK(!strcmp(buffer, "-05")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%010.1e", -5.); + test::sprintf_(buffer, "%010.1e", -5.); CHECK(!strcmp(buffer, "-005.0e+00")); - test::sprintf(buffer, "%07.0E", -5.); + test::sprintf_(buffer, "%07.0E", -5.); CHECK(!strcmp(buffer, "-05E+00")); - test::sprintf(buffer, "%03.0g", -5.); + test::sprintf_(buffer, "%03.0g", -5.); CHECK(!strcmp(buffer, "-05")); #endif } @@ -1142,76 +1142,76 @@ TEST_CASE("float padding neg numbers", "[]" ) { TEST_CASE("length", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%.0s", "Hello testing"); + test::sprintf_(buffer, "%.0s", "Hello testing"); CHECK(!strcmp(buffer, "")); - test::sprintf(buffer, "%20.0s", "Hello testing"); + test::sprintf_(buffer, "%20.0s", "Hello testing"); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%.s", "Hello testing"); + test::sprintf_(buffer, "%.s", "Hello testing"); CHECK(!strcmp(buffer, "")); - test::sprintf(buffer, "%20.s", "Hello testing"); + test::sprintf_(buffer, "%20.s", "Hello testing"); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%20.0d", 1024); + test::sprintf_(buffer, "%20.0d", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%20.0d", -1024); + test::sprintf_(buffer, "%20.0d", -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%20.d", 0); + test::sprintf_(buffer, "%20.d", 0); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%20.0i", 1024); + test::sprintf_(buffer, "%20.0i", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%20.i", -1024); + test::sprintf_(buffer, "%20.i", -1024); CHECK(!strcmp(buffer, " -1024")); - test::sprintf(buffer, "%20.i", 0); + test::sprintf_(buffer, "%20.i", 0); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%20.u", 1024); + test::sprintf_(buffer, "%20.u", 1024); CHECK(!strcmp(buffer, " 1024")); - test::sprintf(buffer, "%20.0u", 4294966272U); + test::sprintf_(buffer, "%20.0u", 4294966272U); CHECK(!strcmp(buffer, " 4294966272")); - test::sprintf(buffer, "%20.u", 0U); + test::sprintf_(buffer, "%20.u", 0U); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%20.o", 511); + test::sprintf_(buffer, "%20.o", 511); CHECK(!strcmp(buffer, " 777")); - test::sprintf(buffer, "%20.0o", 4294966785U); + test::sprintf_(buffer, "%20.0o", 4294966785U); CHECK(!strcmp(buffer, " 37777777001")); - test::sprintf(buffer, "%20.o", 0U); + test::sprintf_(buffer, "%20.o", 0U); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%20.x", 305441741); + test::sprintf_(buffer, "%20.x", 305441741); CHECK(!strcmp(buffer, " 1234abcd")); - test::sprintf(buffer, "%50.x", 305441741); + test::sprintf_(buffer, "%50.x", 305441741); CHECK(!strcmp(buffer, " 1234abcd")); - test::sprintf(buffer, "%50.x%10.u", 305441741, 12345); + test::sprintf_(buffer, "%50.x%10.u", 305441741, 12345); CHECK(!strcmp(buffer, " 1234abcd 12345")); - test::sprintf(buffer, "%20.0x", 3989525555U); + test::sprintf_(buffer, "%20.0x", 3989525555U); CHECK(!strcmp(buffer, " edcb5433")); - test::sprintf(buffer, "%20.x", 0U); + test::sprintf_(buffer, "%20.x", 0U); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%20.X", 305441741); + test::sprintf_(buffer, "%20.X", 305441741); CHECK(!strcmp(buffer, " 1234ABCD")); - test::sprintf(buffer, "%20.0X", 3989525555U); + test::sprintf_(buffer, "%20.0X", 3989525555U); CHECK(!strcmp(buffer, " EDCB5433")); - test::sprintf(buffer, "%20.X", 0U); + test::sprintf_(buffer, "%20.X", 0U); CHECK(!strcmp(buffer, " ")); } @@ -1219,10 +1219,10 @@ TEST_CASE("length", "[]" ) { TEST_CASE("length - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%02.0u", 0U); + test::sprintf_(buffer, "%02.0u", 0U); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%02.0d", 0); + test::sprintf_(buffer, "%02.0d", 0); CHECK(!strcmp(buffer, " ")); } #endif @@ -1232,29 +1232,29 @@ TEST_CASE("float", "[]" ) { char buffer[100]; // test special-case floats using math.h macros - test::sprintf(buffer, "%8f", (double) NAN); + test::sprintf_(buffer, "%8f", (double) NAN); CHECK(!strcmp(buffer, " nan")); - test::sprintf(buffer, "%8f", (double) INFINITY); + test::sprintf_(buffer, "%8f", (double) INFINITY); CHECK(!strcmp(buffer, " inf")); - test::sprintf(buffer, "%-8f", (double) -INFINITY); + test::sprintf_(buffer, "%-8f", (double) -INFINITY); CHECK(!strcmp(buffer, "-inf ")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%+8e", (double) INFINITY); + test::sprintf_(buffer, "%+8e", (double) INFINITY); CHECK(!strcmp(buffer, " +inf")); #endif - test::sprintf(buffer, "%.4f", 3.1415354); + test::sprintf_(buffer, "%.4f", 3.1415354); CHECK(!strcmp(buffer, "3.1415")); - test::sprintf(buffer, "%.3f", 30343.1415354); + test::sprintf_(buffer, "%.3f", 30343.1415354); CHECK(!strcmp(buffer, "30343.142")); // switch from decimal to exponential representation // - test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 ) ); + test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 ) ); if (PRINTF_MAX_FLOAT < 10e+2) { CHECK(!strcmp(buffer, "10e+2")); } @@ -1262,7 +1262,7 @@ TEST_CASE("float", "[]" ) { CHECK(!strcmp(buffer, "1000")); } - test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 ) ); + test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 ) ); if (PRINTF_MAX_FLOAT < 10e+5) { CHECK(!strcmp(buffer, "10e+5")); } @@ -1270,7 +1270,7 @@ TEST_CASE("float", "[]" ) { CHECK(!strcmp(buffer, "1000000")); } - test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 ) ); + test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 ) ); if (PRINTF_MAX_FLOAT < 10e+8) { CHECK(!strcmp(buffer, "10e+8")); } @@ -1278,7 +1278,7 @@ TEST_CASE("float", "[]" ) { CHECK(!strcmp(buffer, "1000000000")); } - test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000) ); + test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000) ); if (PRINTF_MAX_FLOAT < 10e+11) { CHECK(!strcmp(buffer, "10e+11")); } @@ -1286,7 +1286,7 @@ TEST_CASE("float", "[]" ) { CHECK(!strcmp(buffer, "1000000000000")); } - test::sprintf(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000 * 1000) ); + test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000 * 1000) ); if (PRINTF_MAX_FLOAT < 10e+14) { CHECK(!strcmp(buffer, "10e+14")); } @@ -1294,116 +1294,116 @@ TEST_CASE("float", "[]" ) { CHECK(!strcmp(buffer, "1000000000000000")); } - test::sprintf(buffer, "%.0f", 34.1415354); + test::sprintf_(buffer, "%.0f", 34.1415354); CHECK(!strcmp(buffer, "34")); - test::sprintf(buffer, "%.0f", 1.3); + test::sprintf_(buffer, "%.0f", 1.3); CHECK(!strcmp(buffer, "1")); - test::sprintf(buffer, "%.0f", 1.55); + test::sprintf_(buffer, "%.0f", 1.55); CHECK(!strcmp(buffer, "2")); - test::sprintf(buffer, "%.1f", 1.64); + test::sprintf_(buffer, "%.1f", 1.64); CHECK(!strcmp(buffer, "1.6")); - test::sprintf(buffer, "%.2f", 42.8952); + test::sprintf_(buffer, "%.2f", 42.8952); CHECK(!strcmp(buffer, "42.90")); - test::sprintf(buffer, "%.9f", 42.8952); + test::sprintf_(buffer, "%.9f", 42.8952); CHECK(!strcmp(buffer, "42.895200000")); - test::sprintf(buffer, "%.10f", 42.895223); + test::sprintf_(buffer, "%.10f", 42.895223); CHECK(!strcmp(buffer, "42.8952230000")); // this testcase checks, that the precision is truncated to 9 digits. // a perfect working float should return the whole number - test::sprintf(buffer, "%.12f", 42.89522312345678); + test::sprintf_(buffer, "%.12f", 42.89522312345678); CHECK(!strcmp(buffer, "42.895223123000")); // this testcase checks, that the precision is truncated AND rounded to 9 digits. // a perfect working float should return the whole number - test::sprintf(buffer, "%.12f", 42.89522387654321); + test::sprintf_(buffer, "%.12f", 42.89522387654321); CHECK(!strcmp(buffer, "42.895223877000")); - test::sprintf(buffer, "%6.2f", 42.8952); + test::sprintf_(buffer, "%6.2f", 42.8952); CHECK(!strcmp(buffer, " 42.90")); - test::sprintf(buffer, "%+6.2f", 42.8952); + test::sprintf_(buffer, "%+6.2f", 42.8952); CHECK(!strcmp(buffer, "+42.90")); - test::sprintf(buffer, "%+5.1f", 42.9252); + test::sprintf_(buffer, "%+5.1f", 42.9252); CHECK(!strcmp(buffer, "+42.9")); - test::sprintf(buffer, "%f", 42.5); + test::sprintf_(buffer, "%f", 42.5); CHECK(!strcmp(buffer, "42.500000")); - test::sprintf(buffer, "%.1f", 42.5); + test::sprintf_(buffer, "%.1f", 42.5); CHECK(!strcmp(buffer, "42.5")); - test::sprintf(buffer, "%f", 42167.0); + test::sprintf_(buffer, "%f", 42167.0); CHECK(!strcmp(buffer, "42167.000000")); - test::sprintf(buffer, "%.9f", -12345.987654321); + test::sprintf_(buffer, "%.9f", -12345.987654321); CHECK(!strcmp(buffer, "-12345.987654321")); - test::sprintf(buffer, "%.1f", 3.999); + test::sprintf_(buffer, "%.1f", 3.999); CHECK(!strcmp(buffer, "4.0")); - test::sprintf(buffer, "%.0f", 3.5); + test::sprintf_(buffer, "%.0f", 3.5); CHECK(!strcmp(buffer, "4")); - test::sprintf(buffer, "%.0f", 4.5); + test::sprintf_(buffer, "%.0f", 4.5); CHECK(!strcmp(buffer, "4")); - test::sprintf(buffer, "%.0f", 3.49); + test::sprintf_(buffer, "%.0f", 3.49); CHECK(!strcmp(buffer, "3")); - test::sprintf(buffer, "%.1f", 3.49); + test::sprintf_(buffer, "%.1f", 3.49); CHECK(!strcmp(buffer, "3.5")); - test::sprintf(buffer, "a%-5.1f", 0.5); + test::sprintf_(buffer, "a%-5.1f", 0.5); CHECK(!strcmp(buffer, "a0.5 ")); - test::sprintf(buffer, "a%-5.1fend", 0.5); + test::sprintf_(buffer, "a%-5.1fend", 0.5); CHECK(!strcmp(buffer, "a0.5 end")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL test::sprintf(buffer, "%.4g", 1.0); CHECK(!strcmp(buffer, "1")); - test::sprintf(buffer, "%G", 12345.678); + test::sprintf_(buffer, "%G", 12345.678); CHECK(!strcmp(buffer, "12345.7")); - test::sprintf(buffer, "%.7G", 12345.678); + test::sprintf_(buffer, "%.7G", 12345.678); CHECK(!strcmp(buffer, "12345.68")); - test::sprintf(buffer, "%.5G", 123456789.); + test::sprintf_(buffer, "%.5G", 123456789.); CHECK(!strcmp(buffer, "1.2346E+08")); - test::sprintf(buffer, "%.6G", 12345.); + test::sprintf_(buffer, "%.6G", 12345.); CHECK(!strcmp(buffer, "12345.0")); - test::sprintf(buffer, "%+12.4g", 123456789.); + test::sprintf_(buffer, "%+12.4g", 123456789.); CHECK(!strcmp(buffer, " +1.235e+08")); - test::sprintf(buffer, "%.2G", 0.001234); + test::sprintf_(buffer, "%.2G", 0.001234); CHECK(!strcmp(buffer, "0.0012")); - test::sprintf(buffer, "%+10.4G", 0.001234); + test::sprintf_(buffer, "%+10.4G", 0.001234); CHECK(!strcmp(buffer, " +0.001234")); - test::sprintf(buffer, "%+012.4g", 0.00001234); + test::sprintf_(buffer, "%+012.4g", 0.00001234); CHECK(!strcmp(buffer, "+001.234e-05")); - test::sprintf(buffer, "%.3g", -1.2345e-308); + test::sprintf_(buffer, "%.3g", -1.2345e-308); CHECK(!strcmp(buffer, "-1.23e-308")); - test::sprintf(buffer, "%+.3E", 1.23e+308); + test::sprintf_(buffer, "%+.3E", 1.23e+308); CHECK(!strcmp(buffer, "+1.230E+308")); #endif // out of range for float: should switch to exp notation if supported, else empty - test::sprintf(buffer, "%.1f", 1E20); + test::sprintf_(buffer, "%.1f", 1E20); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL CHECK(!strcmp(buffer, "1.0e+20")); #else @@ -1415,7 +1415,7 @@ TEST_CASE("float", "[]" ) { std::stringstream str; str.precision(5); for (float i = -100000; i < 100000; i += 1) { - test::sprintf(buffer, "%.5f", (double)(i / 10000)); + test::sprintf_(buffer, "%.5f", (double)(i / 10000)); str.str(""); str << std::fixed << i / 10000; fail = fail || !!strcmp(buffer, str.str().c_str()); @@ -1427,7 +1427,7 @@ TEST_CASE("float", "[]" ) { // brute force exp str.setf(std::ios::scientific, std::ios::floatfield); for (float i = -1e20; i < (float) 1e20; i += (float) 1e15) { - test::sprintf(buffer, "%.5f", (double) i); + test::sprintf_(buffer, "%.5f", (double) i); str.str(""); str << i; fail = fail || !!strcmp(buffer, str.str().c_str()); @@ -1440,97 +1440,97 @@ TEST_CASE("float", "[]" ) { TEST_CASE("types", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%i", 0); + test::sprintf_(buffer, "%i", 0); CHECK(!strcmp(buffer, "0")); - test::sprintf(buffer, "%i", 1234); + test::sprintf_(buffer, "%i", 1234); CHECK(!strcmp(buffer, "1234")); - test::sprintf(buffer, "%i", 32767); + test::sprintf_(buffer, "%i", 32767); CHECK(!strcmp(buffer, "32767")); - test::sprintf(buffer, "%i", -32767); + test::sprintf_(buffer, "%i", -32767); CHECK(!strcmp(buffer, "-32767")); - test::sprintf(buffer, "%li", 30L); + test::sprintf_(buffer, "%li", 30L); CHECK(!strcmp(buffer, "30")); - test::sprintf(buffer, "%li", -2147483647L); + test::sprintf_(buffer, "%li", -2147483647L); CHECK(!strcmp(buffer, "-2147483647")); - test::sprintf(buffer, "%li", 2147483647L); + test::sprintf_(buffer, "%li", 2147483647L); CHECK(!strcmp(buffer, "2147483647")); - test::sprintf(buffer, "%lli", 30LL); + test::sprintf_(buffer, "%lli", 30LL); CHECK(!strcmp(buffer, "30")); - test::sprintf(buffer, "%lli", -9223372036854775807LL); + test::sprintf_(buffer, "%lli", -9223372036854775807LL); CHECK(!strcmp(buffer, "-9223372036854775807")); - test::sprintf(buffer, "%lli", 9223372036854775807LL); + test::sprintf_(buffer, "%lli", 9223372036854775807LL); CHECK(!strcmp(buffer, "9223372036854775807")); - test::sprintf(buffer, "%lu", 100000L); + test::sprintf_(buffer, "%lu", 100000L); CHECK(!strcmp(buffer, "100000")); - test::sprintf(buffer, "%lu", 0xFFFFFFFFL); + test::sprintf_(buffer, "%lu", 0xFFFFFFFFL); CHECK(!strcmp(buffer, "4294967295")); - test::sprintf(buffer, "%llu", 281474976710656LLU); + test::sprintf_(buffer, "%llu", 281474976710656LLU); CHECK(!strcmp(buffer, "281474976710656")); - test::sprintf(buffer, "%llu", 18446744073709551615LLU); + test::sprintf_(buffer, "%llu", 18446744073709551615LLU); CHECK(!strcmp(buffer, "18446744073709551615")); - test::sprintf(buffer, "%zu", (size_t)2147483647UL); + test::sprintf_(buffer, "%zu", (size_t)2147483647UL); CHECK(!strcmp(buffer, "2147483647")); - test::sprintf(buffer, "%zd", (size_t)2147483647UL); + test::sprintf_(buffer, "%zd", (size_t)2147483647UL); CHECK(!strcmp(buffer, "2147483647")); - test::sprintf(buffer, "%zi", (ssize_t)-2147483647L); + test::sprintf_(buffer, "%zi", (ssize_t)-2147483647L); CHECK(!strcmp(buffer, "-2147483647")); - test::sprintf(buffer, "%o", 60000); + test::sprintf_(buffer, "%o", 60000); CHECK(!strcmp(buffer, "165140")); - test::sprintf(buffer, "%lo", 12345678L); + test::sprintf_(buffer, "%lo", 12345678L); CHECK(!strcmp(buffer, "57060516")); - test::sprintf(buffer, "%lx", 0x12345678L); + test::sprintf_(buffer, "%lx", 0x12345678L); CHECK(!strcmp(buffer, "12345678")); - test::sprintf(buffer, "%llx", 0x1234567891234567LLU); + test::sprintf_(buffer, "%llx", 0x1234567891234567LLU); CHECK(!strcmp(buffer, "1234567891234567")); - test::sprintf(buffer, "%lx", 0xabcdefabL); + test::sprintf_(buffer, "%lx", 0xabcdefabL); CHECK(!strcmp(buffer, "abcdefab")); - test::sprintf(buffer, "%lX", 0xabcdefabL); + test::sprintf_(buffer, "%lX", 0xabcdefabL); CHECK(!strcmp(buffer, "ABCDEFAB")); - test::sprintf(buffer, "%c", 'v'); + test::sprintf_(buffer, "%c", 'v'); CHECK(!strcmp(buffer, "v")); - test::sprintf(buffer, "%cv", 'w'); + test::sprintf_(buffer, "%cv", 'w'); CHECK(!strcmp(buffer, "wv")); - test::sprintf(buffer, "%s", "A Test"); + test::sprintf_(buffer, "%s", "A Test"); CHECK(!strcmp(buffer, "A Test")); - test::sprintf(buffer, "%hhu", (unsigned char) 0xFFU); + test::sprintf_(buffer, "%hhu", (unsigned char) 0xFFU); CHECK(!strcmp(buffer, "255")); - test::sprintf(buffer, "%hu", (unsigned short) 0x1234u); + test::sprintf_(buffer, "%hu", (unsigned short) 0x1234u); CHECK(!strcmp(buffer, "4660")); - test::sprintf(buffer, "%s%hhi %hu", "Test", (char) 100, (unsigned short) 0xFFFF); + test::sprintf_(buffer, "%s%hhi %hu", "Test", (char) 100, (unsigned short) 0xFFFF); CHECK(!strcmp(buffer, "Test100 65535")); - test::sprintf(buffer, "%tx", &buffer[10] - &buffer[0]); + test::sprintf_(buffer, "%tx", &buffer[10] - &buffer[0]); CHECK(!strcmp(buffer, "a")); - test::sprintf(buffer, "%ji", (intmax_t)-2147483647L); + test::sprintf_(buffer, "%ji", (intmax_t)-2147483647L); CHECK(!strcmp(buffer, "-2147483647")); } @@ -1538,10 +1538,10 @@ TEST_CASE("types", "[]" ) { TEST_CASE("types - non-standard format", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%b", 60000); + test::sprintf_(buffer, "%b", 60000); CHECK(!strcmp(buffer, "1110101001100000")); - test::sprintf(buffer, "%lb", 12345678L); + test::sprintf_(buffer, "%lb", 12345678L); CHECK(!strcmp(buffer, "101111000110000101001110")); } #endif @@ -1549,7 +1549,7 @@ TEST_CASE("types - non-standard format", "[]" ) { TEST_CASE("pointer", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%p", (void*)0x1234U); + test::sprintf_(buffer, "%p", (void*)0x1234U); if (sizeof(void*) == 4U) { CHECK(!strcmp(buffer, "0x00001234")); } @@ -1557,7 +1557,7 @@ TEST_CASE("pointer", "[]" ) { CHECK(!strcmp(buffer, "0x0000000000001234")); } - test::sprintf(buffer, "%p", (void*)0x12345678U); + test::sprintf_(buffer, "%p", (void*)0x12345678U); if (sizeof(void*) == 4U) { CHECK(!strcmp(buffer, "0x12345678")); } @@ -1565,7 +1565,7 @@ TEST_CASE("pointer", "[]" ) { CHECK(!strcmp(buffer, "0x0000000012345678")); } - test::sprintf(buffer, "%p-%p", (void*)0x12345678U, (void*)0x7EDCBA98U); + test::sprintf_(buffer, "%p-%p", (void*)0x12345678U, (void*)0x7EDCBA98U); if (sizeof(void*) == 4U) { CHECK(!strcmp(buffer, "0x12345678-0x7edcba98")); } @@ -1574,15 +1574,15 @@ TEST_CASE("pointer", "[]" ) { } if (sizeof(uintptr_t) == sizeof(uint64_t)) { - test::sprintf(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); + test::sprintf_(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); CHECK(!strcmp(buffer, "0x00000000ffffffff")); } else { - test::sprintf(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); + test::sprintf_(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); CHECK(!strcmp(buffer, "0xffffffff")); } - test::sprintf(buffer, "%p", NULL); + test::sprintf_(buffer, "%p", NULL); CHECK(!strcmp(buffer, "(nil)")); } @@ -1590,7 +1590,7 @@ TEST_CASE("pointer", "[]" ) { TEST_CASE("unknown flag (non-standard format)", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%kmarco", 42, 37); + test::sprintf_(buffer, "%kmarco", 42, 37); CHECK(!strcmp(buffer, "kmarco")); } #endif @@ -1598,27 +1598,27 @@ TEST_CASE("unknown flag (non-standard format)", "[]" ) { TEST_CASE("string length", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%.4s", "This is a test"); + test::sprintf_(buffer, "%.4s", "This is a test"); CHECK(!strcmp(buffer, "This")); - test::sprintf(buffer, "%.4s", "test"); + test::sprintf_(buffer, "%.4s", "test"); CHECK(!strcmp(buffer, "test")); - test::sprintf(buffer, "%.7s", "123"); + test::sprintf_(buffer, "%.7s", "123"); CHECK(!strcmp(buffer, "123")); - test::sprintf(buffer, "%.7s", ""); + test::sprintf_(buffer, "%.7s", ""); CHECK(!strcmp(buffer, "")); - test::sprintf(buffer, "%.4s%.2s", "123456", "abcdef"); + test::sprintf_(buffer, "%.4s%.2s", "123456", "abcdef"); CHECK(!strcmp(buffer, "1234ab")); - test::sprintf(buffer, "%.*s", 3, "123456"); + test::sprintf_(buffer, "%.*s", 3, "123456"); CHECK(!strcmp(buffer, "123")); DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW - test::sprintf(buffer, "%.*s", 3, NULL); + test::sprintf_(buffer, "%.*s", 3, NULL); DISABLE_WARNING_POP CHECK(!strcmp(buffer, "(null)")); } @@ -1627,7 +1627,7 @@ DISABLE_WARNING_POP TEST_CASE("string length (non-standard format)", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%.4.2s", "123456"); + test::sprintf_(buffer, "%.4.2s", "123456"); CHECK(!strcmp(buffer, ".2s")); } #endif @@ -1637,26 +1637,26 @@ TEST_CASE("buffer length", "[]" ) { char buffer[100]; int ret; - ret = test::snprintf(nullptr, 10, "%s", "Test"); + ret = test::snprintf_(nullptr, 10, "%s", "Test"); CHECK(ret == 4); - ret = test::snprintf(nullptr, 0, "%s", "Test"); + ret = test::snprintf_(nullptr, 0, "%s", "Test"); CHECK(ret == 4); buffer[0] = (char)0xA5; - ret = test::snprintf(buffer, 0, "%s", "Test"); + ret = test::snprintf_(buffer, 0, "%s", "Test"); CHECK(buffer[0] == (char)0xA5); CHECK(ret == 4); buffer[0] = (char)0xCC; - test::snprintf(buffer, 1, "%s", "Test"); + test::snprintf_(buffer, 1, "%s", "Test"); CHECK(buffer[0] == '\0'); - test::snprintf(buffer, 2, "%s", "Hello"); + test::snprintf_(buffer, 2, "%s", "Hello"); CHECK(!strcmp(buffer, "H")); DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW - test::snprintf(buffer, 2, "%s", NULL); + test::snprintf_(buffer, 2, "%s", NULL); DISABLE_WARNING_POP CHECK(!strcmp(buffer, "(")); } @@ -1666,29 +1666,29 @@ TEST_CASE("ret value", "[]" ) { char buffer[100] ; int ret; - ret = test::snprintf(buffer, 6, "0%s", "1234"); + ret = test::snprintf_(buffer, 6, "0%s", "1234"); CHECK(!strcmp(buffer, "01234")); CHECK(ret == 5); - ret = test::snprintf(buffer, 6, "0%s", "12345"); + ret = test::snprintf_(buffer, 6, "0%s", "12345"); CHECK(!strcmp(buffer, "01234")); CHECK(ret == 6); // "5" is truncated - ret = test::snprintf(buffer, 6, "0%s", "1234567"); + ret = test::snprintf_(buffer, 6, "0%s", "1234567"); CHECK(!strcmp(buffer, "01234")); CHECK(ret == 8); // "567" are truncated DISABLE_WARNING_PUSH DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW - ret = test::snprintf(buffer, 6, "0%s", NULL); + ret = test::snprintf_(buffer, 6, "0%s", NULL); DISABLE_WARNING_POP CHECK(!strcmp(buffer, "0(nul")); CHECK(ret == 7); // "l)" is truncated - ret = test::snprintf(buffer, 10, "hello, world"); + ret = test::snprintf_(buffer, 10, "hello, world"); CHECK(ret == 12); - ret = test::snprintf(buffer, 3, "%d", 10000); + ret = test::snprintf_(buffer, 3, "%d", 10000); CHECK(ret == 5); CHECK(strlen(buffer) == 2U); CHECK(buffer[0] == '1'); @@ -1700,32 +1700,32 @@ DISABLE_WARNING_POP TEST_CASE("misc", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"); + test::sprintf_(buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"); CHECK(!strcmp(buffer, "53000atest-20 bit")); - test::sprintf(buffer, "%.*f", 2, 0.33333333); + test::sprintf_(buffer, "%.*f", 2, 0.33333333); CHECK(!strcmp(buffer, "0.33")); - test::sprintf(buffer, "%.*d", -1, 1); + test::sprintf_(buffer, "%.*d", -1, 1); CHECK(!strcmp(buffer, "1")); - test::sprintf(buffer, "%.3s", "foobar"); + test::sprintf_(buffer, "%.3s", "foobar"); CHECK(!strcmp(buffer, "foo")); - test::sprintf(buffer, "% .0d", 0); + test::sprintf_(buffer, "% .0d", 0); CHECK(!strcmp(buffer, " ")); - test::sprintf(buffer, "%10.5d", 4); + test::sprintf_(buffer, "%10.5d", 4); CHECK(!strcmp(buffer, " 00004")); - test::sprintf(buffer, "%*sx", -3, "hi"); + test::sprintf_(buffer, "%*sx", -3, "hi"); CHECK(!strcmp(buffer, "hi x")); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%.*g", 2, 0.33333333); + test::sprintf_(buffer, "%.*g", 2, 0.33333333); CHECK(!strcmp(buffer, "0.33")); - test::sprintf(buffer, "%.*e", 2, 0.33333333); + test::sprintf_(buffer, "%.*e", 2, 0.33333333); CHECK(!strcmp(buffer, "3.33e-01")); #endif } From a4a16450ef34e53702dfec894394c5ba71da3403 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Mon, 12 Jul 2021 13:55:43 +0300 Subject: [PATCH 07/31] Adopted a testcase from @esselete's repository. --- test/test_suite.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_suite.cpp b/test/test_suite.cpp index 6acbada9..23b0f31c 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -1613,6 +1613,9 @@ TEST_CASE("string length", "[]" ) { test::sprintf_(buffer, "%.4s%.2s", "123456", "abcdef"); CHECK(!strcmp(buffer, "1234ab")); + test::sprintf_(buffer, "%.4.2s", "123456"); + REQUIRE(!strcmp(buffer, ".2s")); + test::sprintf_(buffer, "%.*s", 3, "123456"); CHECK(!strcmp(buffer, "123")); From 91a04a1317998eb090ccf6beda84587a449e733b Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Wed, 14 Jul 2021 20:51:56 +0300 Subject: [PATCH 08/31] Fixes #17: * Compiler definition name changes * CMake option name changes * Comment changes * Option description changes * Changed some compiler definition semantics from checking definition to checking value --- CMakeLists.txt | 59 +++++++++++++++---------------- printf.c | 84 +++++++++++++++++++++------------------------ test/test_suite.cpp | 60 ++++++++++++++++---------------- 3 files changed, 97 insertions(+), 106 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb24436a..1d11ad85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,48 +9,43 @@ project( option(BUILD_TESTS "Build test programs for the library" OFF) -option(SUPPORT_FLOAT_ARGUMENTS "Support floating-point arguments (%f)" ON) -if (NOT SUPPORT_FLOAT_ARGUMENTS) - add_definitions(-DPRINTF_DISABLE_SUPPORT_FLOAT) +option(SUPPORT_FLOAT_SPECIFIERS "Support decimal notation floating-point conversion specifiers (%f,%F)" ON) +if (NOT SUPPORT_FLOAT_SPECIFIERS) + add_definitions(-DPRINTF_DISABLE_FLOAT_SPECIFIERS) endif() -option(SUPPORT_EXPONENTIAL_FLOAT_ARGUMENTS "Support SUPPORT_EXPONENTIAL_FLOAT_ARGUMENTS (%e/%g)" ON) -if (NOT SUPPORT_EXPONENTIAL_FLOAT_ARGUMENTS) - add_definitions(-DPRINTF_DISABLE_SUPPORT_EXPONENTIAL) +option(SUPPORT_EXPONENTIAL_SPECIFIERS "Support exponential floating point format conversion specifiers (%e,%E,%g,%G)" ON) +if (NOT SUPPORT_EXPONENTIAL_SPECIFIERS) + add_definitions(-DPRINTF_DISABLE_EXPONENTIAL_SPECIFIERS) endif() -option(SUPPORT_LONG_LONG_INTS_AND_PTR "Support long long integers and pointers (%ll, %llu, %p)" ON) -if (NOT SUPPORT_LONG_LONG_INTS_AND_PTR) - add_definitions(-DPRINTF_DISABLE_SUPPORT_LONG_LONG) +option(SUPPORT_LONG_LONG "Support long long integral types (allows for the ll length modifier and affects %p)" ON) +if (NOT SUPPORT_LONG_LONG) + add_definitions(-DPRINTF_DISABLE_LONG_LONG) endif() -option(SUPPORT_PTRDIFF_ARGUMENTS "Support SUPPORT_PTRDIFF_ARGUMENTS (%t)" ON) -if (NOT SUPPORT_PTRDIFF_ARGUMENTS) - add_definitions(-DPRINTF_DISABLE_SUPPORT_PTRDIFF_T) +option(SUPPORT_PTRDIFF_LENGTH_MODIFIER "Support the pointer difference specifier (%t)" ON) +if (NOT SUPPORT_PTRDIFF_LENGTH_MODIFIER) + add_definitions(-DPRINTF_DISABLE_PTRDIFF_LENGTH_MODIFIER) endif() -option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the relevant standard library function (printf, sprintf etc.) to the library's implementations" ON) +option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the standard library function names (printf, sprintf etc.) to the library's functions" ON) if (NOT ALIAS_STANDARD_FUNCTION_NAMES) - add_definitions(-DALIAS_STANDARD_FUNCTION_NAMES) + add_definitions(-DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES) endif() -option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the relevant standard library function (printf, sprintf etc.) to the library's implementations" ON) -if (NOT ALIAS_STANDARD_FUNCTION_NAMES) - add_definitions(-DALIAS_STANDARD_FUNCTION_NAMES) -endif() - -# numeric defines +# Numeric defines -set(INTEGER_TO_STRING_BUFFER_SIZE "32" CACHE STRING "Integer to string (ntoa) conversion buffer size") -string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" PRINTF_NTOA_BUFFER_SIZE "${INTEGER_TO_STRING_BUFFER_SIZE}") -if ("${PRINTF_NTOA_BUFFER_SIZE}" STREQUAL "") +set(NTOA_BUFFER_SIZE "32" CACHE STRING "Integer to string (ntoa) conversion buffer size") +string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" NTOA_BUFFER_SIZE "${NTOA_BUFFER_SIZE}") +if ("${NTOA_BUFFER_SIZE}" STREQUAL "") message(FATAL_ERROR "An (integral) buffer size for converting integers to floating-point values must be specified." ) else() - add_definitions(-DPRINTF_NTOA_BUFFER_SIZE=${PRINTF_NTOA_BUFFER_SIZE}) + add_definitions(-DPRINTF_NTOA_BUFFER_SIZE=${NTOA_BUFFER_SIZE}) endif() -set(FLOAT_TO_STRING_BUFFER_SIZE "32" CACHE STRING "Float to string (ftoa) conversion buffer size") -string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" PRINTF_FTOA_BUFFER_SIZE "${FLOAT_TO_STRING_BUFFER_SIZE}") +set(PRINTF_FTOA_BUFFER_SIZE "32" CACHE STRING "Float to string (ftoa) conversion buffer size") +string(REGEX MATCH "^[0-9]+$" PRINTF_FTOA_BUFFER_SIZE "${PRINTF_FTOA_BUFFER_SIZE}") if ("${PRINTF_FTOA_BUFFER_SIZE}" STREQUAL "") message(FATAL_ERROR "An (integral) buffer size for converting floating-point values to strings must be specified." ) else() @@ -58,19 +53,19 @@ else() endif() set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing floating-point values") -string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" DFP_PARSED "${DEFAULT_FLOAT_PRECISION}") -if ("${DFP_PARSED}" STREQUAL "") +string(REGEX MATCH "^[0-9]+$" DEFAULT_FLOAT_PRECISION "${DEFAULT_FLOAT_PRECISION}") +if ("${DEFAULT_FLOAT_PRECISION}" STREQUAL "") message(FATAL_ERROR "An (integral) default floating-point precision value must be specified." ) else() - add_definitions(-DPRINTF_DEFAULT_FLOAT_PRECISION=${DFP_PARSED}) + add_definitions(-DPRINTF_DEFAULT_FLOAT_PRECISION=${DEFAULT_FLOAT_PRECISION}) endif() set(FLOAT_NOTATION_THRESHOLD "1e9" CACHE STRING "Largest floating-point value for which printing with %f uses non-exponential notation") -string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" PRINTF_MAX_FLOAT "${FLOAT_NOTATION_THRESHOLD}") -if ("${PRINTF_MAX_FLOAT}" STREQUAL "") +string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" FLOAT_NOTATION_THRESHOLD "${FLOAT_NOTATION_THRESHOLD}") +if ("${FLOAT_NOTATION_THRESHOLD}" STREQUAL "") message(FATAL_ERROR "The %f notation switch threshold must be an integer." ) else() - add_definitions(-DPRINTF_MAX_FLOAT=${PRINTF_MAX_FLOAT}) + add_definitions(-DPRINTF_FLOAT_NOTATION_THRESHOLD=${FLOAT_NOTATION_THRESHOLD}) endif() option(BUILD_STATIC_LIBRARY "Build the library as static rather than shared" OFF) diff --git a/printf.c b/printf.c index da81d526..f683a439 100644 --- a/printf.c +++ b/printf.c @@ -36,7 +36,7 @@ #include "printf.h" -// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the // printf_config.h header file // default: undefined #ifdef PRINTF_INCLUDE_CONFIG_H @@ -46,53 +46,49 @@ // 'ntoa' conversion buffer size, this must be big enough to hold one converted // numeric number including padded zeros (dynamically created on stack) -// default: 32 byte #ifndef PRINTF_NTOA_BUFFER_SIZE #define PRINTF_NTOA_BUFFER_SIZE 32U #endif // 'ftoa' conversion buffer size, this must be big enough to hold one converted // float number including padded zeros (dynamically created on stack) -// default: 32 byte #ifndef PRINTF_FTOA_BUFFER_SIZE #define PRINTF_FTOA_BUFFER_SIZE 32U #endif -// support for the floating point type (%f) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_FLOAT -#define PRINTF_SUPPORT_FLOAT +// Support for the decimal notation floating point conversion specifiers (%f, %F) +#ifndef PRINTF_SUPPORT_FLOAT_SPECIFIERS +#define PRINTF_SUPPORT_FLOAT_SPECIFIERS 1 #endif -// support for exponential floating point notation (%e/%g) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL -#define PRINTF_SUPPORT_EXPONENTIAL +// Support for the exponential notatin floating point conversion specifiers (%e, %g, %E, %G) +#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 #endif -// define the default floating point precision -// default: 6 digits +// Default precision for the floating point conversion specifiers (the C standard sets this at 6) #ifndef PRINTF_DEFAULT_FLOAT_PRECISION #define PRINTF_DEFAULT_FLOAT_PRECISION 6U #endif -// define the largest float suitable to print with %f -// default: 1e9 -#ifndef PRINTF_MAX_FLOAT -#define PRINTF_MAX_FLOAT 1e9 +// According to the C languages standard, printf() and related functions must be able to print any +// integral number in floating-point notation, regardless of length, when using the %f specifier - +// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, +// all values beyond this threshold are switched to exponential notation. +#ifndef PRINTF_FLOAT_NOTATION_THRESHOLD +#define PRINTF_FLOAT_NOTATION_THRESHOLD 1e9 #endif -// support for the long long types (%llu or %p) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG -#define PRINTF_SUPPORT_LONG_LONG +// Support for the long long integral types (with the ll, z and t length modifiers for specifiers +// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. +#ifndef PRINTF_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG 1 #endif -// support for the ptrdiff_t type (%t) +// Support for the ptrdiff_t length modifier (%t) // ptrdiff_t is normally defined in as long or long long type -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T -#define PRINTF_SUPPORT_PTRDIFF_T +#ifndef PRINTF_DISABLE_PTRDIFF_LENGTH_MODIFIER +#define PRINTF_SUPPORT_PTRDIFF_LENGTH_MODIFIER 1 #endif /////////////////////////////////////////////////////////////////////////////// @@ -115,7 +111,7 @@ // import float.h for DBL_MAX -#if defined(PRINTF_SUPPORT_FLOAT) +#if PRINTF_SUPPORT_FLOAT_SPECIFIERS #include #endif @@ -304,7 +300,7 @@ static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxl // internal itoa for 'long long' type -#if defined(PRINTF_SUPPORT_LONG_LONG) +#if PRINTF_SUPPORT_LONG_LONG static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) { char buf[PRINTF_NTOA_BUFFER_SIZE]; @@ -329,10 +325,10 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t #endif // PRINTF_SUPPORT_LONG_LONG -#if defined(PRINTF_SUPPORT_FLOAT) +#if PRINTF_SUPPORT_FLOAT_SPECIFIERS -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_FLOAT_NOTATION_THRESHOLD static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); #endif @@ -357,8 +353,8 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // test for very large values // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { -#if defined(PRINTF_SUPPORT_EXPONENTIAL) + if ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD)) { +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); #else return 0U; @@ -464,7 +460,7 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d } -#if defined(PRINTF_SUPPORT_EXPONENTIAL) +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) { @@ -574,8 +570,8 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d } return idx; } -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +#endif // PRINTF_SUPPORT_FLOAT_SPECIFIERS // internal vsnprintf @@ -666,7 +662,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const format++; } break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) +#if PRINTF_SUPPORT_PTRDIFF_LENGTH_MODIFIER case 't' : flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); format++; @@ -727,7 +723,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const if ((*format == 'i') || (*format == 'd')) { // signed if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) +#if PRINTF_SUPPORT_LONG_LONG const long long value = va_arg(va, long long); idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); #endif @@ -744,7 +740,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const else { // unsigned if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) +#if PRINTF_SUPPORT_LONG_LONG idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); #endif } @@ -759,14 +755,14 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const format++; break; } -#if defined(PRINTF_SUPPORT_FLOAT) +#if PRINTF_SUPPORT_FLOAT_SPECIFIERS case 'f' : case 'F' : if (*format == 'F') flags |= FLAGS_UPPERCASE; idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); format++; break; -#if defined(PRINTF_SUPPORT_EXPONENTIAL) +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS case 'e': case 'E': case 'g': @@ -776,8 +772,8 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); format++; break; -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +#endif // PRINTF_SUPPORT_FLOAT_SPECIFIERS case 'c' : { unsigned int l = 1U; // pre padding @@ -838,7 +834,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const idx = _out_rev(out, buffer, idx, maxlen, ")lin(", 5, width, flags); } else { -#if defined(PRINTF_SUPPORT_LONG_LONG) +#if PRINTF_SUPPORT_LONG_LONG const bool is_ll = sizeof(uintptr_t) == sizeof(long long); if (is_ll) { idx = _ntoa_long_long(out, buffer, idx, maxlen, value, false, 16U, precision, width, flags); @@ -846,7 +842,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const else { #endif idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) +#if PRINTF_SUPPORT_LONG_LONG } #endif } diff --git a/test/test_suite.cpp b/test/test_suite.cpp index 23b0f31c..9cbf7f80 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -466,14 +466,14 @@ TEST_CASE("- flag - non-standard format", "[]" ) { CHECK(!strcmp(buffer, "-42 ")); test::sprintf_(buffer, "%0-15.3e", -42.); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS CHECK(!strcmp(buffer, "-4.200e+01 ")); #else CHECK(!strcmp(buffer, "e")); #endif test::sprintf_(buffer, "%0-15.3g", -42.); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS CHECK(!strcmp(buffer, "-42.0 ")); #else CHECK(!strcmp(buffer, "g")); @@ -485,38 +485,38 @@ TEST_CASE("- flag - non-standard format", "[]" ) { TEST_CASE("# flag", "[]" ) { char buffer[100]; - test::sprintf(buffer, "%#o", 0); + test::sprintf_(buffer, "%#o", 0); CHECK(!strcmp(buffer, "0")); - test::sprintf(buffer, "%#0o", 0); + test::sprintf_(buffer, "%#0o", 0); CHECK(!strcmp(buffer, "0")); - test::sprintf(buffer, "%#.0o", 0); + test::sprintf_(buffer, "%#.0o", 0); CHECK(!strcmp(buffer, "0")); - test::sprintf(buffer, "%#.1o", 0); + test::sprintf_(buffer, "%#.1o", 0); CHECK(!strcmp(buffer, "0")); - test::sprintf(buffer, "%#4o", 0); + test::sprintf_(buffer, "%#4o", 0); CHECK(!strcmp(buffer, " 0")); - test::sprintf(buffer, "%#.4o", 0); + test::sprintf_(buffer, "%#.4o", 0); CHECK(!strcmp(buffer, "0000")); - test::sprintf(buffer, "%#o", 1); + test::sprintf_(buffer, "%#o", 1); CHECK(!strcmp(buffer, "01")); - test::sprintf(buffer, "%#0o", 1); + test::sprintf_(buffer, "%#0o", 1); CHECK(!strcmp(buffer, "01")); - test::sprintf(buffer, "%#.0o", 1); + test::sprintf_(buffer, "%#.0o", 1); CHECK(!strcmp(buffer, "01")); - test::sprintf(buffer, "%#.1o", 1); + test::sprintf_(buffer, "%#.1o", 1); CHECK(!strcmp(buffer, "01")); - test::sprintf(buffer, "%#4o", 1); + test::sprintf_(buffer, "%#4o", 1); CHECK(!strcmp(buffer, " 01")); - test::sprintf(buffer, "%#.4o", 1); + test::sprintf_(buffer, "%#.4o", 1); CHECK(!strcmp(buffer, "0001")); test::sprintf(buffer, "%#04x", 0x1001); CHECK(!strcmp(buffer, "0x1001")); - test::sprintf(buffer, "%#04o", 01001); + test::sprintf_(buffer, "%#04o", 01001); CHECK(!strcmp(buffer, "01001")); - test::sprintf(buffer, "%#.0llx", (long long)0); + test::sprintf_(buffer, "%#.0llx", (long long)0); CHECK(!strcmp(buffer, "")); test::sprintf_(buffer, "%#.8x", 0x614e); CHECK(!strcmp(buffer, "0x0000614e")); @@ -1096,7 +1096,7 @@ TEST_CASE("float padding neg numbers", "[]" ) { test::sprintf_(buffer, "% 5.1f", -5.); CHECK(!strcmp(buffer, " -5.0")); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS test::sprintf_(buffer, "% 6.1g", -5.); CHECK(!strcmp(buffer, " -5")); @@ -1127,7 +1127,7 @@ TEST_CASE("float padding neg numbers", "[]" ) { test::sprintf_(buffer, "%03.0f", -5.); CHECK(!strcmp(buffer, "-05")); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS test::sprintf_(buffer, "%010.1e", -5.); CHECK(!strcmp(buffer, "-005.0e+00")); @@ -1241,7 +1241,7 @@ TEST_CASE("float", "[]" ) { test::sprintf_(buffer, "%-8f", (double) -INFINITY); CHECK(!strcmp(buffer, "-inf ")); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS test::sprintf_(buffer, "%+8e", (double) INFINITY); CHECK(!strcmp(buffer, " +inf")); #endif @@ -1255,7 +1255,7 @@ TEST_CASE("float", "[]" ) { // switch from decimal to exponential representation // test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 ) ); - if (PRINTF_MAX_FLOAT < 10e+2) { + if (PRINTF_FLOAT_NOTATION_THRESHOLD < 10e+2) { CHECK(!strcmp(buffer, "10e+2")); } else { @@ -1263,7 +1263,7 @@ TEST_CASE("float", "[]" ) { } test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 ) ); - if (PRINTF_MAX_FLOAT < 10e+5) { + if (PRINTF_FLOAT_NOTATION_THRESHOLD < 10e+5) { CHECK(!strcmp(buffer, "10e+5")); } else { @@ -1271,7 +1271,7 @@ TEST_CASE("float", "[]" ) { } test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 ) ); - if (PRINTF_MAX_FLOAT < 10e+8) { + if (PRINTF_FLOAT_NOTATION_THRESHOLD < 10e+8) { CHECK(!strcmp(buffer, "10e+8")); } else { @@ -1279,7 +1279,7 @@ TEST_CASE("float", "[]" ) { } test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000) ); - if (PRINTF_MAX_FLOAT < 10e+11) { + if (PRINTF_FLOAT_NOTATION_THRESHOLD < 10e+11) { CHECK(!strcmp(buffer, "10e+11")); } else { @@ -1287,7 +1287,7 @@ TEST_CASE("float", "[]" ) { } test::sprintf_(buffer, "%.0f", (double) ((int64_t)1 * 1000 * 1000 * 1000 * 1000 * 1000) ); - if (PRINTF_MAX_FLOAT < 10e+14) { + if (PRINTF_FLOAT_NOTATION_THRESHOLD < 10e+14) { CHECK(!strcmp(buffer, "10e+14")); } else { @@ -1367,8 +1367,8 @@ TEST_CASE("float", "[]" ) { test::sprintf_(buffer, "a%-5.1fend", 0.5); CHECK(!strcmp(buffer, "a0.5 end")); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%.4g", 1.0); +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + test::sprintf_(buffer, "%.4g", 1.0); CHECK(!strcmp(buffer, "1")); test::sprintf_(buffer, "%G", 12345.678); @@ -1404,7 +1404,7 @@ TEST_CASE("float", "[]" ) { // out of range for float: should switch to exp notation if supported, else empty test::sprintf_(buffer, "%.1f", 1E20); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS CHECK(!strcmp(buffer, "1.0e+20")); #else CHECK(!strcmp(buffer, "")); @@ -1423,7 +1423,7 @@ TEST_CASE("float", "[]" ) { CHECK(!fail); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS // brute force exp str.setf(std::ios::scientific, std::ios::floatfield); for (float i = -1e20; i < (float) 1e20; i += (float) 1e15) { @@ -1614,7 +1614,7 @@ TEST_CASE("string length", "[]" ) { CHECK(!strcmp(buffer, "1234ab")); test::sprintf_(buffer, "%.4.2s", "123456"); - REQUIRE(!strcmp(buffer, ".2s")); + CHECK(!strcmp(buffer, ".2s")); test::sprintf_(buffer, "%.*s", 3, "123456"); CHECK(!strcmp(buffer, "123")); @@ -1724,7 +1724,7 @@ TEST_CASE("misc", "[]" ) { test::sprintf_(buffer, "%*sx", -3, "hi"); CHECK(!strcmp(buffer, "hi x")); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS test::sprintf_(buffer, "%.*g", 2, 0.33333333); CHECK(!strcmp(buffer, "0.33")); From fe83f18fe6551ee83edcdce5b38de3672af2abba Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Wed, 14 Jul 2021 22:28:39 +0300 Subject: [PATCH 09/31] Fixes #18: Now using a `printf_config.h` file to configure the library's behavior. --- CMakeLists.txt | 81 +++++++++++---------------------------------- printf_config.h.in | 17 ++++++++++ test/CMakeLists.txt | 8 ++++- 3 files changed, 44 insertions(+), 62 deletions(-) create mode 100644 printf_config.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d11ad85..db622f32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,69 +7,23 @@ project( VERSION 0.2.0 ) -option(BUILD_TESTS "Build test programs for the library" OFF) - -option(SUPPORT_FLOAT_SPECIFIERS "Support decimal notation floating-point conversion specifiers (%f,%F)" ON) -if (NOT SUPPORT_FLOAT_SPECIFIERS) - add_definitions(-DPRINTF_DISABLE_FLOAT_SPECIFIERS) -endif() - -option(SUPPORT_EXPONENTIAL_SPECIFIERS "Support exponential floating point format conversion specifiers (%e,%E,%g,%G)" ON) -if (NOT SUPPORT_EXPONENTIAL_SPECIFIERS) - add_definitions(-DPRINTF_DISABLE_EXPONENTIAL_SPECIFIERS) -endif() +option(BUILD_TESTS "Build test programs for the library" OFF) +option(BUILD_STATIC_LIBRARY "Build the library as static rather than shared" OFF) -option(SUPPORT_LONG_LONG "Support long long integral types (allows for the ll length modifier and affects %p)" ON) -if (NOT SUPPORT_LONG_LONG) - add_definitions(-DPRINTF_DISABLE_LONG_LONG) -endif() +# Boolean options which go into config.h +option(SUPPORT_FLOAT_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_LONG_LONG "Support long long integral types (allows for the ll length modifier and affects %p)" ON) option(SUPPORT_PTRDIFF_LENGTH_MODIFIER "Support the pointer difference specifier (%t)" ON) -if (NOT SUPPORT_PTRDIFF_LENGTH_MODIFIER) - add_definitions(-DPRINTF_DISABLE_PTRDIFF_LENGTH_MODIFIER) -endif() - -option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the standard library function names (printf, sprintf etc.) to the library's functions" ON) -if (NOT ALIAS_STANDARD_FUNCTION_NAMES) - add_definitions(-DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES) -endif() - -# Numeric defines - -set(NTOA_BUFFER_SIZE "32" CACHE STRING "Integer to string (ntoa) conversion buffer size") -string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" NTOA_BUFFER_SIZE "${NTOA_BUFFER_SIZE}") -if ("${NTOA_BUFFER_SIZE}" STREQUAL "") - message(FATAL_ERROR "An (integral) buffer size for converting integers to floating-point values must be specified." ) -else() - add_definitions(-DPRINTF_NTOA_BUFFER_SIZE=${NTOA_BUFFER_SIZE}) -endif() - -set(PRINTF_FTOA_BUFFER_SIZE "32" CACHE STRING "Float to string (ftoa) conversion buffer size") -string(REGEX MATCH "^[0-9]+$" PRINTF_FTOA_BUFFER_SIZE "${PRINTF_FTOA_BUFFER_SIZE}") -if ("${PRINTF_FTOA_BUFFER_SIZE}" STREQUAL "") - message(FATAL_ERROR "An (integral) buffer size for converting floating-point values to strings must be specified." ) -else() - add_definitions(-DPRINTF_FTOA_BUFFER_SIZE=${PRINTF_FTOA_BUFFER_SIZE}) -endif() +option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the standard library function names (printf, sprintf etc.) to the library's functions" ON) -set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing floating-point values") -string(REGEX MATCH "^[0-9]+$" DEFAULT_FLOAT_PRECISION "${DEFAULT_FLOAT_PRECISION}") -if ("${DEFAULT_FLOAT_PRECISION}" STREQUAL "") - message(FATAL_ERROR "An (integral) default floating-point precision value must be specified." ) -else() - add_definitions(-DPRINTF_DEFAULT_FLOAT_PRECISION=${DEFAULT_FLOAT_PRECISION}) -endif() +# Numeric defines which go into config.h +set(NTOA_BUFFER_SIZE "32" CACHE STRING "Integer to string (ntoa) conversion buffer size") +set(PRINTF_FTOA_BUFFER_SIZE "32" CACHE STRING "Float to string (ftoa) conversion buffer size") +set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing floating-point values") set(FLOAT_NOTATION_THRESHOLD "1e9" CACHE STRING "Largest floating-point value for which printing with %f uses non-exponential notation") -string(REGEX MATCH "^[0-9]+([eE][0-9]+)?$" FLOAT_NOTATION_THRESHOLD "${FLOAT_NOTATION_THRESHOLD}") -if ("${FLOAT_NOTATION_THRESHOLD}" STREQUAL "") - message(FATAL_ERROR "The %f notation switch threshold must be an integer." ) -else() - add_definitions(-DPRINTF_FLOAT_NOTATION_THRESHOLD=${FLOAT_NOTATION_THRESHOLD}) -endif() - -option(BUILD_STATIC_LIBRARY "Build the library as static rather than shared" OFF) - if (BUILD_STATIC_LIBRARY) add_library(mpaland-printf STATIC printf.c) @@ -77,6 +31,11 @@ else() add_library(mpaland-printf SHARED printf.c) endif() +set(GENERATED_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") +configure_file("printf_config.h.in" "${GENERATED_INCLUDE_DIR}/printf_config.h" @ONLY) +target_compile_definitions(mpaland-printf PRIVATE PRINTF_INCLUDE_CONFIG_H) +target_include_directories(mpaland-printf PRIVATE "$") + set_property(TARGET mpaland-printf PROPERTY C_STANDARD 99) set_property(TARGET mpaland-printf PROPERTY C_STANDARD_REQUIRED ON) set_property(TARGET mpaland-printf PROPERTY C_EXTENSIONS OFF) @@ -95,7 +54,7 @@ set_target_properties(mpaland-printf PROPERTIES if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(mpaland-printf PRIVATE /W4) -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_compile_options(mpaland-printf PRIVATE -Wall -Wextra -pedantic) endif() @@ -106,15 +65,15 @@ if (BUILD_TESTS) endif() if (UNIX) - add_custom_target(mpaland-printf-sizes + add_custom_target(mpaland-printf-sizes COMMAND size -A -t $ > mpaland-printf_sizes.txt DEPENDS mpaland-printf BYPRODUCTS mpaland-printf_sizes.txt COMMENT Prints the sizes of the different sections of the ELF file: text, dat, vss etc.) - add_custom_target(mpaland-printf-symbols + add_custom_target(mpaland-printf-symbols COMMAND nm --numeric-sort --print-size "$" > mpaland-printf_symbols.txt - COMMAND bash -c "nm --numeric-sort --print-size $ | c++filt > mpaland-printf_cpp_symbols.txt" + COMMAND bash -c "nm --numeric-sort --print-size $ | c++filt > mpaland-printf_cpp_symbols.txt" VERBATIM DEPENDS mpaland-printf BYPRODUCTS mpaland-printf_symbols.txt mpaland-printf_cpp_symbols.txt diff --git a/printf_config.h.in b/printf_config.h.in new file mode 100644 index 00000000..abe5661b --- /dev/null +++ b/printf_config.h.in @@ -0,0 +1,17 @@ +#pragma once +#ifndef PRINTF_CONFIG_H_ +#define PRINTF_CONFIG_H_ + +#cmakedefine PRINTF_SUPPORT_FLOAT_SPECIFIERS @SUPPORT_FLOAT_SPECIFIERS@ +#cmakedefine PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS @SUPPORT_EXPONENTIAL_SPECIFIERS@ +#cmakedefine PRINTF_SUPPORT_LONG_LONG @SUPPORT_LONG_LONG@ +#cmakedefine PRINTF_SUPPORT_PTRDIFF_LENGTH_MODIFIER @SUPPORT_PTRDIFF_LENGTH_MODIFIER@ +#cmakedefine PRINTF_ALIAS_STANDARD_FUNCTION_NAMES @ALIAS_STANDARD_FUNCTION_NAMES@ + +#cmakedefine PRINTF_NTOA_BUFFER_SIZE @NTOA_BUFFER_SIZE@ +#cmakedefine PRINTF_FTOA_BUFFER_SIZE @PRINTF_FTOA_BUFFER_SIZE@ +#cmakedefine PRINTF_DEFAULT_FLOAT_PRECISION @DEFAULT_FLOAT_PRECISION@ +#cmakedefine PRINTF_FLOAT_NOTATION_THRESHOLD @FLOAT_NOTATION_THRESHOLD@ + +#endif // PRINTF_CONFIG_H_ + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 62d69fc3..b9008332 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,12 @@ set_target_properties( CXX_EXTENSIONS NO ) +# These two lines are necessary, since the test suite does not actually use the +# compiled library - it includes the library's source .c file; and that means we +# need to include the generated config.h file. +target_compile_definitions(test_suite PRIVATE PRINTF_INCLUDE_CONFIG_H) +target_include_directories(test_suite PRIVATE "${GENERATED_INCLUDE_DIR}") + option(TEST_BROKEN_FORMATS "Include tests using non-standard-compliant format strings?" ON) # ... don't worry, we'll suppress the compiler warnings for those. if (TEST_BROKEN_FORMATS) @@ -22,7 +28,7 @@ target_link_libraries(test_suite PRIVATE mpaland-printf) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(test_suite PRIVATE /W4) -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # lots of warnings and all warnings as errors target_compile_options(test_suite PRIVATE From 0229efec26d735d94a74ecfcb3099b05b420a293 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Wed, 14 Jul 2021 22:31:37 +0300 Subject: [PATCH 10/31] CMakeLists.txt tweak --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db622f32..761a0422 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.13) project( mpaland-printf LANGUAGES C - DESCRIPTION "Mostly- stand-alone C implementation of sprintf and printf functions" + DESCRIPTION "Mostly-stand-alone C implementation of printf, vprintf, sprintf and related functions" HOMEPAGE_URL https://github.com/eyalroz/printf VERSION 0.2.0 ) From 0f2d3e73125521ffb052d5a50d4d3827179748cd Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Thu, 15 Jul 2021 23:20:19 +0300 Subject: [PATCH 11/31] Fixes #19: Now using a number of digits to define the decimal-to-exponential notation threshold value. --- CMakeLists.txt | 2 +- printf.c | 11 ++++++++--- printf_config.h.in | 8 ++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 761a0422..d7aa8cbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the standard library function name set(NTOA_BUFFER_SIZE "32" CACHE STRING "Integer to string (ntoa) conversion buffer size") set(PRINTF_FTOA_BUFFER_SIZE "32" CACHE STRING "Float to string (ftoa) conversion buffer size") set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing floating-point values") -set(FLOAT_NOTATION_THRESHOLD "1e9" CACHE STRING "Largest floating-point value for which printing with %f uses non-exponential notation") +set(MAX_INTEGRAL_DIGITS_FOR_DECIMAL "9" CACHE STRING "Maximum number of integral-part digits of a floating-point value for which printing with %f uses decimal (non-exponential) notation") if (BUILD_STATIC_LIBRARY) add_library(mpaland-printf STATIC printf.c) diff --git a/printf.c b/printf.c index f683a439..a77475f4 100644 --- a/printf.c +++ b/printf.c @@ -73,10 +73,10 @@ // According to the C languages standard, printf() and related functions must be able to print any // integral number in floating-point notation, regardless of length, when using the %f specifier - -// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, +// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, // all values beyond this threshold are switched to exponential notation. -#ifndef PRINTF_FLOAT_NOTATION_THRESHOLD -#define PRINTF_FLOAT_NOTATION_THRESHOLD 1e9 +#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL +#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 #endif // Support for the long long integral types (with the ll, z and t length modifiers for specifiers @@ -93,6 +93,11 @@ /////////////////////////////////////////////////////////////////////////////// +// The following will convert the number-of-digits into an exponential-notation literal +#define PRINTF_CONCATENATE(s1, s2) s1##s2 +#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) +#define PRINTF_FLOAT_NOTATION_THRESHOLD PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL) + // internal flag definitions #define FLAGS_ZEROPAD (1U << 0U) #define FLAGS_LEFT (1U << 1U) diff --git a/printf_config.h.in b/printf_config.h.in index abe5661b..07b7740f 100644 --- a/printf_config.h.in +++ b/printf_config.h.in @@ -8,10 +8,10 @@ #cmakedefine PRINTF_SUPPORT_PTRDIFF_LENGTH_MODIFIER @SUPPORT_PTRDIFF_LENGTH_MODIFIER@ #cmakedefine PRINTF_ALIAS_STANDARD_FUNCTION_NAMES @ALIAS_STANDARD_FUNCTION_NAMES@ -#cmakedefine PRINTF_NTOA_BUFFER_SIZE @NTOA_BUFFER_SIZE@ -#cmakedefine PRINTF_FTOA_BUFFER_SIZE @PRINTF_FTOA_BUFFER_SIZE@ -#cmakedefine PRINTF_DEFAULT_FLOAT_PRECISION @DEFAULT_FLOAT_PRECISION@ -#cmakedefine PRINTF_FLOAT_NOTATION_THRESHOLD @FLOAT_NOTATION_THRESHOLD@ +#cmakedefine PRINTF_NTOA_BUFFER_SIZE @NTOA_BUFFER_SIZE@ +#cmakedefine PRINTF_FTOA_BUFFER_SIZE @PRINTF_FTOA_BUFFER_SIZE@ +#cmakedefine PRINTF_DEFAULT_FLOAT_PRECISION @DEFAULT_FLOAT_PRECISION@ +#cmakedefine PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL @MAX_INTEGRAL_DIGITS_FOR_DECIMAL@ #endif // PRINTF_CONFIG_H_ From c50d42b25ec49257844188506c974393f2d911ab Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Fri, 16 Jul 2021 08:59:18 +0300 Subject: [PATCH 12/31] README update --- README.md | 124 +++++++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 7bb2ade6..ce169530 100644 --- a/README.md +++ b/README.md @@ -13,30 +13,34 @@ Primarily designed for usage in embedded systems, where printf is not available Using the standard libc printf may pull **a lot** of unwanted library stuff and can bloat code size about 20k or is not 100% thread safe. In this cases the following implementation can be used. Absolutely **NO dependencies** are required, *printf.c* brings all necessary routines, even its own fast `ftoa` (floating point), `ntoa` (decimal) conversion. -If memory footprint is really a critical issue, floating point, exponential and 'long long' support and can be turned off via the `PRINTF_DISABLE_SUPPORT_FLOAT`, `PRINTF_DISABLE_SUPPORT_EXPONENTIAL` and `PRINTF_DISABLE_SUPPORT_LONG_LONG` compiler switches. -When using printf (instead of sprintf/snprintf) you have to provide your own `_putchar()` low level function as console/serial output. +If memory footprint is really a critical issue, floating point, exponential and 'long long' support and can be turned off via compiler definitions or CMake c onfiguration options (details below). When using this library's `printf()` and `vprintf()` functions (instead of the standard library's) you have to provide your own `_putchar()` low-level function - as this library is isolated from dealing with console/serial output, files etc. ## Highlights and Design Goals -There is a boatload of so called 'tiny' printf implementations around. So why this one? -I've tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing most of the test suite. -Therefore I decided to write an own, final implementation which meets the following items: - - - Very small implementation (around 600 code lines) - - NO dependencies, no libs, just one module file - - Support of all important flags, width and precision sub-specifiers (see below) - - Support of decimal/floating number representation (with an own fast itoa/ftoa) - - Reentrant and thread-safe, malloc free, no static vars/buffers - - LINT and compiler L4 warning free, mature, coverity clean, automotive ready - - Extensive test suite (> 400 test cases) passing - - Simply the best *printf* around the net +There is a boatload of so called 'tiny' printf implementations around. So why this one? [Macro Paland](https://github.com/mpaland) before creating this one, but most of them had very limited flag/specifier support, a lot of other dependencies or were just not standard compliant, and failing most tests in this repository's current test suite. Macro therefore decided to write his own implementation, with the following goals in mind: + + - Very small implementation (under 700 lines of code as of July 2021) + - NO dependencies on other packages or libraries, no multiple compiled objects, just one object file. + - Support of all standard flags, and all width and precision sub-specifiers (see below) + - Support of decimal/floating number representation (with an internal, relatively fast `itoa`/`ftoa` implementation) + - Reentrant and thread-safe; `malloc()` free; no global or static-local variables or buffers. + - Clean, mature and robust code; passes linting and compilation with no warnings; full coverage by testcases; automotive ready. + - Extensive test suite (currently over 400 test cases) passing. + - Simply the best *printf* around the net <- (although that's always a matter of opinion) - MIT license ## Usage -Add/link *printf.c* to your project and include *printf.h*. That's it. -Implement your low level output function needed for `printf()`: +There are at least 4 ways to use this library: + +1. Use CMake to configure, build and install the library. Then, in another CMake project, use `find_package(mpaland-printf)` and make sure the install location is in the package search path. +2. Use CMake to configure and build the library. You now have a library file (shared or static, depending on your choice), e.g. `printf.a` on Unix machines; and a header file, `printf.h`. In your project, if you include `printf.h` and link against the library file, you're all set (remember - no dependencies). +3. Copy `printf.c` and `printf.h` into your own project, and build them yourself. Note the various preprocessor options controlling the library's behavior! You will have to set them yourself, or live with the default values (which are quite reasonable). Remember that the library requires compilation with the C99 language standard. +4. Include the contents of `printf.c` into your own code. This works well enough - whether it's a C or C++ file, and even within a namespace. With this option also you need to consider the preprocessor options controlling behavior, and the language standard. + +One caveat to the above, is that if you want to use the `printf()` function and print to the standard output stream or to a file - you will need to implement a low-level output function for the library to use: + ```C void _putchar(char character) { @@ -44,24 +48,27 @@ void _putchar(char character) } ``` -Usage is 1:1 like the according stdio.h library version: +Function names within the library correspond to function names in `stdio.h`, but with a `_` suffix so as not to clash with them: ```C -int printf(const char* format, ...); -int sprintf(char* buffer, const char* format, ...); -int snprintf(char* buffer, size_t count, const char* format, ...); -int vsnprintf(char* buffer, size_t count, const char* format, va_list va); - -// use output function (instead of buffer) for streamlike interface -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 printf_(const char* format, ...); +int sprintf_(char* buffer, const char* format, ...); +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + +// Higher-order functions - not exist 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); +``` +You also have the option to enable an aliasing of the standard function names, so that `printf()`, `vprintf()` etc. invoke the functions from this library. + ``` -**Due to general security reasons it is highly recommended to prefer and use `snprintf` (with the max buffer size as `count` parameter) instead of `sprintf`.** -`sprintf` has no buffer limitation, so when needed - use it really with care! +**Important note: Due to general security reasons it is highly recommended to prefer and use `snprintf` (with the max buffer size as `count` parameter) instead of `sprintf`.** `sprintf` has no buffer limitation, and will "happily" overflow your buffer; so only use it when absolutely necessary, and with care! ### Streamlike Usage -Besides the regular standard `printf()` functions, this module also provides `fctprintf()`, which takes an output function as first parameter to build a streamlike output like `fprintf()`: +Besides the regular standard `printf()` functions, this module also provides `fctprintf()` and `vfctprintf()`, which take an output function as their first parameter, instead of a buffer - enabling a stream-like output, generalizing the mechanism of `fprintf()`: ```C + // define the output function void my_stream_output(char character, void* arg) { @@ -100,6 +107,7 @@ The following format specifiers are supported: | p | Pointer address | | % | A % followed by another % character will write a single % | +Note: the `%a` specifier for hexadecimal floating-point notation (introduced in C99 and C++11) is _not_ currently supported. ### Supported Flags @@ -112,7 +120,7 @@ The following format specifiers are supported: | 0 | Left-pads the number with zeros (0) instead of spaces when padding is specified (see width sub-specifier). | -### Supported Width +### Supported Width Specifiers | Width | Description | |----------|-------------| @@ -120,7 +128,7 @@ The following format specifiers are supported: | * | The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | -### Supported Precision +### Supported Precision Specifiers | Precision | Description | |-----------|-------------| @@ -128,37 +136,38 @@ The following format specifiers are supported: | .* | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | -### Supported Length +### Supported Length modifiers The length sub-specifier modifies the length of the data type. -| Length | d i | u o x X | -|--------|------|---------| -| (none) | int | unsigned int | -| hh | signed char | unsigned char | -| h | short int | unsigned short int | -| l | long int | unsigned long int | -| ll | long long int | unsigned long long int (if PRINTF_SUPPORT_LONG_LONG is defined) | -| j | intmax_t | uintmax_t | -| z | size_t | size_t | -| t | ptrdiff_t | ptrdiff_t (if PRINTF_SUPPORT_PTRDIFF_T is defined) | +| Length | d i | u o x X | Support controlled by preprocessor variable... | +|--------|---------------|------------------------|------------------------------------------------| +| (none) | int | unsigned int | | +| hh | signed char | unsigned char | | +| h | short int | unsigned short int | | +| l | long int | unsigned long int | | +| ll | long long int | unsigned long long int | PRINTF_SUPPORT_LONG_LONG | +| j | intmax_t | uintmax_t | | +| z | size_t | size_t | | +| t | ptrdiff_t | ptrdiff_t | PRINTF_SUPPORT_PTRDIFF_T | + +Note: the `L` modifier, for `long double`, is not currently supported. ### Return Value -Upon successful return, all functions return the number of characters written, _excluding_ the terminating null character used to end the string. -Functions `snprintf()` and `vsnprintf()` don't write more than `count` bytes, _including_ the terminating null byte ('\0'). +Upon successful return, all functions return the number of characters written, _excluding_ the terminating NUL character used to end the string. +Functions `snprintf()` and `vsnprintf()` don't write more than `count` bytes, _including_ the terminating NUL character ('\0'). Anyway, if the output was truncated due to this limit, the return value is the number of characters that _could_ have been written. Notice that a value equal or larger than `count` indicates a truncation. Only when the returned value is non-negative and less than `count`, the string has been completely written. If any error is encountered, `-1` is returned. -If `buffer` is set to `NULL` (`nullptr`) nothing is written and just the formatted length is returned. +If `buffer` is set to `NULL` (`nullptr`) nothing is written, but the formatted length is returned. ```C int length = sprintf(NULL, "Hello, world"); // length is set to 12 ``` - ## Compiler Switches/Defines | Name | Default value | Description | @@ -174,34 +183,33 @@ int length = sprintf(NULL, "Hello, world"); // length is set to 12 | PRINTF_DISABLE_SUPPORT_PTRDIFF_T | undefined | Define this to disable ptrdiff_t (%t) support | -## Caveats -None anymore (finally). - - ## Test Suite -For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/catchorg/Catch2) framework for unit-tests, which is auto-adding main(). -Running with the `--wait-for-keypress exit` option waits for the enter key after test end. +For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/catchorg/Catch2) framework for unit-tests, which generates a `main()` function alongside the various testcases. -## Projects Using printf +## Projects Using `printf` - [turnkeyboard](https://github.com/mpaland/turnkeyboard) uses printf as log and generic tty (formatting) output. - printf is part of [embeddedartistry/libc](https://github.com/embeddedartistry/libc), a libc targeted for embedded systems usage. - The [Hatchling Platform]( https://github.com/adrian3git/HatchlingPlatform) uses printf. -(Just send me a mail/issue/PR to get *your* project listed here) +(Just send [eyalroz](https://github.com/eyalroz) a mail, or open an issue/PR to get *your* project listed as well.) ## Contributing +The following assumes Marco Paland's original repository remains mostly-inactive in terms of commits (which it has been, as of the time of writing, for 2 years). + 0. Give this project a :star: 1. Create an issue and describe your idea -2. [Fork it](https://github.com/mpaland/printf/fork) -3. Create your feature branch (`git checkout -b my-new-feature`) +2. [Fork it](https://github.com/eyalroz/printf/fork) +3. Create your feature branch (`git checkout -b my-new-feature`). +4. Implement your features; don't forget to make sure all existing tests still pass. +5. Add new checks or test-cases to the test suite - both for any problems you have identified and for any new functionality you have introduced. 4. Commit your changes (`git commit -am 'Add some feature'`) 5. Publish the branch (`git push origin my-new-feature`) -6. Create a new pull request -7. Profit! :heavy_check_mark: +6. Create a new pull request against this repository. +7. I will try to attend to PRs promptly. ## License -printf is written under the [MIT license](http://www.opensource.org/licenses/MIT). +Both Macro Paland's original `printf` and this fork are published under the [MIT license](http://www.opensource.org/licenses/MIT). From e79077e67a01b6fa9de51d4c90ec26eb66af7d15 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Fri, 16 Jul 2021 10:34:23 +0300 Subject: [PATCH 13/31] Updated `README.md` to reflect many recent changes in the repository (and complete changes due to the fork). --- README.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ce169530..b316e7d1 100644 --- a/README.md +++ b/README.md @@ -170,17 +170,19 @@ int length = sprintf(NULL, "Hello, world"); // length is set to 12 ## Compiler Switches/Defines -| Name | Default value | Description | -|------|---------------|-------------| -| PRINTF_INCLUDE_CONFIG_H | undefined | Define this as compiler switch (e.g. `gcc -DPRINTF_INCLUDE_CONFIG_H`) to include a "printf_config.h" definition file | -| PRINTF_NTOA_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 | -| PRINTF_FTOA_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 | -| PRINTF_DEFAULT_FLOAT_PRECISION | 6 | Define the default floating point precision | -| PRINTF_MAX_FLOAT | 1e9 | Define the largest suitable value to be printed with %f, before using exponential representation. Note that this must be set to a value which fits in a 64-bit signed integer (i.e. less than 2^63) | -| PRINTF_DISABLE_SUPPORT_FLOAT | undefined | Define this to disable floating point (%f) support | -| PRINTF_DISABLE_SUPPORT_EXPONENTIAL | undefined | Define this to disable exponential floating point (%e) support | -| PRINTF_DISABLE_SUPPORT_LONG_LONG | undefined | Define this to disable long long (%ll) support | -| PRINTF_DISABLE_SUPPORT_PTRDIFF_T | undefined | Define this to disable ptrdiff_t (%t) support | +| CMake option name | Preprocessor definition | Default value | Description | +|---------------------------------|----------------------------------------|---------------|-------------| +| (always on) | PRINTF_INCLUDE_CONFIG_H | undefined | Define this as compiler switch (e.g. `gcc -DPRINTF_INCLUDE_CONFIG_H`) to include a "printf_config.h" definition file | +| ALIAS_STANDARD_FUNCTION_NAMES | PRINTF_ALIAS_STANDARD_FUNCTION_NAMES | No | Alias the standard library function names (`printf()`, `sprintf()` etc.) to the library's functions | +| NTOA_BUFFER_SIZE | PRINTF_NTOA_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. | +| FTOA_BUFFER_SIZE | PRINTF_FTOA_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. | +| DEFAULT_FLOAT_PRECISION | PRINTF_DEFAULT_FLOAT_PRECISION | 6 | Define the default floating point precision| +| MAX_INTEGRAL_DIGITS_FOR_DECIMAL | PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL | 9 | Maximum number of integral-part digits of a floating-point value for which printing with %f uses decimal (non-exponential) notation | +| SUPPORT_FLOAT_SPECIFIERS | PRINTF_SUPPORT_FLOAT_SPECIFIERS | Yes | Support decimal notation floating-point conversion specifiers (%f,%F) | +| SUPPORT_EXPONENTIAL_SPECIFIERS | PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS | Yes | Support exponential floating point format conversion specifiers (%e,%E,%g,%G)" | +| SUPPORT_LONG_LONG | PRINTF_SUPPORT_LONG_LONG | Yes | Support long long integral types (allows for the ll length modifier and affects %p) | +| SUPPORT_PTRDIFF_LENGTH_MODIFIER | PRINTF_SUPPORT_PTRDIFF_LENGTH_MODIFIER | Yes | Support the pointer difference specifier (%t), used for `ptrdiff_t` variables" | +| BUILD_STATIC_LIBRARY | (none) | No | Build a library out of a shared object (dynamically linked at load time) rather than a static one (baked into the executables you build)| ## Test Suite @@ -212,4 +214,4 @@ The following assumes Marco Paland's original repository remains mostly-inactive ## License -Both Macro Paland's original `printf` and this fork are published under the [MIT license](http://www.opensource.org/licenses/MIT). +Both Macro Paland's [original `printf`](https://github.com/mpaland/printf/) and this fork are published under the [MIT license](http://www.opensource.org/licenses/MIT). From 007d24b960ac35357aefd92121a8d0341f1fbf18 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Sat, 17 Jul 2021 00:27:14 +0300 Subject: [PATCH 14/31] `CMakeLists.txt` tweak. --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7aa8cbc..23b69b99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,13 +26,13 @@ set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing set(MAX_INTEGRAL_DIGITS_FOR_DECIMAL "9" CACHE STRING "Maximum number of integral-part digits of a floating-point value for which printing with %f uses decimal (non-exponential) notation") if (BUILD_STATIC_LIBRARY) - add_library(mpaland-printf STATIC printf.c) + add_library(mpaland-printf STATIC) else() - add_library(mpaland-printf SHARED printf.c) + add_library(mpaland-printf SHARED) endif() - set(GENERATED_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") configure_file("printf_config.h.in" "${GENERATED_INCLUDE_DIR}/printf_config.h" @ONLY) +target_sources(mpaland-printf PRIVATE printf.c "${GENERATED_INCLUDE_DIR}/printf_config.h" printf.h) target_compile_definitions(mpaland-printf PRIVATE PRINTF_INCLUDE_CONFIG_H) target_include_directories(mpaland-printf PRIVATE "$") From 6e3851e026731d2b1f2b3de5b701c1a4c370a4cc Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Wed, 21 Jul 2021 19:09:08 +0300 Subject: [PATCH 15/31] Fixes #20 - Updates catch2 version to release 2.13.6 (current latest release). --- test/catch.hpp | 6019 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 4511 insertions(+), 1508 deletions(-) diff --git a/test/catch.hpp b/test/catch.hpp index 1850fff1..36eaeb27 100644 --- a/test/catch.hpp +++ b/test/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.7.0 - * Generated: 2019-03-07 21:34:30.252164 + * Catch v2.13.6 + * Generated: 2021-04-16 18:23:38.044268 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 7 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 6 #ifdef __clang__ # pragma clang system_header @@ -66,13 +66,16 @@ #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX @@ -132,30 +135,51 @@ namespace Catch { #endif -#if defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + #endif -#ifdef __clang__ +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ @@ -180,6 +204,7 @@ namespace Catch { // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// @@ -204,20 +229,19 @@ namespace Catch { // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#ifdef _MSC_VER +#if defined(_MSC_VER) -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) // Universal Windows platform does not support SEH // Or console colours (or console at all...) @@ -230,10 +254,17 @@ namespace Catch { // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// @@ -266,40 +297,56 @@ namespace Catch { #endif //////////////////////////////////////////////////////////////////////////////// -// Check if string_view is available and usable -// The check is split apart to work around v140 (VS2015) preprocessor issue... -#if defined(__has_include) -#if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -#endif + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE #endif -//////////////////////////////////////////////////////////////////////////////// -// Check if optional is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif -//////////////////////////////////////////////////////////////////////////////// -// Check if variant is available and usable +// Various stdlib support checks that require __has_include #if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 -# include -# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# define CATCH_CONFIG_NO_CPP17_VARIANT -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__clang__) && (__clang_major__ < 8) -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER @@ -324,10 +371,6 @@ namespace Catch { # define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif @@ -336,6 +379,10 @@ namespace Catch { # define CATCH_CONFIG_CPP17_VARIANT #endif +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) # define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif @@ -352,17 +399,53 @@ namespace Catch { # define CATCH_CONFIG_POLYFILL_ISNAN #endif +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) @@ -427,7 +510,7 @@ namespace Catch { SourceLineInfo( SourceLineInfo&& ) noexcept = default; SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - bool empty() const noexcept; + bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; @@ -468,9 +551,10 @@ namespace Catch { } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h @@ -497,6 +581,7 @@ namespace Catch { virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); @@ -509,53 +594,30 @@ namespace Catch { #include #include #include +#include namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. c_str() must return a null terminated - /// string, however, and so the StringRef will internally take ownership - /// (taking a copy), if necessary. In theory this ownership is not externally - /// visible - but it does mean (substring) StringRefs should not be shared between - /// threads. + /// it may not be null terminated. class StringRef { public: using size_type = std::size_t; + using const_iterator = const char*; private: - friend struct StringRefTestAccess; - - char const* m_start; - size_type m_size; - - char* m_data = nullptr; - - void takeOwnership(); - static constexpr char const* const s_empty = ""; - public: // construction/ assignment - StringRef() noexcept - : StringRef( s_empty, 0 ) - {} - - StringRef( StringRef const& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ) - {} + char const* m_start = s_empty; + size_type m_size = 0; - StringRef( StringRef&& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ), - m_data( other.m_data ) - { - other.m_data = nullptr; - } + public: // construction + constexpr StringRef() noexcept = default; StringRef( char const* rawChars ) noexcept; - StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} @@ -565,101 +627,64 @@ namespace Catch { m_size( stdString.size() ) {} - ~StringRef() noexcept { - delete[] m_data; - } - - auto operator = ( StringRef const &other ) noexcept -> StringRef& { - delete[] m_data; - m_data = nullptr; - m_start = other.m_start; - m_size = other.m_size; - return *this; + explicit operator std::string() const { + return std::string(m_start, m_size); } - operator std::string() const; - - void swap( StringRef& other ) noexcept; - public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } - auto operator[] ( size_type index ) const noexcept -> char; + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } public: // named queries - auto empty() const noexcept -> bool { + constexpr auto empty() const noexcept -> bool { return m_size == 0; } - auto size() const noexcept -> size_type { + constexpr auto size() const noexcept -> size_type { return m_size; } - auto numberOfCharacters() const noexcept -> size_type; + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches - auto substr( size_type start, size_type size ) const noexcept -> StringRef; + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; - // Returns the current start pointer. - // Note that the pointer can change when if the StringRef is a substring - auto currentData() const noexcept -> char const*; + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; - private: // ownership queries - may not be consistent between calls - auto isOwned() const noexcept -> bool; - auto isSubstring() const noexcept -> bool; - }; + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; - auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } - } // namespace Catch -inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { return Catch::StringRef( rawChars, size ); } // end catch_stringref.h -// start catch_type_traits.hpp - - -#include - -namespace Catch{ - -#ifdef CATCH_CPP17_OR_GREATER - template - inline constexpr auto is_unique = std::true_type{}; - - template - inline constexpr auto is_unique = std::bool_constant< - (!std::is_same_v && ...) && is_unique - >{}; -#else - -template -struct is_unique : std::true_type{}; - -template -struct is_unique : std::integral_constant -::value - && is_unique::value - && is_unique::value ->{}; - -#endif -} - -// end catch_type_traits.hpp // start catch_preprocessor.hpp @@ -722,23 +747,170 @@ struct is_unique : std::integral_constant #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else -// MSVC is adding extra space and needs more calls to properly remove () -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif -#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) Catch::TypeList +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template