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 static void string_print(char **s, size_t n, size_t *chars_printed, 14 const char *str) 15 { 16 while (*str != '\0') { 17 if (*chars_printed < n) { 18 *(*s) = *str; 19 (*s)++; 20 } 21 22 (*chars_printed)++; 23 str++; 24 } 25 } 26 27 static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, 28 unsigned long long int unum, 29 unsigned int radix, char padc, int padn, 30 bool capitalise) 31 { 32 /* Just need enough space to store 64 bit decimal integer */ 33 char num_buf[20]; 34 int i = 0; 35 int width; 36 unsigned int rem; 37 char ascii_a = capitalise ? 'A' : 'a'; 38 39 do { 40 rem = unum % radix; 41 if (rem < 10U) { 42 num_buf[i] = '0' + rem; 43 } else { 44 num_buf[i] = ascii_a + (rem - 10U); 45 } 46 i++; 47 unum /= radix; 48 } while (unum > 0U); 49 50 width = i; 51 if (padn > width) { 52 (*chars_printed) += (size_t)padn; 53 } else { 54 (*chars_printed) += (size_t)width; 55 } 56 57 if (*chars_printed < n) { 58 59 if (padn > 0) { 60 while (width < padn) { 61 *(*s)++ = padc; 62 padn--; 63 } 64 } 65 66 while (--i >= 0) { 67 *(*s)++ = num_buf[i]; 68 } 69 70 if (padn < 0) { 71 while (width < -padn) { 72 *(*s)++ = padc; 73 padn++; 74 } 75 } 76 } 77 } 78 79 /******************************************************************* 80 * Reduced vsnprintf to be used for Trusted firmware. 81 * The following type specifiers are supported: 82 * 83 * %x (or %X) - hexadecimal format 84 * %d or %i - signed decimal format 85 * %s - string format 86 * %u - unsigned decimal format 87 * %p - pointer format 88 * 89 * The following padding specifiers are supported by this print 90 * %0NN - Left-pad the number with 0s (NN is a decimal number) 91 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 92 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 93 * 94 * The function panics on all other formats specifiers. 95 * 96 * It returns the number of characters that would be written if the 97 * buffer was big enough. If it returns a value lower than n, the 98 * whole string has been written. 99 *******************************************************************/ 100 int vsnprintf(char *s, size_t n, const char *fmt, va_list args) 101 { 102 int num; 103 unsigned long long int unum; 104 char *str; 105 char padc; /* Padding character */ 106 int padn; /* Number of characters to pad */ 107 bool left; 108 bool capitalise; 109 size_t chars_printed = 0U; 110 111 if (n == 0U) { 112 /* There isn't space for anything. */ 113 } else if (n == 1U) { 114 /* Buffer is too small to actually write anything else. */ 115 *s = '\0'; 116 n = 0U; 117 } else { 118 /* Reserve space for the terminator character. */ 119 n--; 120 } 121 122 while (*fmt != '\0') { 123 left = false; 124 padc ='\0'; 125 padn = 0; 126 capitalise = false; 127 128 if (*fmt == '%') { 129 fmt++; 130 /* Check the format specifier. */ 131 loop: 132 switch (*fmt) { 133 case '%': 134 if (chars_printed < n) { 135 *s = '%'; 136 s++; 137 } 138 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 if (chars_printed < n) { 169 *s = '-'; 170 s++; 171 } 172 chars_printed++; 173 174 unum = (unsigned int)-num; 175 } else { 176 unum = (unsigned int)num; 177 } 178 179 unsigned_num_print(&s, n, &chars_printed, 180 unum, 10, padc, padn, false); 181 break; 182 case 's': 183 str = va_arg(args, char *); 184 string_print(&s, n, &chars_printed, str); 185 break; 186 case 'u': 187 unum = va_arg(args, unsigned int); 188 unsigned_num_print(&s, n, &chars_printed, 189 unum, 10, padc, padn, false); 190 break; 191 case 'p': 192 unum = (uintptr_t)va_arg(args, void *); 193 if (unum > 0U) { 194 string_print(&s, n, &chars_printed, "0x"); 195 padn -= 2; 196 } 197 unsigned_num_print(&s, n, &chars_printed, 198 unum, 16, padc, padn, false); 199 break; 200 case 'X': 201 capitalise = true; 202 case 'x': 203 unum = va_arg(args, unsigned int); 204 unsigned_num_print(&s, n, &chars_printed, 205 unum, 16, padc, padn, 206 capitalise); 207 break; 208 209 default: 210 /* Panic on any other format specifier. */ 211 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 212 *fmt); 213 plat_panic_handler(); 214 assert(0); /* Unreachable */ 215 } 216 fmt++; 217 continue; 218 } 219 220 if (chars_printed < n) { 221 *s = *fmt; 222 s++; 223 } 224 225 fmt++; 226 chars_printed++; 227 } 228 229 if (n > 0U) { 230 *s = '\0'; 231 } 232 233 return (int)chars_printed; 234 } 235 236 /******************************************************************* 237 * Reduced snprintf to be used for Trusted firmware. 238 * The following type specifiers are supported: 239 * 240 * %x (or %X) - hexadecimal format 241 * %d or %i - signed decimal format 242 * %s - string format 243 * %u - unsigned decimal format 244 * %p - pointer format 245 * 246 * The following padding specifiers are supported by this print 247 * %0NN - Left-pad the number with 0s (NN is a decimal number) 248 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 249 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 250 * 251 * The function panics on all other formats specifiers. 252 * 253 * It returns the number of characters that would be written if the 254 * buffer was big enough. If it returns a value lower than n, the 255 * whole string has been written. 256 *******************************************************************/ 257 int snprintf(char *s, size_t n, const char *fmt, ...) 258 { 259 int count; 260 va_list all_args; 261 262 va_start(all_args, fmt); 263 count = vsnprintf(s, n, fmt, all_args); 264 va_end(all_args); 265 266 return count; 267 } 268