Skip to content

Commit

Permalink
Fixes eyalroz#63; (may) improve support for systems where the null po…
Browse files Browse the repository at this point in the history
…inter representation is no all-0-bits.

* "splits" behavior of pointers and hash-marked base-16 values w.r.t the `0x` prefix (although you won't see the difference unless you un-special-case the handling of 0-valued pointer).
* Passing a null pointer for an `%s` now results in `(null)`.
* Added some relevant test suite assertions involving null/0 pointers as `%s` arguments.
  • Loading branch information
Eyal Rozenberg committed Jul 1, 2021
1 parent 8e94bab commit e4baa6a
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 27 deletions.
50 changes: 29 additions & 21 deletions printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
#define FLAGS_LONG_LONG (1U << 9U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_ADAPT_EXP (1U << 11U)
#define FLAGS_POINTER (1U << 12U)
// Note: Similar, but not identical, effect as FLAGS_HASH


// import float.h for DBL_MAX
Expand Down Expand Up @@ -240,7 +242,7 @@ static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t ma
}

// handle hash
if (flags & FLAGS_HASH) {
if (flags & (FLAGS_HASH | FLAGS_POINTER)) {
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
len--;
if (len && (base == 16U)) {
Expand Down Expand Up @@ -798,24 +800,29 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const

case 's' : {
const char* p = va_arg(va, char*);
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
if (p == NULL) {
idx = _out_rev(out, buffer, idx, maxlen, ")llun(", 6, width, flags);
}
else {
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
}
format++;
Expand All @@ -824,12 +831,13 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const

case 'p' : {
width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix
flags |= FLAGS_ZEROPAD | FLAGS_HASH;
flags |= FLAGS_ZEROPAD | FLAGS_POINTER;
uintptr_t value = (uintptr_t)va_arg(va, void*);

if (value == 0) {
if (value == (uintptr_t) NULL) {
idx = _out_rev(out, buffer, idx, maxlen, ")lin(", 5, width, flags);
} else {
}
else {
#if defined(PRINTF_SUPPORT_LONG_LONG)
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
if (is_ll) {
Expand Down
22 changes: 16 additions & 6 deletions test/test_suite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,6 @@ TEST_CASE("space flag - non-standard format", "[]" ) {
test::sprintf(buffer, "% s", "Hello testing");
REQUIRE(!strcmp(buffer, "Hello testing"));

test::sprintf(buffer, "% s", "Hello testing");
REQUIRE(!strcmp(buffer, "Hello testing"));

test::sprintf(buffer, "% u", 1024);
REQUIRE(!strcmp(buffer, "1024"));

Expand Down Expand Up @@ -515,6 +512,9 @@ TEST_CASE("specifier", "[]" ) {
test::sprintf(buffer, "%s", "Hello testing");
REQUIRE(!strcmp(buffer, "Hello testing"));

test::sprintf(buffer, "%s", NULL);
REQUIRE(!strcmp(buffer, "(null)"));

test::sprintf(buffer, "%d", 1024);
REQUIRE(!strcmp(buffer, "1024"));

Expand Down Expand Up @@ -1506,7 +1506,7 @@ TEST_CASE("pointer", "[]" ) {
REQUIRE(!strcmp(buffer, "0xffffffff"));
}

test::sprintf(buffer, "%p", (void*) nullptr);
test::sprintf(buffer, "%p", NULL);
REQUIRE(!strcmp(buffer, "(nil)"));
}

Expand Down Expand Up @@ -1539,6 +1539,9 @@ TEST_CASE("string length", "[]" ) {

test::sprintf(buffer, "%.*s", 3, "123456");
REQUIRE(!strcmp(buffer, "123"));

test::sprintf(buffer, "%.*s", 3, NULL);
REQUIRE(!strcmp(buffer, "(null)"));
}

#ifdef TEST_WITH_NON_STANDARD_FORMAT_STRINGS
Expand Down Expand Up @@ -1571,6 +1574,9 @@ TEST_CASE("buffer length", "[]" ) {

test::snprintf(buffer, 2, "%s", "Hello");
REQUIRE(!strcmp(buffer, "H"));

test::snprintf(buffer, 2, "%s", NULL);
REQUIRE(!strcmp(buffer, "("));
}


Expand All @@ -1584,11 +1590,15 @@ TEST_CASE("ret value", "[]" ) {

ret = test::snprintf(buffer, 6, "0%s", "12345");
REQUIRE(!strcmp(buffer, "01234"));
REQUIRE(ret == 6); // '5' is truncated
REQUIRE(ret == 6); // "5" is truncated

ret = test::snprintf(buffer, 6, "0%s", "1234567");
REQUIRE(!strcmp(buffer, "01234"));
REQUIRE(ret == 8); // '567' are truncated
REQUIRE(ret == 8); // "567" are truncated

ret = test::snprintf(buffer, 6, "0%s", NULL);
REQUIRE(!strcmp(buffer, "0(nul"));
REQUIRE(ret == 7); // "l)" is truncated

ret = test::snprintf(buffer, 10, "hello, world");
REQUIRE(ret == 12);
Expand Down

1 comment on commit e4baa6a

@eyalroz
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you mis-referred to issue eyalroz#63 ; that one is about exposing the static functions.

Please sign in to comment.