Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[*.c]
indent_style = space
indent_size = 4
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.0)
project(tinyprintf C)

macro(make_test t src PRINTF SPRINTF)
add_executable(${t} ${src} tinyprintf.c)
target_include_directories(${t} PUBLIC .)
target_compile_definitions(${t} PRIVATE
TINYPRINTF_OVERRIDE_LIBC=0
TINYPRINTF_DEFINE_TFP_PRINTF=${PRINTF}
TINYPRINTF_DEFINE_TFP_SPRINTF=${SPRINTF})
endmacro()


make_test(test-printf test/printf.c 1 0)
make_test(test-sprintf test/sprintf.c 0 1)
make_test(test-all test/all.c 1 1)
106 changes: 106 additions & 0 deletions test/all.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <stdio.h>
#include <string.h>
#include <limits.h>

#include "tinyprintf.h"

char expected_buffer[1000];
char actual_buffer[1000];

int passes = 0, failures = 0;

void check(const char *expr, int expected_sz, int actual_sz)
{
int size_pass = expected_sz == actual_sz;
int content_pass = strcmp(expected_buffer, actual_buffer) == 0;
int pass = size_pass && content_pass;

if (pass) {
passes++;
printf("PASS printf(%s)\n", expr);
printf("Output %s", actual_buffer);
} else {
failures++;
printf("FAIL printf(%s)\n", expr);

if (!content_pass) {
printf("libc %s\n", expected_buffer);
printf("tinyprintf %s\n", actual_buffer);
}
}
printf("\n");
}

#define TPRINTF(expr...) do { \
int expected_sz = snprintf(expected_buffer, sizeof(expected_buffer), expr);\
int actual_sz = tfp_snprintf(actual_buffer, sizeof(actual_buffer), expr);\
check(#expr, expected_sz, actual_sz);\
} while(0)

void main()
{
TPRINTF("d1=%016llx d2=%016lx d3=%02x d4=%02X 42=%03d",
(long long unsigned) 0xd1, (long unsigned) 0xd2, 0xd3, 0xd4, 42);
TPRINTF("d1=%04x d2=%06x d3=%08x %%100", 0xd1, 0xd2, 0xd3);
TPRINTF("|%-14s| |%-16s| d2=%2x |%-30s|", "str14", "str16", 0xd2,
"12345678901234567890123456789012345");
TPRINTF("|%4s|", "string4");
TPRINTF("|%-4s|", "string4");
TPRINTF("42=%3d d1=%4.4x |%4s| d2=%8.8x", 42, 0xd1, "string4", 0xd2);
TPRINTF("42=%3d d1=%4.4x |%-4s| d2=%8.8x", 42, 0xd1, "string4", 0xd2);
TPRINTF("84=%d 21=%ds |%s| |%sOK| d1=%x d2=%#x",
84, 21, "hello", "fine", 0xd1, 0xd2);
TPRINTF("42=% 3d d1=%4x |%10s| d2=%3.3x", 42, 0xd1, "string4", 0xd2);

TPRINTF("%lld", LLONG_MIN);
TPRINTF("%lld", LLONG_MAX);
TPRINTF("%llu", ULLONG_MAX);
TPRINTF("%llx", LLONG_MIN);
TPRINTF("%llx", LLONG_MAX);
TPRINTF("%llx", ULLONG_MAX);

TPRINTF("d1=%.1x", 0xd1);
TPRINTF("d1=%4.1x", 0xd1);
TPRINTF("d1=%4.x", 0xd1);
TPRINTF("d1=%4.4x", 0xd1);
TPRINTF("d1=%04x", 0xd1);

{
char blah[256];
TPRINTF("a=%zd", sizeof(blah));
TPRINTF("a=%zu", sizeof(blah));
TPRINTF("a=%zi", sizeof(blah));
TPRINTF("a=0x%zx", sizeof(blah));
}

{
int in_stack;
TPRINTF("Adddress of main: %p", main);
TPRINTF("Adddress of stack variable: %p", &in_stack);
}

{
char buf[] = "0123456789";
TPRINTF("%*s", 5, &buf[5]); /* minimum length, too long string */
TPRINTF("%.*s", 5, buf); /* maximum length, too long string */
TPRINTF("%.*s", 5, &buf[8]); /* maximum length, too short string */
TPRINTF("%*.*s", 5, 5, &buf[8]); /* minimum and maximum, too short string*/
TPRINTF("%*.*s", 5, 1, &buf[8]);
TPRINTF("%*.*s", 5, 5, buf);

TPRINTF("%-*d", 5, 123);
TPRINTF("%*d", 5, 123);
}

printf("Sizeof\n");
printf(" char %zd\n", sizeof(char));
printf(" int %zd\n", sizeof(int));
printf(" long %zd\n", sizeof(long));
printf(" long long %zd\n", sizeof(long long));
printf(" void* %zd\n", sizeof(void *));
printf("\n");

printf("Summary\n");
printf(" passes: %d\n", passes);
printf(" failures: %d\n", failures);
}
1 change: 1 addition & 0 deletions test/printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ int main()
TPRINTF("42=%3d d1=%4.4x |%-4s| d2=%8.8x\n", 42, 0xd1, "string4", 0xd2);
TPRINTF("84=%d 21=%ds |%s| |%sOK| d1=%x d2=%#x\n",
84, 21, "hello", "fine", 0xd1, 0xd2);
TPRINTF("42=% 3d d1=%4x |%10s| d2=%3.3x\n", 42, 0xd1, "string4", 0xd2);

