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