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 if (padn > width) { 56 (*chars_printed) += (size_t)padn; 57 } else { 58 (*chars_printed) += (size_t)width; 59 } 60 61 if (*chars_printed < n) { 62 63 if (padn > 0) { 64 while (width < padn) { 65 *(*s)++ = padc; 66 padn--; 67 } 68 } 69 70 while (--i >= 0) { 71 *(*s)++ = num_buf[i]; 72 } 73 74 if (padn < 0) { 75 while (width < -padn) { 76 *(*s)++ = padc; 77 padn++; 78 } 79 } 80 } 81 } 82 83 /******************************************************************* 84 * Reduced vsnprintf to be used for Trusted firmware. 85 * The following type specifiers are supported: 86 * 87 * %x (or %X) - hexadecimal format 88 * %d or %i - signed decimal format 89 * %s - string format 90 * %u - unsigned decimal format 91 * %p - pointer format 92 * 93 * The following padding specifiers are supported by this print 94 * %0NN - Left-pad the number with 0s (NN is a decimal number) 95 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 96 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 97 * 98 * The function panics on all other formats specifiers. 99 * 100 * It returns the number of characters that would be written if the 101 * buffer was big enough. If it returns a value lower than n, the 102 * whole string has been written. 103 *******************************************************************/ 104 int vsnprintf(char *s, size_t n, const char *fmt, va_list args) 105 { 106 int num; 107 unsigned long long int unum; 108 char *str; 109 char padc; /* Padding character */ 110 int padn; /* Number of characters to pad */ 111 bool left; 112 bool capitalise; 113 size_t chars_printed = 0U; 114 115 if (n == 0U) { 116 /* There isn't space for anything. */ 117 } else if (n == 1U) { 118 /* Buffer is too small to actually write anything else. */ 119 *s = '\0'; 120 n = 0U; 121 } else { 122 /* Reserve space for the terminator character. */ 123 n--; 124 } 125 126 while (*fmt != '\0') { 127 left = false; 128 padc ='\0'; 129 padn = 0; 130 capitalise = false; 131 132 if (*fmt == '%') { 133 fmt++; 134 /* Check the format specifier. */ 135 loop: 136 switch (*fmt) { 137 case '%': 138 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 139 break; 140 case '0': 141 case '1': 142 case '2': 143 case '3': 144 case '4': 145 case '5': 146 case '6': 147 case '7': 148 case '8': 149 case '9': 150 padc = (*fmt == '0') ? '0' : ' '; 151 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 152 padn = (padn * 10) + (*fmt - '0'); 153 } 154 if (left) { 155 padn = -padn; 156 } 157 goto loop; 158 case '-': 159 left = true; 160 fmt++; 161 goto loop; 162 163 case 'i': 164 case 'd': 165 num = va_arg(args, int); 166 167 if (num < 0) { 168 CHECK_AND_PUT_CHAR(s, n, chars_printed, 169 '-'); 170 unum = (unsigned int)-num; 171 } else { 172 unum = (unsigned int)num; 173 } 174 175 unsigned_num_print(&s, n, &chars_printed, 176 unum, 10, padc, padn, false); 177 break; 178 case 's': 179 str = va_arg(args, char *); 180 string_print(&s, n, &chars_printed, str); 181 break; 182 case 'u': 183 unum = va_arg(args, unsigned int); 184 unsigned_num_print(&s, n, &chars_printed, 185 unum, 10, padc, padn, false); 186 break; 187 case 'p': 188 unum = (uintptr_t)va_arg(args, void *); 189 if (unum > 0U) { 190 string_print(&s, n, &chars_printed, "0x"); 191 padn -= 2; 192 } 193 unsigned_num_print(&s, n, &chars_printed, 194 unum, 16, padc, padn, false); 195 break; 196 case 'X': 197 capitalise = true; 198 case 'x': 199 unum = va_arg(args, unsigned int); 200 unsigned_num_print(&s, n, &chars_printed, 201 unum, 16, padc, padn, 202 capitalise); 203 break; 204 205 default: 206 /* Panic on any other format specifier. */ 207 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 208 *fmt); 209 plat_panic_handler(); 210 assert(0); /* Unreachable */ 211 } 212 fmt++; 213 continue; 214 } 215 216 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 217 218 fmt++; 219 } 220 221 if (n > 0U) { 222 *s = '\0'; 223 } 224 225 return (int)chars_printed; 226 } 227 228 /******************************************************************* 229 * Reduced snprintf to be used for Trusted firmware. 230 * The following type specifiers are supported: 231 * 232 * %x (or %X) - hexadecimal format 233 * %d or %i - signed decimal format 234 * %s - string format 235 * %u - unsigned decimal format 236 * %p - pointer format 237 * 238 * The following padding specifiers are supported by this print 239 * %0NN - Left-pad the number with 0s (NN is a decimal number) 240 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 241 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 242 * 243 * The function panics on all other formats specifiers. 244 * 245 * It returns the number of characters that would be written if the 246 * buffer was big enough. If it returns a value lower than n, the 247 * whole string has been written. 248 *******************************************************************/ 249 int snprintf(char *s, size_t n, const char *fmt, ...) 250 { 251 int count; 252 va_list all_args; 253 254 va_start(all_args, fmt); 255 count = vsnprintf(s, n, fmt, all_args); 256 va_end(all_args); 257 258 return count; 259 } 260