1 /* 2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <debug.h> 9 #include <platform.h> 10 #include <stdarg.h> 11 12 static void string_print(char **s, size_t n, size_t *chars_printed, 13 const char *str) 14 { 15 while (*str != '\0') { 16 if (*chars_printed < n) { 17 *(*s) = *str; 18 (*s)++; 19 } 20 21 (*chars_printed)++; 22 str++; 23 } 24 } 25 26 static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed, 27 unsigned int unum) 28 { 29 /* Enough for a 32-bit unsigned decimal integer (4294967295). */ 30 char num_buf[10]; 31 int i = 0; 32 unsigned int rem; 33 34 do { 35 rem = unum % 10U; 36 num_buf[i++] = '0' + rem; 37 unum /= 10U; 38 } while (unum > 0U); 39 40 while (--i >= 0) { 41 if (*chars_printed < n) { 42 *(*s) = num_buf[i]; 43 (*s)++; 44 } 45 46 (*chars_printed)++; 47 } 48 } 49 50 /******************************************************************* 51 * Reduced snprintf to be used for Trusted firmware. 52 * The following type specifiers are supported: 53 * 54 * %d or %i - signed decimal format 55 * %s - string format 56 * %u - unsigned decimal format 57 * 58 * The function panics on all other formats specifiers. 59 * 60 * It returns the number of characters that would be written if the 61 * buffer was big enough. If it returns a value lower than n, the 62 * whole string has been written. 63 *******************************************************************/ 64 int snprintf(char *s, size_t n, const char *fmt, ...) 65 { 66 va_list args; 67 int num; 68 unsigned int unum; 69 char *str; 70 size_t chars_printed = 0U; 71 72 if (n == 0U) { 73 /* There isn't space for anything. */ 74 } else if (n == 1U) { 75 /* Buffer is too small to actually write anything else. */ 76 *s = '\0'; 77 n = 0U; 78 } else { 79 /* Reserve space for the terminator character. */ 80 n--; 81 } 82 83 va_start(args, fmt); 84 while (*fmt != '\0') { 85 86 if (*fmt == '%') { 87 fmt++; 88 /* Check the format specifier. */ 89 switch (*fmt) { 90 case 'i': 91 case 'd': 92 num = va_arg(args, int); 93 94 if (num < 0) { 95 if (chars_printed < n) { 96 *s = '-'; 97 s++; 98 } 99 chars_printed++; 100 101 unum = (unsigned int)-num; 102 } else { 103 unum = (unsigned int)num; 104 } 105 106 unsigned_dec_print(&s, n, &chars_printed, unum); 107 break; 108 case 's': 109 str = va_arg(args, char *); 110 string_print(&s, n, &chars_printed, str); 111 break; 112 case 'u': 113 unum = va_arg(args, unsigned int); 114 unsigned_dec_print(&s, n, &chars_printed, unum); 115 break; 116 default: 117 /* Panic on any other format specifier. */ 118 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 119 *fmt); 120 plat_panic_handler(); 121 assert(0); /* Unreachable */ 122 } 123 fmt++; 124 continue; 125 } 126 127 if (chars_printed < n) { 128 *s = *fmt; 129 s++; 130 } 131 132 fmt++; 133 chars_printed++; 134 } 135 136 va_end(args); 137 138 if (n > 0U) 139 *s = '\0'; 140 141 return (int)chars_printed; 142 } 143