chiark / gitweb /
fatal() testing for dateparse()
[disorder] / lib / printf.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2004, 2007, 2008 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 "common.h"
25
26 #include <stdarg.h>
27 #include <errno.h>
28 #include <stddef.h>
29
30 #include "printf.h"
31 #include "sink.h"
32 #include "vacopy.h"
33
34 enum 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
45 enum 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
56 struct conversion;
57
58 struct state {
59   struct sink *output;
60   int bytes;
61   va_list ap;
62 };
63
64 struct 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
73 struct conversion {
74   unsigned flags;
75   int width;
76   int precision;
77   int length;
78   const struct specifier *specifier;
79 };
80
81 static 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. */
85 static 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   }
94   if(s->output->write(s->output, buffer, nbytes) < 0)
95     return -1;
96   s->bytes += nbytes;
97   return 0;
98 }
99
100 /* write character @ch@ @n@ times, reasonably efficiently */
101 static 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)
113     if(do_write(s, padding, 32) < 0)
114       return -1;
115   if(n > 0)
116     if(do_write(s, padding, n) < 0)
117       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)
131     return -1;
132   *intp = n;
133   return e - ptr;
134 }
135
136 /* consistency checks for various conversion specifications */
137
138 static 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:
148   case l_ptrdiff_t:
149     return 0;
150   default:
151     return -1;
152   }
153 }
154
155 static 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
165 static int check_pointer(const struct conversion *c) {
166   if(c->length)
167     return -1;
168   return 0;
169 }
170
171 static int check_percent(const struct conversion *c) {
172   if(c->flags || c->width || c->precision || c->length)
173     return -1;
174   return 0;
175 }
176
177 /* output functions for various conversion specifications */
178
179 static int output_percent(struct state *s,
180                           struct conversion attribute((unused)) *c) {
181   return do_write(s, "%", 1);
182 }
183
184 static 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) {
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;
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) {
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;
223       default: abort();
224       }
225       base = c->specifier->base;
226       sign = 0;
227     }
228     break;
229   case 'p':
230     u = (uintptr_t)va_arg(s->ap, void *);
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 */
240   if((c->flags & f_sign) && !sign)
241     sign = '+';
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) {
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;
300   } else if(c->flags & f_zero) {
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;
311   } else {
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;
322   }
323   return 0;
324 }
325
326 static int output_string(struct state *s, struct conversion *c) {
327   const char *str, *n;
328   int pad, len;
329
330   str = va_arg(s->ap, const char *);
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) {
344     if(do_write(s, str, len) < 0)
345       return -1;
346     if(pad && do_pad(s, ' ', pad) < 0)
347       return -1;
348   } else {
349     if(pad && do_pad(s, ' ', pad) < 0)
350       return -1;
351     if(do_write(s, str, len) < 0)
352       return -1;
353   }
354   return 0;
355   
356 }
357
358 static int output_char(struct state *s, struct conversion *c) {
359   int pad;
360   char ch;
361
362   ch = va_arg(s->ap, int);
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) {
369     if(do_write(s, &ch, 1) < 0)
370       return -1;
371     if(pad && do_pad(s, ' ', pad) < 0)
372       return -1;
373   } else {
374     if(pad && do_pad(s, ' ', pad) < 0)
375       return -1;
376     if(do_write(s, &ch, 1) < 0)
377       return -1;
378   }
379   return 0;
380 }
381
382 static int output_count(struct state *s, struct conversion *c) {
383   switch(c->length) {
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;
392   default: abort();
393   }
394   return 0;
395 }
396
397 /* table of conversion specifiers */
398 static 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 */
414 static 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') {
426     if((n = get_integer(&c->width, ptr)) < 0)
427       return -1;
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') {
439       if((n = get_integer(&c->precision, ptr)) < 0)
440         return -1;
441       ptr += n;
442     } else if(*ptr == '*') {
443       ++ptr;
444       c->precision = -1;
445     } else
446       c->precision = 0;
447     c->flags |= f_precision;
448   }
449   /* length modifier */
450   switch(ch = *ptr++) {
451   case 'h':
452     if((ch = *ptr++) == 'h') {
453       c->length = l_char;
454       ch = *ptr++;
455     }
456     else
457       c->length = l_short;
458     break;
459   case 'l':
460     if((ch = *ptr++) == 'l') {
461       c->length = l_longlong;
462       ch = *ptr++;
463     }
464     else
465       c->length = l_long;
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))
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;
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
492 int 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;
502   va_copy(s.ap,ap);
503   while(*fmt) {
504     /* output text up to next conversion specification */
505     for(ptr = fmt; *fmt && *fmt != '%'; ++fmt)
506       ;
507     if((n = fmt - ptr))
508       if(do_write(&s, ptr, n) < 0)
509         goto error;
510     if(!*fmt)
511       break;
512     ++fmt;
513     /* parse conversion */
514     if((n = parse_conversion(&c, fmt)) < 0)
515       goto error;
516     fmt += n;
517     /* fill in width and precision */
518     if((c.flags & f_width) && c.width == -1)
519       if((c.width = va_arg(s.ap, int)) < 0) {
520         c.width = -c.width;
521         c.flags |= f_left;
522       }
523     if((c.flags & f_precision) && c.precision == -1)
524       if((c.precision = va_arg(s.ap, int)) < 0)
525         c.flags ^= f_precision;
526     /* generate the output */
527     if(c.specifier->output(&s, &c) < 0)
528       goto error;
529   }
530   va_end(s.ap);
531   return s.bytes;
532 error:
533   va_end(s.ap);
534   return -1;
535 }
536
537 int 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
547 /*
548 Local Variables:
549 c-basic-offset:2
550 comment-column:40
551 End:
552 */