1 /*
2  * Copyright (c) 2017-2021, 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 
10 #include <common/debug.h>
11 #include <plat/common/platform.h>
12 
13 #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch)	\
14 	do {						\
15 		if ((chars_printed) < (size)) {		\
16 			*(buf) = (ch);			\
17 			(buf)++;			\
18 		}					\
19 		(chars_printed)++;			\
20 	} while (false)
21 
string_print(char ** s,size_t n,size_t * chars_printed,const char * str)22 static void string_print(char **s, size_t n, size_t *chars_printed,
23 			 const char *str)
24 {
25 	while (*str != '\0') {
26 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
27 		str++;
28 	}
29 }
30 
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)31 static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
32 			      unsigned long long int unum,
33 			      unsigned int radix, char padc, int padn,
34 			      bool capitalise)
35 {
36 	/* Just need enough space to store 64 bit decimal integer */
37 	char num_buf[20];
38 	int i = 0;
39 	int width;
40 	unsigned int rem;
41 	char ascii_a = capitalise ? 'A' : 'a';
42 
43 	do {
44 		rem = unum % radix;
45 		if (rem < 10U) {
46 			num_buf[i] = '0' + rem;
47 		} else {
48 			num_buf[i] = ascii_a + (rem - 10U);
49 		}
50 		i++;
51 		unum /= radix;
52 	} while (unum > 0U);
53 
54 	width = i;
55 	if (padn > width) {
56 		(*chars_printed) += (size_t)padn;
57 	} else {
58 		(*chars_printed) += (size_t)width;
59 	}
60 
61 	if (*chars_printed < n) {
62 
63 		if (padn > 0) {
64 			while (width < padn) {
65 				*(*s)++ = padc;
66 				padn--;
67 			}
68 		}
69 
70 		while (--i >= 0) {
71 			*(*s)++ = num_buf[i];
72 		}
73 
74 		if (padn < 0) {
75 			while (width < -padn) {
76 				*(*s)++ = padc;
77 				padn++;
78 			}
79 		}
80 	}
81 }
82 
83 /*******************************************************************
84  * Reduced vsnprintf to be used for Trusted firmware.
85  * The following type specifiers are supported:
86  *
87  * %x (or %X) - hexadecimal format
88  * %d or %i - signed decimal format
89  * %s - string format
90  * %u - unsigned decimal format
91  * %p - pointer format
92  *
93  * The following padding specifiers are supported by this print
94  * %0NN - Left-pad the number with 0s (NN is a decimal number)
95  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
96  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
97  *
98  * The function panics on all other formats specifiers.
99  *
100  * It returns the number of characters that would be written if the
101  * buffer was big enough. If it returns a value lower than n, the
102  * whole string has been written.
103  *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)104 int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
105 {
106 	int num;
107 	unsigned long long int unum;
108 	char *str;
109 	char padc;		/* Padding character */
110 	int padn;		/* Number of characters to pad */
111 	bool left;
112 	bool capitalise;
113 	size_t chars_printed = 0U;
114 
115 	if (n == 0U) {
116 		/* There isn't space for anything. */
117 	} else if (n == 1U) {
118 		/* Buffer is too small to actually write anything else. */
119 		*s = '\0';
120 		n = 0U;
121 	} else {
122 		/* Reserve space for the terminator character. */
123 		n--;
124 	}
125 
126 	while (*fmt != '\0') {
127 		left = false;
128 		padc ='\0';
129 		padn = 0;
130 		capitalise = false;
131 
132 		if (*fmt == '%') {
133 			fmt++;
134 			/* Check the format specifier. */
135 loop:
136 			switch (*fmt) {
137 			case '%':
138 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
139 				break;
140 			case '0':
141 			case '1':
142 			case '2':
143 			case '3':
144 			case '4':
145 			case '5':
146 			case '6':
147 			case '7':
148 			case '8':
149 			case '9':
150 				padc = (*fmt == '0') ? '0' : ' ';
151 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
152 					padn = (padn * 10) + (*fmt - '0');
153 				}
154 				if (left) {
155 					padn = -padn;
156 				}
157 				goto loop;
158 			case '-':
159 				left = true;
160 				fmt++;
161 				goto loop;
162 
163 			case 'i':
164 			case 'd':
165 				num = va_arg(args, int);
166 
167 				if (num < 0) {
168 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
169 						'-');
170 					unum = (unsigned int)-num;
171 				} else {
172 					unum = (unsigned int)num;
173 				}
174 
175 				unsigned_num_print(&s, n, &chars_printed,
176 						   unum, 10, padc, padn, false);
177 				break;
178 			case 's':
179 				str = va_arg(args, char *);
180 				string_print(&s, n, &chars_printed, str);
181 				break;
182 			case 'u':
183 				unum = va_arg(args, unsigned int);
184 				unsigned_num_print(&s, n, &chars_printed,
185 						   unum, 10, padc, padn, false);
186 				break;
187 			case 'p':
188 				unum = (uintptr_t)va_arg(args, void *);
189 				if (unum > 0U) {
190 					string_print(&s, n, &chars_printed, "0x");
191 					padn -= 2;
192 				}
193 				unsigned_num_print(&s, n, &chars_printed,
194 						   unum, 16, padc, padn, false);
195 				break;
196 			case 'X':
197 				capitalise = true;
198 			case 'x':
199 				unum = va_arg(args, unsigned int);
200 				unsigned_num_print(&s, n, &chars_printed,
201 						   unum, 16, padc, padn,
202 						   capitalise);
203 				break;
204 
205 			default:
206 				/* Panic on any other format specifier. */
207 				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
208 				      *fmt);
209 				plat_panic_handler();
210 				assert(0); /* Unreachable */
211 			}
212 			fmt++;
213 			continue;
214 		}
215 
216 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
217 
218 		fmt++;
219 	}
220 
221 	if (n > 0U) {
222 		*s = '\0';
223 	}
224 
225 	return (int)chars_printed;
226 }
227 
228 /*******************************************************************
229  * Reduced snprintf to be used for Trusted firmware.
230  * The following type specifiers are supported:
231  *
232  * %x (or %X) - hexadecimal format
233  * %d or %i - signed decimal format
234  * %s - string format
235  * %u - unsigned decimal format
236  * %p - pointer format
237  *
238  * The following padding specifiers are supported by this print
239  * %0NN - Left-pad the number with 0s (NN is a decimal number)
240  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
241  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
242  *
243  * The function panics on all other formats specifiers.
244  *
245  * It returns the number of characters that would be written if the
246  * buffer was big enough. If it returns a value lower than n, the
247  * whole string has been written.
248  *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)249 int snprintf(char *s, size_t n, const char *fmt, ...)
250 {
251 	int count;
252 	va_list all_args;
253 
254 	va_start(all_args, fmt);
255 	count = vsnprintf(s, n, fmt, all_args);
256 	va_end(all_args);
257 
258 	return count;
259 }
260