Introduce `tf_snprintf`
authorAntonio Nino Diaz <antonio.ninodiaz@arm.com>
Wed, 17 May 2017 14:34:22 +0000 (15:34 +0100)
committerAntonio Nino Diaz <antonio.ninodiaz@arm.com>
Wed, 24 May 2017 09:47:05 +0000 (10:47 +0100)
This is a reduced version of `snprintf` that only supports formats '%d',
'%i' and '%u'. It can be used when the full `snprintf` is not needed in
order to save memory. If it finds an unknown format specifier, it
prints an error message and panics.

Change-Id: I2cb06fcdf74cda2c43caf73ae0762a91499fc04e
Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
Makefile
common/tf_snprintf.c [new file with mode: 0644]
include/common/debug.h

index ddf875616c42b2d4c11f94a597ad79e886b4d873..9c0ccc08e410102cac38ea0e8773f5977022c6e1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -150,6 +150,7 @@ include lib/stdlib/stdlib.mk
 
 BL_COMMON_SOURCES      +=      common/bl_common.c                      \
                                common/tf_printf.c                      \
+                               common/tf_snprintf.c                    \
                                common/${ARCH}/debug.S                  \
                                lib/${ARCH}/cache_helpers.S             \
                                lib/${ARCH}/misc_helpers.S              \
diff --git a/common/tf_snprintf.c b/common/tf_snprintf.c
new file mode 100644 (file)
index 0000000..a99ab7a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <platform.h>
+#include <stdarg.h>
+
+static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed,
+                              unsigned int unum)
+{
+       /* Enough for a 32-bit unsigned decimal integer (4294967295). */
+       unsigned char num_buf[10];
+       int i = 0, rem;
+
+       do {
+               rem = unum % 10;
+               num_buf[i++] = '0' + rem;
+       } while (unum /= 10);
+
+       while (--i >= 0) {
+               if (*chars_printed < n)
+                       *(*s)++ = num_buf[i];
+               (*chars_printed)++;
+       }
+}
+
+/*******************************************************************
+ * Reduced snprintf to be used for Trusted firmware.
+ * The following type specifiers are supported:
+ *
+ * %d or %i - signed decimal format
+ * %u - unsigned decimal format
+ *
+ * The function panics on all other formats specifiers.
+ *
+ * It returns the number of characters that would be written if the
+ * buffer was big enough. If it returns a value lower than n, the
+ * whole string has been written.
+ *******************************************************************/
+int tf_snprintf(char *s, size_t n, const char *fmt, ...)
+{
+       va_list args;
+       int num;
+       unsigned int unum;
+       size_t chars_printed = 0;
+
+       if (n == 1) {
+               /* Buffer is too small to actually write anything else. */
+               *s = '\0';
+               n = 0;
+       } else if (n >= 2) {
+               /* Reserve space for the terminator character. */
+               n--;
+       }
+
+       va_start(args, fmt);
+       while (*fmt) {
+
+               if (*fmt == '%') {
+                       fmt++;
+                       /* Check the format specifier. */
+                       switch (*fmt) {
+                       case 'i':
+                       case 'd':
+                               num = va_arg(args, int);
+
+                               if (num < 0) {
+                                       if (chars_printed < n)
+                                               *s++ = '-';
+                                       chars_printed++;
+
+                                       unum = (unsigned int)-num;
+                               } else {
+                                       unum = (unsigned int)num;
+                               }
+
+                               unsigned_dec_print(&s, n, &chars_printed, unum);
+                               break;
+                       case 'u':
+                               unum = va_arg(args, unsigned int);
+                               unsigned_dec_print(&s, n, &chars_printed, unum);
+                               break;
+                       default:
+                               /* Panic on any other format specifier. */
+                               ERROR("tf_snprintf: specifier with ASCII code '%d' not supported.",
+                                     *fmt);
+                               plat_panic_handler();
+                       }
+                       fmt++;
+                       continue;
+               }
+
+               if (chars_printed < n)
+                       *s++ = *fmt;
+               fmt++;
+               chars_printed++;
+       }
+
+       va_end(args);
+
+       if (n > 0)
+               *s = '\0';
+
+       return chars_printed;
+}
index 646dabafb84ab374cef0cee5699fe2f6deb260c8..814cf8402a19f0019ffa0adf51d1fe35890ef1ad 100644 (file)
@@ -64,6 +64,7 @@ void __dead2 do_panic(void);
 void __dead2 __stack_chk_fail(void);
 
 void tf_printf(const char *fmt, ...) __printflike(1, 2);
+int tf_snprintf(char *s, size_t n, const char *fmt, ...) __printflike(3, 4);
 
 #endif /* __ASSEMBLY__ */
 #endif /* __DEBUG_H__ */