xref: /linux/arch/x86/boot/printf.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* -*- linux-c -*- ------------------------------------------------------- *
3  *
4  *   Copyright (C) 1991, 1992 Linus Torvalds
5  *   Copyright 2007 rPath, Inc. - All Rights Reserved
6  *
7  * ----------------------------------------------------------------------- */
8 
9 /*
10  * Oh, it's a waste of space, but oh-so-yummy for debugging.  This
11  * version of printf() does not include 64-bit support.  "Live with
12  * it."
13  *
14  */
15 
16 #include "boot.h"
17 
18 static int skip_atoi(const char **s)
19 {
20 	int i = 0;
21 
22 	while (isdigit(**s))
23 		i = i * 10 + *((*s)++) - '0';
24 	return i;
25 }
26 
27 #define ZEROPAD	1		/* pad with zero */
28 #define SIGN	2		/* unsigned/signed long */
29 #define PLUS	4		/* show plus */
30 #define SPACE	8		/* space if plus */
31 #define LEFT	16		/* left justified */
32 #define SMALL	32		/* Must be 32 == 0x20 */
33 #define SPECIAL	64		/* 0x */
34 
35 #define __do_div(n, base) ({ \
36 int __res; \
37 __res = ((unsigned long) n) % (unsigned) base; \
38 n = ((unsigned long) n) / (unsigned) base; \
39 __res; })
40 
41 static char *number(char *str, long num, int base, int size, int precision,
42 		    int type)
43 {
44 	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
45 	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
46 
47 	char tmp[66];
48 	char c, sign, locase;
49 	int i;
50 
51 	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
52 	 * produces same digits or (maybe lowercased) letters */
53 	locase = (type & SMALL);
54 	if (type & LEFT)
55 		type &= ~ZEROPAD;
56 	if (base < 2 || base > 16)
57 		return NULL;
58 	c = (type & ZEROPAD) ? '0' : ' ';
59 	sign = 0;
60 	if (type & SIGN) {
61 		if (num < 0) {
62 			sign = '-';
63 			num = -num;
64 			size--;
65 		} else if (type & PLUS) {
66 			sign = '+';
67 			size--;
68 		} else if (type & SPACE) {
69 			sign = ' ';
70 			size--;
71 		}
72 	}
73 	if (type & SPECIAL) {
74 		if (base == 16)
75 			size -= 2;
76 		else if (base == 8)
77 			size--;
78 	}
79 	i = 0;
80 	if (num == 0)
81 		tmp[i++] = '0';
82 	else
83 		while (num != 0)
84 			tmp[i++] = (digits[__do_div(num, base)] | locase);
85 	if (i > precision)
86 		precision = i;
87 	size -= precision;
88 	if (!(type & (ZEROPAD + LEFT)))
89 		while (size-- > 0)
90 			*str++ = ' ';
91 	if (sign)
92 		*str++ = sign;
93 	if (type & SPECIAL) {
94 		if (base == 8)
95 			*str++ = '0';
96 		else if (base == 16) {
97 			*str++ = '0';
98 			*str++ = ('X' | locase);
99 		}
100 	}
101 	if (!(type & LEFT))
102 		while (size-- > 0)
103 			*str++ = c;
104 	while (i < precision--)
105 		*str++ = '0';
106 	while (i-- > 0)
107 		*str++ = tmp[i];
108 	while (size-- > 0)
109 		*str++ = ' ';
110 	return str;
111 }
112 
113 int vsprintf(char *buf, const char *fmt, va_list args)
114 {
115 	int len;
116 	unsigned long num;
117 	int i, base;
118 	char *str;
119 	const char *s;
120 
121 	int flags;		/* flags to number() */
122 
123 	int field_width;	/* width of output field */
124 	int precision;		/* min. # of digits for integers; max
125 				   number of chars for from string */
126 	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
127 
128 	for (str = buf; *fmt; ++fmt) {
129 		if (*fmt != '%') {
130 			*str++ = *fmt;
131 			continue;
132 		}
133 
134 		/* process flags */
135 		flags = 0;
136 	      repeat:
137 		++fmt;		/* this also skips first '%' */
138 		switch (*fmt) {
139 		case '-':
140 			flags |= LEFT;
141 			goto repeat;
142 		case '+':
143 			flags |= PLUS;
144 			goto repeat;
145 		case ' ':
146 			flags |= SPACE;
147 			goto repeat;
148 		case '#':
149 			flags |= SPECIAL;
150 			goto repeat;
151 		case '0':
152 			flags |= ZEROPAD;
153 			goto repeat;
154 		}
155 
156 		/* get field width */
157 		field_width = -1;
158 		if (isdigit(*fmt))
159 			field_width = skip_atoi(&fmt);
160 		else if (*fmt == '*') {
161 			++fmt;
162 			/* it's the next argument */
163 			field_width = va_arg(args, int);
164 			if (field_width < 0) {
165 				field_width = -field_width;
166 				flags |= LEFT;
167 			}
168 		}
169 
170 		/* get the precision */
171 		precision = -1;
172 		if (*fmt == '.') {
173 			++fmt;
174 			if (isdigit(*fmt))
175 				precision = skip_atoi(&fmt);
176 			else if (*fmt == '*') {
177 				++fmt;
178 				/* it's the next argument */
179 				precision = va_arg(args, int);
180 			}
181 			if (precision < 0)
182 				precision = 0;
183 		}
184 
185 		/* get the conversion qualifier */
186 		qualifier = -1;
187 		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
188 			qualifier = *fmt;
189 			++fmt;
190 		}
191 
192 		/* default base */
193 		base = 10;
194 
195 		switch (*fmt) {
196 		case 'c':
197 			if (!(flags & LEFT))
198 				while (--field_width > 0)
199 					*str++ = ' ';
200 			*str++ = (unsigned char)va_arg(args, int);
201 			while (--field_width > 0)
202 				*str++ = ' ';
203 			continue;
204 
205 		case 's':
206 			s = va_arg(args, char *);
207 			len = strnlen(s, precision);
208 
209 			if (!(flags & LEFT))
210 				while (len < field_width--)
211 					*str++ = ' ';
212 			for (i = 0; i < len; ++i)
213 				*str++ = *s++;
214 			while (len < field_width--)
215 				*str++ = ' ';
216 			continue;
217 
218 		case 'p':
219 			if (field_width == -1) {
220 				field_width = 2 * sizeof(void *);
221 				flags |= ZEROPAD;
222 			}
223 			str = number(str,
224 				     (unsigned long)va_arg(args, void *), 16,
225 				     field_width, precision, flags);
226 			continue;
227 
228 		case 'n':
229 			if (qualifier == 'l') {
230 				long *ip = va_arg(args, long *);
231 				*ip = (str - buf);
232 			} else {
233 				int *ip = va_arg(args, int *);
234 				*ip = (str - buf);
235 			}
236 			continue;
237 
238 		case '%':
239 			*str++ = '%';
240 			continue;
241 
242 			/* integer number formats - set up the flags and "break" */
243 		case 'o':
244 			base = 8;
245 			break;
246 
247 		case 'x':
248 			flags |= SMALL;
249 			fallthrough;
250 		case 'X':
251 			base = 16;
252 			break;
253 
254 		case 'd':
255 		case 'i':
256 			flags |= SIGN;
257 			break;
258 
259 		case 'u':
260 			break;
261 
262 		default:
263 			*str++ = '%';
264 			if (*fmt)
265 				*str++ = *fmt;
266 			else
267 				--fmt;
268 			continue;
269 		}
270 		if (qualifier == 'l')
271 			num = va_arg(args, unsigned long);
272 		else if (qualifier == 'h') {
273 			num = (unsigned short)va_arg(args, int);
274 			if (flags & SIGN)
275 				num = (short)num;
276 		} else if (flags & SIGN)
277 			num = va_arg(args, int);
278 		else
279 			num = va_arg(args, unsigned int);
280 		str = number(str, num, base, field_width, precision, flags);
281 	}
282 	*str = '\0';
283 	return str - buf;
284 }
285 
286 int sprintf(char *buf, const char *fmt, ...)
287 {
288 	va_list args;
289 	int i;
290 
291 	va_start(args, fmt);
292 	i = vsprintf(buf, fmt, args);
293 	va_end(args);
294 	return i;
295 }
296 
297 int printf(const char *fmt, ...)
298 {
299 	char printf_buf[1024];
300 	va_list args;
301 	int printed;
302 
303 	va_start(args, fmt);
304 	printed = vsprintf(printf_buf, fmt, args);
305 	va_end(args);
306 
307 	puts(printf_buf);
308 
309 	return printed;
310 }
311