1 /*
2  * Copyright (c) 2017-2022, 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  * %s - string format
89  * %u - unsigned decimal format
90  * %p - pointer format
91  *
92  * 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  *
97  * 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)
101  *
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  *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)108 int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
109 {
110 	int num;
111 	unsigned long long int unum;
112 	char *str;
113 	char padc;		/* Padding character */
114 	int padn;		/* Number of characters to pad */
115 	bool left;
116 	bool capitalise;
117 	size_t chars_printed = 0U;
118 	unsigned int l_count;
119 
120 	if (n == 0U) {
121 		/* There isn't space for anything. */
122 	} else if (n == 1U) {
123 		/* Buffer is too small to actually write anything else. */
124 		*s = '\0';
125 		n = 0U;
126 	} else {
127 		/* Reserve space for the terminator character. */
128 		n--;
129 	}
130 
131 	while (*fmt != '\0') {
132 		left = false;
133 		padc ='\0';
134 		padn = 0;
135 		capitalise = false;
136 		l_count = 0;
137 
138 		if (*fmt == '%') {
139 			fmt++;
140 			/* Check the format specifier. */
141 loop:
142 			switch (*fmt) {
143 			case '%':
144 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
145 				break;
146 			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 
169 			case 'i':
170 			case 'd':
171 				num = get_num_va_args(args, l_count);
172 
173 				if (num < 0) {
174 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
175 						'-');
176 					unum = (unsigned int)-num;
177 				} else {
178 					unum = (unsigned int)num;
179 				}
180 
181 				unsigned_num_print(&s, n, &chars_printed,
182 						   unum, 10, padc, padn, false);
183 				break;
184 			case 's':
185 				str = va_arg(args, char *);
186 				string_print(&s, n, &chars_printed, str);
187 				break;
188 			case 'u':
189 				unum = get_unum_va_args(args, l_count);
190 				unsigned_num_print(&s, n, &chars_printed,
191 						   unum, 10, padc, padn, false);
192 				break;
193 			case 'z':
194 				l_count = 1;
195 				fmt++;
196 				goto loop;
197 			case 'l':
198 				l_count++;
199 				fmt++;
200 				goto loop;
201 			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':
213 				unum = get_unum_va_args(args, l_count);
214 				unsigned_num_print(&s, n, &chars_printed,
215 						   unum, 16, padc, padn,
216 						   capitalise);
217 				break;
218 
219 			default:
220 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
221 				CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
222 			}
223 			fmt++;
224 			continue;
225 		}
226 
227 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
228 
229 		fmt++;
230 	}
231 
232 	if (n > 0U) {
233 		*s = '\0';
234 	}
235 
236 	return (int)chars_printed;
237 }
238 
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  *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)260 int 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 }
271