123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- /*
- * 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;
- }
|