[PATCH 3/7] os: add support for %f to pnprintf

Peter Hutterer peter.hutterer at who-t.net
Wed Jan 16 23:20:00 PST 2013


This is the lazy man's %f support. Print the decimal part of the number,
then append a decimal point, then print the first two digits of the
fractional part. So %f in sigsafe printing is really %.2f.

No boundary checks in place here.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 include/misc.h        |  1 +
 os/log.c              | 11 +++++++++-
 os/utils.c            | 32 +++++++++++++++++++++++++++++
 test/signal-logging.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/include/misc.h b/include/misc.h
index 3487176..0c67f11 100644
--- a/include/misc.h
+++ b/include/misc.h
@@ -250,6 +250,7 @@ extern char **xstrtokenize(const char *str, const char *separators);
 extern void FormatInt64(int64_t num, char *string);
 extern void FormatUInt64(uint64_t num, char *string);
 extern void FormatUInt64Hex(uint64_t num, char *string);
+extern void FormatDouble(double dbl, char *string);
 
 /**
  * Compare the two version numbers comprising of major.minor.
diff --git a/os/log.c b/os/log.c
index 2139064..7b5c9ee 100644
--- a/os/log.c
+++ b/os/log.c
@@ -351,7 +351,16 @@ pnprintf(char *string, size_t size, const char *f, va_list args)
             for (i = 0; i < p_len && s_idx < size - 1; i++)
                 string[s_idx++] = number[i];
             break;
-
+        case 'f':
+            {
+                double d = va_arg(args, double);
+                FormatDouble(d, number);
+                p_len = strlen_sigsafe(number);
+
+                for (i = 0; i < p_len && s_idx < size - 1; i++)
+                    string[s_idx++] = number[i];
+            }
+            break;
         default:
             va_arg(args, char*);
             string[s_idx++] = '%';
diff --git a/os/utils.c b/os/utils.c
index e396ba4..3ba6499 100644
--- a/os/utils.c
+++ b/os/utils.c
@@ -1990,6 +1990,38 @@ FormatUInt64(uint64_t num, char *string)
     string[len] = '\0';
 }
 
+/**
+ * Format a double number as %.2f.
+ */
+void
+FormatDouble(double dbl, char *string)
+{
+    int slen = 0;
+    uint64_t frac;
+
+    frac = (dbl > 0 ? dbl : -dbl) * 100.0;
+    frac %= 100;
+
+    /* write decimal part to string */
+    if (dbl < 0 && dbl > -1)
+        string[slen++] = '-';
+    FormatInt64((int64_t)dbl, &string[slen]);
+
+    while(string[slen] != '\0')
+        slen++;
+
+    /* append fractional part, but only if we have enough characters. We
+     * expect string to be 21 chars (incl trailing \0) */
+    if (slen <= 17) {
+        string[slen++] = '.';
+        if (frac < 10)
+            string[slen++] = '0';
+
+        FormatUInt64(frac, &string[slen]);
+    }
+}
+
+
 /* Format a number into a hexadecimal string in a signal safe manner. The string
  * should be at least 17 characters in order to handle all uint64_t values. */
 void
diff --git a/test/signal-logging.c b/test/signal-logging.c
index 127a28f..1ef17af 100644
--- a/test/signal-logging.c
+++ b/test/signal-logging.c
@@ -41,6 +41,11 @@ struct signed_number_format_test {
     char string[21];
 };
 
+struct float_number_format_test {
+    double number;
+    char string[21];
+};
+
 static Bool
 check_signed_number_format_test(long int number)
 {
@@ -59,6 +64,25 @@ check_signed_number_format_test(long int number)
 }
 
 static Bool
+check_float_format_test(double number)
+{
+    char string[21];
+    char expected[21];
+
+    /* we currently always print float as .2f */
+    sprintf(expected, "%.2f", number);
+
+    FormatDouble(number, string);
+    if(strncmp(string, expected, 21) != 0) {
+        fprintf(stderr, "Failed to convert %f to string (%s vs %s)\n",
+                number, expected, string);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static Bool
 check_number_format_test(long unsigned int number)
 {
     char string[21];
@@ -84,6 +108,11 @@ check_number_format_test(long unsigned int number)
     return TRUE;
 }
 
+/* FIXME: max range stuff */
+double float_tests[] = { 0, 5, 0.1, 0.01, 5.2342, 10.2301,
+                         -1, -2.00, -0.6023, -1203.30
+                        };
+
 static void
 number_formatting(void)
 {
@@ -116,6 +145,9 @@ number_formatting(void)
 
     for (i = 0; i < sizeof(unsigned_tests) / sizeof(signed_tests[0]); i++)
         assert(check_signed_number_format_test(signed_tests[i]));
+
+    for (i = 0; i < sizeof(float_tests) / sizeof(float_tests[0]); i++)
+        assert(check_float_format_test(float_tests[i]));
 }
 
 #pragma GCC diagnostic ignored "-Wformat-security"
@@ -232,6 +264,30 @@ static void logging_format(void)
         ptr <<= 1;
     } while(ptr);
 
+
+    for (i = 0; i < sizeof(float_tests)/sizeof(float_tests[0]); i++) {
+        double d = float_tests[i];
+        char expected[30];
+        sprintf(expected, "(EE) %.2f\n", d);
+        LogMessageVerbSigSafe(X_ERROR, -1, "%f\n", d);
+        read_log_msg(logmsg);
+        assert(strcmp(logmsg, expected) == 0);
+
+        /* test for length modifiers, we just ignore them atm */
+        LogMessageVerbSigSafe(X_ERROR, -1, "%.3f\n", d);
+        read_log_msg(logmsg);
+        assert(strcmp(logmsg, expected) == 0);
+
+        LogMessageVerbSigSafe(X_ERROR, -1, "%3f\n", d);
+        read_log_msg(logmsg);
+        assert(strcmp(logmsg, expected) == 0);
+
+        LogMessageVerbSigSafe(X_ERROR, -1, "%.0f\n", d);
+        read_log_msg(logmsg);
+        assert(strcmp(logmsg, expected) == 0);
+    }
+
+
     LogClose(EXIT_NO_ERROR);
     unlink(log_file_path);
 
-- 
1.8.1



More information about the xorg-devel mailing list