xref: /aosp_15_r20/external/arm-trusted-firmware/lib/libc/snprintf.c (revision 54fd6939e177f8ff529b10183254802c76df6d08)
1*54fd6939SJiyong Park /*
2*54fd6939SJiyong Park  * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
3*54fd6939SJiyong Park  *
4*54fd6939SJiyong Park  * SPDX-License-Identifier: BSD-3-Clause
5*54fd6939SJiyong Park  */
6*54fd6939SJiyong Park 
7*54fd6939SJiyong Park #include <assert.h>
8*54fd6939SJiyong Park #include <stdarg.h>
9*54fd6939SJiyong Park 
10*54fd6939SJiyong Park #include <common/debug.h>
11*54fd6939SJiyong Park #include <plat/common/platform.h>
12*54fd6939SJiyong Park 
13*54fd6939SJiyong Park #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch)	\
14*54fd6939SJiyong Park 	do {						\
15*54fd6939SJiyong Park 		if ((chars_printed) < (size)) {		\
16*54fd6939SJiyong Park 			*(buf) = (ch);			\
17*54fd6939SJiyong Park 			(buf)++;			\
18*54fd6939SJiyong Park 		}					\
19*54fd6939SJiyong Park 		(chars_printed)++;			\
20*54fd6939SJiyong Park 	} while (false)
21*54fd6939SJiyong Park 
string_print(char ** s,size_t n,size_t * chars_printed,const char * str)22*54fd6939SJiyong Park static void string_print(char **s, size_t n, size_t *chars_printed,
23*54fd6939SJiyong Park 			 const char *str)
24*54fd6939SJiyong Park {
25*54fd6939SJiyong Park 	while (*str != '\0') {
26*54fd6939SJiyong Park 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
27*54fd6939SJiyong Park 		str++;
28*54fd6939SJiyong Park 	}
29*54fd6939SJiyong Park }
30*54fd6939SJiyong Park 
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)31*54fd6939SJiyong Park static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
32*54fd6939SJiyong Park 			      unsigned long long int unum,
33*54fd6939SJiyong Park 			      unsigned int radix, char padc, int padn,
34*54fd6939SJiyong Park 			      bool capitalise)
35*54fd6939SJiyong Park {
36*54fd6939SJiyong Park 	/* Just need enough space to store 64 bit decimal integer */
37*54fd6939SJiyong Park 	char num_buf[20];
38*54fd6939SJiyong Park 	int i = 0;
39*54fd6939SJiyong Park 	int width;
40*54fd6939SJiyong Park 	unsigned int rem;
41*54fd6939SJiyong Park 	char ascii_a = capitalise ? 'A' : 'a';
42*54fd6939SJiyong Park 
43*54fd6939SJiyong Park 	do {
44*54fd6939SJiyong Park 		rem = unum % radix;
45*54fd6939SJiyong Park 		if (rem < 10U) {
46*54fd6939SJiyong Park 			num_buf[i] = '0' + rem;
47*54fd6939SJiyong Park 		} else {
48*54fd6939SJiyong Park 			num_buf[i] = ascii_a + (rem - 10U);
49*54fd6939SJiyong Park 		}
50*54fd6939SJiyong Park 		i++;
51*54fd6939SJiyong Park 		unum /= radix;
52*54fd6939SJiyong Park 	} while (unum > 0U);
53*54fd6939SJiyong Park 
54*54fd6939SJiyong Park 	width = i;
55*54fd6939SJiyong Park 	if (padn > width) {
56*54fd6939SJiyong Park 		(*chars_printed) += (size_t)padn;
57*54fd6939SJiyong Park 	} else {
58*54fd6939SJiyong Park 		(*chars_printed) += (size_t)width;
59*54fd6939SJiyong Park 	}
60*54fd6939SJiyong Park 
61*54fd6939SJiyong Park 	if (*chars_printed < n) {
62*54fd6939SJiyong Park 
63*54fd6939SJiyong Park 		if (padn > 0) {
64*54fd6939SJiyong Park 			while (width < padn) {
65*54fd6939SJiyong Park 				*(*s)++ = padc;
66*54fd6939SJiyong Park 				padn--;
67*54fd6939SJiyong Park 			}
68*54fd6939SJiyong Park 		}
69*54fd6939SJiyong Park 
70*54fd6939SJiyong Park 		while (--i >= 0) {
71*54fd6939SJiyong Park 			*(*s)++ = num_buf[i];
72*54fd6939SJiyong Park 		}
73*54fd6939SJiyong Park 
74*54fd6939SJiyong Park 		if (padn < 0) {
75*54fd6939SJiyong Park 			while (width < -padn) {
76*54fd6939SJiyong Park 				*(*s)++ = padc;
77*54fd6939SJiyong Park 				padn++;
78*54fd6939SJiyong Park 			}
79*54fd6939SJiyong Park 		}
80*54fd6939SJiyong Park 	}
81*54fd6939SJiyong Park }
82*54fd6939SJiyong Park 
83*54fd6939SJiyong Park /*******************************************************************
84*54fd6939SJiyong Park  * Reduced vsnprintf to be used for Trusted firmware.
85*54fd6939SJiyong Park  * The following type specifiers are supported:
86*54fd6939SJiyong Park  *
87*54fd6939SJiyong Park  * %x (or %X) - hexadecimal format
88*54fd6939SJiyong Park  * %d or %i - signed decimal format
89*54fd6939SJiyong Park  * %s - string format
90*54fd6939SJiyong Park  * %u - unsigned decimal format
91*54fd6939SJiyong Park  * %p - pointer format
92*54fd6939SJiyong Park  *
93*54fd6939SJiyong Park  * The following padding specifiers are supported by this print
94*54fd6939SJiyong Park  * %0NN - Left-pad the number with 0s (NN is a decimal number)
95*54fd6939SJiyong Park  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
96*54fd6939SJiyong Park  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
97*54fd6939SJiyong Park  *
98*54fd6939SJiyong Park  * The function panics on all other formats specifiers.
99*54fd6939SJiyong Park  *
100*54fd6939SJiyong Park  * It returns the number of characters that would be written if the
101*54fd6939SJiyong Park  * buffer was big enough. If it returns a value lower than n, the
102*54fd6939SJiyong Park  * whole string has been written.
103*54fd6939SJiyong Park  *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)104*54fd6939SJiyong Park int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
105*54fd6939SJiyong Park {
106*54fd6939SJiyong Park 	int num;
107*54fd6939SJiyong Park 	unsigned long long int unum;
108*54fd6939SJiyong Park 	char *str;
109*54fd6939SJiyong Park 	char padc;		/* Padding character */
110*54fd6939SJiyong Park 	int padn;		/* Number of characters to pad */
111*54fd6939SJiyong Park 	bool left;
112*54fd6939SJiyong Park 	bool capitalise;
113*54fd6939SJiyong Park 	size_t chars_printed = 0U;
114*54fd6939SJiyong Park 
115*54fd6939SJiyong Park 	if (n == 0U) {
116*54fd6939SJiyong Park 		/* There isn't space for anything. */
117*54fd6939SJiyong Park 	} else if (n == 1U) {
118*54fd6939SJiyong Park 		/* Buffer is too small to actually write anything else. */
119*54fd6939SJiyong Park 		*s = '\0';
120*54fd6939SJiyong Park 		n = 0U;
121*54fd6939SJiyong Park 	} else {
122*54fd6939SJiyong Park 		/* Reserve space for the terminator character. */
123*54fd6939SJiyong Park 		n--;
124*54fd6939SJiyong Park 	}
125*54fd6939SJiyong Park 
126*54fd6939SJiyong Park 	while (*fmt != '\0') {
127*54fd6939SJiyong Park 		left = false;
128*54fd6939SJiyong Park 		padc ='\0';
129*54fd6939SJiyong Park 		padn = 0;
130*54fd6939SJiyong Park 		capitalise = false;
131*54fd6939SJiyong Park 
132*54fd6939SJiyong Park 		if (*fmt == '%') {
133*54fd6939SJiyong Park 			fmt++;
134*54fd6939SJiyong Park 			/* Check the format specifier. */
135*54fd6939SJiyong Park loop:
136*54fd6939SJiyong Park 			switch (*fmt) {
137*54fd6939SJiyong Park 			case '%':
138*54fd6939SJiyong Park 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
139*54fd6939SJiyong Park 				break;
140*54fd6939SJiyong Park 			case '0':
141*54fd6939SJiyong Park 			case '1':
142*54fd6939SJiyong Park 			case '2':
143*54fd6939SJiyong Park 			case '3':
144*54fd6939SJiyong Park 			case '4':
145*54fd6939SJiyong Park 			case '5':
146*54fd6939SJiyong Park 			case '6':
147*54fd6939SJiyong Park 			case '7':
148*54fd6939SJiyong Park 			case '8':
149*54fd6939SJiyong Park 			case '9':
150*54fd6939SJiyong Park 				padc = (*fmt == '0') ? '0' : ' ';
151*54fd6939SJiyong Park 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
152*54fd6939SJiyong Park 					padn = (padn * 10) + (*fmt - '0');
153*54fd6939SJiyong Park 				}
154*54fd6939SJiyong Park 				if (left) {
155*54fd6939SJiyong Park 					padn = -padn;
156*54fd6939SJiyong Park 				}
157*54fd6939SJiyong Park 				goto loop;
158*54fd6939SJiyong Park 			case '-':
159*54fd6939SJiyong Park 				left = true;
160*54fd6939SJiyong Park 				fmt++;
161*54fd6939SJiyong Park 				goto loop;
162*54fd6939SJiyong Park 
163*54fd6939SJiyong Park 			case 'i':
164*54fd6939SJiyong Park 			case 'd':
165*54fd6939SJiyong Park 				num = va_arg(args, int);
166*54fd6939SJiyong Park 
167*54fd6939SJiyong Park 				if (num < 0) {
168*54fd6939SJiyong Park 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
169*54fd6939SJiyong Park 						'-');
170*54fd6939SJiyong Park 					unum = (unsigned int)-num;
171*54fd6939SJiyong Park 				} else {
172*54fd6939SJiyong Park 					unum = (unsigned int)num;
173*54fd6939SJiyong Park 				}
174*54fd6939SJiyong Park 
175*54fd6939SJiyong Park 				unsigned_num_print(&s, n, &chars_printed,
176*54fd6939SJiyong Park 						   unum, 10, padc, padn, false);
177*54fd6939SJiyong Park 				break;
178*54fd6939SJiyong Park 			case 's':
179*54fd6939SJiyong Park 				str = va_arg(args, char *);
180*54fd6939SJiyong Park 				string_print(&s, n, &chars_printed, str);
181*54fd6939SJiyong Park 				break;
182*54fd6939SJiyong Park 			case 'u':
183*54fd6939SJiyong Park 				unum = va_arg(args, unsigned int);
184*54fd6939SJiyong Park 				unsigned_num_print(&s, n, &chars_printed,
185*54fd6939SJiyong Park 						   unum, 10, padc, padn, false);
186*54fd6939SJiyong Park 				break;
187*54fd6939SJiyong Park 			case 'p':
188*54fd6939SJiyong Park 				unum = (uintptr_t)va_arg(args, void *);
189*54fd6939SJiyong Park 				if (unum > 0U) {
190*54fd6939SJiyong Park 					string_print(&s, n, &chars_printed, "0x");
191*54fd6939SJiyong Park 					padn -= 2;
192*54fd6939SJiyong Park 				}
193*54fd6939SJiyong Park 				unsigned_num_print(&s, n, &chars_printed,
194*54fd6939SJiyong Park 						   unum, 16, padc, padn, false);
195*54fd6939SJiyong Park 				break;
196*54fd6939SJiyong Park 			case 'X':
197*54fd6939SJiyong Park 				capitalise = true;
198*54fd6939SJiyong Park 			case 'x':
199*54fd6939SJiyong Park 				unum = va_arg(args, unsigned int);
200*54fd6939SJiyong Park 				unsigned_num_print(&s, n, &chars_printed,
201*54fd6939SJiyong Park 						   unum, 16, padc, padn,
202*54fd6939SJiyong Park 						   capitalise);
203*54fd6939SJiyong Park 				break;
204*54fd6939SJiyong Park 
205*54fd6939SJiyong Park 			default:
206*54fd6939SJiyong Park 				/* Panic on any other format specifier. */
207*54fd6939SJiyong Park 				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
208*54fd6939SJiyong Park 				      *fmt);
209*54fd6939SJiyong Park 				plat_panic_handler();
210*54fd6939SJiyong Park 				assert(0); /* Unreachable */
211*54fd6939SJiyong Park 			}
212*54fd6939SJiyong Park 			fmt++;
213*54fd6939SJiyong Park 			continue;
214*54fd6939SJiyong Park 		}
215*54fd6939SJiyong Park 
216*54fd6939SJiyong Park 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
217*54fd6939SJiyong Park 
218*54fd6939SJiyong Park 		fmt++;
219*54fd6939SJiyong Park 	}
220*54fd6939SJiyong Park 
221*54fd6939SJiyong Park 	if (n > 0U) {
222*54fd6939SJiyong Park 		*s = '\0';
223*54fd6939SJiyong Park 	}
224*54fd6939SJiyong Park 
225*54fd6939SJiyong Park 	return (int)chars_printed;
226*54fd6939SJiyong Park }
227*54fd6939SJiyong Park 
228*54fd6939SJiyong Park /*******************************************************************
229*54fd6939SJiyong Park  * Reduced snprintf to be used for Trusted firmware.
230*54fd6939SJiyong Park  * The following type specifiers are supported:
231*54fd6939SJiyong Park  *
232*54fd6939SJiyong Park  * %x (or %X) - hexadecimal format
233*54fd6939SJiyong Park  * %d or %i - signed decimal format
234*54fd6939SJiyong Park  * %s - string format
235*54fd6939SJiyong Park  * %u - unsigned decimal format
236*54fd6939SJiyong Park  * %p - pointer format
237*54fd6939SJiyong Park  *
238*54fd6939SJiyong Park  * The following padding specifiers are supported by this print
239*54fd6939SJiyong Park  * %0NN - Left-pad the number with 0s (NN is a decimal number)
240*54fd6939SJiyong Park  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
241*54fd6939SJiyong Park  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
242*54fd6939SJiyong Park  *
243*54fd6939SJiyong Park  * The function panics on all other formats specifiers.
244*54fd6939SJiyong Park  *
245*54fd6939SJiyong Park  * It returns the number of characters that would be written if the
246*54fd6939SJiyong Park  * buffer was big enough. If it returns a value lower than n, the
247*54fd6939SJiyong Park  * whole string has been written.
248*54fd6939SJiyong Park  *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)249*54fd6939SJiyong Park int snprintf(char *s, size_t n, const char *fmt, ...)
250*54fd6939SJiyong Park {
251*54fd6939SJiyong Park 	int count;
252*54fd6939SJiyong Park 	va_list all_args;
253*54fd6939SJiyong Park 
254*54fd6939SJiyong Park 	va_start(all_args, fmt);
255*54fd6939SJiyong Park 	count = vsnprintf(s, n, fmt, all_args);
256*54fd6939SJiyong Park 	va_end(all_args);
257*54fd6939SJiyong Park 
258*54fd6939SJiyong Park 	return count;
259*54fd6939SJiyong Park }
260