snprintf.c 2.81 KB
Newer Older
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
1
/*
2
 * Copyright (c) 2017-2018, 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++;
	}
}

Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
27
28
29
30
static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed,
			       unsigned int unum)
{
	/* Enough for a 32-bit unsigned decimal integer (4294967295). */
31
32
33
	char num_buf[10];
	int i = 0;
	unsigned int rem;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
34
35

	do {
36
		rem = unum % 10U;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
37
		num_buf[i++] = '0' + rem;
38
39
		unum /= 10U;
	} while (unum > 0U);
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
40
41

	while (--i >= 0) {
42
43
44
45
46
		if (*chars_printed < n) {
			*(*s) = num_buf[i];
			(*s)++;
		}

Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
47
48
49
50
51
52
53
54
55
		(*chars_printed)++;
	}
}

/*******************************************************************
 * Reduced snprintf to be used for Trusted firmware.
 * The following type specifiers are supported:
 *
 * %d or %i - signed decimal format
56
 * %s - string format
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
57
58
59
60
61
62
63
64
 * %u - unsigned decimal format
 *
 * 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.
 *******************************************************************/
65
int snprintf(char *s, size_t n, const char *fmt, ...)
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
66
67
68
69
{
	va_list args;
	int num;
	unsigned int unum;
70
	char *str;
71
	size_t chars_printed = 0U;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
72

73
74
75
	if (n == 0U) {
		/* There isn't space for anything. */
	} else if (n == 1U) {
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
76
77
		/* Buffer is too small to actually write anything else. */
		*s = '\0';
78
79
		n = 0U;
	} else {
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
80
81
82
83
84
		/* Reserve space for the terminator character. */
		n--;
	}

	va_start(args, fmt);
85
	while (*fmt != '\0') {
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
86
87
88
89
90
91
92
93
94
95

		if (*fmt == '%') {
			fmt++;
			/* Check the format specifier. */
			switch (*fmt) {
			case 'i':
			case 'd':
				num = va_arg(args, int);

				if (num < 0) {
96
97
98
99
					if (chars_printed < n) {
						*s = '-';
						s++;
					}
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
100
101
102
103
104
105
106
107
108
					chars_printed++;

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

				unsigned_dec_print(&s, n, &chars_printed, unum);
				break;
109
110
111
112
			case 's':
				str = va_arg(args, char *);
				string_print(&s, n, &chars_printed, str);
				break;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
113
114
115
116
117
118
			case 'u':
				unum = va_arg(args, unsigned int);
				unsigned_dec_print(&s, n, &chars_printed, unum);
				break;
			default:
				/* Panic on any other format specifier. */
119
				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
120
121
				      *fmt);
				plat_panic_handler();
122
				assert(0); /* Unreachable */
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
123
124
125
126
127
			}
			fmt++;
			continue;
		}

128
129
130
131
132
		if (chars_printed < n) {
			*s = *fmt;
			s++;
		}

Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
133
134
135
136
137
138
		fmt++;
		chars_printed++;
	}

	va_end(args);

139
	if (n > 0U)
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
140
141
		*s = '\0';

142
	return (int)chars_printed;
Antonio Nino Diaz's avatar
Antonio Nino Diaz committed
143
}