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