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