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