blob: 6a2f0ba7fdcb045aed1e4427fdce394adf8674a5 [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>
Claus Pedersen785e66c2022-09-12 22:42:58 +00009#include <stdbool.h>
10#include <stddef.h>
Andre Przywara94faff82022-01-27 17:47:55 +000011#include <stdint.h>
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010012
kadabi76c3f5b2022-02-28 14:21:09 -080013#define get_num_va_args(_args, _lcount) \
14 (((_lcount) > 1) ? va_arg(_args, long long int) : \
15 (((_lcount) == 1) ? va_arg(_args, long int) : \
16 va_arg(_args, int)))
17
18#define get_unum_va_args(_args, _lcount) \
19 (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \
20 (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \
21 va_arg(_args, unsigned int)))
22
Heyi Guo58bf3bf2021-01-20 13:55:25 +080023#define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \
24 do { \
25 if ((chars_printed) < (size)) { \
26 *(buf) = (ch); \
27 (buf)++; \
28 } \
29 (chars_printed)++; \
30 } while (false)
31
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010032static void string_print(char **s, size_t n, size_t *chars_printed,
33 const char *str)
34{
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010035 while (*str != '\0') {
Heyi Guo58bf3bf2021-01-20 13:55:25 +080036 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010037 str++;
38 }
39}
40
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010041static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
42 unsigned long long int unum,
43 unsigned int radix, char padc, int padn,
44 bool capitalise)
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010045{
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010046 /* Just need enough space to store 64 bit decimal integer */
47 char num_buf[20];
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010048 int i = 0;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010049 int width;
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010050 unsigned int rem;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010051 char ascii_a = capitalise ? 'A' : 'a';
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010052
Claus Pedersen785e66c2022-09-12 22:42:58 +000053 /* num_buf is only large enough for radix >= 10 */
Andre Przywara933cc5d2022-01-24 18:16:10 +000054 if (radix < 10) {
Claus Pedersen785e66c2022-09-12 22:42:58 +000055 assert(0);
56 return;
Andre Przywara933cc5d2022-01-24 18:16:10 +000057 }
58
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010059 do {
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010060 rem = unum % radix;
61 if (rem < 10U) {
62 num_buf[i] = '0' + rem;
63 } else {
64 num_buf[i] = ascii_a + (rem - 10U);
65 }
66 i++;
67 unum /= radix;
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +010068 } while (unum > 0U);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010069
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010070 width = i;
Andre Przywara3d959092021-12-21 12:35:54 +000071 for (i = padn - width; i > 0; i--) {
72 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
73 }
74 for (i = width; i > 0; i--) {
75 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010076 }
Andre Przywara3d959092021-12-21 12:35:54 +000077 for (i = width + padn; i < 0; i++) {
78 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010079 }
80}
81
82/*******************************************************************
Madhukar Pappireddy38629702020-09-08 19:00:00 -050083 * Reduced vsnprintf to be used for Trusted firmware.
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010084 * The following type specifiers are supported:
85 *
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010086 * %x (or %X) - hexadecimal format
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010087 * %d or %i - signed decimal format
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010088 * %s - string format
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010089 * %u - unsigned decimal format
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010090 * %p - pointer format
91 *
kadabi76c3f5b2022-02-28 14:21:09 -080092 * The following length specifiers are supported by this print
93 * %l - long int
94 * %ll - long long int
95 * %z - size_t sized integer formats
96 *
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +010097 * The following padding specifiers are supported by this print
98 * %0NN - Left-pad the number with 0s (NN is a decimal number)
99 * %NN - Left-pad the number or string with spaces (NN is a decimal number)
100 * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100101 *
102 * The function panics on all other formats specifiers.
103 *
104 * It returns the number of characters that would be written if the
105 * buffer was big enough. If it returns a value lower than n, the
106 * whole string has been written.
107 *******************************************************************/
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500108int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100109{
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100110 int num;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100111 unsigned long long int unum;
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +0100112 char *str;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100113 char padc; /* Padding character */
114 int padn; /* Number of characters to pad */
115 bool left;
116 bool capitalise;
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100117 size_t chars_printed = 0U;
kadabi76c3f5b2022-02-28 14:21:09 -0800118 unsigned int l_count;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100119
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100120 if (n == 0U) {
121 /* There isn't space for anything. */
122 } else if (n == 1U) {
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100123 /* Buffer is too small to actually write anything else. */
124 *s = '\0';
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100125 n = 0U;
126 } else {
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100127 /* Reserve space for the terminator character. */
128 n--;
129 }
130
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100131 while (*fmt != '\0') {
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100132 left = false;
133 padc ='\0';
134 padn = 0;
135 capitalise = false;
kadabi76c3f5b2022-02-28 14:21:09 -0800136 l_count = 0;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100137
138 if (*fmt == '%') {
139 fmt++;
140 /* Check the format specifier. */
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100141loop:
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100142 switch (*fmt) {
Heyi Guo11a16c82020-10-27 08:36:40 +0800143 case '%':
Heyi Guo58bf3bf2021-01-20 13:55:25 +0800144 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
Heyi Guo11a16c82020-10-27 08:36:40 +0800145 break;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100146 case '0':
147 case '1':
148 case '2':
149 case '3':
150 case '4':
151 case '5':
152 case '6':
153 case '7':
154 case '8':
155 case '9':
156 padc = (*fmt == '0') ? '0' : ' ';
157 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
158 padn = (padn * 10) + (*fmt - '0');
159 }
160 if (left) {
161 padn = -padn;
162 }
163 goto loop;
164 case '-':
165 left = true;
166 fmt++;
167 goto loop;
168
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100169 case 'i':
170 case 'd':
kadabi76c3f5b2022-02-28 14:21:09 -0800171 num = get_num_va_args(args, l_count);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100172
173 if (num < 0) {
Heyi Guo58bf3bf2021-01-20 13:55:25 +0800174 CHECK_AND_PUT_CHAR(s, n, chars_printed,
175 '-');
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100176 unum = (unsigned int)-num;
177 } else {
178 unum = (unsigned int)num;
179 }
180
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100181 unsigned_num_print(&s, n, &chars_printed,
182 unum, 10, padc, padn, false);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100183 break;
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +0100184 case 's':
185 str = va_arg(args, char *);
186 string_print(&s, n, &chars_printed, str);
187 break;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100188 case 'u':
kadabi76c3f5b2022-02-28 14:21:09 -0800189 unum = get_unum_va_args(args, l_count);
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100190 unsigned_num_print(&s, n, &chars_printed,
191 unum, 10, padc, padn, false);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100192 break;
kadabi76c3f5b2022-02-28 14:21:09 -0800193 case 'z':
194 l_count = 1;
195 fmt++;
196 goto loop;
197 case 'l':
198 l_count++;
199 fmt++;
200 goto loop;
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100201 case 'p':
202 unum = (uintptr_t)va_arg(args, void *);
203 if (unum > 0U) {
204 string_print(&s, n, &chars_printed, "0x");
205 padn -= 2;
206 }
207 unsigned_num_print(&s, n, &chars_printed,
208 unum, 16, padc, padn, false);
209 break;
210 case 'X':
211 capitalise = true;
212 case 'x':
kadabi76c3f5b2022-02-28 14:21:09 -0800213 unum = get_unum_va_args(args, l_count);
Javier Almansa Sobrinoc58a13e2020-08-21 17:32:03 +0100214 unsigned_num_print(&s, n, &chars_printed,
215 unum, 16, padc, padn,
216 capitalise);
217 break;
218
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100219 default:
Claus Pedersen785e66c2022-09-12 22:42:58 +0000220 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
221 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100222 }
223 fmt++;
224 continue;
225 }
226
Heyi Guo58bf3bf2021-01-20 13:55:25 +0800227 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100228
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100229 fmt++;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100230 }
231
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500232 if (n > 0U) {
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100233 *s = '\0';
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500234 }
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100235
Antonio Nino Diaz2e74f9b2018-08-23 15:11:46 +0100236 return (int)chars_printed;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +0100237}
Madhukar Pappireddy38629702020-09-08 19:00:00 -0500238
239/*******************************************************************
240 * Reduced snprintf to be used for Trusted firmware.
241 * The following type specifiers are supported:
242 *
243 * %x (or %X) - hexadecimal format
244 * %d or %i - signed decimal format
245 * %s - string format
246 * %u - unsigned decimal format
247 * %p - pointer format
248 *
249 * The following padding specifiers are supported by this print
250 * %0NN - Left-pad the number with 0s (NN is a decimal number)
251 * %NN - Left-pad the number or string with spaces (NN is a decimal number)
252 * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
253 *
254 * The function panics on all other formats specifiers.
255 *
256 * It returns the number of characters that would be written if the
257 * buffer was big enough. If it returns a value lower than n, the
258 * whole string has been written.
259 *******************************************************************/
260int snprintf(char *s, size_t n, const char *fmt, ...)
261{
262 int count;
263 va_list all_args;
264
265 va_start(all_args, fmt);
266 count = vsnprintf(s, n, fmt, all_args);
267 va_end(all_args);
268
269 return count;
270}