xref: /rk3399_ARM-atf/lib/libc/snprintf.c (revision 524eecc6a2a279d685755d5b39557ebcd78bb2cf)
1870ce3ddSAntonio Nino Diaz /*
2*524eecc6SJavier Almansa Sobrino  * Copyright (c) 2017-2020, 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 
13870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed,
14870ce3ddSAntonio Nino Diaz 			 const char *str)
15870ce3ddSAntonio Nino Diaz {
16d5ccb754SAntonio Nino Diaz 	while (*str != '\0') {
17d5ccb754SAntonio Nino Diaz 		if (*chars_printed < n) {
18d5ccb754SAntonio Nino Diaz 			*(*s) = *str;
19d5ccb754SAntonio Nino Diaz 			(*s)++;
20d5ccb754SAntonio Nino Diaz 		}
21d5ccb754SAntonio Nino Diaz 
22870ce3ddSAntonio Nino Diaz 		(*chars_printed)++;
23870ce3ddSAntonio Nino Diaz 		str++;
24870ce3ddSAntonio Nino Diaz 	}
25870ce3ddSAntonio Nino Diaz }
26870ce3ddSAntonio Nino Diaz 
27*524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
28*524eecc6SJavier Almansa Sobrino 			      unsigned long long int unum,
29*524eecc6SJavier Almansa Sobrino 			      unsigned int radix, char padc, int padn,
30*524eecc6SJavier Almansa Sobrino 			      bool capitalise)
31870ce3ddSAntonio Nino Diaz {
32*524eecc6SJavier Almansa Sobrino 	/* Just need enough space to store 64 bit decimal integer */
33*524eecc6SJavier Almansa Sobrino 	char num_buf[20];
34d5ccb754SAntonio Nino Diaz 	int i = 0;
35*524eecc6SJavier Almansa Sobrino 	int width;
36d5ccb754SAntonio Nino Diaz 	unsigned int rem;
37*524eecc6SJavier Almansa Sobrino 	char ascii_a = capitalise ? 'A' : 'a';
38870ce3ddSAntonio Nino Diaz 
39870ce3ddSAntonio Nino Diaz 	do {
40*524eecc6SJavier Almansa Sobrino 		rem = unum % radix;
41*524eecc6SJavier Almansa Sobrino 		if (rem < 10U) {
42*524eecc6SJavier Almansa Sobrino 			num_buf[i] = '0' + rem;
43*524eecc6SJavier Almansa Sobrino 		} else {
44*524eecc6SJavier Almansa Sobrino 			num_buf[i] = ascii_a + (rem - 10U);
45*524eecc6SJavier Almansa Sobrino 		}
46*524eecc6SJavier Almansa Sobrino 		i++;
47*524eecc6SJavier Almansa Sobrino 		unum /= radix;
48d5ccb754SAntonio Nino Diaz 	} while (unum > 0U);
49870ce3ddSAntonio Nino Diaz 
50*524eecc6SJavier Almansa Sobrino 	width = i;
51*524eecc6SJavier Almansa Sobrino 	if (padn > width) {
52*524eecc6SJavier Almansa Sobrino 		(*chars_printed) += (size_t)padn;
53*524eecc6SJavier Almansa Sobrino 	} else {
54*524eecc6SJavier Almansa Sobrino 		(*chars_printed) += (size_t)width;
55d5ccb754SAntonio Nino Diaz 	}
56d5ccb754SAntonio Nino Diaz 
57*524eecc6SJavier Almansa Sobrino 	if (*chars_printed < n) {
58*524eecc6SJavier Almansa Sobrino 
59*524eecc6SJavier Almansa Sobrino 		if (padn > 0) {
60*524eecc6SJavier Almansa Sobrino 			while (width < padn) {
61*524eecc6SJavier Almansa Sobrino 				*(*s)++ = padc;
62*524eecc6SJavier Almansa Sobrino 				padn--;
63*524eecc6SJavier Almansa Sobrino 			}
64*524eecc6SJavier Almansa Sobrino 		}
65*524eecc6SJavier Almansa Sobrino 
66*524eecc6SJavier Almansa Sobrino 		while (--i >= 0) {
67*524eecc6SJavier Almansa Sobrino 			*(*s)++ = num_buf[i];
68*524eecc6SJavier Almansa Sobrino 		}
69*524eecc6SJavier Almansa Sobrino 
70*524eecc6SJavier Almansa Sobrino 		if (padn < 0) {
71*524eecc6SJavier Almansa Sobrino 			while (width < -padn) {
72*524eecc6SJavier Almansa Sobrino 				*(*s)++ = padc;
73*524eecc6SJavier Almansa Sobrino 				padn++;
74*524eecc6SJavier Almansa Sobrino 			}
75*524eecc6SJavier Almansa Sobrino 		}
76870ce3ddSAntonio Nino Diaz 	}
77870ce3ddSAntonio Nino Diaz }
78870ce3ddSAntonio Nino Diaz 
79870ce3ddSAntonio Nino Diaz /*******************************************************************
80870ce3ddSAntonio Nino Diaz  * Reduced snprintf to be used for Trusted firmware.
81870ce3ddSAntonio Nino Diaz  * The following type specifiers are supported:
82870ce3ddSAntonio Nino Diaz  *
83*524eecc6SJavier Almansa Sobrino  * %x (or %X) - hexadecimal format
84870ce3ddSAntonio Nino Diaz  * %d or %i - signed decimal format
85870ce3ddSAntonio Nino Diaz  * %s - string format
86870ce3ddSAntonio Nino Diaz  * %u - unsigned decimal format
87*524eecc6SJavier Almansa Sobrino  * %p - pointer format
88*524eecc6SJavier Almansa Sobrino  *
89*524eecc6SJavier Almansa Sobrino  * The following padding specifiers are supported by this print
90*524eecc6SJavier Almansa Sobrino  * %0NN - Left-pad the number with 0s (NN is a decimal number)
91*524eecc6SJavier Almansa Sobrino  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
92*524eecc6SJavier Almansa Sobrino  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
93870ce3ddSAntonio Nino Diaz  *
94870ce3ddSAntonio Nino Diaz  * The function panics on all other formats specifiers.
95870ce3ddSAntonio Nino Diaz  *
96870ce3ddSAntonio Nino Diaz  * It returns the number of characters that would be written if the
97870ce3ddSAntonio Nino Diaz  * buffer was big enough. If it returns a value lower than n, the
98870ce3ddSAntonio Nino Diaz  * whole string has been written.
99870ce3ddSAntonio Nino Diaz  *******************************************************************/
100870ce3ddSAntonio Nino Diaz int snprintf(char *s, size_t n, const char *fmt, ...)
101870ce3ddSAntonio Nino Diaz {
102870ce3ddSAntonio Nino Diaz 	va_list args;
103870ce3ddSAntonio Nino Diaz 	int num;
104*524eecc6SJavier Almansa Sobrino 	unsigned long long int unum;
105870ce3ddSAntonio Nino Diaz 	char *str;
106*524eecc6SJavier Almansa Sobrino 	char padc;		/* Padding character */
107*524eecc6SJavier Almansa Sobrino 	int padn;		/* Number of characters to pad */
108*524eecc6SJavier Almansa Sobrino 	bool left;
109*524eecc6SJavier Almansa Sobrino 	bool capitalise;
110d5ccb754SAntonio Nino Diaz 	size_t chars_printed = 0U;
111870ce3ddSAntonio Nino Diaz 
112d5ccb754SAntonio Nino Diaz 	if (n == 0U) {
113d5ccb754SAntonio Nino Diaz 		/* There isn't space for anything. */
114d5ccb754SAntonio Nino Diaz 	} else if (n == 1U) {
115870ce3ddSAntonio Nino Diaz 		/* Buffer is too small to actually write anything else. */
116870ce3ddSAntonio Nino Diaz 		*s = '\0';
117d5ccb754SAntonio Nino Diaz 		n = 0U;
118d5ccb754SAntonio Nino Diaz 	} else {
119870ce3ddSAntonio Nino Diaz 		/* Reserve space for the terminator character. */
120870ce3ddSAntonio Nino Diaz 		n--;
121870ce3ddSAntonio Nino Diaz 	}
122870ce3ddSAntonio Nino Diaz 
123870ce3ddSAntonio Nino Diaz 	va_start(args, fmt);
124d5ccb754SAntonio Nino Diaz 	while (*fmt != '\0') {
125*524eecc6SJavier Almansa Sobrino 		left = false;
126*524eecc6SJavier Almansa Sobrino 		padc ='\0';
127*524eecc6SJavier Almansa Sobrino 		padn = 0;
128*524eecc6SJavier Almansa Sobrino 		capitalise = false;
129870ce3ddSAntonio Nino Diaz 
130870ce3ddSAntonio Nino Diaz 		if (*fmt == '%') {
131870ce3ddSAntonio Nino Diaz 			fmt++;
132870ce3ddSAntonio Nino Diaz 			/* Check the format specifier. */
133*524eecc6SJavier Almansa Sobrino loop:
134870ce3ddSAntonio Nino Diaz 			switch (*fmt) {
135*524eecc6SJavier Almansa Sobrino 			case '0':
136*524eecc6SJavier Almansa Sobrino 			case '1':
137*524eecc6SJavier Almansa Sobrino 			case '2':
138*524eecc6SJavier Almansa Sobrino 			case '3':
139*524eecc6SJavier Almansa Sobrino 			case '4':
140*524eecc6SJavier Almansa Sobrino 			case '5':
141*524eecc6SJavier Almansa Sobrino 			case '6':
142*524eecc6SJavier Almansa Sobrino 			case '7':
143*524eecc6SJavier Almansa Sobrino 			case '8':
144*524eecc6SJavier Almansa Sobrino 			case '9':
145*524eecc6SJavier Almansa Sobrino 				padc = (*fmt == '0') ? '0' : ' ';
146*524eecc6SJavier Almansa Sobrino 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
147*524eecc6SJavier Almansa Sobrino 					padn = (padn * 10) + (*fmt - '0');
148*524eecc6SJavier Almansa Sobrino 				}
149*524eecc6SJavier Almansa Sobrino 				if (left) {
150*524eecc6SJavier Almansa Sobrino 					padn = -padn;
151*524eecc6SJavier Almansa Sobrino 				}
152*524eecc6SJavier Almansa Sobrino 				goto loop;
153*524eecc6SJavier Almansa Sobrino 			case '-':
154*524eecc6SJavier Almansa Sobrino 				left = true;
155*524eecc6SJavier Almansa Sobrino 				fmt++;
156*524eecc6SJavier Almansa Sobrino 				goto loop;
157*524eecc6SJavier Almansa Sobrino 
158870ce3ddSAntonio Nino Diaz 			case 'i':
159870ce3ddSAntonio Nino Diaz 			case 'd':
160870ce3ddSAntonio Nino Diaz 				num = va_arg(args, int);
161870ce3ddSAntonio Nino Diaz 
162870ce3ddSAntonio Nino Diaz 				if (num < 0) {
163d5ccb754SAntonio Nino Diaz 					if (chars_printed < n) {
164d5ccb754SAntonio Nino Diaz 						*s = '-';
165d5ccb754SAntonio Nino Diaz 						s++;
166d5ccb754SAntonio Nino Diaz 					}
167870ce3ddSAntonio Nino Diaz 					chars_printed++;
168870ce3ddSAntonio Nino Diaz 
169870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)-num;
170870ce3ddSAntonio Nino Diaz 				} else {
171870ce3ddSAntonio Nino Diaz 					unum = (unsigned int)num;
172870ce3ddSAntonio Nino Diaz 				}
173870ce3ddSAntonio Nino Diaz 
174*524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
175*524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
176870ce3ddSAntonio Nino Diaz 				break;
177870ce3ddSAntonio Nino Diaz 			case 's':
178870ce3ddSAntonio Nino Diaz 				str = va_arg(args, char *);
179870ce3ddSAntonio Nino Diaz 				string_print(&s, n, &chars_printed, str);
180870ce3ddSAntonio Nino Diaz 				break;
181870ce3ddSAntonio Nino Diaz 			case 'u':
182870ce3ddSAntonio Nino Diaz 				unum = va_arg(args, unsigned int);
183*524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
184*524eecc6SJavier Almansa Sobrino 						   unum, 10, padc, padn, false);
185870ce3ddSAntonio Nino Diaz 				break;
186*524eecc6SJavier Almansa Sobrino 			case 'p':
187*524eecc6SJavier Almansa Sobrino 				unum = (uintptr_t)va_arg(args, void *);
188*524eecc6SJavier Almansa Sobrino 				if (unum > 0U) {
189*524eecc6SJavier Almansa Sobrino 					string_print(&s, n, &chars_printed, "0x");
190*524eecc6SJavier Almansa Sobrino 					padn -= 2;
191*524eecc6SJavier Almansa Sobrino 				}
192*524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
193*524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn, false);
194*524eecc6SJavier Almansa Sobrino 				break;
195*524eecc6SJavier Almansa Sobrino 			case 'X':
196*524eecc6SJavier Almansa Sobrino 				capitalise = true;
197*524eecc6SJavier Almansa Sobrino 			case 'x':
198*524eecc6SJavier Almansa Sobrino 				unum = va_arg(args, unsigned int);
199*524eecc6SJavier Almansa Sobrino 				unsigned_num_print(&s, n, &chars_printed,
200*524eecc6SJavier Almansa Sobrino 						   unum, 16, padc, padn,
201*524eecc6SJavier Almansa Sobrino 						   capitalise);
202*524eecc6SJavier Almansa Sobrino 				break;
203*524eecc6SJavier Almansa Sobrino 
204870ce3ddSAntonio Nino Diaz 			default:
205870ce3ddSAntonio Nino Diaz 				/* Panic on any other format specifier. */
206870ce3ddSAntonio Nino Diaz 				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
207870ce3ddSAntonio Nino Diaz 				      *fmt);
208870ce3ddSAntonio Nino Diaz 				plat_panic_handler();
209a08a2014SDaniel Boulby 				assert(0); /* Unreachable */
210870ce3ddSAntonio Nino Diaz 			}
211870ce3ddSAntonio Nino Diaz 			fmt++;
212870ce3ddSAntonio Nino Diaz 			continue;
213870ce3ddSAntonio Nino Diaz 		}
214870ce3ddSAntonio Nino Diaz 
215d5ccb754SAntonio Nino Diaz 		if (chars_printed < n) {
216d5ccb754SAntonio Nino Diaz 			*s = *fmt;
217d5ccb754SAntonio Nino Diaz 			s++;
218d5ccb754SAntonio Nino Diaz 		}
219d5ccb754SAntonio Nino Diaz 
220870ce3ddSAntonio Nino Diaz 		fmt++;
221870ce3ddSAntonio Nino Diaz 		chars_printed++;
222870ce3ddSAntonio Nino Diaz 	}
223870ce3ddSAntonio Nino Diaz 
224870ce3ddSAntonio Nino Diaz 	va_end(args);
225870ce3ddSAntonio Nino Diaz 
226d5ccb754SAntonio Nino Diaz 	if (n > 0U)
227870ce3ddSAntonio Nino Diaz 		*s = '\0';
228870ce3ddSAntonio Nino Diaz 
229d5ccb754SAntonio Nino Diaz 	return (int)chars_printed;
230870ce3ddSAntonio Nino Diaz }
231