xref: /kvm-unit-tests/lib/printf.c (revision 2a9c2e2f1240cc274b6b56631fc17a8394fa987a)
14b6da826SThomas Huth /*
24b6da826SThomas Huth  * libc printf and friends
34b6da826SThomas Huth  *
44b6da826SThomas Huth  * This code is free software; you can redistribute it and/or modify it
54b6da826SThomas Huth  * under the terms of the GNU Library General Public License version 2.
64b6da826SThomas Huth  */
74b6da826SThomas Huth 
87d36db35SAvi Kivity #include "libcflat.h"
9*2a9c2e2fSNikos Nikoleris #include "ctype.h"
107d36db35SAvi Kivity 
11cb12ecccSAndrew Jones #define BUFSZ 2000
12cb12ecccSAndrew Jones 
137d36db35SAvi Kivity typedef struct pstream {
147d36db35SAvi Kivity 	char *buffer;
157d36db35SAvi Kivity 	int remain;
167d36db35SAvi Kivity 	int added;
177d36db35SAvi Kivity } pstream_t;
187d36db35SAvi Kivity 
192c978d9bSAndrew Jones typedef struct strprops {
202c978d9bSAndrew Jones 	char pad;
212c978d9bSAndrew Jones 	int npad;
228ef44442SRadim Krčmář 	bool alternate;
23*2a9c2e2fSNikos Nikoleris 	int precision;
242c978d9bSAndrew Jones } strprops_t;
252c978d9bSAndrew Jones 
267d36db35SAvi Kivity static void addchar(pstream_t *p, char c)
277d36db35SAvi Kivity {
287d36db35SAvi Kivity 	if (p->remain) {
297d36db35SAvi Kivity 		*p->buffer++ = c;
307d36db35SAvi Kivity 		--p->remain;
317d36db35SAvi Kivity 	}
327d36db35SAvi Kivity 	++p->added;
337d36db35SAvi Kivity }
347d36db35SAvi Kivity 
3523b8916bSThomas Huth static void print_str(pstream_t *p, const char *s, strprops_t props)
367d36db35SAvi Kivity {
372c978d9bSAndrew Jones 	const char *s_orig = s;
382c978d9bSAndrew Jones 	int npad = props.npad;
392c978d9bSAndrew Jones 
402c978d9bSAndrew Jones 	if (npad > 0) {
412c978d9bSAndrew Jones 		npad -= strlen(s_orig);
422c978d9bSAndrew Jones 		while (npad > 0) {
432c978d9bSAndrew Jones 			addchar(p, props.pad);
442c978d9bSAndrew Jones 			--npad;
452c978d9bSAndrew Jones 		}
462c978d9bSAndrew Jones 	}
472c978d9bSAndrew Jones 
48*2a9c2e2fSNikos Nikoleris 	while (*s && props.precision--)
497d36db35SAvi Kivity 		addchar(p, *s++);
502c978d9bSAndrew Jones 
512c978d9bSAndrew Jones 	if (npad < 0) {
522c978d9bSAndrew Jones 		props.pad = ' ';	/* ignore '0' flag with '-' flag */
532c978d9bSAndrew Jones 		npad += strlen(s_orig);
542c978d9bSAndrew Jones 		while (npad < 0) {
552c978d9bSAndrew Jones 			addchar(p, props.pad);
562c978d9bSAndrew Jones 			++npad;
572c978d9bSAndrew Jones 		}
582c978d9bSAndrew Jones 	}
597d36db35SAvi Kivity }
607d36db35SAvi Kivity 
617d36db35SAvi Kivity static char digits[16] = "0123456789abcdef";
627d36db35SAvi Kivity 
6323b8916bSThomas Huth static void print_int(pstream_t *ps, long long n, int base, strprops_t props)
647d36db35SAvi Kivity {
657d36db35SAvi Kivity 	char buf[sizeof(long) * 3 + 2], *p = buf;
667d36db35SAvi Kivity 	int s = 0, i;
677d36db35SAvi Kivity 
687d36db35SAvi Kivity 	if (n < 0) {
697d36db35SAvi Kivity 		n = -n;
707d36db35SAvi Kivity 		s = 1;
717d36db35SAvi Kivity 	}
727d36db35SAvi Kivity 
737d36db35SAvi Kivity 	while (n) {
747d36db35SAvi Kivity 		*p++ = digits[n % base];
757d36db35SAvi Kivity 		n /= base;
767d36db35SAvi Kivity 	}
777d36db35SAvi Kivity 
78*2a9c2e2fSNikos Nikoleris 	while (p == buf || (p - buf < props.precision))
79*2a9c2e2fSNikos Nikoleris 		*p++ = '0';
80*2a9c2e2fSNikos Nikoleris 	props.precision = -1;
81*2a9c2e2fSNikos Nikoleris 
827d36db35SAvi Kivity 	if (s)
837d36db35SAvi Kivity 		*p++ = '-';
847d36db35SAvi Kivity 
857d36db35SAvi Kivity 	for (i = 0; i < (p - buf) / 2; ++i) {
867d36db35SAvi Kivity 		char tmp;
877d36db35SAvi Kivity 
887d36db35SAvi Kivity 		tmp = buf[i];
897d36db35SAvi Kivity 		buf[i] = p[-1 - i];
907d36db35SAvi Kivity 		p[-1 - i] = tmp;
917d36db35SAvi Kivity 	}
927d36db35SAvi Kivity 
937d36db35SAvi Kivity 	*p = 0;
947d36db35SAvi Kivity 
952c978d9bSAndrew Jones 	print_str(ps, buf, props);
967d36db35SAvi Kivity }
977d36db35SAvi Kivity 
9823b8916bSThomas Huth static void print_unsigned(pstream_t *ps, unsigned long long n, int base,
992c978d9bSAndrew Jones 			   strprops_t props)
1007d36db35SAvi Kivity {
1018ef44442SRadim Krčmář 	char buf[sizeof(long) * 3 + 3], *p = buf;
1027d36db35SAvi Kivity 	int i;
1037d36db35SAvi Kivity 
1047d36db35SAvi Kivity 	while (n) {
1057d36db35SAvi Kivity 		*p++ = digits[n % base];
1067d36db35SAvi Kivity 		n /= base;
1077d36db35SAvi Kivity 	}
1087d36db35SAvi Kivity 
1097d36db35SAvi Kivity 	if (p == buf)
110*2a9c2e2fSNikos Nikoleris 		props.alternate = false;
111*2a9c2e2fSNikos Nikoleris 
112*2a9c2e2fSNikos Nikoleris 	while (p == buf || (p - buf < props.precision))
1137d36db35SAvi Kivity 		*p++ = '0';
114*2a9c2e2fSNikos Nikoleris 	props.precision = -1;
115*2a9c2e2fSNikos Nikoleris 
116*2a9c2e2fSNikos Nikoleris 	if (props.alternate && base == 16) {
117c9af8739SRadim Krčmář 		if (props.pad == '0') {
118c9af8739SRadim Krčmář 			addchar(ps, '0');
119c9af8739SRadim Krčmář 			addchar(ps, 'x');
120c9af8739SRadim Krčmář 
121c9af8739SRadim Krčmář 			if (props.npad > 0)
122c9af8739SRadim Krčmář 				props.npad = MAX(props.npad - 2, 0);
123c9af8739SRadim Krčmář 		} else {
1248ef44442SRadim Krčmář 			*p++ = 'x';
1258ef44442SRadim Krčmář 			*p++ = '0';
1268ef44442SRadim Krčmář 		}
127c9af8739SRadim Krčmář 	}
1287d36db35SAvi Kivity 
1297d36db35SAvi Kivity 	for (i = 0; i < (p - buf) / 2; ++i) {
1307d36db35SAvi Kivity 		char tmp;
1317d36db35SAvi Kivity 
1327d36db35SAvi Kivity 		tmp = buf[i];
1337d36db35SAvi Kivity 		buf[i] = p[-1 - i];
1347d36db35SAvi Kivity 		p[-1 - i] = tmp;
1357d36db35SAvi Kivity 	}
1367d36db35SAvi Kivity 
1377d36db35SAvi Kivity 	*p = 0;
1387d36db35SAvi Kivity 
1392c978d9bSAndrew Jones 	print_str(ps, buf, props);
1402c978d9bSAndrew Jones }
1412c978d9bSAndrew Jones 
1422c978d9bSAndrew Jones static int fmtnum(const char **fmt)
1432c978d9bSAndrew Jones {
1442c978d9bSAndrew Jones 	const char *f = *fmt;
1452c978d9bSAndrew Jones 	int len = 0, num;
1462c978d9bSAndrew Jones 
1472c978d9bSAndrew Jones 	if (*f == '-')
1482c978d9bSAndrew Jones 		++f, ++len;
1492c978d9bSAndrew Jones 
1502c978d9bSAndrew Jones 	while (*f >= '0' && *f <= '9')
1512c978d9bSAndrew Jones 		++f, ++len;
1522c978d9bSAndrew Jones 
1532c978d9bSAndrew Jones 	num = atol(*fmt);
1542c978d9bSAndrew Jones 	*fmt += len;
1552c978d9bSAndrew Jones 	return num;
1567d36db35SAvi Kivity }
1577d36db35SAvi Kivity 
158*2a9c2e2fSNikos Nikoleris /*
159*2a9c2e2fSNikos Nikoleris  * Adapted from drivers/firmware/efi/libstub/vsprintf.c
160*2a9c2e2fSNikos Nikoleris  */
161*2a9c2e2fSNikos Nikoleris static int skip_atoi(const char **s)
162*2a9c2e2fSNikos Nikoleris {
163*2a9c2e2fSNikos Nikoleris 	int i = 0;
164*2a9c2e2fSNikos Nikoleris 
165*2a9c2e2fSNikos Nikoleris 	do {
166*2a9c2e2fSNikos Nikoleris 		i = i*10 + *((*s)++) - '0';
167*2a9c2e2fSNikos Nikoleris 	} while (isdigit(**s));
168*2a9c2e2fSNikos Nikoleris 
169*2a9c2e2fSNikos Nikoleris 	return i;
170*2a9c2e2fSNikos Nikoleris }
171*2a9c2e2fSNikos Nikoleris 
172*2a9c2e2fSNikos Nikoleris /*
173*2a9c2e2fSNikos Nikoleris  * Adapted from drivers/firmware/efi/libstub/vsprintf.c
174*2a9c2e2fSNikos Nikoleris  */
175*2a9c2e2fSNikos Nikoleris static int get_int(const char **fmt, va_list *ap)
176*2a9c2e2fSNikos Nikoleris {
177*2a9c2e2fSNikos Nikoleris 	if (isdigit(**fmt))
178*2a9c2e2fSNikos Nikoleris 		return skip_atoi(fmt);
179*2a9c2e2fSNikos Nikoleris 
180*2a9c2e2fSNikos Nikoleris 	if (**fmt == '*') {
181*2a9c2e2fSNikos Nikoleris 		++(*fmt);
182*2a9c2e2fSNikos Nikoleris 		/* it's the next argument */
183*2a9c2e2fSNikos Nikoleris 		return va_arg(*ap, int);
184*2a9c2e2fSNikos Nikoleris 	}
185*2a9c2e2fSNikos Nikoleris 	return 0;
186*2a9c2e2fSNikos Nikoleris }
187*2a9c2e2fSNikos Nikoleris 
1887d36db35SAvi Kivity int vsnprintf(char *buf, int size, const char *fmt, va_list va)
1897d36db35SAvi Kivity {
1907d36db35SAvi Kivity 	pstream_t s;
191*2a9c2e2fSNikos Nikoleris 	va_list args;
192*2a9c2e2fSNikos Nikoleris 
193*2a9c2e2fSNikos Nikoleris 	/*
194*2a9c2e2fSNikos Nikoleris 	 * We want to pass our input va_list to helper functions by reference,
195*2a9c2e2fSNikos Nikoleris 	 * but there's an annoying edge case. If va_list was originally passed
196*2a9c2e2fSNikos Nikoleris 	 * to us by value, we could just pass &ap down to the helpers. This is
197*2a9c2e2fSNikos Nikoleris 	 * the case on, for example, X86_32.
198*2a9c2e2fSNikos Nikoleris 	 * However, on X86_64 (and possibly others), va_list is actually a
199*2a9c2e2fSNikos Nikoleris 	 * size-1 array containing a structure. Our function parameter ap has
200*2a9c2e2fSNikos Nikoleris 	 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
201*2a9c2e2fSNikos Nikoleris 	 * which is what will be expected by a function taking a va_list *
202*2a9c2e2fSNikos Nikoleris 	 * parameter.
203*2a9c2e2fSNikos Nikoleris 	 * One standard way to solve this mess is by creating a copy in a local
204*2a9c2e2fSNikos Nikoleris 	 * variable of type va_list and then passing a pointer to that local
205*2a9c2e2fSNikos Nikoleris 	 * copy instead, which is what we do here.
206*2a9c2e2fSNikos Nikoleris 	 */
207*2a9c2e2fSNikos Nikoleris 	va_copy(args, va);
2087d36db35SAvi Kivity 
2097d36db35SAvi Kivity 	s.buffer = buf;
2107d36db35SAvi Kivity 	s.remain = size - 1;
2117d36db35SAvi Kivity 	s.added = 0;
2127d36db35SAvi Kivity 	while (*fmt) {
2137d36db35SAvi Kivity 		char f = *fmt++;
2147d36db35SAvi Kivity 		int nlong = 0;
2152c978d9bSAndrew Jones 		strprops_t props;
2162c978d9bSAndrew Jones 		memset(&props, 0, sizeof(props));
2172c978d9bSAndrew Jones 		props.pad = ' ';
218*2a9c2e2fSNikos Nikoleris 		props.precision = -1;
2197d36db35SAvi Kivity 
2207d36db35SAvi Kivity 		if (f != '%') {
2217d36db35SAvi Kivity 			addchar(&s, f);
2227d36db35SAvi Kivity 			continue;
2237d36db35SAvi Kivity 		}
2247d36db35SAvi Kivity morefmt:
2257d36db35SAvi Kivity 		f = *fmt++;
2267d36db35SAvi Kivity 		switch (f) {
2277d36db35SAvi Kivity 		case '%':
2287d36db35SAvi Kivity 			addchar(&s, '%');
2297d36db35SAvi Kivity 			break;
2303d7d5195SMichael S. Tsirkin 		case 'c':
231*2a9c2e2fSNikos Nikoleris 			addchar(&s, va_arg(args, int));
2323d7d5195SMichael S. Tsirkin 			break;
2337d36db35SAvi Kivity 		case '\0':
2347d36db35SAvi Kivity 			--fmt;
2357d36db35SAvi Kivity 			break;
236*2a9c2e2fSNikos Nikoleris 		case '.':
237*2a9c2e2fSNikos Nikoleris 			props.pad = ' ';
238*2a9c2e2fSNikos Nikoleris 			props.precision = get_int(&fmt, &args);
239*2a9c2e2fSNikos Nikoleris 			goto morefmt;
240c9af8739SRadim Krčmář 		case '#':
241c9af8739SRadim Krčmář 			props.alternate = true;
242c9af8739SRadim Krčmář 			goto morefmt;
2432c978d9bSAndrew Jones 		case '0':
2442c978d9bSAndrew Jones 			props.pad = '0';
2452c978d9bSAndrew Jones 			++fmt;
2462c978d9bSAndrew Jones 			/* fall through */
2472c978d9bSAndrew Jones 		case '1' ... '9':
2482c978d9bSAndrew Jones 		case '-':
2492c978d9bSAndrew Jones 			--fmt;
2502c978d9bSAndrew Jones 			props.npad = fmtnum(&fmt);
2512c978d9bSAndrew Jones 			goto morefmt;
2527d36db35SAvi Kivity 		case 'l':
2537d36db35SAvi Kivity 			++nlong;
2547d36db35SAvi Kivity 			goto morefmt;
255cda042caSPaolo Bonzini 		case 't':
256cda042caSPaolo Bonzini 		case 'z':
257cda042caSPaolo Bonzini 			/* Here we only care that sizeof(size_t) == sizeof(long).
258cda042caSPaolo Bonzini 			 * On a 32-bit platform it doesn't matter that size_t is
259cda042caSPaolo Bonzini 			 * typedef'ed to int or long; va_arg will work either way.
260cda042caSPaolo Bonzini 			 * Same for ptrdiff_t (%td).
261cda042caSPaolo Bonzini 			 */
262cda042caSPaolo Bonzini 			nlong = 1;
263cda042caSPaolo Bonzini 			goto morefmt;
2647d36db35SAvi Kivity 		case 'd':
2657d36db35SAvi Kivity 			switch (nlong) {
2667d36db35SAvi Kivity 			case 0:
267*2a9c2e2fSNikos Nikoleris 				print_int(&s, va_arg(args, int), 10, props);
2687d36db35SAvi Kivity 				break;
2697d36db35SAvi Kivity 			case 1:
270*2a9c2e2fSNikos Nikoleris 				print_int(&s, va_arg(args, long), 10, props);
2717d36db35SAvi Kivity 				break;
2727d36db35SAvi Kivity 			default:
273*2a9c2e2fSNikos Nikoleris 				print_int(&s, va_arg(args, long long), 10, props);
2747d36db35SAvi Kivity 				break;
2757d36db35SAvi Kivity 			}
2767d36db35SAvi Kivity 			break;
2773a08f439SAlex Bennée 		case 'u':
2783a08f439SAlex Bennée 			switch (nlong) {
2793a08f439SAlex Bennée 			case 0:
280*2a9c2e2fSNikos Nikoleris 				print_unsigned(&s, va_arg(args, unsigned int), 10, props);
2813a08f439SAlex Bennée 				break;
2823a08f439SAlex Bennée 			case 1:
283*2a9c2e2fSNikos Nikoleris 				print_unsigned(&s, va_arg(args, unsigned long), 10, props);
2843a08f439SAlex Bennée 				break;
2853a08f439SAlex Bennée 			default:
286*2a9c2e2fSNikos Nikoleris 				print_unsigned(&s, va_arg(args, unsigned long long), 10, props);
2873a08f439SAlex Bennée 				break;
2883a08f439SAlex Bennée 			}
2893a08f439SAlex Bennée 			break;
2907d36db35SAvi Kivity 		case 'x':
2917d36db35SAvi Kivity 			switch (nlong) {
2927d36db35SAvi Kivity 			case 0:
293*2a9c2e2fSNikos Nikoleris 				print_unsigned(&s, va_arg(args, unsigned int), 16, props);
2947d36db35SAvi Kivity 				break;
2957d36db35SAvi Kivity 			case 1:
296*2a9c2e2fSNikos Nikoleris 				print_unsigned(&s, va_arg(args, unsigned long), 16, props);
2977d36db35SAvi Kivity 				break;
2987d36db35SAvi Kivity 			default:
299*2a9c2e2fSNikos Nikoleris 				print_unsigned(&s, va_arg(args, unsigned long long), 16, props);
3007d36db35SAvi Kivity 				break;
3017d36db35SAvi Kivity 			}
3027d36db35SAvi Kivity 			break;
3037d36db35SAvi Kivity 		case 'p':
3048ef44442SRadim Krčmář 			props.alternate = true;
305*2a9c2e2fSNikos Nikoleris 			print_unsigned(&s, (unsigned long)va_arg(args, void *), 16, props);
3067d36db35SAvi Kivity 			break;
3077d36db35SAvi Kivity 		case 's':
308*2a9c2e2fSNikos Nikoleris 			print_str(&s, va_arg(args, const char *), props);
3097d36db35SAvi Kivity 			break;
3107d36db35SAvi Kivity 		default:
3117d36db35SAvi Kivity 			addchar(&s, f);
3127d36db35SAvi Kivity 			break;
3137d36db35SAvi Kivity 		}
3147d36db35SAvi Kivity 	}
315*2a9c2e2fSNikos Nikoleris 	va_end(args);
3167d36db35SAvi Kivity 	*s.buffer = 0;
3177d36db35SAvi Kivity 	return s.added;
3187d36db35SAvi Kivity }
3197d36db35SAvi Kivity 
3207d36db35SAvi Kivity int snprintf(char *buf, int size, const char *fmt, ...)
3217d36db35SAvi Kivity {
3227d36db35SAvi Kivity 	va_list va;
3237d36db35SAvi Kivity 	int r;
3247d36db35SAvi Kivity 
3257d36db35SAvi Kivity 	va_start(va, fmt);
3267d36db35SAvi Kivity 	r = vsnprintf(buf, size, fmt, va);
3277d36db35SAvi Kivity 	va_end(va);
3287d36db35SAvi Kivity 	return r;
3297d36db35SAvi Kivity }
3307d36db35SAvi Kivity 
331cb12ecccSAndrew Jones int vprintf(const char *fmt, va_list va)
332cb12ecccSAndrew Jones {
333cb12ecccSAndrew Jones 	char buf[BUFSZ];
334cb12ecccSAndrew Jones 	int r;
335cb12ecccSAndrew Jones 
336cb12ecccSAndrew Jones 	r = vsnprintf(buf, sizeof(buf), fmt, va);
337cb12ecccSAndrew Jones 	puts(buf);
338cb12ecccSAndrew Jones 	return r;
339cb12ecccSAndrew Jones }
340cb12ecccSAndrew Jones 
3417d36db35SAvi Kivity int printf(const char *fmt, ...)
3427d36db35SAvi Kivity {
3437d36db35SAvi Kivity 	va_list va;
344cb12ecccSAndrew Jones 	char buf[BUFSZ];
3457d36db35SAvi Kivity 	int r;
3467d36db35SAvi Kivity 
3477d36db35SAvi Kivity 	va_start(va, fmt);
3487d36db35SAvi Kivity 	r = vsnprintf(buf, sizeof buf, fmt, va);
3497d36db35SAvi Kivity 	va_end(va);
3507d36db35SAvi Kivity 	puts(buf);
3517d36db35SAvi Kivity 	return r;
3527d36db35SAvi Kivity }
353840375e1SPeter Feiner 
354840375e1SPeter Feiner void binstr(unsigned long x, char out[BINSTR_SZ])
355840375e1SPeter Feiner {
356840375e1SPeter Feiner 	int i;
357840375e1SPeter Feiner 	char *c;
358840375e1SPeter Feiner 	int n;
359840375e1SPeter Feiner 
360840375e1SPeter Feiner 	n = sizeof(unsigned long) * 8;
361840375e1SPeter Feiner 	i = 0;
362840375e1SPeter Feiner 	c = &out[0];
363840375e1SPeter Feiner 	for (;;) {
364840375e1SPeter Feiner 		*c++ = (x & (1ul << (n - i - 1))) ? '1' : '0';
365840375e1SPeter Feiner 		i++;
366840375e1SPeter Feiner 
367840375e1SPeter Feiner 		if (i == n) {
368840375e1SPeter Feiner 			*c = '\0';
369840375e1SPeter Feiner 			break;
370840375e1SPeter Feiner 		}
371840375e1SPeter Feiner 		if (i % 4 == 0)
372840375e1SPeter Feiner 			*c++ = '\'';
373840375e1SPeter Feiner 	}
374840375e1SPeter Feiner 	assert(c + 1 - &out[0] == BINSTR_SZ);
375840375e1SPeter Feiner }
376840375e1SPeter Feiner 
377840375e1SPeter Feiner void print_binstr(unsigned long x)
378840375e1SPeter Feiner {
379840375e1SPeter Feiner 	char out[BINSTR_SZ];
380840375e1SPeter Feiner 	binstr(x, out);
381840375e1SPeter Feiner 	printf("%s", out);
382840375e1SPeter Feiner }
383