You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
6.6 KiB
275 lines
6.6 KiB
/*
|
|
* Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#define get_num_va_args(_args, _lcount) \
|
|
(((_lcount) > 1) ? va_arg(_args, long long int) : \
|
|
(((_lcount) == 1) ? va_arg(_args, long int) : \
|
|
va_arg(_args, int)))
|
|
|
|
#define get_unum_va_args(_args, _lcount) \
|
|
(((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \
|
|
(((_lcount) == 1) ? va_arg(_args, unsigned long int) : \
|
|
va_arg(_args, unsigned int)))
|
|
|
|
#define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \
|
|
do { \
|
|
if ((chars_printed) < (size)) { \
|
|
*(buf) = (ch); \
|
|
(buf)++; \
|
|
} \
|
|
(chars_printed)++; \
|
|
} while (false)
|
|
|
|
static void string_print(char **s, size_t n, size_t *chars_printed,
|
|
const char *str)
|
|
{
|
|
while (*str != '\0') {
|
|
CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
|
|
str++;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* Just need enough space to store 64 bit decimal integer */
|
|
char num_buf[20];
|
|
int i = 0;
|
|
int width;
|
|
unsigned int rem;
|
|
char ascii_a = capitalise ? 'A' : 'a';
|
|
|
|
/* num_buf is only large enough for radix >= 10 */
|
|
if (radix < 10) {
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
do {
|
|
rem = unum % radix;
|
|
if (rem < 10U) {
|
|
num_buf[i] = '0' + rem;
|
|
} else {
|
|
num_buf[i] = ascii_a + (rem - 10U);
|
|
}
|
|
i++;
|
|
unum /= radix;
|
|
} while (unum > 0U);
|
|
|
|
width = i;
|
|
for (i = padn - width; i > 0; i--) {
|
|
CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
|
|
}
|
|
for (i = width; i > 0; i--) {
|
|
CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
|
|
}
|
|
for (i = width + padn; i < 0; i++) {
|
|
CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
* Reduced vsnprintf to be used for Trusted firmware.
|
|
* The following type specifiers are supported:
|
|
*
|
|
* %x (or %X) - hexadecimal format
|
|
* %d or %i - signed decimal format
|
|
* %c - character format
|
|
* %s - string format
|
|
* %u - unsigned decimal format
|
|
* %p - pointer format
|
|
*
|
|
* The following length specifiers are supported by this print
|
|
* %l - long int
|
|
* %ll - long long int
|
|
* %z - size_t sized integer formats
|
|
*
|
|
* 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)
|
|
*
|
|
* 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.
|
|
*******************************************************************/
|
|
int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
|
|
{
|
|
int num;
|
|
unsigned long long int unum;
|
|
char *str;
|
|
char padc; /* Padding character */
|
|
int padn; /* Number of characters to pad */
|
|
bool left;
|
|
bool capitalise;
|
|
size_t chars_printed = 0U;
|
|
unsigned int l_count;
|
|
|
|
if (n == 0U) {
|
|
/* There isn't space for anything. */
|
|
} else if (n == 1U) {
|
|
/* Buffer is too small to actually write anything else. */
|
|
*s = '\0';
|
|
n = 0U;
|
|
} else {
|
|
/* Reserve space for the terminator character. */
|
|
n--;
|
|
}
|
|
|
|
while (*fmt != '\0') {
|
|
left = false;
|
|
padc ='\0';
|
|
padn = 0;
|
|
capitalise = false;
|
|
l_count = 0;
|
|
|
|
if (*fmt == '%') {
|
|
fmt++;
|
|
/* Check the format specifier. */
|
|
loop:
|
|
switch (*fmt) {
|
|
case '%':
|
|
CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
|
|
break;
|
|
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;
|
|
|
|
case 'i':
|
|
case 'd':
|
|
num = get_num_va_args(args, l_count);
|
|
|
|
if (num < 0) {
|
|
CHECK_AND_PUT_CHAR(s, n, chars_printed,
|
|
'-');
|
|
unum = (unsigned int)-num;
|
|
} else {
|
|
unum = (unsigned int)num;
|
|
}
|
|
|
|
unsigned_num_print(&s, n, &chars_printed,
|
|
unum, 10, padc, padn, false);
|
|
break;
|
|
case 'c':
|
|
CHECK_AND_PUT_CHAR(s, n, chars_printed, va_arg(args, int));
|
|
break;
|
|
case 's':
|
|
str = va_arg(args, char *);
|
|
string_print(&s, n, &chars_printed, str);
|
|
break;
|
|
case 'u':
|
|
unum = get_unum_va_args(args, l_count);
|
|
unsigned_num_print(&s, n, &chars_printed,
|
|
unum, 10, padc, padn, false);
|
|
break;
|
|
case 'z':
|
|
l_count = 1;
|
|
fmt++;
|
|
goto loop;
|
|
case 'l':
|
|
l_count++;
|
|
fmt++;
|
|
goto loop;
|
|
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;
|
|
/* fallthrough */
|
|
case 'x':
|
|
unum = get_unum_va_args(args, l_count);
|
|
unsigned_num_print(&s, n, &chars_printed,
|
|
unum, 16, padc, padn,
|
|
capitalise);
|
|
break;
|
|
|
|
default:
|
|
CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
|
|
CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
|
|
}
|
|
fmt++;
|
|
continue;
|
|
}
|
|
|
|
CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
|
|
|
|
fmt++;
|
|
}
|
|
|
|
if (n > 0U) {
|
|
*s = '\0';
|
|
}
|
|
|
|
return (int)chars_printed;
|
|
}
|
|
|
|
/*******************************************************************
|
|
* Reduced snprintf to be used for Trusted firmware.
|
|
* The following type specifiers are supported:
|
|
*
|
|
* %x (or %X) - hexadecimal format
|
|
* %d or %i - signed decimal format
|
|
* %s - string format
|
|
* %u - unsigned decimal format
|
|
* %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)
|
|
*
|
|
* 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.
|
|
*******************************************************************/
|
|
int snprintf(char *s, size_t n, const char *fmt, ...)
|
|
{
|
|
int count;
|
|
va_list all_args;
|
|
|
|
va_start(all_args, fmt);
|
|
count = vsnprintf(s, n, fmt, all_args);
|
|
va_end(all_args);
|
|
|
|
return count;
|
|
}
|
|
|