xref: /rk3399_ARM-atf/lib/libc/printf.c (revision 2aaed8608004f7d29be5da49289bb16c1b0df20f)
1 /*
2  * Copyright (c) 2014-2021, 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 #include <stdbool.h>
10 #include <stdint.h>
11 
12 #define get_num_va_args(_args, _lcount)				\
13 	(((_lcount) > 1)  ? va_arg(_args, long long int) :	\
14 	(((_lcount) == 1) ? va_arg(_args, long int) :		\
15 			    va_arg(_args, int)))
16 
17 #define get_unum_va_args(_args, _lcount)				\
18 	(((_lcount) > 1)  ? va_arg(_args, unsigned long long int) :	\
19 	(((_lcount) == 1) ? va_arg(_args, unsigned long int) :		\
20 			    va_arg(_args, unsigned int)))
21 
22 static int string_print(const char *str)
23 {
24 	int count = 0;
25 
26 	assert(str != NULL);
27 
28 	for ( ; *str != '\0'; str++) {
29 		(void)putchar(*str);
30 		count++;
31 	}
32 
33 	return count;
34 }
35 
36 static int unsigned_num_print(unsigned long long int unum, unsigned int radix,
37 			      char padc, int padn)
38 {
39 	/* Just need enough space to store 64 bit decimal integer */
40 	char num_buf[20];
41 	int i = 0, count = 0;
42 	unsigned int rem;
43 
44 	/* num_buf is only large enough for radix >= 10 */
45 	if (radix < 10) {
46 		assert(0);
47 		return 0;
48 	}
49 
50 	do {
51 		rem = unum % radix;
52 		if (rem < 0xa)
53 			num_buf[i] = '0' + rem;
54 		else
55 			num_buf[i] = 'a' + (rem - 0xa);
56 		i++;
57 		unum /= radix;
58 	} while (unum > 0U);
59 
60 	if (padn > 0) {
61 		while (i < padn) {
62 			(void)putchar(padc);
63 			count++;
64 			padn--;
65 		}
66 	}
67 
68 	while (--i >= 0) {
69 		(void)putchar(num_buf[i]);
70 		count++;
71 	}
72 
73 	return count;
74 }
75 
76 /*******************************************************************
77  * Reduced format print for Trusted firmware.
78  * The following type specifiers are supported by this print
79  * %x - hexadecimal format
80  * %s - string format
81  * %d or %i - signed decimal format
82  * %u - unsigned decimal format
83  * %p - pointer format
84  *
85  * The following length specifiers are supported by this print
86  * %l - long int (64-bit on AArch64)
87  * %ll - long long int (64-bit on AArch64)
88  * %z - size_t sized integer formats (64 bit on AArch64)
89  *
90  * The following padding specifiers are supported by this print
91  * %0NN - Left-pad the number with 0s (NN is a decimal number)
92  *
93  * The print exits on all other formats specifiers other than valid
94  * combinations of the above specifiers.
95  *******************************************************************/
96 int vprintf(const char *fmt, va_list args)
97 {
98 	int l_count;
99 	long long int num;
100 	unsigned long long int unum;
101 	char *str;
102 	char padc = '\0'; /* Padding character */
103 	int padn; /* Number of characters to pad */
104 	int count = 0; /* Number of printed characters */
105 
106 	while (*fmt != '\0') {
107 		l_count = 0;
108 		padn = 0;
109 
110 		if (*fmt == '%') {
111 			fmt++;
112 			/* Check the format specifier */
113 loop:
114 			switch (*fmt) {
115 			case '%':
116 				(void)putchar('%');
117 				break;
118 			case 'i': /* Fall through to next one */
119 			case 'd':
120 				num = get_num_va_args(args, l_count);
121 				if (num < 0) {
122 					(void)putchar('-');
123 					unum = (unsigned long long int)-num;
124 					padn--;
125 				} else
126 					unum = (unsigned long long int)num;
127 
128 				count += unsigned_num_print(unum, 10,
129 							    padc, padn);
130 				break;
131 			case 's':
132 				str = va_arg(args, char *);
133 				count += string_print(str);
134 				break;
135 			case 'p':
136 				unum = (uintptr_t)va_arg(args, void *);
137 				if (unum > 0U) {
138 					count += string_print("0x");
139 					padn -= 2;
140 				}
141 
142 				count += unsigned_num_print(unum, 16,
143 							    padc, padn);
144 				break;
145 			case 'x':
146 				unum = get_unum_va_args(args, l_count);
147 				count += unsigned_num_print(unum, 16,
148 							    padc, padn);
149 				break;
150 			case 'z':
151 				if (sizeof(size_t) == 8U)
152 					l_count = 2;
153 
154 				fmt++;
155 				goto loop;
156 			case 'l':
157 				l_count++;
158 				fmt++;
159 				goto loop;
160 			case 'u':
161 				unum = get_unum_va_args(args, l_count);
162 				count += unsigned_num_print(unum, 10,
163 							    padc, padn);
164 				break;
165 			case '0':
166 				padc = '0';
167 				padn = 0;
168 				fmt++;
169 
170 				for (;;) {
171 					char ch = *fmt;
172 					if ((ch < '0') || (ch > '9')) {
173 						goto loop;
174 					}
175 					padn = (padn * 10) + (ch - '0');
176 					fmt++;
177 				}
178 				assert(0); /* Unreachable */
179 			default:
180 				/* Exit on any other format specifier */
181 				return -1;
182 			}
183 			fmt++;
184 			continue;
185 		}
186 		(void)putchar(*fmt);
187 		fmt++;
188 		count++;
189 	}
190 
191 	return count;
192 }
193 
194 int printf(const char *fmt, ...)
195 {
196 	int count;
197 	va_list va;
198 
199 	va_start(va, fmt);
200 	count = vprintf(fmt, va);
201 	va_end(va);
202 
203 	return count;
204 }
205