diff options
Diffstat (limited to 'lib/libc/snprintf.c')
-rw-r--r-- | lib/libc/snprintf.c | 119 |
1 files changed, 103 insertions, 16 deletions
diff --git a/lib/libc/snprintf.c b/lib/libc/snprintf.c index 38ad1c71a..268632722 100644 --- a/lib/libc/snprintf.c +++ b/lib/libc/snprintf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,27 +24,55 @@ static void string_print(char **s, size_t n, size_t *chars_printed, } } -static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed, - unsigned int unum) +static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, + unsigned long long int unum, + unsigned int radix, char padc, int padn, + bool capitalise) { - /* Enough for a 32-bit unsigned decimal integer (4294967295). */ - char num_buf[10]; + /* Just need enough space to store 64 bit decimal integer */ + char num_buf[20]; int i = 0; + int width; unsigned int rem; + char ascii_a = capitalise ? 'A' : 'a'; do { - rem = unum % 10U; - num_buf[i++] = '0' + rem; - unum /= 10U; + rem = unum % radix; + if (rem < 10U) { + num_buf[i] = '0' + rem; + } else { + num_buf[i] = ascii_a + (rem - 10U); + } + i++; + unum /= radix; } while (unum > 0U); - while (--i >= 0) { - if (*chars_printed < n) { - *(*s) = num_buf[i]; - (*s)++; + width = i; + if (padn > width) { + (*chars_printed) += (size_t)padn; + } else { + (*chars_printed) += (size_t)width; + } + + if (*chars_printed < n) { + + if (padn > 0) { + while (width < padn) { + *(*s)++ = padc; + padn--; + } } - (*chars_printed)++; + while (--i >= 0) { + *(*s)++ = num_buf[i]; + } + + if (padn < 0) { + while (width < -padn) { + *(*s)++ = padc; + padn++; + } + } } } @@ -52,9 +80,16 @@ static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed, * Reduced snprintf to be used for Trusted firmware. * The following type specifiers are supported: * + * %x (or %X) - hexadecimal format * %d or %i - signed decimal format * %s - string format * %u - unsigned decimal format + * %p - pointer format + * + * The following padding specifiers are supported by this print + * %0NN - Left-pad the number with 0s (NN is a decimal number) + * %NN - Left-pad the number or string with spaces (NN is a decimal number) + * %-NN - Right-pad the number or string with spaces (NN is a decimal number) * * The function panics on all other formats specifiers. * @@ -66,8 +101,12 @@ int snprintf(char *s, size_t n, const char *fmt, ...) { va_list args; int num; - unsigned int unum; + unsigned long long int unum; char *str; + char padc; /* Padding character */ + int padn; /* Number of characters to pad */ + bool left; + bool capitalise; size_t chars_printed = 0U; if (n == 0U) { @@ -83,11 +122,39 @@ int snprintf(char *s, size_t n, const char *fmt, ...) va_start(args, fmt); while (*fmt != '\0') { + left = false; + padc ='\0'; + padn = 0; + capitalise = false; if (*fmt == '%') { fmt++; /* Check the format specifier. */ +loop: switch (*fmt) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + padc = (*fmt == '0') ? '0' : ' '; + for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { + padn = (padn * 10) + (*fmt - '0'); + } + if (left) { + padn = -padn; + } + goto loop; + case '-': + left = true; + fmt++; + goto loop; + case 'i': case 'd': num = va_arg(args, int); @@ -104,7 +171,8 @@ int snprintf(char *s, size_t n, const char *fmt, ...) unum = (unsigned int)num; } - unsigned_dec_print(&s, n, &chars_printed, unum); + unsigned_num_print(&s, n, &chars_printed, + unum, 10, padc, padn, false); break; case 's': str = va_arg(args, char *); @@ -112,8 +180,27 @@ int snprintf(char *s, size_t n, const char *fmt, ...) break; case 'u': unum = va_arg(args, unsigned int); - unsigned_dec_print(&s, n, &chars_printed, unum); + unsigned_num_print(&s, n, &chars_printed, + unum, 10, padc, padn, false); break; + case 'p': + unum = (uintptr_t)va_arg(args, void *); + if (unum > 0U) { + string_print(&s, n, &chars_printed, "0x"); + padn -= 2; + } + unsigned_num_print(&s, n, &chars_printed, + unum, 16, padc, padn, false); + break; + case 'X': + capitalise = true; + case 'x': + unum = va_arg(args, unsigned int); + unsigned_num_print(&s, n, &chars_printed, + unum, 16, padc, padn, + capitalise); + break; + default: /* Panic on any other format specifier. */ ERROR("snprintf: specifier with ASCII code '%d' not supported.", |