1 /* 2 * Copyright (c) 2017-2023, 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 * %c - character 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 'c': 186 CHECK_AND_PUT_CHAR(s, n, chars_printed, va_arg(args, int)); 187 break; 188 case 's': 189 str = va_arg(args, char *); 190 string_print(&s, n, &chars_printed, str); 191 break; 192 case 'u': 193 unum = get_unum_va_args(args, l_count); 194 unsigned_num_print(&s, n, &chars_printed, 195 unum, 10, padc, padn, false); 196 break; 197 case 'z': 198 l_count = 1; 199 fmt++; 200 goto loop; 201 case 'l': 202 l_count++; 203 fmt++; 204 goto loop; 205 case 'p': 206 unum = (uintptr_t)va_arg(args, void *); 207 if (unum > 0U) { 208 string_print(&s, n, &chars_printed, "0x"); 209 padn -= 2; 210 } 211 unsigned_num_print(&s, n, &chars_printed, 212 unum, 16, padc, padn, false); 213 break; 214 case 'X': 215 capitalise = true; 216 /* fallthrough */ 217 case 'x': 218 unum = get_unum_va_args(args, l_count); 219 unsigned_num_print(&s, n, &chars_printed, 220 unum, 16, padc, padn, 221 capitalise); 222 break; 223 224 default: 225 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 226 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 227 } 228 fmt++; 229 continue; 230 } 231 232 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 233 234 fmt++; 235 } 236 237 if (n > 0U) { 238 *s = '\0'; 239 } 240 241 return (int)chars_printed; 242 } 243 244 /******************************************************************* 245 * Reduced snprintf to be used for Trusted firmware. 246 * The following type specifiers are supported: 247 * 248 * %x (or %X) - hexadecimal format 249 * %d or %i - signed decimal format 250 * %s - string format 251 * %u - unsigned decimal format 252 * %p - pointer format 253 * 254 * The following padding specifiers are supported by this print 255 * %0NN - Left-pad the number with 0s (NN is a decimal number) 256 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 257 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 258 * 259 * The function panics on all other formats specifiers. 260 * 261 * It returns the number of characters that would be written if the 262 * buffer was big enough. If it returns a value lower than n, the 263 * whole string has been written. 264 *******************************************************************/ 265 int snprintf(char *s, size_t n, const char *fmt, ...) 266 { 267 int count; 268 va_list all_args; 269 270 va_start(all_args, fmt); 271 count = vsnprintf(s, n, fmt, all_args); 272 va_end(all_args); 273 274 return count; 275 } 276