chiark / gitweb /
protogen: The bulk of the eclient code generation.
[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 /** @file lib/printf.c
19  * @brief UTF-8 *printf workalike (core)
20  */
21
22 #define NO_MEMORY_ALLOCATION
23 /* because byte_snprintf used from log.c */
24
25 #include "common.h"
26
27 #include <stdarg.h>
28 #include <errno.h>
29 #include <stddef.h>
30
31 #include "printf.h"
32 #include "sink.h"
33 #include "vacopy.h"
34
35 /** @brief Flags from a converstion specification
36  *
37  * Order significant!
38  */
39 enum 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
50 /** @brief Possible lengths of a conversion specification */
51 enum 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
62 struct conversion;
63
64 /** @brief Formatter state */
65 struct state {
66   /** @brief Output stream */
67   struct sink *output;
68
69   /** @brief Number of bytes written */
70   int bytes;
71
72   /** @brief Argument list */
73   va_list ap;
74 };
75
76 /** @brief Definition of a conversion specifier */
77 struct specifier {
78   /** @brief Defining character ('d', 's' etc) */
79   int ch;
80
81   /** @brief Consistency check
82    * @param c Conversion being processed
83    * @return 0 if OK, -1 on error
84    */
85   int (*check)(const struct conversion *c);
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    */
92   int (*output)(struct state *s, struct conversion *c);
93
94   /** @brief Number base */
95   int base;
96
97   /** @brief Digit set */
98   const char *digits;
99
100   /** @brief Alternative-form prefix */
101   const char *xform;
102 };
103
104 /** @brief One conversion specified as it's handled */
105 struct conversion {
106   /** @brief Flags in this conversion */
107   unsigned flags;
108
109   /** @brief Field width (if @ref f_width) */
110   int width;
111
112   /** @brief Precision (if @ref f_precision) */
113   int precision;
114
115   /** @brief Length modifier or 0 */
116   int length;
117
118   /** @brief Specifier used */
119   const struct specifier *specifier;
120 };
121
122 /** @brief Flag characters (order significant!) */
123 static 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. */
127 static 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   }
136   if(s->output->write(s->output, buffer, nbytes) < 0)
137     return -1;
138   s->bytes += nbytes;
139   return 0;
140 }
141
142 /* write character @ch@ @n@ times, reasonably efficiently */
143 static 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)
155     if(do_write(s, padding, 32) < 0)
156       return -1;
157   if(n > 0)
158     if(do_write(s, padding, n) < 0)
159       return -1;
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) */
166 static int get_integer(int *intp, const char *ptr) {
167   long n;
168   char *e;
169
170   errno = 0;
171   n = strtol(ptr, &e, 10);
172   if(errno || n > INT_MAX || n < INT_MIN || e == ptr)
173     return -1;
174   *intp = n;
175   return e - ptr;
176 }
177
178 /* consistency checks for various conversion specifications */
179
180 static 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:
190   case l_ptrdiff_t:
191     return 0;
192   default:
193     return -1;
194   }
195 }
196
197 static 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
207 static int check_pointer(const struct conversion *c) {
208   if(c->length)
209     return -1;
210   return 0;
211 }
212
213 static int check_percent(const struct conversion *c) {
214   if(c->flags || c->width || c->precision || c->length)
215     return -1;
216   return 0;
217 }
218
219 /* output functions for various conversion specifications */
220
221 static int output_percent(struct state *s,
222                           struct conversion attribute((unused)) *c) {
223   return do_write(s, "%", 1);
224 }
225
226 static 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) {
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;
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) {
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;
265       default: abort();
266       }
267       base = c->specifier->base;
268       sign = 0;
269     }
270     break;
271   case 'p':
272     u = (uintptr_t)va_arg(s->ap, void *);
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 */
282   if((c->flags & f_sign) && !sign)
283     sign = '+';
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) {
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;
342   } else if(c->flags & f_zero) {
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;
353   } else {
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;
364   }
365   return 0;
366 }
367
368 static int output_string(struct state *s, struct conversion *c) {
369   const char *str, *n;
370   int pad, len;
371
372   str = va_arg(s->ap, const char *);
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) {
386     if(do_write(s, str, len) < 0)
387       return -1;
388     if(pad && do_pad(s, ' ', pad) < 0)
389       return -1;
390   } else {
391     if(pad && do_pad(s, ' ', pad) < 0)
392       return -1;
393     if(do_write(s, str, len) < 0)
394       return -1;
395   }
396   return 0;
397   
398 }
399
400 static int output_char(struct state *s, struct conversion *c) {
401   int pad;
402   char ch;
403
404   ch = va_arg(s->ap, int);
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) {
411     if(do_write(s, &ch, 1) < 0)
412       return -1;
413     if(pad && do_pad(s, ' ', pad) < 0)
414       return -1;
415   } else {
416     if(pad && do_pad(s, ' ', pad) < 0)
417       return -1;
418     if(do_write(s, &ch, 1) < 0)
419       return -1;
420   }
421   return 0;
422 }
423
424 static int output_count(struct state *s, struct conversion *c) {
425   switch(c->length) {
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;
434   default: abort();
435   }
436   return 0;
437 }
438
439 /* table of conversion specifiers */
440 static 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 */
456 static 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') {
468     if((n = get_integer(&c->width, ptr)) < 0)
469       return -1;
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') {
481       if((n = get_integer(&c->precision, ptr)) < 0)
482         return -1;
483       ptr += n;
484     } else if(*ptr == '*') {
485       ++ptr;
486       c->precision = -1;
487     } else
488       c->precision = 0;
489     c->flags |= f_precision;
490   }
491   /* length modifier */
492   switch(ch = *ptr++) {
493   case 'h':
494     if((ch = *ptr++) == 'h') {
495       c->length = l_char;
496       ch = *ptr++;
497     }
498     else
499       c->length = l_short;
500     break;
501   case 'l':
502     if((ch = *ptr++) == 'l') {
503       c->length = l_longlong;
504       ch = *ptr++;
505     }
506     else
507       c->length = l_long;
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))
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;
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
534 int 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;
544   va_copy(s.ap,ap);
545   while(*fmt) {
546     /* output text up to next conversion specification */
547     for(ptr = fmt; *fmt && *fmt != '%'; ++fmt)
548       ;
549     if((n = fmt - ptr))
550       if(do_write(&s, ptr, n) < 0)
551         goto error;
552     if(!*fmt)
553       break;
554     ++fmt;
555     /* parse conversion */
556     if((n = parse_conversion(&c, fmt)) < 0)
557       goto error;
558     fmt += n;
559     /* fill in width and precision */
560     if((c.flags & f_width) && c.width == -1)
561       if((c.width = va_arg(s.ap, int)) < 0) {
562         c.width = -c.width;
563         c.flags |= f_left;
564       }
565     if((c.flags & f_precision) && c.precision == -1)
566       if((c.precision = va_arg(s.ap, int)) < 0)
567         c.flags ^= f_precision;
568     /* generate the output */
569     if(c.specifier->output(&s, &c) < 0)
570       goto error;
571   }
572   va_end(s.ap);
573   return s.bytes;
574 error:
575   va_end(s.ap);
576   return -1;
577 }
578
579 int 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
589 /*
590 Local Variables:
591 c-basic-offset:2
592 comment-column:40
593 End:
594 */