xref: /rk3399_ARM-atf/lib/libc/snprintf.c (revision e138400d1c19a561eaf9f23b0cadc07226684561)
1870ce3ddSAntonio Nino Diaz /*
2a211fde9SYann Gautier  * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
3870ce3ddSAntonio Nino Diaz  *
4870ce3ddSAntonio Nino Diaz  * SPDX-License-Identifier: BSD-3-Clause
5870ce3ddSAntonio Nino Diaz  */
6870ce3ddSAntonio Nino Diaz 
7a08a2014SDaniel Boulby #include <assert.h>
8870ce3ddSAntonio Nino Diaz #include <stdarg.h>
9885e2683SClaus Pedersen #include <stdbool.h>
10885e2683SClaus Pedersen #include <stddef.h>
11410c925aSAndre Przywara #include <stdint.h>
12870ce3ddSAntonio Nino Diaz 
13701e94b0Skadabi #define get_num_va_args(_args, _lcount)				\
14701e94b0Skadabi 	(((_lcount) > 1)  ? va_arg(_args, long long int) :	\
15701e94b0Skadabi 	(((_lcount) == 1) ? va_arg(_args, long int) :		\
16701e94b0Skadabi 			    va_arg(_args, int)))
17701e94b0Skadabi 
18701e94b0Skadabi #define get_unum_va_args(_args, _lcount)				\
19701e94b0Skadabi 	(((_lcount) > 1)  ? va_arg(_args, unsigned long long int) :	\
20701e94b0Skadabi 	(((_lcount) == 1) ? va_arg(_args, unsigned long int) :		\
21701e94b0Skadabi 			    va_arg(_args, unsigned int)))
22701e94b0Skadabi 
237981c504SHeyi Guo #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch)	\
247981c504SHeyi Guo 	do {						\
257981c504SHeyi Guo 		if ((chars_printed) < (size)) {		\
267981c504SHeyi Guo 			*(buf) = (ch);			\
277981c504SHeyi Guo 			(buf)++;			\
287981c504SHeyi Guo 		}					\
297981c504SHeyi Guo 		(chars_printed)++;			\
307981c504SHeyi Guo 	} while (false)
317981c504SHeyi Guo 
32870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed,
33870ce3ddSAntonio Nino Diaz 			 const char *str)
34870ce3ddSAntonio Nino Diaz {
35d5ccb754SAntonio Nino Diaz 	while (*str != '\0') {
367981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
37870ce3ddSAntonio Nino Diaz 		str++;
38870ce3ddSAntonio Nino Diaz 	}
39870ce3ddSAntonio Nino Diaz }
40870ce3ddSAntonio Nino Diaz 
41524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
42524eecc6SJavier Almansa Sobrino 			      unsigned long long int unum,
43524eecc6SJavier Almansa Sobrino 			      unsigned int radix, char padc, int padn,
44524eecc6SJavier Almansa Sobrino 			      bool capitalise)
45870ce3ddSAntonio Nino Diaz {
46524eecc6SJavier Almansa Sobrino 	/* Just need enough space to store 64 bit decimal integer */
47524eecc6SJavier Almansa Sobrino 	char num_buf[20];
48d5ccb754SAntonio Nino Diaz 	int i = 0;
49524eecc6SJavier Almansa Sobrino 	int width;
50d5ccb754SAntonio Nino Diaz 	unsigned int rem;
51524eecc6SJavier Almansa Sobrino 	char ascii_a = capitalise ? 'A' : 'a';
52870ce3ddSAntonio Nino Diaz 
53885e2683SClaus Pedersen 	/* num_buf is only large enough for radix >= 10 */
54b30dd403SAndre Przywara 	if (radix < 10) {
55885e2683SClaus Pedersen 		assert(0);
56885e2683SClaus Pedersen 		return;
57b30dd403SAndre Przywara 	}
58b30dd403SAndre Przywara 
59870ce3ddSAntonio Nino Diaz 	do {
60524eecc6SJavier Almansa Sobrino 		rem = unum % radix;
61524eecc6SJavier Almansa Sobrino 		if (rem < 10U) {
62524eecc6SJavier Almansa Sobrino 			num_buf[i] = '0' + rem;
63524eecc6SJavier Almansa Sobrino 		} else {
64524eecc6SJavier Almansa Sobrino 			num_buf[i] = ascii_a + (rem - 10U);
65524eecc6SJavier Almansa Sobrino 		}
66524eecc6SJavier Almansa Sobrino 		i++;
67524eecc6SJavier Almansa Sobrino 		unum /= radix;
68d5ccb754SAntonio Nino Diaz 	} while (unum > 0U);
69870ce3ddSAntonio Nino Diaz 
70524eecc6SJavier Almansa Sobrino 	width = i;
71c1f5a092SAndre Przywara 	for (i = padn - width; i > 0; i--) {
72c1f5a092SAndre Przywara 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
73d5ccb754SAntonio Nino Diaz 	}
74c1f5a092SAndre Przywara 	for (i = width; i > 0; i--) {
75c1f5a092SAndre Przywara 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
76524eecc6SJavier Almansa Sobrino 	}
77c1f5a092SAndre Przywara 	for (i = width + padn; i < 0; i++) {
78c1f5a092SAndre Przywara 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
79870ce3ddSAntonio Nino Diaz 	}
80870ce3ddSAntonio Nino Diaz }
81870ce3ddSAntonio Nino Diaz 
82870ce3ddSAntonio Nino Diaz /*******************************************************************
8377648689SMadhukar Pappireddy  * Reduced vsnprintf to be used for Trusted firmware.
84870ce3ddSAntonio Nino Diaz  * The following type specifiers are supported:
85870ce3ddSAntonio Nino Diaz  *
86524eecc6SJavier Almansa Sobrino  * %x (or %X) - hexadecimal format
87870ce3ddSAntonio Nino Diaz  * %d or %i - signed decimal format
88870ce3ddSAntonio Nino Diaz  * %s - string format
89870ce3ddSAntonio Nino Diaz  * %u - unsigned decimal format
90524eecc6SJavier Almansa Sobrino  * %p - pointer format
91524eecc6SJavier Almansa Sobrino  *
92701e94b0Skadabi  * The following length specifiers are supported by this print
93701e94b0Skadabi  * %l - long int
94701e94b0Skadabi  * %ll - long long int
95701e94b0Skadabi  * %z - size_t sized integer formats
96701e94b0Skadabi  *
97524eecc6SJavier Almansa Sobrino  * The following padding specifiers are supported by this print
98524eecc6SJavier Almansa Sobrino  * %0NN - Left-pad the number with 0s (NN is a decimal number)
99524eecc6SJavier Almansa Sobrino  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
100524eecc6SJavier Almansa Sobrino  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
101870ce3ddSAntonio Nino Diaz  *
102870ce3ddSAntonio Nino Diaz  * The function panics on all other formats specifiers.
103870ce3ddSAntonio Nino Diaz  *
104870ce3ddSAntonio Nino Diaz  * It returns the number of characters that would be written if the
105870ce3ddSAntonio Nino Diaz  * buffer was big enough. If it returns a value lower than n, the
106870ce3ddSAntonio Nino Diaz  * whole string has been written.
107870ce3ddSAntonio Nino Diaz  *******************************************************************/
10877648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
109870ce3ddSAntonio Nino Diaz {
110870ce3ddSAntonio Nino Diaz 	int num;
111524eecc6SJavier Almansa Sobrino 	unsigned long long int unum;
112870ce3ddSAntonio Nino Diaz 	char *str;
113524eecc6SJavier Almansa Sobrino 	char padc;		/* Padding character */
114524eecc6SJavier Almansa Sobrino 	int padn;		/* Number of characters to pad */
115524eecc6SJavier Almansa Sobrino 	bool left;
116524eecc6SJavier Almansa Sobrino 	bool capitalise;
117d5ccb754SAntonio Nino Diaz 	size_t chars_printed = 0U;
118701e94b0Skadabi 	unsigned int l_count;
119870ce3ddSAntonio Nino Diaz 
120d5ccb754SAntonio Nino Diaz 	if (n == 0U) {
121d5ccb754SAntonio Nino Diaz 		/* There isn't space for anything. */
122d5ccb754SAntonio Nino Diaz 	} else if (n == 1U) {
123870ce3ddSAntonio Nino Diaz 		/* Buffer is too small to actually write anything else. */
124870ce3ddSAntonio Nino Diaz 		*s = '\0';
125d5ccb754SAntonio Nino Diaz 		n = 0U;
126d5ccb754SAntonio Nino Diaz 	} else {
127870ce3ddSAntonio Nino Diaz 		/* Reserve space for the terminator character. */
128870ce3ddSAntonio Nino Diaz 		n--;
129870ce3ddSAntonio Nino Diaz 	}
130870ce3ddSAntonio Nino Diaz 
131d5ccb754SAntonio Nino Diaz 	while (*fmt != '\0') {
132524eecc6SJavier Almansa Sobrino 		left = false;
133524eecc6SJavier Almansa Sobrino 		padc ='\0';
134524eecc6SJavier Almansa Sobrino 		padn = 0;
135524eecc6SJavier Almansa Sobrino 		capitalise = false;
136701e94b0Skadabi 		l_count = 0;
137870ce3ddSAntonio Nino Diaz 
138870ce3ddSAntonio Nino Diaz 		if (*fmt == '%') {
139870ce3ddSAntonio Nino Diaz 			fmt++;
140870ce3ddSAntonio Nino Diaz 			/* Check the format specifier. */
141524eecc6SJavier Almansa Sobrino loop:
142870ce3ddSAntonio Nino Diaz 			switch (*fmt) {
143c6546154SHeyi Guo 			case '%':
1447981c504SHeyi Guo 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
145c6546154SHeyi Guo 				break;
146524eecc6SJavier Almansa Sobrino 			case '0':
147524eecc6SJavier Almansa Sobrino 			case '1':
148524eecc6SJavier Almansa Sobrino 			case '2':
149524eecc6SJavier Almansa Sobrino 			case '3':
150524eecc6SJavier Almansa Sobrino 			case '4':
151524eecc6SJavier Almansa Sobrino 			case '5':
152524eecc6SJavier Almansa Sobrino 			case '6':
153524eecc6SJavier Almansa Sobrino 			case '7':
154524eecc6SJavier Almansa Sobrino 			case '8':
155524eecc6SJavier Almansa Sobrino 			case '9':
156524eecc6SJavier Almansa Sobrino 				padc = (*fmt == '0') ? '0' : ' ';
157524eecc6SJavier Almansa Sobrino 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
158524eecc6SJavier Almansa Sobrino 					padn = (padn * 10) + (*fmt - '0');
159524eecc6SJavier Almansa Sobrino 				}
160524eecc6SJavier Almansa Sobrino 				if (left) {
161524eecc6SJavier Almansa Sobrino 					padn = -padn;
162524eecc6SJavier Almansa Sobrino 				}
163524eecc6SJavier Almansa Sobrino 				goto loop;
164524eecc6SJavier Almansa Sobrino 			case '-':
165524eecc6SJavier Almansa Sobrino 				left = true;
166524eecc6SJavier Almansa Sobrino 				fmt++;
167524eecc6SJavier Almansa Sobrino 				goto loop;
168524eecc6SJavier Almansa Sobrino 
169870ce3ddSAntonio Nino Diaz 			case 'i':
170870ce3ddSAntonio Nino Diaz 			case 'd':
171701e94b0Skadabi 				num = get_num_va_args(args, l_count);
172870ce3ddSAntonio Nino Diaz 
173870ce3ddSAntonio Nino Diaz 				if (num < 0) {
1747981c504SHeyi Guo 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
1757981c504SHeyi Guo 						'-');
176870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)-num;
177870ce3ddSAntonio Nino Diaz 				} else {
178870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)num;
179870ce3ddSAntonio Nino Diaz 				}
180870ce3ddSAntonio Nino Diaz 
181524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
182524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
183870ce3ddSAntonio Nino Diaz 				break;
184870ce3ddSAntonio Nino Diaz 			case 's':
185870ce3ddSAntonio Nino Diaz 				str = va_arg(args, char *);
186870ce3ddSAntonio Nino Diaz 				string_print(&s, n, &chars_printed, str);
187870ce3ddSAntonio Nino Diaz 				break;
188870ce3ddSAntonio Nino Diaz 			case 'u':
189701e94b0Skadabi 				unum = get_unum_va_args(args, l_count);
190524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
191524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
192870ce3ddSAntonio Nino Diaz 				break;
193701e94b0Skadabi 			case 'z':
194701e94b0Skadabi 				l_count = 1;
195701e94b0Skadabi 				fmt++;
196701e94b0Skadabi 				goto loop;
197701e94b0Skadabi 			case 'l':
198701e94b0Skadabi 				l_count++;
199701e94b0Skadabi 				fmt++;
200701e94b0Skadabi 				goto loop;
201524eecc6SJavier Almansa Sobrino 			case 'p':
202524eecc6SJavier Almansa Sobrino 				unum = (uintptr_t)va_arg(args, void *);
203524eecc6SJavier Almansa Sobrino 				if (unum > 0U) {
204524eecc6SJavier Almansa Sobrino 					string_print(&s, n, &chars_printed, "0x");
205524eecc6SJavier Almansa Sobrino 					padn -= 2;
206524eecc6SJavier Almansa Sobrino 				}
207524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
208524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn, false);
209524eecc6SJavier Almansa Sobrino 				break;
210524eecc6SJavier Almansa Sobrino 			case 'X':
211524eecc6SJavier Almansa Sobrino 				capitalise = true;
212*e138400dSBoyan Karatotev 				/* fallthrough */
213524eecc6SJavier Almansa Sobrino 			case 'x':
214701e94b0Skadabi 				unum = get_unum_va_args(args, l_count);
215524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
216524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn,
217524eecc6SJavier Almansa Sobrino 						   capitalise);
218524eecc6SJavier Almansa Sobrino 				break;
219524eecc6SJavier Almansa Sobrino 
220870ce3ddSAntonio Nino Diaz 			default:
221885e2683SClaus Pedersen 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
222885e2683SClaus Pedersen 				CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
223870ce3ddSAntonio Nino Diaz 			}
224870ce3ddSAntonio Nino Diaz 			fmt++;
225870ce3ddSAntonio Nino Diaz 			continue;
226870ce3ddSAntonio Nino Diaz 		}
227870ce3ddSAntonio Nino Diaz 
2287981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
229d5ccb754SAntonio Nino Diaz 
230870ce3ddSAntonio Nino Diaz 		fmt++;
231870ce3ddSAntonio Nino Diaz 	}
232870ce3ddSAntonio Nino Diaz 
23377648689SMadhukar Pappireddy 	if (n > 0U) {
234870ce3ddSAntonio Nino Diaz 		*s = '\0';
23577648689SMadhukar Pappireddy 	}
236870ce3ddSAntonio Nino Diaz 
237d5ccb754SAntonio Nino Diaz 	return (int)chars_printed;
238870ce3ddSAntonio Nino Diaz }
23977648689SMadhukar Pappireddy 
24077648689SMadhukar Pappireddy /*******************************************************************
24177648689SMadhukar Pappireddy  * Reduced snprintf to be used for Trusted firmware.
24277648689SMadhukar Pappireddy  * The following type specifiers are supported:
24377648689SMadhukar Pappireddy  *
24477648689SMadhukar Pappireddy  * %x (or %X) - hexadecimal format
24577648689SMadhukar Pappireddy  * %d or %i - signed decimal format
24677648689SMadhukar Pappireddy  * %s - string format
24777648689SMadhukar Pappireddy  * %u - unsigned decimal format
24877648689SMadhukar Pappireddy  * %p - pointer format
24977648689SMadhukar Pappireddy  *
25077648689SMadhukar Pappireddy  * The following padding specifiers are supported by this print
25177648689SMadhukar Pappireddy  * %0NN - Left-pad the number with 0s (NN is a decimal number)
25277648689SMadhukar Pappireddy  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
25377648689SMadhukar Pappireddy  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
25477648689SMadhukar Pappireddy  *
25577648689SMadhukar Pappireddy  * The function panics on all other formats specifiers.
25677648689SMadhukar Pappireddy  *
25777648689SMadhukar Pappireddy  * It returns the number of characters that would be written if the
25877648689SMadhukar Pappireddy  * buffer was big enough. If it returns a value lower than n, the
25977648689SMadhukar Pappireddy  * whole string has been written.
26077648689SMadhukar Pappireddy  *******************************************************************/
26177648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...)
26277648689SMadhukar Pappireddy {
26377648689SMadhukar Pappireddy 	int count;
26477648689SMadhukar Pappireddy 	va_list all_args;
26577648689SMadhukar Pappireddy 
26677648689SMadhukar Pappireddy 	va_start(all_args, fmt);
26777648689SMadhukar Pappireddy 	count = vsnprintf(s, n, fmt, all_args);
26877648689SMadhukar Pappireddy 	va_end(all_args);
26977648689SMadhukar Pappireddy 
27077648689SMadhukar Pappireddy 	return count;
27177648689SMadhukar Pappireddy }
272