From 7d06db201fb2eded0bd45a10a460756007eb0679 Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Thu, 19 Jul 2012 16:41:57 -0700 Subject: Enhance printf() 1. Add precision to limit string length. ccprintf("%.4s", "foobar") prints "foob" 2. Handle '*' for length, precision fields. ccprintf("%.*s", 3, "foobar") prints "foo" 3. Add hex-dump code "%h" ccprintf("%.*s", 4, "foobar") prints 666f6f62 BUG=none TEST=at ec console, 'hash' prints the current hash Change-Id: I568310f2727495b021081bf58df2a0bbb3c74e73 Signed-off-by: Randall Spangler Reviewed-on: https://gerrit.chromium.org/gerrit/28704 Reviewed-by: Bill Richardson --- common/printf.c | 110 +++++++++++++++++++++++++++++++++++++++++----------- common/vboot_hash.c | 20 +++------- include/printf.h | 64 ++++++++++++++++++++---------- 3 files changed, 138 insertions(+), 56 deletions(-) diff --git a/common/printf.c b/common/printf.c index d6b0298034..b356e0e58a 100644 --- a/common/printf.c +++ b/common/printf.c @@ -11,6 +11,13 @@ static const char error_str[] = "ERROR"; +#define MAX_FORMAT 1024 /* Maximum chars in a single format field */ + +static int hexdigit(int c) +{ + return c > 9 ? (c + 'a' - 10) : (c + '0'); +} + int vfnprintf(int (*addchar)(void *context, int c), void *context, const char *format, va_list args) { @@ -22,6 +29,7 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context, int is_left; int pad_zero; int pad_width; + int precision; char *vstr; int vlen; @@ -62,38 +70,72 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context, /* Count padding length */ pad_width = 0; - while (c >= '0' && c <= '9') { - pad_width = (10 * pad_width) + c - '0'; + if (c == '*') { + pad_width = va_arg(args, int); c = *format++; + } else { + while (c >= '0' && c <= '9') { + pad_width = (10 * pad_width) + c - '0'; + c = *format++; + } } - if (pad_width > 80) { - /* Sanity check for width failed */ + if (pad_width < 0 || pad_width > MAX_FORMAT) { + /* Sanity check for precision failed */ format = error_str; continue; } + /* Count precision */ + precision = 0; + if (c == '.') { + c = *format++; + if (c == '*') { + precision = va_arg(args, int); + c = *format++; + } else { + while (c >= '0' && c <= '9') { + precision = (10 * precision) + c - '0'; + c = *format++; + } + } + if (precision < 0 || precision > MAX_FORMAT) { + /* Sanity check for precision failed */ + format = error_str; + continue; + } + } + if (c == 's') { vstr = va_arg(args, char *); if (vstr == NULL) vstr = "(NULL)"; + } else if (c == 'h') { + /* Hex dump output */ + vstr = va_arg(args, char *); + + if (!precision) { + /* Hex dump requires precision */ + format = error_str; + continue; + } + + for ( ; precision; precision--, vstr++) { + dropped_chars |= + addchar(context, + hexdigit((*vstr >> 4) & 0x0f)); + dropped_chars |= + addchar(context, + hexdigit(*vstr & 0x0f)); + } + + continue; } else { uint64_t v; int is_negative = 0; int is_64bit = 0; int base = 10; - int fixed_point = 0; - - /* Handle fixed point numbers */ - if (c == '.') { - c = *format++; - if (c < '0' || c > '9') { - format = error_str; - continue; - } - fixed_point = c - '0'; - c = *format++; - } + /* Handle length */ if (c == 'l') { is_64bit = 1; c = *format++; @@ -103,7 +145,7 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context, if (c == 'T') { v = get_time().val; is_64bit = 1; - fixed_point = 6; + precision = 6; } else if (is_64bit) { v = va_arg(args, uint64_t); } else { @@ -148,11 +190,20 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context, vstr = intbuf + sizeof(intbuf) - 1; *(vstr) = '\0'; - /* Handle digits to right of decimal for fixed point - * numbers. */ - for (vlen = 0; vlen < fixed_point; vlen++) + /* + * Fixed-point precision must fit in our buffer. + * Leave space for "0." and the terminating null. + */ + if (precision > sizeof(intbuf) - 3) + precision = sizeof(intbuf) - 3; + + /* + * Handle digits to right of decimal for fixed point + * numbers. + */ + for (vlen = 0; vlen < precision; vlen++) *(--vstr) = '0' + uint64divmod(&v, 10); - if (fixed_point) + if (precision) *(--vstr) = '.'; if (!v) @@ -170,15 +221,30 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context, if (is_negative) *(--vstr) = '-'; + + /* + * Precision field was interpreted by fixed-point + * logic, so clear it. + */ + precision = 0; } /* Copy string (or stringified integer) */ vlen = strlen(vstr); + + /* No padding strings to wider than the precision */ + if (precision > 0 && pad_width > precision) + pad_width = precision; + + /* If precision is zero, print everything */ + if (!precision) + precision = MAX(vlen, pad_width); + while (vlen < pad_width && !is_left) { dropped_chars |= addchar(context, pad_zero ? '0' : ' '); vlen++; } - while (*vstr) + while (*vstr && --precision >= 0) dropped_chars |= addchar(context, *vstr++); while (vlen < pad_width && is_left) { dropped_chars |= addchar(context, ' '); diff --git a/common/vboot_hash.c b/common/vboot_hash.c index 6f944622da..3362917e0d 100644 --- a/common/vboot_hash.c +++ b/common/vboot_hash.c @@ -143,14 +143,9 @@ void vboot_hash_task(void) size); curr_pos += size; if (curr_pos >= data_size) { - int i; - hash = SHA256_final(&ctx); - - CPRINTF("[%T hash done "); - for (i = 0; i < SHA256_DIGEST_SIZE; i++) - CPRINTF("%02x", hash[i]); - CPUTS("]\n"); + CPRINTF("[%T hash done %.*h]\n", + SHA256_DIGEST_SIZE, hash); } /* @@ -196,14 +191,11 @@ static int command_hash(int argc, char **argv) ccprintf("Offset: 0x%08x\n", data_offset); ccprintf("Size: 0x%08x (%d)\n", data_size, data_size); ccprintf("Digest: "); - if (vboot_hash_in_progress()) { + if (vboot_hash_in_progress()) ccprintf("(in progress)\n"); - } else { - int i; - for (i = 0; i < SHA256_DIGEST_SIZE; i++) - ccprintf("%02x", hash[i]); - ccprintf("\n"); - } + else + ccprintf("%.*h\n", SHA256_DIGEST_SIZE, hash); + return EC_SUCCESS; } diff --git a/include/printf.h b/include/printf.h index 66e8d0c8b5..fdc80c2943 100644 --- a/include/printf.h +++ b/include/printf.h @@ -11,31 +11,55 @@ #include /* For va_list */ #include "common.h" -/* SUPPORTED FORMAT CODES: - * char (%c) - * string (%s) - * native int (signed/unsigned) (%d / %u / %x / %X) - * int32_t / uint32_t (%d / %x / %X) - * int64_t / uint64_t (%ld / %lu / %lx / %lX) - * pointer (%p) - * And the following special format codes: - * current time in sec (%T) - interpreted as "%.6T" for fixed-point format - * including padding (%-5s, %8d, %08x, %016lx) - * - * Floating point output (%f / %g) is not supported, but there is a fixed-point - * extension for integers; a padding option of .N (where N is a number) will - * put a decimal before that many digits. For example, printing 123 with - * format code %.6d will result in "0.000123". This is most useful for - * printing times, voltages, and currents. */ - - -/* Print formatted output to a function, like vfprintf() +/* + * Printf formatting: % [flags] [width] [.precision] [length] [type] + * + * Flags may be any/all of the following, and must occur in the following + * order if present: + * - '0' = prefixed with 0's instead of spaces (%08x) + * - '-' = left-justify instead of right-justify (%-5s) + * + * Width is the minimum output width, and may be: + * - A number ("0" - "255") + * - '*' = use next integer argument as width + * + * Precision must be preceded by a decimal point, and may be: + * - A number ("0" - "255") + * - '*' = use next integer argument as precision + * + * For integers, precision will put a decimal point before that many digits. + * So snprintf(buf, size, "%.6d", 123) sets buf="0.000123". This is most + * useful for printing times, voltages, and currents. + * + * Length may be: + * - 'l' = integer is 64-bit instead of native 32-bit + * + * Type may be: + * - 'c' - character + * - 's' - null-terminated ASCII string + * - 'h' - binary data, print as hex; precision is length of data in bytes. + * So "%.8h" prints 8 bytes of binary data + * - 'p' - pointer + * - 'd' - signed integer + * - 'u' - unsigned integer + * - 'x' - unsigned integer, print as lower-case hexadecimal + * - 'X' - unsigned integer, print as upper-case hexadecimal + * - 'b' - unsigned integer, print as binary + * + * Special format codes: + * - "%T" - current time in seconds - interpreted as "%.6T" for precision. + * This does NOT use up any arguments. + */ + +/** + * Print formatted output to a function, like vfprintf() * * addchar() will be called for every character to be printed, with the context * pointer passed to vfnprintf(). addchar() should return 0 if the character * was accepted or non-zero if the character was dropped due to overflow. * - * Returns error if output was truncated. */ + * Returns error if output was truncated. + */ int vfnprintf(int (*addchar)(void *context, int c), void *context, const char *format, va_list args); -- cgit v1.2.1