1 /* 2 * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <stdarg.h> 9 10 #include <common/debug.h> 11 #include <plat/common/platform.h> 12 13 #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \ 14 do { \ 15 if ((chars_printed) < (size)) { \ 16 *(buf) = (ch); \ 17 (buf)++; \ 18 } \ 19 (chars_printed)++; \ 20 } while (false) 21 22 static void string_print(char **s, size_t n, size_t *chars_printed, 23 const char *str) 24 { 25 while (*str != '\0') { 26 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str); 27 str++; 28 } 29 } 30 31 static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, 32 unsigned long long int unum, 33 unsigned int radix, char padc, int padn, 34 bool capitalise) 35 { 36 /* Just need enough space to store 64 bit decimal integer */ 37 char num_buf[20]; 38 int i = 0; 39 int width; 40 unsigned int rem; 41 char ascii_a = capitalise ? 'A' : 'a'; 42 43 do { 44 rem = unum % radix; 45 if (rem < 10U) { 46 num_buf[i] = '0' + rem; 47 } else { 48 num_buf[i] = ascii_a + (rem - 10U); 49 } 50 i++; 51 unum /= radix; 52 } while (unum > 0U); 53 54 width = i; 55 for (i = padn - width; i > 0; i--) { 56 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 57 } 58 for (i = width; i > 0; i--) { 59 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]); 60 } 61 for (i = width + padn; i < 0; i++) { 62 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 63 } 64 } 65 66 /******************************************************************* 67 * Reduced vsnprintf to be used for Trusted firmware. 68 * The following type specifiers are supported: 69 * 70 * %x (or %X) - hexadecimal format 71 * %d or %i - signed decimal format 72 * %s - string format 73 * %u - unsigned decimal format 74 * %p - pointer format 75 * 76 * The following padding specifiers are supported by this print 77 * %0NN - Left-pad the number with 0s (NN is a decimal number) 78 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 79 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 80 * 81 * The function panics on all other formats specifiers. 82 * 83 * It returns the number of characters that would be written if the 84 * buffer was big enough. If it returns a value lower than n, the 85 * whole string has been written. 86 *******************************************************************/ 87 int vsnprintf(char *s, size_t n, const char *fmt, va_list args) 88 { 89 int num; 90 unsigned long long int unum; 91 char *str; 92 char padc; /* Padding character */ 93 int padn; /* Number of characters to pad */ 94 bool left; 95 bool capitalise; 96 size_t chars_printed = 0U; 97 98 if (n == 0U) { 99 /* There isn't space for anything. */ 100 } else if (n == 1U) { 101 /* Buffer is too small to actually write anything else. */ 102 *s = '\0'; 103 n = 0U; 104 } else { 105 /* Reserve space for the terminator character. */ 106 n--; 107 } 108 109 while (*fmt != '\0') { 110 left = false; 111 padc ='\0'; 112 padn = 0; 113 capitalise = false; 114 115 if (*fmt == '%') { 116 fmt++; 117 /* Check the format specifier. */ 118 loop: 119 switch (*fmt) { 120 case '%': 121 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 122 break; 123 case '0': 124 case '1': 125 case '2': 126 case '3': 127 case '4': 128 case '5': 129 case '6': 130 case '7': 131 case '8': 132 case '9': 133 padc = (*fmt == '0') ? '0' : ' '; 134 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 135 padn = (padn * 10) + (*fmt - '0'); 136 } 137 if (left) { 138 padn = -padn; 139 } 140 goto loop; 141 case '-': 142 left = true; 143 fmt++; 144 goto loop; 145 146 case 'i': 147 case 'd': 148 num = va_arg(args, int); 149 150 if (num < 0) { 151 CHECK_AND_PUT_CHAR(s, n, chars_printed, 152 '-'); 153 unum = (unsigned int)-num; 154 } else { 155 unum = (unsigned int)num; 156 } 157 158 unsigned_num_print(&s, n, &chars_printed, 159 unum, 10, padc, padn, false); 160 break; 161 case 's': 162 str = va_arg(args, char *); 163 string_print(&s, n, &chars_printed, str); 164 break; 165 case 'u': 166 unum = va_arg(args, unsigned int); 167 unsigned_num_print(&s, n, &chars_printed, 168 unum, 10, padc, padn, false); 169 break; 170 case 'p': 171 unum = (uintptr_t)va_arg(args, void *); 172 if (unum > 0U) { 173 string_print(&s, n, &chars_printed, "0x"); 174 padn -= 2; 175 } 176 unsigned_num_print(&s, n, &chars_printed, 177 unum, 16, padc, padn, false); 178 break; 179 case 'X': 180 capitalise = true; 181 case 'x': 182 unum = va_arg(args, unsigned int); 183 unsigned_num_print(&s, n, &chars_printed, 184 unum, 16, padc, padn, 185 capitalise); 186 break; 187 188 default: 189 /* Panic on any other format specifier. */ 190 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 191 *fmt); 192 plat_panic_handler(); 193 assert(0); /* Unreachable */ 194 } 195 fmt++; 196 continue; 197 } 198 199 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 200 201 fmt++; 202 } 203 204 if (n > 0U) { 205 *s = '\0'; 206 } 207 208 return (int)chars_printed; 209 } 210 211 /******************************************************************* 212 * Reduced snprintf to be used for Trusted firmware. 213 * The following type specifiers are supported: 214 * 215 * %x (or %X) - hexadecimal format 216 * %d or %i - signed decimal format 217 * %s - string format 218 * %u - unsigned decimal format 219 * %p - pointer format 220 * 221 * The following padding specifiers are supported by this print 222 * %0NN - Left-pad the number with 0s (NN is a decimal number) 223 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 224 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 225 * 226 * The function panics on all other formats specifiers. 227 * 228 * It returns the number of characters that would be written if the 229 * buffer was big enough. If it returns a value lower than n, the 230 * whole string has been written. 231 *******************************************************************/ 232 int snprintf(char *s, size_t n, const char *fmt, ...) 233 { 234 int count; 235 va_list all_args; 236 237 va_start(all_args, fmt); 238 count = vsnprintf(s, n, fmt, all_args); 239 va_end(all_args); 240 241 return count; 242 } 243