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