snprintf.c 4.66 KB
Newer Older
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
1
/*
2
 * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
3
4
5
6
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

7
#include <assert.h>
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
8
9
#include <stdarg.h>

10
11
12
#include <common/debug.h>
#include <plat/common/platform.h>

13
14
15
static void string_print(char **s, size_t n, size_t *chars_printed,
			 const char *str)
{
16
17
18
19
20
21
	while (*str != '\0') {
		if (*chars_printed < n) {
			*(*s) = *str;
			(*s)++;
		}

22
23
24
25
26
		(*chars_printed)++;
		str++;
	}
}

27
28
29
30
static void 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)
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
31
{
32
33
	/* Just need enough space to store 64 bit decimal integer */
	char num_buf[20];
34
	int i = 0;
35
	int width;
36
	unsigned int rem;
37
	char ascii_a = capitalise ? 'A' : 'a';
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
38
39

	do {
40
41
42
43
44
45
46
47
		rem = unum % radix;
		if (rem < 10U) {
			num_buf[i] = '0' + rem;
		} else {
			num_buf[i] = ascii_a + (rem - 10U);
		}
		i++;
		unum /= radix;
48
	} while (unum > 0U);
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
49

50
51
52
53
54
55
56
57
58
59
60
61
62
63
	width = i;
	if (padn > width) {
		(*chars_printed) += (size_t)padn;
	} else {
		(*chars_printed) += (size_t)width;
	}

	if (*chars_printed < n) {

		if (padn > 0) {
			while (width < padn) {
				*(*s)++ = padc;
				padn--;
			}
64
65
		}

66
67
68
69
70
71
72
73
74
75
		while (--i >= 0) {
			*(*s)++ = num_buf[i];
		}

		if (padn < 0) {
			while (width < -padn) {
				*(*s)++ = padc;
				padn++;
			}
		}
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
76
77
78
79
80
81
82
	}
}

/*******************************************************************
 * Reduced snprintf to be used for Trusted firmware.
 * The following type specifiers are supported:
 *
83
 * %x (or %X) - hexadecimal format
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
84
 * %d or %i - signed decimal format
85
 * %s - string format
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
86
 * %u - unsigned decimal format
87
88
89
90
91
92
 * %p - pointer format
 *
 * The following padding specifiers are supported by this print
 * %0NN - Left-pad the number with 0s (NN is a decimal number)
 * %NN - Left-pad the number or string with spaces (NN is a decimal number)
 * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
93
94
95
96
97
98
99
 *
 * The function panics on all other formats specifiers.
 *
 * It returns the number of characters that would be written if the
 * buffer was big enough. If it returns a value lower than n, the
 * whole string has been written.
 *******************************************************************/
100
int snprintf(char *s, size_t n, const char *fmt, ...)
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
101
102
103
{
	va_list args;
	int num;
104
	unsigned long long int unum;
105
	char *str;
106
107
108
109
	char padc;		/* Padding character */
	int padn;		/* Number of characters to pad */
	bool left;
	bool capitalise;
110
	size_t chars_printed = 0U;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
111

112
113
114
	if (n == 0U) {
		/* There isn't space for anything. */
	} else if (n == 1U) {
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
115
116
		/* Buffer is too small to actually write anything else. */
		*s = '\0';
117
118
		n = 0U;
	} else {
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
119
120
121
122
123
		/* Reserve space for the terminator character. */
		n--;
	}

	va_start(args, fmt);
124
	while (*fmt != '\0') {
125
126
127
128
		left = false;
		padc ='\0';
		padn = 0;
		capitalise = false;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
129
130
131
132

		if (*fmt == '%') {
			fmt++;
			/* Check the format specifier. */
133
loop:
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
134
			switch (*fmt) {
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				padc = (*fmt == '0') ? '0' : ' ';
				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
					padn = (padn * 10) + (*fmt - '0');
				}
				if (left) {
					padn = -padn;
				}
				goto loop;
			case '-':
				left = true;
				fmt++;
				goto loop;

Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
158
159
160
161
162
			case 'i':
			case 'd':
				num = va_arg(args, int);

				if (num < 0) {
163
164
165
166
					if (chars_printed < n) {
						*s = '-';
						s++;
					}
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
167
168
169
170
171
172
173
					chars_printed++;

					unum = (unsigned int)-num;
				} else {
					unum = (unsigned int)num;
				}

174
175
				unsigned_num_print(&s, n, &chars_printed,
						   unum, 10, padc, padn, false);
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
176
				break;
177
178
179
180
			case 's':
				str = va_arg(args, char *);
				string_print(&s, n, &chars_printed, str);
				break;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
181
182
			case 'u':
				unum = va_arg(args, unsigned int);
183
184
				unsigned_num_print(&s, n, &chars_printed,
						   unum, 10, padc, padn, false);
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
185
				break;
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
			case 'p':
				unum = (uintptr_t)va_arg(args, void *);
				if (unum > 0U) {
					string_print(&s, n, &chars_printed, "0x");
					padn -= 2;
				}
				unsigned_num_print(&s, n, &chars_printed,
						   unum, 16, padc, padn, false);
				break;
			case 'X':
				capitalise = true;
			case 'x':
				unum = va_arg(args, unsigned int);
				unsigned_num_print(&s, n, &chars_printed,
						   unum, 16, padc, padn,
						   capitalise);
				break;

Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
204
205
			default:
				/* Panic on any other format specifier. */
206
				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
207
208
				      *fmt);
				plat_panic_handler();
209
				assert(0); /* Unreachable */
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
210
211
212
213
214
			}
			fmt++;
			continue;
		}

215
216
217
218
219
		if (chars_printed < n) {
			*s = *fmt;
			s++;
		}

Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
220
221
222
223
224
225
		fmt++;
		chars_printed++;
	}

	va_end(args);

226
	if (n > 0U)
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
227
228
		*s = '\0';

229
	return (int)chars_printed;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
230
}