chiark / gitweb /
New refresh_min option to bound below the web interface refresh
[disorder] / lib / printf.c
index aafe7ce7daeeb11619925ff8b19c49a2eb7862b4..15cd8ad968463f4201913b87c7787acbedf44d91 100644 (file)
@@ -1,39 +1,41 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2004 Richard Kettlewell
+ * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
  *
- * This program is free software; you can redistribute it and/or modify
+ * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/printf.c
+ * @brief UTF-8 *printf workalike (core)
  */
 
 #define NO_MEMORY_ALLOCATION
 /* because byte_snprintf used from log.c */
 
-#include <config.h>
-#include "types.h"
+#include "common.h"
 
-#include <stdio.h>
 #include <stdarg.h>
-#include <string.h>
 #include <errno.h>
-#include <stdlib.h>
 #include <stddef.h>
 
 #include "printf.h"
 #include "sink.h"
+#include "vacopy.h"
 
+/** @brief Flags from a converstion specification
+ *
+ * Order significant!
+ */
 enum flags {
   f_thousands = 1,
   f_left = 2,
@@ -45,6 +47,7 @@ enum flags {
   f_precision = 512
 };
 
+/** @brief Possible lengths of a conversion specification */
 enum lengths {
   l_char = 1,
   l_short,
@@ -58,29 +61,65 @@ enum lengths {
 
 struct conversion;
 
+/** @brief Formatter state */
 struct state {
+  /** @brief Output stream */
   struct sink *output;
+
+  /** @brief Number of bytes written */
   int bytes;
-  va_list *ap;
+
+  /** @brief Argument list */
+  va_list ap;
 };
 
+/** @brief Definition of a conversion specifier */
 struct specifier {
+  /** @brief Defining character ('d', 's' etc) */
   int ch;
+
+  /** @brief Consistency check
+   * @param c Conversion being processed
+   * @return 0 if OK, -1 on error
+   */
   int (*check)(const struct conversion *c);
+
+  /** @brief Generate output
+   * @param s Formatter state
+   * @param c Conversion being processed
+   * @return 0 on success, -1 on error
+   */
   int (*output)(struct state *s, struct conversion *c);
+
+  /** @brief Number base */
   int base;
+
+  /** @brief Digit set */
   const char *digits;
+
+  /** @brief Alternative-form prefix */
   const char *xform;
 };
 
+/** @brief One conversion specified as it's handled */
 struct conversion {
+  /** @brief Flags in this conversion */
   unsigned flags;
+
+  /** @brief Field width (if @ref f_width) */
   int width;
+
+  /** @brief Precision (if @ref f_precision) */
   int precision;
+
+  /** @brief Length modifier or 0 */
   int length;
+
+  /** @brief Specifier used */
   const struct specifier *specifier;
 };
 
+/** @brief Flag characters (order significant!) */
 static const char flags[] = "'-+ #0";
 
 /* write @nbytes@ to the output.  Return -1 on error, 0 on success.
@@ -94,7 +133,8 @@ static int do_write(struct state *s,
 #endif
     return -1;
   }
-  if(s->output->write(s->output, buffer, nbytes) < 0) return -1;
+  if(s->output->write(s->output, buffer, nbytes) < 0)
+    return -1;
   s->bytes += nbytes;
   return 0;
 }
@@ -112,9 +152,11 @@ static int do_pad(struct state *s, int ch, unsigned n) {
   t = n / 32;
   n %= 32;
   while(t-- > 0)
-    if(do_write(s, padding, 32) < 0) return -1;
+    if(do_write(s, padding, 32) < 0)
+      return -1;
   if(n > 0)
-    if(do_write(s, padding, n) < 0) return -1;
+    if(do_write(s, padding, n) < 0)
+      return -1;
   return 0;
 }
 
@@ -127,7 +169,8 @@ static int get_integer(int *intp, const char *ptr) {
 
   errno = 0;
   n = strtol(ptr, &e, 10);
-  if(errno || n > INT_MAX || n < INT_MIN || e == ptr) return -1;
+  if(errno || n > INT_MAX || n < INT_MIN || e == ptr)
+    return -1;
   *intp = n;
   return e - ptr;
 }
@@ -144,6 +187,7 @@ static int check_integer(const struct conversion *c) {
   case l_intmax_t:
   case l_size_t:
   case l_longdouble:
+  case l_ptrdiff_t:
     return 0;
   default:
     return -1;
@@ -161,12 +205,14 @@ static int check_string(const struct conversion *c) {
 }
 
 static int check_pointer(const struct conversion *c) {
-  if(c->length) return -1;
+  if(c->length)
+    return -1;
   return 0;
 }
 
 static int check_percent(const struct conversion *c) {
-  if(c->flags || c->width || c->precision || c->length) return -1;
+  if(c->flags || c->width || c->precision || c->length)
+    return -1;
   return 0;
 }
 
@@ -188,14 +234,14 @@ static int output_integer(struct state *s, struct conversion *c) {
   default:
     if(c->specifier->base < 0) {
       switch(c->length) {
-      case 0: l = va_arg(*s->ap, int); break;
-      case l_char: l = (signed char)va_arg(*s->ap, int); break;
-      case l_short: l = (short)va_arg(*s->ap, int); break;
-      case l_long: l = va_arg(*s->ap, long); break;
-      case l_longlong: l = va_arg(*s->ap, long_long); break;
-      case l_intmax_t: l = va_arg(*s->ap, intmax_t); break;
-      case l_size_t: l = va_arg(*s->ap, ssize_t); break;
-      case l_ptrdiff_t: l = va_arg(*s->ap, ptrdiff_t); break;
+      case 0: l = va_arg(s->ap, int); break;
+      case l_char: l = (signed char)va_arg(s->ap, int); break;
+      case l_short: l = (short)va_arg(s->ap, int); break;
+      case l_long: l = va_arg(s->ap, long); break;
+      case l_longlong: l = va_arg(s->ap, long_long); break;
+      case l_intmax_t: l = va_arg(s->ap, intmax_t); break;
+      case l_size_t: l = va_arg(s->ap, ssize_t); break;
+      case l_ptrdiff_t: l = va_arg(s->ap, ptrdiff_t); break;
       default: abort();
       }
       base = -c->specifier->base;
@@ -208,14 +254,14 @@ static int output_integer(struct state *s, struct conversion *c) {
       }
     } else {
       switch(c->length) {
-      case 0: u = va_arg(*s->ap, unsigned int); break;
-      case l_char: u = (unsigned char)va_arg(*s->ap, unsigned int); break;
-      case l_short: u = (unsigned short)va_arg(*s->ap, unsigned int); break;
-      case l_long: u = va_arg(*s->ap, unsigned long); break;
-      case l_longlong: u = va_arg(*s->ap, u_long_long); break;
-      case l_intmax_t: u = va_arg(*s->ap, uintmax_t); break;
-      case l_size_t: u = va_arg(*s->ap, size_t); break;
-      case l_ptrdiff_t: u = va_arg(*s->ap, ptrdiff_t); break;
+      case 0: u = va_arg(s->ap, unsigned int); break;
+      case l_char: u = (unsigned char)va_arg(s->ap, unsigned int); break;
+      case l_short: u = (unsigned short)va_arg(s->ap, unsigned int); break;
+      case l_long: u = va_arg(s->ap, unsigned long); break;
+      case l_longlong: u = va_arg(s->ap, u_long_long); break;
+      case l_intmax_t: u = va_arg(s->ap, uintmax_t); break;
+      case l_size_t: u = va_arg(s->ap, size_t); break;
+      case l_ptrdiff_t: u = va_arg(s->ap, ptrdiff_t); break;
       default: abort();
       }
       base = c->specifier->base;
@@ -223,7 +269,7 @@ static int output_integer(struct state *s, struct conversion *c) {
     }
     break;
   case 'p':
-    u = (uintptr_t)va_arg(*s->ap, void *);
+    u = (uintptr_t)va_arg(s->ap, void *);
     c->flags |= f_hash;
     base = c->specifier->base;
     sign = 0;
@@ -233,7 +279,8 @@ static int output_integer(struct state *s, struct conversion *c) {
   if(!(c->flags & f_precision))
     c->precision = 1;
   /* enforce sign */
-  if((c->flags & f_sign) && !sign) sign = '+';
+  if((c->flags & f_sign) && !sign)
+    sign = '+';
   /* compute the digits */
   iszero = (u == 0);
   dp = sizeof digits;
@@ -282,23 +329,38 @@ static int output_integer(struct state *s, struct conversion *c) {
    * '-' beats '0'.
    */
   if(c->flags & f_left) {
-    if(pad && do_pad(s, ' ', pad) < 0) return -1;
-    if(sign && do_write(s, &sign, 1)) return -1;
-    if(xform && do_write(s, c->specifier->xform, xform)) return -1;
-    if(prec && do_pad(s, '0', prec) < 0) return -1;
-    if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
+    if(sign && do_write(s, &sign, 1))
+      return -1;
+    if(xform && do_write(s, c->specifier->xform, xform))
+      return -1;
+    if(prec && do_pad(s, '0', prec) < 0)
+      return -1;
+    if(ndigits && do_write(s, digits + dp, ndigits))
+      return -1;
+    if(pad && do_pad(s, ' ', pad) < 0)
+      return -1;
   } else if(c->flags & f_zero) {
-    if(sign && do_write(s, &sign, 1)) return -1;
-    if(xform && do_write(s, c->specifier->xform, xform)) return -1;
-    if(pad && do_pad(s, '0', pad) < 0) return -1;
-    if(prec && do_pad(s, '0', prec) < 0) return -1;
-    if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
+    if(sign && do_write(s, &sign, 1))
+      return -1;
+    if(xform && do_write(s, c->specifier->xform, xform))
+      return -1;
+    if(pad && do_pad(s, '0', pad) < 0)
+      return -1;
+    if(prec && do_pad(s, '0', prec) < 0)
+      return -1;
+    if(ndigits && do_write(s, digits + dp, ndigits))
+      return -1;
   } else {
-    if(sign && do_write(s, &sign, 1)) return -1;
-    if(xform && do_write(s, c->specifier->xform, xform)) return -1;
-    if(prec && do_pad(s, '0', prec) < 0) return -1;
-    if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
-    if(pad && do_pad(s, ' ', pad) < 0) return -1;
+    if(pad && do_pad(s, ' ', pad) < 0)
+      return -1;
+    if(sign && do_write(s, &sign, 1))
+      return -1;
+    if(xform && do_write(s, c->specifier->xform, xform))
+      return -1;
+    if(prec && do_pad(s, '0', prec) < 0)
+      return -1;
+    if(ndigits && do_write(s, digits + dp, ndigits))
+      return -1;
   }
   return 0;
 }
@@ -307,7 +369,7 @@ static int output_string(struct state *s, struct conversion *c) {
   const char *str, *n;
   int pad, len;
 
-  str = va_arg(*s->ap, const char *);
+  str = va_arg(s->ap, const char *);
   if(c->flags & f_precision) {
     if((n = memchr(str, 0, c->precision)))
       len = n - str;
@@ -321,11 +383,15 @@ static int output_string(struct state *s, struct conversion *c) {
   } else
     pad = 0;
   if(c->flags & f_left) {
-    if(pad && do_pad(s, ' ', pad) < 0) return -1;
-    if(do_write(s, str, len) < 0) return -1;
+    if(do_write(s, str, len) < 0)
+      return -1;
+    if(pad && do_pad(s, ' ', pad) < 0)
+      return -1;
   } else {
-    if(do_write(s, str, len) < 0) return -1;
-    if(pad && do_pad(s, ' ', pad) < 0) return -1;
+    if(pad && do_pad(s, ' ', pad) < 0)
+      return -1;
+    if(do_write(s, str, len) < 0)
+      return -1;
   }
   return 0;
   
@@ -335,32 +401,36 @@ static int output_char(struct state *s, struct conversion *c) {
   int pad;
   char ch;
 
-  ch = va_arg(*s->ap, int);
+  ch = va_arg(s->ap, int);
   if(c->flags & f_width) {
     if((pad = c->width - 1) < 0)
       pad = 0;
   } else
     pad = 0;
   if(c->flags & f_left) {
-    if(pad && do_pad(s, ' ', pad) < 0) return -1;
-    if(do_write(s, &ch, 1) < 0) return -1;
+    if(do_write(s, &ch, 1) < 0)
+      return -1;
+    if(pad && do_pad(s, ' ', pad) < 0)
+      return -1;
   } else {
-    if(do_write(s, &ch, 1) < 0) return -1;
-    if(pad && do_pad(s, ' ', pad) < 0) return -1;
+    if(pad && do_pad(s, ' ', pad) < 0)
+      return -1;
+    if(do_write(s, &ch, 1) < 0)
+      return -1;
   }
   return 0;
 }
 
 static int output_count(struct state *s, struct conversion *c) {
   switch(c->length) {
-  case 0: *va_arg(*s->ap, int *) = s->bytes; break;
-  case l_char: *va_arg(*s->ap, signed char *) = s->bytes; break;
-  case l_short: *va_arg(*s->ap, short *) = s->bytes; break;
-  case l_long: *va_arg(*s->ap, long *) = s->bytes; break;
-  case l_longlong: *va_arg(*s->ap, long_long *) = s->bytes; break;
-  case l_intmax_t: *va_arg(*s->ap, intmax_t *) = s->bytes; break;
-  case l_size_t: *va_arg(*s->ap, ssize_t *) = s->bytes; break;
-  case l_ptrdiff_t: *va_arg(*s->ap, ptrdiff_t *) = s->bytes; break;
+  case 0: *va_arg(s->ap, int *) = s->bytes; break;
+  case l_char: *va_arg(s->ap, signed char *) = s->bytes; break;
+  case l_short: *va_arg(s->ap, short *) = s->bytes; break;
+  case l_long: *va_arg(s->ap, long *) = s->bytes; break;
+  case l_longlong: *va_arg(s->ap, long_long *) = s->bytes; break;
+  case l_intmax_t: *va_arg(s->ap, intmax_t *) = s->bytes; break;
+  case l_size_t: *va_arg(s->ap, ssize_t *) = s->bytes; break;
+  case l_ptrdiff_t: *va_arg(s->ap, ptrdiff_t *) = s->bytes; break;
   default: abort();
   }
   return 0;
@@ -395,7 +465,8 @@ static int parse_conversion(struct conversion *c, const char *ptr) {
   }
   /* minimum field width */
   if(*ptr >= '0' && *ptr <= '9') {
-    if((n = get_integer(&c->width, ptr)) < 0) return -1;
+    if((n = get_integer(&c->width, ptr)) < 0)
+      return -1;
     ptr += n;
     c->flags |= f_width;
   } else if(*ptr == '*') {
@@ -407,24 +478,33 @@ static int parse_conversion(struct conversion *c, const char *ptr) {
   if(*ptr == '.') {
     ++ptr;
     if(*ptr >= '0' && *ptr <= '9') {
-      if((n = get_integer(&c->precision, ptr)) < 0) return -1;
+      if((n = get_integer(&c->precision, ptr)) < 0)
+       return -1;
       ptr += n;
     } else if(*ptr == '*') {
       ++ptr;
       c->precision = -1;
     } else
-      return -1;
+      c->precision = 0;
     c->flags |= f_precision;
   }
   /* length modifier */
   switch(ch = *ptr++) {
   case 'h':
-    if((ch = *ptr++) == 'h') { c->length = l_char; ch = *ptr++; }
-    else c->length = l_short;
+    if((ch = *ptr++) == 'h') {
+      c->length = l_char;
+      ch = *ptr++;
+    }
+    else
+      c->length = l_short;
     break;
   case 'l':
-    if((ch = *ptr++) == 'l') { c->length = l_longlong; ch = *ptr++; }
-    else c->length = l_long;
+    if((ch = *ptr++) == 'l') {
+      c->length = l_longlong;
+      ch = *ptr++;
+    }
+    else
+      c->length = l_long;
     break;
   case 'q': c->length = l_longlong; ch = *ptr++; break;
   case 'j': c->length = l_intmax_t; ch = *ptr++; break;
@@ -436,10 +516,14 @@ static int parse_conversion(struct conversion *c, const char *ptr) {
   l = 0;
   r = sizeof specifiers / sizeof *specifiers;
   while(l <= r && (specifiers[m = (l + r) / 2].ch != ch))
-    if(ch < specifiers[m].ch) r = m - 1;
-    else l = m + 1;
-  if(specifiers[m].ch != ch) return -1;
-  if(specifiers[m].check(c)) return -1;
+    if(ch < specifiers[m].ch)
+      r = m - 1;
+    else
+      l = m + 1;
+  if(specifiers[m].ch != ch)
+    return -1;
+  if(specifiers[m].check(c))
+    return -1;
   c->specifier = &specifiers[m];
   return ptr - start;
 }
@@ -457,32 +541,49 @@ int byte_vsinkprintf(struct sink *output,
 
   memset(&s, 0, sizeof s);
   s.output = output;
-  s.ap = &ap;
+  va_copy(s.ap,ap);
   while(*fmt) {
     /* output text up to next conversion specification */
     for(ptr = fmt; *fmt && *fmt != '%'; ++fmt)
       ;
     if((n = fmt - ptr))
-      if(do_write(&s, ptr, n) < 0) return -1;
+      if(do_write(&s, ptr, n) < 0)
+       goto error;
     if(!*fmt)
       break;
     ++fmt;
     /* parse conversion */
-    if((n = parse_conversion(&c, fmt)) < 0) return -1;
+    if((n = parse_conversion(&c, fmt)) < 0)
+      goto error;
     fmt += n;
     /* fill in width and precision */
     if((c.flags & f_width) && c.width == -1)
-      if((c.width = va_arg(*s.ap, int)) < 0) {
+      if((c.width = va_arg(s.ap, int)) < 0) {
        c.width = -c.width;
        c.flags |= f_left;
       }
     if((c.flags & f_precision) && c.precision == -1)
-      if((c.precision = va_arg(*s.ap, int)) < 0)
+      if((c.precision = va_arg(s.ap, int)) < 0)
        c.flags ^= f_precision;
     /* generate the output */
-    if(c.specifier->output(&s, &c) < 0) return -1;
+    if(c.specifier->output(&s, &c) < 0)
+      goto error;
   }
+  va_end(s.ap);
   return s.bytes;
+error:
+  va_end(s.ap);
+  return -1;
+}
+
+int byte_sinkprintf(struct sink *output, const char *fmt, ...) {
+  int n;
+  va_list ap;
+
+  va_start(ap, fmt);
+  n = byte_vsinkprintf(output, fmt, ap);
+  va_end(ap);
+  return n;
 }
 
 /*
@@ -491,4 +592,3 @@ c-basic-offset:2
 comment-column:40
 End:
 */
-/* arch-tag:e6ce806ce060f1d992eed14d9e5f0a6f */