1 /* 2 * Copyright (c) 2017-2020, 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 '0': 134 case '1': 135 case '2': 136 case '3': 137 case '4': 138 case '5': 139 case '6': 140 case '7': 141 case '8': 142 case '9': 143 padc = (*fmt == '0') ? '0' : ' '; 144 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 145 padn = (padn * 10) + (*fmt - '0'); 146 } 147 if (left) { 148 padn = -padn; 149 } 150 goto loop; 151 case '-': 152 left = true; 153 fmt++; 154 goto loop; 155 156 case 'i': 157 case 'd': 158 num = va_arg(args, int); 159 160 if (num < 0) { 161 if (chars_printed < n) { 162 *s = '-'; 163 s++; 164 } 165 chars_printed++; 166 167 unum = (unsigned int)-num; 168 } else { 169 unum = (unsigned int)num; 170 } 171 172 unsigned_num_print(&s, n, &chars_printed, 173 unum, 10, padc, padn, false); 174 break; 175 case 's': 176 str = va_arg(args, char *); 177 string_print(&s, n, &chars_printed, str); 178 break; 179 case 'u': 180 unum = va_arg(args, unsigned int); 181 unsigned_num_print(&s, n, &chars_printed, 182 unum, 10, padc, padn, false); 183 break; 184 case 'p': 185 unum = (uintptr_t)va_arg(args, void *); 186 if (unum > 0U) { 187 string_print(&s, n, &chars_printed, "0x"); 188 padn -= 2; 189 } 190 unsigned_num_print(&s, n, &chars_printed, 191 unum, 16, padc, padn, false); 192 break; 193 case 'X': 194 capitalise = true; 195 case 'x': 196 unum = va_arg(args, unsigned int); 197 unsigned_num_print(&s, n, &chars_printed, 198 unum, 16, padc, padn, 199 capitalise); 200 break; 201 202 default: 203 /* Panic on any other format specifier. */ 204 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 205 *fmt); 206 plat_panic_handler(); 207 assert(0); /* Unreachable */ 208 } 209 fmt++; 210 continue; 211 } 212 213 if (chars_printed < n) { 214 *s = *fmt; 215 s++; 216 } 217 218 fmt++; 219 chars_printed++; 220 } 221 222 if (n > 0U) { 223 *s = '\0'; 224 } 225 226 return (int)chars_printed; 227 } 228 229 /******************************************************************* 230 * Reduced snprintf to be used for Trusted firmware. 231 * The following type specifiers are supported: 232 * 233 * %x (or %X) - hexadecimal format 234 * %d or %i - signed decimal format 235 * %s - string format 236 * %u - unsigned decimal format 237 * %p - pointer format 238 * 239 * The following padding specifiers are supported by this print 240 * %0NN - Left-pad the number with 0s (NN is a decimal number) 241 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 242 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 243 * 244 * The function panics on all other formats specifiers. 245 * 246 * It returns the number of characters that would be written if the 247 * buffer was big enough. If it returns a value lower than n, the 248 * whole string has been written. 249 *******************************************************************/ 250 int snprintf(char *s, size_t n, const char *fmt, ...) 251 { 252 int count; 253 va_list all_args; 254 255 va_start(all_args, fmt); 256 count = vsnprintf(s, n, fmt, all_args); 257 va_end(all_args); 258 259 return count; 260 } 261