]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
printf-hook-builtin: Add some preliminary floating point support
authorMartin Willi <martin@revosec.ch>
Fri, 11 Oct 2013 08:55:05 +0000 (10:55 +0200)
committerMartin Willi <martin@revosec.ch>
Fri, 11 Oct 2013 09:06:09 +0000 (11:06 +0200)
This minimalistic implementation has no aspiration for completeness or
accuracy, and just provides what we need.

src/libstrongswan/tests/test_printf.c
src/libstrongswan/utils/printf_hook/printf_hook_builtin.c

index 60655de8aa4a7abf501096620081e17e6cdb7de1..f822e5f4b09041f250f5a3819028919480153cc1 100644 (file)
@@ -91,6 +91,31 @@ START_TEST(test_printf_hex)
 }
 END_TEST
 
+START_TEST(test_printf_float)
+{
+       verify("0.000000", "%f", 0.0);
+       verify("1.000000", "%f", 1.0);
+       verify("12345.1", "%.1f", 12345.123);
+       verify("1", "%.0f", 1.0);
+       verify("1.4", "%.1f", 1.456789);
+       verify("1.34", "%.2f", 1.3456789);
+       verify("1.234", "%.3f", 1.23456789);
+       verify("1.1234", "%.4f", 1.123456789);
+
+       verify("-1.000000", "%f", -1.0);
+       verify("-12345.1", "%.1f", -12345.123);
+       verify("-1", "%.0f", -1.0);
+       verify("-1.4", "%.1f", -1.456789);
+       verify("-1.34", "%.2f", -1.3456789);
+       verify("-1.234", "%.3f", -1.23456789);
+       verify("-1.1234", "%.4f", -1.123456789);
+
+       verify("  1.2", "%5.1f", 1.234);
+       verify("001.2", "%05.1f", 1.234);
+       verify("1.2  ", "%-5.1f", 1.234);
+}
+END_TEST
+
 Suite *printf_suite_create()
 {
        Suite *s;
@@ -118,5 +143,9 @@ Suite *printf_suite_create()
        tcase_add_test(tc, test_printf_hex);
        suite_add_tcase(s, tc);
 
+       tc = tcase_create("float");
+       tcase_add_test(tc, test_printf_float);
+       suite_add_tcase(s, tc);
+
        return s;
 }
index 1c266d4549fe8598b9a4aad2125655aa71da2652..6ba4841cd248b8c450d883dde600dba3c72d0a26 100644 (file)
@@ -47,6 +47,7 @@
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
+#include <math.h>
 
 #define PRINTF_BUF_LEN 8192
 #define ARGS_MAX 3
