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