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> 9410c925aSAndre Przywara #include <stdint.h> 10870ce3ddSAntonio Nino Diaz 1109d40e0eSAntonio Nino Diaz #include <common/debug.h> 1209d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 1309d40e0eSAntonio Nino Diaz 14*701e94b0Skadabi #define get_num_va_args(_args, _lcount) \ 15*701e94b0Skadabi (((_lcount) > 1) ? va_arg(_args, long long int) : \ 16*701e94b0Skadabi (((_lcount) == 1) ? va_arg(_args, long int) : \ 17*701e94b0Skadabi va_arg(_args, int))) 18*701e94b0Skadabi 19*701e94b0Skadabi #define get_unum_va_args(_args, _lcount) \ 20*701e94b0Skadabi (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \ 21*701e94b0Skadabi (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \ 22*701e94b0Skadabi va_arg(_args, unsigned int))) 23*701e94b0Skadabi 247981c504SHeyi Guo #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \ 257981c504SHeyi Guo do { \ 267981c504SHeyi Guo if ((chars_printed) < (size)) { \ 277981c504SHeyi Guo *(buf) = (ch); \ 287981c504SHeyi Guo (buf)++; \ 297981c504SHeyi Guo } \ 307981c504SHeyi Guo (chars_printed)++; \ 317981c504SHeyi Guo } while (false) 327981c504SHeyi Guo 33870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed, 34870ce3ddSAntonio Nino Diaz const char *str) 35870ce3ddSAntonio Nino Diaz { 36d5ccb754SAntonio Nino Diaz while (*str != '\0') { 377981c504SHeyi Guo CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str); 38870ce3ddSAntonio Nino Diaz str++; 39870ce3ddSAntonio Nino Diaz } 40870ce3ddSAntonio Nino Diaz } 41870ce3ddSAntonio Nino Diaz 42524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, 43524eecc6SJavier Almansa Sobrino unsigned long long int unum, 44524eecc6SJavier Almansa Sobrino unsigned int radix, char padc, int padn, 45524eecc6SJavier Almansa Sobrino bool capitalise) 46870ce3ddSAntonio Nino Diaz { 47524eecc6SJavier Almansa Sobrino /* Just need enough space to store 64 bit decimal integer */ 48524eecc6SJavier Almansa Sobrino char num_buf[20]; 49d5ccb754SAntonio Nino Diaz int i = 0; 50524eecc6SJavier Almansa Sobrino int width; 51d5ccb754SAntonio Nino Diaz unsigned int rem; 52524eecc6SJavier Almansa Sobrino char ascii_a = capitalise ? 'A' : 'a'; 53870ce3ddSAntonio Nino Diaz 54b30dd403SAndre Przywara if (radix < 10) { 55a211fde9SYann Gautier ERROR("snprintf: unsupported radix '%u'.", radix); 56b30dd403SAndre Przywara plat_panic_handler(); 57b30dd403SAndre Przywara assert(0); /* Unreachable */ 58b30dd403SAndre Przywara } 59b30dd403SAndre Przywara 60870ce3ddSAntonio Nino Diaz do { 61524eecc6SJavier Almansa Sobrino rem = unum % radix; 62524eecc6SJavier Almansa Sobrino if (rem < 10U) { 63524eecc6SJavier Almansa Sobrino num_buf[i] = '0' + rem; 64524eecc6SJavier Almansa Sobrino } else { 65524eecc6SJavier Almansa Sobrino num_buf[i] = ascii_a + (rem - 10U); 66524eecc6SJavier Almansa Sobrino } 67524eecc6SJavier Almansa Sobrino i++; 68524eecc6SJavier Almansa Sobrino unum /= radix; 69d5ccb754SAntonio Nino Diaz } while (unum > 0U); 70870ce3ddSAntonio Nino Diaz 71524eecc6SJavier Almansa Sobrino width = i; 72c1f5a092SAndre Przywara for (i = padn - width; i > 0; i--) { 73c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 74d5ccb754SAntonio Nino Diaz } 75c1f5a092SAndre Przywara for (i = width; i > 0; i--) { 76c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]); 77524eecc6SJavier Almansa Sobrino } 78c1f5a092SAndre Przywara for (i = width + padn; i < 0; i++) { 79c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 80870ce3ddSAntonio Nino Diaz } 81870ce3ddSAntonio Nino Diaz } 82870ce3ddSAntonio Nino Diaz 83870ce3ddSAntonio Nino Diaz /******************************************************************* 8477648689SMadhukar Pappireddy * Reduced vsnprintf to be used for Trusted firmware. 85870ce3ddSAntonio Nino Diaz * The following type specifiers are supported: 86870ce3ddSAntonio Nino Diaz * 87524eecc6SJavier Almansa Sobrino * %x (or %X) - hexadecimal format 88870ce3ddSAntonio Nino Diaz * %d or %i - signed decimal format 89870ce3ddSAntonio Nino Diaz * %s - string format 90870ce3ddSAntonio Nino Diaz * %u - unsigned decimal format 91524eecc6SJavier Almansa Sobrino * %p - pointer format 92524eecc6SJavier Almansa Sobrino * 93*701e94b0Skadabi * The following length specifiers are supported by this print 94*701e94b0Skadabi * %l - long int 95*701e94b0Skadabi * %ll - long long int 96*701e94b0Skadabi * %z - size_t sized integer formats 97*701e94b0Skadabi * 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; 119*701e94b0Skadabi 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; 137*701e94b0Skadabi 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': 172*701e94b0Skadabi 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; 185870ce3ddSAntonio Nino Diaz case 's': 186870ce3ddSAntonio Nino Diaz str = va_arg(args, char *); 187870ce3ddSAntonio Nino Diaz string_print(&s, n, &chars_printed, str); 188870ce3ddSAntonio Nino Diaz break; 189870ce3ddSAntonio Nino Diaz case 'u': 190*701e94b0Skadabi unum = get_unum_va_args(args, l_count); 191524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 192524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false); 193870ce3ddSAntonio Nino Diaz break; 194*701e94b0Skadabi case 'z': 195*701e94b0Skadabi l_count = 1; 196*701e94b0Skadabi fmt++; 197*701e94b0Skadabi goto loop; 198*701e94b0Skadabi case 'l': 199*701e94b0Skadabi l_count++; 200*701e94b0Skadabi fmt++; 201*701e94b0Skadabi goto loop; 202524eecc6SJavier Almansa Sobrino case 'p': 203524eecc6SJavier Almansa Sobrino unum = (uintptr_t)va_arg(args, void *); 204524eecc6SJavier Almansa Sobrino if (unum > 0U) { 205524eecc6SJavier Almansa Sobrino string_print(&s, n, &chars_printed, "0x"); 206524eecc6SJavier Almansa Sobrino padn -= 2; 207524eecc6SJavier Almansa Sobrino } 208524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 209524eecc6SJavier Almansa Sobrino unum, 16, padc, padn, false); 210524eecc6SJavier Almansa Sobrino break; 211524eecc6SJavier Almansa Sobrino case 'X': 212524eecc6SJavier Almansa Sobrino capitalise = true; 213524eecc6SJavier Almansa Sobrino case 'x': 214*701e94b0Skadabi 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: 221870ce3ddSAntonio Nino Diaz /* Panic on any other format specifier. */ 222870ce3ddSAntonio Nino Diaz ERROR("snprintf: specifier with ASCII code '%d' not supported.", 223870ce3ddSAntonio Nino Diaz *fmt); 224870ce3ddSAntonio Nino Diaz plat_panic_handler(); 225a08a2014SDaniel Boulby assert(0); /* Unreachable */ 226870ce3ddSAntonio Nino Diaz } 227870ce3ddSAntonio Nino Diaz fmt++; 228870ce3ddSAntonio Nino Diaz continue; 229870ce3ddSAntonio Nino Diaz } 230870ce3ddSAntonio Nino Diaz 2317981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 232d5ccb754SAntonio Nino Diaz 233870ce3ddSAntonio Nino Diaz fmt++; 234870ce3ddSAntonio Nino Diaz } 235870ce3ddSAntonio Nino Diaz 23677648689SMadhukar Pappireddy if (n > 0U) { 237870ce3ddSAntonio Nino Diaz *s = '\0'; 23877648689SMadhukar Pappireddy } 239870ce3ddSAntonio Nino Diaz 240d5ccb754SAntonio Nino Diaz return (int)chars_printed; 241870ce3ddSAntonio Nino Diaz } 24277648689SMadhukar Pappireddy 24377648689SMadhukar Pappireddy /******************************************************************* 24477648689SMadhukar Pappireddy * Reduced snprintf to be used for Trusted firmware. 24577648689SMadhukar Pappireddy * The following type specifiers are supported: 24677648689SMadhukar Pappireddy * 24777648689SMadhukar Pappireddy * %x (or %X) - hexadecimal format 24877648689SMadhukar Pappireddy * %d or %i - signed decimal format 24977648689SMadhukar Pappireddy * %s - string format 25077648689SMadhukar Pappireddy * %u - unsigned decimal format 25177648689SMadhukar Pappireddy * %p - pointer format 25277648689SMadhukar Pappireddy * 25377648689SMadhukar Pappireddy * The following padding specifiers are supported by this print 25477648689SMadhukar Pappireddy * %0NN - Left-pad the number with 0s (NN is a decimal number) 25577648689SMadhukar Pappireddy * %NN - Left-pad the number or string with spaces (NN is a decimal number) 25677648689SMadhukar Pappireddy * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 25777648689SMadhukar Pappireddy * 25877648689SMadhukar Pappireddy * The function panics on all other formats specifiers. 25977648689SMadhukar Pappireddy * 26077648689SMadhukar Pappireddy * It returns the number of characters that would be written if the 26177648689SMadhukar Pappireddy * buffer was big enough. If it returns a value lower than n, the 26277648689SMadhukar Pappireddy * whole string has been written. 26377648689SMadhukar Pappireddy *******************************************************************/ 26477648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...) 26577648689SMadhukar Pappireddy { 26677648689SMadhukar Pappireddy int count; 26777648689SMadhukar Pappireddy va_list all_args; 26877648689SMadhukar Pappireddy 26977648689SMadhukar Pappireddy va_start(all_args, fmt); 27077648689SMadhukar Pappireddy count = vsnprintf(s, n, fmt, all_args); 27177648689SMadhukar Pappireddy va_end(all_args); 27277648689SMadhukar Pappireddy 27377648689SMadhukar Pappireddy return count; 27477648689SMadhukar Pappireddy } 275