1 /*
2 * Copyright (c) 2017-2023, 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 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdint.h>
12
13 #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
23 #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
string_print(char ** s,size_t n,size_t * chars_printed,const char * str)32 static void string_print(char **s, size_t n, size_t *chars_printed,
33 const char *str)
34 {
35 while (*str != '\0') {
36 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
37 str++;
38 }
39 }
40
unsigned_num_print(char ** s,size_t n,size_t * chars_printed,unsigned long long int unum,unsigned int radix,char padc,int padn,bool capitalise)41 static 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)
45 {
46 /* Just need enough space to store 64 bit decimal integer */
47 char num_buf[20];
48 int i = 0;
49 int width;
50 unsigned int rem;
51 char ascii_a = capitalise ? 'A' : 'a';
52
53 /* num_buf is only large enough for radix >= 10 */
54 if (radix < 10) {
55 assert(0);
56 return;
57 }
58
59 do {
60 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;
68 } while (unum > 0U);
69
70 width = i;
71 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]);
76 }
77 for (i = width + padn; i < 0; i++) {
78 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
79 }
80 }
81
82 /*******************************************************************
83 * Reduced vsnprintf to be used for Trusted firmware.
84 * The following type specifiers are supported:
85 *
86 * %x (or %X) - hexadecimal format
87 * %d or %i - signed decimal format
88 * %c - character format
89 * %s - string format
90 * %u - unsigned decimal format
91 * %p - pointer format
92 *
93 * 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 *
98 * 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)
102 *
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 *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)109 int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
110 {
111 int num;
112 unsigned long long int unum;
113 char *str;
114 char padc; /* Padding character */
115 int padn; /* Number of characters to pad */
116 bool left;
117 bool capitalise;
118 size_t chars_printed = 0U;
119 unsigned int l_count;
120
121 if (n == 0U) {
122 /* There isn't space for anything. */
123 } else if (n == 1U) {
124 /* Buffer is too small to actually write anything else. */
125 *s = '\0';
126 n = 0U;
127 } else {
128 /* Reserve space for the terminator character. */
129 n--;
130 }
131
132 while (*fmt != '\0') {
133 left = false;
134 padc ='\0';
135 padn = 0;
136 capitalise = false;
137 l_count = 0;
138
139 if (*fmt == '%') {
140 fmt++;
141 /* Check the format specifier. */
142 loop:
143 switch (*fmt) {
144 case '%':
145 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
146 break;
147 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
170 case 'i':
171 case 'd':
172 num = get_num_va_args(args, l_count);
173
174 if (num < 0) {
175 CHECK_AND_PUT_CHAR(s, n, chars_printed,
176 '-');
177 unum = (unsigned int)-num;
178 } else {
179 unum = (unsigned int)num;
180 }
181
182 unsigned_num_print(&s, n, &chars_printed,
183 unum, 10, padc, padn, false);
184 break;
185 case 'c':
186 CHECK_AND_PUT_CHAR(s, n, chars_printed, va_arg(args, int));
187 break;
188 case 's':
189 str = va_arg(args, char *);
190 string_print(&s, n, &chars_printed, str);
191 break;
192 case 'u':
193 unum = get_unum_va_args(args, l_count);
194 unsigned_num_print(&s, n, &chars_printed,
195 unum, 10, padc, padn, false);
196 break;
197 case 'z':
198 l_count = 1;
199 fmt++;
200 goto loop;
201 case 'l':
202 l_count++;
203 fmt++;
204 goto loop;
205 case 'p':
206 unum = (uintptr_t)va_arg(args, void *);
207 if (unum > 0U) {
208 string_print(&s, n, &chars_printed, "0x");
209 padn -= 2;
210 }
211 unsigned_num_print(&s, n, &chars_printed,
212 unum, 16, padc, padn, false);
213 break;
214 case 'X':
215 capitalise = true;
216 /* fallthrough */
217 case 'x':
218 unum = get_unum_va_args(args, l_count);
219 unsigned_num_print(&s, n, &chars_printed,
220 unum, 16, padc, padn,
221 capitalise);
222 break;
223
224 default:
225 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
226 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
227 }
228 fmt++;
229 continue;
230 }
231
232 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
233
234 fmt++;
235 }
236
237 if (n > 0U) {
238 *s = '\0';
239 }
240
241 return (int)chars_printed;
242 }
243
244 /*******************************************************************
245 * Reduced snprintf to be used for Trusted firmware.
246 * The following type specifiers are supported:
247 *
248 * %x (or %X) - hexadecimal format
249 * %d or %i - signed decimal format
250 * %s - string format
251 * %u - unsigned decimal format
252 * %p - pointer format
253 *
254 * The following padding specifiers are supported by this print
255 * %0NN - Left-pad the number with 0s (NN is a decimal number)
256 * %NN - Left-pad the number or string with spaces (NN is a decimal number)
257 * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
258 *
259 * The function panics on all other formats specifiers.
260 *
261 * It returns the number of characters that would be written if the
262 * buffer was big enough. If it returns a value lower than n, the
263 * whole string has been written.
264 *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)265 int snprintf(char *s, size_t n, const char *fmt, ...)
266 {
267 int count;
268 va_list all_args;
269
270 va_start(all_args, fmt);
271 count = vsnprintf(s, n, fmt, all_args);
272 va_end(all_args);
273
274 return count;
275 }
276