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