TPRINTF("%lld\n", LLONG_MIN);
TPRINTF("%lld\n", LLONG_MAX);
Expand Down
81 changes: 56 additions & 25 deletions tinyprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <sys/types.h>
#endif

#include <limits.h>

#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
Expand Down Expand Up @@ -70,14 +72,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* Implementation
*/
struct param {
char lz:1; /**< Leading zeros */
char alt:1; /**< alternate form */
char uc:1; /**< Upper case (for base16 only) */
char align_left:1; /**< 0 == align right (default), 1 == align left */
unsigned int width; /**< field width */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
char l:1; /**< Add leading character */
char alt:1; /**< alternate form */
char uc:1; /**< Upper case (for base16 only) */
char align_left:1; /**< 0 == align right (default), 1 == align left */
char lchr; /**< Leading character */
unsigned int width; /**< field width */
int precision; /**< field precision */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
};


Expand Down Expand Up @@ -198,15 +202,15 @@ static char a2u(char ch, const char **src, int base, unsigned int *nump)
return ch;
}

static void putchw(void *putp, putcf putf, struct param *p)
static void putchw(void *putp, putcf putf, const struct param *p)
{
char ch;
int n = p->width;
char *bf = p->bf;
int precision = p->precision;

/* Number of filling characters */
while (*bf++ && n > 0)
n--;
while (precision-- > 0 && *bf++ && n-- > 0);
if (p->sign)
n--;
if (p->alt && p->base == 16)
Expand All @@ -215,7 +219,7 @@ static void putchw(void *putp, putcf putf, struct param *p)
n--;

/* Fill with space to align to the right, before alternate or sign */
if (!p->lz && !p->align_left) {
if (!p->l && !p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
Expand All @@ -233,18 +237,19 @@ static void putchw(void *putp, putcf putf, struct param *p)
}

/* Fill with zeros, after alternate or sign */
if (p->lz) {
if (p->l) {
while (n-- > 0)
putf(putp, '0');
putf(putp, p->lchr);
}

/* Put actual buffer */
bf = p->bf;
while ((ch = *bf++))
precision = p->precision;
while (precision-- > 0 && (ch = *bf++))
putf(putp, ch);

/* Fill with space to align to the left, after string */
if (!p->lz && p->align_left) {
if (!p->l && p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
Expand All @@ -259,6 +264,7 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
char bf[12]; /* int = 32b on some architectures */
#endif
char ch;
int w;
p.bf = bf;

while ((ch = *(fmt++))) {
Expand All @@ -269,9 +275,11 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
char lng = 0; /* 1 for long, 2 for long long */
#endif
/* Init parameter struct */
p.lz = 0;
p.l = 0;
p.lchr = ' ';
p.alt = 0;
p.width = 0;
p.precision = INT_MAX;
p.align_left = 0;
p.sign = 0;

Expand All @@ -282,11 +290,22 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
p.align_left = 1;
continue;
case '0':
p.lz = 1;
case ' ':
p.l = 1;
p.lchr = ch;
continue;
case '#':
p.alt = 1;
continue;
case '*':
w = va_arg(va, int);
if (w < 0) {
p.align_left = 1;
p.width = (unsigned int) -w;
} else {
p.width = (unsigned int) w;
}
continue;
default:
break;
}
Expand All @@ -300,13 +319,25 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)

/* We accept 'x.y' format but don't support it completely:
* we ignore the 'y' digit => this ignores 0-fill
* size and makes it == width (ie. 'x') */
* size and makes it == width (ie. 'x'), but use
* its value to set fill character to '0' if greater than 1.
*/
if (ch == '.') {
p.lz = 1; /* zero-padding */
/* ignore actual 0-fill size: */
do {
ch = *(fmt++);
} while ((ch >= '0') && (ch <= '9'));
p.l = 1;
ch = *(fmt++);
if (ch >= '0' && ch <= '9') {
unsigned int num;
ch = a2u(ch, &fmt, 10, &num);
if (num > 1) {
p.lchr = '0';
}
} else if (ch == '*') {
ch = *(fmt++);
int precision = va_arg(va, int);
if (precision >= 0) {
p.precision = precision;
}
}
}

#ifdef PRINTF_SIZE_T_SUPPORT
Expand Down Expand Up @@ -430,7 +461,7 @@ void init_printf(void *putp, putcf putf)
stdout_putp = putp;
}

void tfp_printf(char *fmt, ...)
void tfp_printf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
Expand Down
2 changes: 1 addition & 1 deletion tinyprintf.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ int tfp_sprintf(char *str, const char *fmt, ...) \

#if TINYPRINTF_DEFINE_TFP_PRINTF
void init_printf(void *putp, putcf putf);
void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
void tfp_printf(const char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
# if TINYPRINTF_OVERRIDE_LIBC
# define printf tfp_printf
# endif
Expand Down