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