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