1870ce3ddSAntonio Nino Diaz /* 2a211fde9SYann Gautier * Copyright (c) 2017-2022, 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 88870ce3ddSAntonio Nino Diaz * %s - string format 89870ce3ddSAntonio Nino Diaz * %u - unsigned decimal format 90524eecc6SJavier Almansa Sobrino * %p - pointer format 91524eecc6SJavier Almansa Sobrino * 92701e94b0Skadabi * The following length specifiers are supported by this print 93701e94b0Skadabi * %l - long int 94701e94b0Skadabi * %ll - long long int 95701e94b0Skadabi * %z - size_t sized integer formats 96701e94b0Skadabi * 97524eecc6SJavier Almansa Sobrino * The following padding specifiers are supported by this print 98524eecc6SJavier Almansa Sobrino * %0NN - Left-pad the number with 0s (NN is a decimal number) 99524eecc6SJavier Almansa Sobrino * %NN - Left-pad the number or string with spaces (NN is a decimal number) 100524eecc6SJavier Almansa Sobrino * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 101870ce3ddSAntonio Nino Diaz * 102870ce3ddSAntonio Nino Diaz * The function panics on all other formats specifiers. 103870ce3ddSAntonio Nino Diaz * 104870ce3ddSAntonio Nino Diaz * It returns the number of characters that would be written if the 105870ce3ddSAntonio Nino Diaz * buffer was big enough. If it returns a value lower than n, the 106870ce3ddSAntonio Nino Diaz * whole string has been written. 107870ce3ddSAntonio Nino Diaz *******************************************************************/ 10877648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args) 109870ce3ddSAntonio Nino Diaz { 110870ce3ddSAntonio Nino Diaz int num; 111524eecc6SJavier Almansa Sobrino unsigned long long int unum; 112870ce3ddSAntonio Nino Diaz char *str; 113524eecc6SJavier Almansa Sobrino char padc; /* Padding character */ 114524eecc6SJavier Almansa Sobrino int padn; /* Number of characters to pad */ 115524eecc6SJavier Almansa Sobrino bool left; 116524eecc6SJavier Almansa Sobrino bool capitalise; 117d5ccb754SAntonio Nino Diaz size_t chars_printed = 0U; 118701e94b0Skadabi unsigned int l_count; 119870ce3ddSAntonio Nino Diaz 120d5ccb754SAntonio Nino Diaz if (n == 0U) { 121d5ccb754SAntonio Nino Diaz /* There isn't space for anything. */ 122d5ccb754SAntonio Nino Diaz } else if (n == 1U) { 123870ce3ddSAntonio Nino Diaz /* Buffer is too small to actually write anything else. */ 124870ce3ddSAntonio Nino Diaz *s = '\0'; 125d5ccb754SAntonio Nino Diaz n = 0U; 126d5ccb754SAntonio Nino Diaz } else { 127870ce3ddSAntonio Nino Diaz /* Reserve space for the terminator character. */ 128870ce3ddSAntonio Nino Diaz n--; 129870ce3ddSAntonio Nino Diaz } 130870ce3ddSAntonio Nino Diaz 131d5ccb754SAntonio Nino Diaz while (*fmt != '\0') { 132524eecc6SJavier Almansa Sobrino left = false; 133524eecc6SJavier Almansa Sobrino padc ='\0'; 134524eecc6SJavier Almansa Sobrino padn = 0; 135524eecc6SJavier Almansa Sobrino capitalise = false; 136701e94b0Skadabi l_count = 0; 137870ce3ddSAntonio Nino Diaz 138870ce3ddSAntonio Nino Diaz if (*fmt == '%') { 139870ce3ddSAntonio Nino Diaz fmt++; 140870ce3ddSAntonio Nino Diaz /* Check the format specifier. */ 141524eecc6SJavier Almansa Sobrino loop: 142870ce3ddSAntonio Nino Diaz switch (*fmt) { 143c6546154SHeyi Guo case '%': 1447981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 145c6546154SHeyi Guo break; 146524eecc6SJavier Almansa Sobrino case '0': 147524eecc6SJavier Almansa Sobrino case '1': 148524eecc6SJavier Almansa Sobrino case '2': 149524eecc6SJavier Almansa Sobrino case '3': 150524eecc6SJavier Almansa Sobrino case '4': 151524eecc6SJavier Almansa Sobrino case '5': 152524eecc6SJavier Almansa Sobrino case '6': 153524eecc6SJavier Almansa Sobrino case '7': 154524eecc6SJavier Almansa Sobrino case '8': 155524eecc6SJavier Almansa Sobrino case '9': 156524eecc6SJavier Almansa Sobrino padc = (*fmt == '0') ? '0' : ' '; 157524eecc6SJavier Almansa Sobrino for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 158524eecc6SJavier Almansa Sobrino padn = (padn * 10) + (*fmt - '0'); 159524eecc6SJavier Almansa Sobrino } 160524eecc6SJavier Almansa Sobrino if (left) { 161524eecc6SJavier Almansa Sobrino padn = -padn; 162524eecc6SJavier Almansa Sobrino } 163524eecc6SJavier Almansa Sobrino goto loop; 164524eecc6SJavier Almansa Sobrino case '-': 165524eecc6SJavier Almansa Sobrino left = true; 166524eecc6SJavier Almansa Sobrino fmt++; 167524eecc6SJavier Almansa Sobrino goto loop; 168524eecc6SJavier Almansa Sobrino 169870ce3ddSAntonio Nino Diaz case 'i': 170870ce3ddSAntonio Nino Diaz case 'd': 171701e94b0Skadabi num = get_num_va_args(args, l_count); 172870ce3ddSAntonio Nino Diaz 173870ce3ddSAntonio Nino Diaz if (num < 0) { 1747981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, 1757981c504SHeyi Guo '-'); 176870ce3ddSAntonio Nino Diaz unum = (unsigned int)-num; 177870ce3ddSAntonio Nino Diaz } else { 178870ce3ddSAntonio Nino Diaz unum = (unsigned int)num; 179870ce3ddSAntonio Nino Diaz } 180870ce3ddSAntonio Nino Diaz 181524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 182524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false); 183870ce3ddSAntonio Nino Diaz break; 184870ce3ddSAntonio Nino Diaz case 's': 185870ce3ddSAntonio Nino Diaz str = va_arg(args, char *); 186870ce3ddSAntonio Nino Diaz string_print(&s, n, &chars_printed, str); 187870ce3ddSAntonio Nino Diaz break; 188870ce3ddSAntonio Nino Diaz case 'u': 189701e94b0Skadabi unum = get_unum_va_args(args, l_count); 190524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 191524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false); 192870ce3ddSAntonio Nino Diaz break; 193701e94b0Skadabi case 'z': 194701e94b0Skadabi l_count = 1; 195701e94b0Skadabi fmt++; 196701e94b0Skadabi goto loop; 197701e94b0Skadabi case 'l': 198701e94b0Skadabi l_count++; 199701e94b0Skadabi fmt++; 200701e94b0Skadabi goto loop; 201524eecc6SJavier Almansa Sobrino case 'p': 202524eecc6SJavier Almansa Sobrino unum = (uintptr_t)va_arg(args, void *); 203524eecc6SJavier Almansa Sobrino if (unum > 0U) { 204524eecc6SJavier Almansa Sobrino string_print(&s, n, &chars_printed, "0x"); 205524eecc6SJavier Almansa Sobrino padn -= 2; 206524eecc6SJavier Almansa Sobrino } 207524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 208524eecc6SJavier Almansa Sobrino unum, 16, padc, padn, false); 209524eecc6SJavier Almansa Sobrino break; 210524eecc6SJavier Almansa Sobrino case 'X': 211524eecc6SJavier Almansa Sobrino capitalise = true; 212*e138400dSBoyan Karatotev /* fallthrough */ 213524eecc6SJavier Almansa Sobrino case 'x': 214701e94b0Skadabi unum = get_unum_va_args(args, l_count); 215524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 216524eecc6SJavier Almansa Sobrino unum, 16, padc, padn, 217524eecc6SJavier Almansa Sobrino capitalise); 218524eecc6SJavier Almansa Sobrino break; 219524eecc6SJavier Almansa Sobrino 220870ce3ddSAntonio Nino Diaz default: 221885e2683SClaus Pedersen CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 222885e2683SClaus Pedersen CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 223870ce3ddSAntonio Nino Diaz } 224870ce3ddSAntonio Nino Diaz fmt++; 225870ce3ddSAntonio Nino Diaz continue; 226870ce3ddSAntonio Nino Diaz } 227870ce3ddSAntonio Nino Diaz 2287981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 229d5ccb754SAntonio Nino Diaz 230870ce3ddSAntonio Nino Diaz fmt++; 231870ce3ddSAntonio Nino Diaz } 232870ce3ddSAntonio Nino Diaz 23377648689SMadhukar Pappireddy if (n > 0U) { 234870ce3ddSAntonio Nino Diaz *s = '\0'; 23577648689SMadhukar Pappireddy } 236870ce3ddSAntonio Nino Diaz 237d5ccb754SAntonio Nino Diaz return (int)chars_printed; 238870ce3ddSAntonio Nino Diaz } 23977648689SMadhukar Pappireddy 24077648689SMadhukar Pappireddy /******************************************************************* 24177648689SMadhukar Pappireddy * Reduced snprintf to be used for Trusted firmware. 24277648689SMadhukar Pappireddy * The following type specifiers are supported: 24377648689SMadhukar Pappireddy * 24477648689SMadhukar Pappireddy * %x (or %X) - hexadecimal format 24577648689SMadhukar Pappireddy * %d or %i - signed decimal format 24677648689SMadhukar Pappireddy * %s - string format 24777648689SMadhukar Pappireddy * %u - unsigned decimal format 24877648689SMadhukar Pappireddy * %p - pointer format 24977648689SMadhukar Pappireddy * 25077648689SMadhukar Pappireddy * The following padding specifiers are supported by this print 25177648689SMadhukar Pappireddy * %0NN - Left-pad the number with 0s (NN is a decimal number) 25277648689SMadhukar Pappireddy * %NN - Left-pad the number or string with spaces (NN is a decimal number) 25377648689SMadhukar Pappireddy * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 25477648689SMadhukar Pappireddy * 25577648689SMadhukar Pappireddy * The function panics on all other formats specifiers. 25677648689SMadhukar Pappireddy * 25777648689SMadhukar Pappireddy * It returns the number of characters that would be written if the 25877648689SMadhukar Pappireddy * buffer was big enough. If it returns a value lower than n, the 25977648689SMadhukar Pappireddy * whole string has been written. 26077648689SMadhukar Pappireddy *******************************************************************/ 26177648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...) 26277648689SMadhukar Pappireddy { 26377648689SMadhukar Pappireddy int count; 26477648689SMadhukar Pappireddy va_list all_args; 26577648689SMadhukar Pappireddy 26677648689SMadhukar Pappireddy va_start(all_args, fmt); 26777648689SMadhukar Pappireddy count = vsnprintf(s, n, fmt, all_args); 26877648689SMadhukar Pappireddy va_end(all_args); 26977648689SMadhukar Pappireddy 27077648689SMadhukar Pappireddy return count; 27177648689SMadhukar Pappireddy } 272