1870ce3ddSAntonio Nino Diaz /*
2*44d9706eSMaksims Svecovs * Copyright (c) 2017-2023, 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>
9885e2683SClaus Pedersen #include <stdbool.h>
10885e2683SClaus Pedersen #include <stddef.h>
11410c925aSAndre Przywara #include <stdint.h>
12870ce3ddSAntonio Nino Diaz
13701e94b0Skadabi #define get_num_va_args(_args, _lcount) \
14701e94b0Skadabi (((_lcount) > 1) ? va_arg(_args, long long int) : \
15701e94b0Skadabi (((_lcount) == 1) ? va_arg(_args, long int) : \
16701e94b0Skadabi va_arg(_args, int)))
17701e94b0Skadabi
18701e94b0Skadabi #define get_unum_va_args(_args, _lcount) \
19701e94b0Skadabi (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \
20701e94b0Skadabi (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \
21701e94b0Skadabi va_arg(_args, unsigned int)))
22701e94b0Skadabi
237981c504SHeyi Guo #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \
247981c504SHeyi Guo do { \
257981c504SHeyi Guo if ((chars_printed) < (size)) { \
267981c504SHeyi Guo *(buf) = (ch); \
277981c504SHeyi Guo (buf)++; \
287981c504SHeyi Guo } \
297981c504SHeyi Guo (chars_printed)++; \
307981c504SHeyi Guo } while (false)
317981c504SHeyi Guo
string_print(char ** s,size_t n,size_t * chars_printed,const char * str)32870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed,
33870ce3ddSAntonio Nino Diaz const char *str)
34870ce3ddSAntonio Nino Diaz {
35d5ccb754SAntonio Nino Diaz while (*str != '\0') {
367981c504SHeyi Guo CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
37870ce3ddSAntonio Nino Diaz str++;
38870ce3ddSAntonio Nino Diaz }
39870ce3ddSAntonio Nino Diaz }
40870ce3ddSAntonio Nino Diaz
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)41524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
42524eecc6SJavier Almansa Sobrino unsigned long long int unum,
43524eecc6SJavier Almansa Sobrino unsigned int radix, char padc, int padn,
44524eecc6SJavier Almansa Sobrino bool capitalise)
45870ce3ddSAntonio Nino Diaz {
46524eecc6SJavier Almansa Sobrino /* Just need enough space to store 64 bit decimal integer */
47524eecc6SJavier Almansa Sobrino char num_buf[20];
48d5ccb754SAntonio Nino Diaz int i = 0;
49524eecc6SJavier Almansa Sobrino int width;
50d5ccb754SAntonio Nino Diaz unsigned int rem;
51524eecc6SJavier Almansa Sobrino char ascii_a = capitalise ? 'A' : 'a';
52870ce3ddSAntonio Nino Diaz
53885e2683SClaus Pedersen /* num_buf is only large enough for radix >= 10 */
54b30dd403SAndre Przywara if (radix < 10) {
55885e2683SClaus Pedersen assert(0);
56885e2683SClaus Pedersen return;
57b30dd403SAndre Przywara }
58b30dd403SAndre Przywara
59870ce3ddSAntonio Nino Diaz do {
60524eecc6SJavier Almansa Sobrino rem = unum % radix;
61524eecc6SJavier Almansa Sobrino if (rem < 10U) {
62524eecc6SJavier Almansa Sobrino num_buf[i] = '0' + rem;
63524eecc6SJavier Almansa Sobrino } else {
64524eecc6SJavier Almansa Sobrino num_buf[i] = ascii_a + (rem - 10U);
65524eecc6SJavier Almansa Sobrino }
66524eecc6SJavier Almansa Sobrino i++;
67524eecc6SJavier Almansa Sobrino unum /= radix;
68d5ccb754SAntonio Nino Diaz } while (unum > 0U);
69870ce3ddSAntonio Nino Diaz
70524eecc6SJavier Almansa Sobrino width = i;
71c1f5a092SAndre Przywara for (i = padn - width; i > 0; i--) {
72c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
73d5ccb754SAntonio Nino Diaz }
74c1f5a092SAndre Przywara for (i = width; i > 0; i--) {
75c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
76524eecc6SJavier Almansa Sobrino }
77c1f5a092SAndre Przywara for (i = width + padn; i < 0; i++) {
78c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
79870ce3ddSAntonio Nino Diaz }
80870ce3ddSAntonio Nino Diaz }
81870ce3ddSAntonio Nino Diaz
82870ce3ddSAntonio Nino Diaz /*******************************************************************
8377648689SMadhukar Pappireddy * Reduced vsnprintf to be used for Trusted firmware.
84870ce3ddSAntonio Nino Diaz * The following type specifiers are supported:
85870ce3ddSAntonio Nino Diaz *
86524eecc6SJavier Almansa Sobrino * %x (or %X) - hexadecimal format
87870ce3ddSAntonio Nino Diaz * %d or %i - signed decimal format
88*44d9706eSMaksims Svecovs * %c - character format
89870ce3ddSAntonio Nino Diaz * %s - string format
90870ce3ddSAntonio Nino Diaz * %u - unsigned decimal format
91524eecc6SJavier Almansa Sobrino * %p - pointer format
92524eecc6SJavier Almansa Sobrino *
93701e94b0Skadabi * The following length specifiers are supported by this print
94701e94b0Skadabi * %l - long int
95701e94b0Skadabi * %ll - long long int
96701e94b0Skadabi * %z - size_t sized integer formats
97701e94b0Skadabi *
98524eecc6SJavier Almansa Sobrino * The following padding specifiers are supported by this print
99524eecc6SJavier Almansa Sobrino * %0NN - Left-pad the number with 0s (NN is a decimal number)
100524eecc6SJavier Almansa Sobrino * %NN - Left-pad the number or string with spaces (NN is a decimal number)
101524eecc6SJavier Almansa Sobrino * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
102870ce3ddSAntonio Nino Diaz *
103870ce3ddSAntonio Nino Diaz * The function panics on all other formats specifiers.
104870ce3ddSAntonio Nino Diaz *
105870ce3ddSAntonio Nino Diaz * It returns the number of characters that would be written if the
106870ce3ddSAntonio Nino Diaz * buffer was big enough. If it returns a value lower than n, the
107870ce3ddSAntonio Nino Diaz * whole string has been written.
108870ce3ddSAntonio Nino Diaz *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)10977648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
110870ce3ddSAntonio Nino Diaz {
111870ce3ddSAntonio Nino Diaz int num;
112524eecc6SJavier Almansa Sobrino unsigned long long int unum;
113870ce3ddSAntonio Nino Diaz char *str;
114524eecc6SJavier Almansa Sobrino char padc; /* Padding character */
115524eecc6SJavier Almansa Sobrino int padn; /* Number of characters to pad */
116524eecc6SJavier Almansa Sobrino bool left;
117524eecc6SJavier Almansa Sobrino bool capitalise;
118d5ccb754SAntonio Nino Diaz size_t chars_printed = 0U;
119701e94b0Skadabi unsigned int l_count;
120870ce3ddSAntonio Nino Diaz
121d5ccb754SAntonio Nino Diaz if (n == 0U) {
122d5ccb754SAntonio Nino Diaz /* There isn't space for anything. */
123d5ccb754SAntonio Nino Diaz } else if (n == 1U) {
124870ce3ddSAntonio Nino Diaz /* Buffer is too small to actually write anything else. */
125870ce3ddSAntonio Nino Diaz *s = '\0';
126d5ccb754SAntonio Nino Diaz n = 0U;
127d5ccb754SAntonio Nino Diaz } else {
128870ce3ddSAntonio Nino Diaz /* Reserve space for the terminator character. */
129870ce3ddSAntonio Nino Diaz n--;
130870ce3ddSAntonio Nino Diaz }
131870ce3ddSAntonio Nino Diaz
132d5ccb754SAntonio Nino Diaz while (*fmt != '\0') {
133524eecc6SJavier Almansa Sobrino left = false;
134524eecc6SJavier Almansa Sobrino padc ='\0';
135524eecc6SJavier Almansa Sobrino padn = 0;
136524eecc6SJavier Almansa Sobrino capitalise = false;
137701e94b0Skadabi l_count = 0;
138870ce3ddSAntonio Nino Diaz
139870ce3ddSAntonio Nino Diaz if (*fmt == '%') {
140870ce3ddSAntonio Nino Diaz fmt++;
141870ce3ddSAntonio Nino Diaz /* Check the format specifier. */
142524eecc6SJavier Almansa Sobrino loop:
143870ce3ddSAntonio Nino Diaz switch (*fmt) {
144c6546154SHeyi Guo case '%':
1457981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
146c6546154SHeyi Guo break;
147524eecc6SJavier Almansa Sobrino case '0':
148524eecc6SJavier Almansa Sobrino case '1':
149524eecc6SJavier Almansa Sobrino case '2':
150524eecc6SJavier Almansa Sobrino case '3':
151524eecc6SJavier Almansa Sobrino case '4':
152524eecc6SJavier Almansa Sobrino case '5':
153524eecc6SJavier Almansa Sobrino case '6':
154524eecc6SJavier Almansa Sobrino case '7':
155524eecc6SJavier Almansa Sobrino case '8':
156524eecc6SJavier Almansa Sobrino case '9':
157524eecc6SJavier Almansa Sobrino padc = (*fmt == '0') ? '0' : ' ';
158524eecc6SJavier Almansa Sobrino for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
159524eecc6SJavier Almansa Sobrino padn = (padn * 10) + (*fmt - '0');
160524eecc6SJavier Almansa Sobrino }
161524eecc6SJavier Almansa Sobrino if (left) {
162524eecc6SJavier Almansa Sobrino padn = -padn;
163524eecc6SJavier Almansa Sobrino }
164524eecc6SJavier Almansa Sobrino goto loop;
165524eecc6SJavier Almansa Sobrino case '-':
166524eecc6SJavier Almansa Sobrino left = true;
167524eecc6SJavier Almansa Sobrino fmt++;
168524eecc6SJavier Almansa Sobrino goto loop;
169524eecc6SJavier Almansa Sobrino
170870ce3ddSAntonio Nino Diaz case 'i':
171870ce3ddSAntonio Nino Diaz case 'd':
172701e94b0Skadabi num = get_num_va_args(args, l_count);
173870ce3ddSAntonio Nino Diaz
174870ce3ddSAntonio Nino Diaz if (num < 0) {
1757981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed,
1767981c504SHeyi Guo '-');
177870ce3ddSAntonio Nino Diaz unum = (unsigned int)-num;
178870ce3ddSAntonio Nino Diaz } else {
179870ce3ddSAntonio Nino Diaz unum = (unsigned int)num;
180870ce3ddSAntonio Nino Diaz }
181870ce3ddSAntonio Nino Diaz
182524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed,
183524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false);
184870ce3ddSAntonio Nino Diaz break;
185*44d9706eSMaksims Svecovs case 'c':
186*44d9706eSMaksims Svecovs CHECK_AND_PUT_CHAR(s, n, chars_printed, va_arg(args, int));
187*44d9706eSMaksims Svecovs break;
188870ce3ddSAntonio Nino Diaz case 's':
189870ce3ddSAntonio Nino Diaz str = va_arg(args, char *);
190870ce3ddSAntonio Nino Diaz string_print(&s, n, &chars_printed, str);
191870ce3ddSAntonio Nino Diaz break;
192870ce3ddSAntonio Nino Diaz case 'u':
193701e94b0Skadabi unum = get_unum_va_args(args, l_count);
194524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed,
195524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false);
196870ce3ddSAntonio Nino Diaz break;
197701e94b0Skadabi case 'z':
198701e94b0Skadabi l_count = 1;
199701e94b0Skadabi fmt++;
200701e94b0Skadabi goto loop;
201701e94b0Skadabi case 'l':
202701e94b0Skadabi l_count++;
203701e94b0Skadabi fmt++;
204701e94b0Skadabi goto loop;
205524eecc6SJavier Almansa Sobrino case 'p':
206524eecc6SJavier Almansa Sobrino unum = (uintptr_t)va_arg(args, void *);
207524eecc6SJavier Almansa Sobrino if (unum > 0U) {
208524eecc6SJavier Almansa Sobrino string_print(&s, n, &chars_printed, "0x");
209524eecc6SJavier Almansa Sobrino padn -= 2;
210524eecc6SJavier Almansa Sobrino }
211524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed,
212524eecc6SJavier Almansa Sobrino unum, 16, padc, padn, false);
213524eecc6SJavier Almansa Sobrino break;
214524eecc6SJavier Almansa Sobrino case 'X':
215524eecc6SJavier Almansa Sobrino capitalise = true;
216e138400dSBoyan Karatotev /* fallthrough */
217524eecc6SJavier Almansa Sobrino case 'x':
218701e94b0Skadabi unum = get_unum_va_args(args, l_count);
219524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed,
220524eecc6SJavier Almansa Sobrino unum, 16, padc, padn,
221524eecc6SJavier Almansa Sobrino capitalise);
222524eecc6SJavier Almansa Sobrino break;
223524eecc6SJavier Almansa Sobrino
224870ce3ddSAntonio Nino Diaz default:
225885e2683SClaus Pedersen CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
226885e2683SClaus Pedersen CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
227870ce3ddSAntonio Nino Diaz }
228870ce3ddSAntonio Nino Diaz fmt++;
229870ce3ddSAntonio Nino Diaz continue;
230870ce3ddSAntonio Nino Diaz }
231870ce3ddSAntonio Nino Diaz
2327981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
233d5ccb754SAntonio Nino Diaz
234870ce3ddSAntonio Nino Diaz fmt++;
235870ce3ddSAntonio Nino Diaz }
236870ce3ddSAntonio Nino Diaz
23777648689SMadhukar Pappireddy if (n > 0U) {
238870ce3ddSAntonio Nino Diaz *s = '\0';
23977648689SMadhukar Pappireddy }
240870ce3ddSAntonio Nino Diaz
241d5ccb754SAntonio Nino Diaz return (int)chars_printed;
242870ce3ddSAntonio Nino Diaz }
24377648689SMadhukar Pappireddy
24477648689SMadhukar Pappireddy /*******************************************************************
24577648689SMadhukar Pappireddy * Reduced snprintf to be used for Trusted firmware.
24677648689SMadhukar Pappireddy * The following type specifiers are supported:
24777648689SMadhukar Pappireddy *
24877648689SMadhukar Pappireddy * %x (or %X) - hexadecimal format
24977648689SMadhukar Pappireddy * %d or %i - signed decimal format
25077648689SMadhukar Pappireddy * %s - string format
25177648689SMadhukar Pappireddy * %u - unsigned decimal format
25277648689SMadhukar Pappireddy * %p - pointer format
25377648689SMadhukar Pappireddy *
25477648689SMadhukar Pappireddy * The following padding specifiers are supported by this print
25577648689SMadhukar Pappireddy * %0NN - Left-pad the number with 0s (NN is a decimal number)
25677648689SMadhukar Pappireddy * %NN - Left-pad the number or string with spaces (NN is a decimal number)
25777648689SMadhukar Pappireddy * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
25877648689SMadhukar Pappireddy *
25977648689SMadhukar Pappireddy * The function panics on all other formats specifiers.
26077648689SMadhukar Pappireddy *
26177648689SMadhukar Pappireddy * It returns the number of characters that would be written if the
26277648689SMadhukar Pappireddy * buffer was big enough. If it returns a value lower than n, the
26377648689SMadhukar Pappireddy * whole string has been written.
26477648689SMadhukar Pappireddy *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)26577648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...)
26677648689SMadhukar Pappireddy {
26777648689SMadhukar Pappireddy int count;
26877648689SMadhukar Pappireddy va_list all_args;
26977648689SMadhukar Pappireddy
27077648689SMadhukar Pappireddy va_start(all_args, fmt);
27177648689SMadhukar Pappireddy count = vsnprintf(s, n, fmt, all_args);
27277648689SMadhukar Pappireddy va_end(all_args);
27377648689SMadhukar Pappireddy
27477648689SMadhukar Pappireddy return count;
27577648689SMadhukar Pappireddy }
276