aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Paland <marco@paland.com>2017-10-31 13:49:28 +0100
committerMarco Paland <marco@paland.com>2017-10-31 13:49:28 +0100
commit5813e52e3f08ca1c4bb4e4ea41e03ce787ac25c8 (patch)
treef32fa0d2b1bea0f549557a2d4c09d395cee82145
parente50beaa69ea4832dd984fef2e4c297df3495801b (diff)
downloadprintf-5813e52e3f08ca1c4bb4e4ea41e03ce787ac25c8.tar.gz
printf-5813e52e3f08ca1c4bb4e4ea41e03ce787ac25c8.tar.bz2
printf-5813e52e3f08ca1c4bb4e4ea41e03ce787ac25c8.zip
Fix compiler/coverity warnings, add test cases
-rw-r--r--.travis.yml2
-rw-r--r--README.md11
-rw-r--r--printf.cpp44
-rw-r--r--test/test_suite.cpp104
4 files changed, 135 insertions, 26 deletions
diff --git a/.travis.yml b/.travis.yml
index 0d7cadc..c3047a4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,7 +40,7 @@ before_install:
# Active branches
branches:
only:
- - travis
+ - master
script:
# Link gcc-6 and g++-6 to their standard commands
diff --git a/README.md b/README.md
index fd7d142..f2e230f 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,10 @@
[![Github Releases](https://img.shields.io/github/release/mpaland/printf.svg)](https://github.com/mpaland/printf/releases)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/mpaland/avl_array/master/LICENSE)
-This is a tiny but fully loaded printf, sprintf and snprintf implementation.
+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.
+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.
@@ -27,8 +27,8 @@ Therefore I decided to write an own implementation which meets the following ite
- Support of all important flags, width and precision sub-specifiers (see below)
- 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
@@ -41,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
diff --git a/printf.cpp b/printf.cpp
index 5eb8840..d5f3603 100644
--- a/printf.cpp
+++ b/printf.cpp
@@ -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');
}
@@ -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));
@@ -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);
@@ -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"));
}