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