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