blob: 38ad1c71abb3fd5aa124d74612e1f4435c7beafe [file] [log] [blame]
Ambroise Vincent4128f9f2019-02-11 13:34:41 +00001/*
2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <stdarg.h>
9
10#include <common/debug.h>
11#include <plat/common/platform.h>
12
13static void string_print(char **s, size_t n, size_t *chars_printed,
14 const char *str)
15{
16 while (*str != '\0') {
17 if (*chars_printed < n) {
18 *(*s) = *str;
19 (*s)++;
20 }
21
22 (*chars_printed)++;
23 str++;
24 }
25}
26
27static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed,
28 unsigned int unum)
29{
30 /* Enough for a 32-bit unsigned decimal integer (4294967295). */
31 char num_buf[10];
32 int i = 0;
33 unsigned int rem;
34
35 do {
36 rem = unum % 10U;
37 num_buf[i++] = '0' + rem;
38 unum /= 10U;
39 } while (unum > 0U);
40
41 while (--i >= 0) {
42 if (*chars_printed < n) {
43 *(*s) = num_buf[i];
44 (*s)++;
45 }
46
47 (*chars_printed)++;
48 }
49}
50
51/*******************************************************************
52 * Reduced snprintf to be used for Trusted firmware.
53 * The following type specifiers are supported:
54 *
55 * %d or %i - signed decimal format
56 * %s - string format
57 * %u - unsigned decimal format
58 *
59 * The function panics on all other formats specifiers.
60 *
61 * It returns the number of characters that would be written if the
62 * buffer was big enough. If it returns a value lower than n, the
63 * whole string has been written.
64 *******************************************************************/
65int snprintf(char *s, size_t n, const char *fmt, ...)
66{
67 va_list args;
68 int num;
69 unsigned int unum;
70 char *str;
71 size_t chars_printed = 0U;
72
73 if (n == 0U) {
74 /* There isn't space for anything. */
75 } else if (n == 1U) {
76 /* Buffer is too small to actually write anything else. */
77 *s = '\0';
78 n = 0U;
79 } else {
80 /* Reserve space for the terminator character. */
81 n--;
82 }
83
84 va_start(args, fmt);
85 while (*fmt != '\0') {
86
87 if (*fmt == '%') {
88 fmt++;
89 /* Check the format specifier. */
90 switch (*fmt) {
91 case 'i':
92 case 'd':
93 num = va_arg(args, int);
94
95 if (num < 0) {
96 if (chars_printed < n) {
97 *s = '-';
98 s++;
99 }
100 chars_printed++;
101
102 unum = (unsigned int)-num;
103 } else {
104 unum = (unsigned int)num;
105 }
106
107 unsigned_dec_print(&s, n, &chars_printed, unum);
108 break;
109 case 's':
110 str = va_arg(args, char *);
111 string_print(&s, n, &chars_printed, str);
112 break;
113 case 'u':
114 unum = va_arg(args, unsigned int);
115 unsigned_dec_print(&s, n, &chars_printed, unum);
116 break;
117 default:
118 /* Panic on any other format specifier. */
119 ERROR("snprintf: specifier with ASCII code '%d' not supported.",
120 *fmt);
121 plat_panic_handler();
122 assert(0); /* Unreachable */
123 }
124 fmt++;
125 continue;
126 }
127
128 if (chars_printed < n) {
129 *s = *fmt;
130 s++;
131 }
132
133 fmt++;
134 chars_printed++;
135 }
136
137 va_end(args);
138
139 if (n > 0U)
140 *s = '\0';
141
142 return (int)chars_printed;
143}