blob: 12f51c07fc9db2ef44b42a2e3feee212b68eadc5 [file] [log] [blame]
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +01001/*
Yann Gautiereee19592022-02-14 10:29:32 +01002 * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
Daniel Boulby8942a1b2018-06-22 14:16:03 +01007#include <assert.h>
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +01008#include <stdarg.h>
Andre Przywara94faff82022-01-27 17:47:55 +00009#include <stdint.h>
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010010
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000011#include <common/debug.h>
12#include <plat/common/platform.h>
13
kadabi76c3f5b2022-02-28 14:21:09 -080014#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
Heyi Guo58bf3bf2021-01-20 13:55:25 +080024#define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \
25 do { \
26 if ((chars_printed) < (size)) { \
27 *(buf) = (ch); \
28 (buf)++; \
29 } \
30 (chars_printed)++; \
31 } while (false)
32
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010033static void string_print(char **s, size_t n, size_t *chars_printed,
34 const char *str)
35{
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010036 while (*str != '\0') {
Heyi Guo58bf3bf2021-01-20 13:55:25 +080037 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010038 str++;
39 }
40}
41
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010042static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
43 unsigned long long int unum,
44 unsigned int radix, char padc, int padn,
45 bool capitalise)
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010046{
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010047 /* Just need enough space to store 64 bit decimal integer */
48 char num_buf[20];
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010049 int i = 0;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010050 int width;
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010051 unsigned int rem;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010052 char ascii_a = capitalise ? 'A' : 'a';
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010053
Andre Przywara933cc5d2022-01-24 18:16:10 +000054 if (radix < 10) {
Yann Gautiereee19592022-02-14 10:29:32 +010055 ERROR("snprintf: unsupported radix '%u'.", radix);
Andre Przywara933cc5d2022-01-24 18:16:10 +000056 plat_panic_handler();
57 assert(0); /* Unreachable */
58 }
59
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010060 do {
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010061 rem = unum % radix;
62 if (rem < 10U) {
63 num_buf[i] = '0' + rem;
64 } else {
65 num_buf[i] = ascii_a + (rem - 10U);
66 }
67 i++;
68 unum /= radix;
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010069 } while (unum > 0U);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010070
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010071 width = i;
Andre Przywara3d959092021-12-21 12:35:54 +000072 for (i = padn - width; i > 0; i--) {
73 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
74 }
75 for (i = width; i > 0; i--) {
76 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010077 }
Andre Przywara3d959092021-12-21 12:35:54 +000078 for (i = width + padn; i < 0; i++) {
79 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010080 }
81}
82
83/*******************************************************************
Madhukar Pappireddy38629702020-09-08 19:00:00 -050084 * Reduced vsnprintf to be used for Trusted firmware.
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010085 * The following type specifiers are supported:
86 *
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010087 * %x (or %X) - hexadecimal format
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010088 * %d or %i - signed decimal format
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010089 * %s - string format
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010090 * %u - unsigned decimal format
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010091 * %p - pointer format
92 *
kadabi76c3f5b2022-02-28 14:21:09 -080093 * The following length specifiers are supported by this print
94 * %l - long int
95 * %ll - long long int
96 * %z - size_t sized integer formats
97 *
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010098 * The following padding specifiers are supported by this print
99 * %0NN - Left-pad the number with 0s (NN is a decimal number)
100 * %NN - Left-pad the number or string with spaces (NN is a decimal number)
101 * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100102 *
103 * The function panics on all other formats specifiers.
104 *
105 * It returns the number of characters that would be written if the
106 * buffer was big enough. If it returns a value lower than n, the
107 * whole string has been written.
108 *******************************************************************/
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500109int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100110{
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100111 int num;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100112 unsigned long long int unum;
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +0100113 char *str;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100114 char padc; /* Padding character */
115 int padn; /* Number of characters to pad */
116 bool left;
117 bool capitalise;
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100118 size_t chars_printed = 0U;
kadabi76c3f5b2022-02-28 14:21:09 -0800119 unsigned int l_count;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100120
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100121 if (n == 0U) {
122 /* There isn't space for anything. */
123 } else if (n == 1U) {
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100124 /* Buffer is too small to actually write anything else. */
125 *s = '\0';
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100126 n = 0U;
127 } else {
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100128 /* Reserve space for the terminator character. */
129 n--;
130 }
131
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100132 while (*fmt != '\0') {
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100133 left = false;
134 padc ='\0';
135 padn = 0;
136 capitalise = false;
kadabi76c3f5b2022-02-28 14:21:09 -0800137 l_count = 0;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100138
139 if (*fmt == '%') {
140 fmt++;
141 /* Check the format specifier. */
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100142loop:
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100143 switch (*fmt) {
Heyi Guo11a16c82020-10-27 08:36:40 +0800144 case '%':
Heyi Guo58bf3bf2021-01-20 13:55:25 +0800145 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
Heyi Guo11a16c82020-10-27 08:36:40 +0800146 break;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100147 case '0':
148 case '1':
149 case '2':
150 case '3':
151 case '4':
152 case '5':
153 case '6':
154 case '7':
155 case '8':
156 case '9':
157 padc = (*fmt == '0') ? '0' : ' ';
158 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
159 padn = (padn * 10) + (*fmt - '0');
160 }
161 if (left) {
162 padn = -padn;
163 }
164 goto loop;
165 case '-':
166 left = true;
167 fmt++;
168 goto loop;
169
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100170 case 'i':
171 case 'd':
kadabi76c3f5b2022-02-28 14:21:09 -0800172 num = get_num_va_args(args, l_count);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100173
174 if (num < 0) {
Heyi Guo58bf3bf2021-01-20 13:55:25 +0800175 CHECK_AND_PUT_CHAR(s, n, chars_printed,
176 '-');
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100177 unum = (unsigned int)-num;
178 } else {
179 unum = (unsigned int)num;
180 }
181
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100182 unsigned_num_print(&s, n, &chars_printed,
183 unum, 10, padc, padn, false);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100184 break;
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +0100185 case 's':
186 str = va_arg(args, char *);
187 string_print(&s, n, &chars_printed, str);
188 break;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100189 case 'u':
kadabi76c3f5b2022-02-28 14:21:09 -0800190 unum = get_unum_va_args(args, l_count);
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100191 unsigned_num_print(&s, n, &chars_printed,
192 unum, 10, padc, padn, false);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100193 break;
kadabi76c3f5b2022-02-28 14:21:09 -0800194 case 'z':
195 l_count = 1;
196 fmt++;
197 goto loop;
198 case 'l':
199 l_count++;
200 fmt++;
201 goto loop;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100202 case 'p':
203 unum = (uintptr_t)va_arg(args, void *);
204 if (unum > 0U) {
205 string_print(&s, n, &chars_printed, "0x");
206 padn -= 2;
207 }
208 unsigned_num_print(&s, n, &chars_printed,
209 unum, 16, padc, padn, false);
210 break;
211 case 'X':
212 capitalise = true;
213 case 'x':
kadabi76c3f5b2022-02-28 14:21:09 -0800214 unum = get_unum_va_args(args, l_count);
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100215 unsigned_num_print(&s, n, &chars_printed,
216 unum, 16, padc, padn,
217 capitalise);
218 break;
219
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100220 default:
221 /* Panic on any other format specifier. */
Antonio Nino Diazc0c8eb62018-08-15 17:02:28 +0100222 ERROR("snprintf: specifier with ASCII code '%d' not supported.",
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100223 *fmt);
224 plat_panic_handler();
Daniel Boulby8942a1b2018-06-22 14:16:03 +0100225 assert(0); /* Unreachable */
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100226 }
227 fmt++;
228 continue;
229 }
230
Heyi Guo58bf3bf2021-01-20 13:55:25 +0800231 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100232
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100233 fmt++;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100234 }
235
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500236 if (n > 0U) {
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100237 *s = '\0';
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500238 }
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100239
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100240 return (int)chars_printed;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100241}
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500242
243/*******************************************************************
244 * Reduced snprintf to be used for Trusted firmware.
245 * The following type specifiers are supported:
246 *
247 * %x (or %X) - hexadecimal format
248 * %d or %i - signed decimal format
249 * %s - string format
250 * %u - unsigned decimal format
251 * %p - pointer format
252 *
253 * The following padding specifiers are supported by this print
254 * %0NN - Left-pad the number with 0s (NN is a decimal number)
255 * %NN - Left-pad the number or string with spaces (NN is a decimal number)
256 * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
257 *
258 * The function panics on all other formats specifiers.
259 *
260 * It returns the number of characters that would be written if the
261 * buffer was big enough. If it returns a value lower than n, the
262 * whole string has been written.
263 *******************************************************************/
264int snprintf(char *s, size_t n, const char *fmt, ...)
265{
266 int count;
267 va_list all_args;
268
269 va_start(all_args, fmt);
270 count = vsnprintf(s, n, fmt, all_args);
271 va_end(all_args);
272
273 return count;
274}