1870ce3ddSAntonio Nino Diaz /* 2*a211fde9SYann 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 147981c504SHeyi Guo #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \ 157981c504SHeyi Guo do { \ 167981c504SHeyi Guo if ((chars_printed) < (size)) { \ 177981c504SHeyi Guo *(buf) = (ch); \ 187981c504SHeyi Guo (buf)++; \ 197981c504SHeyi Guo } \ 207981c504SHeyi Guo (chars_printed)++; \ 217981c504SHeyi Guo } while (false) 227981c504SHeyi Guo 23870ce3ddSAntonio Nino Diaz static void string_print(char **s, size_t n, size_t *chars_printed, 24870ce3ddSAntonio Nino Diaz const char *str) 25870ce3ddSAntonio Nino Diaz { 26d5ccb754SAntonio Nino Diaz while (*str != '\0') { 277981c504SHeyi Guo CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str); 28870ce3ddSAntonio Nino Diaz str++; 29870ce3ddSAntonio Nino Diaz } 30870ce3ddSAntonio Nino Diaz } 31870ce3ddSAntonio Nino Diaz 32524eecc6SJavier Almansa Sobrino static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, 33524eecc6SJavier Almansa Sobrino unsigned long long int unum, 34524eecc6SJavier Almansa Sobrino unsigned int radix, char padc, int padn, 35524eecc6SJavier Almansa Sobrino bool capitalise) 36870ce3ddSAntonio Nino Diaz { 37524eecc6SJavier Almansa Sobrino /* Just need enough space to store 64 bit decimal integer */ 38524eecc6SJavier Almansa Sobrino char num_buf[20]; 39d5ccb754SAntonio Nino Diaz int i = 0; 40524eecc6SJavier Almansa Sobrino int width; 41d5ccb754SAntonio Nino Diaz unsigned int rem; 42524eecc6SJavier Almansa Sobrino char ascii_a = capitalise ? 'A' : 'a'; 43870ce3ddSAntonio Nino Diaz 44b30dd403SAndre Przywara if (radix < 10) { 45*a211fde9SYann Gautier ERROR("snprintf: unsupported radix '%u'.", radix); 46b30dd403SAndre Przywara plat_panic_handler(); 47b30dd403SAndre Przywara assert(0); /* Unreachable */ 48b30dd403SAndre Przywara } 49b30dd403SAndre Przywara 50870ce3ddSAntonio Nino Diaz do { 51524eecc6SJavier Almansa Sobrino rem = unum % radix; 52524eecc6SJavier Almansa Sobrino if (rem < 10U) { 53524eecc6SJavier Almansa Sobrino num_buf[i] = '0' + rem; 54524eecc6SJavier Almansa Sobrino } else { 55524eecc6SJavier Almansa Sobrino num_buf[i] = ascii_a + (rem - 10U); 56524eecc6SJavier Almansa Sobrino } 57524eecc6SJavier Almansa Sobrino i++; 58524eecc6SJavier Almansa Sobrino unum /= radix; 59d5ccb754SAntonio Nino Diaz } while (unum > 0U); 60870ce3ddSAntonio Nino Diaz 61524eecc6SJavier Almansa Sobrino width = i; 62c1f5a092SAndre Przywara for (i = padn - width; i > 0; i--) { 63c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 64d5ccb754SAntonio Nino Diaz } 65c1f5a092SAndre Przywara for (i = width; i > 0; i--) { 66c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]); 67524eecc6SJavier Almansa Sobrino } 68c1f5a092SAndre Przywara for (i = width + padn; i < 0; i++) { 69c1f5a092SAndre Przywara CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 70870ce3ddSAntonio Nino Diaz } 71870ce3ddSAntonio Nino Diaz } 72870ce3ddSAntonio Nino Diaz 73870ce3ddSAntonio Nino Diaz /******************************************************************* 7477648689SMadhukar Pappireddy * Reduced vsnprintf to be used for Trusted firmware. 75870ce3ddSAntonio Nino Diaz * The following type specifiers are supported: 76870ce3ddSAntonio Nino Diaz * 77524eecc6SJavier Almansa Sobrino * %x (or %X) - hexadecimal format 78870ce3ddSAntonio Nino Diaz * %d or %i - signed decimal format 79870ce3ddSAntonio Nino Diaz * %s - string format 80870ce3ddSAntonio Nino Diaz * %u - unsigned decimal format 81524eecc6SJavier Almansa Sobrino * %p - pointer format 82524eecc6SJavier Almansa Sobrino * 83524eecc6SJavier Almansa Sobrino * The following padding specifiers are supported by this print 84524eecc6SJavier Almansa Sobrino * %0NN - Left-pad the number with 0s (NN is a decimal number) 85524eecc6SJavier Almansa Sobrino * %NN - Left-pad the number or string with spaces (NN is a decimal number) 86524eecc6SJavier Almansa Sobrino * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 87870ce3ddSAntonio Nino Diaz * 88870ce3ddSAntonio Nino Diaz * The function panics on all other formats specifiers. 89870ce3ddSAntonio Nino Diaz * 90870ce3ddSAntonio Nino Diaz * It returns the number of characters that would be written if the 91870ce3ddSAntonio Nino Diaz * buffer was big enough. If it returns a value lower than n, the 92870ce3ddSAntonio Nino Diaz * whole string has been written. 93870ce3ddSAntonio Nino Diaz *******************************************************************/ 9477648689SMadhukar Pappireddy int vsnprintf(char *s, size_t n, const char *fmt, va_list args) 95870ce3ddSAntonio Nino Diaz { 96870ce3ddSAntonio Nino Diaz int num; 97524eecc6SJavier Almansa Sobrino unsigned long long int unum; 98870ce3ddSAntonio Nino Diaz char *str; 99524eecc6SJavier Almansa Sobrino char padc; /* Padding character */ 100524eecc6SJavier Almansa Sobrino int padn; /* Number of characters to pad */ 101524eecc6SJavier Almansa Sobrino bool left; 102524eecc6SJavier Almansa Sobrino bool capitalise; 103d5ccb754SAntonio Nino Diaz size_t chars_printed = 0U; 104870ce3ddSAntonio Nino Diaz 105d5ccb754SAntonio Nino Diaz if (n == 0U) { 106d5ccb754SAntonio Nino Diaz /* There isn't space for anything. */ 107d5ccb754SAntonio Nino Diaz } else if (n == 1U) { 108870ce3ddSAntonio Nino Diaz /* Buffer is too small to actually write anything else. */ 109870ce3ddSAntonio Nino Diaz *s = '\0'; 110d5ccb754SAntonio Nino Diaz n = 0U; 111d5ccb754SAntonio Nino Diaz } else { 112870ce3ddSAntonio Nino Diaz /* Reserve space for the terminator character. */ 113870ce3ddSAntonio Nino Diaz n--; 114870ce3ddSAntonio Nino Diaz } 115870ce3ddSAntonio Nino Diaz 116d5ccb754SAntonio Nino Diaz while (*fmt != '\0') { 117524eecc6SJavier Almansa Sobrino left = false; 118524eecc6SJavier Almansa Sobrino padc ='\0'; 119524eecc6SJavier Almansa Sobrino padn = 0; 120524eecc6SJavier Almansa Sobrino capitalise = false; 121870ce3ddSAntonio Nino Diaz 122870ce3ddSAntonio Nino Diaz if (*fmt == '%') { 123870ce3ddSAntonio Nino Diaz fmt++; 124870ce3ddSAntonio Nino Diaz /* Check the format specifier. */ 125524eecc6SJavier Almansa Sobrino loop: 126870ce3ddSAntonio Nino Diaz switch (*fmt) { 127c6546154SHeyi Guo case '%': 1287981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 129c6546154SHeyi Guo break; 130524eecc6SJavier Almansa Sobrino case '0': 131524eecc6SJavier Almansa Sobrino case '1': 132524eecc6SJavier Almansa Sobrino case '2': 133524eecc6SJavier Almansa Sobrino case '3': 134524eecc6SJavier Almansa Sobrino case '4': 135524eecc6SJavier Almansa Sobrino case '5': 136524eecc6SJavier Almansa Sobrino case '6': 137524eecc6SJavier Almansa Sobrino case '7': 138524eecc6SJavier Almansa Sobrino case '8': 139524eecc6SJavier Almansa Sobrino case '9': 140524eecc6SJavier Almansa Sobrino padc = (*fmt == '0') ? '0' : ' '; 141524eecc6SJavier Almansa Sobrino for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 142524eecc6SJavier Almansa Sobrino padn = (padn * 10) + (*fmt - '0'); 143524eecc6SJavier Almansa Sobrino } 144524eecc6SJavier Almansa Sobrino if (left) { 145524eecc6SJavier Almansa Sobrino padn = -padn; 146524eecc6SJavier Almansa Sobrino } 147524eecc6SJavier Almansa Sobrino goto loop; 148524eecc6SJavier Almansa Sobrino case '-': 149524eecc6SJavier Almansa Sobrino left = true; 150524eecc6SJavier Almansa Sobrino fmt++; 151524eecc6SJavier Almansa Sobrino goto loop; 152524eecc6SJavier Almansa Sobrino 153870ce3ddSAntonio Nino Diaz case 'i': 154870ce3ddSAntonio Nino Diaz case 'd': 155870ce3ddSAntonio Nino Diaz num = va_arg(args, int); 156870ce3ddSAntonio Nino Diaz 157870ce3ddSAntonio Nino Diaz if (num < 0) { 1587981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, 1597981c504SHeyi Guo '-'); 160870ce3ddSAntonio Nino Diaz unum = (unsigned int)-num; 161870ce3ddSAntonio Nino Diaz } else { 162870ce3ddSAntonio Nino Diaz unum = (unsigned int)num; 163870ce3ddSAntonio Nino Diaz } 164870ce3ddSAntonio Nino Diaz 165524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 166524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false); 167870ce3ddSAntonio Nino Diaz break; 168870ce3ddSAntonio Nino Diaz case 's': 169870ce3ddSAntonio Nino Diaz str = va_arg(args, char *); 170870ce3ddSAntonio Nino Diaz string_print(&s, n, &chars_printed, str); 171870ce3ddSAntonio Nino Diaz break; 172870ce3ddSAntonio Nino Diaz case 'u': 173870ce3ddSAntonio Nino Diaz unum = va_arg(args, unsigned int); 174524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 175524eecc6SJavier Almansa Sobrino unum, 10, padc, padn, false); 176870ce3ddSAntonio Nino Diaz break; 177524eecc6SJavier Almansa Sobrino case 'p': 178524eecc6SJavier Almansa Sobrino unum = (uintptr_t)va_arg(args, void *); 179524eecc6SJavier Almansa Sobrino if (unum > 0U) { 180524eecc6SJavier Almansa Sobrino string_print(&s, n, &chars_printed, "0x"); 181524eecc6SJavier Almansa Sobrino padn -= 2; 182524eecc6SJavier Almansa Sobrino } 183524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 184524eecc6SJavier Almansa Sobrino unum, 16, padc, padn, false); 185524eecc6SJavier Almansa Sobrino break; 186524eecc6SJavier Almansa Sobrino case 'X': 187524eecc6SJavier Almansa Sobrino capitalise = true; 188524eecc6SJavier Almansa Sobrino case 'x': 189524eecc6SJavier Almansa Sobrino unum = va_arg(args, unsigned int); 190524eecc6SJavier Almansa Sobrino unsigned_num_print(&s, n, &chars_printed, 191524eecc6SJavier Almansa Sobrino unum, 16, padc, padn, 192524eecc6SJavier Almansa Sobrino capitalise); 193524eecc6SJavier Almansa Sobrino break; 194524eecc6SJavier Almansa Sobrino 195870ce3ddSAntonio Nino Diaz default: 196870ce3ddSAntonio Nino Diaz /* Panic on any other format specifier. */ 197870ce3ddSAntonio Nino Diaz ERROR("snprintf: specifier with ASCII code '%d' not supported.", 198870ce3ddSAntonio Nino Diaz *fmt); 199870ce3ddSAntonio Nino Diaz plat_panic_handler(); 200a08a2014SDaniel Boulby assert(0); /* Unreachable */ 201870ce3ddSAntonio Nino Diaz } 202870ce3ddSAntonio Nino Diaz fmt++; 203870ce3ddSAntonio Nino Diaz continue; 204870ce3ddSAntonio Nino Diaz } 205870ce3ddSAntonio Nino Diaz 2067981c504SHeyi Guo CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 207d5ccb754SAntonio Nino Diaz 208870ce3ddSAntonio Nino Diaz fmt++; 209870ce3ddSAntonio Nino Diaz } 210870ce3ddSAntonio Nino Diaz 21177648689SMadhukar Pappireddy if (n > 0U) { 212870ce3ddSAntonio Nino Diaz *s = '\0'; 21377648689SMadhukar Pappireddy } 214870ce3ddSAntonio Nino Diaz 215d5ccb754SAntonio Nino Diaz return (int)chars_printed; 216870ce3ddSAntonio Nino Diaz } 21777648689SMadhukar Pappireddy 21877648689SMadhukar Pappireddy /******************************************************************* 21977648689SMadhukar Pappireddy * Reduced snprintf to be used for Trusted firmware. 22077648689SMadhukar Pappireddy * The following type specifiers are supported: 22177648689SMadhukar Pappireddy * 22277648689SMadhukar Pappireddy * %x (or %X) - hexadecimal format 22377648689SMadhukar Pappireddy * %d or %i - signed decimal format 22477648689SMadhukar Pappireddy * %s - string format 22577648689SMadhukar Pappireddy * %u - unsigned decimal format 22677648689SMadhukar Pappireddy * %p - pointer format 22777648689SMadhukar Pappireddy * 22877648689SMadhukar Pappireddy * The following padding specifiers are supported by this print 22977648689SMadhukar Pappireddy * %0NN - Left-pad the number with 0s (NN is a decimal number) 23077648689SMadhukar Pappireddy * %NN - Left-pad the number or string with spaces (NN is a decimal number) 23177648689SMadhukar Pappireddy * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 23277648689SMadhukar Pappireddy * 23377648689SMadhukar Pappireddy * The function panics on all other formats specifiers. 23477648689SMadhukar Pappireddy * 23577648689SMadhukar Pappireddy * It returns the number of characters that would be written if the 23677648689SMadhukar Pappireddy * buffer was big enough. If it returns a value lower than n, the 23777648689SMadhukar Pappireddy * whole string has been written. 23877648689SMadhukar Pappireddy *******************************************************************/ 23977648689SMadhukar Pappireddy int snprintf(char *s, size_t n, const char *fmt, ...) 24077648689SMadhukar Pappireddy { 24177648689SMadhukar Pappireddy int count; 24277648689SMadhukar Pappireddy va_list all_args; 24377648689SMadhukar Pappireddy 24477648689SMadhukar Pappireddy va_start(all_args, fmt); 24577648689SMadhukar Pappireddy count = vsnprintf(s, n, fmt, all_args); 24677648689SMadhukar Pappireddy va_end(all_args); 24777648689SMadhukar Pappireddy 24877648689SMadhukar Pappireddy return count; 24977648689SMadhukar Pappireddy } 250