1// SPDX-License-Identifier: GPL-2.0-only
2#include "test_util.h"
3#include "kvm_util.h"
4#include "ucall_common.h"
5
6#define APPEND_BUFFER_SAFE(str, end, v) \
7do {					\
8	GUEST_ASSERT(str < end);	\
9	*str++ = (v);			\
10} while (0)
11
12static int isdigit(int ch)
13{
14	return (ch >= '0') && (ch <= '9');
15}
16
17static int skip_atoi(const char **s)
18{
19	int i = 0;
20
21	while (isdigit(**s))
22		i = i * 10 + *((*s)++) - '0';
23	return i;
24}
25
26#define ZEROPAD	1		/* pad with zero */
27#define SIGN	2		/* unsigned/signed long */
28#define PLUS	4		/* show plus */
29#define SPACE	8		/* space if plus */
30#define LEFT	16		/* left justified */
31#define SMALL	32		/* Must be 32 == 0x20 */
32#define SPECIAL	64		/* 0x */
33
34#define __do_div(n, base)				\
35({							\
36	int __res;					\
37							\
38	__res = ((uint64_t) n) % (uint32_t) base;	\
39	n = ((uint64_t) n) / (uint32_t) base;		\
40	__res;						\
41})
42
43static char *number(char *str, const char *end, long num, int base, int size,
44		    int precision, 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	/*
54	 * locase = 0 or 0x20. ORing digits or letters with 'locase'
55	 * produces same digits or (maybe lowercased) letters
56	 */
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			APPEND_BUFFER_SAFE(str, end, ' ');
95	if (sign)
96		APPEND_BUFFER_SAFE(str, end, sign);
97	if (type & SPECIAL) {
98		if (base == 8)
99			APPEND_BUFFER_SAFE(str, end, '0');
100		else if (base == 16) {
101			APPEND_BUFFER_SAFE(str, end, '0');
102			APPEND_BUFFER_SAFE(str, end, 'x');
103		}
104	}
105	if (!(type & LEFT))
106		while (size-- > 0)
107			APPEND_BUFFER_SAFE(str, end, c);
108	while (i < precision--)
109		APPEND_BUFFER_SAFE(str, end, '0');
110	while (i-- > 0)
111		APPEND_BUFFER_SAFE(str, end, tmp[i]);
112	while (size-- > 0)
113		APPEND_BUFFER_SAFE(str, end, ' ');
114
115	return str;
116}
117
118int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args)
119{
120	char *str, *end;
121	const char *s;
122	uint64_t num;
123	int i, base;
124	int len;
125
126	int flags;		/* flags to number() */
127
128	int field_width;	/* width of output field */
129	int precision;		/*
130				 * min. # of digits for integers; max
131				 * number of chars for from string
132				 */
133	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
134
135	end = buf + n;
136	GUEST_ASSERT(buf < end);
137	GUEST_ASSERT(n > 0);
138
139	for (str = buf; *fmt; ++fmt) {
140		if (*fmt != '%') {
141			APPEND_BUFFER_SAFE(str, end, *fmt);
142			continue;
143		}
144
145		/* process flags */
146		flags = 0;
147repeat:
148		++fmt;		/* this also skips first '%' */
149		switch (*fmt) {
150		case '-':
151			flags |= LEFT;
152			goto repeat;
153		case '+':
154			flags |= PLUS;
155			goto repeat;
156		case ' ':
157			flags |= SPACE;
158			goto repeat;
159		case '#':
160			flags |= SPECIAL;
161			goto repeat;
162		case '0':
163			flags |= ZEROPAD;
164			goto repeat;
165		}
166
167		/* get field width */
168		field_width = -1;
169		if (isdigit(*fmt))
170			field_width = skip_atoi(&fmt);
171		else if (*fmt == '*') {
172			++fmt;
173			/* it's the next argument */
174			field_width = va_arg(args, int);
175			if (field_width < 0) {
176				field_width = -field_width;
177				flags |= LEFT;
178			}
179		}
180
181		/* get the precision */
182		precision = -1;
183		if (*fmt == '.') {
184			++fmt;
185			if (isdigit(*fmt))
186				precision = skip_atoi(&fmt);
187			else if (*fmt == '*') {
188				++fmt;
189				/* it's the next argument */
190				precision = va_arg(args, int);
191			}
192			if (precision < 0)
193				precision = 0;
194		}
195
196		/* get the conversion qualifier */
197		qualifier = -1;
198		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
199			qualifier = *fmt;
200			++fmt;
201		}
202
203		/*
204		 * Play nice with %llu, %llx, etc.  KVM selftests only support
205		 * 64-bit builds, so just treat %ll* the same as %l*.
206		 */
207		if (qualifier == 'l' && *fmt == 'l')
208			++fmt;
209
210		/* default base */
211		base = 10;
212
213		switch (*fmt) {
214		case 'c':
215			if (!(flags & LEFT))
216				while (--field_width > 0)
217					APPEND_BUFFER_SAFE(str, end, ' ');
218			APPEND_BUFFER_SAFE(str, end,
219					    (uint8_t)va_arg(args, int));
220			while (--field_width > 0)
221				APPEND_BUFFER_SAFE(str, end, ' ');
222			continue;
223
224		case 's':
225			s = va_arg(args, char *);
226			len = strnlen(s, precision);
227
228			if (!(flags & LEFT))
229				while (len < field_width--)
230					APPEND_BUFFER_SAFE(str, end, ' ');
231			for (i = 0; i < len; ++i)
232				APPEND_BUFFER_SAFE(str, end, *s++);
233			while (len < field_width--)
234				APPEND_BUFFER_SAFE(str, end, ' ');
235			continue;
236
237		case 'p':
238			if (field_width == -1) {
239				field_width = 2 * sizeof(void *);
240				flags |= SPECIAL | SMALL | ZEROPAD;
241			}
242			str = number(str, end,
243				     (uint64_t)va_arg(args, void *), 16,
244				     field_width, precision, flags);
245			continue;
246
247		case 'n':
248			if (qualifier == 'l') {
249				long *ip = va_arg(args, long *);
250				*ip = (str - buf);
251			} else {
252				int *ip = va_arg(args, int *);
253				*ip = (str - buf);
254			}
255			continue;
256
257		case '%':
258			APPEND_BUFFER_SAFE(str, end, '%');
259			continue;
260
261		/* integer number formats - set up the flags and "break" */
262		case 'o':
263			base = 8;
264			break;
265
266		case 'x':
267			flags |= SMALL;
268		case 'X':
269			base = 16;
270			break;
271
272		case 'd':
273		case 'i':
274			flags |= SIGN;
275		case 'u':
276			break;
277
278		default:
279			APPEND_BUFFER_SAFE(str, end, '%');
280			if (*fmt)
281				APPEND_BUFFER_SAFE(str, end, *fmt);
282			else
283				--fmt;
284			continue;
285		}
286		if (qualifier == 'l')
287			num = va_arg(args, uint64_t);
288		else if (qualifier == 'h') {
289			num = (uint16_t)va_arg(args, int);
290			if (flags & SIGN)
291				num = (int16_t)num;
292		} else if (flags & SIGN)
293			num = va_arg(args, int);
294		else
295			num = va_arg(args, uint32_t);
296		str = number(str, end, num, base, field_width, precision, flags);
297	}
298
299	GUEST_ASSERT(str < end);
300	*str = '\0';
301	return str - buf;
302}
303
304int guest_snprintf(char *buf, int n, const char *fmt, ...)
305{
306	va_list va;
307	int len;
308
309	va_start(va, fmt);
310	len = guest_vsnprintf(buf, n, fmt, va);
311	va_end(va);
312
313	return len;
314}
315