xref: /rk3399_ARM-atf/lib/libc/printf.c (revision f60a5004acabccf573e0eb943cdbfad7d7f540f6)
1 /*
2  * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 #include <assert.h>
7 #include <debug.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 	do {
45 		rem = unum % radix;
46 		if (rem < 0xa)
47 			num_buf[i] = '0' + rem;
48 		else
49 			num_buf[i] = 'a' + (rem - 0xa);
50 		i++;
51 		unum /= radix;
52 	} while (unum > 0U);
53 
54 	if (padn > 0) {
55 		while (i < padn) {
56 			(void)putchar(padc);
57 			count++;
58 			padn--;
59 		}
60 	}
61 
62 	while (--i >= 0) {
63 		(void)putchar(num_buf[i]);
64 		count++;
65 	}
66 
67 	return count;
68 }
69 
70 /*******************************************************************
71  * Reduced format print for Trusted firmware.
72  * The following type specifiers are supported by this print
73  * %x - hexadecimal format
74  * %s - string format
75  * %d or %i - signed decimal format
76  * %u - unsigned decimal format
77  * %p - pointer format
78  *
79  * The following length specifiers are supported by this print
80  * %l - long int (64-bit on AArch64)
81  * %ll - long long int (64-bit on AArch64)
82  * %z - size_t sized integer formats (64 bit on AArch64)
83  *
84  * The following padding specifiers are supported by this print
85  * %0NN - Left-pad the number with 0s (NN is a decimal number)
86  *
87  * The print exits on all other formats specifiers other than valid
88  * combinations of the above specifiers.
89  *******************************************************************/
90 int vprintf(const char *fmt, va_list args)
91 {
92 	int l_count;
93 	long long int num;
94 	unsigned long long int unum;
95 	char *str;
96 	char padc = '\0'; /* Padding character */
97 	int padn; /* Number of characters to pad */
98 	int count = 0; /* Number of printed characters */
99 
100 	while (*fmt != '\0') {
101 		l_count = 0;
102 		padn = 0;
103 
104 		if (*fmt == '%') {
105 			fmt++;
106 			/* Check the format specifier */
107 loop:
108 			switch (*fmt) {
109 			case 'i': /* Fall through to next one */
110 			case 'd':
111 				num = get_num_va_args(args, l_count);
112 				if (num < 0) {
113 					(void)putchar('-');
114 					unum = (unsigned long long int)-num;
115 					padn--;
116 				} else
117 					unum = (unsigned long long int)num;
118 
119 				count += unsigned_num_print(unum, 10,
120 							    padc, padn);
121 				break;
122 			case 's':
123 				str = va_arg(args, char *);
124 				count += string_print(str);
125 				break;
126 			case 'p':
127 				unum = (uintptr_t)va_arg(args, void *);
128 				if (unum > 0U) {
129 					count += string_print("0x");
130 					padn -= 2;
131 				}
132 
133 				count += unsigned_num_print(unum, 16,
134 							    padc, padn);
135 				break;
136 			case 'x':
137 				unum = get_unum_va_args(args, l_count);
138 				count += unsigned_num_print(unum, 16,
139 							    padc, padn);
140 				break;
141 			case 'z':
142 				if (sizeof(size_t) == 8U)
143 					l_count = 2;
144 
145 				fmt++;
146 				goto loop;
147 			case 'l':
148 				l_count++;
149 				fmt++;
150 				goto loop;
151 			case 'u':
152 				unum = get_unum_va_args(args, l_count);
153 				count += unsigned_num_print(unum, 10,
154 							    padc, padn);
155 				break;
156 			case '0':
157 				padc = '0';
158 				padn = 0;
159 				fmt++;
160 
161 				for (;;) {
162 					char ch = *fmt;
163 					if ((ch < '0') || (ch > '9')) {
164 						goto loop;
165 					}
166 					padn = (padn * 10) + (ch - '0');
167 					fmt++;
168 				}
169 			default:
170 				/* Exit on any other format specifier */
171 				return -1;
172 			}
173 			fmt++;
174 			continue;
175 		}
176 		(void)putchar(*fmt);
177 		fmt++;
178 		count++;
179 	}
180 
181 	return count;
182 }
183 
184 int printf(const char *fmt, ...)
185 {
186 	int count;
187 	va_list va;
188 
189 	va_start(va, fmt);
190 	count = vprintf(fmt, va);
191 	va_end(va);
192 
193 	return count;
194 }
195