xref: /kvm-unit-tests/lib/printf.c (revision 2a9c2e2f1240cc274b6b56631fc17a8394fa987a)
1 /*
2  * libc printf and friends
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Library General Public License version 2.
6  */
7 
8 #include "libcflat.h"
9 #include "ctype.h"
10 
11 #define BUFSZ 2000
12 
13 typedef struct pstream {
14 	char *buffer;
15 	int remain;
16 	int added;
17 } pstream_t;
18 
19 typedef struct strprops {
20 	char pad;
21 	int npad;
22 	bool alternate;
23 	int precision;
24 } strprops_t;
25 
26 static void addchar(pstream_t *p, char c)
27 {
28 	if (p->remain) {
29 		*p->buffer++ = c;
30 		--p->remain;
31 	}
32 	++p->added;
33 }
34 
35 static void print_str(pstream_t *p, const char *s, strprops_t props)
36 {
37 	const char *s_orig = s;
38 	int npad = props.npad;
39 
40 	if (npad > 0) {
41 		npad -= strlen(s_orig);
42 		while (npad > 0) {
43 			addchar(p, props.pad);
44 			--npad;
45 		}
46 	}
47 
48 	while (*s && props.precision--)
49 		addchar(p, *s++);
50 
51 	if (npad < 0) {
52 		props.pad = ' ';	/* ignore '0' flag with '-' flag */
53 		npad += strlen(s_orig);
54 		while (npad < 0) {
55 			addchar(p, props.pad);
56 			++npad;
57 		}
58 	}
59 }
60 
61 static char digits[16] = "0123456789abcdef";
62 
63 static void print_int(pstream_t *ps, long long n, int base, strprops_t props)
64 {
65 	char buf[sizeof(long) * 3 + 2], *p = buf;
66 	int s = 0, i;
67 
68 	if (n < 0) {
69 		n = -n;
70 		s = 1;
71 	}
72 
73 	while (n) {
74 		*p++ = digits[n % base];
75 		n /= base;
76 	}
77 
78 	while (p == buf || (p - buf < props.precision))
79 		*p++ = '0';
80 	props.precision = -1;
81 
82 	if (s)
83 		*p++ = '-';
84 
85 	for (i = 0; i < (p - buf) / 2; ++i) {
86 		char tmp;
87 
88 		tmp = buf[i];
89 		buf[i] = p[-1 - i];
90 		p[-1 - i] = tmp;
91 	}
92 
93 	*p = 0;
94 
95 	print_str(ps, buf, props);
96 }
97 
98 static void print_unsigned(pstream_t *ps, unsigned long long n, int base,
99 			   strprops_t props)
100 {
101 	char buf[sizeof(long) * 3 + 3], *p = buf;
102 	int i;
103 
104 	while (n) {
105 		*p++ = digits[n % base];
106 		n /= base;
107 	}
108 
109 	if (p == buf)
110 		props.alternate = false;
111 
112 	while (p == buf || (p - buf < props.precision))
113 		*p++ = '0';
114 	props.precision = -1;
115 
116 	if (props.alternate && base == 16) {
117 		if (props.pad == '0') {
118 			addchar(ps, '0');
119 			addchar(ps, 'x');
120 
121 			if (props.npad > 0)
122 				props.npad = MAX(props.npad - 2, 0);
123 		} else {
124 			*p++ = 'x';
125 			*p++ = '0';
126 		}
127 	}
128 
129 	for (i = 0; i < (p - buf) / 2; ++i) {
130 		char tmp;
131 
132 		tmp = buf[i];
133 		buf[i] = p[-1 - i];
134 		p[-1 - i] = tmp;
135 	}
136 
137 	*p = 0;
138 
139 	print_str(ps, buf, props);
140 }
141 
142 static int fmtnum(const char **fmt)
143 {
144 	const char *f = *fmt;
145 	int len = 0, num;
146 
147 	if (*f == '-')
148 		++f, ++len;
149 
150 	while (*f >= '0' && *f <= '9')
151 		++f, ++len;
152 
153 	num = atol(*fmt);
154 	*fmt += len;
155 	return num;
156 }
157 
158 /*
159  * Adapted from drivers/firmware/efi/libstub/vsprintf.c
160  */
161 static int skip_atoi(const char **s)
162 {
163 	int i = 0;
164 
165 	do {
166 		i = i*10 + *((*s)++) - '0';
167 	} while (isdigit(**s));
168 
169 	return i;
170 }
171 
172 /*
173  * Adapted from drivers/firmware/efi/libstub/vsprintf.c
174  */
175 static int get_int(const char **fmt, va_list *ap)
176 {
177 	if (isdigit(**fmt))
178 		return skip_atoi(fmt);
179 
180 	if (**fmt == '*') {
181 		++(*fmt);
182 		/* it's the next argument */
183 		return va_arg(*ap, int);
184 	}
185 	return 0;
186 }
187 
188 int vsnprintf(char *buf, int size, const char *fmt, va_list va)
189 {
190 	pstream_t s;
191 	va_list args;
192 
193 	/*
194 	 * We want to pass our input va_list to helper functions by reference,
195 	 * but there's an annoying edge case. If va_list was originally passed
196 	 * to us by value, we could just pass &ap down to the helpers. This is
197 	 * the case on, for example, X86_32.
198 	 * However, on X86_64 (and possibly others), va_list is actually a
199 	 * size-1 array containing a structure. Our function parameter ap has
200 	 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
201 	 * which is what will be expected by a function taking a va_list *
202 	 * parameter.
203 	 * One standard way to solve this mess is by creating a copy in a local
204 	 * variable of type va_list and then passing a pointer to that local
205 	 * copy instead, which is what we do here.
206 	 */
207 	va_copy(args, va);
208 
209 	s.buffer = buf;
210 	s.remain = size - 1;
211 	s.added = 0;
212 	while (*fmt) {
213 		char f = *fmt++;
214 		int nlong = 0;
215 		strprops_t props;
216 		memset(&props, 0, sizeof(props));
217 		props.pad = ' ';
218 		props.precision = -1;
219 
220 		if (f != '%') {
221 			addchar(&s, f);
222 			continue;
223 		}
224 morefmt:
225 		f = *fmt++;
226 		switch (f) {
227 		case '%':
228 			addchar(&s, '%');
229 			break;
230 		case 'c':
231 			addchar(&s, va_arg(args, int));
232 			break;
233 		case '\0':
234 			--fmt;
235 			break;
236 		case '.':
237 			props.pad = ' ';
238 			props.precision = get_int(&fmt, &args);
239 			goto morefmt;
240 		case '#':
241 			props.alternate = true;
242 			goto morefmt;
243 		case '0':
244 			props.pad = '0';
245 			++fmt;
246 			/* fall through */
247 		case '1' ... '9':
248 		case '-':
249 			--fmt;
250 			props.npad = fmtnum(&fmt);
251 			goto morefmt;
252 		case 'l':
253 			++nlong;
254 			goto morefmt;
255 		case 't':
256 		case 'z':
257 			/* Here we only care that sizeof(size_t) == sizeof(long).
258 			 * On a 32-bit platform it doesn't matter that size_t is
259 			 * typedef'ed to int or long; va_arg will work either way.
260 			 * Same for ptrdiff_t (%td).
261 			 */
262 			nlong = 1;
263 			goto morefmt;
264 		case 'd':
265 			switch (nlong) {
266 			case 0:
267 				print_int(&s, va_arg(args, int), 10, props);
268 				break;
269 			case 1:
270 				print_int(&s, va_arg(args, long), 10, props);
271 				break;
272 			default:
273 				print_int(&s, va_arg(args, long long), 10, props);
274 				break;
275 			}
276 			break;
277 		case 'u':
278 			switch (nlong) {
279 			case 0:
280 				print_unsigned(&s, va_arg(args, unsigned int), 10, props);
281 				break;
282 			case 1:
283 				print_unsigned(&s, va_arg(args, unsigned long), 10, props);
284 				break;
285 			default:
286 				print_unsigned(&s, va_arg(args, unsigned long long), 10, props);
287 				break;
288 			}
289 			break;
290 		case 'x':
291 			switch (nlong) {
292 			case 0:
293 				print_unsigned(&s, va_arg(args, unsigned int), 16, props);
294 				break;
295 			case 1:
296 				print_unsigned(&s, va_arg(args, unsigned long), 16, props);
297 				break;
298 			default:
299 				print_unsigned(&s, va_arg(args, unsigned long long), 16, props);
300 				break;
301 			}
302 			break;
303 		case 'p':
304 			props.alternate = true;
305 			print_unsigned(&s, (unsigned long)va_arg(args, void *), 16, props);
306 			break;
307 		case 's':
308 			print_str(&s, va_arg(args, const char *), props);
309 			break;
310 		default:
311 			addchar(&s, f);
312 			break;
313 		}
314 	}
315 	va_end(args);
316 	*s.buffer = 0;
317 	return s.added;
318 }
319 
320 int snprintf(char *buf, int size, const char *fmt, ...)
321 {
322 	va_list va;
323 	int r;
324 
325 	va_start(va, fmt);
326 	r = vsnprintf(buf, size, fmt, va);
327 	va_end(va);
328 	return r;
329 }
330 
331 int vprintf(const char *fmt, va_list va)
332 {
333 	char buf[BUFSZ];
334 	int r;
335 
336 	r = vsnprintf(buf, sizeof(buf), fmt, va);
337 	puts(buf);
338 	return r;
339 }
340 
341 int printf(const char *fmt, ...)
342 {
343 	va_list va;
344 	char buf[BUFSZ];
345 	int r;
346 
347 	va_start(va, fmt);
348 	r = vsnprintf(buf, sizeof buf, fmt, va);
349 	va_end(va);
350 	puts(buf);
351 	return r;
352 }
353 
354 void binstr(unsigned long x, char out[BINSTR_SZ])
355 {
356 	int i;
357 	char *c;
358 	int n;
359 
360 	n = sizeof(unsigned long) * 8;
361 	i = 0;
362 	c = &out[0];
363 	for (;;) {
364 		*c++ = (x & (1ul << (n - i - 1))) ? '1' : '0';
365 		i++;
366 
367 		if (i == n) {
368 			*c = '\0';
369 			break;
370 		}
371 		if (i % 4 == 0)
372 			*c++ = '\'';
373 	}
374 	assert(c + 1 - &out[0] == BINSTR_SZ);
375 }
376 
377 void print_binstr(unsigned long x)
378 {
379 	char out[BINSTR_SZ];
380 	binstr(x, out);
381 	printf("%s", out);
382 }
383