Docment how CLABELS can affect calculations.
[pspp] / src / data / data-out.c
index 6622c7d4fe8fe05a31f1df5cd93d294395c6de87..cda2b75fb0b01a48e0ac14d1e4300d2304922716 100644 (file)
@@ -1,25 +1,22 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
 
 
-   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 (at your option) any later version.
+   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 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
 
    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., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 
 
 #include <config.h>
 
-#include "data-out.h"
+#include "data/data-out.h"
 
 #include <ctype.h>
 #include <float.h>
 
 #include <ctype.h>
 #include <float.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <time.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <time.h>
-
-#include "calendar.h"
-#include "format.h"
-#include "settings.h"
-#include "variable.h"
-
-#include <libpspp/assertion.h>
-#include <libpspp/float-format.h>
-#include <libpspp/integer-format.h>
-#include <libpspp/magic.h>
-#include <libpspp/message.h>
-#include <libpspp/misc.h>
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-
-#include "minmax.h"
+#include <unistr.h>
+
+#include "data/calendar.h"
+#include "data/format.h"
+#include "data/settings.h"
+#include "data/value.h"
+#include "libpspp/assertion.h"
+#include "libpspp/cast.h"
+#include "libpspp/float-format.h"
+#include "libpspp/i18n.h"
+#include "libpspp/integer-format.h"
+#include "libpspp/message.h"
+#include "libpspp/misc.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
+
+#include "gl/minmax.h"
+#include "gl/c-snprintf.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -59,28 +58,25 @@ struct rounder
     bool negative;      /* Is the number negative? */
   };
 
     bool negative;      /* Is the number negative? */
   };
 
-static void rounder_init (struct rounder *, double number, int max_decimals);
+static void rounder_init (struct rounder *, const struct fmt_number_style *,
+                          double number, int max_decimals);
 static int rounder_width (const struct rounder *, int decimals,
                           int *integer_digits, bool *negative);
 static void rounder_format (const struct rounder *, int decimals,
                             char *output);
 \f
 static int rounder_width (const struct rounder *, int decimals,
                           int *integer_digits, bool *negative);
 static void rounder_format (const struct rounder *, int decimals,
                             char *output);
 \f
-/* Format of integers in output (SET WIB). */
-static enum integer_format output_integer_format = INTEGER_NATIVE;
-
-/* Format of reals in output (SET WRB). */
-static enum float_format output_float_format = FLOAT_NATIVE_DOUBLE;
-
 typedef void data_out_converter_func (const union value *,
                                       const struct fmt_spec *,
 typedef void data_out_converter_func (const union value *,
                                       const struct fmt_spec *,
-                                      char *);
+                                      const struct fmt_settings *, char *);
 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
         static data_out_converter_func output_##METHOD;
 #include "format.def"
 
 static bool output_decimal (const struct rounder *, const struct fmt_spec *,
 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
         static data_out_converter_func output_##METHOD;
 #include "format.def"
 
 static bool output_decimal (const struct rounder *, const struct fmt_spec *,
+                            const struct fmt_number_style *,
                             bool require_affixes, char *);
 static bool output_scientific (double, const struct fmt_spec *,
                             bool require_affixes, char *);
 static bool output_scientific (double, const struct fmt_spec *,
+                               const struct fmt_number_style *,
                                bool require_affixes, char *);
 
 static double power10 (int) PURE_FUNCTION;
                                bool require_affixes, char *);
 
 static double power10 (int) PURE_FUNCTION;
@@ -94,52 +90,154 @@ static void output_binary_integer (uint64_t, int bytes, enum integer_format,
                                    char *);
 static void output_hex (const void *, size_t bytes, char *);
 \f
                                    char *);
 static void output_hex (const void *, size_t bytes, char *);
 \f
-/* Converts the INPUT value into printable form in the exactly
-   FORMAT->W characters in OUTPUT according to format
-   specification FORMAT.  No null terminator is appended to the
-   buffer.  */
-void
-data_out (const union value *input, const struct fmt_spec *format,
-          char *output)
-{
-  static data_out_converter_func *const converters[FMT_NUMBER_OF_FORMATS] = 
+
+static data_out_converter_func *const converters[FMT_NUMBER_OF_FORMATS] =
     {
 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) output_##METHOD,
 #include "format.def"
     };
 
     {
 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) output_##METHOD,
 #include "format.def"
     };
 
-  assert (fmt_check_output (format));
+/* Converts the INPUT value, encoded in INPUT_ENCODING, according to format
+   specification FORMAT, appending the output to OUTPUT in OUTPUT_ENCODING.
+   However, binary formats (FMT_P, FMT_PK, FMT_IB, FMT_PIB, FMT_RB) yield the
+   binary results, which may not be properly encoded for OUTPUT_ENCODING.
+
+   VALUE must be the correct width for FORMAT, that is, its width must be
+   fmt_var_width(FORMAT).
 
 
-  converters[format->type] (input, format, output);
+   INPUT_ENCODING can normally be obtained by calling dict_get_encoding() on
+   the dictionary with which INPUT is associated.  ENCODING is only important
+   when FORMAT's type is FMT_A. */
+void
+data_out_recode (const union value *input, const char *input_encoding,
+                 const struct fmt_spec *format,
+                 const struct fmt_settings *settings,
+                 struct string *output, const char *output_encoding)
+{
+  assert (fmt_check_output (format));
+  if (format->type == FMT_A)
+    {
+      char *in = CHAR_CAST (char *, input->s);
+      char *out = recode_string (output_encoding, input_encoding,
+                                 in, format->w);
+      ds_put_cstr (output, out);
+      free (out);
+    }
+  else if (fmt_get_category (format->type) == FMT_CAT_BINARY)
+    converters[format->type] (input, format, settings,
+                              ds_put_uninit (output, format->w));
+  else
+    {
+      char *utf8_encoded = data_out (input, input_encoding, format, settings);
+      char *output_encoded = recode_string (output_encoding, UTF8,
+                                            utf8_encoded, -1);
+      ds_put_cstr (output, output_encoded);
+      free (output_encoded);
+      free (utf8_encoded);
+    }
 }
 
 }
 
-/* Returns the current output integer format. */
-enum integer_format
-data_out_get_integer_format (void) 
+static char *
+binary_to_utf8 (const char *in, struct pool *pool)
 {
 {
-  return output_integer_format;
+  uint8_t *out = pool_alloc_unaligned (pool, strlen (in) * 2 + 1);
+  uint8_t *p = out;
+
+  while (*in != '\0')
+    {
+      uint8_t byte = *in++;
+      int mblen = u8_uctomb (p, byte, 2);
+      assert (mblen > 0);
+      p += mblen;
+    }
+  *p = '\0';
+
+  return CHAR_CAST (char *, out);
 }
 
 }
 