@@ -222,14 +223,15 @@ typedef enum {
 
 #define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
 
+static const char lcdigits[] = "0123456789abcdef";
+static const char ucdigits[] = "0123456789ABCDEF";
+
 /**
  * Write an integer argument to q, using flags, base, width and precision
  */
 static size_t format_int(char *q, size_t n, uintmax_t val, bpf_flag_t flags,
                                                 int base, int width, int prec)
 {
-       static const char lcdigits[] = "0123456789abcdef";
-       static const char ucdigits[] = "0123456789ABCDEF";
        char *qq;
        size_t o = 0, oo;
        const char *digits;
@@ -393,6 +395,155 @@ static size_t format_int(char *q, size_t n, uintmax_t val, bpf_flag_t flags,
        return o;
 }
 
+/**
+ * Write an double argument to q, using flags, base, width and precision
+ */
+static size_t format_double(char *q, size_t n, double val, bpf_flag_t flags,
+                                                       int base, int width, int prec)
+{
+       char *qq;
+       size_t o = 0, oo;
+       const char *digits;
+       uintmax_t tmpval;
+       int minus = 0;
+       int ndigits = 0, nchars;
+
+       /* Select type of digits */
+       digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
+
+       if (prec < 0)
+       {
+               /* default precision */
+               prec = 6;
+       }
+       if (val < 0)
+       {
+               minus = 1;
+       }
+
+       tmpval = (uintmax_t)fabs(val);
+       while (tmpval)
+       {
+               tmpval /= base;
+               ndigits++;
+       }
+       if (val == 0)
+       {
+               ndigits++;
+       }
+
+       /* Now compute the number of nondigits */
+       nchars = ndigits;
+
+       if (prec)
+       {
+               /* Space for decimal-point and digits after that */
+               nchars += prec + 1;
+       }
+       if (minus || (flags & (FL_PLUS | FL_SPACE)))
+       {
+               /* Need space for sign */
+               nchars++;
+       }
+       if ((flags & FL_HASH) && base == 16)
+       {
+               /* Add 0x for hex */
+               nchars += 2;
+       }
+
+       /* Emit early space padding */
+       if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars)
+       {
+               while (width > nchars)
+               {
+                       EMIT(' ');
+                       width--;
+               }
+       }
+
+       /* Emit nondigits */
+       if (minus)
+       {
+               EMIT('-');
+       }
+       else if (flags & FL_PLUS)
+       {
+               EMIT('+');
+       }
+       else if (flags & FL_SPACE)
+       {
+               EMIT(' ');
+       }
+
+       if ((flags & FL_HASH) && base == 16)
+       {
+               EMIT('0');
+               EMIT((flags & FL_UPPER) ? 'X' : 'x');
+       }
+
+       /* Emit zero padding */
+       if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits)
+       {
+               while (width > nchars)
+               {
+                       EMIT('0');
+                       width--;
+               }
+       }
+
+       /* Generate the number.  This is done from right to left. */
+       /* Advance the pointer to end of number */
+       q += ndigits;
+       o += ndigits;
+       /* Temporary values */
+       qq = q;
+       oo = o;
+
+       tmpval = (uintmax_t)fabs(val);
+       while (ndigits > 0)
+       {
+               qq--;
+               oo--;
+               ndigits--;
+               if (oo < n)
+               {
+                       *qq = digits[tmpval % base];
+               }
+               tmpval /= base;
+       }
+
+       if (prec)
+       {
+               EMIT('.');
+
+               q += prec;
+               o += prec;
+               qq = q;
+               oo = o;
+
+               while (prec > 0)
+               {
+                       tmpval = (uintmax_t)(fabs(val) * pow(base, prec));
+                       qq--;
+                       oo--;
+                       prec--;
+                       if (oo < n)
+                       {
+                               *qq = digits[tmpval % base];
+                       }
+               }
+       }
+
+       /* Emit late space padding */
+       while ((flags & FL_MINUS) && width > nchars)
+       {
+               EMIT(' ');
+               width--;
+       }
+
+       return o;
+}
+
 int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
 {
        const char *p = format;
@@ -724,6 +875,47 @@ int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
                                                                }
                                                                break;
                                                        }
+                                                       case 'A':
+                                                       {
+                                                               base = 16;
+                                                               flags |= FL_UPPER;
+                                                               goto is_double;
+                                                       }
+                                                       case 'E':
+                                                       case 'G':
+                                                       {
+                                                               /* currently not supported, fall */
+                                                       }
+                                                       case 'F':
+                                                       {
+                                                               base = 10;
+                                                               flags |= FL_UPPER;
+                                                               goto is_double;
+                                                       }
+                                                       case 'a':
+                                                       {
+                                                               base = 16;
+                                                               goto is_double;
+                                                       }
+                                                       case 'e':
+                                                       case 'g':
+                                                       {
+                                                               /* currently not supported, fall */
+                                                       }
+                                                       case 'f':
+                                                       {
+                                                               base = 10;
+                                                               goto is_double;
+                                                       }
+                                                       is_double:
+                                                       {
+                                                               sz = format_double(q, (o < n) ? n - o : 0,
+                                                                                                  va_arg(ap, double),
+                                                                                                  flags, base, width, prec);
+                                                               q += sz;
+                                                               o += sz;
+                                                               break;
+                                                       }
                                                        case 'n':
                                                        {
                                                                /* Output the number of characters written */