chiark / gitweb /
More event_*
[disorder] / lib / printf.c
CommitLineData
460b9539 1/*
2 * This file is part of DisOrder
681cb9fb 3 * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
460b9539 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
05b75f8d 24#include "common.h"
460b9539 25
460b9539 26#include <stdarg.h>
460b9539 27#include <errno.h>
460b9539 28#include <stddef.h>
29
30#include "printf.h"
31#include "sink.h"
e3426f7b 32#include "vacopy.h"
460b9539 33
34enum flags {
35 f_thousands = 1,
36 f_left = 2,
37 f_sign = 4,
38 f_space = 8,
39 f_hash = 16,
40 f_zero = 32,
41 f_width = 256,
42 f_precision = 512
43};
44
45enum lengths {
46 l_char = 1,
47 l_short,
48 l_long,
49 l_longlong,
50 l_size_t,
51 l_intmax_t,
52 l_ptrdiff_t,
53 l_longdouble
54};
55
56struct conversion;
57
58struct state {
59 struct sink *output;
60 int bytes;
6a6b327d 61 va_list ap;
460b9539 62};
63
64struct specifier {
65 int ch;
66 int (*check)(const struct conversion *c);
67 int (*output)(struct state *s, struct conversion *c);
68 int base;
69 const char *digits;
70 const char *xform;
71};
72
73struct conversion {
74 unsigned flags;
75 int width;
76 int precision;
77 int length;
78 const struct specifier *specifier;
79};
80
81static const char flags[] = "'-+ #0";
82
83/* write @nbytes@ to the output. Return -1 on error, 0 on success.
84 * Keeps track of the number of bytes written. */
85static int do_write(struct state *s,
86 const void *buffer,
87 int nbytes) {
88 if(s->bytes > INT_MAX - nbytes) {
89#ifdef EOVERFLOW
90 errno = EOVERFLOW;
91#endif
92 return -1;
93 }
681cb9fb
RK
94 if(s->output->write(s->output, buffer, nbytes) < 0)
95 return -1;
460b9539 96 s->bytes += nbytes;
97 return 0;
98}
99
100/* write character @ch@ @n@ times, reasonably efficiently */
101static int do_pad(struct state *s, int ch, unsigned n) {
102 unsigned t;
103 const char *padding;
104
105 switch(ch) {
106 case ' ': padding = " "; break;
107 case '0': padding = "00000000000000000000000000000000"; break;
108 default: abort();
109 }
110 t = n / 32;
111 n %= 32;
112 while(t-- > 0)
681cb9fb
RK
113 if(do_write(s, padding, 32) < 0)
114 return -1;
460b9539 115 if(n > 0)
681cb9fb
RK
116 if(do_write(s, padding, n) < 0)
117 return -1;
460b9539 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) */
124static int get_integer(int *intp, const char *ptr) {
125 long n;
126 char *e;
127
128 errno = 0;
129 n = strtol(ptr, &e, 10);
681cb9fb
RK
130 if(errno || n > INT_MAX || n < INT_MIN || e == ptr)
131 return -1;
460b9539 132 *intp = n;
133 return e - ptr;
134}
135
136/* consistency checks for various conversion specifications */
137
138static int check_integer(const struct conversion *c) {
139 switch(c->length) {
140 case 0:
141 case l_char:
142 case l_short:
143 case l_long:
144 case l_longlong:
145 case l_intmax_t:
146 case l_size_t:
147 case l_longdouble:
cb9a695c 148 case l_ptrdiff_t:
460b9539 149 return 0;
150 default:
151 return -1;
152 }
153}
154
155static int check_string(const struct conversion *c) {
156 switch(c->length) {
157 case 0:
158 /* XXX don't support %ls, %lc */
159 return 0;
160 default:
161 return -1;
162 }
163}
164
165static int check_pointer(const struct conversion *c) {
681cb9fb
RK
166 if(c->length)
167 return -1;
460b9539 168 return 0;
169}
170
171static int check_percent(const struct conversion *c) {
681cb9fb
RK
172 if(c->flags || c->width || c->precision || c->length)
173 return -1;
460b9539 174 return 0;
175}
176
177/* output functions for various conversion specifications */
178
179static int output_percent(struct state *s,
180 struct conversion attribute((unused)) *c) {
181 return do_write(s, "%", 1);
182}
183
184static int output_integer(struct state *s, struct conversion *c) {
185 uintmax_t u;
186 intmax_t l;
187 char sign;
188 int base, dp, iszero, ndigits, prec, xform, sign_bytes, pad;
189 char digits[CHAR_BIT * sizeof (uintmax_t)]; /* overestimate */
190
191 switch(c->specifier->ch) {
192 default:
193 if(c->specifier->base < 0) {
194 switch(c->length) {
6a6b327d
RK
195 case 0: l = va_arg(s->ap, int); break;
196 case l_char: l = (signed char)va_arg(s->ap, int); break;
197 case l_short: l = (short)va_arg(s->ap, int); break;
198 case l_long: l = va_arg(s->ap, long); break;
199 case l_longlong: l = va_arg(s->ap, long_long); break;
200 case l_intmax_t: l = va_arg(s->ap, intmax_t); break;
201 case l_size_t: l = va_arg(s->ap, ssize_t); break;
202 case l_ptrdiff_t: l = va_arg(s->ap, ptrdiff_t); break;
460b9539 203 default: abort();
204 }
205 base = -c->specifier->base;
206 if(l < 0) {
207 u = -l;
208 sign = '-';
209 } else {
210 u = l;
211 sign = 0;
212 }
213 } else {
214 switch(c->length) {
6a6b327d
RK
215 case 0: u = va_arg(s->ap, unsigned int); break;
216 case l_char: u = (unsigned char)va_arg(s->ap, unsigned int); break;
217 case l_short: u = (unsigned short)va_arg(s->ap, unsigned int); break;
218 case l_long: u = va_arg(s->ap, unsigned long); break;
219 case l_longlong: u = va_arg(s->ap, u_long_long); break;
220 case l_intmax_t: u = va_arg(s->ap, uintmax_t); break;
221 case l_size_t: u = va_arg(s->ap, size_t); break;
222 case l_ptrdiff_t: u = va_arg(s->ap, ptrdiff_t); break;
460b9539 223 default: abort();
224 }
225 base = c->specifier->base;
226 sign = 0;
227 }
228 break;
229 case 'p':
6a6b327d 230 u = (uintptr_t)va_arg(s->ap, void *);
460b9539 231 c->flags |= f_hash;
232 base = c->specifier->base;
233 sign = 0;
234 break;
235 }
236 /* default precision */
237 if(!(c->flags & f_precision))
238 c->precision = 1;
239 /* enforce sign */
681cb9fb
RK
240 if((c->flags & f_sign) && !sign)
241 sign = '+';
460b9539 242 /* compute the digits */
243 iszero = (u == 0);
244 dp = sizeof digits;
245 while(u) {
246 digits[--dp] = c->specifier->digits[u % base];
247 u /= base;
248 }
249 ndigits = sizeof digits - dp;
250 /* alternative form */
251 if(c->flags & f_hash) {
252 switch(base) {
253 case 8:
254 if((dp == sizeof digits || digits[dp] != '0')
255 && c->precision <= ndigits)
256 c->precision = ndigits + 1;
257 break;
258 }
259 if(!iszero && c->specifier->xform)
260 xform = strlen(c->specifier->xform);
261 else
262 xform = 0;
263 } else
264 xform = 0;
265 /* calculate number of 0s to add for precision */
266 if(ndigits < c->precision)
267 prec = c->precision - ndigits;
268 else
269 prec = 0;
270 /* bytes occupied by the sign */
271 if(sign)
272 sign_bytes = 1;
273 else
274 sign_bytes = 0;
275 /* XXX implement the ' ' flag */
276 /* calculate number of bytes of padding */
277 if(c->flags & f_width) {
278 if((pad = c->width - (ndigits + prec + xform + sign_bytes)) < 0)
279 pad = 0;
280 } else
281 pad = 0;
282 /* now we are ready to output. Possibilities are:
283 * [space pad][sign][xform][0 prec]digits
284 * [sign][xform][0 pad][0 prec]digits
285 * [sign][xform][0 prec]digits[space pad]
286 *
287 * '-' beats '0'.
288 */
289 if(c->flags & f_left) {
681cb9fb
RK
290 if(sign && do_write(s, &sign, 1))
291 return -1;
292 if(xform && do_write(s, c->specifier->xform, xform))
293 return -1;
294 if(prec && do_pad(s, '0', prec) < 0)
295 return -1;
296 if(ndigits && do_write(s, digits + dp, ndigits))
297 return -1;
298 if(pad && do_pad(s, ' ', pad) < 0)
299 return -1;
460b9539 300 } else if(c->flags & f_zero) {
681cb9fb
RK
301 if(sign && do_write(s, &sign, 1))
302 return -1;
303 if(xform && do_write(s, c->specifier->xform, xform))
304 return -1;
305 if(pad && do_pad(s, '0', pad) < 0)
306 return -1;
307 if(prec && do_pad(s, '0', prec) < 0)
308 return -1;
309 if(ndigits && do_write(s, digits + dp, ndigits))
310 return -1;
460b9539 311 } else {
681cb9fb
RK
312 if(pad && do_pad(s, ' ', pad) < 0)
313 return -1;
314 if(sign && do_write(s, &sign, 1))
315 return -1;
316 if(xform && do_write(s, c->specifier->xform, xform))
317 return -1;
318 if(prec && do_pad(s, '0', prec) < 0)
319 return -1;
320 if(ndigits && do_write(s, digits + dp, ndigits))
321 return -1;
460b9539 322 }
323 return 0;
324}
325
326static int output_string(struct state *s, struct conversion *c) {
327 const char *str, *n;
328 int pad, len;
329
6a6b327d 330 str = va_arg(s->ap, const char *);
460b9539 331 if(c->flags & f_precision) {
332 if((n = memchr(str, 0, c->precision)))
333 len = n - str;
334 else
335 len = c->precision;
336 } else
337 len = strlen(str);
338 if(c->flags & f_width) {
339 if((pad = c->width - len) < 0)
340 pad = 0;
341 } else
342 pad = 0;
343 if(c->flags & f_left) {
681cb9fb
RK
344 if(do_write(s, str, len) < 0)
345 return -1;
346 if(pad && do_pad(s, ' ', pad) < 0)
347 return -1;
460b9539 348 } else {
681cb9fb
RK
349 if(pad && do_pad(s, ' ', pad) < 0)
350 return -1;
351 if(do_write(s, str, len) < 0)
352 return -1;
460b9539 353 }
354 return 0;
355
356}
357
358static int output_char(struct state *s, struct conversion *c) {
359 int pad;
360 char ch;
361
6a6b327d 362 ch = va_arg(s->ap, int);
460b9539 363 if(c->flags & f_width) {
364 if((pad = c->width - 1) < 0)
365 pad = 0;
366 } else
367 pad = 0;
368 if(c->flags & f_left) {
681cb9fb
RK
369 if(do_write(s, &ch, 1) < 0)
370 return -1;
371 if(pad && do_pad(s, ' ', pad) < 0)
372 return -1;
460b9539 373 } else {
681cb9fb
RK
374 if(pad && do_pad(s, ' ', pad) < 0)
375 return -1;
376 if(do_write(s, &ch, 1) < 0)
377 return -1;
460b9539 378 }
379 return 0;
380}
381
382static int output_count(struct state *s, struct conversion *c) {
383 switch(c->length) {
6a6b327d
RK
384 case 0: *va_arg(s->ap, int *) = s->bytes; break;
385 case l_char: *va_arg(s->ap, signed char *) = s->bytes; break;
386 case l_short: *va_arg(s->ap, short *) = s->bytes; break;
387 case l_long: *va_arg(s->ap, long *) = s->bytes; break;
388 case l_longlong: *va_arg(s->ap, long_long *) = s->bytes; break;
389 case l_intmax_t: *va_arg(s->ap, intmax_t *) = s->bytes; break;
390 case l_size_t: *va_arg(s->ap, ssize_t *) = s->bytes; break;
391 case l_ptrdiff_t: *va_arg(s->ap, ptrdiff_t *) = s->bytes; break;
460b9539 392 default: abort();
393 }
394 return 0;
395}
396
397/* table of conversion specifiers */
398static const struct specifier specifiers[] = {
399 /* XXX don't support floating point conversions */
400 { '%', check_percent, output_percent, 0, 0, 0 },
401 { 'X', check_integer, output_integer, 16, "0123456789ABCDEF", "0X" },
402 { 'c', check_string, output_char, 0, 0, 0 },
403 { 'd', check_integer, output_integer, -10, "0123456789", 0 },
404 { 'i', check_integer, output_integer, -10, "0123456789", 0 },
405 { 'n', check_integer, output_count, 0, 0, 0 },
406 { 'o', check_integer, output_integer, 8, "01234567", 0 },
407 { 'p', check_pointer, output_integer, 16, "0123456789abcdef", "0x" },
408 { 's', check_string, output_string, 0, 0, 0 },
409 { 'u', check_integer, output_integer, 10, "0123456789", 0 },
410 { 'x', check_integer, output_integer, 16, "0123456789abcdef", "0x" },
411};
412
413/* collect and check information about a conversion specification */
414static int parse_conversion(struct conversion *c, const char *ptr) {
415 int n, ch, l, r, m;
416 const char *q, *start = ptr;
417
418 memset(c, 0, sizeof *c);
419 /* flags */
420 while(*ptr && (q = strchr(flags, *ptr))) {
421 c->flags |= (1 << (q - flags));
422 ++ptr;
423 }
424 /* minimum field width */
425 if(*ptr >= '0' && *ptr <= '9') {
681cb9fb
RK
426 if((n = get_integer(&c->width, ptr)) < 0)
427 return -1;
460b9539 428 ptr += n;
429 c->flags |= f_width;
430 } else if(*ptr == '*') {
431 ++ptr;
432 c->width = -1;
433 c->flags |= f_width;
434 }
435 /* precision */
436 if(*ptr == '.') {
437 ++ptr;
438 if(*ptr >= '0' && *ptr <= '9') {
681cb9fb
RK
439 if((n = get_integer(&c->precision, ptr)) < 0)
440 return -1;
460b9539 441 ptr += n;
442 } else if(*ptr == '*') {
443 ++ptr;
444 c->precision = -1;
445 } else
0c740e59 446 c->precision = 0;
460b9539 447 c->flags |= f_precision;
448 }
449 /* length modifier */
450 switch(ch = *ptr++) {
451 case 'h':
681cb9fb
RK
452 if((ch = *ptr++) == 'h') {
453 c->length = l_char;
454 ch = *ptr++;
455 }
456 else
457 c->length = l_short;
460b9539 458 break;
459 case 'l':
681cb9fb
RK
460 if((ch = *ptr++) == 'l') {
461 c->length = l_longlong;
462 ch = *ptr++;
463 }
464 else
465 c->length = l_long;
460b9539 466 break;
467 case 'q': c->length = l_longlong; ch = *ptr++; break;
468 case 'j': c->length = l_intmax_t; ch = *ptr++; break;
469 case 'z': c->length = l_size_t; ch = *ptr++; break;
470 case 't': c->length = l_ptrdiff_t; ch = *ptr++; break;
471 case 'L': c->length = l_longdouble; ch = *ptr++; break;
472 }
473 /* conversion specifier */
474 l = 0;
475 r = sizeof specifiers / sizeof *specifiers;
476 while(l <= r && (specifiers[m = (l + r) / 2].ch != ch))
681cb9fb
RK
477 if(ch < specifiers[m].ch)
478 r = m - 1;
479 else
480 l = m + 1;
481 if(specifiers[m].ch != ch)
482 return -1;
483 if(specifiers[m].check(c))
484 return -1;
460b9539 485 c->specifier = &specifiers[m];
486 return ptr - start;
487}
488
489/* ISO/IEC 9899:1999 7.19.6.1 */
490/* http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html */
491
492int byte_vsinkprintf(struct sink *output,
493 const char *fmt,
494 va_list ap) {
495 int n;
496 const char *ptr;
497 struct state s;
498 struct conversion c;
499
500 memset(&s, 0, sizeof s);
501 s.output = output;
6a6b327d 502 va_copy(s.ap,ap);
460b9539 503 while(*fmt) {
504 /* output text up to next conversion specification */
505 for(ptr = fmt; *fmt && *fmt != '%'; ++fmt)
506 ;
507 if((n = fmt - ptr))
681cb9fb
RK
508 if(do_write(&s, ptr, n) < 0)
509 goto error;
460b9539 510 if(!*fmt)
511 break;
512 ++fmt;
513 /* parse conversion */
681cb9fb
RK
514 if((n = parse_conversion(&c, fmt)) < 0)
515 goto error;
460b9539 516 fmt += n;
517 /* fill in width and precision */
518 if((c.flags & f_width) && c.width == -1)
6a6b327d 519 if((c.width = va_arg(s.ap, int)) < 0) {
460b9539 520 c.width = -c.width;
521 c.flags |= f_left;
522 }
523 if((c.flags & f_precision) && c.precision == -1)
6a6b327d 524 if((c.precision = va_arg(s.ap, int)) < 0)
460b9539 525 c.flags ^= f_precision;
526 /* generate the output */
681cb9fb
RK
527 if(c.specifier->output(&s, &c) < 0)
528 goto error;
460b9539 529 }
6a6b327d 530 va_end(s.ap);
460b9539 531 return s.bytes;
6a6b327d
RK
532error:
533 va_end(s.ap);
534 return -1;
460b9539 535}
536
681cb9fb
RK
537int byte_sinkprintf(struct sink *output, const char *fmt, ...) {
538 int n;
539 va_list ap;
540
541 va_start(ap, fmt);
542 n = byte_vsinkprintf(output, fmt, ap);
543 va_end(ap);
544 return n;
545}
546
460b9539 547/*
548Local Variables:
549c-basic-offset:2
550comment-column:40
551End:
552*/