-/* Sets the output integer format to INTEGER_FORMAT. */
-void
-data_out_set_integer_format (enum integer_format integer_format) 
+/* Converts the INPUT value into a UTF-8 encoded string, according to format
+   specification FORMAT.
+
+   VALUE must be the correct width for FORMAT, that is, its width must be
+   fmt_var_width(FORMAT).
+
+   INPUT_ENCODING must be the encoding of INPUT.  Normally this can be obtained
+   by calling dict_get_encoding() on the dictionary with which INPUT is
+   associated.  INPUT_ENCODING is only important when FORMAT's type is FMT_A.
+
+   The return value is dynamically allocated, and must be freed by the caller.
+   If POOL is non-null, then the return value is allocated on that pool.  */
+char *
+data_out_pool (const union value *input, const char *input_encoding,
+              const struct fmt_spec *format,
+               const struct fmt_settings *settings, struct pool *pool)
 {
 {
-  output_integer_format = integer_format;
+  assert (fmt_check_output (format));
+  if (format->type == FMT_A)
+    {
+      char *in = CHAR_CAST (char *, input->s);
+      return recode_string_pool (UTF8, input_encoding, in, format->w, pool);
+    }
+  else if (fmt_get_category (format->type) == FMT_CAT_BINARY)
+    {
+      char tmp[16];
+
+      assert (format->w + 1 <= sizeof tmp);
+      converters[format->type] (input, format, settings, tmp);
+      return binary_to_utf8 (tmp, pool);
+    }
+  else
+    {
+      const struct fmt_number_style *style = fmt_settings_get_style (
+        settings, format->type);
+      size_t size = format->w + style->extra_bytes + 1;
+      char *output;
+
+      output = pool_alloc_unaligned (pool, size);
+      converters[format->type] (input, format, settings, output);
+      return output;
+    }
 }
 
 }
 
-/* Returns the current output float format. */
-enum float_format
-data_out_get_float_format (void) 
+/* Like data_out_pool(), except that for basic numeric formats (F, COMMA, DOT,
+   COLLAR, PCT, E) and custom currency formats are formatted as wide as
+   necessary to fully display the selected number of decimal places. */
+char *
+data_out_stretchy (const union value *input, const char *encoding,
+                   const struct fmt_spec *format,
+                   const struct fmt_settings *settings, struct pool *pool)
 {
 {
-  return output_float_format;
+
+  if (fmt_get_category (format->type) & (FMT_CAT_BASIC | FMT_CAT_CUSTOM))
+    {
+      const struct fmt_number_style *style
+        = fmt_settings_get_style (settings, format->type);
+      struct fmt_spec wide_format;
+      char tmp[128];
+      size_t size;
+
+      wide_format.type = format->type;
+      wide_format.w = 40;
+      wide_format.d = format->d;
+
+      size = format->w + style->extra_bytes + 1;
+      if (size <= sizeof tmp)
+        {
+          output_number (input, &wide_format, settings, tmp);
+          return pool_strdup (pool, tmp + strspn (tmp, " "));
+        }
+    }
+
+  return data_out_pool (input, encoding, format, settings, pool);
 }
 
 }
 
-/* Sets the output float format to FLOAT_FORMAT. */
-void
-data_out_set_float_format (enum float_format float_format) 
+char *
+data_out (const union value *input, const char *input_encoding,
+          const struct fmt_spec *format, const struct fmt_settings *settings)
 {
 {
-  output_float_format = float_format;
+  return data_out_pool (input, input_encoding, format, settings, NULL);
 }
 }
+
 \f
 /* Main conversion functions. */
 
 \f
 /* Main conversion functions. */
 
