xref: /rk3399_ARM-atf/lib/libc/snprintf.c (revision 3011e1afeb351c46fc6fa39db51f99b5e3fca6ee)
1870ce3ddSAntonio Nino Diaz /*
2*44d9706eSMaksims Svecovs  * Copyright (c) 2017-2023, 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 
string_print(char ** s,size_t n,size_t * chars_printed,const char * str)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 
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)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
88*44d9706eSMaksims Svecovs  * %c - character format
89870ce3ddSAntonio Nino Diaz  * %s - string format
90870ce3ddSAntonio Nino Diaz  * %u - unsigned decimal format
91524eecc6SJavier Almansa Sobrino  * %p - pointer format
92524eecc6SJavier Almansa Sobrino  *
93701e94b0Skadabi  * The following length specifiers are supported by this print
94701e94b0Skadabi  * %l - long int
95701e94b0Skadabi  * %ll - long long int
96701e94b0Skadabi  * %z - size_t sized integer formats
97701e94b0Skadabi  *
98524eecc6SJavier Almansa Sobrino  * The following padding specifiers are supported by this print
99524eecc6SJavier Almansa Sobrino  * %0NN - Left-pad the number with 0s (NN is a decimal number)
100524eecc6SJavier Almansa Sobrino  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
101524eecc6SJavier Almansa Sobrino  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
102870ce3ddSAntonio Nino Diaz  *
103870ce3ddSAntonio Nino Diaz  * The function panics on all other formats specifiers.
104870ce3ddSAntonio Nino Diaz  *
105870ce3ddSAntonio Nino Diaz  * It returns the number of characters that would be written if the
106870ce3ddSAntonio Nino Diaz  * buffer was big enough. If it returns a value lower than n, the
107870ce3ddSAntonio Nino Diaz  * whole string has been written.
108870ce3ddSAntonio Nino Diaz  *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)10977648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
110870ce3ddSAntonio Nino Diaz {
111870ce3ddSAntonio Nino Diaz 	int num;
112524eecc6SJavier Almansa Sobrino 	unsigned long long int unum;
113870ce3ddSAntonio Nino Diaz 	char *str;
114524eecc6SJavier Almansa Sobrino 	char padc;		/* Padding character */
115524eecc6SJavier Almansa Sobrino 	int padn;		/* Number of characters to pad */
116524eecc6SJavier Almansa Sobrino 	bool left;
117524eecc6SJavier Almansa Sobrino 	bool capitalise;
118d5ccb754SAntonio Nino Diaz 	size_t chars_printed = 0U;
119701e94b0Skadabi 	unsigned int l_count;
120870ce3ddSAntonio Nino Diaz 
121d5ccb754SAntonio Nino Diaz 	if (n == 0U) {
122d5ccb754SAntonio Nino Diaz 		/* There isn't space for anything. */
123d5ccb754SAntonio Nino Diaz 	} else if (n == 1U) {
124870ce3ddSAntonio Nino Diaz 		/* Buffer is too small to actually write anything else. */
125870ce3ddSAntonio Nino Diaz 		*s = '\0';
126d5ccb754SAntonio Nino Diaz 		n = 0U;
127d5ccb754SAntonio Nino Diaz 	} else {
128870ce3ddSAntonio Nino Diaz 		/* Reserve space for the terminator character. */
129870ce3ddSAntonio Nino Diaz 		n--;
130870ce3ddSAntonio Nino Diaz 	}
131870ce3ddSAntonio Nino Diaz 
132d5ccb754SAntonio Nino Diaz 	while (*fmt != '\0') {
133524eecc6SJavier Almansa Sobrino 		left = false;
134524eecc6SJavier Almansa Sobrino 		padc ='\0';
135524eecc6SJavier Almansa Sobrino 		padn = 0;
136524eecc6SJavier Almansa Sobrino 		capitalise = false;
137701e94b0Skadabi 		l_count = 0;
138870ce3ddSAntonio Nino Diaz 
139870ce3ddSAntonio Nino Diaz 		if (*fmt == '%') {
140870ce3ddSAntonio Nino Diaz 			fmt++;
141870ce3ddSAntonio Nino Diaz 			/* Check the format specifier. */
142524eecc6SJavier Almansa Sobrino loop:
143870ce3ddSAntonio Nino Diaz 			switch (*fmt) {
144c6546154SHeyi Guo 			case '%':
1457981c504SHeyi Guo 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
146c6546154SHeyi Guo 				break;
147524eecc6SJavier Almansa Sobrino 			case '0':
148524eecc6SJavier Almansa Sobrino 			case '1':
149524eecc6SJavier Almansa Sobrino 			case '2':
150524eecc6SJavier Almansa Sobrino 			case '3':
151524eecc6SJavier Almansa Sobrino 			case '4':
152524eecc6SJavier Almansa Sobrino 			case '5':
153524eecc6SJavier Almansa Sobrino 			case '6':
154524eecc6SJavier Almansa Sobrino 			case '7':
155524eecc6SJavier Almansa Sobrino 			case '8':
156524eecc6SJavier Almansa Sobrino 			case '9':
157524eecc6SJavier Almansa Sobrino 				padc = (*fmt == '0') ? '0' : ' ';
158524eecc6SJavier Almansa Sobrino 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
159524eecc6SJavier Almansa Sobrino 					padn = (padn * 10) + (*fmt - '0');
160524eecc6SJavier Almansa Sobrino 				}
161524eecc6SJavier Almansa Sobrino 				if (left) {
162524eecc6SJavier Almansa Sobrino 					padn = -padn;
163524eecc6SJavier Almansa Sobrino 				}
164524eecc6SJavier Almansa Sobrino 				goto loop;
165524eecc6SJavier Almansa Sobrino 			case '-':
166524eecc6SJavier Almansa Sobrino 				left = true;
167524eecc6SJavier Almansa Sobrino 				fmt++;
168524eecc6SJavier Almansa Sobrino 				goto loop;
169524eecc6SJavier Almansa Sobrino 
170870ce3ddSAntonio Nino Diaz 			case 'i':
171870ce3ddSAntonio Nino Diaz 			case 'd':
172701e94b0Skadabi 				num = get_num_va_args(args, l_count);
173870ce3ddSAntonio Nino Diaz 
174870ce3ddSAntonio Nino Diaz 				if (num < 0) {
1757981c504SHeyi Guo 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
1767981c504SHeyi Guo 						'-');
177870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)-num;
178870ce3ddSAntonio Nino Diaz 				} else {
179870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)num;
180870ce3ddSAntonio Nino Diaz 				}
181870ce3ddSAntonio Nino Diaz 
182524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
183524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
184870ce3ddSAntonio Nino Diaz 				break;
185*44d9706eSMaksims Svecovs 			case 'c':
186*44d9706eSMaksims Svecovs 				CHECK_AND_PUT_CHAR(s, n, chars_printed, va_arg(args, int));
187*44d9706eSMaksims Svecovs 				break;
188870ce3ddSAntonio Nino Diaz 			case 's':
189870ce3ddSAntonio Nino Diaz 				str = va_arg(args, char *);
190870ce3ddSAntonio Nino Diaz 				string_print(&s, n, &chars_printed, str);
191870ce3ddSAntonio Nino Diaz 				break;
192870ce3ddSAntonio Nino Diaz 			case 'u':
193701e94b0Skadabi 				unum = get_unum_va_args(args, l_count);
194524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
195524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
196870ce3ddSAntonio Nino Diaz 				break;
197701e94b0Skadabi 			case 'z':
198701e94b0Skadabi 				l_count = 1;
199701e94b0Skadabi 				fmt++;
200701e94b0Skadabi 				goto loop;
201701e94b0Skadabi 			case 'l':
202701e94b0Skadabi 				l_count++;
203701e94b0Skadabi 				fmt++;
204701e94b0Skadabi 				goto loop;
205524eecc6SJavier Almansa Sobrino 			case 'p':
206524eecc6SJavier Almansa Sobrino 				unum = (uintptr_t)va_arg(args, void *);
207524eecc6SJavier Almansa Sobrino 				if (unum > 0U) {
208524eecc6SJavier Almansa Sobrino 					string_print(&s, n, &chars_printed, "0x");
209524eecc6SJavier Almansa Sobrino 					padn -= 2;
210524eecc6SJavier Almansa Sobrino 				}
211524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
212524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn, false);
213524eecc6SJavier Almansa Sobrino 				break;
214524eecc6SJavier Almansa Sobrino 			case 'X':
215524eecc6SJavier Almansa Sobrino 				capitalise = true;
216e138400dSBoyan Karatotev 				/* fallthrough */
217524eecc6SJavier Almansa Sobrino 			case 'x':
218701e94b0Skadabi 				unum = get_unum_va_args(args, l_count);
219524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
220524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn,
221524eecc6SJavier Almansa Sobrino 						   capitalise);
222524eecc6SJavier Almansa Sobrino 				break;
223524eecc6SJavier Almansa Sobrino 
224870ce3ddSAntonio Nino Diaz 			default:
225885e2683SClaus Pedersen 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
226885e2683SClaus Pedersen 				CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
227870ce3ddSAntonio Nino Diaz 			}
228870ce3ddSAntonio Nino Diaz 			fmt++;
229870ce3ddSAntonio Nino Diaz 			continue;
230870ce3ddSAntonio Nino Diaz 		}
231870ce3ddSAntonio Nino Diaz 
2327981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
233d5ccb754SAntonio Nino Diaz 
234870ce3ddSAntonio Nino Diaz 		fmt++;
235870ce3ddSAntonio Nino Diaz 	}
236870ce3ddSAntonio Nino Diaz 
23777648689SMadhukar Pappireddy 	if (n > 0U) {
238870ce3ddSAntonio Nino Diaz 		*s = '\0';
23977648689SMadhukar Pappireddy 	}
240870ce3ddSAntonio Nino Diaz 
241d5ccb754SAntonio Nino Diaz 	return (int)chars_printed;
242870ce3ddSAntonio Nino Diaz }
24377648689SMadhukar Pappireddy 
24477648689SMadhukar Pappireddy /*******************************************************************
24577648689SMadhukar Pappireddy  * Reduced snprintf to be used for Trusted firmware.
24677648689SMadhukar Pappireddy  * The following type specifiers are supported:
24777648689SMadhukar Pappireddy  *
24877648689SMadhukar Pappireddy  * %x (or %X) - hexadecimal format
24977648689SMadhukar Pappireddy  * %d or %i - signed decimal format
25077648689SMadhukar Pappireddy  * %s - string format
25177648689SMadhukar Pappireddy  * %u - unsigned decimal format
25277648689SMadhukar Pappireddy  * %p - pointer format
25377648689SMadhukar Pappireddy  *
25477648689SMadhukar Pappireddy  * The following padding specifiers are supported by this print
25577648689SMadhukar Pappireddy  * %0NN - Left-pad the number with 0s (NN is a decimal number)
25677648689SMadhukar Pappireddy  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
25777648689SMadhukar Pappireddy  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
25877648689SMadhukar Pappireddy  *
25977648689SMadhukar Pappireddy  * The function panics on all other formats specifiers.
26077648689SMadhukar Pappireddy  *
26177648689SMadhukar Pappireddy  * It returns the number of characters that would be written if the
26277648689SMadhukar Pappireddy  * buffer was big enough. If it returns a value lower than n, the
26377648689SMadhukar Pappireddy  * whole string has been written.
26477648689SMadhukar Pappireddy  *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)26577648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...)
26677648689SMadhukar Pappireddy {
26777648689SMadhukar Pappireddy 	int count;
26877648689SMadhukar Pappireddy 	va_list all_args;
26977648689SMadhukar Pappireddy 
27077648689SMadhukar Pappireddy 	va_start(all_args, fmt);
27177648689SMadhukar Pappireddy 	count = vsnprintf(s, n, fmt, all_args);
27277648689SMadhukar Pappireddy 	va_end(all_args);
27377648689SMadhukar Pappireddy 
27477648689SMadhukar Pappireddy 	return count;
27577648689SMadhukar Pappireddy }
276