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