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 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 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 *******************************************************************/ 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 *******************************************************************/ 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