chiark / gitweb /
pinpoint tests for a couple more unicode.c bits
[disorder] / lib / printf.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2004, 2007 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) return -1;
99   s->bytes += nbytes;
100   return 0;
101 }
102
103 /* write character @ch@ @n@ times, reasonably efficiently */
104 static int do_pad(struct state *s, int ch, unsigned n) {
105   unsigned t;
106   const char *padding;
107   
108   switch(ch) {
109   case ' ': padding = "                                "; break;
110   case '0': padding = "00000000000000000000000000000000"; break;
111   default: abort();
112   }
113   t = n / 32;
114   n %= 32;
115   while(t-- > 0)
116     if(do_write(s, padding, 32) < 0) return -1;
117   if(n > 0)
118     if(do_write(s, padding, n) < 0) return -1;
119   return 0;
120 }
121
122 /* pick up the integer at @ptr@, returning it via @intp@.  Return the
123  * number of characters consumed.  Return 0 if there is no integer
124  * there and -1 if an error occurred (e.g. too big) */
125 static int get_integer(int *intp, const char *ptr) {
126   long n;
127   char *e;
128
129   errno = 0;
130   n = strtol(ptr, &e, 10);
131   if(errno || n > INT_MAX || n < INT_MIN || e == ptr) 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) return -1;
167   return 0;
168 }
169
170 static int check_percent(const struct conversion *c) {
171   if(c->flags || c->width || c->precision || c->length) return -1;
172   return 0;
173 }
174
175 /* output functions for various conversion specifications */
176
177 static int output_percent(struct state *s,
178                           struct conversion attribute((unused)) *c) {
179   return do_write(s, "%", 1);
180 }
181
182 static int output_integer(struct state *s, struct conversion *c) {
183   uintmax_t u;
184   intmax_t l;
185   char sign;
186   int base, dp, iszero, ndigits, prec, xform, sign_bytes, pad;
187   char digits[CHAR_BIT * sizeof (uintmax_t)]; /* overestimate */
188
189   switch(c->specifier->ch) {
190   default:
191     if(c->specifier->base < 0) {
192       switch(c->length) {
193       case 0: l = va_arg(s->ap, int); break;
194       case l_char: l = (signed char)va_arg(s->ap, int); break;
195       case l_short: l = (short)va_arg(s->ap, int); break;
196       case l_long: l = va_arg(s->ap, long); break;
197       case l_longlong: l = va_arg(s->ap, long_long); break;
198       case l_intmax_t: l = va_arg(s->ap, intmax_t); break;
199       case l_size_t: l = va_arg(s->ap, ssize_t); break;
200       case l_ptrdiff_t: l = va_arg(s->ap, ptrdiff_t); break;
201       default: abort();
202       }
203       base = -c->specifier->base;
204       if(l < 0) {
205         u = -l;
206         sign = '-';
207       } else {
208         u = l;
209         sign = 0;
210       }
211     } else {
212       switch(c->length) {
213       case 0: u = va_arg(s->ap, unsigned int); break;
214       case l_char: u = (unsigned char)va_arg(s->ap, unsigned int); break;
215       case l_short: u = (unsigned short)va_arg(s->ap, unsigned int); break;
216       case l_long: u = va_arg(s->ap, unsigned long); break;
217       case l_longlong: u = va_arg(s->ap, u_long_long); break;
218       case l_intmax_t: u = va_arg(s->ap, uintmax_t); break;
219       case l_size_t: u = va_arg(s->ap, size_t); break;
220       case l_ptrdiff_t: u = va_arg(s->ap, ptrdiff_t); break;
221       default: abort();
222       }
223       base = c->specifier->base;
224       sign = 0;
225     }
226     break;
227   case 'p':
228     u = (uintptr_t)va_arg(s->ap, void *);
229     c->flags |= f_hash;
230     base = c->specifier->base;
231     sign = 0;
232     break;
233   }
234   /* default precision */
235   if(!(c->flags & f_precision))
236     c->precision = 1;
237   /* enforce sign */
238   if((c->flags & f_sign) && !sign) sign = '+';
239   /* compute the digits */
240   iszero = (u == 0);
241   dp = sizeof digits;
242   while(u) {
243     digits[--dp] = c->specifier->digits[u % base];
244     u /= base;
245   }
246   ndigits = sizeof digits - dp;
247   /* alternative form */
248   if(c->flags & f_hash) {
249     switch(base) {
250     case 8:
251       if((dp == sizeof digits || digits[dp] != '0')
252          && c->precision <= ndigits)
253         c->precision = ndigits + 1;
254       break;
255     }
256     if(!iszero && c->specifier->xform)
257       xform = strlen(c->specifier->xform);
258     else
259       xform = 0;
260   } else
261     xform = 0;
262   /* calculate number of 0s to add for precision */
263   if(ndigits < c->precision)
264     prec = c->precision - ndigits;
265   else
266     prec = 0;
267   /* bytes occupied by the sign */
268   if(sign)
269     sign_bytes = 1;
270   else
271     sign_bytes = 0;
272   /* XXX implement the ' ' flag */
273   /* calculate number of bytes of padding */
274   if(c->flags & f_width) {
275     if((pad = c->width - (ndigits + prec + xform + sign_bytes)) < 0)
276       pad = 0;
277   } else
278     pad = 0;
279   /* now we are ready to output.  Possibilities are:
280    * [space pad][sign][xform][0 prec]digits
281    * [sign][xform][0 pad][0 prec]digits
282    * [sign][xform][0 prec]digits[space pad]
283    *
284    * '-' beats '0'.
285    */
286   if(c->flags & f_left) {
287     if(pad && do_pad(s, ' ', pad) < 0) return -1;
288     if(sign && do_write(s, &sign, 1)) return -1;
289     if(xform && do_write(s, c->specifier->xform, xform)) return -1;
290     if(prec && do_pad(s, '0', prec) < 0) return -1;
291     if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
292   } else if(c->flags & f_zero) {
293     if(sign && do_write(s, &sign, 1)) return -1;
294     if(xform && do_write(s, c->specifier->xform, xform)) return -1;
295     if(pad && do_pad(s, '0', pad) < 0) return -1;
296     if(prec && do_pad(s, '0', prec) < 0) return -1;
297     if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
298   } else {
299     if(sign && do_write(s, &sign, 1)) return -1;
300     if(xform && do_write(s, c->specifier->xform, xform)) return -1;
301     if(prec && do_pad(s, '0', prec) < 0) return -1;
302     if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
303     if(pad && do_pad(s, ' ', pad) < 0) return -1;
304   }
305   return 0;
306 }
307
308 static int output_string(struct state *s, struct conversion *c) {
309   const char *str, *n;
310   int pad, len;
311
312   str = va_arg(s->ap, const char *);
313   if(c->flags & f_precision) {
314     if((n = memchr(str, 0, c->precision)))
315       len = n - str;
316     else
317       len = c->precision;
318   } else
319     len = strlen(str);
320   if(c->flags & f_width) {
321     if((pad = c->width - len) < 0)
322       pad = 0;
323   } else
324     pad = 0;
325   if(c->flags & f_left) {
326     if(pad && do_pad(s, ' ', pad) < 0) return -1;
327     if(do_write(s, str, len) < 0) return -1;
328   } else {
329     if(do_write(s, str, len) < 0) return -1;
330     if(pad && do_pad(s, ' ', pad) < 0) return -1;
331   }
332   return 0;
333   
334 }
335
336 static int output_char(struct state *s, struct conversion *c) {
337   int pad;
338   char ch;
339
340   ch = va_arg(s->ap, int);
341   if(c->flags & f_width) {
342     if((pad = c->width - 1) < 0)
343       pad = 0;
344   } else
345     pad = 0;
346   if(c->flags & f_left) {
347     if(pad && do_pad(s, ' ', pad) < 0) return -1;
348     if(do_write(s, &ch, 1) < 0) return -1;
349   } else {
350     if(do_write(s, &ch, 1) < 0) return -1;
351     if(pad && do_pad(s, ' ', pad) < 0) return -1;
352   }
353   return 0;
354 }
355
356 static int output_count(struct state *s, struct conversion *c) {
357   switch(c->length) {
358   case 0: *va_arg(s->ap, int *) = s->bytes; break;
359   case l_char: *va_arg(s->ap, signed char *) = s->bytes; break;
360   case l_short: *va_arg(s->ap, short *) = s->bytes; break;
361   case l_long: *va_arg(s->ap, long *) = s->bytes; break;
362   case l_longlong: *va_arg(s->ap, long_long *) = s->bytes; break;
363   case l_intmax_t: *va_arg(s->ap, intmax_t *) = s->bytes; break;
364   case l_size_t: *va_arg(s->ap, ssize_t *) = s->bytes; break;
365   case l_ptrdiff_t: *va_arg(s->ap, ptrdiff_t *) = s->bytes; break;
366   default: abort();
367   }
368   return 0;
369 }
370
371 /* table of conversion specifiers */
372 static const struct specifier specifiers[] = {
373   /* XXX don't support floating point conversions */
374   { '%', check_percent, output_percent,  0,  0,                  0    },
375   { 'X', check_integer, output_integer,  16, "0123456789ABCDEF", "0X" },
376   { 'c', check_string,  output_char,     0,  0,                  0    },
377   { 'd', check_integer, output_integer, -10, "0123456789",       0    },
378   { 'i', check_integer, output_integer, -10, "0123456789",       0    },
379   { 'n', check_integer, output_count,    0,  0,                  0    },
380   { 'o', check_integer, output_integer,  8,  "01234567",         0    },
381   { 'p', check_pointer, output_integer,  16, "0123456789abcdef", "0x" },
382   { 's', check_string,  output_string,   0,  0,                  0    },
383   { 'u', check_integer, output_integer,  10, "0123456789",       0    },
384   { 'x', check_integer, output_integer,  16, "0123456789abcdef", "0x" },
385 };
386
387 /* collect and check information about a conversion specification */
388 static int parse_conversion(struct conversion *c, const char *ptr) {
389   int n, ch, l, r, m;
390   const char *q, *start = ptr;
391     
392   memset(c, 0, sizeof *c);
393   /* flags */
394   while(*ptr && (q = strchr(flags, *ptr))) {
395     c->flags |= (1 << (q - flags));
396     ++ptr;
397   }
398   /* minimum field width */
399   if(*ptr >= '0' && *ptr <= '9') {
400     if((n = get_integer(&c->width, ptr)) < 0) return -1;
401     ptr += n;
402     c->flags |= f_width;
403   } else if(*ptr == '*') {
404     ++ptr;
405     c->width = -1;
406     c->flags |= f_width;
407   }
408   /* precision */
409   if(*ptr == '.') {
410     ++ptr;
411     if(*ptr >= '0' && *ptr <= '9') {
412       if((n = get_integer(&c->precision, ptr)) < 0) return -1;
413       ptr += n;
414     } else if(*ptr == '*') {
415       ++ptr;
416       c->precision = -1;
417     } else
418       return -1;
419     c->flags |= f_precision;
420   }
421   /* length modifier */
422   switch(ch = *ptr++) {
423   case 'h':
424     if((ch = *ptr++) == 'h') { c->length = l_char; ch = *ptr++; }
425     else c->length = l_short;
426     break;
427   case 'l':
428     if((ch = *ptr++) == 'l') { c->length = l_longlong; ch = *ptr++; }
429     else c->length = l_long;
430     break;
431   case 'q': c->length = l_longlong; ch = *ptr++; break;
432   case 'j': c->length = l_intmax_t; ch = *ptr++; break;
433   case 'z': c->length = l_size_t; ch = *ptr++; break;
434   case 't': c->length = l_ptrdiff_t; ch = *ptr++; break;
435   case 'L': c->length = l_longdouble; ch = *ptr++; break;
436   }
437   /* conversion specifier */
438   l = 0;
439   r = sizeof specifiers / sizeof *specifiers;
440   while(l <= r && (specifiers[m = (l + r) / 2].ch != ch))
441     if(ch < specifiers[m].ch) r = m - 1;
442     else l = m + 1;
443   if(specifiers[m].ch != ch) return -1;
444   if(specifiers[m].check(c)) return -1;
445   c->specifier = &specifiers[m];
446   return ptr - start;
447 }
448
449 /* ISO/IEC 9899:1999 7.19.6.1 */
450 /* http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html */
451
452 int byte_vsinkprintf(struct sink *output,
453                      const char *fmt,
454                      va_list ap) {
455   int n;
456   const char *ptr;
457   struct state s;
458   struct conversion c;
459
460   memset(&s, 0, sizeof s);
461   s.output = output;
462   va_copy(s.ap,ap);
463   while(*fmt) {
464     /* output text up to next conversion specification */
465     for(ptr = fmt; *fmt && *fmt != '%'; ++fmt)
466       ;
467     if((n = fmt - ptr))
468       if(do_write(&s, ptr, n) < 0) goto error;
469     if(!*fmt)
470       break;
471     ++fmt;
472     /* parse conversion */
473     if((n = parse_conversion(&c, fmt)) < 0) goto error;
474     fmt += n;
475     /* fill in width and precision */
476     if((c.flags & f_width) && c.width == -1)
477       if((c.width = va_arg(s.ap, int)) < 0) {
478         c.width = -c.width;
479         c.flags |= f_left;
480       }
481     if((c.flags & f_precision) && c.precision == -1)
482       if((c.precision = va_arg(s.ap, int)) < 0)
483         c.flags ^= f_precision;
484     /* generate the output */
485     if(c.specifier->output(&s, &c) < 0) goto error;
486   }
487   va_end(s.ap);
488   return s.bytes;
489 error:
490   va_end(s.ap);
491   return -1;
492 }
493
494 /*
495 Local Variables:
496 c-basic-offset:2
497 comment-column:40
498 End:
499 */