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 snprintf 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 snprintf(char *s, size_t n, const char *fmt, ...) 101 { 102 va_list args; 103 int num; 104 unsigned long long int unum; 105 char *str; 106 char padc; /* Padding character */ 107 int padn; /* Number of characters to pad */ 108 bool left; 109 bool capitalise; 110 size_t chars_printed = 0U; 111 112 if (n == 0U) { 113 /* There isn't space for anything. */ 114 } else if (n == 1U) { 115 /* Buffer is too small to actually write anything else. */ 116 *s = '\0'; 117 n = 0U; 118 } else { 119 /* Reserve space for the terminator character. */ 120 n--; 121 } 122 123 va_start(args, fmt); 124 while (*fmt != '\0') { 125 left = false; 126 padc ='\0'; 127 padn = 0; 128 capitalise = false; 129 130 if (*fmt == '%') { 131 fmt++; 132 /* Check the format specifier. */ 133 loop: 134 switch (*fmt) { 135 case '0': 136 case '1': 137 case '2': 138 case '3': 139 case '4': 140 case '5': 141 case '6': 142 case '7': 143 case '8': 144 case '9': 145 padc = (*fmt == '0') ? '0' : ' '; 146 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 147 padn = (padn * 10) + (*fmt - '0'); 148 } 149 if (left) { 150 padn = -padn; 151 } 152 goto loop; 153 case '-': 154 left = true; 155 fmt++; 156 goto loop; 157 158 case 'i': 159 case 'd': 160 num = va_arg(args, int); 161 162 if (num < 0) { 163 if (chars_printed < n) { 164 *s = '-'; 165 s++; 166 } 167 chars_printed++; 168 169 unum = (unsigned int)-num; 170 } else { 171 unum = (unsigned int)num; 172 } 173 174 unsigned_num_print(&s, n, &chars_printed, 175 unum, 10, padc, padn, false); 176 break; 177 case 's': 178 str = va_arg(args, char *); 179 string_print(&s, n, &chars_printed, str); 180 break; 181 case 'u': 182 unum = va_arg(args, unsigned int); 183 unsigned_num_print(&s, n, &chars_printed, 184 unum, 10, padc, padn, false); 185 break; 186 case 'p': 187 unum = (uintptr_t)va_arg(args, void *); 188 if (unum > 0U) { 189 string_print(&s, n, &chars_printed, "0x"); 190 padn -= 2; 191 } 192 unsigned_num_print(&s, n, &chars_printed, 193 unum, 16, padc, padn, false); 194 break; 195 case 'X': 196 capitalise = true; 197 case 'x': 198 unum = va_arg(args, unsigned int); 199 unsigned_num_print(&s, n, &chars_printed, 200 unum, 16, padc, padn, 201 capitalise); 202 break; 203 204 default: 205 /* Panic on any other format specifier. */ 206 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 207 *fmt); 208 plat_panic_handler(); 209 assert(0); /* Unreachable */ 210 } 211 fmt++; 212 continue; 213 } 214 215 if (chars_printed < n) { 216 *s = *fmt; 217 s++; 218 } 219 220 fmt++; 221 chars_printed++; 222 } 223 224 va_end(args); 225 226 if (n > 0U) 227 *s = '\0'; 228 229 return (int)chars_printed; 230 } 231