├── .gitattributes ├── .github_disabled └── workflows │ └── build_and_test.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── config ├── CMakeLists.txt ├── printf-config.cmake.in └── printf_config.h.in ├── src └── printf │ ├── printf.c │ └── printf.h └── test ├── CMakeLists.txt ├── aliasing.c ├── autotest.cpp ├── getopt.h └── test_suite.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # ignore test path 2 | test/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.github_disabled/workflows/build_and_test.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: [ master, develop, github-actions-ci ] 6 | pull_request: 7 | branches: [ master, develop, github-actions-ci ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | EXTRA_CONFIG_OPTIONS: -DBUILD_TESTS:BOOL=ON 13 | 14 | jobs: 15 | build: 16 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 17 | # You can convert this to a matrix build if you need cross-platform coverage. 18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Configure CMake 25 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 26 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 27 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{env.EXTRA_CONFIG_OPTIONS}} 28 | 29 | - name: Build 30 | # Build your program with the given configuration 31 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 32 | 33 | - name: Test 34 | working-directory: ${{github.workspace}}/build 35 | # Execute tests defined by the CMake configuration. 36 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 37 | run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure 38 | 39 | # - name: Upload code coverage report as an artifact 40 | # uses: actions/upload-artifact@v2 41 | # with: 42 | # name: codecov_report 43 | # path: codecov_report.html 44 | 45 | # - name: Upload code coverage reports to codecov.io page 46 | # run: bash <(curl -s https://codecov.io/bash) 47 | 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | *.patch 4 | *.diff 5 | 6 | # Generated build output 7 | buildresults/ 8 | 9 | # Temporary, cache, swap files 10 | \#\#* 11 | *.swp 12 | *.bkp 13 | 14 | # Build artifacts 15 | *.a 16 | *.o 17 | *.so* 18 | bin/* 19 | lib/* 20 | build/* 21 | install_manifest.txt 22 | 23 | # Core dumps 24 | core 25 | core.* 26 | 27 | # CMake & CTest 28 | CMakeCache.txt 29 | CMakeFiles/* 30 | cmake_install.cmake 31 | CMakeScripts/* 32 | CMakeTmp/* 33 | CPack*.cmake 34 | Makefile 35 | CTestTestfile.cmake 36 | /_CPack_Packages 37 | /CMakeScripts 38 | 39 | # Eclise IDE 40 | .project 41 | .cproject 42 | .settings 43 | 44 | # CLion IDE 45 | .idea/ 46 | cmake-build-*/ 47 | 48 | # Visual Studio Code IDE 49 | .vscode/ 50 | .vs/ 51 | 52 | # Android Studio, Gradle build system etc. 53 | .gradle/ 54 | gradle/ 55 | gradlew* 56 | .externalNativeBuild/ 57 | local.properties 58 | *.iml 59 | 60 | # XCode 61 | *.xcodeproj 62 | 63 | # Patching 64 | *.diff 65 | *.rej 66 | *.orig 67 | 68 | # Files/folders downloaded from other repositories as part of the build 69 | external/* 70 | third-party/* 71 | 72 | # Generated by Doxygen 73 | doxygen.log 74 | Doxyfile 75 | docs/ 76 | /doc/doxyxml 77 | /doc/html 78 | /docs/node_modules 79 | /docs/doxyxml 80 | /docs/html 81 | /docs/node_modules 82 | 83 | # Archives 84 | *.zip 85 | *.gz 86 | *.bz2 87 | *.tgz 88 | *.tar 89 | *.xz 90 | 91 | # Miscellaneous 92 | tags 93 | log 94 | *.log 95 | *.v3breakpoints 96 | gmon.out 97 | .DS_Store 98 | run-msbuild.bat 99 | virtualenv 100 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cmake-buildsystem"] 2 | path = cmake 3 | url = https://github.com/embeddedartistry/cmake-buildsystem 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.14) 2 | project( 3 | printf 4 | LANGUAGES C 5 | DESCRIPTION "Self-contained C implementation of printf, vprintf, sprintf and related functions" 6 | HOMEPAGE_URL https://github.com/eyalroz/printf 7 | VERSION 6.0.0 8 | ) 9 | 10 | ######################### 11 | # Project Build Options # 12 | ######################### 13 | 14 | option(BUILD_TESTS "Build test programs for the library" ON) 15 | option(BUILD_SHARED_LIBS "Build the library as a shared (dynamically-linked) object rather than a static one " OFF) 16 | 17 | ######################## 18 | # Type Size Validation # 19 | ######################## 20 | 21 | include(CheckTypeSize) 22 | 23 | # Checks related to the 'j', 'z' and 't' size modifiers 24 | 25 | check_type_size( "long" SIZEOF_LONG ) 26 | check_type_size( "long long" SIZEOF_LONG_LONG ) 27 | 28 | set(ACCEPTABLE_JZT_TYPE_SIZES ${SIZEOF_LONG} ${SIZEOF_LONG_LONG}) 29 | 30 | function(validate_type_size type_name) 31 | check_type_size(${type_name} TYPE_SIZE) 32 | if (NOT ${TYPE_SIZE} IN_LIST ACCEPTABLE_JZT_TYPE_SIZES) 33 | 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.") 34 | endif() 35 | endfunction() 36 | validate_type_size("intmax_t") 37 | validate_type_size("size_t") 38 | validate_type_size("ptrdiff_t") 39 | 40 | ###################### 41 | # Configuration File # 42 | ###################### 43 | add_subdirectory(config) 44 | 45 | ################## 46 | # Library Target # 47 | ################## 48 | add_library(printf) 49 | add_library("printf::printf" ALIAS printf) 50 | 51 | target_sources(printf PRIVATE src/printf/printf.c "${CONFIG_HEADER_DIR}/printf_config.h" src/printf/printf.h) 52 | target_compile_definitions(printf PRIVATE PRINTF_INCLUDE_CONFIG_H) 53 | target_include_directories(printf PRIVATE "$") 54 | 55 | if (BUILD_SHARED_LIBS) 56 | set_property(TARGET printf PROPERTY VERSION ${PROJECT_VERSION}) 57 | set_property(TARGET printf PROPERTY SOVERSION ${PROJECT_VERSION}) 58 | endif() 59 | 60 | set_property(TARGET printf PROPERTY C_STANDARD 99) 61 | set_property(TARGET printf PROPERTY C_STANDARD_REQUIRED ON) 62 | set_property(TARGET printf PROPERTY C_EXTENSIONS OFF) 63 | 64 | target_include_directories( 65 | printf 66 | PRIVATE 67 | src 68 | PUBLIC 69 | "$" 70 | "$" 71 | ) 72 | 73 | set_target_properties(printf PROPERTIES 74 | LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib 75 | ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 76 | 77 | if (CMAKE_C_COMPILER_ID STREQUAL "MSVC") 78 | target_compile_options(printf PRIVATE /W4) 79 | elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR 80 | CMAKE_C_COMPILER_ID STREQUAL "Clang") 81 | target_compile_options(printf PRIVATE -Wall -Wextra -pedantic -Wconversion) 82 | if (ALIAS_STANDARD_FUNCTION_NAMES) 83 | # This is important for preventing our aliased implementation 84 | # from being replaced, e.g. printf("%c", 'a') by putchar('a'); 85 | # clang and GCC apparently do this as an optimization 86 | target_compile_options(printf PUBLIC -fno-builtin-printf) 87 | endif() 88 | endif() 89 | 90 | ########### 91 | # Testing # 92 | ########### 93 | 94 | include(cmake/test/catch2.cmake) 95 | 96 | if (BUILD_TESTS) 97 | enable_testing() 98 | add_subdirectory(test) 99 | endif() 100 | 101 | ####################### 102 | # Supporting Commands # 103 | ####################### 104 | 105 | if (UNIX) 106 | add_custom_target(printf-sizes 107 | COMMAND size -A -t $ > printf_sizes.txt 108 | DEPENDS printf 109 | BYPRODUCTS printf_sizes.txt 110 | COMMENT Prints the sizes of the different sections of the ELF file: text, dat, vss etc.) 111 | 112 | add_custom_target(printf-symbols 113 | COMMAND nm --numeric-sort --print-size "$" > printf_symbols.txt 114 | COMMAND bash -c "nm --numeric-sort --print-size $ | c++filt > printf_cpp_symbols.txt" 115 | VERBATIM 116 | DEPENDS printf 117 | BYPRODUCTS printf_symbols.txt printf_cpp_symbols.txt 118 | COMMENT Produces lists of the symbols, and C++demangled symbols, inside the library) 119 | 120 | add_custom_target(printf-lst 121 | COMMAND objdump --disassemble --line-numbers -S "$" > printf.list 122 | DEPENDS printf 123 | BYPRODUCTS printf.lst 124 | COMMENT Dissassembles the compiled library into an .lst file) 125 | endif() 126 | 127 | ################ 128 | # Installation # 129 | ################ 130 | 131 | include(GNUInstallDirs) 132 | 133 | # Note: No need for a config.cmake file for setting dependencies - as there 134 | # are no dependencies; this library is self-contained 135 | 136 | install( 137 | TARGETS printf 138 | EXPORT printf_export 139 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 140 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 141 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 142 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 143 | ) 144 | 145 | install( 146 | FILES "src/printf/printf.h" 147 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/printf" 148 | ) 149 | 150 | export( 151 | EXPORT printf_export 152 | NAMESPACE "printf::" 153 | FILE "${PROJECT_BINARY_DIR}/printf-targets.cmake" 154 | ) 155 | 156 | install( 157 | EXPORT printf_export 158 | FILE "printf-targets.cmake" 159 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/printf" 160 | NAMESPACE "printf::" 161 | ) 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marco Paland 4 | Copyright (c) 2021 Eyal Rozenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Standalone printf/sprintf formatted printing function library 2 | 3 | [![tests passing](https://github.com/eyalroz/printf/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/eyalroz/printf/actions/workflows/build_and_test.yml) 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/eyalroz/printf/master/LICENSE) 5 | [![Github Bug-type issues](https://shields.io/github/issues-search/eyalroz/printf?query=is:open%20is:issue%20label:bug&label=open%20bugs)](http://github.com/eyalroz/printf/issues) 6 | Parent repository: [![Github issues (original repo)](https://img.shields.io/github/issues/mpaland/printf.svg)](http://github.com/mpaland/printf/issues) 7 | 8 | 9 | 10 | 11 | | Table of contents | 12 | |:------------------| 13 | |[Highlights, design goals and the fork](#highlights-design-goals-and-the-fork)
[Using the `printf` library in your project](#using-the-printf-library-in-your-project)
- [CMake options and preprocessor definitions](#cmake-options-and-preprocessor-definitions)
[Library API](#library-api)
- [Implemented functions](#implemented-functions)
- [Supported Format Specifiers](#supported-format-specifiers)
- [Return Value](#return-value)
[Contributing](#contributing)
[License](#license)
| 14 | 15 | 16 | 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**. 17 | 18 | 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. 19 | 20 | ## Highlights, design goals and the fork 21 | 22 | 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? 23 | 24 | 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. 25 | 26 | Marco therefore decided to write his own implementation, with the following goals in mind (I've dropped a few relative to his original description): 27 | 28 | - Very small implementation 29 | - NO dependencies on other packages or libraries; no multiple compiled objects, just one object file. 30 | - Support for all standard specifiers and flags, and all width and precision sub-specifiers (see below). 31 | - Support of decimal/floating number representation (with an internal, relatively fast `itoa`/`ftoa` implementation) 32 | - Reentrancy and thread-safety; `malloc()` freeness. 33 | - Clean, robust code. 34 | - Extensive test coverage. 35 | - MIT license 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | ## Using the `printf` library in your project 42 | 43 | **Use involving CMake:** 44 | 45 | 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. 46 | 2. Use CMake to configure and build the library. This results in the following files: 47 | 48 | * 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) 49 | * A header file named `printf.h` 50 | * (Not strictly necessary) An optional extra header file `printf_config.h` with the build configuration details. 51 | 52 | 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. 53 | 3. Use CMake's `FetchContent` module to obtain the project source code and make it part of your own project's build, e.g.: 54 | ``` 55 | FetchContent_Declare(printf_library 56 | GIT_REPOSITORY https://github.com/eyalroz/printf.git 57 | GIT_TAG v12.34.45 # Replace this with a real available version 58 | ) 59 | FetchContent_MakeAvailable(printf_library) 60 | ``` 61 | 62 | **Use not involving CMake:** 63 | 64 | 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. 65 | 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. 66 | 67 | Whichever way you choose to use the library: 68 | 69 | * 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. 70 | * 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.) 71 | * Two of the functions --- `printf_()` and `vprintf_()` --- will only be usable if you implement a `putchar_(char c)` function to underlie them. 72 | * **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. 73 | 74 | 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. 75 | 76 | ### CMake options and preprocessor definitions 77 | 78 | Options used both in CMake and in the library source code via a preprocessor define: 79 | 80 | | Option name | Default | Description | 81 | |----------------------------------------|---------|--------------| 82 | | PRINTF_ALIAS_STANDARD_FUNCTION_NAMES | NONE | Alias the standard library function names (`printf()`, `sprintf()` etc.) to the library's functions.
The possible values are `NONE`, `SOFT` and `HARD`. With Soft aliasing, the library's object files contain symbols which do not clash with the standard library's: `printf_`, `sprintd_` etc; and a macro in `printf.h` replaces usages of `printf()`, `sprintf()` etc. with the underscored versions. With Hard aliasing, no such macro is used, and the library's object files contain `printf`, `sprintf` etc. - and thus cannot be linked together with a full-fledged standard library. **Note:** The preprocessort definitions `#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT` and `#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD` should be defined to have the same values when using the library as when having compiled the list. | 83 | | 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. | 84 | | 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. | 85 | | PRINTF_DEFAULT_FLOAT_PRECISION | 6 | Define the default floating point precision digits | 86 | | 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 | 87 | | PRINTF_SUPPORT_DECIMAL_SPECIFIERS | YES | Support decimal notation floating-point conversion specifiers (%f, %F) | 88 | | PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS | YES | Support exponential floating point format conversion specifiers (%e, %E, %g, %G) | 89 | | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS | YES | Support the 'I' + bit size integer specifiers (%I8, %I16, %I32, %I64) as in Microsoft Visual C++ | 90 | | PRINTF_SUPPORT_WRITEBACK_SPECIFIER | YES | Support the length write-back specifier (%n) | 91 | | PRINTF_SUPPORT_LONG_LONG | YES | Support long long integral types (allows for the ll length modifier and affects %p) | 92 | | PRINTF_USE_DOUBLE_INTERNALLY | YES | Use the `double` for internal floating-point calculations (rather than using the single-precision `float` type | 93 | 94 | Within CMake, these options lack the `PRINTF_` prefix. 95 | 96 | CMake-only options: 97 | 98 | | Option name | Default | Description | 99 | |----------------------------------------|---------|--------------| 100 | | 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) | 101 | 102 | Source-only options: 103 | 104 | | Option name | Default | Description | 105 | |----------------------------------------|---------|--------------| 106 | | 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. | 107 | 108 | Note: The preprocessor definitions are taken into account when compiling `printf.c`, _not_ when using the compiled library by including `printf.h`. 109 | 110 | 111 | ### Reducing compiled code size 112 | 113 | The library's accompanying `CMakeLists.txt` does not set any special optimization flags. If you'd like to benefit from `libprintf` actually being small - you would need to your `CFLAGS` and `LDFLAGS` environment variables appropriately. For example, with GCC, consider: 114 | 115 | * Compiling with `-Os` to make the compiler directly optimize for size. 116 | * Compiling with `-ffunction-sections -fdata-sections` and linking with `-WL,--gc-sections`, so that each function and piece of data gets a distinct sections, and discarded if it isn't used. 117 | 118 | You would also be advised to turn off all CMake options for printf functionality you don't actually need (listed in the [previous subsection](#cmake-options-and-preprocessor-definitions) above); and to consider [stripping](https://unix.stackexchange.com/q/2969/34868) the resulting executable. 119 | 120 | 121 | 122 | ## Library API 123 | 124 | ### Implemented functions 125 | 126 | The library offers the following, with the same signatures as in the standard C library (plus an extra underscore): 127 | ``` 128 | int printf_(const char* format, ...); 129 | int sprintf_(char* s, const char* format, ...); 130 | int vsprintf_(char* s, const char* format, va_list arg); 131 | int snprintf_(char* s, size_t n, const char* format, ...); 132 | int vsnprintf_(char* s, size_t n, const char* format, va_list arg); 133 | int vprintf_(const char* format, va_list arg); 134 | ``` 135 | 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. 136 | 137 | Two additional functions are provided beyond those available in the standard library: 138 | ``` 139 | int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...); 140 | int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg); 141 | ``` 142 | 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. 143 | 144 | #### "... but I don't like the underscore-suffix names :-(" 145 | 146 | 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. 147 | 148 | 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. 149 | 150 |
151 | 152 | Alternatively, you can write short wrappers with your preferred names. This is completely trivial with the v-functions, e.g.: 153 | ``` 154 | int my_vprintf(const char* format, va_list va) 155 | { 156 | return vprintf_(format, va); 157 | } 158 | ``` 159 | and is still pretty straightforward with the variable-number-of-arguments functions: 160 | ``` 161 | int my_sprintf(char* buffer, const char* format, ...) 162 | { 163 | va_list va; 164 | va_start(va, format); 165 | const int ret = vsprintf_(buffer, format, va); 166 | va_end(va); 167 | return ret; 168 | } 169 | ``` 170 | 171 | ### Supported Format Specifiers 172 | 173 | A format specifier follows this prototype: `%[flags][width][.precision][length]type` 174 | The following format specifiers are supported: 175 | 176 | #### Types 177 | 178 | | Type | Output | 179 | |------------|--------------------------| 180 | | `d` or `i` | Signed decimal integer | 181 | | `u` | Unsigned decimal integer | 182 | | `b` | Unsigned binary | 183 | | `o` | Unsigned octal | 184 | | `x` | Unsigned hexadecimal integer (lowercase) | 185 | | `X` | Unsigned hexadecimal integer (uppercase) | 186 | | `f` or `F` | Decimal floating point | 187 | | `e` or `E` | Scientific-notation (exponential) floating point | 188 | | `g` or `G` | Scientific or decimal floating point | 189 | | `c` | Single character | 190 | | `s` | String of characters | 191 | | `p` | Pointer address | 192 | | `n` | None; number of characters produced so far written to argument pointer | 193 | 194 | Notes: 195 | 196 | * The `%a` specifier for hexadecimal floating-point notation (introduced in C99 and C++11) is _not_ currently supported. 197 | * If you want to print the percent sign (`%`, US-ASCII character 37), use "%%" in your format string. 198 | * 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. 199 | 200 | #### Flags 201 | 202 | | Flags | Description | 203 | |---------|-------------| 204 | | `-` | Left-justify within the given field width; Right justification is the default. | 205 | | `+` | Forces to precede the result with a plus or minus sign (+ or -) even for positive numbers.
By default, only negative numbers are preceded with a - sign. | 206 | | (space) | If no sign is going to be written, a blank space is inserted before the value. | 207 | | `#` | Used with o, b, x or X specifiers the value is preceded with 0, 0b, 0x or 0X respectively for values different than zero.
Used with f, F it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written. | 208 | | `0` | Left-pads the number with zeros (0) instead of spaces when padding is specified (see width sub-specifier). | 209 | 210 | 211 | #### Width Specifiers 212 | 213 | | Width | Description | 214 | |----------|-------------| 215 | | (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. | 216 | | `*` | The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | 217 | 218 | 219 | #### Precision Specifiers 220 | 221 | | Precision | Description | 222 | |-------------|-------------| 223 | | `.`(number) | For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.
For f and F specifiers: this is the number of digits to be printed after the decimal point. **By default, this is 6, and a maximum is defined when building the library**.
For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.
If the period is specified without an explicit value for precision, 0 is assumed. | 224 | | `.*` | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. | 225 | 226 | 227 | #### Length modifiers 228 | 229 | The length sub-specifier modifies the length of the data type. 230 | 231 | | Length | With `d`, `i` | With `u`,`o`,`x`, `X` | Support enabled by... | 232 | |----------|-----------------------------|--------------------------|---------------------------------------| 233 | | (none) | `int` | `unsigned int` | | 234 | | `hh` | `signed char` | `unsigned char` | | 235 | | `h` | `short int` | `unsigned short int` | | 236 | | `l` | `long int` | `unsigned long int` | | 237 | | `ll` | `long long int` | `unsigned long long int` | PRINTF_SUPPORT_LONG_LONG | 238 | | `j` | `intmax_t` | `uintmax_t` | | 239 | | `z` | signed version of `size_t` | `size_t` | | 240 | | `t` | `ptrdiff_t` | `ptrdiff_t` | | 241 | | `I8` | `int8_t` | `uint8_t` | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS | 242 | | `I16` | `int16_t` | `uint16_t` | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS | 243 | | `I32` | `int32_t` | `uint32_t` | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS | 244 | | `I64` | `int64_t` | `uint64_t` | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS | 245 | 246 | 247 | Notes: 248 | 249 | * The `L` modifier, for `long double`, is not currently supported. 250 | * A `"%zd"` or `"%zi"` takes a signed integer of the same size as `size_t`. 251 | * The implementation currently assumes `intmax_t` has the same size as either `long int` or `long long int`. If this is not the case for your platform, please open an issue. 252 | * 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 original value. 253 | 254 | ### Return Value 255 | 256 | Upon successful return, all functions return the number of characters written, _excluding_ the terminating NUL character used to end the string. 257 | Functions `snprintf()` and `vsnprintf()` don't write more than `count` bytes, including the terminating NUL character ('\0'). 258 | Anyway, if the output was truncated due to this limit, the return value is the number of characters that _could_ have been written. 259 | Notice that a value equal or larger than `count` indicates a truncation. Only when the returned value is non-negative and less than `count`, 260 | the string has been completely written with a terminating NUL. 261 | If any error is encountered, `-1` is returned. 262 | 263 | If `NULL` is passed for the `buffer` parameter, nothing is written, but the formatted length is returned. For example: 264 | ```C 265 | int length = sprintf(NULL, "Hello, world"); // length is set to 12 266 | ``` 267 | 268 | ## Contributing 269 | 270 | The following assumes Marco Paland's original repository remains mostly-inactive in terms of commits. 271 | 272 | 0. Give this repository a :star: (even if you've already starred the original repository). 273 | 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. 274 | 2. Fork the repository 275 | 3. Create your feature branch (`git checkout -b my-new-feature`). 276 | 4. Implement your feature/idea; don't forget to make sure all existing tests still pass. 277 | 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. 278 | 4. Commit your changes (`git commit -a -m "Added some feature"`) 279 | 5. Publish the branch (`git push origin my-new-feature`) 280 | 6. Create a new pull request against this repository. Note: Please don't create a PR without a related issue. 281 | 282 | I try to attend to issues and PRs promptly. 283 | 284 | 285 | ## License 286 | 287 | This library is published under the terms of the [MIT license](http://www.opensource.org/licenses/MIT). 288 | 289 | -------------------------------------------------------------------------------- /config/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ########### 2 | # Options # 3 | ########### 4 | 5 | # Boolean options which go into config.h 6 | 7 | option(SUPPORT_DECIMAL_SPECIFIERS "Support decimal notation floating-point conversion specifiers (%f,%F)" ON) 8 | option(SUPPORT_EXPONENTIAL_SPECIFIERS "Support exponential floating point format conversion specifiers (%e,%E,%g,%G)" ON) 9 | option(SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS "Support the I + bit size integer specifiers (%I8, %I16, %I32, %I64) as in Microsoft Visual C++" ON) 10 | option(SUPPORT_WRITEBACK_SPECIFIER "Support the length write-back specifier (%n)" ON) 11 | option(SUPPORT_LONG_LONG "Support long long integral types (allows for the ll length modifier and affects %p)" ON) 12 | option(USE_DOUBLE_INTERNALLY "Use the C `double` type - typically 64-bit in size - for internal floating-point arithmetic " ON) 13 | option(CHECK_FOR_NUL_IN_FORMAT_SPECIFIER "Be defensive in the undefined-behavior case of a format specifier not ending before the string ends" ON) 14 | 15 | set(ALIASING_MODES NONE HARD SOFT) 16 | set(ALIAS_STANDARD_FUNCTION_NAMES NONE CACHE STRING "Alias the standard library function names (printf, sprintf etc.) to the library's functions - concretely, via a macro, or not at all") 17 | set_property(CACHE ALIAS_STANDARD_FUNCTION_NAMES PROPERTY STRINGS ${ALIASING_MODES}) 18 | 19 | if (NOT ${ALIAS_STANDARD_FUNCTION_NAMES} STREQUAL NONE) 20 | set("ALIAS_STANDARD_FUNCTION_NAMES_${ALIAS_STANDARD_FUNCTION_NAMES}" 1) 21 | endif() 22 | 23 | foreach(opt 24 | SUPPORT_DECIMAL_SPECIFIERS 25 | SUPPORT_EXPONENTIAL_SPECIFIERS 26 | SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 27 | SUPPORT_WRITEBACK_SPECIFIER 28 | SUPPORT_LONG_LONG 29 | USE_DOUBLE_INTERNALLY 30 | ALIAS_STANDARD_FUNCTION_NAMES_SOFT 31 | ALIAS_STANDARD_FUNCTION_NAMES_HARD 32 | CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 33 | ) 34 | if (${${opt}}) 35 | set("PRINTF_${opt}" 1) 36 | else() 37 | set("PRINTF_${opt}" 0) 38 | endif() 39 | endforeach() 40 | 41 | # Numeric defines which go into printf_config.h 42 | 43 | set(PRINTF_INTEGER_BUFFER_SIZE "32" CACHE STRING "Integer to string conversion buffer size") 44 | set(PRINTF_DECIMAL_BUFFER_SIZE "32" CACHE STRING "Floating-point to decimal conversion buffer size") 45 | set(DEFAULT_FLOAT_PRECISION "6" CACHE STRING "Default precision when printing floating-point values") 46 | 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") 47 | set(LOG10_TAYLOR_TERMS "4" CACHE STRING "The number of terms in a Taylor series expansion of log_10(x) to use for approximation") 48 | 49 | ################### 50 | # Generate Header # 51 | ################### 52 | 53 | set(CONFIG_HEADER_DIR "${CMAKE_CURRENT_BINARY_DIR}/include" CACHE INTERNAL "") 54 | configure_file("printf_config.h.in" "${CONFIG_HEADER_DIR}/printf_config.h" @ONLY) 55 | 56 | ######################### 57 | # Package config header # 58 | ######################### 59 | 60 | # For now, these go in CMAKE_BINARY_DIR to preserve the previous 61 | # output structure. They *could* go into config/ with CMAKE_CURRENT_BINARY_DIR 62 | # though maybe that doesn't really work. 63 | 64 | include(CMakePackageConfigHelpers) 65 | 66 | configure_package_config_file( 67 | "printf-config.cmake.in" 68 | "${CMAKE_BINARY_DIR}/printf-config.cmake" 69 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/printf" 70 | ) 71 | 72 | write_basic_package_version_file( 73 | "${CMAKE_BINARY_DIR}/printf-config-version.cmake" 74 | VERSION ${PROJECT_VERSION} 75 | COMPATIBILITY SameMinorVersion 76 | ) 77 | 78 | install( 79 | FILES 80 | "${CMAKE_BINARY_DIR}/printf-config-version.cmake" 81 | "${CMAKE_BINARY_DIR}/printf-config.cmake" 82 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/printf" 83 | ) 84 | -------------------------------------------------------------------------------- /config/printf-config.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(PRINTF_CMAKE_CONFIG_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | 3 | if(NOT TARGET printf::printf) 4 | include("${PRINTF_CMAKE_CONFIG_DIR}/printf-targets.cmake") 5 | endif() -------------------------------------------------------------------------------- /config/printf_config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef PRINTF_CONFIG_H_ 3 | #define PRINTF_CONFIG_H_ 4 | 5 | #define PRINTF_SUPPORT_DECIMAL_SPECIFIERS @PRINTF_SUPPORT_DECIMAL_SPECIFIERS@ 6 | #define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS @PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS@ 7 | #define PRINTF_SUPPORT_WRITEBACK_SPECIFIER @PRINTF_SUPPORT_WRITEBACK_SPECIFIER@ 8 | #define PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS @PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS@ 9 | #define PRINTF_SUPPORT_LONG_LONG @PRINTF_SUPPORT_LONG_LONG@ 10 | #define PRINTF_USE_DOUBLE_INTERNALLY @PRINTF_USE_DOUBLE_INTERNALLY@ 11 | #define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT @PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT@ 12 | #define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD @PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD@ 13 | 14 | #define PRINTF_INTEGER_BUFFER_SIZE @PRINTF_INTEGER_BUFFER_SIZE@ 15 | #define PRINTF_DECIMAL_BUFFER_SIZE @PRINTF_DECIMAL_BUFFER_SIZE@ 16 | #define PRINTF_DEFAULT_FLOAT_PRECISION @DEFAULT_FLOAT_PRECISION@ 17 | #define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL @MAX_INTEGRAL_DIGITS_FOR_DECIMAL@ 18 | #define PRINTF_LOG10_TAYLOR_TERMS @LOG10_TAYLOR_TERMS@ 19 | #define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER @PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER@ 20 | 21 | #endif // PRINTF_CONFIG_H_ 22 | 23 | -------------------------------------------------------------------------------- /src/printf/printf.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author (c) Eyal Rozenberg 3 | * 2021-2022, Haifa, Palestine/Israel 4 | * @author (c) Marco Paland (info@paland.com) 5 | * 2014-2019, PALANDesign Hannover, Germany 6 | * 7 | * @note Others have made smaller contributions to this file: see the 8 | * contributors page at https://github.com/eyalroz/printf/graphs/contributors 9 | * or ask one of the authors. The original code for exponential specifiers was 10 | * contributed by Martijn Jasperse . 11 | * 12 | * @brief Small stand-alone implementation of the printf family of functions 13 | * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with 14 | * a very limited resources. 15 | * 16 | * @note the implementations are thread-safe; re-entrant; use no functions from 17 | * the standard library; and do not dynamically allocate any memory. 18 | * 19 | * @license The MIT License (MIT) 20 | * 21 | * Permission is hereby granted, free of charge, to any person obtaining a copy 22 | * of this software and associated documentation files (the "Software"), to deal 23 | * in the Software without restriction, including without limitation the rights 24 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | * copies of the Software, and to permit persons to whom the Software is 26 | * furnished to do so, subject to the following conditions: 27 | * 28 | * The above copyright notice and this permission notice shall be included in 29 | * all copies or substantial portions of the Software. 30 | * 31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 37 | * THE SOFTWARE. 38 | */ 39 | 40 | #include 41 | 42 | #ifdef __cplusplus 43 | #include 44 | #include 45 | #else 46 | #include 47 | #include 48 | #include 49 | #endif // __cplusplus 50 | 51 | #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 52 | # define printf_ printf 53 | # define sprintf_ sprintf 54 | # define vsprintf_ vsprintf 55 | # define snprintf_ snprintf 56 | # define vsnprintf_ vsnprintf 57 | # define vprintf_ vprintf 58 | #endif 59 | 60 | // If you have -Wfloat-equal enabled, this source code will emit a number 61 | // of warnings for float comparisons for equality. However, within the 62 | // context of their use in this library, these are ok to use. So we 63 | // disable the warning for this file. 64 | // Define PRINTF_DISABLE_WARNING_PRAGMAS to disable this block 65 | #ifndef PRINTF_DISABLE_WARNING_PRAGMAS 66 | #if defined(__GNUC__) || defined (__clang__) 67 | #pragma GCC diagnostic push 68 | #pragma GCC diagnostic ignored "-Wfloat-equal" 69 | #endif // __GNUC__ || defined (__clang__) 70 | #endif // PRINTF_DISABLE_WARNING_PRAGMAS 71 | 72 | // 'ntoa' conversion buffer size, this must be big enough to hold one converted 73 | // numeric number including padded zeros (dynamically created on stack) 74 | #ifndef PRINTF_INTEGER_BUFFER_SIZE 75 | #define PRINTF_INTEGER_BUFFER_SIZE 32 76 | #endif 77 | 78 | // size of the fixed (on-stack) buffer for printing individual decimal numbers. 79 | // this must be big enough to hold one converted floating-point value including 80 | // padded zeros. 81 | #ifndef PRINTF_DECIMAL_BUFFER_SIZE 82 | #define PRINTF_DECIMAL_BUFFER_SIZE 32 83 | #endif 84 | 85 | // Support for the decimal notation floating point conversion specifiers (%f, %F) 86 | #ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS 87 | #define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 88 | #endif 89 | 90 | // Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) 91 | #ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 92 | #define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 93 | #endif 94 | 95 | // Support for the length write-back specifier (%n) 96 | #ifndef PRINTF_SUPPORT_WRITEBACK_SPECIFIER 97 | #define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 98 | #endif 99 | 100 | // Default precision for the floating point conversion specifiers (the C standard sets this at 6) 101 | #ifndef PRINTF_DEFAULT_FLOAT_PRECISION 102 | #define PRINTF_DEFAULT_FLOAT_PRECISION 6 103 | #endif 104 | 105 | // Default choice of type to use for internal floating-point computations 106 | #ifndef PRINTF_USE_DOUBLE_INTERNALLY 107 | #define PRINTF_USE_DOUBLE_INTERNALLY 1 108 | #endif 109 | 110 | // Default choice - do not support MSVC int specifiers 111 | #ifndef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 112 | #define PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 0 113 | #endif 114 | 115 | // According to the C languages standard, printf() and related functions must be able to print any 116 | // integral number in floating-point notation, regardless of length, when using the %f specifier - 117 | // possibly hundreds of characters, potentially overflowing your buffers. In this implementation, 118 | // all values beyond this threshold are switched to exponential notation. 119 | #ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 120 | #define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 121 | #endif 122 | 123 | // Support for the long long integral types (with the ll, z and t length modifiers for specifiers 124 | // %d,%i,%o,%x,%X,%u, and with the %p specifier). 125 | #ifndef PRINTF_SUPPORT_LONG_LONG 126 | #define PRINTF_SUPPORT_LONG_LONG 1 127 | #endif 128 | 129 | // The number of terms in a Taylor series expansion of log_10(x) to 130 | // use for approximation - including the power-zero term (i.e. the 131 | // value at the point of expansion). 132 | #ifndef PRINTF_LOG10_TAYLOR_TERMS 133 | #define PRINTF_LOG10_TAYLOR_TERMS 4 134 | #endif 135 | 136 | #if PRINTF_LOG10_TAYLOR_TERMS <= 1 137 | #error "At least one non-constant Taylor expansion is necessary for the log10() calculation" 138 | #endif 139 | 140 | // Be extra-safe, and don't assume format specifiers are completed correctly 141 | // before the format string end. 142 | #ifndef PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 143 | #define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 1 144 | #endif 145 | 146 | #define PRINTF_PREFER_DECIMAL false 147 | #define PRINTF_PREFER_EXPONENTIAL true 148 | 149 | /////////////////////////////////////////////////////////////////////////////// 150 | 151 | // The following will convert the number-of-digits into an exponential-notation literal 152 | #define PRINTF_CONCATENATE(s1, s2) s1##s2 153 | #define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) 154 | #define PRINTF_FLOAT_NOTATION_THRESHOLD ((floating_point_t) PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) 155 | 156 | // internal flag definitions 157 | #define FLAGS_ZEROPAD (1U << 0U) 158 | #define FLAGS_LEFT (1U << 1U) 159 | #define FLAGS_PLUS (1U << 2U) 160 | #define FLAGS_SPACE (1U << 3U) 161 | #define FLAGS_HASH (1U << 4U) 162 | #define FLAGS_UPPERCASE (1U << 5U) 163 | #define FLAGS_CHAR (1U << 6U) 164 | #define FLAGS_SHORT (1U << 7U) 165 | #define FLAGS_INT (1U << 8U) 166 | // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 167 | #define FLAGS_LONG (1U << 9U) 168 | #define FLAGS_LONG_LONG (1U << 10U) 169 | #define FLAGS_PRECISION (1U << 11U) 170 | #define FLAGS_ADAPT_EXP (1U << 12U) 171 | #define FLAGS_POINTER (1U << 13U) 172 | // Note: Similar, but not identical, effect as FLAGS_HASH 173 | #define FLAGS_SIGNED (1U << 14U) 174 | #define FLAGS_LONG_DOUBLE (1U << 15U) 175 | // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 176 | 177 | #if PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 178 | 179 | #define FLAGS_INT8 FLAGS_CHAR 180 | 181 | 182 | #if (SHRT_MAX == 32767LL) 183 | #define FLAGS_INT16 FLAGS_SHORT 184 | #elif (INT_MAX == 32767LL) 185 | #define FLAGS_INT16 FLAGS_INT 186 | #elif (LONG_MAX == 32767LL) 187 | #define FLAGS_INT16 FLAGS_LONG 188 | #elif (LLONG_MAX == 32767LL) 189 | #define FLAGS_INT16 FLAGS_LONG_LONG 190 | #else 191 | #error "No basic integer type has a size of 16 bits exactly" 192 | #endif 193 | 194 | #if (SHRT_MAX == 2147483647LL) 195 | #define FLAGS_INT32 FLAGS_SHORT 196 | #elif (INT_MAX == 2147483647LL) 197 | #define FLAGS_INT32 FLAGS_INT 198 | #elif (LONG_MAX == 2147483647LL) 199 | #define FLAGS_INT32 FLAGS_LONG 200 | #elif (LLONG_MAX == 2147483647LL) 201 | #define FLAGS_INT32 FLAGS_LONG_LONG 202 | #else 203 | #error "No basic integer type has a size of 32 bits exactly" 204 | #endif 205 | 206 | #if (SHRT_MAX == 9223372036854775807LL) 207 | #define FLAGS_INT64 FLAGS_SHORT 208 | #elif (INT_MAX == 9223372036854775807LL) 209 | #define FLAGS_INT64 FLAGS_INT 210 | #elif (LONG_MAX == 9223372036854775807LL) 211 | #define FLAGS_INT64 FLAGS_LONG 212 | #elif (LLONG_MAX == 9223372036854775807LL) 213 | #define FLAGS_INT64 FLAGS_LONG_LONG 214 | #else 215 | #error "No basic integer type has a size of 64 bits exactly" 216 | #endif 217 | 218 | #endif // PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 219 | 220 | 221 | typedef unsigned int printf_flags_t; 222 | 223 | #define BASE_BINARY 2 224 | #define BASE_OCTAL 8 225 | #define BASE_DECIMAL 10 226 | #define BASE_HEX 16 227 | 228 | typedef uint8_t numeric_base_t; 229 | 230 | #if PRINTF_SUPPORT_LONG_LONG 231 | typedef unsigned long long printf_unsigned_value_t; 232 | typedef long long printf_signed_value_t; 233 | #else 234 | typedef unsigned long printf_unsigned_value_t; 235 | typedef long printf_signed_value_t; 236 | #endif // PRINTF_SUPPORT_LONG_LONG 237 | 238 | // The printf()-family functions return an `int`; it is therefore 239 | // unnecessary/inappropriate to use size_t - often larger than int 240 | // in practice - for non-negative related values, such as widths, 241 | // precisions, offsets into buffers used for printing and the sizes 242 | // of these buffers. instead, we use: 243 | typedef unsigned int printf_size_t; 244 | #define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX 245 | // If we were to nitpick, this would actually be INT_MAX + 1, 246 | // since INT_MAX is the maximum return value, which excludes the 247 | // trailing '\0'. 248 | 249 | #define SIGN(_negative,_x) ( (_negative) ? -(_x) : (_x)) 250 | 251 | #if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) 252 | #include 253 | #if FLT_RADIX != 2 254 | #error "Non-binary-radix floating-point types are unsupported." 255 | #endif // PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 256 | 257 | /** 258 | * This library supports taking float-point arguments up to and including 259 | * long double's; but - it currently does _not_ support internal 260 | * representation and manipulation of values as long doubles; the options 261 | * are either single-precision `float` or double-precision `double`. 262 | */ 263 | #if PRINTF_USE_DOUBLE_INTERNALLY 264 | typedef double floating_point_t; 265 | #define FP_TYPE_MANT_DIG DBL_MANT_DIG 266 | #else 267 | typedef float floating_point_t; 268 | #define FP_TYPE_MANT_DIG FLT_MANT_DIG 269 | #endif 270 | 271 | #define NUM_DECIMAL_DIGITS_IN_INT64_T 18 272 | 273 | #if FP_TYPE_MANT_DIG == 24 274 | 275 | typedef uint32_t printf_fp_uint_t; 276 | #define FP_TYPE_SIZE_IN_BITS 32 277 | #define FP_TYPE_EXPONENT_MASK 0xFFU 278 | #define FP_TYPE_BASE_EXPONENT 127 279 | #define FP_TYPE_MAX FLT_MAX 280 | #define FP_TYPE_MAX_10_EXP FLT_MAX_10_EXP 281 | #define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 282 | #define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-38f 283 | #define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 10 284 | 285 | #elif FP_TYPE_MANT_DIG == 53 286 | 287 | typedef uint64_t printf_fp_uint_t; 288 | #define FP_TYPE_SIZE_IN_BITS 64 289 | #define FP_TYPE_EXPONENT_MASK 0x7FFU 290 | #define FP_TYPE_BASE_EXPONENT 1023 291 | #define FP_TYPE_MAX DBL_MAX 292 | #define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP 293 | #define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP 294 | #define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 295 | #define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-308 296 | #define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T - 1 297 | 298 | 299 | #else // FP_TYPE_MANT_DIG is neither 24 nor 53 300 | #error "Unsupported floating point type configuration" 301 | #endif // FP_TYPE_MANT_DIG 302 | #define FP_TYPE_STORED_MANTISSA_BITS (FP_TYPE_MANT_DIG - 1) 303 | 304 | typedef union { 305 | printf_fp_uint_t U; 306 | floating_point_t F; 307 | } floating_point_with_bit_access; 308 | 309 | // This is unnecessary in C99, since compound initializers can be used, 310 | // but: 311 | // 1. Some compilers are finicky about this; 312 | // 2. Some people may want to convert this to C89; 313 | // 3. If you try to use it as C++, only C++20 supports compound literals 314 | static inline floating_point_with_bit_access get_bit_access(floating_point_t x) 315 | { 316 | floating_point_with_bit_access dwba; 317 | dwba.F = x; 318 | return dwba; 319 | } 320 | 321 | static inline int get_sign_bit(floating_point_t x) 322 | { 323 | // The sign is stored in the highest bit 324 | return (int) (get_bit_access(x).U >> (FP_TYPE_SIZE_IN_BITS - 1)); 325 | } 326 | 327 | static inline int get_exp2(floating_point_with_bit_access x) 328 | { 329 | // The exponent in an IEEE-754 floating-point number occupies a contiguous 330 | // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An 331 | // unsigned offset from some negative value (with the extremal offset values reserved for 332 | // special use). 333 | return (int)((x.U >> FP_TYPE_STORED_MANTISSA_BITS ) & FP_TYPE_EXPONENT_MASK) - FP_TYPE_BASE_EXPONENT; 334 | } 335 | #define PRINTF_ABS(_x) SIGN( (_x) < 0, (_x) ) 336 | 337 | #endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) 338 | 339 | // Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid 340 | // and well-defined, but if you're not careful you can easily trigger undefined 341 | // behavior with -LONG_MIN or -LLONG_MIN 342 | #define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) )) 343 | 344 | // wrapper (used as buffer) for output function type 345 | // 346 | // One of the following must hold: 347 | // 1. max_chars is 0 348 | // 2. buffer is non-null 349 | // 3. function is non-null 350 | // 351 | // ... otherwise bad things will happen. 352 | typedef struct { 353 | void (*function)(char c, void* extra_arg); 354 | void* extra_function_arg; 355 | char* buffer; 356 | printf_size_t pos; 357 | printf_size_t max_chars; 358 | } output_gadget_t; 359 | 360 | // Note: This function currently assumes it is not passed a '\0' c, 361 | // or alternatively, that '\0' can be passed to the function in the output 362 | // gadget. The former assumption holds within the printf library. It also 363 | // assumes that the output gadget has been properly initialized. 364 | static inline void putchar_via_gadget(output_gadget_t* gadget, char c) 365 | { 366 | printf_size_t write_pos = gadget->pos++; 367 | // We're _always_ increasing pos, so as to count how may characters 368 | // _would_ have been written if not for the max_chars limitation 369 | if (write_pos >= gadget->max_chars) { 370 | return; 371 | } 372 | if (gadget->function != NULL) { 373 | // No check for c == '\0' . 374 | gadget->function(c, gadget->extra_function_arg); 375 | } 376 | else { 377 | // it must be the case that gadget->buffer != NULL , due to the constraint 378 | // on output_gadget_t ; and note we're relying on write_pos being non-negative. 379 | gadget->buffer[write_pos] = c; 380 | } 381 | } 382 | 383 | // Possibly-write the string-terminating '\0' character 384 | static inline void append_termination_with_gadget(output_gadget_t* gadget) 385 | { 386 | if (gadget->function != NULL || gadget->max_chars == 0) { 387 | return; 388 | } 389 | if (gadget->buffer == NULL) { 390 | return; 391 | } 392 | printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; 393 | gadget->buffer[null_char_pos] = '\0'; 394 | } 395 | 396 | // We can't use putchar_ as is, since our output gadget 397 | // only takes pointers to functions with an extra argument 398 | static inline void putchar_wrapper(char c, void* unused) 399 | { 400 | (void) unused; 401 | putchar_(c); 402 | } 403 | 404 | static inline output_gadget_t discarding_gadget(void) 405 | { 406 | output_gadget_t gadget; 407 | gadget.function = NULL; 408 | gadget.extra_function_arg = NULL; 409 | gadget.buffer = NULL; 410 | gadget.pos = 0; 411 | gadget.max_chars = 0; 412 | return gadget; 413 | } 414 | 415 | static inline output_gadget_t buffer_gadget(char* buffer, size_t buffer_size) 416 | { 417 | printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? 418 | PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t) buffer_size; 419 | output_gadget_t result = discarding_gadget(); 420 | if (buffer != NULL) { 421 | result.buffer = buffer; 422 | result.max_chars = usable_buffer_size; 423 | } 424 | return result; 425 | } 426 | 427 | static inline output_gadget_t function_gadget(void (*function)(char, void*), void* extra_arg) 428 | { 429 | output_gadget_t result = discarding_gadget(); 430 | result.function = function; 431 | result.extra_function_arg = extra_arg; 432 | result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; 433 | return result; 434 | } 435 | 436 | static inline output_gadget_t extern_putchar_gadget(void) 437 | { 438 | return function_gadget(putchar_wrapper, NULL); 439 | } 440 | 441 | // internal secure strlen 442 | // @return The length of the string (excluding the terminating 0) limited by 'maxsize' 443 | // @note strlen uses size_t, but wes only use this function with printf_size_t 444 | // variables - hence the signature. 445 | static inline printf_size_t strnlen_s_(const char* str, printf_size_t maxsize) 446 | { 447 | const char* s; 448 | for (s = str; *s && maxsize--; ++s); 449 | return (printf_size_t)(s - str); 450 | } 451 | 452 | 453 | // internal test if char is a digit (0-9) 454 | // @return true if char is a digit 455 | static inline bool is_digit_(char ch) 456 | { 457 | return (ch >= '0') && (ch <= '9'); 458 | } 459 | 460 | 461 | // internal ASCII string to printf_size_t conversion 462 | static printf_size_t atou_(const char** str) 463 | { 464 | printf_size_t i = 0U; 465 | while (is_digit_(**str)) { 466 | i = i * 10U + (printf_size_t)(*((*str)++) - '0'); 467 | } 468 | return i; 469 | } 470 | 471 | 472 | // output the specified string in reverse, taking care of any zero-padding 473 | static void out_rev_(output_gadget_t* output, const char* buf, printf_size_t len, printf_size_t width, printf_flags_t flags) 474 | { 475 | const printf_size_t start_pos = output->pos; 476 | 477 | // pad spaces up to given width 478 | if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { 479 | for (printf_size_t i = len; i < width; i++) { 480 | putchar_via_gadget(output, ' '); 481 | } 482 | } 483 | 484 | // reverse string 485 | while (len) { 486 | putchar_via_gadget(output, buf[--len]); 487 | } 488 | 489 | // append pad spaces up to given width 490 | if (flags & FLAGS_LEFT) { 491 | while (output->pos - start_pos < width) { 492 | putchar_via_gadget(output, ' '); 493 | } 494 | } 495 | } 496 | 497 | 498 | // Invoked by print_integer after the actual number has been printed, performing necessary 499 | // work on the number's prefix (as the number is initially printed in reverse order) 500 | static void print_integer_finalization(output_gadget_t* output, char* buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) 501 | { 502 | printf_size_t unpadded_len = len; 503 | 504 | // pad with leading zeros 505 | { 506 | if (!(flags & FLAGS_LEFT)) { 507 | if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { 508 | width--; 509 | } 510 | while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { 511 | buf[len++] = '0'; 512 | } 513 | } 514 | 515 | while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { 516 | buf[len++] = '0'; 517 | } 518 | 519 | if (base == BASE_OCTAL && (len > unpadded_len)) { 520 | // Since we've written some zeros, we've satisfied the alternative format leading space requirement 521 | flags &= ~FLAGS_HASH; 522 | } 523 | } 524 | 525 | // handle hash 526 | if (flags & (FLAGS_HASH | FLAGS_POINTER)) { 527 | if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { 528 | // Let's take back some padding digits to fit in what will eventually 529 | // be the format-specific prefix 530 | if (unpadded_len < len) { 531 | len--; // This should suffice for BASE_OCTAL 532 | } 533 | if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) { 534 | len--; // ... and an extra one for 0x or 0b 535 | } 536 | } 537 | if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { 538 | buf[len++] = 'x'; 539 | } 540 | else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { 541 | buf[len++] = 'X'; 542 | } 543 | else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { 544 | buf[len++] = 'b'; 545 | } 546 | if (len < PRINTF_INTEGER_BUFFER_SIZE) { 547 | buf[len++] = '0'; 548 | } 549 | } 550 | 551 | if (len < PRINTF_INTEGER_BUFFER_SIZE) { 552 | if (negative) { 553 | buf[len++] = '-'; 554 | } 555 | else if (flags & FLAGS_PLUS) { 556 | buf[len++] = '+'; // ignore the space if the '+' exists 557 | } 558 | else if (flags & FLAGS_SPACE) { 559 | buf[len++] = ' '; 560 | } 561 | } 562 | 563 | out_rev_(output, buf, len, width, flags); 564 | } 565 | 566 | // An internal itoa-like function 567 | static void print_integer(output_gadget_t* output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) 568 | { 569 | char buf[PRINTF_INTEGER_BUFFER_SIZE]; 570 | printf_size_t len = 0U; 571 | 572 | if (!value) { 573 | if ( !(flags & FLAGS_PRECISION) ) { 574 | buf[len++] = '0'; 575 | flags &= ~FLAGS_HASH; 576 | // We drop this flag this since either the alternative and regular modes of the specifier 577 | // don't differ on 0 values, or (in the case of octal) we've already provided the special 578 | // handling for this mode. 579 | } 580 | else if (base == BASE_HEX) { 581 | flags &= ~FLAGS_HASH; 582 | // We drop this flag this since either the alternative and regular modes of the specifier 583 | // don't differ on 0 values 584 | } 585 | } 586 | else { 587 | do { 588 | const char digit = (char)(value % base); 589 | buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); 590 | value /= base; 591 | } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); 592 | } 593 | 594 | print_integer_finalization(output, buf, len, negative, base, precision, width, flags); 595 | } 596 | 597 | #if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) 598 | 599 | // Stores a fixed-precision representation of a floating-point number relative 600 | // to a fixed precision (which cannot be determined by examining this structure) 601 | struct floating_point_components { 602 | int_fast64_t integral; 603 | int_fast64_t fractional; 604 | // ... truncation of the actual fractional part of the floating_point_t value, scaled 605 | // by the precision value 606 | bool is_negative; 607 | }; 608 | 609 | static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + 1] = { 610 | 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, 1e10 611 | #if PRINTF_MAX_PRECOMPUTED_POWER_OF_10 > 10 612 | , 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 613 | #endif 614 | }; 615 | 616 | // Note: This value does not mean that all floating-point values printed with the 617 | // library will be correct up to this precision; it is just an upper-bound for 618 | // avoiding buffer overruns and such 619 | #define PRINTF_MAX_SUPPORTED_PRECISION (NUM_DECIMAL_DIGITS_IN_INT64_T - 1) 620 | 621 | 622 | // Break up a floating-point number - which is known to be a finite non-negative number - 623 | // into its base-10 parts: integral - before the decimal point, and fractional - after it. 624 | // Taken the precision into account, but does not change it even internally. 625 | static struct floating_point_components get_components(floating_point_t number, printf_size_t precision) 626 | { 627 | struct floating_point_components number_; 628 | number_.is_negative = get_sign_bit(number); 629 | floating_point_t abs_number = SIGN(number_.is_negative, number); 630 | number_.integral = (int_fast64_t) abs_number; 631 | floating_point_t scaled_remainder = (abs_number - (floating_point_t) number_.integral) * powers_of_10[precision]; 632 | number_.fractional = (int_fast64_t) scaled_remainder; // for precision == 0U, this will be 0 633 | 634 | floating_point_t remainder = scaled_remainder - (floating_point_t) number_.fractional; 635 | const floating_point_t one_half = (floating_point_t) 0.5; 636 | 637 | if (remainder > one_half) { 638 | ++number_.fractional; 639 | // handle rollover, e.g. case 0.99 with precision 1 is 1.0 640 | if ((floating_point_t) number_.fractional >= powers_of_10[precision]) { 641 | number_.fractional = 0; 642 | ++number_.integral; 643 | } 644 | } 645 | else if ((remainder == one_half) && (number_.fractional & 1U)) { 646 | // Banker's rounding, i.e. round half to even: 647 | // 1.5 -> 2, but 2.5 -> 2 648 | ++number_.fractional; 649 | } 650 | 651 | if (precision == 0U) { 652 | remainder = abs_number - (floating_point_t) number_.integral; 653 | if ((remainder == one_half) && (number_.integral & 1U)) { 654 | // Banker's rounding, i.e. round half to even: 655 | // 1.5 -> 2, but 2.5 -> 2 656 | ++number_.integral; 657 | } 658 | } 659 | return number_; 660 | } 661 | 662 | #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 663 | struct scaling_factor { 664 | floating_point_t raw_factor; 665 | bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it 666 | }; 667 | 668 | static floating_point_t apply_scaling(floating_point_t num, struct scaling_factor normalization) 669 | { 670 | return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; 671 | } 672 | 673 | static floating_point_t unapply_scaling(floating_point_t normalized, struct scaling_factor normalization) 674 | { 675 | return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; 676 | } 677 | 678 | static struct scaling_factor update_normalization(struct scaling_factor sf, floating_point_t extra_multiplicative_factor) 679 | { 680 | struct scaling_factor result; 681 | if (sf.multiply) { 682 | result.multiply = true; 683 | result.raw_factor = sf.raw_factor * extra_multiplicative_factor; 684 | } 685 | else { 686 | int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); 687 | int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); 688 | 689 | // Divide the larger-exponent raw raw_factor by the smaller 690 | if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) { 691 | result.multiply = false; 692 | result.raw_factor = sf.raw_factor / extra_multiplicative_factor; 693 | } 694 | else { 695 | result.multiply = true; 696 | result.raw_factor = extra_multiplicative_factor / sf.raw_factor; 697 | } 698 | } 699 | return result; 700 | } 701 | 702 | static struct floating_point_components get_normalized_components(bool negative, printf_size_t precision, floating_point_t non_normalized, struct scaling_factor normalization, int floored_exp10) 703 | { 704 | struct floating_point_components components; 705 | components.is_negative = negative; 706 | floating_point_t scaled = apply_scaling(non_normalized, normalization); 707 | 708 | bool close_to_representation_extremum = ( (-floored_exp10 + (int) precision) >= FP_TYPE_MAX_10_EXP - 1 ); 709 | if (close_to_representation_extremum) { 710 | // We can't have a normalization factor which also accounts for the precision, i.e. moves 711 | // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. 712 | // So, we'll give up early on getting extra precision... 713 | return get_components(SIGN(negative, scaled), precision); 714 | } 715 | components.integral = (int_fast64_t) scaled; 716 | floating_point_t remainder = non_normalized - unapply_scaling((floating_point_t) components.integral, normalization); 717 | floating_point_t prec_power_of_10 = powers_of_10[precision]; 718 | struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); 719 | floating_point_t scaled_remainder = apply_scaling(remainder, account_for_precision); 720 | floating_point_t rounding_threshold = 0.5; 721 | 722 | components.fractional = (int_fast64_t) scaled_remainder; // when precision == 0, the assigned value should be 0 723 | scaled_remainder -= (floating_point_t) components.fractional; //when precision == 0, this will not change scaled_remainder 724 | 725 | components.fractional += (scaled_remainder >= rounding_threshold); 726 | if (scaled_remainder == rounding_threshold) { 727 | // banker's rounding: Round towards the even number (making the mean error 0) 728 | components.fractional &= ~((int_fast64_t) 0x1); 729 | } 730 | // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), 731 | // and must then be corrected into (1, 0). 732 | // Note: for precision = 0, this will "translate" the rounding effect from 733 | // the fractional part to the integral part where it should actually be 734 | // felt (as prec_power_of_10 is 1) 735 | if ((floating_point_t) components.fractional >= prec_power_of_10) { 736 | components.fractional = 0; 737 | ++components.integral; 738 | } 739 | return components; 740 | } 741 | #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 742 | 743 | static void print_broken_up_decimal( 744 | struct floating_point_components number_, output_gadget_t* output, printf_size_t precision, 745 | printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) 746 | { 747 | if (precision != 0U) { 748 | // do fractional part, as an unsigned number 749 | 750 | printf_size_t count = precision; 751 | 752 | // %g/%G mandates we skip the trailing 0 digits... 753 | if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) { 754 | while(true) { 755 | int_fast64_t digit = number_.fractional % 10U; 756 | if (digit != 0) { 757 | break; 758 | } 759 | --count; 760 | number_.fractional /= 10U; 761 | 762 | } 763 | // ... and even the decimal point if there are no 764 | // non-zero fractional part digits (see below) 765 | } 766 | 767 | if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH) ) { 768 | while (len < PRINTF_DECIMAL_BUFFER_SIZE) { 769 | --count; 770 | buf[len++] = (char)('0' + number_.fractional % 10U); 771 | if (!(number_.fractional /= 10U)) { 772 | break; 773 | } 774 | } 775 | // add extra 0s 776 | while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) { 777 | buf[len++] = '0'; 778 | --count; 779 | } 780 | if (len < PRINTF_DECIMAL_BUFFER_SIZE) { 781 | buf[len++] = '.'; 782 | } 783 | } 784 | } 785 | else { 786 | if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { 787 | buf[len++] = '.'; 788 | } 789 | } 790 | 791 | // Write the integer part of the number (it comes after the fractional 792 | // since the character order is reversed) 793 | while (len < PRINTF_DECIMAL_BUFFER_SIZE) { 794 | buf[len++] = (char)('0' + (number_.integral % 10)); 795 | if (!(number_.integral /= 10)) { 796 | break; 797 | } 798 | } 799 | 800 | // pad leading zeros 801 | if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { 802 | if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { 803 | width--; 804 | } 805 | while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { 806 | buf[len++] = '0'; 807 | } 808 | } 809 | 810 | if (len < PRINTF_DECIMAL_BUFFER_SIZE) { 811 | if (number_.is_negative) { 812 | buf[len++] = '-'; 813 | } 814 | else if (flags & FLAGS_PLUS) { 815 | buf[len++] = '+'; // ignore the space if the '+' exists 816 | } 817 | else if (flags & FLAGS_SPACE) { 818 | buf[len++] = ' '; 819 | } 820 | } 821 | 822 | out_rev_(output, buf, len, width, flags); 823 | } 824 | 825 | // internal ftoa for fixed decimal floating point 826 | static void print_decimal_number(output_gadget_t* output, floating_point_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len) 827 | { 828 | struct floating_point_components value_ = get_components(number, precision); 829 | print_broken_up_decimal(value_, output, precision, width, flags, buf, len); 830 | } 831 | 832 | #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 833 | 834 | // A floor function - but one which only works for numbers whose 835 | // floor value is representable by an int. 836 | static int bastardized_floor(floating_point_t x) 837 | { 838 | if (x >= 0) { return (int) x; } 839 | int n = (int) x; 840 | return ( ((floating_point_t) n) == x ) ? n : n-1; 841 | } 842 | 843 | // Computes the base-10 logarithm of the input number - which must be an actual 844 | // positive number (not infinity or NaN, nor a sub-normal) 845 | static floating_point_t log10_of_positive(floating_point_t positive_number) 846 | { 847 | // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). 848 | // 849 | // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of 850 | // our input number, and need only solve log_10(M) for M between 1 and 2 (as 851 | // the base-2 mantissa is always 1-point-something). In that limited range, a 852 | // Taylor series expansion of log10(x) should serve us well enough; and we'll 853 | // take the mid-point, 1.5, as the point of expansion. 854 | 855 | floating_point_with_bit_access dwba = get_bit_access(positive_number); 856 | // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) 857 | int exp2 = get_exp2(dwba); 858 | // drop the exponent, so dwba.F comes into the range [1,2) 859 | dwba.U = (dwba.U & (((printf_fp_uint_t) (1) << FP_TYPE_STORED_MANTISSA_BITS) - 1U)) | 860 | ((printf_fp_uint_t) FP_TYPE_BASE_EXPONENT << FP_TYPE_STORED_MANTISSA_BITS); 861 | floating_point_t z = (dwba.F - (floating_point_t) 1.5); 862 | return ( 863 | // Taylor expansion around 1.5: 864 | (floating_point_t) 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) 865 | + z * (floating_point_t) 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) 866 | #if PRINTF_LOG10_TAYLOR_TERMS > 2 867 | - z*z * (floating_point_t) 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) 868 | #if PRINTF_LOG10_TAYLOR_TERMS > 3 869 | + z*z*z * (floating_point_t) 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) 870 | #endif 871 | #endif 872 | // exact log_2 of the exponent x, with logarithm base change 873 | + (floating_point_t) exp2 * (floating_point_t) 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) 874 | ); 875 | } 876 | 877 | 878 | static floating_point_t pow10_of_int(int floored_exp10) 879 | { 880 | // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. 881 | if (floored_exp10 == FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10) { 882 | return FP_TYPE_MAX_SUBNORMAL_POWER_OF_10; 883 | } 884 | // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow 885 | floating_point_with_bit_access dwba; 886 | int exp2 = bastardized_floor((floating_point_t) (floored_exp10 * 3.321928094887362 + 0.5)); 887 | const floating_point_t z = (floating_point_t) (floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453); 888 | const floating_point_t z2 = z * z; 889 | dwba.U = ((printf_fp_uint_t)(exp2) + FP_TYPE_BASE_EXPONENT) << FP_TYPE_STORED_MANTISSA_BITS; 890 | // compute exp(z) using continued fractions, 891 | // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex 892 | dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); 893 | return dwba.F; 894 | } 895 | 896 | static void print_exponential_number(output_gadget_t* output, floating_point_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len) 897 | { 898 | const bool negative = get_sign_bit(number); 899 | // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it 900 | floating_point_t abs_number = SIGN(negative, number); 901 | 902 | int floored_exp10; 903 | bool abs_exp10_covered_by_powers_table; 904 | struct scaling_factor normalization; 905 | 906 | 907 | // Determine the decimal exponent 908 | if (abs_number == (floating_point_t) 0.0) { 909 | // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. 910 | floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table 911 | } 912 | else { 913 | floating_point_t exp10 = log10_of_positive(abs_number); 914 | floored_exp10 = bastardized_floor(exp10); 915 | floating_point_t p10 = pow10_of_int(floored_exp10); 916 | // correct for rounding errors 917 | if (abs_number < p10) { 918 | floored_exp10--; 919 | p10 /= 10; 920 | } 921 | abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; 922 | normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; 923 | } 924 | 925 | if (flags & FLAGS_ADAPT_EXP) { 926 | // Note: For now, still assuming we _don't_ fall-back to "%f" mode; we can't decide 927 | // that until we've established the exact exponent. 928 | 929 | // In "%g" mode, "precision" is the number of _significant digits_; we must 930 | // "translate" that to an actual number of decimal digits. 931 | precision = (precision > 1) ? (precision - 1U) : 0U; 932 | flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice 933 | } 934 | 935 | // We now begin accounting for the widths of the two parts of our printed field: 936 | // the decimal part after decimal exponent extraction, and the base-10 exponent part. 937 | // For both of these, the value of 0 has a special meaning, but not the same one: 938 | // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width 939 | // means "use as many characters as necessary". 940 | 941 | normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); 942 | struct floating_point_components decimal_part_components = 943 | (floored_exp10 == 0) ? 944 | get_components(SIGN(negative, abs_number), precision) : 945 | get_normalized_components(negative, precision, abs_number, normalization, floored_exp10); 946 | 947 | // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects 948 | // the exponent and may require additional tweaking of the parts 949 | // (and saving the floored_exp10 in case we'll need to undo the roll-over). 950 | int original_floored_exp10 = floored_exp10; 951 | if (decimal_part_components.integral >= 10) { 952 | floored_exp10++; 953 | decimal_part_components.integral = 1; 954 | decimal_part_components.fractional = 0; 955 | } 956 | 957 | // Should we want to fall-back to "%f" mode, and only print the decimal part? 958 | // (and remember we have decreased "precision" by 1 959 | bool fall_back_to_decimal_only_mode = (flags & FLAGS_ADAPT_EXP) && (floored_exp10 >= -4) && (floored_exp10 < (int) precision + 1); 960 | 961 | if (fall_back_to_decimal_only_mode) { 962 | precision = ((int) precision > floored_exp10) ? (unsigned) ((int) precision - floored_exp10) : 0U; 963 | // Redo some work :-) 964 | floored_exp10 = original_floored_exp10; 965 | decimal_part_components = get_components(SIGN(negative, abs_number), precision); 966 | 967 | if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && ((double)decimal_part_components.integral == powers_of_10[floored_exp10 + 1])) { 968 | floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used 969 | if (precision > 0U) { precision--; } 970 | // ... and it should already be the case that decimal_part_components.fractional == 0 971 | } 972 | // TODO: What about rollover strictly within the fractional part? 973 | } 974 | 975 | // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double 976 | // is "307" (for 2^1023), so we set aside 4-5 characters overall 977 | printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U : 5U; 978 | 979 | printf_size_t decimal_part_width = 980 | ((flags & FLAGS_LEFT) && exp10_part_width) ? 981 | // We're padding on the right, so the width constraint is the exponent part's 982 | // problem, not the decimal part's, so we'll use as many characters as we need: 983 | 0U : 984 | // We're padding on the left; so the width constraint is the decimal part's 985 | // problem. Well, can both the decimal part and the exponent part fit within our overall width? 986 | ((width > exp10_part_width) ? 987 | // Yes, so we limit our decimal part's width. 988 | // (Note this is trivially valid even if we've fallen back to "%f" mode) 989 | width - exp10_part_width : 990 | // No; we just give up on any restriction on the decimal part and use as many 991 | // characters as we need 992 | 0U); 993 | 994 | const printf_size_t printed_exponential_start_pos = output->pos; 995 | print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); 996 | 997 | if (! fall_back_to_decimal_only_mode) { 998 | putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); 999 | print_integer(output, 1000 | ABS_FOR_PRINTING(floored_exp10), 1001 | floored_exp10 < 0, 10, 0, exp10_part_width - 1, 1002 | FLAGS_ZEROPAD | FLAGS_PLUS); 1003 | if (flags & FLAGS_LEFT) { 1004 | // We need to right-pad with spaces to meet the width requirement 1005 | while (output->pos - printed_exponential_start_pos < width) { 1006 | putchar_via_gadget(output, ' '); 1007 | } 1008 | } 1009 | } 1010 | } 1011 | #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1012 | 1013 | static void print_floating_point(output_gadget_t* output, floating_point_t value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) 1014 | { 1015 | char buf[PRINTF_DECIMAL_BUFFER_SIZE]; 1016 | printf_size_t len = 0U; 1017 | 1018 | // test for special values 1019 | if (value != value) { 1020 | out_rev_(output, "nan", 3, width, flags); 1021 | return; 1022 | } 1023 | if (value < -FP_TYPE_MAX) { 1024 | out_rev_(output, "fni-", 4, width, flags); 1025 | return; 1026 | } 1027 | if (value > FP_TYPE_MAX) { 1028 | out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); 1029 | return; 1030 | } 1031 | 1032 | if (!prefer_exponential && 1033 | ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) { 1034 | // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean 1035 | // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated 1036 | // implementation. 1037 | #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1038 | print_exponential_number(output, value, precision, width, flags, buf, len); 1039 | #endif 1040 | return; 1041 | } 1042 | 1043 | // set default precision, if not set explicitly 1044 | if (!(flags & FLAGS_PRECISION)) { 1045 | precision = PRINTF_DEFAULT_FLOAT_PRECISION; 1046 | } 1047 | 1048 | // limit precision so that our integer holding the fractional part does not overflow 1049 | while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { 1050 | buf[len++] = '0'; // This respects the precision in terms of result length only 1051 | precision--; 1052 | } 1053 | 1054 | #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1055 | if (prefer_exponential) 1056 | print_exponential_number(output, value, precision, width, flags, buf, len); 1057 | else 1058 | #endif 1059 | print_decimal_number(output, value, precision, width, flags, buf, len); 1060 | } 1061 | 1062 | #endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) 1063 | 1064 | // Advances the format pointer past the flags, and returns the parsed flags 1065 | // due to the characters passed 1066 | static printf_flags_t parse_flags(const char** format) 1067 | { 1068 | printf_flags_t flags = 0U; 1069 | do { 1070 | switch (**format) { 1071 | case '0': flags |= FLAGS_ZEROPAD; (*format)++; break; 1072 | case '-': flags |= FLAGS_LEFT; (*format)++; break; 1073 | case '+': flags |= FLAGS_PLUS; (*format)++; break; 1074 | case ' ': flags |= FLAGS_SPACE; (*format)++; break; 1075 | case '#': flags |= FLAGS_HASH; (*format)++; break; 1076 | default : return flags; 1077 | } 1078 | } while (true); 1079 | } 1080 | 1081 | static inline void format_string_loop(output_gadget_t* output, const char* format, va_list args) 1082 | { 1083 | #if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 1084 | #define ADVANCE_IN_FORMAT_STRING(cptr_) do { (cptr_)++; if (!*(cptr_)) return; } while(0) 1085 | #else 1086 | #define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ 1087 | #endif 1088 | 1089 | while (*format) 1090 | { 1091 | if (*format != '%') { 1092 | // A regular content character 1093 | putchar_via_gadget(output, *format); 1094 | format++; 1095 | continue; 1096 | } 1097 | // We're parsing a format specifier: %[flags][width][.precision][length] 1098 | ADVANCE_IN_FORMAT_STRING(format); 1099 | 1100 | printf_flags_t flags = parse_flags(&format); 1101 | 1102 | // evaluate width field 1103 | printf_size_t width = 0U; 1104 | if (is_digit_(*format)) { 1105 | // Note: If the width is negative, we've already parsed its 1106 | // sign character '-' as a FLAG_LEFT 1107 | width = (printf_size_t) atou_(&format); 1108 | } 1109 | else if (*format == '*') { 1110 | const int w = va_arg(args, int); 1111 | if (w < 0) { 1112 | flags |= FLAGS_LEFT; // reverse padding 1113 | width = (printf_size_t)-w; 1114 | } 1115 | else { 1116 | width = (printf_size_t)w; 1117 | } 1118 | ADVANCE_IN_FORMAT_STRING(format); 1119 | } 1120 | 1121 | // evaluate precision field 1122 | printf_size_t precision = 0U; 1123 | if (*format == '.') { 1124 | flags |= FLAGS_PRECISION; 1125 | ADVANCE_IN_FORMAT_STRING(format); 1126 | if (*format == '-') { 1127 | do { 1128 | ADVANCE_IN_FORMAT_STRING(format); 1129 | } while (is_digit_(*format)); 1130 | flags &= ~FLAGS_PRECISION; 1131 | } 1132 | else if (is_digit_(*format)) { 1133 | precision = atou_(&format); 1134 | } 1135 | else if (*format == '*') { 1136 | const int precision_ = va_arg(args, int); 1137 | if (precision_ < 0) { 1138 | flags &= ~FLAGS_PRECISION; 1139 | } 1140 | else { 1141 | precision = precision_ > 0 ? (printf_size_t) precision_ : 0U; 1142 | } 1143 | ADVANCE_IN_FORMAT_STRING(format); 1144 | } 1145 | } 1146 | 1147 | // evaluate length field 1148 | switch (*format) { 1149 | #if PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS 1150 | case 'I' : { 1151 | ADVANCE_IN_FORMAT_STRING(format); 1152 | // Greedily parse for size in bits: 8, 16, 32 or 64 1153 | switch(*format) { 1154 | case '8': flags |= FLAGS_INT8; 1155 | ADVANCE_IN_FORMAT_STRING(format); 1156 | break; 1157 | case '1': 1158 | ADVANCE_IN_FORMAT_STRING(format); 1159 | if (*format == '6') { format++; flags |= FLAGS_INT16; } 1160 | break; 1161 | case '3': 1162 | ADVANCE_IN_FORMAT_STRING(format); 1163 | if (*format == '2') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT32; } 1164 | break; 1165 | case '6': 1166 | ADVANCE_IN_FORMAT_STRING(format); 1167 | if (*format == '4') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT64; } 1168 | break; 1169 | default: break; 1170 | } 1171 | break; 1172 | } 1173 | #endif 1174 | case 'l' : 1175 | flags |= FLAGS_LONG; 1176 | ADVANCE_IN_FORMAT_STRING(format); 1177 | if (*format == 'l') { 1178 | flags |= FLAGS_LONG_LONG; 1179 | ADVANCE_IN_FORMAT_STRING(format); 1180 | } 1181 | break; 1182 | case 'L' : 1183 | flags |= FLAGS_LONG_DOUBLE; 1184 | ADVANCE_IN_FORMAT_STRING(format); 1185 | break; 1186 | case 'h' : 1187 | flags |= FLAGS_SHORT; 1188 | ADVANCE_IN_FORMAT_STRING(format); 1189 | if (*format == 'h') { 1190 | flags |= FLAGS_CHAR; 1191 | ADVANCE_IN_FORMAT_STRING(format); 1192 | } 1193 | break; 1194 | case 't' : 1195 | flags |= (sizeof(ptrdiff_t) <= sizeof(int) ) ? FLAGS_INT : (sizeof(ptrdiff_t) == sizeof(long)) ? FLAGS_LONG : FLAGS_LONG_LONG; 1196 | ADVANCE_IN_FORMAT_STRING(format); 1197 | break; 1198 | case 'j' : 1199 | flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 1200 | ADVANCE_IN_FORMAT_STRING(format); 1201 | break; 1202 | case 'z' : 1203 | flags |= (sizeof(size_t) <= sizeof(int) ) ? FLAGS_INT : (sizeof(size_t) == sizeof(long)) ? FLAGS_LONG : FLAGS_LONG_LONG; 1204 | ADVANCE_IN_FORMAT_STRING(format); 1205 | break; 1206 | default: 1207 | break; 1208 | } 1209 | 1210 | // evaluate specifier 1211 | switch (*format) { 1212 | case 'd' : 1213 | case 'i' : 1214 | case 'u' : 1215 | case 'x' : 1216 | case 'X' : 1217 | case 'o' : 1218 | case 'b' : { 1219 | 1220 | if (*format == 'd' || *format == 'i') { 1221 | flags |= FLAGS_SIGNED; 1222 | } 1223 | 1224 | numeric_base_t base; 1225 | if (*format == 'x' || *format == 'X') { 1226 | base = BASE_HEX; 1227 | } 1228 | else if (*format == 'o') { 1229 | base = BASE_OCTAL; 1230 | } 1231 | else if (*format == 'b') { 1232 | base = BASE_BINARY; 1233 | } 1234 | else { 1235 | base = BASE_DECIMAL; 1236 | flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation 1237 | } 1238 | 1239 | if (*format == 'X') { 1240 | flags |= FLAGS_UPPERCASE; 1241 | } 1242 | 1243 | format++; 1244 | // ignore '0' flag when precision is given 1245 | if (flags & FLAGS_PRECISION) { 1246 | flags &= ~FLAGS_ZEROPAD; 1247 | } 1248 | 1249 | if (flags & FLAGS_SIGNED) { 1250 | // A signed specifier: d, i or possibly I + bit size if enabled 1251 | 1252 | if (flags & FLAGS_LONG_LONG) { 1253 | #if PRINTF_SUPPORT_LONG_LONG 1254 | const long long value = va_arg(args, long long); 1255 | print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); 1256 | #endif 1257 | } 1258 | else if (flags & FLAGS_LONG) { 1259 | const long value = va_arg(args, long); 1260 | print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); 1261 | } 1262 | else { 1263 | // We never try to interpret the argument as something potentially-smaller than int, 1264 | // due to integer promotion rules: Even if the user passed a short int, short unsigned 1265 | // etc. - these will come in after promotion, as int's (or unsigned for the case of 1266 | // short unsigned when it has the same size as int) 1267 | const int value = 1268 | (flags & FLAGS_CHAR) ? (signed char) va_arg(args, int) : 1269 | (flags & FLAGS_SHORT) ? (short int) va_arg(args, int) : 1270 | va_arg(args, int); 1271 | print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); 1272 | } 1273 | } 1274 | else { 1275 | // An unsigned specifier: u, x, X, o, b 1276 | 1277 | flags &= ~(FLAGS_PLUS | FLAGS_SPACE); 1278 | 1279 | if (flags & FLAGS_LONG_LONG) { 1280 | #if PRINTF_SUPPORT_LONG_LONG 1281 | print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long long), false, base, precision, width, flags); 1282 | #endif 1283 | } 1284 | else if (flags & FLAGS_LONG) { 1285 | print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long), false, base, precision, width, flags); 1286 | } 1287 | else { 1288 | const unsigned int value = 1289 | (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : 1290 | (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) : 1291 | va_arg(args, unsigned int); 1292 | print_integer(output, (printf_unsigned_value_t) value, false, base, precision, width, flags); 1293 | } 1294 | } 1295 | break; 1296 | } 1297 | #if PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1298 | case 'f' : 1299 | case 'F' : { 1300 | floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : (long double) va_arg(args, double)); 1301 | if (*format == 'F') flags |= FLAGS_UPPERCASE; 1302 | print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_DECIMAL); 1303 | format++; 1304 | break; 1305 | } 1306 | #endif 1307 | #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1308 | case 'e': 1309 | case 'E': 1310 | case 'g': 1311 | case 'G': { 1312 | floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : (long double) va_arg(args, double)); 1313 | if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; 1314 | if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; 1315 | print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_EXPONENTIAL); 1316 | format++; 1317 | break; 1318 | } 1319 | #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1320 | case 'c' : { 1321 | printf_size_t l = 1U; 1322 | // pre padding 1323 | if (!(flags & FLAGS_LEFT)) { 1324 | while (l++ < width) { 1325 | putchar_via_gadget(output, ' '); 1326 | } 1327 | } 1328 | // char output 1329 | putchar_via_gadget(output, (char) va_arg(args, int) ); 1330 | // post padding 1331 | if (flags & FLAGS_LEFT) { 1332 | while (l++ < width) { 1333 | putchar_via_gadget(output, ' '); 1334 | } 1335 | } 1336 | format++; 1337 | break; 1338 | } 1339 | 1340 | case 's' : { 1341 | const char* p = va_arg(args, char*); 1342 | if (p == NULL) { 1343 | out_rev_(output, ")llun(", 6, width, flags); 1344 | } 1345 | else { 1346 | printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); 1347 | // pre padding 1348 | if (flags & FLAGS_PRECISION) { 1349 | l = (l < precision ? l : precision); 1350 | } 1351 | if (!(flags & FLAGS_LEFT)) { 1352 | while (l++ < width) { 1353 | putchar_via_gadget(output, ' '); 1354 | } 1355 | } 1356 | // string output 1357 | while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { 1358 | putchar_via_gadget(output, *(p++)); 1359 | --precision; 1360 | } 1361 | // post padding 1362 | if (flags & FLAGS_LEFT) { 1363 | while (l++ < width) { 1364 | putchar_via_gadget(output, ' '); 1365 | } 1366 | } 1367 | } 1368 | format++; 1369 | break; 1370 | } 1371 | 1372 | case 'p' : { 1373 | width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix 1374 | flags |= FLAGS_ZEROPAD | FLAGS_POINTER; 1375 | uintptr_t value = (uintptr_t)va_arg(args, void*); 1376 | (value == (uintptr_t) NULL) ? 1377 | out_rev_(output, ")lin(", 5, width, flags) : 1378 | print_integer(output, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); 1379 | format++; 1380 | break; 1381 | } 1382 | 1383 | case '%' : 1384 | putchar_via_gadget(output, '%'); 1385 | format++; 1386 | break; 1387 | 1388 | // Many people prefer to disable support for %n, as it lets the caller 1389 | // engineer a write to an arbitrary location, of a value the caller 1390 | // effectively controls - which could be a security concern in some cases. 1391 | #if PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1392 | case 'n' : { 1393 | if (flags & FLAGS_CHAR) *(va_arg(args, char*)) = (char) output->pos; 1394 | else if (flags & FLAGS_SHORT) *(va_arg(args, short*)) = (short) output->pos; 1395 | else if (flags & FLAGS_LONG) *(va_arg(args, long*)) = (long) output->pos; 1396 | #if PRINTF_SUPPORT_LONG_LONG 1397 | else if (flags & FLAGS_LONG_LONG) *(va_arg(args, long long*)) = (long long int) output->pos; 1398 | #endif // PRINTF_SUPPORT_LONG_LONG 1399 | else *(va_arg(args, int*)) = (int) output->pos; 1400 | format++; 1401 | break; 1402 | } 1403 | #endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1404 | 1405 | default : 1406 | putchar_via_gadget(output, *format); 1407 | format++; 1408 | break; 1409 | } 1410 | } 1411 | } 1412 | 1413 | // internal vsnprintf - used for implementing _all library functions 1414 | static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list args) 1415 | { 1416 | // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is 1417 | // possible to call this function with a non-zero pos value for some "remedial printing". 1418 | format_string_loop(output, format, args); 1419 | 1420 | // termination 1421 | append_termination_with_gadget(output); 1422 | 1423 | // return written chars without terminating \0 1424 | return (int)output->pos; 1425 | } 1426 | 1427 | /////////////////////////////////////////////////////////////////////////////// 1428 | 1429 | int vprintf_(const char* format, va_list arg) 1430 | { 1431 | output_gadget_t gadget = extern_putchar_gadget(); 1432 | return vsnprintf_impl(&gadget, format, arg); 1433 | } 1434 | 1435 | int vsnprintf_(char* s, size_t n, const char* format, va_list arg) 1436 | { 1437 | output_gadget_t gadget = buffer_gadget(s, n); 1438 | return vsnprintf_impl(&gadget, format, arg); 1439 | } 1440 | 1441 | int vsprintf_(char* s, const char* format, va_list arg) 1442 | { 1443 | return vsnprintf_(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); 1444 | } 1445 | 1446 | int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) 1447 | { 1448 | if (out == NULL) { return 0; } 1449 | output_gadget_t gadget = function_gadget(out, extra_arg); 1450 | return vsnprintf_impl(&gadget, format, arg); 1451 | } 1452 | 1453 | int printf_(const char* format, ...) 1454 | { 1455 | va_list args; 1456 | va_start(args, format); 1457 | const int ret = vprintf_(format, args); 1458 | va_end(args); 1459 | return ret; 1460 | } 1461 | 1462 | int sprintf_(char* s, const char* format, ...) 1463 | { 1464 | va_list args; 1465 | va_start(args, format); 1466 | const int ret = vsprintf_(s, format, args); 1467 | va_end(args); 1468 | return ret; 1469 | } 1470 | 1471 | int snprintf_(char* s, size_t n, const char* format, ...) 1472 | { 1473 | va_list args; 1474 | va_start(args, format); 1475 | const int ret = vsnprintf_(s, n, format, args); 1476 | va_end(args); 1477 | return ret; 1478 | } 1479 | 1480 | int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) 1481 | { 1482 | va_list args; 1483 | va_start(args, format); 1484 | const int ret = vfctprintf(out, extra_arg, format, args); 1485 | va_end(args); 1486 | return ret; 1487 | } 1488 | 1489 | // Restore the previous diagnostic state 1490 | // Define PRINTF_DISABLE_WARNING_PRAGMAS to disable this block 1491 | #ifndef PRINTF_DISABLE_WARNING_PRAGMAS 1492 | #if defined(__GNUC__) || defined(__clang__) 1493 | #pragma GCC diagnostic pop 1494 | #endif // __GNUC__ || __clang__ 1495 | #endif // PRINTF_DISABLE_WARNING_PRAGMAS 1496 | -------------------------------------------------------------------------------- /src/printf/printf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author (c) Eyal Rozenberg 3 | * 2021-2022, Haifa, Palestine/Israel 4 | * @author (c) Marco Paland (info@paland.com) 5 | * 2014-2019, PALANDesign Hannover, Germany 6 | * 7 | * @note Others have made smaller contributions to this file: see the 8 | * contributors page at https://github.com/eyalroz/printf/graphs/contributors 9 | * or ask one of the authors. 10 | * 11 | * @brief Small stand-alone implementation of the printf family of functions 12 | * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems 13 | * with a very limited resources. 14 | * 15 | * @note the implementations are thread-safe; re-entrant; use no functions from 16 | * the standard library; and do not dynamically allocate any memory. 17 | * 18 | * @license The MIT License (MIT) 19 | * 20 | * Permission is hereby granted, free of charge, to any person obtaining a copy 21 | * of this software and associated documentation files (the "Software"), to deal 22 | * in the Software without restriction, including without limitation the rights 23 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | * copies of the Software, and to permit persons to whom the Software is 25 | * furnished to do so, subject to the following conditions: 26 | * 27 | * The above copyright notice and this permission notice shall be included in 28 | * all copies or substantial portions of the Software. 29 | * 30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 | * THE SOFTWARE. 37 | */ 38 | 39 | #ifndef PRINTF_H_ 40 | #define PRINTF_H_ 41 | 42 | #ifdef __cplusplus 43 | # include 44 | # include 45 | extern "C" { 46 | #else 47 | # include 48 | # include 49 | #endif 50 | 51 | // Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H=1 ...) to include the 52 | // printf_config.h header file 53 | #if PRINTF_INCLUDE_CONFIG_H 54 | #include "printf_config.h" 55 | #endif 56 | 57 | #ifdef __GNUC__ 58 | # if ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4) 59 | # define ATTR_PRINTF(one_based_format_index, first_arg) \ 60 | __attribute__((format(gnu_printf, (one_based_format_index), (first_arg)))) 61 | # else 62 | # define ATTR_PRINTF(one_based_format_index, first_arg) \ 63 | __attribute__((format(printf, (one_based_format_index), (first_arg)))) 64 | # endif 65 | # define ATTR_VPRINTF(one_based_format_index) \ 66 | ATTR_PRINTF((one_based_format_index), 0) 67 | #else 68 | # define ATTR_PRINTF(one_based_format_index, first_arg) 69 | # define ATTR_VPRINTF(one_based_format_index) 70 | #endif 71 | 72 | #ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 73 | #define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 0 74 | #endif 75 | 76 | #ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 77 | #define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 0 78 | #endif 79 | 80 | #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 81 | # define printf_ printf 82 | # define sprintf_ sprintf 83 | # define vsprintf_ vsprintf 84 | # define snprintf_ snprintf 85 | # define vsnprintf_ vsnprintf 86 | # define vprintf_ vprintf 87 | #endif 88 | 89 | // If you want to include this implementation file directly rather than 90 | // link against it, this will let you control the functions' visibility, 91 | // e.g. make them static so as not to clash with other objects also 92 | // using them. 93 | #ifndef PRINTF_VISIBILITY 94 | #define PRINTF_VISIBILITY 95 | #endif 96 | 97 | /** 98 | * Prints/send a single character to some opaque output entity 99 | * 100 | * @note This function is not implemented by the library, only declared; you 101 | * must provide an implementation if you wish to use the @ref printf / @ref 102 | * vprintf function (and possibly for linking against the library, if your 103 | * toolchain does not support discarding unused functions) 104 | * 105 | * @note The output could be as simple as a wrapper for the `write()` system 106 | * call on a Unix-like * system, or even libc's @ref putchar , for replicating 107 | * actual functionality of libc's @ref printf * function; but on an embedded 108 | * system it may involve interaction with a special output device, like a UART, 109 | * etc. 110 | * 111 | * @note in libc's @ref putchar, the parameter type is an int; this was intended 112 | * to support the representation of either a proper character or EOF in a 113 | * variable - but this is really not meaningful to pass into @ref putchar and is 114 | * discouraged today. See further discussion in: 115 | * @link https://stackoverflow.com/q/17452847/1593077 116 | * 117 | * @param c the single character to print 118 | */ 119 | PRINTF_VISIBILITY 120 | void putchar_(char c); 121 | 122 | 123 | /** 124 | * An implementation of the C standard's printf/vprintf 125 | * 126 | * @note you must implement a @ref putchar_ function for using this function - 127 | * it invokes @ref putchar_ * rather than directly performing any I/O (which 128 | * insulates it from any dependence on the operating system * and external 129 | * libraries). 130 | * 131 | * @param format A string specifying the format of the output, with %-marked 132 | * specifiers of how to interpret additional arguments. 133 | * @param arg Additional arguments to the function, one for each %-specifier in 134 | * @p format 135 | * @return The number of characters written into @p s, not counting the 136 | * terminating null character 137 | */ 138 | ///@{ 139 | PRINTF_VISIBILITY 140 | int printf_(const char* format, ...) ATTR_PRINTF(1, 2); 141 | PRINTF_VISIBILITY 142 | int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1); 143 | ///@} 144 | 145 | 146 | /** 147 | * An implementation of the C standard's sprintf/vsprintf 148 | * 149 | * @note For security considerations (the potential for exceeding the buffer 150 | * bounds), please consider using the size-constrained variant, @ref snprintf / 151 | * @ref vsnprintf, instead. 152 | * 153 | * @param s An array in which to store the formatted string. It must be large 154 | * enough to fit the formatted output! 155 | * @param format A string specifying the format of the output, with %-marked 156 | * specifiers of how to interpret additional arguments 157 | * @param arg Additional arguments to the function, one for each specifier in 158 | * @p format 159 | * @return The number of characters written into @p s, not counting the 160 | * terminating null character 161 | */ 162 | ///@{ 163 | PRINTF_VISIBILITY 164 | int sprintf_(char* s, const char* format, ...) ATTR_PRINTF(2, 3); 165 | PRINTF_VISIBILITY 166 | int vsprintf_(char* s, const char* format, va_list arg) ATTR_VPRINTF(2); 167 | ///@} 168 | 169 | 170 | /** 171 | * An implementation of the C standard's snprintf/vsnprintf 172 | * 173 | * @param s An array in which to store the formatted string. It must be large 174 | * enough to fit either the entire formatted output, or at least @p n 175 | * characters. Alternatively, it can be NULL, in which case nothing will 176 | * be printed, and only the number of characters which _could_ have been 177 | * printed is tallied and returned. 178 | * @param n The maximum number of characters to write to the array, including 179 | * a terminating null character 180 | * @param format A string specifying the format of the output, with %-marked 181 | * specifiers of how to interpret additional arguments. 182 | * @param arg Additional arguments to the function, one for each specifier in 183 | * @p format 184 | * @return The number of characters that COULD have been written into @p s, not 185 | * counting the terminating null character. A value equal or larger than 186 | * @p n indicates truncation. Only when the returned value is non-negative 187 | * and less than @p n, the null-terminated string has been fully and 188 | * successfully printed. 189 | */ 190 | ///@{ 191 | PRINTF_VISIBILITY 192 | int snprintf_(char* s, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); 193 | PRINTF_VISIBILITY 194 | int vsnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3); 195 | ///@} 196 | 197 | /** 198 | * printf/vprintf with user-specified output function 199 | * 200 | * An alternative to @ref printf_, in which the output function is specified 201 | * dynamically (rather than @ref putchar_ being used) 202 | * 203 | * @param out An output function which takes one character and a type-erased 204 | * additional parameters 205 | * @param extra_arg The type-erased argument to pass to the output function @p 206 | * out with each call 207 | * @param format A string specifying the format of the output, with %-marked 208 | * specifiers of how to interpret additional arguments. 209 | * @param arg Additional arguments to the function, one for each specifier in 210 | * @p format 211 | * @return The number of characters for which the output f unction was invoked, 212 | * not counting the terminating null character 213 | * 214 | */ 215 | PRINTF_VISIBILITY 216 | int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) ATTR_PRINTF(3, 4); 217 | PRINTF_VISIBILITY 218 | int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3); 219 | 220 | #ifdef __cplusplus 221 | } // extern "C" 222 | #endif 223 | 224 | #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 225 | # undef printf_ 226 | # undef sprintf_ 227 | # undef vsprintf_ 228 | # undef snprintf_ 229 | # undef vsnprintf_ 230 | # undef vprintf_ 231 | #else 232 | #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 233 | # define printf printf_ 234 | # define sprintf sprintf_ 235 | # define vsprintf vsprintf_ 236 | # define snprintf snprintf_ 237 | # define vsnprintf vsnprintf_ 238 | # define vprintf vprintf_ 239 | #endif 240 | #endif 241 | 242 | #endif // PRINTF_H_ 243 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | enable_language(CXX) 4 | 5 | option(TEST_WITH_NON_STANDARD_FORMAT_STRINGS "Include tests using non-standard-compliant format strings?" ON) 6 | # ... don't worry, we'll suppress the compiler warnings for those. 7 | 8 | ############################# 9 | # Primary catch2 Test Suite # 10 | ############################# 11 | 12 | if (NOT ALIAS_STANDARD_FUNCTION_NAMES STREQUAL SOFT) 13 | add_executable(test_suite) 14 | target_sources(test_suite PRIVATE test_suite.cpp) 15 | target_link_libraries(test_suite PRIVATE catch2_dep) 16 | set_target_properties( 17 | test_suite 18 | PROPERTIES 19 | CXX_STANDARD 14 20 | CXX_STANDARD_REQUIRED YES 21 | CXX_EXTENSIONS NO 22 | ) 23 | 24 | if (TEST_WITH_NON_STANDARD_FORMAT_STRINGS) 25 | target_compile_definitions(test_suite PRIVATE TEST_WITH_NON_STANDARD_FORMAT_STRINGS) 26 | endif() 27 | 28 | # These two lines are necessary, since the test suite does not actually use the 29 | # compiled library - it includes the library's source .c file; and that means we 30 | # need to include the generated config.h file. 31 | target_compile_definitions(test_suite PRIVATE PRINTF_INCLUDE_CONFIG_H) 32 | target_include_directories( 33 | test_suite 34 | PRIVATE 35 | "${CONFIG_HEADER_DIR}" 36 | "$" 37 | ) 38 | endif() 39 | 40 | ######################### 41 | # Comparison Test Suite # 42 | ######################### 43 | 44 | add_executable(autotest autotest.cpp) 45 | set_target_properties( 46 | autotest 47 | PROPERTIES 48 | CXX_STANDARD 14 49 | CXX_STANDARD_REQUIRED YES 50 | CXX_EXTENSIONS NO 51 | ) 52 | target_link_libraries(autotest PRIVATE printf) 53 | target_include_directories(autotest PRIVATE "$") 54 | 55 | ####################### 56 | # Aliasing Test Suite # 57 | ####################### 58 | 59 | add_executable(aliasing "aliasing.c") 60 | set_target_properties( 61 | ${tgt} 62 | PROPERTIES 63 | C_STANDARD 11 64 | C_STANDARD_REQUIRED YES 65 | C_EXTENSIONS NO 66 | ) 67 | 68 | if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") 69 | target_compile_options(aliasing PRIVATE -fno-builtin-printf) 70 | endif() 71 | 72 | target_link_libraries(aliasing PRIVATE printf) 73 | target_include_directories(aliasing PRIVATE "$") 74 | 75 | ################### 76 | # Common Settings # 77 | ################### 78 | 79 | list(APPEND test_targets aliasing test_suite autotest) 80 | 81 | foreach(tgt ${test_targets}) 82 | get_target_property(tgt_lang ${tgt} LANGUAGE) 83 | if (tgt_lang EQUAL "CXX") 84 | set(tgt_compiler_id ${CMAKE_CXX_COMPILER_ID}) 85 | else() # it's C 86 | set(tgt_compiler_id ${CMAKE_C_COMPILER_ID}) 87 | endif() 88 | 89 | if (tgt_compiler_id STREQUAL "MSVC") 90 | target_compile_options(${tgt} PRIVATE /W4) 91 | elseif (tgt_compiler_id STREQUAL "GNU" OR 92 | tgt_compiler_id STREQUAL "Clang") 93 | # lots of warnings and all warnings as errors 94 | target_compile_options(${tgt} PRIVATE 95 | -g 96 | -Wall 97 | -Wextra 98 | -pedantic 99 | -Wundef 100 | -Wsign-conversion 101 | -Wuninitialized 102 | -Wshadow 103 | -Wunreachable-code 104 | -Wswitch-default 105 | -Wswitch 106 | -Wcast-align 107 | -Wmissing-include-dirs 108 | -Winit-self 109 | -Wdouble-promotion 110 | -gdwarf-2 111 | -fno-exceptions 112 | -ffunction-sections 113 | -fdata-sections 114 | -fverbose-asm 115 | -Wunused-parameter 116 | ) 117 | target_compile_options(${tgt} PRIVATE 118 | "$<$:-Wstrict-prototypes>" 119 | ) 120 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 121 | target_compile_options(${tgt} PRIVATE -ffat-lto-objects) 122 | endif() 123 | endif() 124 | 125 | endforeach() 126 | 127 | ##################### 128 | # Test Registration # 129 | ##################### 130 | 131 | register_catch2_test(${PROJECT_NAME}.test_suite test_suite) 132 | 133 | add_test( 134 | NAME "${PROJECT_NAME}.aliasing" 135 | COMMAND "aliasing" 136 | ) 137 | 138 | # TODO - run autotest? 139 | # Old status: Not running autotest by default - it's randomized after all. 140 | -------------------------------------------------------------------------------- /test/aliasing.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int strcmp_(const char* lhs, const char* rhs) 4 | { 5 | while (1) { 6 | char lhs_c = *lhs++; 7 | char rhs_c = *rhs++; 8 | if (lhs_c != rhs_c) { return lhs_c - rhs_c; } 9 | if (lhs_c == '\0') { return 0; } 10 | } 11 | } 12 | 13 | enum { BufferSize = 100 }; 14 | char buffer[BufferSize]; 15 | 16 | void putchar_(char c) 17 | { 18 | for(char* ptr = buffer; ptr - buffer < BufferSize; ptr++) { 19 | if (*ptr == '\0') { 20 | *ptr++ = c; 21 | *ptr++ = '\0'; 22 | return; 23 | } 24 | } 25 | } 26 | 27 | void clear_buffer(void) 28 | { 29 | for(int i = 0; i < BufferSize; i++) { 30 | buffer[i] = '\0'; 31 | } 32 | } 33 | 34 | int main(void) 35 | { 36 | #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT || PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 37 | clear_buffer(); 38 | printf("printf'ing an integer: %d and a string: %s\n", 12, "Hello world"); 39 | const char* expected = "printf'ing an integer: 12 and a string: Hello world\n"; 40 | if (strcmp_(buffer, expected) != 0) { 41 | return(-1); 42 | } 43 | clear_buffer(); 44 | sprintf(buffer, "sprintf'ing an integer: %d and a string: %s\n", 34, "quick brown fox"); 45 | expected = "sprintf'ing an integer: 34 and a string: quick brown fox\n"; 46 | if (strcmp_(buffer, expected) != 0) { 47 | return(-1); 48 | } 49 | #endif 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /test/autotest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #ifdef _WIN32 8 | #include "getopt.h" 9 | #else 10 | #include 11 | #endif 12 | #include 13 | 14 | #include "../src/printf/printf.h" 15 | 16 | #ifdef __clang__ 17 | #pragma clang diagnostic push 18 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 19 | #elif defined(__GNUC__) 20 | #pragma GCC diagnostic push 21 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 22 | #endif 23 | 24 | 25 | #ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 26 | #define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 0 27 | #endif 28 | 29 | #ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 30 | #define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 0 31 | #endif 32 | 33 | #if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT || PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 34 | # define printf_ printf 35 | # define sprintf_ sprintf 36 | # define vsprintf_ vsprintf 37 | # define snprintf_ snprintf 38 | # define vsnprintf_ vsnprintf 39 | # define vprintf_ vprintf 40 | #endif 41 | 42 | 43 | //******************************************************* 44 | // Defines 45 | //******************************************************* 46 | 47 | #define min(a,b) ( (a) < (b) ? (a) : (b) ) 48 | #define MAIN_TITLE "autotest" 49 | 50 | #define BUF_SIZE 100 51 | #define WIDTH_MAX 25 52 | 53 | // range for float testing 54 | #define FLOAT_TST_MIN 1E-5 55 | #define FLOAT_TST_MAX 1E5 56 | 57 | #define PRECISION_LIMIT_DEFAULT 17 58 | 59 | struct cmdopt_struct 60 | { 61 | bool i; 62 | bool x; 63 | bool o; 64 | bool u; 65 | bool f; 66 | bool e; 67 | bool g; 68 | bool left_justify; 69 | bool zero_pad; 70 | bool hash; 71 | bool width; 72 | bool prec; 73 | int prec_max; 74 | }; 75 | 76 | // Valid short options 77 | #define VALIDOPTS "ixoufeglz#wp::ah" 78 | 79 | #define MSG_USAGE "\n\ 80 | Usage: " MAIN_TITLE " [OPTION/s]\n\ 81 | Compare randomly formatted strings with the stdio printf()\n\ 82 | Matching strings are output to stdout\n\ 83 | Errors are output to stderr\r\n\ 84 | \n\ 85 | -i test %%i \n\ 86 | -x test %%x\n\ 87 | -o test %%o\n\ 88 | -u test %%u\n\ 89 | -f test %%f\n\ 90 | -e test %%e\n\ 91 | -g test %%g (float falling back to exp)\n\ 92 | -l test left justification\r\n\ 93 | -z test zero padding\n\ 94 | -# test prepending base 0 0x\n\ 95 | -w test width specifier\n\ 96 | -p test precision specifier, with an optional limit for %%f %%e %%g\n\ 97 | -a test all of the above, with a default precision limit of %i for %%f %%e %%g\n\ 98 | \n\ 99 | -h show these options\n\ 100 | \n\ 101 | Examples:\n\ 102 | " MAIN_TITLE " -a test with all options, showing all passes and failures\n\ 103 | " MAIN_TITLE " -a 1>/dev/null test with all options, showing only errors, with stdout > null\n\ 104 | " 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\ 105 | " MAIN_TITLE " -ixou 1>/dev/null test only %%i %%x %%o %%u, showing only errors, with stdout > null\n\ 106 | \n\ 107 | " 108 | 109 | //******************************************************* 110 | // Variables 111 | //******************************************************* 112 | 113 | static struct cmdopt_struct opts; 114 | 115 | //******************************************************* 116 | // Prototypes 117 | //******************************************************* 118 | 119 | static bool parse_options(int argc, char *argv[]); 120 | static void run_tests(void); 121 | 122 | static void test_i(void); 123 | static void test_x(void); 124 | static void test_o(void); 125 | static void test_u(void); 126 | static void test_f(void); 127 | static void test_e(void); 128 | static void test_g(void); 129 | 130 | float rand_float(float a, float b); 131 | 132 | //******************************************************* 133 | // Functions 134 | //******************************************************* 135 | 136 | void putchar_(char character) 137 | { 138 | (void)character; 139 | } 140 | 141 | int main(int argc, char *argv[]) 142 | { 143 | if(!parse_options(argc, argv)) 144 | printf(MSG_USAGE, PRECISION_LIMIT_DEFAULT); 145 | else 146 | run_tests(); 147 | return 0; 148 | } 149 | 150 | static bool parse_options(int argc, char *argv[]) 151 | { 152 | int c; 153 | bool gotopt = false; 154 | 155 | opts.i = false; 156 | opts.x = false; 157 | opts.o = false; 158 | opts.u = false; 159 | opts.f = false; 160 | opts.e = false; 161 | opts.g = false; 162 | opts.left_justify = false; 163 | opts.zero_pad = false; 164 | opts.hash = false; 165 | opts.width = false; 166 | opts.prec = false; 167 | opts.prec_max = -1; 168 | 169 | while ((c = getopt (argc, argv, VALIDOPTS)) != -1) 170 | { 171 | gotopt = true; 172 | if(c == 'i') 173 | opts.i = true; 174 | else if(c == 'x') 175 | opts.x = true; 176 | else if(c == 'o') 177 | opts.o = true; 178 | else if(c == 'u') 179 | opts.u = true; 180 | else if(c == 'f') 181 | opts.f = true; 182 | else if(c == 'e') 183 | opts.e = true; 184 | else if(c == 'g') 185 | opts.g = true; 186 | else if(c == 'l') 187 | opts.left_justify = true; 188 | else if(c == 'z') 189 | opts.zero_pad = true; 190 | else if(c == '#') 191 | opts.hash = true; 192 | else if(c == 'w') 193 | opts.width = true; 194 | else if(c == 'p') 195 | { 196 | opts.prec = true; 197 | if(optarg) 198 | opts.prec_max = atoi(optarg); 199 | } 200 | else if(c == 'a') 201 | { 202 | opts.i = true; 203 | opts.x = true; 204 | opts.o = true; 205 | opts.u = true; 206 | opts.f = true; 207 | opts.e = true; 208 | opts.g = true; 209 | opts.left_justify = true; 210 | opts.zero_pad = true; 211 | opts.hash = true; 212 | opts.width = true; 213 | opts.prec = true; 214 | } 215 | else if(c != 'h') 216 | { 217 | fprintf(stderr, "Unknown option\n"); 218 | assert(false); 219 | }; 220 | }; 221 | 222 | if(opts.prec_max == -1) 223 | opts.prec_max = PRECISION_LIMIT_DEFAULT; 224 | 225 | return gotopt; 226 | } 227 | 228 | static void run_tests(void) 229 | { 230 | while(true) 231 | { 232 | if(opts.i) 233 | test_i(); 234 | if(opts.x) 235 | test_x(); 236 | if(opts.o) 237 | test_o(); 238 | if(opts.u) 239 | test_u(); 240 | if(opts.f) 241 | test_f(); 242 | if(opts.e) 243 | test_e(); 244 | if(opts.g) 245 | test_g(); 246 | }; 247 | } 248 | 249 | static void test_i(void) 250 | { 251 | FILE* dst = stdout; 252 | char fmt_buf[BUF_SIZE]; 253 | char std_buf[BUF_SIZE]; 254 | char tst_buf[BUF_SIZE]; 255 | bool width_flag; 256 | int width; 257 | int value; 258 | 259 | strcpy(fmt_buf, "%"); 260 | if(rand()&1 && opts.left_justify) 261 | strcat(fmt_buf, "-"); 262 | if(rand()&1) 263 | strcat(fmt_buf, "+"); 264 | else if(rand()&1) 265 | strcat(fmt_buf, " "); 266 | 267 | width_flag = (rand()&1 && opts.width); 268 | 269 | width = 1+rand()%WIDTH_MAX; 270 | if(width_flag) 271 | { 272 | if(rand()&1 && opts.zero_pad) 273 | strcat(fmt_buf, "0"); 274 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 275 | }; 276 | 277 | if(rand()&1 && opts.prec) 278 | { 279 | strcat(fmt_buf, "."); 280 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width); 281 | }; 282 | 283 | strcat(fmt_buf, "i"); 284 | value = rand(); 285 | if(rand()&1) 286 | value = 0; 287 | if(rand()&1) 288 | value*=-1; 289 | 290 | sprintf(std_buf, fmt_buf, value); 291 | sprintf_(tst_buf, fmt_buf, value); 292 | 293 | if(strcmp(std_buf,tst_buf)) 294 | dst = stderr; 295 | 296 | fprintf(dst, "\nfmt = \"%s\" value = %i\n", fmt_buf, value); 297 | fprintf(dst, "libc : \"%s\"\n", std_buf); 298 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 299 | } 300 | 301 | static void test_x(void) 302 | { 303 | FILE* dst = stdout; 304 | char fmt_buf[BUF_SIZE]; 305 | char std_buf[BUF_SIZE]; 306 | char tst_buf[BUF_SIZE]; 307 | bool width_flag; 308 | int width; 309 | unsigned int value; 310 | 311 | strcpy(fmt_buf, "%"); 312 | if(rand()&1 && opts.left_justify) 313 | strcat(fmt_buf, "-"); 314 | if(rand()&1 && opts.hash) 315 | strcat(fmt_buf, "#"); 316 | 317 | width_flag = (rand()&1 && opts.width); 318 | 319 | width = 1+rand()%WIDTH_MAX; 320 | if(width_flag) 321 | { 322 | if(rand()&1 && opts.zero_pad) 323 | strcat(fmt_buf, "0"); 324 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 325 | }; 326 | 327 | if(rand()&1 && opts.prec) 328 | { 329 | strcat(fmt_buf, "."); 330 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width); 331 | }; 332 | 333 | strcat(fmt_buf, "x"); 334 | value = (unsigned)rand(); 335 | if(rand()&1) 336 | value = 0; 337 | 338 | sprintf(std_buf, fmt_buf, value); 339 | sprintf_(tst_buf, fmt_buf, value); 340 | 341 | if(strcmp(std_buf,tst_buf)) 342 | dst = stderr; 343 | 344 | fprintf(dst, "\nfmt = \"%s\" value = %#x\n", fmt_buf, value); 345 | fprintf(dst, "libc: \"%s\"\n", std_buf); 346 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 347 | } 348 | 349 | static void test_o(void) 350 | { 351 | FILE* dst = stdout; 352 | char fmt_buf[BUF_SIZE]; 353 | char std_buf[BUF_SIZE]; 354 | char tst_buf[BUF_SIZE]; 355 | bool width_flag; 356 | int width; 357 | unsigned int value; 358 | 359 | strcpy(fmt_buf, "%"); 360 | if(rand()&1 && opts.left_justify) 361 | strcat(fmt_buf, "-"); 362 | if(rand()&1 && opts.hash) 363 | strcat(fmt_buf, "#"); 364 | 365 | width_flag = (rand()&1 && opts.width); 366 | 367 | width = 1+rand()%WIDTH_MAX; 368 | if(width_flag) 369 | { 370 | if(rand()&1 && opts.zero_pad) 371 | strcat(fmt_buf, "0"); 372 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 373 | }; 374 | 375 | if(rand()&1 && opts.prec) 376 | { 377 | strcat(fmt_buf, "."); 378 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width); 379 | }; 380 | 381 | strcat(fmt_buf, "o"); 382 | value = (unsigned)rand(); 383 | if(rand()&1) 384 | value = 0; 385 | 386 | sprintf(std_buf, fmt_buf, value); 387 | sprintf_(tst_buf, fmt_buf, value); 388 | 389 | if(strcmp(std_buf,tst_buf)) 390 | dst = stderr; 391 | 392 | fprintf(dst, "\nfmt = \"%s\" value = %o\n", fmt_buf, value); 393 | fprintf(dst, "libc: \"%s\"\n", std_buf); 394 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 395 | } 396 | 397 | static void test_u(void) 398 | { 399 | FILE* dst = stdout; 400 | char fmt_buf[BUF_SIZE]; 401 | char std_buf[BUF_SIZE]; 402 | char tst_buf[BUF_SIZE]; 403 | bool width_flag; 404 | int width; 405 | unsigned int value; 406 | 407 | strcpy(fmt_buf, "%"); 408 | if(rand()&1 && opts.left_justify) 409 | strcat(fmt_buf, "-"); 410 | 411 | width_flag = (rand()&1 && opts.width); 412 | 413 | width = 1+rand()%WIDTH_MAX; 414 | if(width_flag) 415 | { 416 | if(rand()&1 && opts.zero_pad) 417 | strcat(fmt_buf, "0"); 418 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 419 | }; 420 | 421 | if(rand()&1 && opts.prec) 422 | { 423 | strcat(fmt_buf, "."); 424 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width); 425 | }; 426 | 427 | strcat(fmt_buf, "u"); 428 | value = (unsigned)rand(); 429 | if(rand()&1) 430 | value = 0; 431 | 432 | sprintf(std_buf, fmt_buf, value); 433 | sprintf_(tst_buf, fmt_buf, value); 434 | 435 | if(strcmp(std_buf,tst_buf)) 436 | dst = stderr; 437 | 438 | fprintf(dst, "\nfmt = \"%s\" value = %u\n", fmt_buf, value); 439 | fprintf(dst, "libc: \"%s\"\n", std_buf); 440 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 441 | } 442 | 443 | static void test_f(void) 444 | { 445 | FILE* dst = stdout; 446 | char fmt_buf[BUF_SIZE]; 447 | char std_buf[BUF_SIZE]; 448 | char tst_buf[BUF_SIZE]; 449 | bool width_flag; 450 | int width; 451 | double value; 452 | 453 | strcpy(fmt_buf, "%"); 454 | if(rand()&1 && opts.left_justify) 455 | strcat(fmt_buf, "-"); 456 | if(rand()&1 && opts.hash) 457 | strcat(fmt_buf, "#"); 458 | if(rand()&1) 459 | strcat(fmt_buf, "+"); 460 | else if(rand()&1) 461 | strcat(fmt_buf, " "); 462 | 463 | width_flag = (rand()&1 && opts.width); 464 | 465 | width = 1+rand()%WIDTH_MAX; 466 | if(width_flag) 467 | { 468 | if(rand()&1 && opts.zero_pad) 469 | strcat(fmt_buf, "0"); 470 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 471 | }; 472 | 473 | if(rand()&1 && opts.prec) 474 | { 475 | strcat(fmt_buf, "."); 476 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width, opts.prec_max)); 477 | }; 478 | 479 | strcat(fmt_buf, "f"); 480 | 481 | if(rand()&1) 482 | value = 0; 483 | else 484 | value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX); 485 | 486 | if(rand()&1) 487 | value*=-1; 488 | 489 | sprintf(std_buf, fmt_buf, value); 490 | sprintf_(tst_buf, fmt_buf, value); 491 | 492 | if(strcmp(std_buf,tst_buf)) 493 | dst = stderr; 494 | 495 | fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value); 496 | fprintf(dst, "libc: \"%s\"\n", std_buf); 497 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 498 | } 499 | 500 | static void test_e(void) 501 | { 502 | FILE* dst = stdout; 503 | char fmt_buf[BUF_SIZE]; 504 | char std_buf[BUF_SIZE]; 505 | char tst_buf[BUF_SIZE]; 506 | bool width_flag; 507 | int width; 508 | double value; 509 | 510 | strcpy(fmt_buf, "%"); 511 | if(rand()&1 && opts.left_justify) 512 | strcat(fmt_buf, "-"); 513 | if(rand()&1 && opts.hash) 514 | strcat(fmt_buf, "#"); 515 | if(rand()&1) 516 | strcat(fmt_buf, "+"); 517 | else if(rand()&1) 518 | strcat(fmt_buf, " "); 519 | 520 | width_flag = (rand()&1 && opts.width); 521 | 522 | width = 1+rand()%WIDTH_MAX; 523 | if(width_flag) 524 | { 525 | if(rand()&1 && opts.zero_pad) 526 | strcat(fmt_buf, "0"); 527 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 528 | }; 529 | 530 | if(rand()&1 && opts.prec) 531 | { 532 | strcat(fmt_buf, "."); 533 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width,opts.prec_max)); 534 | }; 535 | 536 | strcat(fmt_buf, "e"); 537 | 538 | if(rand()&1) 539 | value = 0; 540 | else 541 | value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX); 542 | 543 | if(rand()&1) 544 | value*=-1; 545 | 546 | sprintf(std_buf, fmt_buf, value); 547 | sprintf_(tst_buf, fmt_buf, value); 548 | 549 | if(strcmp(std_buf,tst_buf)) 550 | dst = stderr; 551 | 552 | fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value); 553 | fprintf(dst, "libc: \"%s\"\n", std_buf); 554 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 555 | } 556 | 557 | static void test_g(void) 558 | { 559 | FILE* dst = stdout; 560 | char fmt_buf[BUF_SIZE]; 561 | char std_buf[BUF_SIZE]; 562 | char tst_buf[BUF_SIZE]; 563 | bool width_flag; 564 | int width; 565 | double value; 566 | 567 | strcpy(fmt_buf, "%"); 568 | if(rand()&1 && opts.left_justify) 569 | strcat(fmt_buf, "-"); 570 | if(rand()&1 && opts.hash) 571 | strcat(fmt_buf, "#"); 572 | if(rand()&1) 573 | strcat(fmt_buf, "+"); 574 | else if(rand()&1) 575 | strcat(fmt_buf, " "); 576 | 577 | width_flag = (rand()&1 && opts.width); 578 | 579 | width = 1+rand()%WIDTH_MAX; 580 | if(width_flag) 581 | { 582 | if(rand()&1 && opts.zero_pad) 583 | strcat(fmt_buf, "0"); 584 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width); 585 | }; 586 | 587 | if(rand()&1 && opts.prec) 588 | { 589 | strcat(fmt_buf, "."); 590 | sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width,opts.prec_max)); 591 | }; 592 | 593 | strcat(fmt_buf, "g"); 594 | 595 | if(rand()&1) 596 | value = 0; 597 | else 598 | value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX); 599 | 600 | if(rand()&1) 601 | value*=-1; 602 | 603 | sprintf(std_buf, fmt_buf, value); 604 | sprintf_(tst_buf, fmt_buf, value); 605 | 606 | if(strcmp(std_buf,tst_buf)) 607 | dst = stderr; 608 | 609 | fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value); 610 | fprintf(dst, "libc: \"%s\"\n", std_buf); 611 | fprintf(dst, "our lib: \"%s\"\n", tst_buf); 612 | } 613 | 614 | 615 | float rand_float(float a, float b) 616 | { 617 | float random = ((float) rand()) / (float) RAND_MAX; 618 | float diff = b - a; 619 | float r = random * diff; 620 | return a + r; 621 | } 622 | -------------------------------------------------------------------------------- /test/getopt.h: -------------------------------------------------------------------------------- 1 | #ifndef __GETOPT_H__ 2 | /** 3 | * DISCLAIMER 4 | * This file is part of the mingw-w64 runtime package. 5 | * 6 | * The mingw-w64 runtime package and its code is distributed in the hope that it 7 | * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR 8 | * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to 9 | * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | */ 11 | /* 12 | * Copyright (c) 2002 Todd C. Miller 13 | * 14 | * Permission to use, copy, modify, and distribute this software for any 15 | * purpose with or without fee is hereby granted, provided that the above 16 | * copyright notice and this permission notice appear in all copies. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 19 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 20 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 21 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 23 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 24 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25 | * 26 | * Sponsored in part by the Defense Advanced Research Projects 27 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 28 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 29 | */ 30 | /*- 31 | * Copyright (c) 2000 The NetBSD Foundation, Inc. 32 | * All rights reserved. 33 | * 34 | * This code is derived from software contributed to The NetBSD Foundation 35 | * by Dieter Baron and Thomas Klausner. 36 | * 37 | * Redistribution and use in source and binary forms, with or without 38 | * modification, are permitted provided that the following conditions 39 | * are met: 40 | * 1. Redistributions of source code must retain the above copyright 41 | * notice, this list of conditions and the following disclaimer. 42 | * 2. Redistributions in binary form must reproduce the above copyright 43 | * notice, this list of conditions and the following disclaimer in the 44 | * documentation and/or other materials provided with the distribution. 45 | * 46 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 47 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 48 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 49 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 50 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 51 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 52 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 53 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 54 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 55 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 56 | * POSSIBILITY OF SUCH DAMAGE. 57 | */ 58 | 59 | #pragma warning(disable:4996); 60 | 61 | #define __GETOPT_H__ 62 | 63 | /* All the headers include this file. */ 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | #ifdef __cplusplus 73 | extern "C" { 74 | #endif 75 | 76 | #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ 77 | 78 | #ifdef REPLACE_GETOPT 79 | int opterr = 1; /* if error message should be printed */ 80 | int optind = 1; /* index into parent argv vector */ 81 | int optopt = '?'; /* character checked for validity */ 82 | #undef optreset /* see getopt.h */ 83 | #define optreset __mingw_optreset 84 | int optreset; /* reset getopt */ 85 | char *optarg; /* argument associated with option */ 86 | #endif 87 | 88 | //extern int optind; /* index of first non-option in argv */ 89 | //extern int optopt; /* single option character, as parsed */ 90 | //extern int opterr; /* flag to enable built-in diagnostics... */ 91 | // /* (user may set to zero, to suppress) */ 92 | // 93 | //extern char *optarg; /* pointer to argument of current option */ 94 | 95 | #define PRINT_ERROR ((opterr) && (*options != ':')) 96 | 97 | #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ 98 | #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ 99 | #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ 100 | 101 | /* return values */ 102 | #define BADCH (int)'?' 103 | #define BADARG ((*options == ':') ? (int)':' : (int)'?') 104 | #define INORDER (int)1 105 | 106 | #ifndef __CYGWIN__ 107 | #define __progname __argv[0] 108 | #else 109 | extern char __declspec(dllimport) *__progname; 110 | #endif 111 | 112 | #ifdef __CYGWIN__ 113 | static char EMSG[] = ""; 114 | #else 115 | #define EMSG "" 116 | #endif 117 | 118 | static int getopt_internal(int, char * const *, const char *, 119 | const struct option *, int *, int); 120 | static int parse_long_options(char * const *, const char *, 121 | const struct option *, int *, int); 122 | static int gcd(int, int); 123 | static void permute_args(int, int, int, char * const *); 124 | 125 | static char *place = EMSG; /* option letter processing */ 126 | 127 | /* XXX: set optreset to 1 rather than these two */ 128 | static int nonopt_start = -1; /* first non option argument (for permute) */ 129 | static int nonopt_end = -1; /* first option after non options (for permute) */ 130 | 131 | /* Error messages */ 132 | static const char recargchar[] = "option requires an argument -- %c"; 133 | static const char recargstring[] = "option requires an argument -- %s"; 134 | static const char ambig[] = "ambiguous option -- %.*s"; 135 | static const char noarg[] = "option doesn't take an argument -- %.*s"; 136 | static const char illoptchar[] = "unknown option -- %c"; 137 | static const char illoptstring[] = "unknown option -- %s"; 138 | 139 | static void 140 | _vwarnx(const char *fmt,va_list ap) 141 | { 142 | (void)fprintf(stderr,"%s: ",__progname); 143 | if (fmt != NULL) 144 | (void)vfprintf(stderr,fmt,ap); 145 | (void)fprintf(stderr,"\n"); 146 | } 147 | 148 | static void 149 | warnx(const char *fmt,...) 150 | { 151 | va_list ap; 152 | va_start(ap,fmt); 153 | _vwarnx(fmt,ap); 154 | va_end(ap); 155 | } 156 | 157 | /* 158 | * Compute the greatest common divisor of a and b. 159 | */ 160 | static int 161 | gcd(int a, int b) 162 | { 163 | int c; 164 | 165 | c = a % b; 166 | while (c != 0) { 167 | a = b; 168 | b = c; 169 | c = a % b; 170 | } 171 | 172 | return (b); 173 | } 174 | 175 | /* 176 | * Exchange the block from nonopt_start to nonopt_end with the block 177 | * from nonopt_end to opt_end (keeping the same order of arguments 178 | * in each block). 179 | */ 180 | static void 181 | permute_args(int panonopt_start, int panonopt_end, int opt_end, 182 | char * const *nargv) 183 | { 184 | int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 185 | char *swap; 186 | 187 | /* 188 | * compute lengths of blocks and number and size of cycles 189 | */ 190 | nnonopts = panonopt_end - panonopt_start; 191 | nopts = opt_end - panonopt_end; 192 | ncycle = gcd(nnonopts, nopts); 193 | cyclelen = (opt_end - panonopt_start) / ncycle; 194 | 195 | for (i = 0; i < ncycle; i++) { 196 | cstart = panonopt_end+i; 197 | pos = cstart; 198 | for (j = 0; j < cyclelen; j++) { 199 | if (pos >= panonopt_end) 200 | pos -= nnonopts; 201 | else 202 | pos += nopts; 203 | swap = nargv[pos]; 204 | /* LINTED const cast */ 205 | ((char **) nargv)[pos] = nargv[cstart]; 206 | /* LINTED const cast */ 207 | ((char **)nargv)[cstart] = swap; 208 | } 209 | } 210 | } 211 | 212 | #ifdef REPLACE_GETOPT 213 | /* 214 | * getopt -- 215 | * Parse argc/argv argument vector. 216 | * 217 | * [eventually this will replace the BSD getopt] 218 | */ 219 | int 220 | getopt(int nargc, char * const *nargv, const char *options) 221 | { 222 | 223 | /* 224 | * We don't pass FLAG_PERMUTE to getopt_internal() since 225 | * the BSD getopt(3) (unlike GNU) has never done this. 226 | * 227 | * Furthermore, since many privileged programs call getopt() 228 | * before dropping privileges it makes sense to keep things 229 | * as simple (and bug-free) as possible. 230 | */ 231 | return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); 232 | } 233 | #endif /* REPLACE_GETOPT */ 234 | 235 | //extern int getopt(int nargc, char * const *nargv, const char *options); 236 | 237 | #ifdef _BSD_SOURCE 238 | /* 239 | * BSD adds the non-standard `optreset' feature, for reinitialisation 240 | * of `getopt' parsing. We support this feature, for applications which 241 | * proclaim their BSD heritage, before including this header; however, 242 | * to maintain portability, developers are advised to avoid it. 243 | */ 244 | # define optreset __mingw_optreset 245 | extern int optreset; 246 | #endif 247 | #ifdef __cplusplus 248 | } 249 | #endif 250 | /* 251 | * POSIX requires the `getopt' API to be specified in `unistd.h'; 252 | * thus, `unistd.h' includes this header. However, we do not want 253 | * to expose the `getopt_long' or `getopt_long_only' APIs, when 254 | * included in this manner. Thus, close the standard __GETOPT_H__ 255 | * declarations block, and open an additional __GETOPT_LONG_H__ 256 | * specific block, only when *not* __UNISTD_H_SOURCED__, in which 257 | * to declare the extended API. 258 | */ 259 | #endif /* !defined(__GETOPT_H__) */ 260 | 261 | #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) 262 | #define __GETOPT_LONG_H__ 263 | 264 | #ifdef __cplusplus 265 | extern "C" { 266 | #endif 267 | 268 | struct option /* specification for a long form option... */ 269 | { 270 | const char *name; /* option name, without leading hyphens */ 271 | int has_arg; /* does it take an argument? */ 272 | int *flag; /* where to save its status, or NULL */ 273 | int val; /* its associated status value */ 274 | }; 275 | 276 | enum /* permitted values for its `has_arg' field... */ 277 | { 278 | no_argument = 0, /* option never takes an argument */ 279 | required_argument, /* option always requires an argument */ 280 | optional_argument /* option may take an argument */ 281 | }; 282 | 283 | /* 284 | * parse_long_options -- 285 | * Parse long options in argc/argv argument vector. 286 | * Returns -1 if short_too is set and the option does not match long_options. 287 | */ 288 | static int 289 | parse_long_options(char * const *nargv, const char *options, 290 | const struct option *long_options, int *idx, int short_too) 291 | { 292 | char *current_argv, *has_equal; 293 | size_t current_argv_len; 294 | int i, ambiguous, match; 295 | 296 | #define IDENTICAL_INTERPRETATION(_x, _y) \ 297 | (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ 298 | long_options[(_x)].flag == long_options[(_y)].flag && \ 299 | long_options[(_x)].val == long_options[(_y)].val) 300 | 301 | current_argv = place; 302 | match = -1; 303 | ambiguous = 0; 304 | 305 | optind++; 306 | 307 | if ((has_equal = strchr(current_argv, '=')) != NULL) { 308 | /* argument found (--option=arg) */ 309 | current_argv_len = has_equal - current_argv; 310 | has_equal++; 311 | } else 312 | current_argv_len = strlen(current_argv); 313 | 314 | for (i = 0; long_options[i].name; i++) { 315 | /* find matching long option */ 316 | if (strncmp(current_argv, long_options[i].name, 317 | current_argv_len)) 318 | continue; 319 | 320 | if (strlen(long_options[i].name) == current_argv_len) { 321 | /* exact match */ 322 | match = i; 323 | ambiguous = 0; 324 | break; 325 | } 326 | /* 327 | * If this is a known short option, don't allow 328 | * a partial match of a single character. 329 | */ 330 | if (short_too && current_argv_len == 1) 331 | continue; 332 | 333 | if (match == -1) /* partial match */ 334 | match = i; 335 | else if (!IDENTICAL_INTERPRETATION(i, match)) 336 | ambiguous = 1; 337 | } 338 | if (ambiguous) { 339 | /* ambiguous abbreviation */ 340 | if (PRINT_ERROR) 341 | warnx(ambig, (int)current_argv_len, 342 | current_argv); 343 | optopt = 0; 344 | return (BADCH); 345 | } 346 | if (match != -1) { /* option found */ 347 | if (long_options[match].has_arg == no_argument 348 | && has_equal) { 349 | if (PRINT_ERROR) 350 | warnx(noarg, (int)current_argv_len, 351 | current_argv); 352 | /* 353 | * XXX: GNU sets optopt to val regardless of flag 354 | */ 355 | if (long_options[match].flag == NULL) 356 | optopt = long_options[match].val; 357 | else 358 | optopt = 0; 359 | return (BADARG); 360 | } 361 | if (long_options[match].has_arg == required_argument || 362 | long_options[match].has_arg == optional_argument) { 363 | if (has_equal) 364 | optarg = has_equal; 365 | else if (long_options[match].has_arg == 366 | required_argument) { 367 | /* 368 | * optional argument doesn't use next nargv 369 | */ 370 | optarg = nargv[optind++]; 371 | } 372 | } 373 | if ((long_options[match].has_arg == required_argument) 374 | && (optarg == NULL)) { 375 | /* 376 | * Missing argument; leading ':' indicates no error 377 | * should be generated. 378 | */ 379 | if (PRINT_ERROR) 380 | warnx(recargstring, 381 | current_argv); 382 | /* 383 | * XXX: GNU sets optopt to val regardless of flag 384 | */ 385 | if (long_options[match].flag == NULL) 386 | optopt = long_options[match].val; 387 | else 388 | optopt = 0; 389 | --optind; 390 | return (BADARG); 391 | } 392 | } else { /* unknown option */ 393 | if (short_too) { 394 | --optind; 395 | return (-1); 396 | } 397 | if (PRINT_ERROR) 398 | warnx(illoptstring, current_argv); 399 | optopt = 0; 400 | return (BADCH); 401 | } 402 | if (idx) 403 | *idx = match; 404 | if (long_options[match].flag) { 405 | *long_options[match].flag = long_options[match].val; 406 | return (0); 407 | } else 408 | return (long_options[match].val); 409 | #undef IDENTICAL_INTERPRETATION 410 | } 411 | 412 | /* 413 | * getopt_internal -- 414 | * Parse argc/argv argument vector. Called by user level routines. 415 | */ 416 | static int 417 | getopt_internal(int nargc, char * const *nargv, const char *options, 418 | const struct option *long_options, int *idx, int flags) 419 | { 420 | char *oli; /* option letter list index */ 421 | int optchar, short_too; 422 | static int posixly_correct = -1; 423 | 424 | if (options == NULL) 425 | return (-1); 426 | 427 | /* 428 | * XXX Some GNU programs (like cvs) set optind to 0 instead of 429 | * XXX using optreset. Work around this braindamage. 430 | */ 431 | if (optind == 0) 432 | optind = optreset = 1; 433 | 434 | /* 435 | * Disable GNU extensions if POSIXLY_CORRECT is set or options 436 | * string begins with a '+'. 437 | * 438 | * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or 439 | * optreset != 0 for GNU compatibility. 440 | */ 441 | if (posixly_correct == -1 || optreset != 0) 442 | posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); 443 | if (*options == '-') 444 | flags |= FLAG_ALLARGS; 445 | else if (posixly_correct || *options == '+') 446 | flags &= ~FLAG_PERMUTE; 447 | if (*options == '+' || *options == '-') 448 | options++; 449 | 450 | optarg = NULL; 451 | if (optreset) 452 | nonopt_start = nonopt_end = -1; 453 | start: 454 | if (optreset || !*place) { /* update scanning pointer */ 455 | optreset = 0; 456 | if (optind >= nargc) { /* end of argument vector */ 457 | place = EMSG; 458 | if (nonopt_end != -1) { 459 | /* do permutation, if we have to */ 460 | permute_args(nonopt_start, nonopt_end, 461 | optind, nargv); 462 | optind -= nonopt_end - nonopt_start; 463 | } 464 | else if (nonopt_start != -1) { 465 | /* 466 | * If we skipped non-options, set optind 467 | * to the first of them. 468 | */ 469 | optind = nonopt_start; 470 | } 471 | nonopt_start = nonopt_end = -1; 472 | return (-1); 473 | } 474 | if (*(place = nargv[optind]) != '-' || 475 | (place[1] == '\0' && strchr(options, '-') == NULL)) { 476 | place = EMSG; /* found non-option */ 477 | if (flags & FLAG_ALLARGS) { 478 | /* 479 | * GNU extension: 480 | * return non-option as argument to option 1 481 | */ 482 | optarg = nargv[optind++]; 483 | return (INORDER); 484 | } 485 | if (!(flags & FLAG_PERMUTE)) { 486 | /* 487 | * If no permutation wanted, stop parsing 488 | * at first non-option. 489 | */ 490 | return (-1); 491 | } 492 | /* do permutation */ 493 | if (nonopt_start == -1) 494 | nonopt_start = optind; 495 | else if (nonopt_end != -1) { 496 | permute_args(nonopt_start, nonopt_end, 497 | optind, nargv); 498 | nonopt_start = optind - 499 | (nonopt_end - nonopt_start); 500 | nonopt_end = -1; 501 | } 502 | optind++; 503 | /* process next argument */ 504 | goto start; 505 | } 506 | if (nonopt_start != -1 && nonopt_end == -1) 507 | nonopt_end = optind; 508 | 509 | /* 510 | * If we have "-" do nothing, if "--" we are done. 511 | */ 512 | if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { 513 | optind++; 514 | place = EMSG; 515 | /* 516 | * We found an option (--), so if we skipped 517 | * non-options, we have to permute. 518 | */ 519 | if (nonopt_end != -1) { 520 | permute_args(nonopt_start, nonopt_end, 521 | optind, nargv); 522 | optind -= nonopt_end - nonopt_start; 523 | } 524 | nonopt_start = nonopt_end = -1; 525 | return (-1); 526 | } 527 | } 528 | 529 | /* 530 | * Check long options if: 531 | * 1) we were passed some 532 | * 2) the arg is not just "-" 533 | * 3) either the arg starts with -- we are getopt_long_only() 534 | */ 535 | if (long_options != NULL && place != nargv[optind] && 536 | (*place == '-' || (flags & FLAG_LONGONLY))) { 537 | short_too = 0; 538 | if (*place == '-') 539 | place++; /* --foo long option */ 540 | else if (*place != ':' && strchr(options, *place) != NULL) 541 | short_too = 1; /* could be short option too */ 542 | 543 | optchar = parse_long_options(nargv, options, long_options, 544 | idx, short_too); 545 | if (optchar != -1) { 546 | place = EMSG; 547 | return (optchar); 548 | } 549 | } 550 | 551 | if ((optchar = (int)*place++) == (int)':' || 552 | (optchar == (int)'-' && *place != '\0') || 553 | (oli = (char*)strchr(options, optchar)) == NULL) { 554 | /* 555 | * If the user specified "-" and '-' isn't listed in 556 | * options, return -1 (non-option) as per POSIX. 557 | * Otherwise, it is an unknown option character (or ':'). 558 | */ 559 | if (optchar == (int)'-' && *place == '\0') 560 | return (-1); 561 | if (!*place) 562 | ++optind; 563 | if (PRINT_ERROR) 564 | warnx(illoptchar, optchar); 565 | optopt = optchar; 566 | return (BADCH); 567 | } 568 | if (long_options != NULL && optchar == 'W' && oli[1] == ';') { 569 | /* -W long-option */ 570 | if (*place) /* no space */ 571 | /* NOTHING */; 572 | else if (++optind >= nargc) { /* no arg */ 573 | place = EMSG; 574 | if (PRINT_ERROR) 575 | warnx(recargchar, optchar); 576 | optopt = optchar; 577 | return (BADARG); 578 | } else /* white space */ 579 | place = nargv[optind]; 580 | optchar = parse_long_options(nargv, options, long_options, 581 | idx, 0); 582 | place = EMSG; 583 | return (optchar); 584 | } 585 | if (*++oli != ':') { /* doesn't take argument */ 586 | if (!*place) 587 | ++optind; 588 | } else { /* takes (optional) argument */ 589 | optarg = NULL; 590 | if (*place) /* no white space */ 591 | optarg = place; 592 | else if (oli[1] != ':') { /* arg not optional */ 593 | if (++optind >= nargc) { /* no arg */ 594 | place = EMSG; 595 | if (PRINT_ERROR) 596 | warnx(recargchar, optchar); 597 | optopt = optchar; 598 | return (BADARG); 599 | } else 600 | optarg = nargv[optind]; 601 | } 602 | place = EMSG; 603 | ++optind; 604 | } 605 | /* dump back option letter */ 606 | return (optchar); 607 | } 608 | 609 | /* 610 | * getopt_long -- 611 | * Parse argc/argv argument vector. 612 | */ 613 | int 614 | getopt_long(int nargc, char * const *nargv, const char *options, 615 | const struct option *long_options, int *idx) 616 | { 617 | 618 | return (getopt_internal(nargc, nargv, options, long_options, idx, 619 | FLAG_PERMUTE)); 620 | } 621 | 622 | /* 623 | * getopt_long_only -- 624 | * Parse argc/argv argument vector. 625 | */ 626 | int 627 | getopt_long_only(int nargc, char * const *nargv, const char *options, 628 | const struct option *long_options, int *idx) 629 | { 630 | 631 | return (getopt_internal(nargc, nargv, options, long_options, idx, 632 | FLAG_PERMUTE|FLAG_LONGONLY)); 633 | } 634 | 635 | //extern int getopt_long(int nargc, char * const *nargv, const char *options, 636 | // const struct option *long_options, int *idx); 637 | //extern int getopt_long_only(int nargc, char * const *nargv, const char *options, 638 | // const struct option *long_options, int *idx); 639 | /* 640 | * Previous MinGW implementation had... 641 | */ 642 | #ifndef HAVE_DECL_GETOPT 643 | /* 644 | * ...for the long form API only; keep this for compatibility. 645 | */ 646 | # define HAVE_DECL_GETOPT 1 647 | #endif 648 | 649 | #ifdef __cplusplus 650 | } 651 | #endif 652 | 653 | #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ 654 | --------------------------------------------------------------------------------