From 4646bb5af526edcf8670933c539e910dca0bffb4 Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Sat, 28 Nov 2015 11:25:59 -0600 Subject: [PATCH 1/7] Added C .gitignore --- .gitignore | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebb6e01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ + +# Editor +*.swp From 68a8e599a8d31a9c33ce51ed222721350a25c3fb Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Thu, 26 Nov 2015 12:14:57 -0600 Subject: [PATCH 2/7] Moved newlines into TPRINTF macro for printf tests This makes the printf.c and sprintf.c files easier to diff. --- test/printf.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/test/printf.c b/test/printf.c index dd84a9b..915ea9a 100644 --- a/test/printf.c +++ b/test/printf.c @@ -13,8 +13,8 @@ #define UNUSED(x) (void)(x) #define TPRINTF(expr...) \ - ({ printf("libc_printf(%s) -> ", #expr); printf(expr); \ - printf(" tfp_printf(%s) -> ", #expr); tfp_printf(expr); }) + ({ printf("libc_printf(%s) -> ", #expr); printf(expr); printf("\n"); \ + printf(" tfp_printf(%s) -> ", #expr); tfp_printf(expr); printf("\n"); }) static void stdout_putf(void *unused, char c) { @@ -28,41 +28,41 @@ int main() printf("Fun with printf and %%!\n"); - TPRINTF("d1=%016llx d2=%016lx d3=%02x d4=%02X 42=%03d\n", + 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\n", 0xd1, 0xd2, 0xd3); - TPRINTF("|%-14s| |%-16s| d2=%2x |%-30s|\n", "str14", "str16", 0xd2, + TPRINTF("d1=%04x d2=%06x d3=%08x %%100", 0xd1, 0xd2, 0xd3); + TPRINTF("|%-14s| |%-16s| d2=%2x |%-30s|", "str14", "str16", 0xd2, "12345678901234567890123456789012345"); - TPRINTF("|%4s|\n", "string4"); - TPRINTF("|%-4s|\n", "string4"); - TPRINTF("42=%3d d1=%4.4x |%4s| d2=%8.8x\n", 42, 0xd1, "string4", 0xd2); - 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", + 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("%lld\n", LLONG_MIN); - TPRINTF("%lld\n", LLONG_MAX); - TPRINTF("%llu\n", ULLONG_MAX); - TPRINTF("%llx\n", LLONG_MIN); - TPRINTF("%llx\n", LLONG_MAX); - TPRINTF("%llx\n", ULLONG_MAX); + 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\n", 0xd1); - TPRINTF("d1=%4.1x\n", 0xd1); - TPRINTF("d1=%4.x\n", 0xd1); + TPRINTF("d1=%.1x", 0xd1); + TPRINTF("d1=%4.1x", 0xd1); + TPRINTF("d1=%4.x", 0xd1); { char blah[256]; - TPRINTF("a=%zd\n", sizeof(blah)); - TPRINTF("a=%zu\n", sizeof(blah)); - TPRINTF("a=%zi\n", sizeof(blah)); - TPRINTF("a=0x%zx\n", sizeof(blah)); + 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\n", main); - TPRINTF("Adddress of stack variable: %p\n", &in_stack); + TPRINTF("Adddress of main: %p", main); + TPRINTF("Adddress of stack variable: %p", &in_stack); } return 0; From afe2c78f785a883e832a8f55f5825bac3cf912dc Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Thu, 26 Nov 2015 12:19:37 -0600 Subject: [PATCH 3/7] Synchronized test cases in printf.c and sprintf.c One of the sprintf.c test cases is failing, which needs to be resolved. --- test/printf.c | 3 +++ test/sprintf.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/test/printf.c b/test/printf.c index 915ea9a..3485a32 100644 --- a/test/printf.c +++ b/test/printf.c @@ -50,6 +50,7 @@ int main() TPRINTF("d1=%.1x", 0xd1); TPRINTF("d1=%4.1x", 0xd1); TPRINTF("d1=%4.x", 0xd1); + TPRINTF("d1=%4.4x", 0xd1); { char blah[256]; @@ -59,6 +60,8 @@ int main() TPRINTF("a=0x%zx", sizeof(blah)); } + TPRINTF("Hello |%15s|.", "12345678901234"); + { int in_stack; TPRINTF("Adddress of main: %p", main); diff --git a/test/sprintf.c b/test/sprintf.c index 06a01ed..5227603 100644 --- a/test/sprintf.c +++ b/test/sprintf.c @@ -58,6 +58,9 @@ int main() 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); { From c9a7169a855e511375ea5b97e05fb993d1842540 Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Sat, 28 Nov 2015 01:46:42 -0600 Subject: [PATCH 4/7] Added precision support All features of precision should work, except that numbers get limited to 10 or 20 characters for precision (depending on if LONG is supported). This seemed like a reasonable complexity trade-off, since it would allow precision-based zero padding to the maximum character width of the largest integer type. --- test/sprintf.c | 34 ++++++++++++- tinyprintf.c | 131 +++++++++++++++++++++++++++++++------------------ tinyprintf.h | 9 ++-- 3 files changed, 121 insertions(+), 53 deletions(-) diff --git a/test/sprintf.c b/test/sprintf.c index 5227603..cb1d5c3 100644 --- a/test/sprintf.c +++ b/test/sprintf.c @@ -51,6 +51,27 @@ int main() TPRINTF("84=%d 21=%ds |%s| |%sOK| d1=%x d2=%#x", 84, 21, "hello", "fine", 0xd1, 0xd2); + TPRINTF("|%.6s|", "stringXXX"); + TPRINTF("|%10.6s|", "stringXXX"); + TPRINTF("|%-10.6s|", "stringXXX"); + TPRINTF("|%.20s|", "stringXXX"); + TPRINTF("|%.s|", "stringXXX"); + TPRINTF("|%.50s|", "0123456789012345678901234567890123456789"); + + TPRINTF("%d", INT_MIN); + TPRINTF("%d", INT_MAX); + TPRINTF("%u", UINT_MAX); + TPRINTF("%x", INT_MIN); + TPRINTF("%x", INT_MAX); + TPRINTF("%x", UINT_MAX); + + TPRINTF("%ld", LONG_MIN); + TPRINTF("%ld", LONG_MAX); + TPRINTF("%lu", ULONG_MAX); + TPRINTF("%lx", LONG_MIN); + TPRINTF("%lx", LONG_MAX); + TPRINTF("%lx", ULONG_MAX); + TPRINTF("%lld", LLONG_MIN); TPRINTF("%lld", LLONG_MAX); TPRINTF("%llu", ULLONG_MAX); @@ -58,10 +79,21 @@ int main() TPRINTF("%llx", LLONG_MAX); TPRINTF("%llx", ULLONG_MAX); + TPRINTF("d1=%.x", 0xd1); TPRINTF("d1=%.1x", 0xd1); - TPRINTF("d1=%4.1x", 0xd1); + TPRINTF("d1=%.2x", 0xd1); + TPRINTF("d1=%.4x", 0xd1); + TPRINTF("d1=%4.x", 0xd1); + TPRINTF("d1=%4.1x", 0xd1); + TPRINTF("d1=%4.2x", 0xd1); TPRINTF("d1=%4.4x", 0xd1); + TPRINTF("d1=%4.20x", 0xd1); + TPRINTF("d1=%8.4x", 0xd1); + + TPRINTF("0=|%.d|", 0); + TPRINTF("0=|%.4d|", 0); + TPRINTF("0=|%4.d|", 0); { char blah[256]; diff --git a/tinyprintf.c b/tinyprintf.c index bb22700..7c980eb 100644 --- a/tinyprintf.c +++ b/tinyprintf.c @@ -19,8 +19,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "tinyprintf.h" +#include +#include "tinyprintf.h" /* * Configuration @@ -38,10 +39,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /* * Configuration adjustments */ -#ifdef PRINTF_SIZE_T_SUPPORT -#include -#endif - #ifdef PRINTF_LONG_LONG_SUPPORT # define PRINTF_LONG_SUPPORT #endif @@ -66,6 +63,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # define _TFP_GCC_NO_INLINE_ #endif +#define IS_DIGIT(x) ((x) >= '0' && (x) <= '9') + +#ifdef PRINTF_LONG_SUPPORT +#define BF_MAX 20 /* long = 64b on some architectures */ +#else +#define BF_MAX 10 /* int = 32b on some architectures */ +#endif + /* * Implementation */ @@ -74,10 +79,13 @@ struct param { 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 prec_used:1; /**< precision specified */ unsigned int width; /**< field width */ + unsigned int prec; /**< precision */ char sign; /**< The sign to display (if any) */ unsigned int base; /**< number base (e.g.: 8, 10, 16) */ char *bf; /**< Buffer to output */ + size_t bf_len; /**< Buffer length */ }; @@ -85,21 +93,28 @@ struct param { static void _TFP_GCC_NO_INLINE_ ulli2a( unsigned long long int num, struct param *p) { - int n = 0; unsigned long long int d = 1; char *bf = p->bf; - while (num / d >= p->base) + int prec = p->prec; + if (p->prec_used && (prec == 0) && (num == 0)) + return; + if (prec > BF_MAX) + prec = BF_MAX; + /* at least one digit */ + prec--; + while (num / d >= p->base) { d *= p->base; + prec--; + } + while (prec-- > 0) + *bf++ = '0'; while (d != 0) { int dgt = num / d; num %= d; d /= p->base; - if (n || dgt > 0 || d == 0) { - *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); - ++n; - } + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); } - *bf = 0; + p->bf_len = bf - p->bf; } static void lli2a(long long int num, struct param *p) @@ -115,21 +130,28 @@ static void lli2a(long long int num, struct param *p) #ifdef PRINTF_LONG_SUPPORT static void uli2a(unsigned long int num, struct param *p) { - int n = 0; unsigned long int d = 1; char *bf = p->bf; - while (num / d >= p->base) + int prec = p->prec; + if (p->prec_used && (prec == 0) && (num == 0)) + return; + if (prec > BF_MAX) + prec = BF_MAX; + /* at least one digit */ + prec--; + while (num / d >= p->base) { d *= p->base; + prec--; + } + while (prec-- > 0) + *bf++ = '0'; while (d != 0) { int dgt = num / d; num %= d; d /= p->base; - if (n || dgt > 0 || d == 0) { - *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); - ++n; - } + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); } - *bf = 0; + p->bf_len = bf - p->bf; } static void li2a(long num, struct param *p) @@ -144,21 +166,28 @@ static void li2a(long num, struct param *p) static void ui2a(unsigned int num, struct param *p) { - int n = 0; unsigned int d = 1; char *bf = p->bf; - while (num / d >= p->base) + int prec = p->prec; + if (p->prec_used && (prec == 0) && (num == 0)) + return; + if (prec > BF_MAX) + prec = BF_MAX; + /* at least one digit */ + prec--; + while (num / d >= p->base) { d *= p->base; + prec--; + } + while (prec-- > 0) + *bf++ = '0'; while (d != 0) { int dgt = num / d; num %= d; d /= p->base; - if (n || dgt > 0 || d == 0) { - *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); - ++n; - } + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); } - *bf = 0; + p->bf_len = bf - p->bf; } static void i2a(int num, struct param *p) @@ -172,7 +201,7 @@ static void i2a(int num, struct param *p) static int a2d(char ch) { - if (ch >= '0' && ch <= '9') + if (IS_DIGIT(ch)) return ch - '0'; else if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; @@ -203,10 +232,10 @@ static void putchw(void *putp, putcf putf, struct param *p) char ch; int n = p->width; char *bf = p->bf; + size_t bf_len = p->bf_len; /* Number of filling characters */ - while (*bf++ && n > 0) - n--; + n -= p->bf_len; if (p->sign) n--; if (p->alt && p->base == 16) @@ -239,8 +268,7 @@ static void putchw(void *putp, putcf putf, struct param *p) } /* Put actual buffer */ - bf = p->bf; - while ((ch = *bf++)) + while ((bf_len-- > 0) && (ch = *bf++)) putf(putp, ch); /* Fill with space to align to the left, after string */ @@ -253,13 +281,8 @@ static void putchw(void *putp, putcf putf, struct param *p) void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) { struct param p; -#ifdef PRINTF_LONG_SUPPORT - char bf[23]; /* long = 64b on some architectures */ -#else - char bf[12]; /* int = 32b on some architectures */ -#endif + char bf[BF_MAX]; char ch; - p.bf = bf; while ((ch = *(fmt++))) { if (ch != '%') { @@ -271,9 +294,14 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) /* Init parameter struct */ p.lz = 0; p.alt = 0; - p.width = 0; + p.uc = 0; p.align_left = 0; + p.prec_used = 0; + p.width = 0; + p.prec = 0; p.sign = 0; + p.bf = bf; + p.bf_len = 0; /* Flags */ while ((ch = *(fmt++))) { @@ -294,19 +322,17 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) } /* Width */ - if (ch >= '0' && ch <= '9') { + if (IS_DIGIT(ch)) { ch = a2u(ch, &fmt, 10, &(p.width)); } - /* 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') */ + /* Precision */ if (ch == '.') { - p.lz = 1; /* zero-padding */ - /* ignore actual 0-fill size: */ - do { ch = *(fmt++); - } while ((ch >= '0') && (ch <= '9')); + p.prec_used = 1; + if (IS_DIGIT(ch)) { + ch = a2u(ch, &fmt, 10, &(p.prec)); + } } #ifdef PRINTF_SIZE_T_SUPPORT @@ -406,9 +432,20 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) putf(putp, (char)(va_arg(va, int))); break; case 's': + { + unsigned int prec = p.prec; + char *b; p.bf = va_arg(va, char *); + b = p.bf; + while (*b++) { + if (p.prec_used && (prec == 0)) { + break; + } + p.bf_len++; + prec--; + } putchw(putp, putf, &p); - p.bf = bf; + } break; case '%': putf(putp, ch); diff --git a/tinyprintf.h b/tinyprintf.h index a769f4a..d384067 100644 --- a/tinyprintf.h +++ b/tinyprintf.h @@ -34,7 +34,10 @@ functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf'). The formats supported by this implementation are: 'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'. -Zero padding and field width are also supported. +Zero padding, field width, and precision are also supported. When precision +is used for 'd', 'i', 'o', 'u', 'x', and 'X', the precision is limited to the +max number of digits used by the largest supported size (either 10 or 20 +characters). If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then the long specifier is also supported. Note that this will pull in some @@ -125,10 +128,6 @@ regs Kusti, 23.10.2004 /* Optional external types dependencies */ -#if TINYPRINTF_DEFINE_TFP_SPRINTF -# include /* size_t */ -#endif - /* Declarations */ #ifdef __GNUC__ From 82d24852b9ba070b94009dca206f795bafd2ba4a Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Sat, 28 Nov 2015 10:25:34 -0600 Subject: [PATCH 5/7] Added argument defined width/precision (using '*') This standard format string feature allows the width or precision to be provided as an additional argument. --- test/sprintf.c | 6 ++++++ tinyprintf.c | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/sprintf.c b/test/sprintf.c index cb1d5c3..c8a13c9 100644 --- a/test/sprintf.c +++ b/test/sprintf.c @@ -95,6 +95,12 @@ int main() TPRINTF("0=|%.4d|", 0); TPRINTF("0=|%4.d|", 0); + TPRINTF("42=|%*d|", 5, 42); + TPRINTF("42=|%-*d|", 5, 42); + TPRINTF("42=|%0*d|", 5, 42); + TPRINTF("42=|%.*d|", 5, 42); + TPRINTF("42=|%-*.*d|", 10, 5, 42); + { char blah[256]; TPRINTF("a=%zd", sizeof(blah)); diff --git a/tinyprintf.c b/tinyprintf.c index 7c980eb..9037d45 100644 --- a/tinyprintf.c +++ b/tinyprintf.c @@ -322,7 +322,10 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) } /* Width */ - if (IS_DIGIT(ch)) { + if (ch == '*') { + ch = *(fmt++); + p.width = va_arg(va, unsigned int); + } else if (IS_DIGIT(ch)) { ch = a2u(ch, &fmt, 10, &(p.width)); } @@ -330,7 +333,10 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) if (ch == '.') { ch = *(fmt++); p.prec_used = 1; - if (IS_DIGIT(ch)) { + if (ch == '*') { + ch = *(fmt++); + p.prec = va_arg(va, unsigned int); + } else if (IS_DIGIT(ch)) { ch = a2u(ch, &fmt, 10, &(p.prec)); } } From 6268c398d7b34fbb2d383e63899d0d771bc6b792 Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Sat, 28 Nov 2015 11:30:31 -0600 Subject: [PATCH 6/7] Simplified integer precision handling As a side effect, integer precision is no longer bounded by BF_MAX. Precision should now be fully compatible with the standard printf. --- test/sprintf.c | 5 +++-- tinyprintf.c | 53 +++++++++++++++++--------------------------------- tinyprintf.h | 5 +---- 3 files changed, 22 insertions(+), 41 deletions(-) diff --git a/test/sprintf.c b/test/sprintf.c index c8a13c9..d5d7792 100644 --- a/test/sprintf.c +++ b/test/sprintf.c @@ -88,8 +88,9 @@ int main() TPRINTF("d1=%4.1x", 0xd1); TPRINTF("d1=%4.2x", 0xd1); TPRINTF("d1=%4.4x", 0xd1); - TPRINTF("d1=%4.20x", 0xd1); - TPRINTF("d1=%8.4x", 0xd1); + TPRINTF("d1=%4.50x", 0xd1); + TPRINTF("d1=|%8.4x|", 0xd1); + TPRINTF("d1=|%-8.4x|", 0xd1); TPRINTF("0=|%.d|", 0); TPRINTF("0=|%.4d|", 0); diff --git a/tinyprintf.c b/tinyprintf.c index 9037d45..8d0ca04 100644 --- a/tinyprintf.c +++ b/tinyprintf.c @@ -95,19 +95,11 @@ static void _TFP_GCC_NO_INLINE_ ulli2a( { unsigned long long int d = 1; char *bf = p->bf; - int prec = p->prec; - if (p->prec_used && (prec == 0) && (num == 0)) + if (p->prec_used && (p->prec == 0) && (num == 0)) return; - if (prec > BF_MAX) - prec = BF_MAX; - /* at least one digit */ - prec--; while (num / d >= p->base) { d *= p->base; - prec--; } - while (prec-- > 0) - *bf++ = '0'; while (d != 0) { int dgt = num / d; num %= d; @@ -132,19 +124,11 @@ static void uli2a(unsigned long int num, struct param *p) { unsigned long int d = 1; char *bf = p->bf; - int prec = p->prec; - if (p->prec_used && (prec == 0) && (num == 0)) + if (p->prec_used && (p->prec == 0) && (num == 0)) return; - if (prec > BF_MAX) - prec = BF_MAX; - /* at least one digit */ - prec--; while (num / d >= p->base) { d *= p->base; - prec--; } - while (prec-- > 0) - *bf++ = '0'; while (d != 0) { int dgt = num / d; num %= d; @@ -168,19 +152,11 @@ static void ui2a(unsigned int num, struct param *p) { unsigned int d = 1; char *bf = p->bf; - int prec = p->prec; - if (p->prec_used && (prec == 0) && (num == 0)) + if (p->prec_used && (p->prec == 0) && (num == 0)) return; - if (prec > BF_MAX) - prec = BF_MAX; - /* at least one digit */ - prec--; while (num / d >= p->base) { d *= p->base; - prec--; } - while (prec-- > 0) - *bf++ = '0'; while (d != 0) { int dgt = num / d; num %= d; @@ -230,22 +206,26 @@ static char a2u(char ch, const char **src, int base, unsigned int *nump) static void putchw(void *putp, putcf putf, struct param *p) { char ch; - int n = p->width; + int width = p->width; + int prec = p->prec; char *bf = p->bf; size_t bf_len = p->bf_len; /* Number of filling characters */ - n -= p->bf_len; + width -= bf_len; + prec -= bf_len; if (p->sign) - n--; + width--; if (p->alt && p->base == 16) - n -= 2; + width -= 2; else if (p->alt && p->base == 8) - n--; + width--; + if (prec > 0) + width -= prec; /* Fill with space to align to the right, before alternate or sign */ if (!p->lz && !p->align_left) { - while (n-- > 0) + while (width-- > 0) putf(putp, ' '); } @@ -262,8 +242,10 @@ static void putchw(void *putp, putcf putf, struct param *p) } /* Fill with zeros, after alternate or sign */ + while (prec-- > 0) + putf(putp, '0'); if (p->lz) { - while (n-- > 0) + while (width-- > 0) putf(putp, '0'); } @@ -273,7 +255,7 @@ static void putchw(void *putp, putcf putf, struct param *p) /* Fill with space to align to the left, after string */ if (!p->lz && p->align_left) { - while (n-- > 0) + while (width-- > 0) putf(putp, ' '); } } @@ -450,6 +432,7 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) p.bf_len++; prec--; } + p.prec = 0; putchw(putp, putf, &p); } break; diff --git a/tinyprintf.h b/tinyprintf.h index d384067..7ecde57 100644 --- a/tinyprintf.h +++ b/tinyprintf.h @@ -34,10 +34,7 @@ functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf'). The formats supported by this implementation are: 'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'. -Zero padding, field width, and precision are also supported. When precision -is used for 'd', 'i', 'o', 'u', 'x', and 'X', the precision is limited to the -max number of digits used by the largest supported size (either 10 or 20 -characters). +Zero padding, field width, and precision are also supported. If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then the long specifier is also supported. Note that this will pull in some From cf56af99c848a7430ad29bba116eefb5ed11d53b Mon Sep 17 00:00:00 2001 From: Hans Anderson Date: Sat, 28 Nov 2015 11:37:27 -0600 Subject: [PATCH 7/7] Made precision and '*' specifiers conform to C99 specifications If zero padding is specified when either left-justify or precision are used, the zero padding should be ignored. The '*' specifiers can be integers, where negatives are handled in specific ways. The prec_used flag is no longer needed with these changes. --- test/sprintf.c | 6 ++++- tinyprintf.c | 61 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/test/sprintf.c b/test/sprintf.c index d5d7792..43800e5 100644 --- a/test/sprintf.c +++ b/test/sprintf.c @@ -95,12 +95,16 @@ int main() TPRINTF("0=|%.d|", 0); TPRINTF("0=|%.4d|", 0); TPRINTF("0=|%4.d|", 0); + TPRINTF("0=|%4.*d|", -1, 0); TPRINTF("42=|%*d|", 5, 42); TPRINTF("42=|%-*d|", 5, 42); TPRINTF("42=|%0*d|", 5, 42); TPRINTF("42=|%.*d|", 5, 42); - TPRINTF("42=|%-*.*d|", 10, 5, 42); + TPRINTF("42=|%*.*d|", 10, 5, 42); + TPRINTF("42=|%*.*d|", -10, 5, 42); + TPRINTF("42=|%*.*d|", 10, -5, 42); + TPRINTF("42=|%-*.*d|", -10, 5, 42); { char blah[256]; diff --git a/tinyprintf.c b/tinyprintf.c index 8d0ca04..5f77594 100644 --- a/tinyprintf.c +++ b/tinyprintf.c @@ -79,9 +79,8 @@ struct param { 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 prec_used:1; /**< precision specified */ - unsigned int width; /**< field width */ - unsigned int prec; /**< precision */ + int width; /**< field width */ + int prec; /**< precision */ char sign; /**< The sign to display (if any) */ unsigned int base; /**< number base (e.g.: 8, 10, 16) */ char *bf; /**< Buffer to output */ @@ -95,7 +94,7 @@ static void _TFP_GCC_NO_INLINE_ ulli2a( { unsigned long long int d = 1; char *bf = p->bf; - if (p->prec_used && (p->prec == 0) && (num == 0)) + if ((p->prec == 0) && (num == 0)) return; while (num / d >= p->base) { d *= p->base; @@ -124,7 +123,7 @@ static void uli2a(unsigned long int num, struct param *p) { unsigned long int d = 1; char *bf = p->bf; - if (p->prec_used && (p->prec == 0) && (num == 0)) + if ((p->prec == 0) && (num == 0)) return; while (num / d >= p->base) { d *= p->base; @@ -152,7 +151,7 @@ static void ui2a(unsigned int num, struct param *p) { unsigned int d = 1; char *bf = p->bf; - if (p->prec_used && (p->prec == 0) && (num == 0)) + if ((p->prec == 0) && (num == 0)) return; while (num / d >= p->base) { d *= p->base; @@ -278,9 +277,8 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) p.alt = 0; p.uc = 0; p.align_left = 0; - p.prec_used = 0; p.width = 0; - p.prec = 0; + p.prec = -1; p.sign = 0; p.bf = bf; p.bf_len = 0; @@ -303,25 +301,46 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) break; } + if (p.align_left) + p.lz = 0; + /* Width */ if (ch == '*') { ch = *(fmt++); - p.width = va_arg(va, unsigned int); + p.width = va_arg(va, int); + if (p.width < 0) { + p.align_left = 1; + p.width = -p.width; + } } else if (IS_DIGIT(ch)) { - ch = a2u(ch, &fmt, 10, &(p.width)); + unsigned int width; + ch = a2u(ch, &fmt, 10, &(width)); + p.width = width; } /* Precision */ if (ch == '.') { ch = *(fmt++); - p.prec_used = 1; if (ch == '*') { + int prec; ch = *(fmt++); - p.prec = va_arg(va, unsigned int); + prec = va_arg(va, int); + if (prec < 0) + /* act as if precision was omitted */ + p.prec = -1; + else + p.prec = prec; } else if (IS_DIGIT(ch)) { - ch = a2u(ch, &fmt, 10, &(p.prec)); + unsigned int prec; + ch = a2u(ch, &fmt, 10, &(prec)); + p.prec = prec; + } else { + p.prec = 0; } } + if (p.prec >= 0) + /* precision causes zero pad to be ignored */ + p.lz = 0; #ifdef PRINTF_SIZE_T_SUPPORT # ifdef PRINTF_LONG_SUPPORT @@ -354,6 +373,8 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) goto abort; case 'u': p.base = 10; + if (p.prec < 0) + p.prec = 1; #ifdef PRINTF_LONG_SUPPORT #ifdef PRINTF_LONG_LONG_SUPPORT if (2 == lng) @@ -370,6 +391,8 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) case 'd': case 'i': p.base = 10; + if (p.prec < 0) + p.prec = 1; #ifdef PRINTF_LONG_SUPPORT #ifdef PRINTF_LONG_LONG_SUPPORT if (2 == lng) @@ -398,6 +421,8 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) case 'X': p.base = 16; p.uc = (ch == 'X')?1:0; + if (p.prec < 0) + p.prec = 1; #ifdef PRINTF_LONG_SUPPORT #ifdef PRINTF_LONG_LONG_SUPPORT if (2 == lng) @@ -413,6 +438,8 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) break; case 'o': p.base = 8; + if (p.prec < 0) + p.prec = 1; ui2a(va_arg(va, unsigned int), &p); putchw(putp, putf, &p); break; @@ -425,14 +452,10 @@ void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) char *b; p.bf = va_arg(va, char *); b = p.bf; - while (*b++) { - if (p.prec_used && (prec == 0)) { - break; - } + while ((prec-- != 0) && *b++) { p.bf_len++; - prec--; } - p.prec = 0; + p.prec = -1; putchw(putp, putf, &p); } break;