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