xref: /rk3399_ARM-atf/lib/libc/snprintf.c (revision a211fde940d4dbd8e95e4f352af2a066a4f89f30)
1870ce3ddSAntonio Nino Diaz /*
2*a211fde9SYann 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>
9410c925aSAndre Przywara #include <stdint.h>
10870ce3ddSAntonio Nino Diaz 
1109d40e0eSAntonio Nino Diaz #include <common/debug.h>
1209d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
1309d40e0eSAntonio Nino Diaz 
147981c504SHeyi Guo #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch)	\
157981c504SHeyi Guo 	do {						\
167981c504SHeyi Guo 		if ((chars_printed) < (size)) {		\
177981c504SHeyi Guo 			*(buf) = (ch);			\
187981c504SHeyi Guo 			(buf)++;			\
197981c504SHeyi Guo 		}					\
207981c504SHeyi Guo 		(chars_printed)++;			\
217981c504SHeyi Guo 	} while (false)
227981c504SHeyi Guo 
23870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed,
24870ce3ddSAntonio Nino Diaz 			 const char *str)
25870ce3ddSAntonio Nino Diaz {
26d5ccb754SAntonio Nino Diaz 	while (*str != '\0') {
277981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
28870ce3ddSAntonio Nino Diaz 		str++;
29870ce3ddSAntonio Nino Diaz 	}
30870ce3ddSAntonio Nino Diaz }
31870ce3ddSAntonio Nino Diaz 
32524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
33524eecc6SJavier Almansa Sobrino 			      unsigned long long int unum,
34524eecc6SJavier Almansa Sobrino 			      unsigned int radix, char padc, int padn,
35524eecc6SJavier Almansa Sobrino 			      bool capitalise)
36870ce3ddSAntonio Nino Diaz {
37524eecc6SJavier Almansa Sobrino 	/* Just need enough space to store 64 bit decimal integer */
38524eecc6SJavier Almansa Sobrino 	char num_buf[20];
39d5ccb754SAntonio Nino Diaz 	int i = 0;
40524eecc6SJavier Almansa Sobrino 	int width;
41d5ccb754SAntonio Nino Diaz 	unsigned int rem;
42524eecc6SJavier Almansa Sobrino 	char ascii_a = capitalise ? 'A' : 'a';
43870ce3ddSAntonio Nino Diaz 
44b30dd403SAndre Przywara 	if (radix < 10) {
45*a211fde9SYann Gautier 		ERROR("snprintf: unsupported radix '%u'.", radix);
46b30dd403SAndre Przywara 		plat_panic_handler();
47b30dd403SAndre Przywara 		assert(0); /* Unreachable */
48b30dd403SAndre Przywara 	}
49b30dd403SAndre Przywara 
50870ce3ddSAntonio Nino Diaz 	do {
51524eecc6SJavier Almansa Sobrino 		rem = unum % radix;
52524eecc6SJavier Almansa Sobrino 		if (rem < 10U) {
53524eecc6SJavier Almansa Sobrino 			num_buf[i] = '0' + rem;
54524eecc6SJavier Almansa Sobrino 		} else {
55524eecc6SJavier Almansa Sobrino 			num_buf[i] = ascii_a + (rem - 10U);
56524eecc6SJavier Almansa Sobrino 		}
57524eecc6SJavier Almansa Sobrino 		i++;
58524eecc6SJavier Almansa Sobrino 		unum /= radix;
59d5ccb754SAntonio Nino Diaz 	} while (unum > 0U);
60870ce3ddSAntonio Nino Diaz 
61524eecc6SJavier Almansa Sobrino 	width = i;
62c1f5a092SAndre Przywara 	for (i = padn - width; i > 0; i--) {
63c1f5a092SAndre Przywara 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
64d5ccb754SAntonio Nino Diaz 	}
65c1f5a092SAndre Przywara 	for (i = width; i > 0; i--) {
66c1f5a092SAndre Przywara 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
67524eecc6SJavier Almansa Sobrino 	}
68c1f5a092SAndre Przywara 	for (i = width + padn; i < 0; i++) {
69c1f5a092SAndre Przywara 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
70870ce3ddSAntonio Nino Diaz 	}
71870ce3ddSAntonio Nino Diaz }
72870ce3ddSAntonio Nino Diaz 
73870ce3ddSAntonio Nino Diaz /*******************************************************************
7477648689SMadhukar Pappireddy  * Reduced vsnprintf to be used for Trusted firmware.
75870ce3ddSAntonio Nino Diaz  * The following type specifiers are supported:
76870ce3ddSAntonio Nino Diaz  *
77524eecc6SJavier Almansa Sobrino  * %x (or %X) - hexadecimal format
78870ce3ddSAntonio Nino Diaz  * %d or %i - signed decimal format
79870ce3ddSAntonio Nino Diaz  * %s - string format
80870ce3ddSAntonio Nino Diaz  * %u - unsigned decimal format
81524eecc6SJavier Almansa Sobrino  * %p - pointer format
82524eecc6SJavier Almansa Sobrino  *
83524eecc6SJavier Almansa Sobrino  * The following padding specifiers are supported by this print
84524eecc6SJavier Almansa Sobrino  * %0NN - Left-pad the number with 0s (NN is a decimal number)
85524eecc6SJavier Almansa Sobrino  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
86524eecc6SJavier Almansa Sobrino  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
87870ce3ddSAntonio Nino Diaz  *
88870ce3ddSAntonio Nino Diaz  * The function panics on all other formats specifiers.
89870ce3ddSAntonio Nino Diaz  *
90870ce3ddSAntonio Nino Diaz  * It returns the number of characters that would be written if the
91870ce3ddSAntonio Nino Diaz  * buffer was big enough. If it returns a value lower than n, the
92870ce3ddSAntonio Nino Diaz  * whole string has been written.
93870ce3ddSAntonio Nino Diaz  *******************************************************************/
9477648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
95870ce3ddSAntonio Nino Diaz {
96870ce3ddSAntonio Nino Diaz 	int num;
97524eecc6SJavier Almansa Sobrino 	unsigned long long int unum;
98870ce3ddSAntonio Nino Diaz 	char *str;
99524eecc6SJavier Almansa Sobrino 	char padc;		/* Padding character */
100524eecc6SJavier Almansa Sobrino 	int padn;		/* Number of characters to pad */
101524eecc6SJavier Almansa Sobrino 	bool left;
102524eecc6SJavier Almansa Sobrino 	bool capitalise;
103d5ccb754SAntonio Nino Diaz 	size_t chars_printed = 0U;
104870ce3ddSAntonio Nino Diaz 
105d5ccb754SAntonio Nino Diaz 	if (n == 0U) {
106d5ccb754SAntonio Nino Diaz 		/* There isn't space for anything. */
107d5ccb754SAntonio Nino Diaz 	} else if (n == 1U) {
108870ce3ddSAntonio Nino Diaz 		/* Buffer is too small to actually write anything else. */
109870ce3ddSAntonio Nino Diaz 		*s = '\0';
110d5ccb754SAntonio Nino Diaz 		n = 0U;
111d5ccb754SAntonio Nino Diaz 	} else {
112870ce3ddSAntonio Nino Diaz 		/* Reserve space for the terminator character. */
113870ce3ddSAntonio Nino Diaz 		n--;
114870ce3ddSAntonio Nino Diaz 	}
115870ce3ddSAntonio Nino Diaz 
116d5ccb754SAntonio Nino Diaz 	while (*fmt != '\0') {
117524eecc6SJavier Almansa Sobrino 		left = false;
118524eecc6SJavier Almansa Sobrino 		padc ='\0';
119524eecc6SJavier Almansa Sobrino 		padn = 0;
120524eecc6SJavier Almansa Sobrino 		capitalise = false;
121870ce3ddSAntonio Nino Diaz 
122870ce3ddSAntonio Nino Diaz 		if (*fmt == '%') {
123870ce3ddSAntonio Nino Diaz 			fmt++;
124870ce3ddSAntonio Nino Diaz 			/* Check the format specifier. */
125524eecc6SJavier Almansa Sobrino loop:
126870ce3ddSAntonio Nino Diaz 			switch (*fmt) {
127c6546154SHeyi Guo 			case '%':
1287981c504SHeyi Guo 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
129c6546154SHeyi Guo 				break;
130524eecc6SJavier Almansa Sobrino 			case '0':
131524eecc6SJavier Almansa Sobrino 			case '1':
132524eecc6SJavier Almansa Sobrino 			case '2':
133524eecc6SJavier Almansa Sobrino 			case '3':
134524eecc6SJavier Almansa Sobrino 			case '4':
135524eecc6SJavier Almansa Sobrino 			case '5':
136524eecc6SJavier Almansa Sobrino 			case '6':
137524eecc6SJavier Almansa Sobrino 			case '7':
138524eecc6SJavier Almansa Sobrino 			case '8':
139524eecc6SJavier Almansa Sobrino 			case '9':
140524eecc6SJavier Almansa Sobrino 				padc = (*fmt == '0') ? '0' : ' ';
141524eecc6SJavier Almansa Sobrino 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
142524eecc6SJavier Almansa Sobrino 					padn = (padn * 10) + (*fmt - '0');
143524eecc6SJavier Almansa Sobrino 				}
144524eecc6SJavier Almansa Sobrino 				if (left) {
145524eecc6SJavier Almansa Sobrino 					padn = -padn;
146524eecc6SJavier Almansa Sobrino 				}
147524eecc6SJavier Almansa Sobrino 				goto loop;
148524eecc6SJavier Almansa Sobrino 			case '-':
149524eecc6SJavier Almansa Sobrino 				left = true;
150524eecc6SJavier Almansa Sobrino 				fmt++;
151524eecc6SJavier Almansa Sobrino 				goto loop;
152524eecc6SJavier Almansa Sobrino 
153870ce3ddSAntonio Nino Diaz 			case 'i':
154870ce3ddSAntonio Nino Diaz 			case 'd':
155870ce3ddSAntonio Nino Diaz 				num = va_arg(args, int);
156870ce3ddSAntonio Nino Diaz 
157870ce3ddSAntonio Nino Diaz 				if (num < 0) {
1587981c504SHeyi Guo 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
1597981c504SHeyi Guo 						'-');
160870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)-num;
161870ce3ddSAntonio Nino Diaz 				} else {
162870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)num;
163870ce3ddSAntonio Nino Diaz 				}
164870ce3ddSAntonio Nino Diaz 
165524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
166524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
167870ce3ddSAntonio Nino Diaz 				break;
168870ce3ddSAntonio Nino Diaz 			case 's':
169870ce3ddSAntonio Nino Diaz 				str = va_arg(args, char *);
170870ce3ddSAntonio Nino Diaz 				string_print(&s, n, &chars_printed, str);
171870ce3ddSAntonio Nino Diaz 				break;
172870ce3ddSAntonio Nino Diaz 			case 'u':
173870ce3ddSAntonio Nino Diaz 				unum = va_arg(args, unsigned int);
174524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
175524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
176870ce3ddSAntonio Nino Diaz 				break;
177524eecc6SJavier Almansa Sobrino 			case 'p':
178524eecc6SJavier Almansa Sobrino 				unum = (uintptr_t)va_arg(args, void *);
179524eecc6SJavier Almansa Sobrino 				if (unum > 0U) {
180524eecc6SJavier Almansa Sobrino 					string_print(&s, n, &chars_printed, "0x");
181524eecc6SJavier Almansa Sobrino 					padn -= 2;
182524eecc6SJavier Almansa Sobrino 				}
183524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
184524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn, false);
185524eecc6SJavier Almansa Sobrino 				break;
186524eecc6SJavier Almansa Sobrino 			case 'X':
187524eecc6SJavier Almansa Sobrino 				capitalise = true;
188524eecc6SJavier Almansa Sobrino 			case 'x':
189524eecc6SJavier Almansa Sobrino 				unum = va_arg(args, unsigned int);
190524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
191524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn,
192524eecc6SJavier Almansa Sobrino 						   capitalise);
193524eecc6SJavier Almansa Sobrino 				break;
194524eecc6SJavier Almansa Sobrino 
195870ce3ddSAntonio Nino Diaz 			default:
196870ce3ddSAntonio Nino Diaz 				/* Panic on any other format specifier. */
197870ce3ddSAntonio Nino Diaz 				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
198870ce3ddSAntonio Nino Diaz 				      *fmt);
199870ce3ddSAntonio Nino Diaz 				plat_panic_handler();
200a08a2014SDaniel Boulby 				assert(0); /* Unreachable */
201870ce3ddSAntonio Nino Diaz 			}
202870ce3ddSAntonio Nino Diaz 			fmt++;
203870ce3ddSAntonio Nino Diaz 			continue;
204870ce3ddSAntonio Nino Diaz 		}
205870ce3ddSAntonio Nino Diaz 
2067981c504SHeyi Guo 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
207d5ccb754SAntonio Nino Diaz 
208870ce3ddSAntonio Nino Diaz 		fmt++;
209870ce3ddSAntonio Nino Diaz 	}
210870ce3ddSAntonio Nino Diaz 
21177648689SMadhukar Pappireddy 	if (n > 0U) {
212870ce3ddSAntonio Nino Diaz 		*s = '\0';
21377648689SMadhukar Pappireddy 	}
214870ce3ddSAntonio Nino Diaz 
215d5ccb754SAntonio Nino Diaz 	return (int)chars_printed;
216870ce3ddSAntonio Nino Diaz }
21777648689SMadhukar Pappireddy 
21877648689SMadhukar Pappireddy /*******************************************************************
21977648689SMadhukar Pappireddy  * Reduced snprintf to be used for Trusted firmware.
22077648689SMadhukar Pappireddy  * The following type specifiers are supported:
22177648689SMadhukar Pappireddy  *
22277648689SMadhukar Pappireddy  * %x (or %X) - hexadecimal format
22377648689SMadhukar Pappireddy  * %d or %i - signed decimal format
22477648689SMadhukar Pappireddy  * %s - string format
22577648689SMadhukar Pappireddy  * %u - unsigned decimal format
22677648689SMadhukar Pappireddy  * %p - pointer format
22777648689SMadhukar Pappireddy  *
22877648689SMadhukar Pappireddy  * The following padding specifiers are supported by this print
22977648689SMadhukar Pappireddy  * %0NN - Left-pad the number with 0s (NN is a decimal number)
23077648689SMadhukar Pappireddy  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
23177648689SMadhukar Pappireddy  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
23277648689SMadhukar Pappireddy  *
23377648689SMadhukar Pappireddy  * The function panics on all other formats specifiers.
23477648689SMadhukar Pappireddy  *
23577648689SMadhukar Pappireddy  * It returns the number of characters that would be written if the
23677648689SMadhukar Pappireddy  * buffer was big enough. If it returns a value lower than n, the
23777648689SMadhukar Pappireddy  * whole string has been written.
23877648689SMadhukar Pappireddy  *******************************************************************/
23977648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...)
24077648689SMadhukar Pappireddy {
24177648689SMadhukar Pappireddy 	int count;
24277648689SMadhukar Pappireddy 	va_list all_args;
24377648689SMadhukar Pappireddy 
24477648689SMadhukar Pappireddy 	va_start(all_args, fmt);
24577648689SMadhukar Pappireddy 	count = vsnprintf(s, n, fmt, all_args);
24677648689SMadhukar Pappireddy 	va_end(all_args);
24777648689SMadhukar Pappireddy 
24877648689SMadhukar Pappireddy 	return count;
24977648689SMadhukar Pappireddy }
250