libc: Import from TF-A

Based on arm-trusted-firware commit 873e394b3bf93214a441f9f98237b58fbbea55aa

Change-Id: I510e092f2b9ff333e9461bdde8d80ed1fab1460c
Signed-off-by: Ambroise Vincent <ambroise.vincent@arm.com>
diff --git a/lib/libc/snprintf.c b/lib/libc/snprintf.c
new file mode 100644
index 0000000..38ad1c7
--- /dev/null
+++ b/lib/libc/snprintf.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include <common/debug.h>
+#include <plat/common/platform.h>
+
+static void string_print(char **s, size_t n, size_t *chars_printed,
+			 const char *str)
+{
+	while (*str != '\0') {
+		if (*chars_printed < n) {
+			*(*s) = *str;
+			(*s)++;
+		}
+
+		(*chars_printed)++;
+		str++;
+	}
+}
+
+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). */
+	char num_buf[10];
+	int i = 0;
+	unsigned int rem;
+
+	do {
+		rem = unum % 10U;
+		num_buf[i++] = '0' + rem;
+		unum /= 10U;
+	} while (unum > 0U);
+
+	while (--i >= 0) {
+		if (*chars_printed < n) {
+			*(*s) = num_buf[i];
+			(*s)++;
+		}
+
+		(*chars_printed)++;
+	}
+}
+
+/*******************************************************************
+ * Reduced snprintf to be used for Trusted firmware.
+ * The following type specifiers are supported:
+ *
+ * %d or %i - signed decimal format
+ * %s - string 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 snprintf(char *s, size_t n, const char *fmt, ...)
+{
+	va_list args;
+	int num;
+	unsigned int unum;
+	char *str;
+	size_t chars_printed = 0U;
+
+	if (n == 0U) {
+		/* There isn't space for anything. */
+	} else if (n == 1U) {
+		/* Buffer is too small to actually write anything else. */
+		*s = '\0';
+		n = 0U;
+	} else {
+		/* Reserve space for the terminator character. */
+		n--;
+	}
+
+	va_start(args, fmt);
+	while (*fmt != '\0') {
+
+		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 = '-';
+						s++;
+					}
+					chars_printed++;
+
+					unum = (unsigned int)-num;
+				} else {
+					unum = (unsigned int)num;
+				}
+
+				unsigned_dec_print(&s, n, &chars_printed, unum);
+				break;
+			case 's':
+				str = va_arg(args, char *);
+				string_print(&s, n, &chars_printed, str);
+				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("snprintf: specifier with ASCII code '%d' not supported.",
+				      *fmt);
+				plat_panic_handler();
+				assert(0); /* Unreachable */
+			}
+			fmt++;
+			continue;
+		}
+
+		if (chars_printed < n) {
+			*s = *fmt;
+			s++;
+		}
+
+		fmt++;
+		chars_printed++;
+	}
+
+	va_end(args);
+
+	if (n > 0U)
+		*s = '\0';
+
+	return (int)chars_printed;
+}