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