blob: 6ad284f017ac1a08f5d9383f8591cab10cb90614 [file] [log] [blame]
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include <common/debug.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)))
static void string_print(char **s, size_t n, size_t *chars_printed,
const char *str)
{
while (*str != '\0') {
if (*chars_printed < n) {
*(*s) = *str;
(*s)++;
}
(*chars_printed)++;
str++;
}
}
static void unsigned_num_print(char **s, size_t n, size_t *count,
unsigned long long int unum, unsigned int radix,
char padc, int padn)
{
/* Just need enough space to store 64 bit decimal integer */
char num_buf[20];
int i = 0;
int width;
unsigned int rem;
do {
rem = unum % radix;
if (rem < 0xa)
num_buf[i] = '0' + rem;
else
num_buf[i] = 'a' + (rem - 0xa);
i++;
unum /= radix;
} while (unum > 0U);
width = i;
if (padn > 0) {
while (width < padn) {
if (*count < n) {
*(*s) = padc;
(*s)++;
}
(*count)++;
padn--;
}
}
while (--i >= 0) {
if (*count < n) {
*(*s) = num_buf[i];
(*s)++;
}
(*count)++;
}
if (padn < 0) {
while (width < -padn) {
if (*count < n) {
*(*s) = padc;
(*s)++;
}
(*count)++;
padn++;
}
}
}
/*
* Scaled down version of vsnprintf(3).
*/
int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
{
int l_count;
int left;
char *str;
int num;
unsigned long long int unum;
char padc; /* Padding character */
int padn; /* Number of characters to pad */
size_t count = 0U;
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') {
l_count = 0;
left = 0;
padc = '\0';
padn = 0;
if (*fmt == '%') {
fmt++;
/* Check the format specifier. */
loop:
switch (*fmt) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
padc = ' ';
for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
padn = (padn * 10) + (*fmt - '0');
if (left)
padn = -padn;
goto loop;
case '-':
left = 1;
fmt++;
goto loop;
case 'i':
case 'd':
num = get_num_va_args(args, l_count);
if (num < 0) {
if (count < n) {
*s = '-';
s++;
}
count++;
unum = (unsigned int)-num;
} else {
unum = (unsigned int)num;
}
unsigned_num_print(&s, n, &count, unum, 10,
padc, padn);
break;
case 'l':
l_count++;
fmt++;
goto loop;
case 's':
str = va_arg(args, char *);
string_print(&s, n, &count, str);
break;
case 'u':
unum = get_unum_va_args(args, l_count);
unsigned_num_print(&s, n, &count, unum, 10,
padc, padn);
break;
case 'x':
unum = get_unum_va_args(args, l_count);
unsigned_num_print(&s, n, &count, unum, 16,
padc, padn);
break;
case '0':
padc = '0';
padn = 0;
fmt++;
for (;;) {
char ch = *fmt;
if ((ch < '0') || (ch > '9')) {
goto loop;
}
padn = (padn * 10) + (ch - '0');
fmt++;
}
assert(0); /* Unreachable */
default:
/*
* Exit on any other format specifier and abort
* when in debug mode.
*/
WARN("snprintf: specifier with ASCII code '%d' not supported.\n",
*fmt);
assert(0);
return -1;
}
fmt++;
continue;
}
if (count < n) {
*s = *fmt;
s++;
}
fmt++;
count++;
}
if (n > 0U)
*s = '\0';
return (int)count;
}
/*******************************************************************
* Reduced snprintf to be used for Trusted firmware.
* The following type specifiers are supported:
*
* %d or %i - signed decimal format
* %s - string format
* %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.
*******************************************************************/
int snprintf(char *s, size_t n, const char *fmt, ...)
{
va_list args;
int chars_printed;
va_start(args, fmt);
chars_printed = vsnprintf(s, n, fmt, args);
va_end(args);
return chars_printed;
}