blob: 29c50df4d366c0c93ad2e549b652e3793811fccf [file] [log] [blame]
Ambroise Vincent4128f9f2019-02-11 13:34:41 +00001/*
Ambroise Vincent8a573de2019-02-11 13:54:30 +00002 * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
Ambroise Vincent4128f9f2019-02-11 13:34:41 +00003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <stdarg.h>
Ambroise Vincent8a573de2019-02-11 13:54:30 +00009#include <stdlib.h>
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000010
11#include <common/debug.h>
12#include <plat/common/platform.h>
13
Ambroise Vincent8a573de2019-02-11 13:54:30 +000014#define get_num_va_args(_args, _lcount) \
15 (((_lcount) > 1) ? va_arg(_args, long long int) : \
16 (((_lcount) == 1) ? va_arg(_args, long int) : \
17 va_arg(_args, int)))
18
19#define get_unum_va_args(_args, _lcount) \
20 (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \
21 (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \
22 va_arg(_args, unsigned int)))
23
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000024static void string_print(char **s, size_t n, size_t *chars_printed,
25 const char *str)
26{
27 while (*str != '\0') {
28 if (*chars_printed < n) {
29 *(*s) = *str;
30 (*s)++;
31 }
32
33 (*chars_printed)++;
34 str++;
35 }
36}
37
Ambroise Vincent8a573de2019-02-11 13:54:30 +000038static void unsigned_num_print(char **s, size_t n, size_t *count,
39 unsigned long long int unum, unsigned int radix,
40 char padc, int padn)
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000041{
Ambroise Vincent8a573de2019-02-11 13:54:30 +000042 /* Just need enough space to store 64 bit decimal integer */
43 char num_buf[20];
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000044 int i = 0;
Ambroise Vincent8a573de2019-02-11 13:54:30 +000045 int width;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000046 unsigned int rem;
47
48 do {
Ambroise Vincent8a573de2019-02-11 13:54:30 +000049 rem = unum % radix;
50 if (rem < 0xa)
51 num_buf[i] = '0' + rem;
52 else
53 num_buf[i] = 'a' + (rem - 0xa);
54 i++;
55 unum /= radix;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000056 } while (unum > 0U);
57
Ambroise Vincent8a573de2019-02-11 13:54:30 +000058 width = i;
59
60 if (padn > 0) {
61 while (width < padn) {
62 if (*count < n) {
63 *(*s) = padc;
64 (*s)++;
65 }
66 (*count)++;
67 padn--;
68 }
69 }
70
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000071 while (--i >= 0) {
Ambroise Vincent8a573de2019-02-11 13:54:30 +000072 if (*count < n) {
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000073 *(*s) = num_buf[i];
74 (*s)++;
75 }
Ambroise Vincent8a573de2019-02-11 13:54:30 +000076 (*count)++;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000077 }
Ambroise Vincent8a573de2019-02-11 13:54:30 +000078
79 if (padn < 0) {
80 while (width < -padn) {
81 if (*count < n) {
82 *(*s) = padc;
83 (*s)++;
84 }
85 (*count)++;
86 padn++;
87 }
88 }
89}
90
91/*
92 * Scaled down version of vsnprintf(3).
93 */
94int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
95{
96 int l_count;
97 int left;
98 char *str;
99 int num;
100 unsigned long long int unum;
101 char padc; /* Padding character */
102 int padn; /* Number of characters to pad */
103 size_t count = 0U;
104
105 if (n == 0U) {
106 /* There isn't space for anything. */
107 } else if (n == 1U) {
108 /* Buffer is too small to actually write anything else. */
109 *s = '\0';
110 n = 0U;
111 } else {
112 /* Reserve space for the terminator character. */
113 n--;
114 }
115
116 while (*fmt != '\0') {
117 l_count = 0;
118 left = 0;
119 padc = '\0';
120 padn = 0;
121
122 if (*fmt == '%') {
123 fmt++;
124 /* Check the format specifier. */
125loop:
126 switch (*fmt) {
127 case '1':
128 case '2':
129 case '3':
130 case '4':
131 case '5':
132 case '6':
133 case '7':
134 case '8':
135 case '9':
136 padc = ' ';
137 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
138 padn = (padn * 10) + (*fmt - '0');
139 if (left)
140 padn = -padn;
141 goto loop;
142 case '-':
143 left = 1;
144 fmt++;
145 goto loop;
146 case 'i':
147 case 'd':
148 num = get_num_va_args(args, l_count);
149
150 if (num < 0) {
151 if (count < n) {
152 *s = '-';
153 s++;
154 }
155 count++;
156
157 unum = (unsigned int)-num;
158 } else {
159 unum = (unsigned int)num;
160 }
161
162 unsigned_num_print(&s, n, &count, unum, 10,
163 padc, padn);
164 break;
165 case 'l':
166 l_count++;
167 fmt++;
168 goto loop;
169 case 's':
170 str = va_arg(args, char *);
171 string_print(&s, n, &count, str);
172 break;
173 case 'u':
174 unum = get_unum_va_args(args, l_count);
175 unsigned_num_print(&s, n, &count, unum, 10,
176 padc, padn);
177 break;
178 case 'x':
179 unum = get_unum_va_args(args, l_count);
180 unsigned_num_print(&s, n, &count, unum, 16,
181 padc, padn);
182 break;
183 case '0':
184 padc = '0';
185 padn = 0;
186 fmt++;
187
188 for (;;) {
189 char ch = *fmt;
190 if ((ch < '0') || (ch > '9')) {
191 goto loop;
192 }
193 padn = (padn * 10) + (ch - '0');
194 fmt++;
195 }
196 assert(0); /* Unreachable */
197 default:
198 /*
199 * Exit on any other format specifier and abort
200 * when in debug mode.
201 */
202 WARN("snprintf: specifier with ASCII code '%d' not supported.\n",
203 *fmt);
204 assert(0);
205 return -1;
206 }
207 fmt++;
208 continue;
209 }
210
211 if (count < n) {
212 *s = *fmt;
213 s++;
214 }
215
216 fmt++;
217 count++;
218 }
219
220 if (n > 0U)
221 *s = '\0';
222
223 return (int)count;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000224}
225
226/*******************************************************************
227 * Reduced snprintf to be used for Trusted firmware.
228 * The following type specifiers are supported:
229 *
230 * %d or %i - signed decimal format
231 * %s - string format
232 * %u - unsigned decimal format
233 *
234 * The function panics on all other formats specifiers.
235 *
236 * It returns the number of characters that would be written if the
237 * buffer was big enough. If it returns a value lower than n, the
238 * whole string has been written.
239 *******************************************************************/
240int snprintf(char *s, size_t n, const char *fmt, ...)
241{
242 va_list args;
Ambroise Vincent8a573de2019-02-11 13:54:30 +0000243 int chars_printed;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000244
245 va_start(args, fmt);
Ambroise Vincent8a573de2019-02-11 13:54:30 +0000246 chars_printed = vsnprintf(s, n, fmt, args);
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000247 va_end(args);
248
Ambroise Vincent8a573de2019-02-11 13:54:30 +0000249 return chars_printed;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000250}