xref: /kvm-unit-tests/lib/printf.c (revision 0e9812980ee56ace8d40f1001949de571f30dddc)
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 
10 #define BUFSZ 2000
11 
12 typedef struct pstream {
13 	char *buffer;
14 	int remain;
15 	int added;
16 } pstream_t;
17 
18 typedef struct strprops {
19 	char pad;
20 	int npad;
21 	bool alternate;
22 } strprops_t;
23 
24 static void addchar(pstream_t *p, char c)
25 {
26 	if (p->remain) {
27 		*p->buffer++ = c;
28 		--p->remain;
29 	}
30 	++p->added;
31 }
32 
33 static void print_str(pstream_t *p, const char *s, strprops_t props)
34 {
35 	const char *s_orig = s;
36 	int npad = props.npad;
37 
38 	if (npad > 0) {
39 		npad -= strlen(s_orig);
40 		while (npad > 0) {
41 			addchar(p, props.pad);
42 			--npad;
43 		}
44 	}
45 
46 	while (*s)
47 		addchar(p, *s++);
48 
49 	if (npad < 0) {
50 		props.pad = ' ';	/* ignore '0' flag with '-' flag */
51 		npad += strlen(s_orig);
52 		while (npad < 0) {
53 			addchar(p, props.pad);
54 			++npad;
55 		}
56 	}
57 }
58 
59 static char digits[16] = "0123456789abcdef";
60 
61 static void print_int(pstream_t *ps, long long n, int base, strprops_t props)
62 {
63 	char buf[sizeof(long) * 3 + 2], *p = buf;
64 	int s = 0, i;
65 
66 	if (n < 0) {
67 		n = -n;
68 		s = 1;
69 	}
70 
71 	while (n) {
72 		*p++ = digits[n % base];
73 		n /= base;
74 	}
75 
76 	if (s)
77 		*p++ = '-';
78 
79 	if (p == buf)
80 		*p++ = '0';
81 
82 	for (i = 0; i < (p - buf) / 2; ++i) {
83 		char tmp;
84 
85 		tmp = buf[i];
86 		buf[i] = p[-1 - i];
87 		p[-1 - i] = tmp;
88 	}
89 
90 	*p = 0;
91 
92 	print_str(ps, buf, props);
93 }
94 
95 static void print_unsigned(pstream_t *ps, unsigned long long n, int base,
96 			   strprops_t props)
97 {
98 	char buf[sizeof(long) * 3 + 3], *p = buf;
99 	int i;
100 
101 	while (n) {
102 		*p++ = digits[n % base];
103 		n /= base;
104 	}
105 
106 	if (p == buf)
107 		*p++ = '0';
108 	else if (props.alternate && base == 16) {
109 		if (props.pad == '0') {
110 			addchar(ps, '0');
111 			addchar(ps, 'x');
112 
113 			if (props.npad > 0)
114 				props.npad = MAX(props.npad - 2, 0);
115 		} else {
116 			*p++ = 'x';
117 			*p++ = '0';
118 		}
119 	}
120 
121 	for (i = 0; i < (p - buf) / 2; ++i) {
122 		char tmp;
123 
124 		tmp = buf[i];
125 		buf[i] = p[-1 - i];
126 		p[-1 - i] = tmp;
127 	}
128 
129 	*p = 0;
130 
131 	print_str(ps, buf, props);
132 }
133 
134 static int fmtnum(const char **fmt)
135 {
136 	const char *f = *fmt;
137 	int len = 0, num;
138 
139 	if (*f == '-')
140 		++f, ++len;
141 
142 	while (*f >= '0' && *f <= '9')
143 		++f, ++len;
144 
145 	num = atol(*fmt);
146 	*fmt += len;
147 	return num;
148 }
149 
150 int vsnprintf(char *buf, int size, const char *fmt, va_list va)
151 {
152 	pstream_t s;
153 
154 	s.buffer = buf;
155 	s.remain = size - 1;
156 	s.added = 0;
157 	while (*fmt) {
158 		char f = *fmt++;
159 		int nlong = 0;
160 		strprops_t props;
161 		memset(&props, 0, sizeof(props));
162 		props.pad = ' ';
163 
164 		if (f != '%') {
165 			addchar(&s, f);
166 			continue;
167 		}
168 morefmt:
169 		f = *fmt++;
170 		switch (f) {
171 		case '%':
172 			addchar(&s, '%');
173 			break;
174 		case 'c':
175 			addchar(&s, va_arg(va, int));
176 			break;
177 		case '\0':
178 			--fmt;
179 			break;
180 		case '#':
181 			props.alternate = true;
182 			goto morefmt;
183 		case '0':
184 			props.pad = '0';
185 			++fmt;
186 			/* fall through */
187 		case '1' ... '9':
188 		case '-':
189 			--fmt;
190 			props.npad = fmtnum(&fmt);
191 			goto morefmt;
192 		case 'l':
193 			++nlong;
194 			goto morefmt;
195 		case 't':
196 		case 'z':
197 			/* Here we only care that sizeof(size_t) == sizeof(long).
198 			 * On a 32-bit platform it doesn't matter that size_t is
199 			 * typedef'ed to int or long; va_arg will work either way.
200 			 * Same for ptrdiff_t (%td).
201 			 */
202 			nlong = 1;
203 			goto morefmt;
204 		case 'd':
205 			switch (nlong) {
206 			case 0:
207 				print_int(&s, va_arg(va, int), 10, props);
208 				break;
209 			case 1:
210 				print_int(&s, va_arg(va, long), 10, props);
211 				break;
212 			default:
213 				print_int(&s, va_arg(va, long long), 10, props);
214 				break;
215 			}
216 			break;
217 		case 'u':
218 			switch (nlong) {
219 			case 0:
220 				print_unsigned(&s, va_arg(va, unsigned), 10, props);
221 				break;
222 			case 1:
223 				print_unsigned(&s, va_arg(va, unsigned long), 10, props);
224 				break;
225 			default:
226 				print_unsigned(&s, va_arg(va, unsigned long long), 10, props);
227 				break;
228 			}
229 			break;
230 		case 'x':
231 			switch (nlong) {
232 			case 0:
233 				print_unsigned(&s, va_arg(va, unsigned), 16, props);
234 				break;
235 			case 1:
236 				print_unsigned(&s, va_arg(va, unsigned long), 16, props);
237 				break;
238 			default:
239 				print_unsigned(&s, va_arg(va, unsigned long long), 16, props);
240 				break;
241 			}
242 			break;
243 		case 'p':
244 			props.alternate = true;
245 			print_unsigned(&s, (unsigned long)va_arg(va, void *), 16, props);
246 			break;
247 		case 's':
248 			print_str(&s, va_arg(va, const char *), props);
249 			break;
250 		default:
251 			addchar(&s, f);
252 			break;
253 		}
254 	}
255 	*s.buffer = 0;
256 	return s.added;
257 }
258 
259 int snprintf(char *buf, int size, const char *fmt, ...)
260 {
261 	va_list va;
262 	int r;
263 
264 	va_start(va, fmt);
265 	r = vsnprintf(buf, size, fmt, va);
266 	va_end(va);
267 	return r;
268 }
269 
270 int vprintf(const char *fmt, va_list va)
271 {
272 	char buf[BUFSZ];
273 	int r;
274 
275 	r = vsnprintf(buf, sizeof(buf), fmt, va);
276 	puts(buf);
277 	return r;
278 }
279 
280 int printf(const char *fmt, ...)
281 {
282 	va_list va;
283 	char buf[BUFSZ];
284 	int r;
285 
286 	va_start(va, fmt);
287 	r = vsnprintf(buf, sizeof buf, fmt, va);
288 	va_end(va);
289 	puts(buf);
290 	return r;
291 }
292 
293 void binstr(unsigned long x, char out[BINSTR_SZ])
294 {
295 	int i;
296 	char *c;
297 	int n;
298 
299 	n = sizeof(unsigned long) * 8;
300 	i = 0;
301 	c = &out[0];
302 	for (;;) {
303 		*c++ = (x & (1ul << (n - i - 1))) ? '1' : '0';
304 		i++;
305 
306 		if (i == n) {
307 			*c = '\0';
308 			break;
309 		}
310 		if (i % 4 == 0)
311 			*c++ = '\'';
312 	}
313 	assert(c + 1 - &out[0] == BINSTR_SZ);
314 }
315 
316 void print_binstr(unsigned long x)
317 {
318 	char out[BINSTR_SZ];
319 	binstr(x, out);
320 	printf("%s", out);
321 }
322