460b9539 |
1 | /* |
2 | * This file is part of DisOrder |
3 | * Copyright (C) 2004 Richard Kettlewell |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, but |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
18 | * USA |
19 | */ |
20 | |
21 | #define NO_MEMORY_ALLOCATION |
22 | /* because byte_snprintf used from log.c */ |
23 | |
24 | #include <config.h> |
25 | #include "types.h" |
26 | |
27 | #include <stdio.h> |
28 | #include <stdarg.h> |
29 | #include <string.h> |
30 | #include <errno.h> |
31 | #include <stdlib.h> |
32 | #include <stddef.h> |
33 | |
34 | #include "printf.h" |
35 | #include "sink.h" |
36 | |
37 | enum flags { |
38 | f_thousands = 1, |
39 | f_left = 2, |
40 | f_sign = 4, |
41 | f_space = 8, |
42 | f_hash = 16, |
43 | f_zero = 32, |
44 | f_width = 256, |
45 | f_precision = 512 |
46 | }; |
47 | |
48 | enum lengths { |
49 | l_char = 1, |
50 | l_short, |
51 | l_long, |
52 | l_longlong, |
53 | l_size_t, |
54 | l_intmax_t, |
55 | l_ptrdiff_t, |
56 | l_longdouble |
57 | }; |
58 | |
59 | struct conversion; |
60 | |
61 | struct state { |
62 | struct sink *output; |
63 | int bytes; |
64 | va_list *ap; |
65 | }; |
66 | |
67 | struct specifier { |
68 | int ch; |
69 | int (*check)(const struct conversion *c); |
70 | int (*output)(struct state *s, struct conversion *c); |
71 | int base; |
72 | const char *digits; |
73 | const char *xform; |
74 | }; |
75 | |
76 | struct conversion { |
77 | unsigned flags; |
78 | int width; |
79 | int precision; |
80 | int length; |
81 | const struct specifier *specifier; |
82 | }; |
83 | |
84 | static const char flags[] = "'-+ #0"; |
85 | |
86 | /* write @nbytes@ to the output. Return -1 on error, 0 on success. |
87 | * Keeps track of the number of bytes written. */ |
88 | static int do_write(struct state *s, |
89 | const void *buffer, |
90 | int nbytes) { |
91 | if(s->bytes > INT_MAX - nbytes) { |
92 | #ifdef EOVERFLOW |
93 | errno = EOVERFLOW; |
94 | #endif |
95 | return -1; |
96 | } |
97 | if(s->output->write(s->output, buffer, nbytes) < 0) return -1; |
98 | s->bytes += nbytes; |
99 | return 0; |
100 | } |
101 | |
102 | /* write character @ch@ @n@ times, reasonably efficiently */ |
103 | static int do_pad(struct state *s, int ch, unsigned n) { |
104 | unsigned t; |
105 | const char *padding; |
106 | |
107 | switch(ch) { |
108 | case ' ': padding = " "; break; |
109 | case '0': padding = "00000000000000000000000000000000"; break; |
110 | default: abort(); |
111 | } |
112 | t = n / 32; |
113 | n %= 32; |
114 | while(t-- > 0) |
115 | if(do_write(s, padding, 32) < 0) return -1; |
116 | if(n > 0) |
117 | if(do_write(s, padding, n) < 0) return -1; |
118 | return 0; |
119 | } |
120 | |
121 | /* pick up the integer at @ptr@, returning it via @intp@. Return the |
122 | * number of characters consumed. Return 0 if there is no integer |
123 | * there and -1 if an error occurred (e.g. too big) */ |
124 | static int get_integer(int *intp, const char *ptr) { |
125 | long n; |
126 | char *e; |
127 | |
128 | errno = 0; |
129 | n = strtol(ptr, &e, 10); |
130 | if(errno || n > INT_MAX || n < INT_MIN || e == ptr) return -1; |
131 | *intp = n; |
132 | return e - ptr; |
133 | } |
134 | |
135 | /* consistency checks for various conversion specifications */ |
136 | |
137 | static int check_integer(const struct conversion *c) { |
138 | switch(c->length) { |
139 | case 0: |
140 | case l_char: |
141 | case l_short: |
142 | case l_long: |
143 | case l_longlong: |
144 | case l_intmax_t: |
145 | case l_size_t: |
146 | case l_longdouble: |
147 | return 0; |
148 | default: |
149 | return -1; |
150 | } |
151 | } |
152 | |
153 | static int check_string(const struct conversion *c) { |
154 | switch(c->length) { |
155 | case 0: |
156 | /* XXX don't support %ls, %lc */ |
157 | return 0; |
158 | default: |
159 | return -1; |
160 | } |
161 | } |
162 | |
163 | static int check_pointer(const struct conversion *c) { |
164 | if(c->length) return -1; |
165 | return 0; |
166 | } |
167 | |
168 | static int check_percent(const struct conversion *c) { |
169 | if(c->flags || c->width || c->precision || c->length) return -1; |
170 | return 0; |
171 | } |
172 | |
173 | /* output functions for various conversion specifications */ |
174 | |
175 | static int output_percent(struct state *s, |
176 | struct conversion attribute((unused)) *c) { |
177 | return do_write(s, "%", 1); |
178 | } |
179 | |
180 | static int output_integer(struct state *s, struct conversion *c) { |
181 | uintmax_t u; |
182 | intmax_t l; |
183 | char sign; |
184 | int base, dp, iszero, ndigits, prec, xform, sign_bytes, pad; |
185 | char digits[CHAR_BIT * sizeof (uintmax_t)]; /* overestimate */ |
186 | |
187 | switch(c->specifier->ch) { |
188 | default: |
189 | if(c->specifier->base < 0) { |
190 | switch(c->length) { |
191 | case 0: l = va_arg(*s->ap, int); break; |
192 | case l_char: l = (signed char)va_arg(*s->ap, int); break; |
193 | case l_short: l = (short)va_arg(*s->ap, int); break; |
194 | case l_long: l = va_arg(*s->ap, long); break; |
195 | case l_longlong: l = va_arg(*s->ap, long_long); break; |
196 | case l_intmax_t: l = va_arg(*s->ap, intmax_t); break; |
197 | case l_size_t: l = va_arg(*s->ap, ssize_t); break; |
198 | case l_ptrdiff_t: l = va_arg(*s->ap, ptrdiff_t); break; |
199 | default: abort(); |
200 | } |
201 | base = -c->specifier->base; |
202 | if(l < 0) { |
203 | u = -l; |
204 | sign = '-'; |
205 | } else { |
206 | u = l; |
207 | sign = 0; |
208 | } |
209 | } else { |
210 | switch(c->length) { |
211 | case 0: u = va_arg(*s->ap, unsigned int); break; |
212 | case l_char: u = (unsigned char)va_arg(*s->ap, unsigned int); break; |
213 | case l_short: u = (unsigned short)va_arg(*s->ap, unsigned int); break; |
214 | case l_long: u = va_arg(*s->ap, unsigned long); break; |
215 | case l_longlong: u = va_arg(*s->ap, u_long_long); break; |
216 | case l_intmax_t: u = va_arg(*s->ap, uintmax_t); break; |
217 | case l_size_t: u = va_arg(*s->ap, size_t); break; |
218 | case l_ptrdiff_t: u = va_arg(*s->ap, ptrdiff_t); break; |
219 | default: abort(); |
220 | } |
221 | base = c->specifier->base; |
222 | sign = 0; |
223 | } |
224 | break; |
225 | case 'p': |
226 | u = (uintptr_t)va_arg(*s->ap, void *); |
227 | c->flags |= f_hash; |
228 | base = c->specifier->base; |
229 | sign = 0; |
230 | break; |
231 | } |
232 | /* default precision */ |
233 | if(!(c->flags & f_precision)) |
234 | c->precision = 1; |
235 | /* enforce sign */ |
236 | if((c->flags & f_sign) && !sign) sign = '+'; |
237 | /* compute the digits */ |
238 | iszero = (u == 0); |
239 | dp = sizeof digits; |
240 | while(u) { |
241 | digits[--dp] = c->specifier->digits[u % base]; |
242 | u /= base; |
243 | } |
244 | ndigits = sizeof digits - dp; |
245 | /* alternative form */ |
246 | if(c->flags & f_hash) { |
247 | switch(base) { |
248 | case 8: |
249 | if((dp == sizeof digits || digits[dp] != '0') |
250 | && c->precision <= ndigits) |
251 | c->precision = ndigits + 1; |
252 | break; |
253 | } |
254 | if(!iszero && c->specifier->xform) |
255 | xform = strlen(c->specifier->xform); |
256 | else |
257 | xform = 0; |
258 | } else |
259 | xform = 0; |
260 | /* calculate number of 0s to add for precision */ |
261 | if(ndigits < c->precision) |
262 | prec = c->precision - ndigits; |
263 | else |
264 | prec = 0; |
265 | /* bytes occupied by the sign */ |
266 | if(sign) |
267 | sign_bytes = 1; |
268 | else |
269 | sign_bytes = 0; |
270 | /* XXX implement the ' ' flag */ |
271 | /* calculate number of bytes of padding */ |
272 | if(c->flags & f_width) { |
273 | if((pad = c->width - (ndigits + prec + xform + sign_bytes)) < 0) |
274 | pad = 0; |
275 | } else |
276 | pad = 0; |
277 | /* now we are ready to output. Possibilities are: |
278 | * [space pad][sign][xform][0 prec]digits |
279 | * [sign][xform][0 pad][0 prec]digits |
280 | * [sign][xform][0 prec]digits[space pad] |
281 | * |
282 | * '-' beats '0'. |
283 | */ |
284 | if(c->flags & f_left) { |
285 | if(pad && do_pad(s, ' ', pad) < 0) return -1; |
286 | if(sign && do_write(s, &sign, 1)) return -1; |
287 | if(xform && do_write(s, c->specifier->xform, xform)) return -1; |
288 | if(prec && do_pad(s, '0', prec) < 0) return -1; |
289 | if(ndigits && do_write(s, digits + dp, ndigits)) return -1; |
290 | } else if(c->flags & f_zero) { |
291 | if(sign && do_write(s, &sign, 1)) return -1; |
292 | if(xform && do_write(s, c->specifier->xform, xform)) return -1; |
293 | if(pad && do_pad(s, '0', pad) < 0) return -1; |
294 | if(prec && do_pad(s, '0', prec) < 0) return -1; |
295 | if(ndigits && do_write(s, digits + dp, ndigits)) return -1; |
296 | } else { |
297 | if(sign && do_write(s, &sign, 1)) return -1; |
298 | if(xform && do_write(s, c->specifier->xform, xform)) return -1; |
299 | if(prec && do_pad(s, '0', prec) < 0) return -1; |
300 | if(ndigits && do_write(s, digits + dp, ndigits)) return -1; |
301 | if(pad && do_pad(s, ' ', pad) < 0) return -1; |
302 | } |
303 | return 0; |
304 | } |
305 | |
306 | static int output_string(struct state *s, struct conversion *c) { |
307 | const char *str, *n; |
308 | int pad, len; |
309 | |
310 | str = va_arg(*s->ap, const char *); |
311 | if(c->flags & f_precision) { |
312 | if((n = memchr(str, 0, c->precision))) |
313 | len = n - str; |
314 | else |
315 | len = c->precision; |
316 | } else |
317 | len = strlen(str); |
318 | if(c->flags & f_width) { |
319 | if((pad = c->width - len) < 0) |
320 | pad = 0; |
321 | } else |
322 | pad = 0; |
323 | if(c->flags & f_left) { |
324 | if(pad && do_pad(s, ' ', pad) < 0) return -1; |
325 | if(do_write(s, str, len) < 0) return -1; |
326 | } else { |
327 | if(do_write(s, str, len) < 0) return -1; |
328 | if(pad && do_pad(s, ' ', pad) < 0) return -1; |
329 | } |
330 | return 0; |
331 | |
332 | } |
333 | |
334 | static int output_char(struct state *s, struct conversion *c) { |
335 | int pad; |
336 | char ch; |
337 | |
338 | ch = va_arg(*s->ap, int); |
339 | if(c->flags & f_width) { |
340 | if((pad = c->width - 1) < 0) |
341 | pad = 0; |
342 | } else |
343 | pad = 0; |
344 | if(c->flags & f_left) { |
345 | if(pad && do_pad(s, ' ', pad) < 0) return -1; |
346 | if(do_write(s, &ch, 1) < 0) return -1; |
347 | } else { |
348 | if(do_write(s, &ch, 1) < 0) return -1; |
349 | if(pad && do_pad(s, ' ', pad) < 0) return -1; |
350 | } |
351 | return 0; |
352 | } |
353 | |
354 | static int output_count(struct state *s, struct conversion *c) { |
355 | switch(c->length) { |
356 | case 0: *va_arg(*s->ap, int *) = s->bytes; break; |
357 | case l_char: *va_arg(*s->ap, signed char *) = s->bytes; break; |
358 | case l_short: *va_arg(*s->ap, short *) = s->bytes; break; |
359 | case l_long: *va_arg(*s->ap, long *) = s->bytes; break; |
360 | case l_longlong: *va_arg(*s->ap, long_long *) = s->bytes; break; |
361 | case l_intmax_t: *va_arg(*s->ap, intmax_t *) = s->bytes; break; |
362 | case l_size_t: *va_arg(*s->ap, ssize_t *) = s->bytes; break; |
363 | case l_ptrdiff_t: *va_arg(*s->ap, ptrdiff_t *) = s->bytes; break; |
364 | default: abort(); |
365 | } |
366 | return 0; |
367 | } |
368 | |
369 | /* table of conversion specifiers */ |
370 | static const struct specifier specifiers[] = { |
371 | /* XXX don't support floating point conversions */ |
372 | { '%', check_percent, output_percent, 0, 0, 0 }, |
373 | { 'X', check_integer, output_integer, 16, "0123456789ABCDEF", "0X" }, |
374 | { 'c', check_string, output_char, 0, 0, 0 }, |
375 | { 'd', check_integer, output_integer, -10, "0123456789", 0 }, |
376 | { 'i', check_integer, output_integer, -10, "0123456789", 0 }, |
377 | { 'n', check_integer, output_count, 0, 0, 0 }, |
378 | { 'o', check_integer, output_integer, 8, "01234567", 0 }, |
379 | { 'p', check_pointer, output_integer, 16, "0123456789abcdef", "0x" }, |
380 | { 's', check_string, output_string, 0, 0, 0 }, |
381 | { 'u', check_integer, output_integer, 10, "0123456789", 0 }, |
382 | { 'x', check_integer, output_integer, 16, "0123456789abcdef", "0x" }, |
383 | }; |
384 | |
385 | /* collect and check information about a conversion specification */ |
386 | static int parse_conversion(struct conversion *c, const char *ptr) { |
387 | int n, ch, l, r, m; |
388 | const char *q, *start = ptr; |
389 | |
390 | memset(c, 0, sizeof *c); |
391 | /* flags */ |
392 | while(*ptr && (q = strchr(flags, *ptr))) { |
393 | c->flags |= (1 << (q - flags)); |
394 | ++ptr; |
395 | } |
396 | /* minimum field width */ |
397 | if(*ptr >= '0' && *ptr <= '9') { |
398 | if((n = get_integer(&c->width, ptr)) < 0) return -1; |
399 | ptr += n; |
400 | c->flags |= f_width; |
401 | } else if(*ptr == '*') { |
402 | ++ptr; |
403 | c->width = -1; |
404 | c->flags |= f_width; |
405 | } |
406 | /* precision */ |
407 | if(*ptr == '.') { |
408 | ++ptr; |
409 | if(*ptr >= '0' && *ptr <= '9') { |
410 | if((n = get_integer(&c->precision, ptr)) < 0) return -1; |
411 | ptr += n; |
412 | } else if(*ptr == '*') { |
413 | ++ptr; |
414 | c->precision = -1; |
415 | } else |
416 | return -1; |
417 | c->flags |= f_precision; |
418 | } |
419 | /* length modifier */ |
420 | switch(ch = *ptr++) { |
421 | case 'h': |
422 | if((ch = *ptr++) == 'h') { c->length = l_char; ch = *ptr++; } |
423 | else c->length = l_short; |
424 | break; |
425 | case 'l': |
426 | if((ch = *ptr++) == 'l') { c->length = l_longlong; ch = *ptr++; } |
427 | else c->length = l_long; |
428 | break; |
429 | case 'q': c->length = l_longlong; ch = *ptr++; break; |
430 | case 'j': c->length = l_intmax_t; ch = *ptr++; break; |
431 | case 'z': c->length = l_size_t; ch = *ptr++; break; |
432 | case 't': c->length = l_ptrdiff_t; ch = *ptr++; break; |
433 | case 'L': c->length = l_longdouble; ch = *ptr++; break; |
434 | } |
435 | /* conversion specifier */ |
436 | l = 0; |
437 | r = sizeof specifiers / sizeof *specifiers; |
438 | while(l <= r && (specifiers[m = (l + r) / 2].ch != ch)) |
439 | if(ch < specifiers[m].ch) r = m - 1; |
440 | else l = m + 1; |
441 | if(specifiers[m].ch != ch) return -1; |
442 | if(specifiers[m].check(c)) return -1; |
443 | c->specifier = &specifiers[m]; |
444 | return ptr - start; |
445 | } |
446 | |
447 | /* ISO/IEC 9899:1999 7.19.6.1 */ |
448 | /* http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html */ |
449 | |
450 | int byte_vsinkprintf(struct sink *output, |
451 | const char *fmt, |
452 | va_list ap) { |
453 | int n; |
454 | const char *ptr; |
455 | struct state s; |
456 | struct conversion c; |
457 | |
458 | memset(&s, 0, sizeof s); |
459 | s.output = output; |
460 | s.ap = ≈ |
461 | while(*fmt) { |
462 | /* output text up to next conversion specification */ |
463 | for(ptr = fmt; *fmt && *fmt != '%'; ++fmt) |
464 | ; |
465 | if((n = fmt - ptr)) |
466 | if(do_write(&s, ptr, n) < 0) return -1; |
467 | if(!*fmt) |
468 | break; |
469 | ++fmt; |
470 | /* parse conversion */ |
471 | if((n = parse_conversion(&c, fmt)) < 0) return -1; |
472 | fmt += n; |
473 | /* fill in width and precision */ |
474 | if((c.flags & f_width) && c.width == -1) |
475 | if((c.width = va_arg(*s.ap, int)) < 0) { |
476 | c.width = -c.width; |
477 | c.flags |= f_left; |
478 | } |
479 | if((c.flags & f_precision) && c.precision == -1) |
480 | if((c.precision = va_arg(*s.ap, int)) < 0) |
481 | c.flags ^= f_precision; |
482 | /* generate the output */ |
483 | if(c.specifier->output(&s, &c) < 0) return -1; |
484 | } |
485 | return s.bytes; |
486 | } |
487 | |
488 | /* |
489 | Local Variables: |
490 | c-basic-offset:2 |
491 | comment-column:40 |
492 | End: |
493 | */ |