@@ -147,7 +245,7 @@ data_out_set_float_format (enum float_format float_format)
    CCE formats. */
 static void
 output_number (const union value *input, const struct fmt_spec *format,
    CCE formats. */
 static void
 output_number (const union value *input, const struct fmt_spec *format,
-               char *output)
+               const struct fmt_settings *settings, char *output)
 {
   double number = input->f;
 
 {
   double number = input->f;
 
@@ -155,20 +253,23 @@ output_number (const union value *input, const struct fmt_spec *format,
     output_missing (format, output);
   else if (!isfinite (number))
     output_infinite (number, format, output);
     output_missing (format, output);
   else if (!isfinite (number))
     output_infinite (number, format, output);
-  else 
+  else
     {
     {
-      if (format->type != FMT_E && fabs (number) < 1.5 * power10 (format->w)) 
+      const struct fmt_number_style *style =
+        fmt_settings_get_style (settings, format->type);
+
+      if (format->type != FMT_E && fabs (number) < 1.5 * power10 (format->w))
         {
           struct rounder r;
         {
           struct rounder r;
-          rounder_init (&r, number, format->d);
+          rounder_init (&r, style, number, format->d);
 
 
-          if (output_decimal (&r, format, true, output)
-              || output_scientific (number, format, true, output)
-              || output_decimal (&r, format, false, output))
+          if (output_decimal (&r, format, style, true, output)
+              || output_scientific (number, format, style, true, output)
+              || output_decimal (&r, format, style, false, output))
             return;
         }
 
             return;
         }
 
-      if (!output_scientific (number, format, false, output))
+      if (!output_scientific (number, format, style, false, output))
         output_overflow (format, output);
     }
 }
         output_overflow (format, output);
     }
 }
@@ -176,7 +277,7 @@ output_number (const union value *input, const struct fmt_spec *format,
 /* Outputs N format. */
 static void
 output_N (const union value *input, const struct fmt_spec *format,
 /* Outputs N format. */
 static void
 output_N (const union value *input, const struct fmt_spec *format,
-          char *output)
+          const struct fmt_settings *settings UNUSED, char *output)
 {
   double number = input->f * power10 (format->d);
   if (input->f == SYSMIS || number < 0)
 {
   double number = input->f * power10 (format->d);
   if (input->f == SYSMIS || number < 0)
@@ -186,41 +287,44 @@ output_N (const union value *input, const struct fmt_spec *format,
       char buf[128];
       number = fabs (round (number));
       if (number < power10 (format->w)
       char buf[128];
       number = fabs (round (number));
       if (number < power10 (format->w)
-          && sprintf (buf, "%0*.0f", format->w, number) == format->w)
+          && c_snprintf (buf, 128, "%0*.0f", format->w, number) == format->w)
         memcpy (output, buf, format->w);
       else
         output_overflow (format, output);
     }
         memcpy (output, buf, format->w);
       else
         output_overflow (format, output);
     }
+
+  output[format->w] = '\0';
 }
 
 /* Outputs Z format. */
 static void
 output_Z (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs Z format. */
 static void
 output_Z (const union value *input, const struct fmt_spec *format,
-          char *output)
+          const struct fmt_settings *settings UNUSED, char *output)
 {
   double number = input->f * power10 (format->d);
   char buf[128];
   if (input->f == SYSMIS)
     output_missing (format, output);
 {
   double number = input->f * power10 (format->d);
   char buf[128];
   if (input->f == SYSMIS)
     output_missing (format, output);
-  else if (fabs (number) >= power10 (format->w)
-           || sprintf (buf, "%0*.0f", format->w,
-                       fabs (round (number))) != format->w)
-    output_overflow (format, output);
-  else
+  else if (fabs (number) < power10 (format->w)
+           && c_snprintf (buf, 128, "%0*.0f", format->w,
+                       fabs (round (number))) == format->w)
     {
     {
-      if (number < 0 && strspn (buf, "0") < format->w) 
+      if (number < 0 && strspn (buf, "0") < format->w)
         {
           char *p = &buf[format->w - 1];
           *p = "}JKLMNOPQR"[*p - '0'];
         }
         {
           char *p = &buf[format->w - 1];
           *p = "}JKLMNOPQR"[*p - '0'];
         }
-      memcpy (output, buf, format->w); 
+      memcpy (output, buf, format->w);
+      output[format->w] = '\0';
     }
     }
+  else
+    output_overflow (format, output);
 }
 
 /* Outputs P format. */
 static void
 output_P (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs P format. */
 static void
 output_P (const union value *input, const struct fmt_spec *format,
-          char *output)
+          const struct fmt_settings *settings UNUSED, char *output)
 {
   if (output_bcd_integer (fabs (input->f * power10 (format->d)),
                           format->w * 2 - 1, output)
 {
   if (output_bcd_integer (fabs (input->f * power10 (format->d)),
                           format->w * 2 - 1, output)
@@ -233,7 +337,7 @@ output_P (const union value *input, const struct fmt_spec *format,
 /* Outputs PK format. */
 static void
 output_PK (const union value *input, const struct fmt_spec *format,
 /* Outputs PK format. */
 static void
 output_PK (const union value *input, const struct fmt_spec *format,
-           char *output)
+           const struct fmt_settings *settings UNUSED, char *output)
 {
   output_bcd_integer (input->f * power10 (format->d), format->w * 2, output);
 }
 {
   output_bcd_integer (input->f * power10 (format->d), format->w * 2, output);
 }
@@ -241,7 +345,7 @@ output_PK (const union value *input, const struct fmt_spec *format,
 /* Outputs IB format. */
 static void
 output_IB (const union value *input, const struct fmt_spec *format,
 /* Outputs IB format. */
 static void
 output_IB (const union value *input, const struct fmt_spec *format,
-           char *output)
+           const struct fmt_settings *settings UNUSED, char *output)
 {
   double number = round (input->f * power10 (format->d));
   if (input->f == SYSMIS
 {
   double number = round (input->f * power10 (format->d));
   if (input->f == SYSMIS
@@ -253,28 +357,34 @@ output_IB (const union value *input, const struct fmt_spec *format,
       uint64_t integer = fabs (number);
       if (number < 0)
         integer = -integer;
       uint64_t integer = fabs (number);
       if (number < 0)
         integer = -integer;
-      output_binary_integer (integer, format->w, output_integer_format,
+      output_binary_integer (integer, format->w,
+                            settings_get_output_integer_format (),
                              output);
     }
                              output);
     }
+
+  output[format->w] = '\0';
 }
 
 /* Outputs PIB format. */
 static void
 output_PIB (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs PIB format. */
 static void
 output_PIB (const union value *input, const struct fmt_spec *format,
-            char *output)
+            const struct fmt_settings *settings UNUSED, char *output)
 {
   double number = round (input->f * power10 (format->d));
   if (input->f == SYSMIS
       || number < 0 || number >= power256 (format->w))
     memset (output, 0, format->w);
   else
 {
   double number = round (input->f * power10 (format->d));
   if (input->f == SYSMIS
       || number < 0 || number >= power256 (format->w))
     memset (output, 0, format->w);
   else
-    output_binary_integer (number, format->w, output_integer_format, output);
+    output_binary_integer (number, format->w,
+                          settings_get_output_integer_format (), output);
+
+  output[format->w] = '\0';
 }
 
 /* Outputs PIBHEX format. */
 static void
 output_PIBHEX (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs PIBHEX format. */
 static void
 output_PIBHEX (const union value *input, const struct fmt_spec *format,
-               char *output)
+               const struct fmt_settings *settings UNUSED, char *output)
 {
   double number = round (input->f);
   if (input->f == SYSMIS)
 {
   double number = round (input->f);
   if (input->f == SYSMIS)
@@ -287,23 +397,27 @@ output_PIBHEX (const union value *input, const struct fmt_spec *format,
       output_binary_integer (number, format->w / 2, INTEGER_MSB_FIRST, tmp);
       output_hex (tmp, format->w / 2, output);
     }
       output_binary_integer (number, format->w / 2, INTEGER_MSB_FIRST, tmp);
       output_hex (tmp, format->w / 2, output);
     }
+
 }
 
 /* Outputs RB format. */
 static void
 output_RB (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs RB format. */
 static void
 output_RB (const union value *input, const struct fmt_spec *format,
-           char *output)
+           const struct fmt_settings *settings UNUSED, char *output)
 {
   double d = input->f;
   memcpy (output, &d, format->w);
 {
   double d = input->f;
   memcpy (output, &d, format->w);
+
+  output[format->w] = '\0';
 }
 
 /* Outputs RBHEX format. */
 static void
 output_RBHEX (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs RBHEX format. */
 static void
 output_RBHEX (const union value *input, const struct fmt_spec *format,
-              char *output)
+              const struct fmt_settings *settings UNUSED, char *output)
 {
   double d = input->f;
 {
   double d = input->f;
+
   output_hex (&d, format->w / 2, output);
 }
 
   output_hex (&d, format->w / 2, output);
 }
 
@@ -311,41 +425,40 @@ output_RBHEX (const union value *input, const struct fmt_spec *format,
    DATETIME, TIME, and DTIME formats. */
 static void
 output_date (const union value *input, const struct fmt_spec *format,
    DATETIME, TIME, and DTIME formats. */
 static void
 output_date (const union value *input, const struct fmt_spec *format,
-             char *output)
+             const struct fmt_settings *settings, char *output)
 {
   double number = input->f;
 {
   double number = input->f;
-  double magnitude = fabs (number);
   int year, month, day, yday;
 
   int year, month, day, yday;
 
-  const char *template = fmt_date_template (format->type);
-  size_t template_width = strlen (template);
-  int excess_width = format->w - template_width;
+  const char *template = fmt_date_template (format->type, format->w);
 
   char tmp[64];
   char *p = tmp;
 
 
   char tmp[64];
   char *p = tmp;
 
-  assert (format->w >= template_width);
   if (number == SYSMIS)
     goto missing;
 
   if (number == SYSMIS)
     goto missing;
 
-  if (fmt_get_category (format->type) == FMT_CAT_DATE) 
+  if (fmt_get_category (format->type) == FMT_CAT_DATE)
     {
       if (number <= 0)
         goto missing;
       calendar_offset_to_gregorian (number / 60. / 60. / 24.,
                                     &year, &month, &day, &yday);
     {
       if (number <= 0)
         goto missing;
       calendar_offset_to_gregorian (number / 60. / 60. / 24.,
                                     &year, &month, &day, &yday);
+      number = fmod (number, 60. * 60. * 24.);
     }
   else
     year = month = day = yday = 0;
 
   while (*template != '\0')
     {
     }
   else
     year = month = day = yday = 0;
 
   while (*template != '\0')
     {
+      int excess_width;
+
       int ch = *template;
       int count = 1;
       int ch = *template;
       int count = 1;
-      while (template[count] == ch) 
+      while (template[count] == ch)
         count++;
       template += count;
         count++;
       template += count;
-      
+
       switch (ch)
         {
         case 'd':
       switch (ch)
         {
         case 'd':
@@ -359,30 +472,32 @@ output_date (const union value *input, const struct fmt_spec *format,
             p += sprintf (p, "%02d", month);
           else
             {
             p += sprintf (p, "%02d", month);
           else
             {
-              static const char *months[12] =
+              static const char *const months[12] =
                 {
                   "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                   "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
                 };
                 {
                   "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                   "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
                 };
-              p = stpcpy (p, months[month - 1]); 
+              p = stpcpy (p, months[month - 1]);
             }
           break;
         case 'y':
             }
           break;
         case 'y':
-          if (count >= 4 || excess_width >= 2) 
+          if (count >= 4)
             {
               if (year <= 9999)
                 p += sprintf (p, "%04d", year);
             {
               if (year <= 9999)
                 p += sprintf (p, "%04d", year);
-              else if (format->type == FMT_DATETIME)
+              else if (format->type == FMT_DATETIME
+                       || format->type == FMT_YMDHMS)
                 p = stpcpy (p, "****");
               else
                 p = stpcpy (p, "****");
               else
-                goto overflow; 
+                goto overflow;
             }
             }
-          else 
+          else
             {
             {
-              int offset = year - get_epoch ();
+              int epoch = fmt_settings_get_epoch (settings);
+              int offset = year - epoch;
               if (offset < 0 || offset > 99)
                 goto overflow;
               if (offset < 0 || offset > 99)
                 goto overflow;
-              p += sprintf (p, "%02d", abs (year) % 100); 
+              p += sprintf (p, "%02d", abs (year) % 100);
             }
           break;
         case 'q':
             }
           break;
         case 'q':
@@ -394,48 +509,54 @@ output_date (const union value *input, const struct fmt_spec *format,
         case 'D':
           if (number < 0)
             *p++ = '-';
         case 'D':
           if (number < 0)
             *p++ = '-';
-          p += sprintf (p, "%.0f", floor (magnitude / 60. / 60. / 24.));
+          number = fabs (number);
+          p += c_snprintf (p, 64, "%*.0f", count, floor (number / 60. / 60. / 24.));
+          number = fmod (number, 60. * 60. * 24.);
           break;
           break;
-        case 'h':
+        case 'H':
           if (number < 0)
             *p++ = '-';
           if (number < 0)
             *p++ = '-';
-          p += sprintf (p, "%.0f", floor (magnitude / 60. / 60.));
-          break;
-        case 'H':
-          p += sprintf (p, "%02d",
-                        (int) fmod (floor (magnitude / 60. / 60.), 24.));
+          number = fabs (number);
+          p += c_snprintf (p, 64, "%0*.0f", count, floor (number / 60. / 60.));
+          number = fmod (number, 60. * 60.);
           break;
         case 'M':
           break;
         case 'M':
-          p += sprintf (p, "%02d",
-                        (int) fmod (floor (magnitude / 60.), 60.));
+          if (number < 0)
+            *p++ = '-';
+          number = fabs (number);
+          p += sprintf (p, "%02d", (int) floor (number / 60.));
+          number = fmod (number, 60.);
           excess_width = format->w - (p - tmp);
           excess_width = format->w - (p - tmp);
-          if (excess_width < 0) 
+          if (excess_width < 0
+              || (format->type == FMT_MTIME && excess_width < 3))
             goto overflow;
           if (excess_width == 3 || excess_width == 4
               || (excess_width >= 5 && format->d == 0))
             goto overflow;
           if (excess_width == 3 || excess_width == 4
               || (excess_width >= 5 && format->d == 0))
-            p += sprintf (p, ":%02d", (int) fmod (magnitude, 60.));
+            p += sprintf (p, ":%02d", (int) number);
           else if (excess_width >= 5)
             {
               int d = MIN (format->d, excess_width - 4);
               int w = d + 3;
           else if (excess_width >= 5)
             {
               int d = MIN (format->d, excess_width - 4);
               int w = d + 3;
-              sprintf (p, ":%0*.*f", w, d, fmod (magnitude, 60.));
-              if (fmt_decimal_char (FMT_F) != '.') 
+              c_snprintf (p, 64, ":%0*.*f", w, d, number);
+             if (settings->decimal != '.')
                 {
                   char *cp = strchr (p, '.');
                   if (cp != NULL)
                 {
                   char *cp = strchr (p, '.');
                   if (cp != NULL)
-                    *cp = fmt_decimal_char (FMT_F);
+                   *cp = settings->decimal;
                 }
               p += strlen (p);
             }
                 }
               p += strlen (p);
             }
-          break;
+          goto done;
         default:
           assert (count == 1);
           *p++ = ch;
         default:
           assert (count == 1);
           *p++ = ch;
-          break; 
+          break;
         }
     }
 
         }
     }
 
-  buf_copy_lpad (output, format->w, tmp, p - tmp);
+ done:
+  buf_copy_lpad (output, format->w, tmp, p - tmp, ' ');
+  output[format->w] = '\0';
   return;
 
  overflow:
   return;
 
  overflow:
@@ -450,59 +571,69 @@ output_date (const union value *input, const struct fmt_spec *format,
 /* Outputs WKDAY format. */
 static void
 output_WKDAY (const union value *input, const struct fmt_spec *format,
 /* Outputs WKDAY format. */
 static void
 output_WKDAY (const union value *input, const struct fmt_spec *format,
-              char *output)
+              const struct fmt_settings *settings UNUSED, char *output)
 {
 {
-  static const char *weekdays[7] =
+  static const char *const weekdays[7] =
     {
       "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
       "THURSDAY", "FRIDAY", "SATURDAY",
     };
 
   if (input->f >= 1 && input->f < 8)
     {
       "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
       "THURSDAY", "FRIDAY", "SATURDAY",
     };
 
   if (input->f >= 1 && input->f < 8)
-    buf_copy_str_rpad (output, format->w, weekdays[(int) input->f - 1]);
+    {
+      buf_copy_str_rpad (output, format->w,
+                         weekdays[(int) input->f - 1], ' ');
+      output[format->w] = '\0';
+    }
   else
     {
       if (input->f != SYSMIS)
         msg (ME, _("Weekday number %f is not between 1 and 7."), input->f);
       output_missing (format, output);
     }
   else
     {
       if (input->f != SYSMIS)
         msg (ME, _("Weekday number %f is not between 1 and 7."), input->f);
       output_missing (format, output);
     }
+
 }
 
 /* Outputs MONTH format. */
 static void
 output_MONTH (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs MONTH format. */
 static void
 output_MONTH (const union value *input, const struct fmt_spec *format,
-              char *output)
+              const struct fmt_settings *settings UNUSED, char *output)
 {
 {
-  static const char *months[12] =
+  static const char *const months[12] =
     {
       "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
       "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
     };
 
   if (input->f >= 1 && input->f < 13)
     {
       "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
       "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
     };
 
   if (input->f >= 1 && input->f < 13)
-    buf_copy_str_rpad (output, format->w, months[(int) input->f - 1]);
+    {
+      buf_copy_str_rpad (output, format->w, months[(int) input->f - 1], ' ');
+      output[format->w] = '\0';
+    }
   else
     {
       if (input->f != SYSMIS)
         msg (ME, _("Month number %f is not between 1 and 12."), input->f);
       output_missing (format, output);
     }
   else
     {
       if (input->f != SYSMIS)
         msg (ME, _("Month number %f is not between 1 and 12."), input->f);
       output_missing (format, output);
     }
+
 }
 
 /* Outputs A format. */
 static void
 }
 
 /* Outputs A format. */
 static void
-output_A (const union value *input, const struct fmt_spec *format,
-          char *output)
+output_A (const union value *input UNUSED,
+          const struct fmt_spec *format UNUSED,
+          const struct fmt_settings *settings UNUSED, char *output UNUSED)
 {
 {
-  memcpy (output, input->s, format->w);
+  NOT_REACHED ();
 }
 
 /* Outputs AHEX format. */
 static void
 output_AHEX (const union value *input, const struct fmt_spec *format,
 }
 
 /* Outputs AHEX format. */
 static void
 output_AHEX (const union value *input, const struct fmt_spec *format,
-             char *output)
+             const struct fmt_settings *settings UNUSED, char *output)
 {
 {
-  output_hex (input->s, format->w, output);
+  output_hex (input->s, format->w / 2, output);
 }
 \f
 /* Decimal and scientific formatting. */
 }
 \f
 /* Decimal and scientific formatting. */
@@ -514,7 +645,7 @@ static bool
 allocate_space (int request, int max_width, int *width)
 {
   assert (*width <= max_width);
 allocate_space (int request, int max_width, int *width)
 {
   assert (*width <= max_width);
-  if (request + *width <= max_width) 
+  if (request + *width <= max_width)
     {
       *width += request;
       return true;
     {
       *width += request;
       return true;
@@ -524,19 +655,19 @@ allocate_space (int request, int max_width, int *width)
 }
 
 /* Tries to compose the number represented by R, in the style of
 }
 
 /* Tries to compose the number represented by R, in the style of
-   FORMAT, into OUTPUT.  Returns true if successful, false on
-   failure, which occurs if FORMAT's width is too narrow.  If
+   FORMAT and STYLE, into OUTPUT.  Returns true if successful, false on
+   failure, which cocurs if FORMAT's width is too narrow.  If
    REQUIRE_AFFIXES is true, then the prefix and suffix specified
    by FORMAT's style must be included; otherwise, they may be
    omitted to make the number fit. */
 static bool
 output_decimal (const struct rounder *r, const struct fmt_spec *format,
    REQUIRE_AFFIXES is true, then the prefix and suffix specified
    by FORMAT's style must be included; otherwise, they may be
    omitted to make the number fit. */
 static bool
 output_decimal (const struct rounder *r, const struct fmt_spec *format,
-                bool require_affixes, char *output)
+                const struct fmt_number_style *style, bool require_affixes,
+                char *output)
 {
 {
-  const struct fmt_number_style *style = fmt_get_style (format->type);
   int decimals;
 
   int decimals;
 
-  for (decimals = format->d; decimals >= 0; decimals--) 
+  for (decimals = format->d; decimals >= 0; decimals--)
     {
       /* Formatted version of magnitude of NUMBER. */
       char magnitude[64];
     {
       /* Formatted version of magnitude of NUMBER. */
       char magnitude[64];
@@ -562,9 +693,9 @@ output_decimal (const struct rounder *r, const struct fmt_spec *format,
          the negative suffix, plus (if negative) the negative
          prefix. */
       width = rounder_width (r, decimals, &integer_digits, &add_neg_prefix);
          the negative suffix, plus (if negative) the negative
          prefix. */
       width = rounder_width (r, decimals, &integer_digits, &add_neg_prefix);
-      width += ss_length (style->neg_suffix);
+      width += style->neg_suffix.width;
       if (add_neg_prefix)
       if (add_neg_prefix)
-        width += ss_length (style->neg_prefix);
+        width += style->neg_prefix.width;
       if (width > format->w)
         continue;
 
       if (width > format->w)
         continue;
 
@@ -575,7 +706,7 @@ output_decimal (const struct rounder *r, const struct fmt_spec *format,
                                     format->w, &width);
       if (!add_affixes && require_affixes)
         continue;
                                     format->w, &width);
       if (!add_affixes && require_affixes)
         continue;
-      
+
       /* Check whether we should include grouping characters.
          We need room for a complete set or we don't insert any at all.
          We don't include grouping characters if decimal places were
       /* Check whether we should include grouping characters.
          We need room for a complete set or we don't insert any at all.
          We don't include grouping characters if decimal places were
@@ -588,16 +719,15 @@ output_decimal (const struct rounder *r, const struct fmt_spec *format,
 
       /* Format the number's magnitude. */
       rounder_format (r, decimals, magnitude);
 
       /* Format the number's magnitude. */
       rounder_format (r, decimals, magnitude);
-  
+
       /* Assemble number. */
       p = output;
       if (format->w > width)
         p = mempset (p, ' ', format->w - width);
       if (add_neg_prefix)
       /* Assemble number. */
       p = output;
       if (format->w > width)
         p = mempset (p, ' ', format->w - width);
       if (add_neg_prefix)
-        p = mempcpy (p, ss_data (style->neg_prefix),
-                     ss_length (style->neg_prefix));
+        p = stpcpy (p, style->neg_prefix.s);
       if (add_affixes)
       if (add_affixes)
-        p = mempcpy (p, ss_data (style->prefix), ss_length (style->prefix));
+        p = stpcpy (p, style->prefix.s);
       if (!add_grouping)
         p = mempcpy (p, magnitude, integer_digits);
       else
       if (!add_grouping)
         p = mempcpy (p, magnitude, integer_digits);
       else
@@ -608,7 +738,7 @@ output_decimal (const struct rounder *r, const struct fmt_spec *format,
               if (i > 0 && (integer_digits - i) % 3 == 0)
                 *p++ = style->grouping;
               *p++ = magnitude[i];
               if (i > 0 && (integer_digits - i) % 3 == 0)
                 *p++ = style->grouping;
               *p++ = magnitude[i];
-            } 
+            }
         }
       if (decimals > 0)
         {
         }
       if (decimals > 0)
         {
@@ -616,35 +746,37 @@ output_decimal (const struct rounder *r, const struct fmt_spec *format,
           p = mempcpy (p, &magnitude[integer_digits + 1], decimals);
         }
       if (add_affixes)
           p = mempcpy (p, &magnitude[integer_digits + 1], decimals);
         }
       if (add_affixes)
-        p = mempcpy (p, ss_data (style->suffix), ss_length (style->suffix));
+        p = stpcpy (p, style->suffix.s);
       if (add_neg_prefix)
       if (add_neg_prefix)
-        p = mempcpy (p, ss_data (style->neg_suffix),
-                     ss_length (style->neg_suffix));
+        p = stpcpy (p, style->neg_suffix.s);
       else
       else
-        p = mempset (p, ' ', ss_length (style->neg_suffix));
-      assert (p == output + format->w);
+        p = mempset (p, ' ', style->neg_suffix.width);
+
+      assert (p >= output + format->w);
+      assert (p <= output + format->w + style->extra_bytes);
+      *p = '\0';
 
       return true;
     }
   return false;
 }
 
 
       return true;
     }
   return false;
 }
 
-/* Formats NUMBER into OUTPUT in scientific notation according to
-   the style of the format specified in FORMAT. */
+/* Formats NUMBER into OUTPUT in scientific notation according to FORMAT and
+   STYLE. */
 static bool
 output_scientific (double number, const struct fmt_spec *format,
 static bool
 output_scientific (double number, const struct fmt_spec *format,
+                   const struct fmt_number_style *style,
                    bool require_affixes, char *output)
 {
                    bool require_affixes, char *output)
 {
-  const struct fmt_number_style *style = fmt_get_style (format->type);
   int width;
   int fraction_width;
   bool add_affixes;
   int width;
   int fraction_width;
   bool add_affixes;
-  char buf[64], *p;
+  char *p;
 
   /* Allocate minimum required space. */
 
   /* Allocate minimum required space. */
-  width = 6 + ss_length (style->neg_suffix);
+  width = 6 + style->neg_suffix.width;
   if (number < 0)
   if (number < 0)
-    width += ss_length (style->neg_prefix);
+    width += style->neg_prefix.width;
   if (width > format->w)
     return false;
 
   if (width > format->w)
     return false;
 
@@ -656,27 +788,24 @@ output_scientific (double number, const struct fmt_spec *format,
   /* Figure out number of characters we can use for the fraction,
      if any.  (If that turns out to be 1, then we'll output a
      decimal point without any digits following; that's what the
   /* Figure out number of characters we can use for the fraction,
      if any.  (If that turns out to be 1, then we'll output a
      decimal point without any digits following; that's what the
-     # flag does in the call to sprintf, below.) */
+     # flag does in the call to c_snprintf, below.) */
   fraction_width = MIN (MIN (format->d + 1, format->w - width), 16);
   fraction_width = MIN (MIN (format->d + 1, format->w - width), 16);
-  if (format->type != FMT_E
-      && (fraction_width == 1
-          || format->w - width + (style->grouping == 0 && number < 0) <= 2))
-    fraction_width = 0; 
+  if (format->type != FMT_E && fraction_width == 1)
+    fraction_width = 0;
   width += fraction_width;
 
   /* Format (except suffix). */
   width += fraction_width;
 
   /* Format (except suffix). */
-  p = buf;
+  p = output;
   if (width < format->w)
     p = mempset (p, ' ', format->w - width);
   if (number < 0)
   if (width < format->w)
     p = mempset (p, ' ', format->w - width);
   if (number < 0)
-    p = mempcpy (p, ss_data (style->neg_prefix),
-                 ss_length (style->neg_prefix));
+    p = stpcpy (p, style->neg_prefix.s);
   if (add_affixes)
   if (add_affixes)
-    p = mempcpy (p, ss_data (style->prefix), ss_length (style->prefix));
+    p = stpcpy (p, style->prefix.s);
   if (fraction_width > 0)
   if (fraction_width > 0)
-    sprintf (p, "%#.*E", fraction_width - 1, fabs (number));
+    c_snprintf (p, 64, "%#.*E", fraction_width - 1, fabs (number));
   else
   else
-    sprintf (p, "%.0E", fabs (number));
+    c_snprintf (p, 64, "%.0E", fabs (number));
 
   /* The C locale always uses a period `.' as a decimal point.
      Translate to comma if necessary. */
 
   /* The C locale always uses a period `.' as a decimal point.
      Translate to comma if necessary. */
@@ -691,7 +820,7 @@ output_scientific (double number, const struct fmt_spec *format,
   {
     char *cp = strchr (p, 'E') + 1;
     long int exponent = strtol (cp, NULL, 10);
   {
     char *cp = strchr (p, 'E') + 1;
     long int exponent = strtol (cp, NULL, 10);
-    if (abs (exponent) > 999) 
+    if (labs (exponent) > 999)
       return false;
     sprintf (cp, "%+04ld", exponent);
   }
       return false;
     sprintf (cp, "%+04ld", exponent);
   }
@@ -699,59 +828,50 @@ output_scientific (double number, const struct fmt_spec *format,
   /* Add suffixes. */
   p = strchr (p, '\0');
   if (add_affixes)
   /* Add suffixes. */
   p = strchr (p, '\0');
   if (add_affixes)
-    p = mempcpy (p, ss_data (style->suffix), ss_length (style->suffix));
+    p = stpcpy (p, style->suffix.s);
   if (number < 0)
   if (number < 0)
-    p = mempcpy (p, ss_data (style->neg_suffix),
-                 ss_length (style->neg_suffix));
+    p = stpcpy (p, style->neg_suffix.s);
   else
   else
-    p = mempset (p, ' ', ss_length (style->neg_suffix));
+    p = mempset (p, ' ', style->neg_suffix.width);
 
 
-  assert (p == buf + format->w);
+  assert (p >= output + format->w);
+  assert (p <= output + format->w + style->extra_bytes);
+  *p = '\0';
 
 
-  buf_copy_str_lpad (output, format->w, buf);
   return true;
 }
 \f
   return true;
 }
 \f
-#ifndef HAVE_ROUND
-/* Return X rounded to the nearest integer,
-   rounding ties away from zero. */
-static double
-round (double x) 
-{
-  return x >= 0.0 ? floor (x + .5) : ceil (x - .5);
-}
-#endif /* !HAVE_ROUND */
-
 /* Returns true if the magnitude represented by R should be
    rounded up when chopped off at DECIMALS decimal places, false
    if it should be rounded down. */
 static bool
 /* Returns true if the magnitude represented by R should be
    rounded up when chopped off at DECIMALS decimal places, false
    if it should be rounded down. */
 static bool
-should_round_up (const struct rounder *r, int decimals) 
+should_round_up (const struct rounder *r, int decimals)
 {
   int digit = r->string[r->integer_digits + decimals + 1];
   assert (digit >= '0' && digit <= '9');
   return digit >= '5';
 }
 
 {
   int digit = r->string[r->integer_digits + decimals + 1];
   assert (digit >= '0' && digit <= '9');
   return digit >= '5';
 }
 
-/* Initializes R for formatting the magnitude of NUMBER to no
+/* Initializes R for formatting the magnitude of NUMBER with STYLE to no
    more than MAX_DECIMAL decimal places. */
 static void
    more than MAX_DECIMAL decimal places. */
 static void
-rounder_init (struct rounder *r, double number, int max_decimals)
+rounder_init (struct rounder *r, const struct fmt_number_style *style,
+              double number, int max_decimals)
 {
   assert (fabs (number) < 1e41);
   assert (max_decimals >= 0 && max_decimals <= 16);
 {
   assert (fabs (number) < 1e41);
   assert (max_decimals >= 0 && max_decimals <= 16);
-  if (max_decimals == 0) 
+  if (max_decimals == 0)
     {
       /* Fast path.  No rounding needed.
 
          We append ".00" to the integer representation because
          round_up assumes that fractional digits are present.  */
     {
       /* Fast path.  No rounding needed.
 
          We append ".00" to the integer representation because
          round_up assumes that fractional digits are present.  */
-      sprintf (r->string, "%.0f.00", fabs (round (number)));
+      c_snprintf (r->string, 64, "%.0f.00", fabs (round (number)));
     }
     }
-  else 
+  else
     {
       /* Slow path.
     {
       /* Slow path.
-         
+
          This is more difficult than it really should be because
          we have to make sure that numbers that are exactly
          halfway between two representations are always rounded
          This is more difficult than it really should be because
          we have to make sure that numbers that are exactly
          halfway between two representations are always rounded
@@ -759,13 +879,13 @@ rounder_init (struct rounder *r, double number, int max_decimals)
          (usually it rounds to even), so we have to fake it as
          best we can, by formatting with extra precision and then
          doing the rounding ourselves.
          (usually it rounds to even), so we have to fake it as
          best we can, by formatting with extra precision and then
          doing the rounding ourselves.
-     
+
          We take up to two rounds to format numbers.  In the
          first round, we obtain 2 digits of precision beyond
          those requested by the user.  If those digits are
          exactly "50", then in a second round we format with as
          many digits as are significant in a "double".
          We take up to two rounds to format numbers.  In the
          first round, we obtain 2 digits of precision beyond
          those requested by the user.  If those digits are
          exactly "50", then in a second round we format with as
          many digits as are significant in a "double".
-     
+
          It might be better to directly implement our own
          floating-point formatting routine instead of relying on
          the system's sprintf implementation.  But the classic
          It might be better to directly implement our own
          floating-point formatting routine instead of relying on
          the system's sprintf implementation.  But the classic
@@ -773,7 +893,7 @@ rounder_init (struct rounder *r, double number, int max_decimals)
          numbers does not hint how to do what we want, and it's
          not obvious how to change their algorithms to do so.  It
          would also be a lot of work. */
          numbers does not hint how to do what we want, and it's
          not obvious how to change their algorithms to do so.  It
          would also be a lot of work. */
-      sprintf (r->string, "%.*f", max_decimals + 2, fabs (number));
+      c_snprintf (r->string, 64, "%.*f", max_decimals + 2, fabs (number));
       if (!strcmp (r->string + strlen (r->string) - 2, "50"))
         {
           int binary_exponent, decimal_exponent, format_decimals;
       if (!strcmp (r->string + strlen (r->string) - 2, "50"))
         {
           int binary_exponent, decimal_exponent, format_decimals;
@@ -781,16 +901,18 @@ rounder_init (struct rounder *r, double number, int max_decimals)
           decimal_exponent = binary_exponent * 3 / 10;
           format_decimals = (DBL_DIG + 1) - decimal_exponent;
           if (format_decimals > max_decimals + 2)
           decimal_exponent = binary_exponent * 3 / 10;
           format_decimals = (DBL_DIG + 1) - decimal_exponent;
           if (format_decimals > max_decimals + 2)
-            sprintf (r->string, "%.*f", format_decimals, fabs (number));
+            c_snprintf (r->string, 64, "%.*f", format_decimals, fabs (number));
         }
     }
         }
     }
-  
-  if (r->string[0] == '0'
+
+  if (r->string[0] == '0' && !style->include_leading_zero)
     memmove (r->string, &r->string[1], strlen (r->string));
 
   r->leading_zeros = strspn (r->string, "0.");
   r->leading_nines = strspn (r->string, "9.");
   r->integer_digits = strchr (r->string, '.') - r->string;
     memmove (r->string, &r->string[1], strlen (r->string));
 
   r->leading_zeros = strspn (r->string, "0.");
   r->leading_nines = strspn (r->string, "9.");
   r->integer_digits = strchr (r->string, '.') - r->string;
+  assert (r->integer_digits < 64);
+  assert (r->integer_digits >= 0);
   r->negative = number < 0;
 }
 
   r->negative = number < 0;
 }
 
@@ -808,7 +930,7 @@ rounder_init (struct rounder *r, double number, int max_decimals)
    *NEGATIVE is set to true; otherwise, it is set to false. */
 static int
 rounder_width (const struct rounder *r, int decimals,
    *NEGATIVE is set to true; otherwise, it is set to false. */
 static int
 rounder_width (const struct rounder *r, int decimals,
-               int *integer_digits, bool *negative) 
+               int *integer_digits, bool *negative)
 {
   /* Calculate base measures. */
   int width = r->integer_digits;
 {
   /* Calculate base measures. */
   int width = r->integer_digits;
@@ -821,28 +943,28 @@ rounder_width (const struct rounder *r, int decimals,
   if (should_round_up (r, decimals))
     {
       /* Rounding up leading 9s adds a new digit (a 1). */
   if (should_round_up (r, decimals))
     {
       /* Rounding up leading 9s adds a new digit (a 1). */
-      if (r->leading_nines >= width) 
+      if (r->leading_nines >= width)
         {
           width++;
         {
           width++;
-          ++*integer_digits; 
+          ++*integer_digits;
         }
     }
   else
     {
       /* Rounding down. */
         }
     }
   else
     {
       /* Rounding down. */
-      if (r->leading_zeros >= width) 
+      if (r->leading_zeros >= width)
         {
           /* All digits that remain after rounding are zeros.
              Therefore we drop the negative sign. */
           *negative = false;
         {
           /* All digits that remain after rounding are zeros.
              Therefore we drop the negative sign. */
           *negative = false;
-          if (r->integer_digits == 0 && decimals == 0) 
+          if (r->integer_digits == 0 && decimals == 0)
             {
               /* No digits at all are left.  We need to display
                  at least a single digit (a zero). */
               assert (width == 0);
               width++;
               *integer_digits = 1;
             {
               /* No digits at all are left.  We need to display
                  at least a single digit (a zero). */
               assert (width == 0);
               width++;
               *integer_digits = 1;
-            } 
+            }
         }
     }
   return width;
         }
     }
   return width;
@@ -853,23 +975,23 @@ rounder_width (const struct rounder *r, int decimals,
    indicated by rounder_width are written.  No terminating null
    is appended. */
 static void
    indicated by rounder_width are written.  No terminating null
    is appended. */
 static void
-rounder_format (const struct rounder *r, int decimals, char *output) 
+rounder_format (const struct rounder *r, int decimals, char *output)
 {
   int base_width = r->integer_digits + (decimals > 0 ? decimals + 1 : 0);
 {
   int base_width = r->integer_digits + (decimals > 0 ? decimals + 1 : 0);
-  if (should_round_up (r, decimals)) 
+  if (should_round_up (r, decimals))
     {
     {
-      if (r->leading_nines < base_width) 
+      if (r->leading_nines < base_width)
         {
           /* Rounding up.  This is the common case where rounding
              up doesn't add an extra digit. */
           char *p;
           memcpy (output, r->string, base_width);
         {
           /* Rounding up.  This is the common case where rounding
              up doesn't add an extra digit. */
           char *p;
           memcpy (output, r->string, base_width);
-          for (p = output + base_width - 1; ; p--) 
+          for (p = output + base_width - 1; ; p--)
             {
               assert (p >= output);
               if (*p == '9')
                 *p = '0';
             {
               assert (p >= output);
               if (*p == '9')
                 *p = '0';
-              else if (*p >= '0' && *p <= '8') 
+              else if (*p >= '0' && *p <= '8')
                 {
                   (*p)++;
                   break;
                 {
                   (*p)++;
                   break;
@@ -878,14 +1000,14 @@ rounder_format (const struct rounder *r, int decimals, char *output)
                 assert (*p == '.');
             }
         }
                 assert (*p == '.');
             }
         }
-      else 
+      else
         {
           /* Rounding up leading 9s causes the result to be a 1
              followed by a number of 0s, plus a decimal point. */
           char *p = output;
           *p++ = '1';
           p = mempset (p, '0', r->integer_digits);
         {
           /* Rounding up leading 9s causes the result to be a 1
              followed by a number of 0s, plus a decimal point. */
           char *p = output;
           *p++ = '1';
           p = mempset (p, '0', r->integer_digits);
-          if (decimals > 0) 
+          if (decimals > 0)
             {
               *p++ = '.';
               p = mempset (p, '0', decimals);
             {
               *p++ = '.';
               p = mempset (p, '0', decimals);
@@ -893,18 +1015,18 @@ rounder_format (const struct rounder *r, int decimals, char *output)
           assert (p == output + base_width + 1);
         }
     }
           assert (p == output + base_width + 1);
         }
     }
-  else 
+  else
     {
       /* Rounding down. */
     {
       /* Rounding down. */
-      if (r->integer_digits != 0 || decimals != 0) 
+      if (r->integer_digits != 0 || decimals != 0)
         {
           /* Common case: just copy the digits. */
         {
           /* Common case: just copy the digits. */
-          memcpy (output, r->string, base_width); 
+          memcpy (output, r->string, base_width);
         }
         }
-      else 
+      else
         {
           /* No digits remain.  The output is just a zero. */
         {
           /* No digits remain.  The output is just a zero. */
-          output[0] = '0'; 
+          output[0] = '0';
         }
     }
 }
         }
     }
 }
@@ -913,7 +1035,7 @@ rounder_format (const struct rounder *r, int decimals, char *output)
 
 /* Returns 10**X. */
 static double PURE_FUNCTION
 
 /* Returns 10**X. */
 static double PURE_FUNCTION
-power10 (int x) 
+power10 (int x)
 {
   static const double p[] =
     {
 {
   static const double p[] =
     {
@@ -928,9 +1050,9 @@ power10 (int x)
 
 /* Returns 256**X. */
 static double PURE_FUNCTION
 
 /* Returns 256**X. */
 static double PURE_FUNCTION
-power256 (int x) 
+power256 (int x)
 {
 {
-  static const double p[] = 
+  static const double p[] =
     {
       1.0,
       256.0,
     {
       1.0,
       256.0,
@@ -951,7 +1073,7 @@ static void
 output_infinite (double number, const struct fmt_spec *format, char *output)
 {
   assert (!isfinite (number));
 output_infinite (double number, const struct fmt_spec *format, char *output)
 {
   assert (!isfinite (number));
-  
+
   if (format->w >= 3)
     {
       const char *s;
   if (format->w >= 3)
     {
       const char *s;
@@ -963,10 +1085,12 @@ output_infinite (double number, const struct fmt_spec *format, char *output)
       else
         s = "Unknown";
 
       else
         s = "Unknown";
 
-      buf_copy_str_lpad (output, format->w, s);
+      buf_copy_str_lpad (output, format->w, s, ' ');
     }
     }
-  else 
+  else
     output_overflow (format, output);
     output_overflow (format, output);
+
+  output[format->w] = '\0';
 }
 
 /* Formats OUTPUT as a missing value for the given FORMAT. */
 }
 
 /* Formats OUTPUT as a missing value for the given FORMAT. */
@@ -975,22 +1099,25 @@ output_missing (const struct fmt_spec *format, char *output)
 {
   memset (output, ' ', format->w);
 
 {
   memset (output, ' ', format->w);
 
-  if (format->type != FMT_N) 
+  if (format->type != FMT_N)
     {
       int dot_ofs = (format->type == FMT_PCT ? 2
                      : format->type == FMT_E ? 5
                      : 1);
     {
       int dot_ofs = (format->type == FMT_PCT ? 2
                      : format->type == FMT_E ? 5
                      : 1);
-      output[MAX (0, format->w - format->d - dot_ofs)] = '.'; 
+      output[MAX (0, format->w - format->d - dot_ofs)] = '.';
     }
   else
     output[format->w - 1] = '.';
     }
   else
     output[format->w - 1] = '.';
+
+  output[format->w] = '\0';
 }
 
 /* Formats OUTPUT for overflow given FORMAT. */
 static void
 }
 
 /* Formats OUTPUT for overflow given FORMAT. */
 static void
-output_overflow (const struct fmt_spec *format, char *output) 
+output_overflow (const struct fmt_spec *format, char *output)
 {
   memset (output, '*', format->w);
 {
   memset (output, '*', format->w);
+  output[format->w] = '\0';
 }
 
 /* Converts the integer part of NUMBER to a packed BCD number
 }
 
 /* Converts the integer part of NUMBER to a packed BCD number
@@ -1000,34 +1127,36 @@ output_overflow (const struct fmt_spec *format, char *output)
    representable.  On failure, OUTPUT is cleared to all zero
    bytes. */
 static bool
    representable.  On failure, OUTPUT is cleared to all zero
    bytes. */
 static bool
-output_bcd_integer (double number, int digits, char *output) 
+output_bcd_integer (double number, int digits, char *output)
 {
   char decimal[64];
 
   assert (digits < sizeof decimal);
 {
   char decimal[64];
 
   assert (digits < sizeof decimal);
+
+  output[DIV_RND_UP (digits, 2)] = '\0';
   if (number != SYSMIS
       && number >= 0.
       && number < power10 (digits)
   if (number != SYSMIS
       && number >= 0.
       && number < power10 (digits)
-      && sprintf (decimal, "%0*.0f", digits, round (number)) == digits)
+      && c_snprintf (decimal, 64, "%0*.0f", digits, round (number)) == digits)
     {
       const char *src = decimal;
       int i;
 
     {
       const char *src = decimal;
       int i;
 
-      for (i = 0; i < digits / 2; i++) 
+      for (i = 0; i < digits / 2; i++)
         {
           int d0 = *src++ - '0';
           int d1 = *src++ - '0';
         {
           int d0 = *src++ - '0';
           int d1 = *src++ - '0';
-          *output++ = (d0 << 4) + d1; 
+          *output++ = (d0 << 4) + d1;
         }
       if (digits % 2)
         *output = (*src - '0') << 4;
         }
       if (digits % 2)
         *output = (*src - '0') << 4;
-      
+
       return true;
     }
       return true;
     }
-  else 
+  else
     {
       memset (output, 0, DIV_RND_UP (digits, 2));
     {
       memset (output, 0, DIV_RND_UP (digits, 2));
-      return false; 
+      return false;
     }
 }
 
     }
 }
 
@@ -1035,7 +1164,7 @@ output_bcd_integer (double number, int digits, char *output)
    given INTEGER_FORMAT. */
 static void
 output_binary_integer (uint64_t value, int bytes,
    given INTEGER_FORMAT. */
 static void
 output_binary_integer (uint64_t value, int bytes,
-                       enum integer_format integer_format, char *output) 
+                       enum integer_format integer_format, char *output)
 {
   integer_put (value, integer_format, output, bytes);
 }
 {
   integer_put (value, integer_format, output, bytes);
 }
@@ -1043,7 +1172,7 @@ output_binary_integer (uint64_t value, int bytes,
 /* Converts the BYTES bytes in DATA to twice as many hexadecimal
    digits in OUTPUT. */
 static void
 /* Converts the BYTES bytes in DATA to twice as many hexadecimal
    digits in OUTPUT. */
 static void
-output_hex (const void *data_, size_t bytes, char *output) 
+output_hex (const void *data_, size_t bytes, char *output)
 {
   const uint8_t *data = data_;
   size_t i;
 {
   const uint8_t *data = data_;
   size_t i;
@@ -1054,4 +1183,5 @@ output_hex (const void *data_, size_t bytes, char *output)
       *output++ = hex_digits[data[i] >> 4];
       *output++ = hex_digits[data[i] & 15];
     }
       *output++ = hex_digits[data[i] >> 4];
       *output++ = hex_digits[data[i] & 15];
     }
+  *output = '\0';
 }
 }