xref: /rk3399_ARM-atf/lib/libc/snprintf.c (revision 7981c5043b3b77b87847148ea0df81e9ec59aa56)
1870ce3ddSAntonio Nino Diaz /*
2c6546154SHeyi Guo  * Copyright (c) 2017-2021, 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>
9870ce3ddSAntonio Nino Diaz 
1009d40e0eSAntonio Nino Diaz #include <common/debug.h>
1109d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
1209d40e0eSAntonio Nino Diaz 
13*7981c504SHeyi Guo #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch)	\
14*7981c504SHeyi Guo 	do {						\
15*7981c504SHeyi Guo 		if ((chars_printed) < (size)) {		\
16*7981c504SHeyi Guo 			*(buf) = (ch);			\
17*7981c504SHeyi Guo 			(buf)++;			\
18*7981c504SHeyi Guo 		}					\
19*7981c504SHeyi Guo 		(chars_printed)++;			\
20*7981c504SHeyi Guo 	} while (false)
21*7981c504SHeyi Guo 
22870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed,
23870ce3ddSAntonio Nino Diaz 			 const char *str)
24870ce3ddSAntonio Nino Diaz {
25d5ccb754SAntonio Nino Diaz 	while (*str != '\0') {
26*7981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
27870ce3ddSAntonio Nino Diaz 		str++;
28870ce3ddSAntonio Nino Diaz 	}
29870ce3ddSAntonio Nino Diaz }
30870ce3ddSAntonio Nino Diaz 
31524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
32524eecc6SJavier Almansa Sobrino 			      unsigned long long int unum,
33524eecc6SJavier Almansa Sobrino 			      unsigned int radix, char padc, int padn,
34524eecc6SJavier Almansa Sobrino 			      bool capitalise)
35870ce3ddSAntonio Nino Diaz {
36524eecc6SJavier Almansa Sobrino 	/* Just need enough space to store 64 bit decimal integer */
37524eecc6SJavier Almansa Sobrino 	char num_buf[20];
38d5ccb754SAntonio Nino Diaz 	int i = 0;
39524eecc6SJavier Almansa Sobrino 	int width;
40d5ccb754SAntonio Nino Diaz 	unsigned int rem;
41524eecc6SJavier Almansa Sobrino 	char ascii_a = capitalise ? 'A' : 'a';
42870ce3ddSAntonio Nino Diaz 
43870ce3ddSAntonio Nino Diaz 	do {
44524eecc6SJavier Almansa Sobrino 		rem = unum % radix;
45524eecc6SJavier Almansa Sobrino 		if (rem < 10U) {
46524eecc6SJavier Almansa Sobrino 			num_buf[i] = '0' + rem;
47524eecc6SJavier Almansa Sobrino 		} else {
48524eecc6SJavier Almansa Sobrino 			num_buf[i] = ascii_a + (rem - 10U);
49524eecc6SJavier Almansa Sobrino 		}
50524eecc6SJavier Almansa Sobrino 		i++;
51524eecc6SJavier Almansa Sobrino 		unum /= radix;
52d5ccb754SAntonio Nino Diaz 	} while (unum > 0U);
53870ce3ddSAntonio Nino Diaz 
54524eecc6SJavier Almansa Sobrino 	width = i;
55524eecc6SJavier Almansa Sobrino 	if (padn > width) {
56524eecc6SJavier Almansa Sobrino 		(*chars_printed) += (size_t)padn;
57524eecc6SJavier Almansa Sobrino 	} else {
58524eecc6SJavier Almansa Sobrino 		(*chars_printed) += (size_t)width;
59d5ccb754SAntonio Nino Diaz 	}
60d5ccb754SAntonio Nino Diaz 
61524eecc6SJavier Almansa Sobrino 	if (*chars_printed < n) {
62524eecc6SJavier Almansa Sobrino 
63524eecc6SJavier Almansa Sobrino 		if (padn > 0) {
64524eecc6SJavier Almansa Sobrino 			while (width < padn) {
65524eecc6SJavier Almansa Sobrino 				*(*s)++ = padc;
66524eecc6SJavier Almansa Sobrino 				padn--;
67524eecc6SJavier Almansa Sobrino 			}
68524eecc6SJavier Almansa Sobrino 		}
69524eecc6SJavier Almansa Sobrino 
70524eecc6SJavier Almansa Sobrino 		while (--i >= 0) {
71524eecc6SJavier Almansa Sobrino 			*(*s)++ = num_buf[i];
72524eecc6SJavier Almansa Sobrino 		}
73524eecc6SJavier Almansa Sobrino 
74524eecc6SJavier Almansa Sobrino 		if (padn < 0) {
75524eecc6SJavier Almansa Sobrino 			while (width < -padn) {
76524eecc6SJavier Almansa Sobrino 				*(*s)++ = padc;
77524eecc6SJavier Almansa Sobrino 				padn++;
78524eecc6SJavier Almansa Sobrino 			}
79524eecc6SJavier Almansa Sobrino 		}
80870ce3ddSAntonio Nino Diaz 	}
81870ce3ddSAntonio Nino Diaz }
82870ce3ddSAntonio Nino Diaz 
83870ce3ddSAntonio Nino Diaz /*******************************************************************
8477648689SMadhukar Pappireddy  * Reduced vsnprintf to be used for Trusted firmware.
85870ce3ddSAntonio Nino Diaz  * The following type specifiers are supported:
86870ce3ddSAntonio Nino Diaz  *
87524eecc6SJavier Almansa Sobrino  * %x (or %X) - hexadecimal format
88870ce3ddSAntonio Nino Diaz  * %d or %i - signed decimal format
89870ce3ddSAntonio Nino Diaz  * %s - string format
90870ce3ddSAntonio Nino Diaz  * %u - unsigned decimal format
91524eecc6SJavier Almansa Sobrino  * %p - pointer format
92524eecc6SJavier Almansa Sobrino  *
93524eecc6SJavier Almansa Sobrino  * The following padding specifiers are supported by this print
94524eecc6SJavier Almansa Sobrino  * %0NN - Left-pad the number with 0s (NN is a decimal number)
95524eecc6SJavier Almansa Sobrino  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
96524eecc6SJavier Almansa Sobrino  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
97870ce3ddSAntonio Nino Diaz  *
98870ce3ddSAntonio Nino Diaz  * The function panics on all other formats specifiers.
99870ce3ddSAntonio Nino Diaz  *
100870ce3ddSAntonio Nino Diaz  * It returns the number of characters that would be written if the
101870ce3ddSAntonio Nino Diaz  * buffer was big enough. If it returns a value lower than n, the
102870ce3ddSAntonio Nino Diaz  * whole string has been written.
103870ce3ddSAntonio Nino Diaz  *******************************************************************/
10477648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
105870ce3ddSAntonio Nino Diaz {
106870ce3ddSAntonio Nino Diaz 	int num;
107524eecc6SJavier Almansa Sobrino 	unsigned long long int unum;
108870ce3ddSAntonio Nino Diaz 	char *str;
109524eecc6SJavier Almansa Sobrino 	char padc;		/* Padding character */
110524eecc6SJavier Almansa Sobrino 	int padn;		/* Number of characters to pad */
111524eecc6SJavier Almansa Sobrino 	bool left;
112524eecc6SJavier Almansa Sobrino 	bool capitalise;
113d5ccb754SAntonio Nino Diaz 	size_t chars_printed = 0U;
114870ce3ddSAntonio Nino Diaz 
115d5ccb754SAntonio Nino Diaz 	if (n == 0U) {
116d5ccb754SAntonio Nino Diaz 		/* There isn't space for anything. */
117d5ccb754SAntonio Nino Diaz 	} else if (n == 1U) {
118870ce3ddSAntonio Nino Diaz 		/* Buffer is too small to actually write anything else. */
119870ce3ddSAntonio Nino Diaz 		*s = '\0';
120d5ccb754SAntonio Nino Diaz 		n = 0U;
121d5ccb754SAntonio Nino Diaz 	} else {
122870ce3ddSAntonio Nino Diaz 		/* Reserve space for the terminator character. */
123870ce3ddSAntonio Nino Diaz 		n--;
124870ce3ddSAntonio Nino Diaz 	}
125870ce3ddSAntonio Nino Diaz 
126d5ccb754SAntonio Nino Diaz 	while (*fmt != '\0') {
127524eecc6SJavier Almansa Sobrino 		left = false;
128524eecc6SJavier Almansa Sobrino 		padc ='\0';
129524eecc6SJavier Almansa Sobrino 		padn = 0;
130524eecc6SJavier Almansa Sobrino 		capitalise = false;
131870ce3ddSAntonio Nino Diaz 
132870ce3ddSAntonio Nino Diaz 		if (*fmt == '%') {
133870ce3ddSAntonio Nino Diaz 			fmt++;
134870ce3ddSAntonio Nino Diaz 			/* Check the format specifier. */
135524eecc6SJavier Almansa Sobrino loop:
136870ce3ddSAntonio Nino Diaz 			switch (*fmt) {
137c6546154SHeyi Guo 			case '%':
138*7981c504SHeyi Guo 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
139c6546154SHeyi Guo 				break;
140524eecc6SJavier Almansa Sobrino 			case '0':
141524eecc6SJavier Almansa Sobrino 			case '1':
142524eecc6SJavier Almansa Sobrino 			case '2':
143524eecc6SJavier Almansa Sobrino 			case '3':
144524eecc6SJavier Almansa Sobrino 			case '4':
145524eecc6SJavier Almansa Sobrino 			case '5':
146524eecc6SJavier Almansa Sobrino 			case '6':
147524eecc6SJavier Almansa Sobrino 			case '7':
148524eecc6SJavier Almansa Sobrino 			case '8':
149524eecc6SJavier Almansa Sobrino 			case '9':
150524eecc6SJavier Almansa Sobrino 				padc = (*fmt == '0') ? '0' : ' ';
151524eecc6SJavier Almansa Sobrino 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
152524eecc6SJavier Almansa Sobrino 					padn = (padn * 10) + (*fmt - '0');
153524eecc6SJavier Almansa Sobrino 				}
154524eecc6SJavier Almansa Sobrino 				if (left) {
155524eecc6SJavier Almansa Sobrino 					padn = -padn;
156524eecc6SJavier Almansa Sobrino 				}
157524eecc6SJavier Almansa Sobrino 				goto loop;
158524eecc6SJavier Almansa Sobrino 			case '-':
159524eecc6SJavier Almansa Sobrino 				left = true;
160524eecc6SJavier Almansa Sobrino 				fmt++;
161524eecc6SJavier Almansa Sobrino 				goto loop;
162524eecc6SJavier Almansa Sobrino 
163870ce3ddSAntonio Nino Diaz 			case 'i':
164870ce3ddSAntonio Nino Diaz 			case 'd':
165870ce3ddSAntonio Nino Diaz 				num = va_arg(args, int);
166870ce3ddSAntonio Nino Diaz 
167870ce3ddSAntonio Nino Diaz 				if (num < 0) {
168*7981c504SHeyi Guo 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
169*7981c504SHeyi Guo 						'-');
170870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)-num;
171870ce3ddSAntonio Nino Diaz 				} else {
172870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)num;
173870ce3ddSAntonio Nino Diaz 				}
174870ce3ddSAntonio Nino Diaz 
175524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
176524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
177870ce3ddSAntonio Nino Diaz 				break;
178870ce3ddSAntonio Nino Diaz 			case 's':
179870ce3ddSAntonio Nino Diaz 				str = va_arg(args, char *);
180870ce3ddSAntonio Nino Diaz 				string_print(&s, n, &chars_printed, str);
181870ce3ddSAntonio Nino Diaz 				break;
182870ce3ddSAntonio Nino Diaz 			case 'u':
183870ce3ddSAntonio Nino Diaz 				unum = va_arg(args, unsigned int);
184524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
185524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
186870ce3ddSAntonio Nino Diaz 				break;
187524eecc6SJavier Almansa Sobrino 			case 'p':
188524eecc6SJavier Almansa Sobrino 				unum = (uintptr_t)va_arg(args, void *);
189524eecc6SJavier Almansa Sobrino 				if (unum > 0U) {
190524eecc6SJavier Almansa Sobrino 					string_print(&s, n, &chars_printed, "0x");
191524eecc6SJavier Almansa Sobrino 					padn -= 2;
192524eecc6SJavier Almansa Sobrino 				}
193524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
194524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn, false);
195524eecc6SJavier Almansa Sobrino 				break;
196524eecc6SJavier Almansa Sobrino 			case 'X':
197524eecc6SJavier Almansa Sobrino 				capitalise = true;
198524eecc6SJavier Almansa Sobrino 			case 'x':
199524eecc6SJavier Almansa Sobrino 				unum = va_arg(args, unsigned int);
200524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
201524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn,
202524eecc6SJavier Almansa Sobrino 						   capitalise);
203524eecc6SJavier Almansa Sobrino 				break;
204524eecc6SJavier Almansa Sobrino 
205870ce3ddSAntonio Nino Diaz 			default:
206870ce3ddSAntonio Nino Diaz 				/* Panic on any other format specifier. */
207870ce3ddSAntonio Nino Diaz 				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
208870ce3ddSAntonio Nino Diaz 				      *fmt);
209870ce3ddSAntonio Nino Diaz 				plat_panic_handler();
210a08a2014SDaniel Boulby 				assert(0); /* Unreachable */
211870ce3ddSAntonio Nino Diaz 			}
212870ce3ddSAntonio Nino Diaz 			fmt++;
213870ce3ddSAntonio Nino Diaz 			continue;
214870ce3ddSAntonio Nino Diaz 		}
215870ce3ddSAntonio Nino Diaz 
216*7981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
217d5ccb754SAntonio Nino Diaz 
218870ce3ddSAntonio Nino Diaz 		fmt++;
219870ce3ddSAntonio Nino Diaz 	}
220870ce3ddSAntonio Nino Diaz 
22177648689SMadhukar Pappireddy 	if (n > 0U) {
222870ce3ddSAntonio Nino Diaz 		*s = '\0';
22377648689SMadhukar Pappireddy 	}
224870ce3ddSAntonio Nino Diaz 
225d5ccb754SAntonio Nino Diaz 	return (int)chars_printed;
226870ce3ddSAntonio Nino Diaz }
22777648689SMadhukar Pappireddy 
22877648689SMadhukar Pappireddy /*******************************************************************
22977648689SMadhukar Pappireddy  * Reduced snprintf to be used for Trusted firmware.
23077648689SMadhukar Pappireddy  * The following type specifiers are supported:
23177648689SMadhukar Pappireddy  *
23277648689SMadhukar Pappireddy  * %x (or %X) - hexadecimal format
23377648689SMadhukar Pappireddy  * %d or %i - signed decimal format
23477648689SMadhukar Pappireddy  * %s - string format
23577648689SMadhukar Pappireddy  * %u - unsigned decimal format
23677648689SMadhukar Pappireddy  * %p - pointer format
23777648689SMadhukar Pappireddy  *
23877648689SMadhukar Pappireddy  * The following padding specifiers are supported by this print
23977648689SMadhukar Pappireddy  * %0NN - Left-pad the number with 0s (NN is a decimal number)
24077648689SMadhukar Pappireddy  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
24177648689SMadhukar Pappireddy  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
24277648689SMadhukar Pappireddy  *
24377648689SMadhukar Pappireddy  * The function panics on all other formats specifiers.
24477648689SMadhukar Pappireddy  *
24577648689SMadhukar Pappireddy  * It returns the number of characters that would be written if the
24677648689SMadhukar Pappireddy  * buffer was big enough. If it returns a value lower than n, the
24777648689SMadhukar Pappireddy  * whole string has been written.
24877648689SMadhukar Pappireddy  *******************************************************************/
24977648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...)
25077648689SMadhukar Pappireddy {
25177648689SMadhukar Pappireddy 	int count;
25277648689SMadhukar Pappireddy 	va_list all_args;
25377648689SMadhukar Pappireddy 
25477648689SMadhukar Pappireddy 	va_start(all_args, fmt);
25577648689SMadhukar Pappireddy 	count = vsnprintf(s, n, fmt, all_args);
25677648689SMadhukar Pappireddy 	va_end(all_args);
25777648689SMadhukar Pappireddy 
25877648689SMadhukar Pappireddy 	return count;
25977648689SMadhukar Pappireddy }
260