diff options
-rw-r--r-- | .travis.yml | 65 | ||||
-rw-r--r-- | Makefile | 271 | ||||
-rw-r--r-- | README.md | 26 | ||||
-rw-r--r-- | printf.cpp | 54 | ||||
-rw-r--r-- | test/test_suite.cpp | 104 |
5 files changed, 486 insertions, 34 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c3047a4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,65 @@ +# Use a C++11 distro
+dist: trusty
+sudo: required
+
+# Enable C++ support
+language: cpp
+
+# Compiler selection
+compiler: gcc
+
+env:
+ global:
+ # coverity key
+ - secure: "NKZbBnMALGIIQJy/s2kc3EST/stw+gjhtrGq0jkbsWr7Wx3FH+lmLeHNsDXRnD1VbpG02c5YsLllqz9OVu+0yxWGepvKNmCz1cNITIALEHbrax8/Af9LzPRL/QZxS/Qe11sMuySp4X16mFBUyxMd/X+I9i96Xf1vKkZABklYD1Q="
+
+# addons
+addons:
+ apt:
+ packages:
+ - gcc-6
+ - g++-6
+ sources:
+ - ubuntu-toolchain-r-test
+
+ coverity_scan:
+ project:
+ name: "mpaland/printf"
+ description: "Tiny printf implementation"
+ notification_email: marco@paland.com
+ build_command_prepend: "make clean"
+ build_command: "make"
+ branch_pattern: master
+
+before_install:
+ # install coveralls
+ - pip install --user cpp-coveralls
+ # connect coverity
+ - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
+
+# Active branches
+branches:
+ only:
+ - master
+
+script:
+ # Link gcc-6 and g++-6 to their standard commands
+ - sudo rm /usr/bin/gcc
+ - sudo rm /usr/bin/g++
+ - sudo ln -s /usr/bin/gcc-6 /usr/bin/gcc
+ - sudo ln -s /usr/bin/g++-6 /usr/bin/g++
+ # Export CC and CXX
+ - export CC=/usr/bin/gcc-6
+ - export CXX=/usr/bin/g++-6
+ # Check versions of gcc, g++
+ - gcc -v && g++ -v
+ # Run build commands
+ - make
+ # execute the text suite
+ - bin/test_suite -d yes
+ # coverall profiling
+ - tmp/cov/test_suite
+
+after_success:
+ # Report to coveralls
+ - coveralls --build-root ${TRAVIS_BUILD_DIR} --include printf.cpp --gcov 'gcov-6' --gcov-options '\-lp'
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7b28a1a --- /dev/null +++ b/Makefile @@ -0,0 +1,271 @@ +# ------------------------------------------------------------------------------
+#
+# Generic Makefile
+#
+# Copyright Marco Paland 2007 - 2017
+# Distributed under the MIT License
+#
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# Paths
+# ------------------------------------------------------------------------------
+PATH_TOOLS_CC = /usr/bin/
+PATH_TOOLS_CC_LIB = /usr/lib/
+PATH_TOOLS_UTIL =
+
+PATH_BIN = bin
+PATH_TMP = tmp
+PATH_NUL = /dev/null
+PATH_OBJ = $(PATH_TMP)/obj
+PATH_LST = $(PATH_TMP)/lst
+PATH_ERR = $(PATH_TMP)/err
+PATH_PRE = $(PATH_TMP)/pre
+PATH_COV = $(PATH_TMP)/cov
+
+
+# ------------------------------------------------------------------------------
+# Application to build
+# ------------------------------------------------------------------------------
+
+APP = test_suite
+
+
+# -----------------------------------------------------------------------------
+# Project file list
+# Format is:
+# FILES_PRJ = file1 \
+# foo/file2 \
+# bar/file3
+# -----------------------------------------------------------------------------
+
+FILES_PRJ = test/test_suite
+
+
+# ------------------------------------------------------------------------------
+# Additional include files and compiler defines
+# Format is:
+# C_INCLUDES = -Iinclude_path1 \
+# -Iinclude_path2 \
+# -Iinclude_path3 \
+# ------------------------------------------------------------------------------
+
+C_INCLUDES =
+
+C_DEFINES =
+
+
+# ------------------------------------------------------------------------------
+# The target name and location
+# ------------------------------------------------------------------------------
+TRG = $(PATH_BIN)/$(APP)
+
+
+# ------------------------------------------------------------------------------
+# object files
+# ------------------------------------------------------------------------------
+FILES_TMP = $(FILES_PRJ)
+FILES_O = $(addsuffix .o, $(FILES_TMP))
+
+
+# ------------------------------------------------------------------------------
+# VPATH definition
+#
+# VPATH is required for the maker to find the C-/ASM-Source files.
+# Extract the directory/module names from the file list with the dir
+# command and remove the duplicated directory names with the sort command.
+# FILES_PRJ is listed first to make sure that the source files in the project
+# directory are searched first.
+# ------------------------------------------------------------------------------
+VPATH := $(sort $(dir $(FILES_TMP)))
+
+
+# ------------------------------------------------------------------------------
+# Development tools
+# ------------------------------------------------------------------------------
+AR = $(PATH_TOOLS_CC)ar
+AS = $(PATH_TOOLS_CC)g++
+CC = $(PATH_TOOLS_CC)g++
+CL = $(PATH_TOOLS_CC)g++
+NM = $(PATH_TOOLS_CC)nm
+GCOV = $(PATH_TOOLS_CC)gcov
+OBJDUMP = $(PATH_TOOLS_CC)objdump
+OBJCOPY = $(PATH_TOOLS_CC)objcopy
+READELF = $(PATH_TOOLS_CC)readelf
+SIZE = $(PATH_TOOLS_CC)size
+
+ECHO = $(PATH_TOOLS_UTIL)echo
+MAKE = $(PATH_TOOLS_UTIL)make
+MKDIR = $(PATH_TOOLS_UTIL)mkdir
+RM = $(PATH_TOOLS_UTIL)rm
+SED = $(PATH_TOOLS_UTIL)sed
+
+
+# ------------------------------------------------------------------------------
+# Compiler flags for the target architecture
+# ------------------------------------------------------------------------------
+
+GCCFLAGS = $(C_INCLUDES) \
+ $(C_DEFINES) \
+ -std=c++11 \
+ -g \
+ -Wall \
+ -pedantic \
+ -Wmain \
+ -Wundef \
+ -Wsign-conversion \
+ -Wuninitialized \
+ -Wshadow \
+ -Wunreachable-code \
+ -Wswitch-default \
+ -Wswitch \
+ -Wcast-align \
+ -Wmissing-include-dirs \
+ -Winit-self \
+ -Wdouble-promotion \
+ -gdwarf-2 \
+ -fno-exceptions \
+ -O2 \
+ -ffunction-sections \
+ -ffat-lto-objects \
+ -fdata-sections \
+ -fverbose-asm \
+ -Wextra \
+ -Wunused-parameter \
+ -Wfloat-equal
+
+CFLAGS = $(GCCFLAGS) \
+ -Wunsuffixed-float-constants \
+ -x c \
+ -std=c99
+
+CPPFLAGS = $(GCCFLAGS) \
+ -x c++ \
+ -fno-rtti \
+ -fstrict-enums \
+ -fno-use-cxa-atexit \
+ -fno-use-cxa-get-exception-ptr \
+ -fno-nonansi-builtins \
+ -fno-threadsafe-statics \
+ -fno-enforce-eh-specs \
+ -ftemplate-depth-64 \
+ -fexceptions
+
+AFLAGS = $(GCCFLAGS) \
+ -x assembler
+
+LFLAGS = $(GCCFLAGS) \
+ -x none \
+ -Wl,--gc-sections
+
+# ------------------------------------------------------------------------------
+# Targets
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# Main-Dependencies (app: all)
+# ------------------------------------------------------------------------------
+.PHONY: all
+all: clean_prj $(TRG) $(TRG)_nm.txt
+
+
+# ------------------------------------------------------------------------------
+# Main-Dependencies (app: rebuild)
+# ------------------------------------------------------------------------------
+.PHONY: rebuild
+rebuild: clean $(TRG) $(TRG)_nm.txt
+
+
+# ------------------------------------------------------------------------------
+# clean project
+# ------------------------------------------------------------------------------
+.PHONY: clean_prj
+clean_prj:
+ @-$(ECHO) +++ cleaning project
+ @-$(RM) -rf $(PATH_BIN) 2> $(PATH_NUL)
+ @-$(MKDIR) -p $(PATH_BIN)
+ @-$(MKDIR) -p $(PATH_OBJ)
+ @-$(MKDIR) -p $(PATH_ERR)
+ @-$(MKDIR) -p $(PATH_LST)
+ @-$(MKDIR) -p $(PATH_PRE)
+ @-$(MKDIR) -p $(PATH_COV)
+
+
+# ------------------------------------------------------------------------------
+# clean all
+# ------------------------------------------------------------------------------
+.PHONY: clean
+clean:
+ @-$(ECHO) +++ cleaning all
+ @-$(RM) -rf $(PATH_BIN) 2> $(PATH_NUL)
+ @-$(RM) -rf $(PATH_TMP) 2> $(PATH_NUL)
+ @-$(MKDIR) -p $(PATH_BIN)
+ @-$(MKDIR) -p $(PATH_OBJ)
+ @-$(MKDIR) -p $(PATH_ERR)
+ @-$(MKDIR) -p $(PATH_LST)
+ @-$(MKDIR) -p $(PATH_COV)
+
+
+# ------------------------------------------------------------------------------
+# print the GNUmake version and the compiler version
+# ------------------------------------------------------------------------------
+.PHONY: version
+version:
+ # Print the GNU make version and the compiler version
+ @$(ECHO) GNUmake version:
+ @$(MAKE) --version
+ @$(ECHO) GCC version:
+ @$(CL) -v
+
+
+# ------------------------------------------------------------------------------
+# Rules
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# Link/locate application
+# ------------------------------------------------------------------------------
+$(TRG) : $(FILES_O)
+ @-$(ECHO) +++ linkink application to generate: $(TRG)
+ @-$(CL) $(LFLAGS) -L. -lc $(PATH_OBJ)/*.o -Wl,-Map,$(TRG).map -o $(TRG)
+ # profiling
+ @-$(CL) $(LFLAGS) -L. -lc $(PATH_COV)/*.o --coverage -o $(PATH_COV)/$(APP)
+
+
+# ------------------------------------------------------------------------------
+# parse the object files to obtain symbol information, and create a size summary
+# ------------------------------------------------------------------------------
+$(TRG)_nm.txt : $(TRG)
+ @-$(ECHO) +++ parsing symbols with nm to generate: $(TRG)_nm.txt
+ @-$(NM) --numeric-sort --print-size $(TRG) > $(TRG)_nm.txt
+ @-$(ECHO) +++ demangling symbols with c++filt to generate: $(TRG)_cppfilt.txt
+ @-$(NM) --numeric-sort --print-size $(TRG) | $(CPPFILT) > $(TRG)_cppfilt.txt
+ @-$(ECHO) +++ creating size summary table with size to generate: $(TRG)_size.txt
+ @-$(SIZE) -A -t $(TRG) > $(TRG)_size.txt
+
+
+%.o : %.cpp
+ @$(ECHO) +++ compile: $<
+ # Compile the source file
+ # ...and Reformat (using sed) any possible error/warning messages for the VisualStudio(R) output window
+ # ...and Create an assembly listing using objdump
+ # ...and Generate a dependency file (using the -MM flag)
+ @-$(CL) $(CPPFLAGS) $< -E -o $(PATH_PRE)/$(basename $(@F)).pre
+ @-$(CL) $(CPPFLAGS) $< -c -o $(PATH_OBJ)/$(basename $(@F)).o 2> $(PATH_ERR)/$(basename $(@F)).err
+ @-$(SED) -e 's|.h:\([0-9]*\),|.h(\1) :|' -e 's|:\([0-9]*\):|(\1) :|' $(PATH_ERR)/$(basename $(@F)).err
+ @-$(OBJDUMP) --disassemble --line-numbers -S $(PATH_OBJ)/$(basename $(@F)).o > $(PATH_LST)/$(basename $(@F)).lst
+ @-$(CL) $(CPPFLAGS) $< -MM > $(PATH_OBJ)/$(basename $(@F)).d
+ # profiling
+ @-$(CL) $(CPPFLAGS) -O0 --coverage $< -c -o $(PATH_COV)/$(basename $(@F)).o 2> $(PATH_NUL)
+
+%.o : %.c
+ @$(ECHO) +++ compile: $<
+ # Compile the source file
+ # ...and Reformat (using sed) any possible error/warning messages for the VisualStudio(R) output window
+ # ...and Create an assembly listing using objdump
+ # ...and Generate a dependency file (using the -MM flag)
+ @-$(CL) $(CFLAGS) $< -E -o $(PATH_PRE)/$(basename $(@F)).pre
+ @-$(CC) $(CFLAGS) $< -c -o $(PATH_OBJ)/$(basename $(@F)).o 2> $(PATH_ERR)/$(basename $(@F)).err
+ @-$(SED) -e 's|.h:\([0-9]*\),|.h(\1) :|' -e 's|:\([0-9]*\):|(\1) :|' $(PATH_ERR)/$(basename $(@F)).err
+ @-$(OBJDUMP) -S $(PATH_OBJ)/$(basename $(@F)).o > $(PATH_LST)/$(basename $(@F)).lst
+ @-$(CC) $(CFLAGS) $< -MM > $(PATH_OBJ)/$(basename $(@F)).d
@@ -1,27 +1,34 @@ # printf / sprintf for embedded systems
-This is a tiny but fully loaded printf, sprintf and snprintf implementation.
+[](https://travis-ci.org/mpaland/printf)
+[](https://coveralls.io/github/mpaland/printf?branch=master)
+[](https://scan.coverity.com/projects/mpaland-printf)
+[](http://github.com/mpaland/printf/issues)
+[](https://github.com/mpaland/printf/releases)
+[](https://raw.githubusercontent.com/mpaland/avl_array/master/LICENSE)
+
+This is a tiny but **fully loaded** printf, sprintf and snprintf implementation.
Primarily designed for usage in embedded systems, where printf is not available due to memory issues or in avoidance of linking against libc.
-Using the standard libc printf may pull a lot of unwanted library stuff and can bloat code size about 20k. In this case the following implementation can be used.
-Absolutely **NO dependencies** are required, printf.cpp brings all necessary routines, even its own fast ftoa conversion.
+Using the standard libc printf may pull **a lot** of unwanted library stuff and can bloat code size about 20k. In this case the following implementation can be used.
+Absolutely **NO dependencies** are required, printf.cpp brings all necessary routines, even its own fast `ftoa` conversion.
If memory footprint is really a critical issue, floating point support can be turned off via the `PRINTF_FLOAT_SUPPORT` compiler switch.
When using printf (instead of sprintf) you have to provide your own `_putchar()` low level function as console output.
-## Design goals
+## Highligths and design goals
There is a boatload of so called 'tiny' printf implementations around. So why this one?
-I tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing the test suite.
+I've tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing the test suite.
Therefore I decided to write an own implementation which meets the following items:
- Very small implementation (< 500 code lines)
- NO dependencies, no libs, just one module file
- Support of all important flags, width and precision sub-specifiers (see below)
- - Support of float number representation (with an own fast ftoa)
+ - Support of dec/float number representation (with an own fast itoa/ftoa)
- Reentrant and thread-safe, malloc free
- - LINT and compiler L4 warning free, clean code
- - Extensive test suite passing
+ - LINT and compiler L4 warning free, coverity clean, automotive ready
+ - Extensive test suite (> 260 test cases) passing
- MIT license
@@ -34,7 +41,8 @@ Usage is 1:1 like the according stdio.h library version: `int sprintf(char* buffer, const char* format, ...);`
`int snprintf(char* buffer, size_t count, const char* format, ...);`
-**Due to genaral security reasons it is highly recommended to use snprintf (with the max buffer size as `count` parameter) only.**
+**Due to genaral security reasons it is highly recommended to use `snprintf` (with the max buffer size as `count` parameter) only.**
+`sprintf` has no buffer limitation, so when necessary - use it with care!
## Format specifiers
@@ -65,18 +65,18 @@ // internal strlen, returns the length of the string
static inline size_t _strlen(const char* str)
{
- size_t len = 0U;
- while (str[len] != '\0') {
- len++;
- }
- return len;
-}
+ size_t len = 0U;
+ while (str[len] != '\0') {
+ len++;
+ }
+ return len;
+}
-// returns 1 if char is a digit, 0 if not
-static inline unsigned int _is_digit(char ch)
+// returns true if char is a digit
+static inline bool _is_digit(char ch)
{
- return (ch >= '0' && ch <= '9') ? 1U : 0U;
+ return (ch >= '0') && (ch <= '9');
}
@@ -85,7 +85,7 @@ static inline unsigned int _atoi(const char** str) {
unsigned int i = 0U;
while (_is_digit(**str)) {
- i = i * 10U + *((*str)++) - '0';
+ i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
@@ -113,7 +113,7 @@ static size_t _ntoa(T value, char* buffer, unsigned int base, size_t maxlen, uns // write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || (value != 0)) {
do {
- char digit = (char)((unsigned)value % base);
+ char digit = (char)(value % (T)base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= (T)base;
} while ((len < NTOA_BUFFER_SIZE) && (value > 0));
@@ -197,8 +197,8 @@ static size_t _ftoa(double value, char* buffer, size_t maxlen, unsigned int prec const double thres_max = (double)0x7FFFFFFF;
char buf[FTOA_BUFFER_SIZE];
- size_t len = 0U;
- double diff = 0.0;
+ size_t len = 0U;
+ double diff = 0;
// powers of 10
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
@@ -225,7 +225,7 @@ static size_t _ftoa(double value, char* buffer, size_t maxlen, unsigned int prec if (diff > 0.5) {
++frac;
- // handle rollover, e.g. case 0.99 with prec 1 is 1.0
+ // handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec]) {
frac = 0;
++whole;
@@ -271,13 +271,13 @@ static size_t _ftoa(double value, char* buffer, size_t maxlen, unsigned int prec }
}
- // do whole part
- // Take care of sign conversion. Number is reversed
- size_t wlen = 0U;
- do {
+ // do whole part, number is reversed
+ while (len < FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
- wlen++;
- } while ((len < FTOA_BUFFER_SIZE) && (whole /= 10));
+ if (!(whole /= 10)) {
+ break;
+ }
+ }
// pad leading zeros
while (!(flags & FLAGS_LEFT) && (len < prec) && (len < FTOA_BUFFER_SIZE)) {
@@ -368,7 +368,7 @@ static size_t vsnprintf(char* buffer, size_t buffer_len, const char* format, va_ width = _atoi(&format);
}
else if (*format == '*') {
- const int w = (unsigned int)va_arg(va, int);
+ const int w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int)-w;
@@ -410,8 +410,6 @@ static size_t vsnprintf(char* buffer, size_t buffer_len, const char* format, va_ case 'X' :
case 'o' :
case 'b' :
- // no plus or space flag for the types above
- flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
case 'd' :
case 'i' : {
// set the base
@@ -435,8 +433,13 @@ static size_t vsnprintf(char* buffer, size_t buffer_len, const char* format, va_ flags |= FLAGS_UPPERCASE;
}
+ // no plus or space flag for u, x, X, o, b
+ if ((*format != 'i') && (*format != 'd')) {
+ flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+ }
+
// convert the integer
- if (*format == 'i' || *format == 'd') {
+ if ((*format == 'i') || (*format == 'd')) {
// signed
if (flags & FLAGS_LONG_LONG) {
idx += _ntoa<long long>(va_arg(va, long long), &buffer[idx], base, buffer_len - idx, precision, width, flags);
@@ -479,7 +482,7 @@ static size_t vsnprintf(char* buffer, size_t buffer_len, const char* format, va_ }
}
// char output
- buffer[idx++] = va_arg(va, char);
+ buffer[idx++] = (char)va_arg(va, int);
// post padding
if (flags & FLAGS_LEFT) {
while ((idx < buffer_len) && (l++ < width)) {
@@ -537,6 +540,7 @@ static size_t vsnprintf(char* buffer, size_t buffer_len, const char* format, va_ default :
buffer[idx++] = *format;
+ format++;
break;
}
}
diff --git a/test/test_suite.cpp b/test/test_suite.cpp index a90f701..0b2f680 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -203,6 +203,9 @@ TEST_CASE("0 flag", "[]" ) { test::sprintf(buffer, "%0d", 42);
REQUIRE(!strcmp(buffer, "42"));
+ test::sprintf(buffer, "%0ld", 42L);
+ REQUIRE(!strcmp(buffer, "42"));
+
test::sprintf(buffer, "%0d", -42);
REQUIRE(!strcmp(buffer, "-42"));
@@ -864,4 +867,105 @@ TEST_CASE("float", "[]" ) { test::sprintf(buffer, "%+6.2f", 42.8952);
REQUIRE(!strcmp(buffer, "+42.90"));
+ test::sprintf(buffer, "%+5.1f", 42.9252);
+ REQUIRE(!strcmp(buffer, "+42.9"));
+
+ test::sprintf(buffer, "%f", 42.5);
+ REQUIRE(!strcmp(buffer, "42.500000"));
+
+ test::sprintf(buffer, "%.1f", 42.5);
+ REQUIRE(!strcmp(buffer, "42.5"));
+
+ test::sprintf(buffer, "%f", (float)42167);
+ REQUIRE(!strcmp(buffer, "42167.000000"));
+}
+
+
+TEST_CASE("types", "[]" ) {
+ char buffer[100];
+
+ test::sprintf(buffer, "%i", 1234);
+ REQUIRE(!strcmp(buffer, "1234"));
+
+ test::sprintf(buffer, "%li", 30L);
+ REQUIRE(!strcmp(buffer, "30"));
+
+ test::sprintf(buffer, "%lli", 30LL);
+ REQUIRE(!strcmp(buffer, "30"));
+
+ test::sprintf(buffer, "%lu", 100000L);
+ REQUIRE(!strcmp(buffer, "100000"));
+
+ test::sprintf(buffer, "%llu", 281474976710656LLU);
+ REQUIRE(!strcmp(buffer, "281474976710656"));
+
+ test::sprintf(buffer, "%b", 60000);
+ REQUIRE(!strcmp(buffer, "1110101001100000"));
+
+ test::sprintf(buffer, "%lb", 12345678L);
+ REQUIRE(!strcmp(buffer, "101111000110000101001110"));
+
+ test::sprintf(buffer, "%o", 60000);
+ REQUIRE(!strcmp(buffer, "165140"));
+
+ test::sprintf(buffer, "%lo", 12345678L);
+ REQUIRE(!strcmp(buffer, "57060516"));
+
+ test::sprintf(buffer, "%lx", 0x12345678L);
+ REQUIRE(!strcmp(buffer, "12345678"));
+
+ test::sprintf(buffer, "%llx", 0x1234567891234567LLU);
+ REQUIRE(!strcmp(buffer, "1234567891234567"));
+
+ test::sprintf(buffer, "%lx", 0xabcdefabL);
+ REQUIRE(!strcmp(buffer, "abcdefab"));
+
+ test::sprintf(buffer, "%lX", 0xabcdefabL);
+ REQUIRE(!strcmp(buffer, "ABCDEFAB"));
+
+ test::sprintf(buffer, "%c", 'v');
+ REQUIRE(!strcmp(buffer, "v"));
+
+ test::sprintf(buffer, "%cv", 'w');
+ REQUIRE(!strcmp(buffer, "wv"));
+
+ test::sprintf(buffer, "%s", "A Test");
+ REQUIRE(!strcmp(buffer, "A Test"));
+}
+
+
+TEST_CASE("pointer", "[]" ) {
+ char buffer[100];
+
+ test::sprintf(buffer, "%p", (void*)0x1234U);
+ if (sizeof(void*) == 4U) {
+ REQUIRE(!strcmp(buffer, "00001234"));
+ }
+ else {
+ REQUIRE(!strcmp(buffer, "0000000000001234"));
+ }
+
+ test::sprintf(buffer, "%p", (void*)0x12345678U);
+ if (sizeof(void*) == 4U) {
+ REQUIRE(!strcmp(buffer, "12345678"));
+ }
+ else {
+ REQUIRE(!strcmp(buffer, "0000000012345678"));
+ }
+}
+
+
+TEST_CASE("unknown flag", "[]" ) {
+ char buffer[100];
+
+ test::sprintf(buffer, "%kmarco", 42, 37);
+ REQUIRE(!strcmp(buffer, "kmarco"));
+}
+
+
+TEST_CASE("misc", "[]" ) {
+ char buffer[100];
+
+ test::sprintf(buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit");
+ REQUIRE(!strcmp(buffer, "53000atest-20 bit"));
}
|