xref: /rk3399_ARM-atf/lib/libc/snprintf.c (revision a08a2014300a495381cdb8f6d59523bcd5d3b883)
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