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