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