blob: 6ad284f017ac1a08f5d9383f8591cab10cb90614 [file] [log] [blame]
Ambroise Vincent4128f9f2019-02-11 13:34:41 +00001/*
nabkah01002e5692022-10-10 12:36:46 +01002 * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
Ambroise Vincent4128f9f2019-02-11 13:34:41 +00003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <stdarg.h>
Ambroise Vincent8a573de2019-02-11 13:54:30 +00009#include <stdlib.h>
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000010
11#include <common/debug.h>
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000012
Ambroise Vincent8a573de2019-02-11 13:54:30 +000013#define get_num_va_args(_args, _lcount) \
14 (((_lcount) > 1) ? va_arg(_args, long long int) : \
15 (((_lcount) == 1) ? va_arg(_args, long int) : \
16 va_arg(_args, int)))
17
18#define get_unum_va_args(_args, _lcount) \
19 (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \
20 (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \
21 va_arg(_args, unsigned int)))
22
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000023static void string_print(char **s, size_t n, size_t *chars_printed,
24 const char *str)
25{
26 while (*str != '\0') {
27 if (*chars_printed < n) {
28 *(*s) = *str;
29 (*s)++;
30 }
31
32 (*chars_printed)++;
33 str++;
34 }
35}
36
Ambroise Vincent8a573de2019-02-11 13:54:30 +000037static void unsigned_num_print(char **s, size_t n, size_t *count,
38 unsigned long long int unum, unsigned int radix,
39 char padc, int padn)
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000040{
Ambroise Vincent8a573de2019-02-11 13:54:30 +000041 /* Just need enough space to store 64 bit decimal integer */
42 char num_buf[20];
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000043 int i = 0;
Ambroise Vincent8a573de2019-02-11 13:54:30 +000044 int width;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000045 unsigned int rem;
46
47 do {
Ambroise Vincent8a573de2019-02-11 13:54:30 +000048 rem = unum % radix;
49 if (rem < 0xa)
50 num_buf[i] = '0' + rem;
51 else
52 num_buf[i] = 'a' + (rem - 0xa);
53 i++;
54 unum /= radix;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000055 } while (unum > 0U);
56
Ambroise Vincent8a573de2019-02-11 13:54:30 +000057 width = i;
58
59 if (padn > 0) {
60 while (width < padn) {
61 if (*count < n) {
62 *(*s) = padc;
63 (*s)++;
64 }
65 (*count)++;
66 padn--;
67 }
68 }
69
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000070 while (--i >= 0) {
Ambroise Vincent8a573de2019-02-11 13:54:30 +000071 if (*count < n) {
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000072 *(*s) = num_buf[i];
73 (*s)++;
74 }
Ambroise Vincent8a573de2019-02-11 13:54:30 +000075 (*count)++;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +000076 }
Ambroise Vincent8a573de2019-02-11 13:54:30 +000077
78 if (padn < 0) {
79 while (width < -padn) {
80 if (*count < n) {
81 *(*s) = padc;
82 (*s)++;
83 }
84 (*count)++;
85 padn++;
86 }
87 }
88}
89
90/*
91 * Scaled down version of vsnprintf(3).
92 */
93int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
94{
95 int l_count;
96 int left;
97 char *str;
98 int num;
99 unsigned long long int unum;
100 char padc; /* Padding character */
101 int padn; /* Number of characters to pad */
102 size_t count = 0U;
103
104 if (n == 0U) {
105 /* There isn't space for anything. */
106 } else if (n == 1U) {
107 /* Buffer is too small to actually write anything else. */
108 *s = '\0';
109 n = 0U;
110 } else {
111 /* Reserve space for the terminator character. */
112 n--;
113 }
114
115 while (*fmt != '\0') {
116 l_count = 0;
117 left = 0;
118 padc = '\0';
119 padn = 0;
120
121 if (*fmt == '%') {
122 fmt++;
123 /* Check the format specifier. */
124loop:
125 switch (*fmt) {
126 case '1':
127 case '2':
128 case '3':
129 case '4':
130 case '5':
131 case '6':
132 case '7':
133 case '8':
134 case '9':
135 padc = ' ';
136 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
137 padn = (padn * 10) + (*fmt - '0');
138 if (left)
139 padn = -padn;
140 goto loop;
141 case '-':
142 left = 1;
143 fmt++;
144 goto loop;
145 case 'i':
146 case 'd':
147 num = get_num_va_args(args, l_count);
148
149 if (num < 0) {
150 if (count < n) {
151 *s = '-';
152 s++;
153 }
154 count++;
155
156 unum = (unsigned int)-num;
157 } else {
158 unum = (unsigned int)num;
159 }
160
161 unsigned_num_print(&s, n, &count, unum, 10,
162 padc, padn);
163 break;
164 case 'l':
165 l_count++;
166 fmt++;
167 goto loop;
168 case 's':
169 str = va_arg(args, char *);
170 string_print(&s, n, &count, str);
171 break;
172 case 'u':
173 unum = get_unum_va_args(args, l_count);
174 unsigned_num_print(&s, n, &count, unum, 10,
175 padc, padn);
176 break;
177 case 'x':
178 unum = get_unum_va_args(args, l_count);
179 unsigned_num_print(&s, n, &count, unum, 16,
180 padc, padn);
181 break;
182 case '0':
183 padc = '0';
184 padn = 0;
185 fmt++;
186
187 for (;;) {
188 char ch = *fmt;
189 if ((ch < '0') || (ch > '9')) {
190 goto loop;
191 }
192 padn = (padn * 10) + (ch - '0');
193 fmt++;
194 }
195 assert(0); /* Unreachable */
196 default:
197 /*
198 * Exit on any other format specifier and abort
199 * when in debug mode.
200 */
201 WARN("snprintf: specifier with ASCII code '%d' not supported.\n",
202 *fmt);
203 assert(0);
204 return -1;
205 }
206 fmt++;
207 continue;
208 }
209
210 if (count < n) {
211 *s = *fmt;
212 s++;
213 }
214
215 fmt++;
216 count++;
217 }
218
219 if (n > 0U)
220 *s = '\0';
221
222 return (int)count;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000223}
224
225/*******************************************************************
226 * Reduced snprintf to be used for Trusted firmware.
227 * The following type specifiers are supported:
228 *
229 * %d or %i - signed decimal format
230 * %s - string format
231 * %u - unsigned decimal format
232 *
233 * The function panics on all other formats specifiers.
234 *
235 * It returns the number of characters that would be written if the
236 * buffer was big enough. If it returns a value lower than n, the
237 * whole string has been written.
238 *******************************************************************/
239int snprintf(char *s, size_t n, const char *fmt, ...)
240{
241 va_list args;
Ambroise Vincent8a573de2019-02-11 13:54:30 +0000242 int chars_printed;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000243
244 va_start(args, fmt);
Ambroise Vincent8a573de2019-02-11 13:54:30 +0000245 chars_printed = vsnprintf(s, n, fmt, args);
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000246 va_end(args);
247
Ambroise Vincent8a573de2019-02-11 13:54:30 +0000248 return chars_printed;
Ambroise Vincent4128f9f2019-02-11 13:34:41 +0000249}