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