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