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