一个精简的printf支持,剪裁后只占用几KB ROM
This commit is contained in:
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -291,7 +291,9 @@
|
||||
"mk_sys.h": "c",
|
||||
"sram.h": "c",
|
||||
"semaphore.h": "c",
|
||||
"libc.h": "c"
|
||||
"libc.h": "c",
|
||||
"printf.h": "c",
|
||||
"printf_config.h": "c"
|
||||
},
|
||||
"cortex-debug.showRTOS": false,
|
||||
"cortex-debug.variableUseNaturalFormat": false,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
|
||||
#include "stm32f10x_conf.h"
|
||||
void _memset(void *data, int val, int size)
|
||||
void memset(void *data, int val, int size)
|
||||
{
|
||||
unsigned int *_d = data;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
_d += i;
|
||||
_d[0] = 0;
|
||||
}
|
||||
}
|
||||
@@ -21,9 +20,9 @@ void FSMC_SRAM_Init(void)
|
||||
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
|
||||
_memset(&FSMC_NORSRAMInitStructure, 0, sizeof(FSMC_NORSRAMInitStructure));
|
||||
_memset(&readWriteTiming, 0, sizeof(readWriteTiming));
|
||||
_memset(&GPIO_InitStructure, 0, sizeof(GPIO_InitStructure));
|
||||
memset(&FSMC_NORSRAMInitStructure, 0, sizeof(FSMC_NORSRAMInitStructure));
|
||||
memset(&readWriteTiming, 0, sizeof(readWriteTiming));
|
||||
memset(&GPIO_InitStructure, 0, sizeof(GPIO_InitStructure));
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG, ENABLE);
|
||||
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);
|
||||
|
||||
@@ -13,7 +13,7 @@ void sram_init(void)
|
||||
void jump2kernel(void)
|
||||
{
|
||||
sram_init();
|
||||
// sram_test();
|
||||
sram_test();
|
||||
if (((*(__IO uint32_t *)KERNEL_IMG_START_ADDR) & 0x2FFE0000) == 0x20000000) // 检查栈顶地址是否合法,即检查此段Flash中是否已有APP程序
|
||||
{
|
||||
__set_PRIMASK(1);
|
||||
|
||||
@@ -17,5 +17,6 @@ add_subdirectory(cpio)
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(mr)
|
||||
add_subdirectory(lwip)
|
||||
add_subdirectory(printf)
|
||||
add_subdirectory(letter-shell/demo/mkrtos)
|
||||
# add_subdirectory(at_device)
|
||||
|
||||
190
mkrtos_user/lib/printf/CMakeLists.txt
Normal file
190
mkrtos_user/lib/printf/CMakeLists.txt
Normal file
@@ -0,0 +1,190 @@
|
||||
cmake_minimum_required (VERSION 3.13)
|
||||
|
||||
include(CheckTypeSize)
|
||||
|
||||
project(
|
||||
printf
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Self-contained C implementation of printf, vprintf, sprintf and related functions"
|
||||
HOMEPAGE_URL https://github.com/eyalroz/printf
|
||||
VERSION 6.0.0
|
||||
)
|
||||
option(BUILD_TESTS "Build test programs for the library" OFF)
|
||||
option(BUILD_STATIC_LIBRARY "Build the library as static rather than shared" ON)
|
||||
|
||||
# Boolean options which go into config.h
|
||||
|
||||
option(SUPPORT_DECIMAL_SPECIFIERS "Support decimal notation floating-point conversion specifiers (%f,%F)" OFF)
|
||||
option(SUPPORT_EXPONENTIAL_SPECIFIERS "Support exponential floating point format conversion specifiers (%e,%E,%g,%G)" OFF)
|
||||
option(SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS "Support the I + bit size integer specifiers (%I8, %I16, %I32, %I64) as in Microsoft Visual C++" OFF)
|
||||
option(SUPPORT_WRITEBACK_SPECIFIER "Support the length write-back specifier (%n)" OFF)
|
||||
option(SUPPORT_LONG_LONG "Support long long integral types (allows for the ll length modifier and affects %p)" OFF)
|
||||
option(ALIAS_STANDARD_FUNCTION_NAMES "Alias the standard library function names (printf, sprintf etc.) to the library's functions" ON)
|
||||
|
||||
foreach(opt
|
||||
SUPPORT_DECIMAL_SPECIFIERS
|
||||
SUPPORT_EXPONENTIAL_SPECIFIERS
|
||||
SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS
|
||||
SUPPORT_WRITEBACK_SPECIFIER
|
||||
SUPPORT_LONG_LONG
|
||||
ALIAS_STANDARD_FUNCTION_NAMES)
|
||||
if (${${opt}})
|
||||
set("PRINTF_${opt}" 1)
|
||||
else()
|
||||
set("PRINTF_${opt}" 0)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Numeric defines which go into printf_config.h
|
||||
|
||||
set(PRINTF_INTEGER_BUFFER_SIZE "32" CACHE STRING "Integer to string conversion buffer size")
|
||||
set(PRINTF_DECIMAL_BUFFER_SIZE "32" CACHE STRING "Floating-point to decimal conversion buffer size")
|
||||
set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing floating-point values")
|
||||
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")
|
||||
set(LOG10_TAYLOR_TERMS "4" CACHE STRING "The number of terms in a Taylor series expansion of log_10(x) to use for approximation")
|
||||
|
||||
# Checks related to the 'j', 'z' and 't' size modifiers
|
||||
|
||||
check_type_size( "long" SIZEOF_LONG )
|
||||
check_type_size( "long long" SIZEOF_LONG_LONG )
|
||||
|
||||
set(ACCEPTABLE_JZT_TYPE_SIZES ${SIZEOF_LONG} ${SIZEOF_LONG_LONG})
|
||||
|
||||
function(validate_type_size type_name)
|
||||
check_type_size(${type_name} TYPE_SIZE)
|
||||
if (NOT ${TYPE_SIZE} IN_LIST ACCEPTABLE_JZT_TYPE_SIZES)
|
||||
message(FATAL_ERROR "sizeof(${type_name}) is ${TYPE_SIZE}, which is neither sizeof(long) (${SIZEOF_LONG}) nor sizeof(long long) (${SIZEOF_LONG_LONG}). Please contact the library maintainers with your platform details.")
|
||||
endif()
|
||||
endfunction()
|
||||
validate_type_size("intmax_t")
|
||||
validate_type_size("size_t")
|
||||
validate_type_size("ptrdiff_t")
|
||||
|
||||
if (BUILD_STATIC_LIBRARY)
|
||||
add_library(printf STATIC)
|
||||
else()
|
||||
add_library(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(printf PRIVATE
|
||||
src/printf/printf.c
|
||||
src/printf/mkrtos_putchar.c
|
||||
"${GENERATED_INCLUDE_DIR}/printf_config.h"
|
||||
src/printf/printf.h)
|
||||
target_compile_definitions(printf PRIVATE PRINTF_INCLUDE_CONFIG_H)
|
||||
target_include_directories(printf PRIVATE
|
||||
"$<BUILD_INTERFACE:${GENERATED_INCLUDE_DIR}>"
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/arch/arm/
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/arch/generic
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/obj/src/internal
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/src/include
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/src/internal
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/obj/include
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/mlibc/include
|
||||
)
|
||||
|
||||
# set_property(TARGET printf PROPERTY C_STANDARD 99)
|
||||
set_property(TARGET printf PROPERTY C_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET printf PROPERTY C_EXTENSIONS OFF)
|
||||
|
||||
target_include_directories(
|
||||
printf
|
||||
PRIVATE
|
||||
src
|
||||
PUBLIC
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/src/>"
|
||||
)
|
||||
|
||||
# set_target_properties(printf PROPERTIES
|
||||
# LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
|
||||
# ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
target_compile_options(printf PRIVATE /W4)
|
||||
elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
|
||||
CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
target_compile_options(printf PRIVATE -Wall -Wextra -pedantic -Wconversion)
|
||||
if (ALIAS_STANDARD_FUNCTION_NAMES)
|
||||
# This is important for preventing our aliased implementation
|
||||
# from being replaced, e.g. printf("%c", 'a') by putchar('a');
|
||||
# clang and GCC apparently do this as an optimization
|
||||
target_compile_options(printf PUBLIC -fno-builtin-printf)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
if (UNIX)
|
||||
add_custom_target(printf-sizes
|
||||
COMMAND size -A -t $<TARGET_FILE:printf> > printf_sizes.txt
|
||||
DEPENDS printf
|
||||
BYPRODUCTS printf_sizes.txt
|
||||
COMMENT Prints the sizes of the different sections of the ELF file: text, dat, vss etc.)
|
||||
|
||||
add_custom_target(printf-symbols
|
||||
COMMAND nm --numeric-sort --print-size "$<TARGET_FILE:printf>" > printf_symbols.txt
|
||||
COMMAND bash -c "nm --numeric-sort --print-size $<TARGET_FILE:printf> | c++filt > printf_cpp_symbols.txt"
|
||||
VERBATIM
|
||||
DEPENDS printf
|
||||
BYPRODUCTS printf_symbols.txt printf_cpp_symbols.txt
|
||||
COMMENT Produces lists of the symbols, and C++demangled symbols, inside the library)
|
||||
|
||||
add_custom_target(printf-lst
|
||||
COMMAND objdump --disassemble --line-numbers -S "$<TARGET_FILE:printf>" > printf.list
|
||||
DEPENDS printf
|
||||
BYPRODUCTS printf.lst
|
||||
COMMENT Dissassembles the compiled library into an .lst file)
|
||||
|
||||
endif()
|
||||
target_link_libraries(
|
||||
printf
|
||||
muslc
|
||||
)
|
||||
|
||||
# -------------------------
|
||||
# Installation
|
||||
# -------------------------
|
||||
|
||||
# include(GNUInstallDirs)
|
||||
|
||||
# install(
|
||||
# TARGETS printf
|
||||
# EXPORT printf_export
|
||||
# RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
# ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
# LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
# INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
# )
|
||||
|
||||
# install(
|
||||
# FILES "src/printf/printf.h"
|
||||
# DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/printf"
|
||||
# )
|
||||
|
||||
# install(
|
||||
# EXPORT printf_export
|
||||
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/printf"
|
||||
# NAMESPACE "printf::"
|
||||
# FILE "printf-config.cmake"
|
||||
# )
|
||||
|
||||
# include(CMakePackageConfigHelpers)
|
||||
|
||||
# write_basic_package_version_file(
|
||||
# "printf-config-version.cmake"
|
||||
# VERSION ${PROJECT_VERSION}
|
||||
# COMPATIBILITY SameMinorVersion
|
||||
# )
|
||||
|
||||
# install(
|
||||
# FILES "${CMAKE_CURRENT_BINARY_DIR}/printf-config-version.cmake"
|
||||
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/printf"
|
||||
# )
|
||||
|
||||
|
||||
|
||||
23
mkrtos_user/lib/printf/LICENSE
Normal file
23
mkrtos_user/lib/printf/LICENSE
Normal file
@@ -0,0 +1,23 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Marco Paland
|
||||
Copyright (c) 2021 Eyal Rozenberg
|
||||
|
||||
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.
|
||||
|
||||
275
mkrtos_user/lib/printf/README.md
Normal file
275
mkrtos_user/lib/printf/README.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# Standalone printf/sprintf formatted printing function library
|
||||
|
||||
[](https://github.com/eyalroz/printf/actions/workflows/build_and_test.yml)
|
||||
[](https://raw.githubusercontent.com/eyalroz/printf/master/LICENSE)
|
||||
[](http://github.com/eyalroz/printf/issues)
|
||||
<sup>Parent repository: </sup>[](http://github.com/mpaland/printf/issues)
|
||||
<!-- Can't use Travis - they stopped offering free builds [](https://travis-ci.com/eyalroz/printf) -->
|
||||
<!-- No releases yet... [](https://github.com/mpaland/eyalroz/releases)-->
|
||||
|
||||
|
||||
| Table of contents |
|
||||
|:------------------|
|
||||
|<sub>[Highlights, design goals and the fork](#highlights-design-goals-and-the-fork)<br>[Using the `printf` library in your project](#using-the-printf-library-in-your-project)<br> - [CMake options and preprocessor definitions](#cmake-options-and-preprocessor-definitions)<br>[Library API](#library-api)<br> - [Implemented functions](#implemented-functions)<br> - [Supported Format Specifiers](#supported-format-specifiers)<br> - [Return Value](#return-value)<br>[Contributing](#contributing)<br>[License](#license) </sub>|
|
||||
|
||||
|
||||
This is a small but **fully-loaded** implementation of C's formatted printing family of functions. It was originally designed by Marco Paland, with the primary use case being in embedded systems - where these functions are unavailable, or when one needs to avoid the memory footprint of linking against a full-fledged libc. The library can be made even smaller by partially excluding some of the supported format specifiers during compilation. The library stands alone, with **No external dependencies**.
|
||||
|
||||
It is a fork of the original [mpaland/printf](https://github.com/mpaland/printf) repository by [Marco Paland](https://github.com/mpaland), with multiple bug fixes and a few more features.
|
||||
|
||||
## Highlights, design goals and the fork
|
||||
|
||||
If you use a typical libc's `sprintf()` implementation (or similar function), you are likely to pull in a *lot* of unwanted library definitions and can bloat code size - typically by as much as 20 KiB. Now, there is a boatload of so called 'tiny' `printf()`-family implementations around. So why this one? Or rather, why [mpaland/printf](https://github.com/mpaland/printf), and then why this fork?
|
||||
|
||||
Well, Marco tried out many of the available `printf()` implementations, but was disappointed: Some are not thread-safe; some have indirect dependencies on libc or other libraries, making them inconvenient to build and larger when compiled; some only offer extremely limited flag and specifier support; and some produce non-standard-compiled output, failing many tests no found in the repository's test suite.
|
||||
|
||||
Marco therefore decided to write his own implementation, with the following goals in mind (I've dropped a few relative to his original description):
|
||||
|
||||
- Very small implementation
|
||||
- NO dependencies on other packages or libraries; no multiple compiled objects, just one object file.
|
||||
- Support for all standard specifiers and flags, and all width and precision sub-specifiers (see below).
|
||||
- Support of decimal/floating number representation (with an internal, relatively fast `itoa`/`ftoa` implementation)
|
||||
- Reentrancy and thread-safety; `malloc()` freeness.
|
||||
- Clean, robust code.
|
||||
- Extensive test coverage.
|
||||
- MIT license
|
||||
|
||||
Marco's repository upheld most of these goals - but did not quite make it all of the way. As of mid-2021, it still had many C-standard-non-compliance bugs; the test suite was quite lacking in coverage; some goals were simply discarded (like avoiding global/local-static constants) etc. The repository had become quite popular, but unfortunately, Marco had been otherwise preoccupied; he had not really touched the code in the two years prior; many bug reports were pending, and so were many pull requests from eary adopters who had fixed some of the bugs they had encountered.
|
||||
|
||||
The author of this fork was one of the latercomer bug-reporters-and-PR-authors; and when noticing nothing was moving forward, decided to take up the same goals (sans the discarded ones); and integrate the existing forks and available PRs into a single "consensus fork" which would continue where Marco had left off. Along the way, numerous other issues were observed; the build system was improved; the test suite streamlined and expanded; and other contributors also lent a hand (especially [@mickjc750](https://github.com/mickjc750/)). We are now very close to fully realizing the project goals.
|
||||
|
||||
## Using the `printf` library in your project
|
||||
|
||||
**Use involving CMake:**
|
||||
|
||||
1. Use CMake to configure, build and install the library. Then, in another CMake project, use `find_package(printf)` and make sure the library's install location is in CMake's package search path.
|
||||
2. Use CMake to configure and build the library. This results in the following files:
|
||||
|
||||
* An object code library file (named `printf.a`, or `printf.so`, or `printf.dll` depending on your platform and choice of static vs dynamic linking)
|
||||
* A header file named `printf.h`
|
||||
* (Not strictly necessary) An optional extra header file `printf_config.h` with the build configuration details.
|
||||
|
||||
Now, in your project, include `printf.h` and link against the library file, you're all set: There are no dependencies to satisfy or keep track of.
|
||||
3. Use CMake's `FetchContent` module to obtain the project source code and make it part of your own project's build, e.g.:
|
||||
```
|
||||
FetchContent_Declare(printf_library
|
||||
GIT_REPOSITORY https://github.com/eyalroz/printf.git
|
||||
GIT_TAG v12.34.45 # Replace this with a real available version
|
||||
)
|
||||
FetchContent_MakeAvailable(printf_library)
|
||||
```
|
||||
**Use not involving CMake:**
|
||||
|
||||
4. Copy `printf.c` and `printf.h` into your own project, and compile the source however you see fit. Remember that the library requires compilation with the C99 language standard enabled.
|
||||
5. Include the contents of `printf.c` into your own code - which can be either C or C++. Remember, though, the library is written in the "intersection" of C99 and C++11, so older-standard C programs may not just accept it.
|
||||
|
||||
Whichever way you choose to use the library:
|
||||
|
||||
* You can have this library stand-in for the C standard library's `printf()` family of functions, e.g. provide `snprintf()` instead of `snprintf_()`, by setting an appropriate [preprocessor definition](#cmake-options-and-preprocessor-definitions) during compilation and use.
|
||||
* Speaking of the [preprocessor definitions](#cmake-options-and-preprocessor-definitions) which affect the library's behavior - you have to be consistent in their choice when building and when using the library. (The easiest way to do that is just not to change any of them and accept the reasonable defaults.)
|
||||
* Two of the functions --- `printf_()` and `vprintf_()` --- will only be usable if you implement a `putchar_(char c)` function to underlie them.
|
||||
* **Avoid `sprintf()` in favor of `snprintf()` for safety and security** - and that goes for the standard C library `sprintf()` as well:. `sprintf()` is unaware of the amount of memory allocated for the string it writes into, and will "happily" overflow your buffer; instead of calling it, pass your buffer size to `snprintf()` - and avoid overflow.
|
||||
|
||||
Finally, if you've started using the library in a publicly-available (FOSS or commercial) project, please consider emailing [@eyalroz](https://github.com/eyalroz), or open an [issue](https://github.com/eyalroz/printf/issues/), to announce this.
|
||||
|
||||
|
||||
### CMake options and preprocessor definitions
|
||||
|
||||
Options used both in CMake and in the library source code via a preprocessor define:
|
||||
|
||||
| Option name | Default | Description |
|
||||
|----------------------------------------|---------|--------------|
|
||||
| PRINTF_ALIAS_STANDARD_FUNCTION_NAMES | NO | Alias the standard library function names (`printf()`, `sprintf()` etc.) to the library's functions.<br>**Note:** If you build the library with this option turned on, you must also have written<br>`#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 1`<br>before including the `printf.h` header. |
|
||||
| PRINTF_INTEGER_BUFFER_SIZE | 32 | ntoa (integer) conversion buffer size. This must be big enough to hold one converted numeric number _including_ leading zeros, normally 32 is a sufficient value. Created on the stack. |
|
||||
| PRINTF_DECIMAL_BUFFER_SIZE | 32 | ftoa (float) conversion buffer size. This must be big enough to hold one converted float number _including_ leading zeros, normally 32 is a sufficient value. Created on the stack. |
|
||||
| PRINTF_DEFAULT_FLOAT_PRECISION | 6 | Define the default floating point precision|
|
||||
| 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 |
|
||||
| PRINTF_SUPPORT_DECIMAL_SPECIFIERS | YES | Support decimal notation floating-point conversion specifiers (%f, %F) |
|
||||
| PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS | YES | Support exponential floating point format conversion specifiers (%e, %E, %g, %G) |
|
||||
| SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS | YES | Support the 'I' + bit size integer specifiers (%I8, %I16, %I32, %I64) as in Microsoft Visual C++ |
|
||||
| PRINTF_SUPPORT_WRITEBACK_SPECIFIER | YES | Support the length write-back specifier (%n) |
|
||||
| PRINTF_SUPPORT_LONG_LONG | YES | Support long long integral types (allows for the ll length modifier and affects %p) |
|
||||
|
||||
Within CMake, these options lack the `PRINTF_` prefix.
|
||||
|
||||
CMake-only options:
|
||||
|
||||
| Option name | Default | Description |
|
||||
|----------------------------------------|---------|--------------|
|
||||
| PRINTF_BUILD_STATIC_LIBRARY | 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) |
|
||||
|
||||
Source-only options:
|
||||
|
||||
| Option name | Default | Description |
|
||||
|----------------------------------------|---------|--------------|
|
||||
| PRINTF_INCLUDE_CONFIG_H | NO | Triggers inclusing by `printf.c` of a "printf_config.h" file, which in turn contains the values of all of the CMake-and-preprocessor options above. A CMake build of the library uses this mechanism to apply the user's choice of options, so it can't have the mechanism itself as an option. |
|
||||
|
||||
Note: The preprocessor definitions are taken into account when compiling `printf.c`, _not_ when using the compiled library by including `printf.h`.
|
||||
|
||||
## Library API
|
||||
|
||||
### Implemented functions
|
||||
|
||||
The library offers the following, with the same signatures as in the standard C library (plus an extra underscore):
|
||||
```
|
||||
int printf_(const char* format, ...);
|
||||
int sprintf_(char* s, const char* format, ...);
|
||||
int vsprintf_(char* s, const char* format, va_list arg);
|
||||
int snprintf_(char* s, size_t n, const char* format, ...);
|
||||
int vsnprintf_(char* s, size_t n, const char* format, va_list arg);
|
||||
int vprintf_(const char* format, va_list arg);
|
||||
```
|
||||
Note that `printf()` and `vprintf()` don't actually write anything on their own: In addition to their parameters, you must provide them with a lower-level `putchar_()` function which they can call for actual printing. This is part of this library's independence: It is isolated from dealing with console/serial output, files etc.
|
||||
|
||||
Two additional functions are provided beyond those available in the standard library:
|
||||
```
|
||||
int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...);
|
||||
int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg);
|
||||
```
|
||||
These higher-order functions allow for better flexibility of use: You can decide to do different things with the individual output characters: Encode them, compress them, filter them, append them to a buffer or a file, or just discard them. This is achieved by you passing a pointer to your own state information - through `(v)fctprintf()` and all the way to your own `out()` function.
|
||||
|
||||
#### "... but I don't like the underscore-suffix names :-("
|
||||
|
||||
You can [configure](#CMake-options-and-preprocessor-definitions) the library to alias the standard library's names, in which case it exposes `printf()`, `sprintf()`, `vsprintf()` and so on.
|
||||
|
||||
If you alias the standard library function names, *be careful of GCC/clang's `printf()` optimizations!*: GCC and clang recognize patterns such as `printf("%s", str)` or `printf("%c", ch)`, and perform a "strength reduction" of sorts by invoking `puts(stdout, str)` or `putchar(ch)`. If you enable the `PRINTF_ALIAS_STANDARD_FUNCTION_NAMES` option (see below), and do not ensure your code is compiled with the `-fno-builtin-printf` option - you might inadvertantly pull in the standard library implementation - either succeeding and depending on it, or failing with a linker error. When using `printf` as a CMake imported target, that should already be arranged for, but again: Double-check.
|
||||
|
||||
<br>
|
||||
|
||||
Alternatively, you can write short wrappers with your preferred names. This is completely trivial with the v-functions, e.g.:
|
||||
```
|
||||
int my_vprintf(const char* format, va_list va)
|
||||
{
|
||||
return vprintf_(format, va);
|
||||
}
|
||||
```
|
||||
and is still pretty straightforward with the variable-number-of-arguments functions:
|
||||
```
|
||||
int my_sprintf(char* buffer, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
const int ret = vsprintf_(buffer, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
### Supported Format Specifiers
|
||||
|
||||
A format specifier follows this prototype: `%[flags][width][.precision][length]type`
|
||||
The following format specifiers are 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 |
|
||||
| n | None; number of characters produced so far written to argument pointer |
|
||||
|
||||
Notes:
|
||||
|
||||
* The `%a` specifier for hexadecimal floating-point notation (introduced in C99 and C++11) is _not_ currently supported.
|
||||
* If you want to print the percent sign (`%`, US-ASCII character 37), use "%%" in your format string.
|
||||
* The C standard library's `printf()`-style functions don't accept `float` arguments, only `double`'s; that is true for this library as well. `float`'s get converted to `double`'s.
|
||||
|
||||
#### 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.<br>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.<br>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). |
|
||||
|
||||
|
||||
#### Width Specifiers
|
||||
|
||||
| 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. |
|
||||
|
||||
|
||||
#### Precision Specifiers
|
||||
|
||||
| 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.<br>For f and F specifiers: this is the number of digits to be printed after the decimal point. **By default, this is 6, and a maximum is defined when building the library**.<br>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.<br>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. |
|
||||
|
||||
|
||||
#### Length modifiers
|
||||
|
||||
The length sub-specifier modifies the length of the data type.
|
||||
|
||||
| Length | With `d`, `i` | With `u`,`o`,`x`, `X` | Support enabled by... |
|
||||
|--------|---------------------------|------------------------|---------------------------------------|
|
||||
| (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 | signed version of size_t | size_t | |
|
||||
| t | ptrdiff_t | ptrdiff_t | |
|
||||
| I8 | int8_t | uint8_t | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS |
|
||||
| I16 | int16_t | uint16_t | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS |
|
||||
| I32 | int32_t | uint32_t | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS |
|
||||
| I64 | int64_t | uint64_t | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS |
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
* The `L` modifier, for `long double`, is not currently supported.
|
||||
* A `"%zd"` or `"%zi"` takes a signed integer of the same size as `size_t`.
|
||||
* The implementation currently assumes each of `intmax_t`, signed `size_t`, and `ptrdiff_t` has the same size as `long int` or as `long long int`. If this is not the case for your platform, please open an issue.
|
||||
* The `Ixx` length modifiers are not in the C (nor C++) standard, but are somewhat popular, as it makes it easier to handle integer types of specific size. One must specify the argument size in bits immediately after the `I`. The printing is "integer-promotion-safe", i.e. the fact that an `int8_t` may actually be passed in promoted into a larger `int` will not prevent it from being printed using its origina value.
|
||||
|
||||
### Return Value
|
||||
|
||||
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 with a terminating NUL.
|
||||
If any error is encountered, `-1` is returned.
|
||||
|
||||
If `NULL` is passed for the `buffer` parameter, nothing is written, but the formatted length is returned. For example:
|
||||
```C
|
||||
int length = sprintf(NULL, "Hello, world"); // length is set to 12
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
The following assumes Marco Paland's original repository remains mostly-inactive in terms of commits.
|
||||
|
||||
0. Give this repository a :star: (even if you've already starred the original repository).
|
||||
1. Create an [issue](https://github.com/eyalroz/issues) and describe your idea. Make sure it is in line with the library's design goals.
|
||||
2. Fork the repository
|
||||
3. Create your feature branch (`git checkout -b my-new-feature`).
|
||||
4. Implement your feature/idea; 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 -a -m "Added some feature"`)
|
||||
5. Publish the branch (`git push origin my-new-feature`)
|
||||
6. Create a new pull request against this repository. Note: Please don't create a PR without a related issue.
|
||||
|
||||
I try to attend to issues and PRs promptly.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This library is published under the terms of the [MIT license](http://www.opensource.org/licenses/MIT).
|
||||
|
||||
19
mkrtos_user/lib/printf/printf_config.h.in
Normal file
19
mkrtos_user/lib/printf/printf_config.h.in
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#ifndef PRINTF_CONFIG_H_
|
||||
#define PRINTF_CONFIG_H_
|
||||
|
||||
#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS @PRINTF_SUPPORT_DECIMAL_SPECIFIERS@
|
||||
#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS @PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS@
|
||||
#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER @PRINTF_SUPPORT_WRITEBACK_SPECIFIER@
|
||||
#define PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS @PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS@
|
||||
#define PRINTF_SUPPORT_LONG_LONG @PRINTF_SUPPORT_LONG_LONG@
|
||||
#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES @PRINTF_ALIAS_STANDARD_FUNCTION_NAMES@
|
||||
|
||||
#define PRINTF_INTEGER_BUFFER_SIZE @PRINTF_INTEGER_BUFFER_SIZE@
|
||||
#define PRINTF_DECIMAL_BUFFER_SIZE @PRINTF_DECIMAL_BUFFER_SIZE@
|
||||
#define PRINTF_DEFAULT_FLOAT_PRECISION @DEFAULT_FLOAT_PRECISION@
|
||||
#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL @MAX_INTEGRAL_DIGITS_FOR_DECIMAL@
|
||||
#define PRINTF_LOG10_TAYLOR_TERMS @LOG10_TAYLOR_TERMS@
|
||||
|
||||
#endif // PRINTF_CONFIG_H_
|
||||
|
||||
7
mkrtos_user/lib/printf/src/printf/mkrtos_putchar.c
Normal file
7
mkrtos_user/lib/printf/src/printf/mkrtos_putchar.c
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
#include "printf.h"
|
||||
#include "cons_cli.h"
|
||||
void putchar_(char character)
|
||||
{
|
||||
cons_write((const uint8_t *)&character, 1);
|
||||
}
|
||||
1421
mkrtos_user/lib/printf/src/printf/printf.c
Normal file
1421
mkrtos_user/lib/printf/src/printf/printf.c
Normal file
File diff suppressed because it is too large
Load Diff
206
mkrtos_user/lib/printf/src/printf/printf.h
Normal file
206
mkrtos_user/lib/printf/src/printf/printf.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
|
||||
* 2021-2022, Haifa, Palestine/Israel
|
||||
* @author (c) Marco Paland (info@paland.com)
|
||||
* 2014-2019, PALANDesign Hannover, Germany
|
||||
*
|
||||
* @note Others have made smaller contributions to this file: see the
|
||||
* contributors page at https://github.com/eyalroz/printf/graphs/contributors
|
||||
* or ask one of the authors.
|
||||
*
|
||||
* @brief Small stand-alone implementation of the printf family of functions
|
||||
* (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with
|
||||
* a very limited resources.
|
||||
*
|
||||
* @note the implementations are thread-safe; re-entrant; use no functions from
|
||||
* the standard library; and do not dynamically allocate any memory.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
|
||||
#ifndef PRINTF_H_
|
||||
#define PRINTF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <cstdarg>
|
||||
# include <cstddef>
|
||||
extern "C" {
|
||||
#else
|
||||
# include <stdarg.h>
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
# if ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4)
|
||||
# define ATTR_PRINTF(one_based_format_index, first_arg) \
|
||||
__attribute__((format(gnu_printf, (one_based_format_index), (first_arg))))
|
||||
# else
|
||||
# define ATTR_PRINTF(one_based_format_index, first_arg) \
|
||||
__attribute__((format(printf, (one_based_format_index), (first_arg))))
|
||||
# endif
|
||||
# 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
|
||||
|
||||
#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
||||
#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0
|
||||
#endif
|
||||
|
||||
// #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
||||
# define printf_ mk_printf
|
||||
# define sprintf_ mk_sprintf
|
||||
# define vsprintf_ mk_vsprintf
|
||||
# define snprintf_ mk_snprintf
|
||||
# define vsnprintf_ mk_vsnprintf
|
||||
# define vprintf_ mk_vprintf
|
||||
// #endif
|
||||
|
||||
|
||||
// If you want to include this implementation file directly rather than
|
||||
// link against, this will let you control the functions' visibility,
|
||||
// e.g. make them static so as not to clash with other objects also
|
||||
// using them.
|
||||
#ifndef PRINTF_VISIBILITY
|
||||
#define PRINTF_VISIBILITY
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Prints/send a single character to some opaque output entity
|
||||
*
|
||||
* @note This function is not implemented by the library, only declared; you must provide an
|
||||
* implementation if you wish to use the @ref printf / @ref vprintf function (and possibly
|
||||
* for linking against the library, if your toolchain does not support discarding unused functions)
|
||||
*
|
||||
* @note The output could be as simple as a wrapper for the `write()` system call on a Unix-like
|
||||
* system, or even libc's @ref putchar , for replicating actual functionality of libc's @ref printf
|
||||
* function; but on an embedded system it may involve interaction with a special output device,
|
||||
* like a UART, etc.
|
||||
*
|
||||
* @note in libc's @ref putchar, the parameter type is an int; this was intended to support the
|
||||
* representation of either a proper character or EOF in a variable - but this is really not
|
||||
* meaningful to pass into @ref putchar and is discouraged today. See further discussion in:
|
||||
* @link https://stackoverflow.com/q/17452847/1593077
|
||||
*
|
||||
* @param c the single character to print
|
||||
*/
|
||||
PRINTF_VISIBILITY
|
||||
void putchar_(char c);
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of the C standard's printf/vprintf
|
||||
*
|
||||
* @note you must implement a @ref putchar_ function for using this function - it invokes @ref putchar_
|
||||
* rather than directly performing any I/O (which insulates it from any dependence on the operating system
|
||||
* and external libraries).
|
||||
*
|
||||
* @param format A string specifying the format of the output, with %-marked specifiers of how to interpret
|
||||
* additional arguments.
|
||||
* @param arg Additional arguments to the function, one for each %-specifier in @p format string
|
||||
* @return The number of characters written into @p s, not counting the terminating null character
|
||||
*/
|
||||
///@{
|
||||
PRINTF_VISIBILITY
|
||||
int printf_(const char* format, ...) ATTR_PRINTF(1, 2);
|
||||
PRINTF_VISIBILITY
|
||||
int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1);
|
||||
///@}
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of the C standard's sprintf/vsprintf
|
||||
*
|
||||
* @note For security considerations (the potential for exceeding the buffer bounds), please consider using
|
||||
* the size-constrained variant, @ref snprintf / @ref vsnprintf , instead.
|
||||
*
|
||||
* @param s An array in which to store the formatted string. It must be large enough to fit the formatted
|
||||
* output!
|
||||
* @param format A string specifying the format of the output, with %-marked specifiers of how to interpret
|
||||
* additional arguments.
|
||||
* @param arg Additional arguments to the function, one for each specifier in @p format
|
||||
* @return The number of characters written into @p s, not counting the terminating null character
|
||||
*/
|
||||
///@{
|
||||
PRINTF_VISIBILITY
|
||||
int sprintf_(char* s, const char* format, ...) ATTR_PRINTF(2, 3);
|
||||
PRINTF_VISIBILITY
|
||||
int vsprintf_(char* s, const char* format, va_list arg) ATTR_VPRINTF(2);
|
||||
///@}
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of the C standard's snprintf/vsnprintf
|
||||
*
|
||||
* @param s An array in which to store the formatted string. It must be large enough to fit either the
|
||||
* entire formatted output, or at least @p n characters. Alternatively, it can be NULL, in which case
|
||||
* nothing will be printed, and only the number of characters which _could_ have been printed is
|
||||
* tallied and returned.
|
||||
* @param n The maximum number of characters to write to the array, including a terminating null character
|
||||
* @param format A string specifying the format of the output, with %-marked specifiers of how to interpret
|
||||
* additional arguments.
|
||||
* @param arg Additional arguments to the function, one for each specifier in @p format
|
||||
* @return The number of characters that COULD have been written into @p s, not counting the terminating
|
||||
* null character. A value equal or larger than @p n indicates truncation. Only when the returned value
|
||||
* is non-negative and less than @p n, the null-terminated string has been fully and successfully printed.
|
||||
*/
|
||||
///@{
|
||||
PRINTF_VISIBILITY
|
||||
int snprintf_(char* s, size_t count, const char* format, ...) ATTR_PRINTF(3, 4);
|
||||
PRINTF_VISIBILITY
|
||||
int vsnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3);
|
||||
///@}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* printf/vprintf with user-specified output function
|
||||
*
|
||||
* An alternative to @ref printf_, in which the output function is specified dynamically
|
||||
* (rather than @ref putchar_ being used)
|
||||
*
|
||||
* @param out An output function which takes one character and a type-erased additional parameters
|
||||
* @param extra_arg The type-erased argument to pass to the output function @p out with each call
|
||||
* @param format A string specifying the format of the output, with %-marked specifiers of how to interpret
|
||||
* additional arguments.
|
||||
* @param arg Additional arguments to the function, one for each specifier in @p format
|
||||
* @return The number of characters for which the output f unction was invoked, not counting the terminating null character
|
||||
*
|
||||
*/
|
||||
PRINTF_VISIBILITY
|
||||
int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) ATTR_PRINTF(3, 4);
|
||||
PRINTF_VISIBILITY
|
||||
int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
// #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
||||
# undef printf_
|
||||
# undef sprintf_
|
||||
# undef vsprintf_
|
||||
# undef snprintf_
|
||||
# undef vsnprintf_
|
||||
# undef vprintf_
|
||||
// #endif
|
||||
|
||||
#endif // PRINTF_H_
|
||||
112
mkrtos_user/lib/printf/test/CMakeLists.txt
Normal file
112
mkrtos_user/lib/printf/test/CMakeLists.txt
Normal file
@@ -0,0 +1,112 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
set(test_targets autotest)
|
||||
if (NOT ALIAS_STANDARD_FUNCTION_NAMES)
|
||||
list(APPEND test_targets test_suite)
|
||||
endif()
|
||||
|
||||
option(TEST_WITH_NON_STANDARD_FORMAT_STRINGS "Include tests using non-standard-compliant format strings?" ON)
|
||||
# ... don't worry, we'll suppress the compiler warnings for those.
|
||||
|
||||
foreach(tgt ${test_targets})
|
||||
add_executable(${tgt} "${tgt}.cpp")
|
||||
set_target_properties(
|
||||
${tgt}
|
||||
PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
CXX_EXTENSIONS NO
|
||||
)
|
||||
|
||||
if (TEST_WITH_NON_STANDARD_FORMAT_STRINGS)
|
||||
target_compile_definitions(${tgt} PRIVATE TEST_WITH_NON_STANDARD_FORMAT_STRINGS)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_executable(aliasing "aliasing.c")
|
||||
set_target_properties(
|
||||
${tgt}
|
||||
PROPERTIES
|
||||
C_STANDARD 11
|
||||
C_STANDARD_REQUIRED YES
|
||||
C_EXTENSIONS NO
|
||||
)
|
||||
list(APPEND test_targets aliasing)
|
||||
|
||||
foreach(tgt ${test_targets})
|
||||
get_target_property(tgt_lang ${tgt} LANGUAGE)
|
||||
if (tgt_lang EQUAL "CXX")
|
||||
set(tgt_compiler_id ${CMAKE_CXX_COMPILER_ID})
|
||||
else() # it's C
|
||||
set(tgt_compiler_id ${CMAKE_C_COMPILER_ID})
|
||||
endif()
|
||||
|
||||
if (tgt_compiler_id STREQUAL "MSVC")
|
||||
target_compile_options(${tgt} PRIVATE /W4)
|
||||
elseif (tgt_compiler_id STREQUAL "GNU" OR
|
||||
tgt_compiler_id STREQUAL "Clang")
|
||||
# lots of warnings and all warnings as errors
|
||||
target_compile_options(${tgt} PRIVATE
|
||||
-g
|
||||
-Wall
|
||||
-Wextra
|
||||
-pedantic
|
||||
-Wundef
|
||||
-Wsign-conversion
|
||||
-Wuninitialized
|
||||
-Wshadow
|
||||
-Wunreachable-code
|
||||
-Wswitch-default
|
||||
-Wswitch
|
||||
-Wcast-align
|
||||
-Wmissing-include-dirs
|
||||
-Winit-self
|
||||
-Wdouble-promotion
|
||||
-gdwarf-2
|
||||
-fno-exceptions
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
-fverbose-asm
|
||||
-Wunused-parameter
|
||||
)
|
||||
target_compile_options(${tgt} PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
|
||||
)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(${tgt} PRIVATE -ffat-lto-objects)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
|
||||
if (NOT ALIAS_STANDARD_FUNCTION_NAMES)
|
||||
# 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}"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/../src/>"
|
||||
)
|
||||
add_test(
|
||||
NAME "${PROJECT_NAME}.test_suite"
|
||||
COMMAND "test_suite" # ${TEST_RUNNER_PARAMS}
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(autotest PRIVATE printf)
|
||||
target_include_directories(autotest PRIVATE "$<BUILD_INTERFACE:${GENERATED_INCLUDE_DIR}>")
|
||||
|
||||
add_test(
|
||||
NAME "${PROJECT_NAME}.aliasing"
|
||||
COMMAND "aliasing"
|
||||
)
|
||||
target_link_libraries(aliasing PRIVATE printf)
|
||||
target_include_directories(aliasing PRIVATE "$<BUILD_INTERFACE:${GENERATED_INCLUDE_DIR}>")
|
||||
|
||||
# Not running autotest by default - it's randomized after all.
|
||||
|
||||
52
mkrtos_user/lib/printf/test/aliasing.c
Normal file
52
mkrtos_user/lib/printf/test/aliasing.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <printf_config.h>
|
||||
#include <printf/printf.h>
|
||||
|
||||
int strcmp_(const char* lhs, const char* rhs)
|
||||
{
|
||||
while (1) {
|
||||
char lhs_c = *lhs++;
|
||||
char rhs_c = *rhs++;
|
||||
if (lhs_c != rhs_c) { return lhs_c - rhs_c; }
|
||||
if (lhs_c == '\0') { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
enum { BufferSize = 100 };
|
||||
char buffer[BufferSize];
|
||||
|
||||
void putchar_(char c)
|
||||
{
|
||||
for(char* ptr = buffer; ptr - buffer < BufferSize; ptr++) {
|
||||
if (*ptr == '\0') {
|
||||
*ptr++ = c;
|
||||
*ptr++ = '\0';
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear_buffer(void)
|
||||
{
|
||||
for(int i = 0; i < BufferSize; i++) {
|
||||
buffer[i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
||||
clear_buffer();
|
||||
printf("printf'ing an integer: %d and a string: %s\n", 12, "Hello world");
|
||||
const char* expected = "printf'ing an integer: 12 and a string: Hello world\n";
|
||||
if (strcmp_(buffer, expected) != 0) {
|
||||
return(-1);
|
||||
}
|
||||
clear_buffer();
|
||||
sprintf(buffer, "sprintf'ing an integer: %d and a string: %s\n", 34, "quick brown fox");
|
||||
expected = "sprintf'ing an integer: 34 and a string: quick brown fox\n";
|
||||
if (strcmp_(buffer, expected) != 0) {
|
||||
return(-1);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
608
mkrtos_user/lib/printf/test/autotest.cpp
Normal file
608
mkrtos_user/lib/printf/test/autotest.cpp
Normal file
@@ -0,0 +1,608 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#ifdef _WIN32
|
||||
#include "getopt.h"
|
||||
#else
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#include "printf_config.h"
|
||||
#include "../src/printf/printf.h"
|
||||
|
||||
#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
||||
#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0
|
||||
#endif
|
||||
|
||||
#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
||||
# define printf_ printf
|
||||
# define sprintf_ sprintf
|
||||
# define vsprintf_ vsprintf
|
||||
# define snprintf_ snprintf
|
||||
# define vsnprintf_ vsnprintf
|
||||
# define vprintf_ vprintf
|
||||
#endif
|
||||
|
||||
|
||||
//*******************************************************
|
||||
// Defines
|
||||
//*******************************************************
|
||||
|
||||
#define min(a,b) ( (a) < (b) ? (a) : (b) )
|
||||
#define MAIN_TITLE "autotest"
|
||||
|
||||
#define BUF_SIZE 100
|
||||
#define WIDTH_MAX 25
|
||||
|
||||
// range for float testing
|
||||
#define FLOAT_TST_MIN 1E-5
|
||||
#define FLOAT_TST_MAX 1E5
|
||||
|
||||
#define PRECISION_LIMIT_DEFAULT 17
|
||||
|
||||
struct cmdopt_struct
|
||||
{
|
||||
bool i;
|
||||
bool x;
|
||||
bool o;
|
||||
bool u;
|
||||
bool f;
|
||||
bool e;
|
||||
bool g;
|
||||
bool left_justify;
|
||||
bool zero_pad;
|
||||
bool hash;
|
||||
bool width;
|
||||
bool prec;
|
||||
int prec_max;
|
||||
};
|
||||
|
||||
// Valid short options
|
||||
#define VALIDOPTS "ixoufeglz#wp::ah"
|
||||
|
||||
#define MSG_USAGE "\n\
|
||||
Usage: " MAIN_TITLE " [OPTION/s]\n\
|
||||
Compare randomly formatted strings with the stdio printf()\n\
|
||||
Matching strings are output to stdout\n\
|
||||
Errors are output to stderr\r\n\
|
||||
\n\
|
||||
-i test %%i \n\
|
||||
-x test %%x\n\
|
||||
-o test %%o\n\
|
||||
-u test %%u\n\
|
||||
-f test %%f\n\
|
||||
-e test %%e\n\
|
||||
-g test %%g (float falling back to exp)\n\
|
||||
-l test left justification\r\n\
|
||||
-z test zero padding\n\
|
||||
-# test prepending base 0 0x\n\
|
||||
-w test width specifier\n\
|
||||
-p <limit> test precision specifier, with an optional limit for %%f %%e %%g\n\
|
||||
-a test all of the above, with a default precision limit of %i for %%f %%e %%g\n\
|
||||
\n\
|
||||
-h show these options\n\
|
||||
\n\
|
||||
Examples:\n\
|
||||
" MAIN_TITLE " -a test with all options, showing all passes and failures\n\
|
||||
" MAIN_TITLE " -a 1>/dev/null test with all options, showing only errors, with stdout > null\n\
|
||||
" MAIN_TITLE " -ap14 1>/dev/null test with all options and precision limit of 14 for %%f %%e %%g, showing only errors, with stdout > null\n\
|
||||
" MAIN_TITLE " -ixou 1>/dev/null test only %%i %%x %%o %%u, showing only errors, with stdout > null\n\
|
||||
\n\
|
||||
"
|
||||
|
||||
//*******************************************************
|
||||
// Variables
|
||||
//*******************************************************
|
||||
|
||||
static struct cmdopt_struct opts;
|
||||
|
||||
//*******************************************************
|
||||
// Prototypes
|
||||
//*******************************************************
|
||||
|
||||
static bool parse_options(int argc, char *argv[]);
|
||||
static void run_tests(void);
|
||||
|
||||
static void test_i(void);
|
||||
static void test_x(void);
|
||||
static void test_o(void);
|
||||
static void test_u(void);
|
||||
static void test_f(void);
|
||||
static void test_e(void);
|
||||
static void test_g(void);
|
||||
|
||||
float rand_float(float a, float b);
|
||||
|
||||
//*******************************************************
|
||||
// Functions
|
||||
//*******************************************************
|
||||
|
||||
void putchar_(char character)
|
||||
{
|
||||
(void)character;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(!parse_options(argc, argv))
|
||||
printf(MSG_USAGE, PRECISION_LIMIT_DEFAULT);
|
||||
else
|
||||
run_tests();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool parse_options(int argc, char *argv[])
|
||||
{
|
||||
char c;
|
||||
bool gotopt = false;
|
||||
|
||||
opts.i = false;
|
||||
opts.x = false;
|
||||
opts.o = false;
|
||||
opts.u = false;
|
||||
opts.f = false;
|
||||
opts.e = false;
|
||||
opts.g = false;
|
||||
opts.left_justify = false;
|
||||
opts.zero_pad = false;
|
||||
opts.hash = false;
|
||||
opts.width = false;
|
||||
opts.prec = false;
|
||||
opts.prec_max = -1;
|
||||
|
||||
while ((c = getopt (argc, argv, VALIDOPTS)) != -1)
|
||||
{
|
||||
gotopt = true;
|
||||
if(c == 'i')
|
||||
opts.i = true;
|
||||
else if(c == 'x')
|
||||
opts.x = true;
|
||||
else if(c == 'o')
|
||||
opts.o = true;
|
||||
else if(c == 'u')
|
||||
opts.u = true;
|
||||
else if(c == 'f')
|
||||
opts.f = true;
|
||||
else if(c == 'e')
|
||||
opts.e = true;
|
||||
else if(c == 'g')
|
||||
opts.g = true;
|
||||
else if(c == 'l')
|
||||
opts.left_justify = true;
|
||||
else if(c == 'z')
|
||||
opts.zero_pad = true;
|
||||
else if(c == '#')
|
||||
opts.hash = true;
|
||||
else if(c == 'w')
|
||||
opts.width = true;
|
||||
else if(c == 'p')
|
||||
{
|
||||
opts.prec = true;
|
||||
if(optarg)
|
||||
opts.prec_max = atoi(optarg);
|
||||
}
|
||||
else if(c == 'a')
|
||||
{
|
||||
opts.i = true;
|
||||
opts.x = true;
|
||||
opts.o = true;
|
||||
opts.u = true;
|
||||
opts.f = true;
|
||||
opts.e = true;
|
||||
opts.g = true;
|
||||
opts.left_justify = true;
|
||||
opts.zero_pad = true;
|
||||
opts.hash = true;
|
||||
opts.width = true;
|
||||
opts.prec = true;
|
||||
}
|
||||
else if(c != 'h')
|
||||
{
|
||||
fprintf(stderr, "Unknown option\n");
|
||||
assert(false);
|
||||
};
|
||||
};
|
||||
|
||||
if(opts.prec_max == -1)
|
||||
opts.prec_max = PRECISION_LIMIT_DEFAULT;
|
||||
|
||||
return gotopt;
|
||||
}
|
||||
|
||||
static void run_tests(void)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(opts.i)
|
||||
test_i();
|
||||
if(opts.x)
|
||||
test_x();
|
||||
if(opts.o)
|
||||
test_o();
|
||||
if(opts.u)
|
||||
test_u();
|
||||
if(opts.f)
|
||||
test_f();
|
||||
if(opts.e)
|
||||
test_e();
|
||||
if(opts.g)
|
||||
test_g();
|
||||
};
|
||||
}
|
||||
|
||||
static void test_i(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
int value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
if(rand()&1)
|
||||
strcat(fmt_buf, "+");
|
||||
else if(rand()&1)
|
||||
strcat(fmt_buf, " ");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "i");
|
||||
value = rand();
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
if(rand()&1)
|
||||
value*=-1;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %i\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
static void test_x(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
unsigned int value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
if(rand()&1 && opts.hash)
|
||||
strcat(fmt_buf, "#");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "x");
|
||||
value = (unsigned)rand();
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %#x\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
static void test_o(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
unsigned int value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
if(rand()&1 && opts.hash)
|
||||
strcat(fmt_buf, "#");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "o");
|
||||
value = (unsigned)rand();
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %o\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
static void test_u(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
unsigned int value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "u");
|
||||
value = (unsigned)rand();
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %u\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
static void test_f(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
double value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
if(rand()&1 && opts.hash)
|
||||
strcat(fmt_buf, "#");
|
||||
if(rand()&1)
|
||||
strcat(fmt_buf, "+");
|
||||
else if(rand()&1)
|
||||
strcat(fmt_buf, " ");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width, opts.prec_max));
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "f");
|
||||
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
else
|
||||
value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX);
|
||||
|
||||
if(rand()&1)
|
||||
value*=-1;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
static void test_e(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
double value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
if(rand()&1 && opts.hash)
|
||||
strcat(fmt_buf, "#");
|
||||
if(rand()&1)
|
||||
strcat(fmt_buf, "+");
|
||||
else if(rand()&1)
|
||||
strcat(fmt_buf, " ");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width,opts.prec_max));
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "e");
|
||||
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
else
|
||||
value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX);
|
||||
|
||||
if(rand()&1)
|
||||
value*=-1;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
static void test_g(void)
|
||||
{
|
||||
FILE* dst = stdout;
|
||||
char fmt_buf[BUF_SIZE];
|
||||
char std_buf[BUF_SIZE];
|
||||
char tst_buf[BUF_SIZE];
|
||||
bool width_flag;
|
||||
int width;
|
||||
double value;
|
||||
|
||||
strcpy(fmt_buf, "%");
|
||||
if(rand()&1 && opts.left_justify)
|
||||
strcat(fmt_buf, "-");
|
||||
if(rand()&1 && opts.hash)
|
||||
strcat(fmt_buf, "#");
|
||||
if(rand()&1)
|
||||
strcat(fmt_buf, "+");
|
||||
else if(rand()&1)
|
||||
strcat(fmt_buf, " ");
|
||||
|
||||
width_flag = (rand()&1 && opts.width);
|
||||
|
||||
width = 1+rand()%WIDTH_MAX;
|
||||
if(width_flag)
|
||||
{
|
||||
if(rand()&1 && opts.zero_pad)
|
||||
strcat(fmt_buf, "0");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
||||
};
|
||||
|
||||
if(rand()&1 && opts.prec)
|
||||
{
|
||||
strcat(fmt_buf, ".");
|
||||
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width,opts.prec_max));
|
||||
};
|
||||
|
||||
strcat(fmt_buf, "g");
|
||||
|
||||
if(rand()&1)
|
||||
value = 0;
|
||||
else
|
||||
value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX);
|
||||
|
||||
if(rand()&1)
|
||||
value*=-1;
|
||||
|
||||
sprintf(std_buf, fmt_buf, value);
|
||||
sprintf_(tst_buf, fmt_buf, value);
|
||||
|
||||
if(strcmp(std_buf,tst_buf))
|
||||
dst = stderr;
|
||||
|
||||
fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value);
|
||||
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
||||
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
||||
}
|
||||
|
||||
float rand_float(float a, float b)
|
||||
{
|
||||
float random = ((float) rand()) / (float) RAND_MAX;
|
||||
float diff = b - a;
|
||||
float r = random * diff;
|
||||
return a + r;
|
||||
}
|
||||
17937
mkrtos_user/lib/printf/test/catch.hpp
Normal file
17937
mkrtos_user/lib/printf/test/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
653
mkrtos_user/lib/printf/test/getopt.h
Normal file
653
mkrtos_user/lib/printf/test/getopt.h
Normal file
@@ -0,0 +1,653 @@
|
||||
#ifndef __GETOPT_H__
|
||||
/**
|
||||
* DISCLAIMER
|
||||
* This file is part of the mingw-w64 runtime package.
|
||||
*
|
||||
* The mingw-w64 runtime package and its code is distributed in the hope that it
|
||||
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
|
||||
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
|
||||
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma warning(disable:4996);
|
||||
|
||||
#define __GETOPT_H__
|
||||
|
||||
/* All the headers include this file. */
|
||||
#include <crtdefs.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = '?'; /* character checked for validity */
|
||||
#undef optreset /* see getopt.h */
|
||||
#define optreset __mingw_optreset
|
||||
int optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
#endif
|
||||
|
||||
//extern int optind; /* index of first non-option in argv */
|
||||
//extern int optopt; /* single option character, as parsed */
|
||||
//extern int opterr; /* flag to enable built-in diagnostics... */
|
||||
// /* (user may set to zero, to suppress) */
|
||||
//
|
||||
//extern char *optarg; /* pointer to argument of current option */
|
||||
|
||||
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER (int)1
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
#define __progname __argv[0]
|
||||
#else
|
||||
extern char __declspec(dllimport) *__progname;
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
static char EMSG[] = "";
|
||||
#else
|
||||
#define EMSG ""
|
||||
#endif
|
||||
|
||||
static int getopt_internal(int, char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int parse_long_options(char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char * const *);
|
||||
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
|
||||
/* XXX: set optreset to 1 rather than these two */
|
||||
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||
|
||||
/* Error messages */
|
||||
static const char recargchar[] = "option requires an argument -- %c";
|
||||
static const char recargstring[] = "option requires an argument -- %s";
|
||||
static const char ambig[] = "ambiguous option -- %.*s";
|
||||
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||
static const char illoptchar[] = "unknown option -- %c";
|
||||
static const char illoptstring[] = "unknown option -- %s";
|
||||
|
||||
static void
|
||||
_vwarnx(const char *fmt,va_list ap)
|
||||
{
|
||||
(void)fprintf(stderr,"%s: ",__progname);
|
||||
if (fmt != NULL)
|
||||
(void)vfprintf(stderr,fmt,ap);
|
||||
(void)fprintf(stderr,"\n");
|
||||
}
|
||||
|
||||
static void
|
||||
warnx(const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
_vwarnx(fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
((char **) nargv)[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
((char **)nargv)[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*
|
||||
* [eventually this will replace the BSD getopt]
|
||||
*/
|
||||
int
|
||||
getopt(int nargc, char * const *nargv, const char *options)
|
||||
{
|
||||
|
||||
/*
|
||||
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||
*
|
||||
* Furthermore, since many privileged programs call getopt()
|
||||
* before dropping privileges it makes sense to keep things
|
||||
* as simple (and bug-free) as possible.
|
||||
*/
|
||||
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
|
||||
}
|
||||
#endif /* REPLACE_GETOPT */
|
||||
|
||||
//extern int getopt(int nargc, char * const *nargv, const char *options);
|
||||
|
||||
#ifdef _BSD_SOURCE
|
||||
/*
|
||||
* BSD adds the non-standard `optreset' feature, for reinitialisation
|
||||
* of `getopt' parsing. We support this feature, for applications which
|
||||
* proclaim their BSD heritage, before including this header; however,
|
||||
* to maintain portability, developers are advised to avoid it.
|
||||
*/
|
||||
# define optreset __mingw_optreset
|
||||
extern int optreset;
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* POSIX requires the `getopt' API to be specified in `unistd.h';
|
||||
* thus, `unistd.h' includes this header. However, we do not want
|
||||
* to expose the `getopt_long' or `getopt_long_only' APIs, when
|
||||
* included in this manner. Thus, close the standard __GETOPT_H__
|
||||
* declarations block, and open an additional __GETOPT_LONG_H__
|
||||
* specific block, only when *not* __UNISTD_H_SOURCED__, in which
|
||||
* to declare the extended API.
|
||||
*/
|
||||
#endif /* !defined(__GETOPT_H__) */
|
||||
|
||||
#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
|
||||
#define __GETOPT_LONG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct option /* specification for a long form option... */
|
||||
{
|
||||
const char *name; /* option name, without leading hyphens */
|
||||
int has_arg; /* does it take an argument? */
|
||||
int *flag; /* where to save its status, or NULL */
|
||||
int val; /* its associated status value */
|
||||
};
|
||||
|
||||
enum /* permitted values for its `has_arg' field... */
|
||||
{
|
||||
no_argument = 0, /* option never takes an argument */
|
||||
required_argument, /* option always requires an argument */
|
||||
optional_argument /* option may take an argument */
|
||||
};
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int short_too)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
size_t current_argv_len;
|
||||
int i, ambiguous, match;
|
||||
|
||||
#define IDENTICAL_INTERPRETATION(_x, _y) \
|
||||
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
|
||||
long_options[(_x)].flag == long_options[(_y)].flag && \
|
||||
long_options[(_x)].val == long_options[(_y)].val)
|
||||
|
||||
current_argv = place;
|
||||
match = -1;
|
||||
ambiguous = 0;
|
||||
|
||||
optind++;
|
||||
|
||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
ambiguous = 0;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* partial match */
|
||||
match = i;
|
||||
else if (!IDENTICAL_INTERPRETATION(i, match))
|
||||
ambiguous = 1;
|
||||
}
|
||||
if (ambiguous) {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
warnx(ambig, (int)current_argv_len,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
warnx(noarg, (int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
return (BADARG);
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargstring,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
--optind;
|
||||
return (BADARG);
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--optind;
|
||||
return (-1);
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptstring, current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return (0);
|
||||
} else
|
||||
return (long_options[match].val);
|
||||
#undef IDENTICAL_INTERPRETATION
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
static int posixly_correct = -1;
|
||||
|
||||
if (options == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (optind == 0)
|
||||
optind = optreset = 1;
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||
* string begins with a '+'.
|
||||
*
|
||||
* CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
|
||||
* optreset != 0 for GNU compatibility.
|
||||
*/
|
||||
if (posixly_correct == -1 || optreset != 0)
|
||||
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
optarg = NULL;
|
||||
if (optreset)
|
||||
nonopt_start = nonopt_end = -1;
|
||||
start:
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc) { /* end of argument vector */
|
||||
place = EMSG;
|
||||
if (nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
else if (nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
optind = nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
if (*(place = nargv[optind]) != '-' ||
|
||||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||
place = EMSG; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
return (INORDER);
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
/* do permutation */
|
||||
if (nonopt_start == -1)
|
||||
nonopt_start = optind;
|
||||
else if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
nonopt_start = optind -
|
||||
(nonopt_end - nonopt_start);
|
||||
nonopt_end = -1;
|
||||
}
|
||||
optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (nonopt_start != -1 && nonopt_end == -1)
|
||||
nonopt_end = optind;
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||
optind++;
|
||||
place = EMSG;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && place != nargv[optind] &&
|
||||
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
if (*place == '-')
|
||||
place++; /* --foo long option */
|
||||
else if (*place != ':' && strchr(options, *place) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, short_too);
|
||||
if (optchar != -1) {
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optchar = (int)*place++) == (int)':' ||
|
||||
(optchar == (int)'-' && *place != '\0') ||
|
||||
(oli = (char*)strchr(options, optchar)) == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *place == '\0')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*place) /* no space */
|
||||
/* NOTHING */;
|
||||
else if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else /* white space */
|
||||
place = nargv[optind];
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, 0);
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* takes (optional) argument */
|
||||
optarg = NULL;
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else
|
||||
optarg = nargv[optind];
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return (optchar);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||
}
|
||||
|
||||
//extern int getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
// const struct option *long_options, int *idx);
|
||||
//extern int getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
// const struct option *long_options, int *idx);
|
||||
/*
|
||||
* Previous MinGW implementation had...
|
||||
*/
|
||||
#ifndef HAVE_DECL_GETOPT
|
||||
/*
|
||||
* ...for the long form API only; keep this for compatibility.
|
||||
*/
|
||||
# define HAVE_DECL_GETOPT 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
|
||||
427
mkrtos_user/lib/printf/test/test_suite.cpp
Normal file
427
mkrtos_user/lib/printf/test/test_suite.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
|
||||
// 2021-2022, Haifa, Palestine/Israel
|
||||
// \author (c) Marco Paland (info@paland.com)
|
||||
// 2017-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 printf unit tests
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define PRINTF_VISIBILITY static
|
||||
#if PRINTF_INCLUDE_CONFIG_H
|
||||
#include <printf_config.h>
|
||||
#endif
|
||||
#include <printf/printf.c>
|
||||
|
||||
// use the 'catch' test framework
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__)
|
||||
#include <sys/types.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#else
|
||||
// Let's just cross our fingers and hope `ssize_t` is defined.
|
||||
#endif
|
||||
|
||||
#define CAPTURE_AND_PRINT(printer_, ...) \
|
||||
do { \
|
||||
INFO( #printer_ << \
|
||||
" arguments (ignore the equations; interpret \"expr" \
|
||||
"\" := expr\" as just \"expr\"): "); \
|
||||
CAPTURE( __VA_ARGS__); \
|
||||
printer_(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define CAPTURE_AND_PRINT_WITH_RETVAL(retval, printer_, ...) \
|
||||
do { \
|
||||
INFO( #printer_ << \
|
||||
" arguments (ignore the equations; interpret \"expr" \
|
||||
"\" := expr\" as just \"expr\"): "); \
|
||||
CAPTURE( __VA_ARGS__); \
|
||||
retval = printer_(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define PRINTING_CHECK_WITH_BUF_SIZE(expected_, dummy, printer_, buffer_, buffer_size, ...) \
|
||||
do { \
|
||||
INFO( #printer_ << " arguments, replicated ( \"arg := arg\" " \
|
||||
"):\n----"); \
|
||||
CAPTURE( __VA_ARGS__); \
|
||||
std::memset(buffer_, 0xCC, base_buffer_size); \
|
||||
printer_(buffer_, buffer_size, __VA_ARGS__); \
|
||||
if (!strcmp(buffer_, expected_)) { \
|
||||
buffer_[strlen(expected_) + 1] = '\0'; \
|
||||
} \
|
||||
INFO( "----"); \
|
||||
INFO( "Resulting buffer contents: " << '"' << buffer_ << '"'); \
|
||||
CHECK(!strcmp(buffer_, expected_)); \
|
||||
} while(0)
|
||||
|
||||
#define PRINTING_CHECK(expected_, dummy, printer_, buffer_, ...) \
|
||||
do { \
|
||||
INFO( #printer_ << " arguments, replicated ( \"arg := arg\" " \
|
||||
"):\n----"); \
|
||||
CAPTURE( __VA_ARGS__); \
|
||||
std::memset(buffer_, 0xCC, base_buffer_size); \
|
||||
printer_(buffer_, __VA_ARGS__); \
|
||||
if (!strcmp(buffer_, expected_)) { \
|
||||
buffer_[strlen(expected_) + 1] = '\0'; \
|
||||
} \
|
||||
INFO( "----"); \
|
||||
INFO( "Resulting buffer contents: " << '"' << buffer_ << '"'); \
|
||||
CHECK(!strcmp(buffer_, expected_)); \
|
||||
} while(0)
|
||||
|
||||
// Multi-compiler-compatible local warning suppression
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define DISABLE_WARNING_PUSH __pragma(warning( push ))
|
||||
#define DISABLE_WARNING_POP __pragma(warning( pop ))
|
||||
#define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber ))
|
||||
|
||||
// TODO: find the right warning number for this
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_EXTRA_ARGS
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_INVALID_SPECIFIER
|
||||
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define DO_PRAGMA(X) _Pragma(#X)
|
||||
#define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push)
|
||||
#define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop)
|
||||
#define DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName)
|
||||
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT DISABLE_WARNING(-Wformat)
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_EXTRA_ARGS DISABLE_WARNING(-Wformat-extra-args)
|
||||
#if defined(__clang__)
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_INVALID_SPECIFIER DISABLE_WARNING(-Wformat-invalid-specifier)
|
||||
#else
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW DISABLE_WARNING(-Wformat-overflow)
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_INVALID_SPECIFIER
|
||||
#endif
|
||||
#else
|
||||
#define DISABLE_WARNING_PUSH
|
||||
#define DISABLE_WARNING_POP
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_EXTRA_ARGS
|
||||
#define DISABLE_WARNING_PRINTF_FORMAT_INVALID_SPECIFIER
|
||||
#endif
|
||||
|
||||
#ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS
|
||||
DISABLE_WARNING_PUSH
|
||||
DISABLE_WARNING_PRINTF_FORMAT
|
||||
DISABLE_WARNING_PRINTF_FORMAT_EXTRA_ARGS
|
||||
DISABLE_WARNING_PRINTF_FORMAT_INVALID_SPECIFIER
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
DISABLE_WARNING(4996) // Discouragement of use of std::sprintf()
|
||||
DISABLE_WARNING(4310) // Casting to smaller type
|
||||
DISABLE_WARNING(4127) // Constant conditional expression
|
||||
#endif
|
||||
|
||||
constexpr const size_t base_buffer_size { 100 };
|
||||
|
||||
// This macro is idempotent here, but for other platforms may
|
||||
// be defined differently
|
||||
#define mkstr(_str) _str
|
||||
|
||||
// dummy putchar
|
||||
static char printf_buffer[base_buffer_size];
|
||||
static size_t printf_idx = 0U;
|
||||
|
||||
void putchar_(char character)
|
||||
{
|
||||
printf_buffer[printf_idx++] = character;
|
||||
}
|
||||
|
||||
void _out_fct(char character, void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
printf_buffer[printf_idx++] = character;
|
||||
}
|
||||
|
||||
#ifndef STRINGIFY
|
||||
#define STRINGIFY(_x) #_x
|
||||
#endif
|
||||
#define PRINTF_TEST_CASE(unstringified_name) TEST_CASE(STRINGIFY(unstringified_name), "[]")
|
||||
|
||||
PRINTF_TEST_CASE(printf) {
|
||||
printf_idx = 0U;
|
||||
memset(printf_buffer, 0xCC, base_buffer_size);
|
||||
INFO("printf_ format string and arguments: ");
|
||||
CAPTURE("% d", 4232);
|
||||
CHECK(printf_("% d", 4232) == 5);
|
||||
INFO("printf_ format string and arguments: ");
|
||||
CAPTURE("% d", 4232);
|
||||
CHECK(printf_buffer[5] == (char)0xCC);
|
||||
printf_buffer[5] = 0;
|
||||
INFO("printf_ format string and arguments: ");
|
||||
CAPTURE("% d", 4232);
|
||||
CHECK(!strcmp(printf_buffer, " 4232"));
|
||||
}
|
||||
|
||||
|
||||
PRINTF_TEST_CASE(fctprintf) {
|
||||
printf_idx = 0U;
|
||||
memset(printf_buffer, 0xCC, base_buffer_size);
|
||||
fctprintf(&_out_fct, nullptr, "This is a test of %X", 0x12EFU);
|
||||
INFO("fctprintf format string and arguments: ");
|
||||
CAPTURE("This is a test of %X", 0x12EFU);
|
||||
CHECK(!strncmp(printf_buffer, "This is a test of 12EF", 22U));
|
||||
CHECK(printf_buffer[22] == (char)0xCC);
|
||||
}
|
||||
|
||||
// output function type
|
||||
typedef void (*out_fct_type_)(char character, void* arg);
|
||||
|
||||
|
||||
static void vfctprintf_builder_1(out_fct_type_ f, char* buffer, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, buffer);
|
||||
CAPTURE_AND_PRINT(vfctprintf, f, nullptr, "This is a test of %X", args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
PRINTF_TEST_CASE(vfctprintf) {
|
||||
printf_idx = 0U;
|
||||
memset(printf_buffer, 0xCC, base_buffer_size);
|
||||
vfctprintf_builder_1(&_out_fct, nullptr, 0x12EFU);
|
||||
CHECK(!strncmp(printf_buffer, "This is a test of 12EF", 22U));
|
||||
CHECK(printf_buffer[22] == (char)0xCC);
|
||||
}
|
||||
|
||||
PRINTF_TEST_CASE(snprintf_) {
|
||||
char buffer[base_buffer_size];
|
||||
PRINTING_CHECK_WITH_BUF_SIZE("-1000", ==, snprintf_, buffer, base_buffer_size, "%d", -1000);
|
||||
PRINTING_CHECK_WITH_BUF_SIZE("-1", ==, snprintf_, buffer, 3U, "%d", -1000);
|
||||
}
|
||||
|
||||
static void vprintf_builder_1(char* buffer, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, buffer);
|
||||
vprintf_("%d", args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void vsprintf_builder_1(char* buffer, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, buffer);
|
||||
CAPTURE_AND_PRINT(vsprintf_, buffer, "%d", args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void vsnprintf_builder_1(char* buffer, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, buffer);
|
||||
CAPTURE_AND_PRINT(vsnprintf_, buffer, 100U, "%d", args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void vsprintf_builder_3(char* buffer, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, buffer);
|
||||
CAPTURE_AND_PRINT(vsprintf_, buffer, "%d %d %s", args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void vsnprintf_builder_3(char* buffer, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, buffer);
|
||||
CAPTURE_AND_PRINT(vsnprintf_, buffer, 100U, "%d %d %s", args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
PRINTF_TEST_CASE(vprintf) {
|
||||
char buffer[base_buffer_size];
|
||||
printf_idx = 0U;
|
||||
memset(printf_buffer, 0xCC, base_buffer_size);
|
||||
vprintf_builder_1(buffer, 2345);
|
||||
CHECK(printf_buffer[4] == (char)0xCC);
|
||||
printf_buffer[4] = 0;
|
||||
CHECK(!strcmp(printf_buffer, "2345"));
|
||||
}
|
||||
|
||||
|
||||
PRINTF_TEST_CASE(vsprintf) {
|
||||
char buffer[base_buffer_size];
|
||||
|
||||
vsprintf_builder_1(buffer, -1);
|
||||
CHECK(!strcmp(buffer, "-1"));
|
||||
|
||||
vsprintf_builder_3(buffer, 3, -1000, "test");
|
||||
CHECK(!strcmp(buffer, "3 -1000 test"));
|
||||
}
|
||||
|
||||
|
||||
PRINTF_TEST_CASE(vsnprintf_) {
|
||||
char buffer[base_buffer_size];
|
||||
|
||||
vsnprintf_builder_1(buffer, -1);
|
||||
CHECK(!strcmp(buffer, "-1"));
|
||||
|
||||
vsnprintf_builder_3(buffer, 3, -1000, "test");
|
||||
CHECK(!strcmp(buffer, "3 -1000 test"));
|
||||
}
|
||||
|
||||
PRINTF_TEST_CASE(writeback_specifier) {
|
||||
char buffer[base_buffer_size];
|
||||
|
||||
struct {
|
||||
char char_;
|
||||
short short_;
|
||||
int int_;
|
||||
long long_;
|
||||
long long long_long_;
|
||||
} num_written;
|
||||
|
||||
|
||||
num_written.int_ = 1234;
|
||||
printf_("%n", &num_written.int_);
|
||||
CHECK(num_written.int_ == 0);
|
||||
num_written.int_ = 1234;
|
||||
printf_("foo%nbar", &num_written.int_);
|
||||
CHECK(num_written.int_ == 3);
|
||||
|
||||
num_written.int_ = 1234;
|
||||
PRINTING_CHECK("", ==, sprintf_, buffer, "%n", &num_written.int_);
|
||||
CHECK(num_written.int_ == 0);
|
||||
num_written.int_ = 1234;
|
||||
PRINTING_CHECK("foobar", ==, sprintf_, buffer, "foo%nbar", &num_written.int_);
|
||||
CHECK(num_written.int_ == 3);
|
||||
}
|
||||
|
||||
PRINTF_TEST_CASE(ret_value)
|
||||
{
|
||||
char buffer[base_buffer_size];
|
||||
int ret;
|
||||
|
||||
ret = snprintf_(buffer, 6, "0%s", "1234");
|
||||
CHECK(!strcmp(buffer, "01234"));
|
||||
CHECK(ret == 5);
|
||||
|
||||
std::memset(buffer, 0xCC, sizeof(buffer));
|
||||
ret = snprintf_(buffer, 6, "0%s", "12345");
|
||||
CHECK(!strcmp(buffer, "01234"));
|
||||
CHECK(ret == 6); // "5" is truncated
|
||||
|
||||
std::memset(buffer, 0xCC, sizeof(buffer));
|
||||
ret = snprintf_(buffer, 6, "0%s", "1234567");
|
||||
CHECK(!strcmp(buffer, "01234"));
|
||||
CHECK(ret == 8); // "567" are truncated
|
||||
|
||||
std::memset(buffer, 0xCC, sizeof(buffer));
|
||||
DISABLE_WARNING_PUSH
|
||||
DISABLE_WARNING_PRINTF_FORMAT_OVERFLOW
|
||||
ret = snprintf_(buffer, 6, "0%s", (const char *) NULL);
|
||||
DISABLE_WARNING_POP
|
||||
CHECK(!strcmp(buffer, "0(nul"));
|
||||
CHECK(ret == 7); // "l)" is truncated
|
||||
|
||||
std::memset(buffer, 0xCC, sizeof(buffer));
|
||||
ret = snprintf_(buffer, 10, "hello, world");
|
||||
CHECK(ret == 12);
|
||||
|
||||
std::memset(buffer, 0xCC, sizeof(buffer));
|
||||
ret = snprintf_(buffer, 3, "%d", 10000);
|
||||
CHECK(ret == 5);
|
||||
CHECK(strlen(buffer) == 2U);
|
||||
CHECK(buffer[0] == '1');
|
||||
CHECK(buffer[1] == '0');
|
||||
CHECK(buffer[2] == '\0');
|
||||
}
|
||||
|
||||
#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
|
||||
PRINTF_TEST_CASE(brute_force_float)
|
||||
{
|
||||
char buffer[base_buffer_size];
|
||||
#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS
|
||||
// brute force float
|
||||
bool any_failed = false;
|
||||
std::stringstream sstr;
|
||||
sstr.precision(5);
|
||||
for (float i = -100000; i < 100000; i += (float) 1) {
|
||||
sprintf_(buffer, "%.5f", (double) (i / 10000));
|
||||
sstr.str("");
|
||||
sstr << std::fixed << i / 10000;
|
||||
if (strcmp(buffer, sstr.str().c_str()) != 0) {
|
||||
std::cerr
|
||||
<< ": sprintf_(\"%.5f\", " << std::setw(6) << i << ") = " << std::setw(10) << buffer << " , "
|
||||
<< "expected " << std::setw(10) << sstr.str().c_str() << "\n";
|
||||
any_failed = true;
|
||||
}
|
||||
}
|
||||
CHECK(not any_failed);
|
||||
|
||||
#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
|
||||
// This is tested when _both_ decimal and exponential specifiers are supported.
|
||||
// brute force exp
|
||||
sstr.setf(std::ios::scientific, std::ios::floatfield);
|
||||
any_failed = false;
|
||||
long n = 0;
|
||||
for (float i = (float) -1e20; i < (float) 1e20; i += (float) 1e15, n++) {
|
||||
sprintf_(buffer, "%.5f", (double) i);
|
||||
sstr.str("");
|
||||
sstr << i;
|
||||
if (strcmp(buffer, sstr.str().c_str()) != 0) {
|
||||
std::cerr
|
||||
<< n << ": sprintf_(\"%.5f\", " << std::setw(18) << std::setprecision(30) << i << ") = " << std::setw(15)
|
||||
<< buffer << " , "
|
||||
<< "expected " << std::setw(12) << sstr.str().c_str() << "\n";
|
||||
any_failed = true;
|
||||
}
|
||||
}
|
||||
CHECK(not any_failed);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
|
||||
|
||||
|
||||
#include "test_suite_main_testcases.hpp"
|
||||
|
||||
#ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS
|
||||
DISABLE_WARNING_POP
|
||||
#endif
|
||||
|
||||
1063
mkrtos_user/lib/printf/test/test_suite_main_testcases.hpp
Normal file
1063
mkrtos_user/lib/printf/test/test_suite_main_testcases.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,8 @@
|
||||
|
||||
static inline uint16_t swap_u16(uint16_t data)
|
||||
{
|
||||
return ((data >> 8) & 0xff) | ((data & 0xff) << 8);
|
||||
return (uint16_t)((data >> (uint16_t)8) & (uint16_t)0xffUL) |
|
||||
(uint16_t)((uint16_t)(data & (uint16_t)0xff) << (uint16_t)8);
|
||||
}
|
||||
|
||||
int mk_syscall(unsigned long nr, ...);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
int cons_write(const uint8_t *data, int len);
|
||||
static inline int cons_write_str(const char *str)
|
||||
{
|
||||
return cons_write(str, strlen(str));
|
||||
return cons_write((const uint8_t *)str, (int)strlen(str));
|
||||
}
|
||||
int cons_read(uint8_t *data, int len);
|
||||
int cons_active(void);
|
||||
|
||||
@@ -12,6 +12,7 @@ add_executable(hello.elf
|
||||
target_link_libraries(hello.elf
|
||||
PUBLIC
|
||||
start_tiny
|
||||
printf
|
||||
muslc
|
||||
sys
|
||||
sys_util
|
||||
@@ -36,9 +37,12 @@ target_include_directories(
|
||||
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/server/hello/bsp/core_inc
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/server/hello/bsp/inc
|
||||
|
||||
${CMAKE_SOURCE_DIR}/mkrtos_user/lib/printf/src/printf
|
||||
)
|
||||
add_dependencies(hello.elf
|
||||
muslc
|
||||
printf
|
||||
)
|
||||
set_target_properties(hello.elf PROPERTIES LINK_FLAGS
|
||||
"-T ${CMAKE_CURRENT_LIST_DIR}/link.lds -pie --gc-section -no-dynamic-linker "
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
|
||||
// #include <stdio.h>
|
||||
#include <cons_cli.h>
|
||||
#include <printf.h>
|
||||
#include <unistd.h>
|
||||
int main(int argc, char *args[])
|
||||
{
|
||||
cons_write_str("Hello world.\n");
|
||||
// printf("print test0.\n");
|
||||
// printf("print test1.\n");
|
||||
// printf("print test2.\n");
|
||||
// float a = 1.1;
|
||||
// float b = 1.2;
|
||||
// float c;
|
||||
mk_printf("print test0.\n");
|
||||
mk_printf("print test1.\n");
|
||||
mk_printf("print test2.\n");
|
||||
float a = 1.1;
|
||||
float b = 1.2;
|
||||
float c;
|
||||
|
||||
// while (1)
|
||||
// {
|
||||
// c = a + b;
|
||||
// printf("%c %d %f\n", 'a', 1234, 1.1);
|
||||
// printf("%c %d %lf\n", 'a', 1234, a * b);
|
||||
// }
|
||||
while (1)
|
||||
{
|
||||
c = a + b;
|
||||
// mk_printf("%c %d %f\n", 'a', 1234, 1.1);
|
||||
// mk_printf("%c %d %lf\n", 'a', 1234, a * b);
|
||||
mk_printf("%c %d %d\n", 'a', 1234, (int)1.1);
|
||||
mk_printf("%c %d %d\n", 'a', 1234, (int)(a * b));
|
||||
sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ endif()
|
||||
|
||||
|
||||
# -mfloat-abi=soft -u _printf_float
|
||||
set(CMAKE_C_FLAGS "-mcpu=${CONFIG_ARCH} -Os -g3 -lc -lrdimon -mfloat-abi=${FLOAT_TYPE} -u _printf_float -D=MKRTOS \
|
||||
set(CMAKE_C_FLAGS "-mcpu=${CONFIG_ARCH} -O0 -g3 -lc -lrdimon -mfloat-abi=${FLOAT_TYPE} -u _printf_float -D=MKRTOS \
|
||||
-std=gnu11 -ffunction-sections -fdata-sections -fno-builtin \
|
||||
-nostartfiles -nodefaultlibs -nostdlib -nostdinc -Xlinker \
|
||||
-fno-stack-protector -Wl,--gc-sections \
|
||||
|
||||
Reference in New Issue
Block a user