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