xref: /kvm-unit-tests/lib/printf.c (revision 4363f1d9a646a5c7ea673bee8fc33ca6f2cddbd8)
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 
260 int snprintf(char *buf, int size, const char *fmt, ...)
261 {
262     va_list va;
263     int r;
264 
265     va_start(va, fmt);
266     r = vsnprintf(buf, size, fmt, va);
267     va_end(va);
268     return r;
269 }
270 
271 int vprintf(const char *fmt, va_list va)
272 {
273     char buf[BUFSZ];
274     int r;
275 
276     r = vsnprintf(buf, sizeof(buf), fmt, va);
277     puts(buf);
278     return r;
279 }
280 
281 int printf(const char *fmt, ...)
282 {
283     va_list va;
284     char buf[BUFSZ];
285     int r;
286 
287     va_start(va, fmt);
288     r = vsnprintf(buf, sizeof buf, fmt, va);
289     va_end(va);
290     puts(buf);
291     return r;
292 }
293 
294 void binstr(unsigned long x, char out[BINSTR_SZ])
295 {
296 	int i;
297 	char *c;
298 	int n;
299 
300 	n = sizeof(unsigned long) * 8;
301 	i = 0;
302 	c = &out[0];
303 	for (;;) {
304 		*c++ = (x & (1ul << (n - i - 1))) ? '1' : '0';
305 		i++;
306 
307 		if (i == n) {
308 			*c = '\0';
309 			break;
310 		}
311 		if (i % 4 == 0)
312 			*c++ = '\'';
313 	}
314 	assert(c + 1 - &out[0] == BINSTR_SZ);
315 }
316 
317 void print_binstr(unsigned long x)
318 {
319 	char out[BINSTR_SZ];
320 	binstr(x, out);
321 	printf("%s", out);
322 }
323