maint: update all copyright year number ranges
[platform/upstream/coreutils.git] / src / od.c
index 09c4c15..e7ec59a 100644 (file)
--- a/src/od.c
+++ b/src/od.c
@@ -1,10 +1,10 @@
 /* od -- dump files in octal and other formats
-   Copyright (C) 92, 1995-2005 Free Software Foundation, Inc.
+   Copyright (C) 1992-2013 Free Software Foundation, Inc.
 
-   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, or (at your option)
-   any later version.
+   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
@@ -12,8 +12,7 @@
    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /* Written by Jim Meyering.  */
 
 #include <sys/types.h>
 #include "system.h"
 #include "error.h"
+#include "ftoastr.h"
 #include "quote.h"
+#include "xfreopen.h"
+#include "xprintf.h"
 #include "xstrtol.h"
 
-/* The official name of this program (e.g., no `g' prefix).  */
+/* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "od"
 
-#define AUTHORS "Jim Meyering"
-
-#if defined(__GNUC__) || defined(STDC_HEADERS)
-# include <float.h>
-#endif
-
-#ifdef HAVE_LONG_DOUBLE
-typedef long double LONG_DOUBLE;
-#else
-typedef double LONG_DOUBLE;
-#endif
+#define AUTHORS proper_name ("Jim Meyering")
 
 /* The default number of input bytes per output line.  */
 #define DEFAULT_BYTES_PER_BLOCK 16
 
-/* The number of decimal digits of precision in a float.  */
-#ifndef FLT_DIG
-# define FLT_DIG 7
-#endif
-
-/* The number of decimal digits of precision in a double.  */
-#ifndef DBL_DIG
-# define DBL_DIG 15
-#endif
-
-/* The number of decimal digits of precision in a long double.  */
-#ifndef LDBL_DIG
-# define LDBL_DIG DBL_DIG
-#endif
-
-#if HAVE_UNSIGNED_LONG_LONG
-typedef unsigned long long int ulonglong_t;
+#if HAVE_UNSIGNED_LONG_LONG_INT
+typedef unsigned long long int unsigned_long_long_int;
 #else
-/* This is just a place-holder to avoid a few `#if' directives.
+/* This is just a place-holder to avoid a few '#if' directives.
    In this case, the type isn't actually used.  */
-typedef unsigned long int ulonglong_t;
+typedef unsigned long int unsigned_long_long_int;
 #endif
 
 enum size_spec
@@ -95,35 +72,44 @@ enum output_format
     CHARACTER
   };
 
-/* The maximum number of bytes needed for a format string,
-   including the trailing null.  */
+#define MAX_INTEGRAL_TYPE_SIZE sizeof (unsigned_long_long_int)
+
+/* The maximum number of bytes needed for a format string, including
+   the trailing nul.  Each format string expects a variable amount of
+   padding (guaranteed to be at least 1 plus the field width), then an
+   element that will be formatted in the field.  */
 enum
   {
     FMT_BYTES_ALLOCATED =
-      MAX ((sizeof " %0" - 1 + INT_STRLEN_BOUND (int)
-           + MAX (sizeof "ld",
-                  MAX (sizeof PRIdMAX,
-                       MAX (sizeof PRIoMAX,
-                            MAX (sizeof PRIuMAX,
-                                 sizeof PRIxMAX))))),
-          sizeof " %.Le" + 2 * INT_STRLEN_BOUND (int))
+           (sizeof "%*.99" - 1
+            + MAX (sizeof "ld",
+                   MAX (sizeof PRIdMAX,
+                        MAX (sizeof PRIoMAX,
+                             MAX (sizeof PRIuMAX,
+                                  sizeof PRIxMAX)))))
   };
 
-/* Each output format specification (from `-t spec' or from
+/* Ensure that our choice for FMT_BYTES_ALLOCATED is reasonable.  */
+verify (MAX_INTEGRAL_TYPE_SIZE * CHAR_BIT / 3 <= 99);
+
+/* Each output format specification (from '-t spec' or from
    old-style options) is represented by one of these structures.  */
 struct tspec
   {
     enum output_format fmt;
-    enum size_spec size;
-    void (*print_function) (size_t, void const *, char const *);
-    char fmt_string[FMT_BYTES_ALLOCATED];
+    enum size_spec size; /* Type of input object.  */
+    /* FIELDS is the number of fields per line, BLANK is the number of
+       fields to leave blank.  WIDTH is width of one field, excluding
+       leading space, and PAD is total pad to divide among FIELDS.
+       PAD is at least as large as FIELDS.  */
+    void (*print_function) (size_t fields, size_t blank, void const *data,
+                            char const *fmt, int width, int pad);
+    char fmt_string[FMT_BYTES_ALLOCATED]; /* Of the style "%*d".  */
     bool hexl_mode_trailer;
-    int field_width;
+    int field_width; /* Minimum width of a field, excluding leading space.  */
+    int pad_width; /* Total padding to be divided among fields.  */
   };
 
-/* The name this program was run with.  */
-char *program_name;
-
 /* Convert the number of 8-bit bytes of a binary representation to
    the number of characters (digits + sign if the type is signed)
    required to represent the same quantity in the specified base/type.
@@ -134,18 +120,28 @@ char *program_name;
    10  unsigned decimal
    8   unsigned hexadecimal  */
 
-static char const bytes_to_oct_digits[] =
+static unsigned int const bytes_to_oct_digits[] =
 {0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
 
-static char const bytes_to_signed_dec_digits[] =
+static unsigned int const bytes_to_signed_dec_digits[] =
 {1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
 
-static char const bytes_to_unsigned_dec_digits[] =
+static unsigned int const bytes_to_unsigned_dec_digits[] =
 {0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
 
-static char const bytes_to_hex_digits[] =
+static unsigned int const bytes_to_hex_digits[] =
 {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
 
+/* It'll be a while before we see integral types wider than 16 bytes,
+   but if/when it happens, this check will catch it.  Without this check,
+   a wider type would provoke a buffer overrun.  */
+verify (MAX_INTEGRAL_TYPE_SIZE < ARRAY_CARDINALITY (bytes_to_hex_digits));
+
+/* Make sure the other arrays have the same length.  */
+verify (sizeof bytes_to_oct_digits == sizeof bytes_to_signed_dec_digits);
+verify (sizeof bytes_to_oct_digits == sizeof bytes_to_unsigned_dec_digits);
+verify (sizeof bytes_to_oct_digits == sizeof bytes_to_hex_digits);
+
 /* Convert enum size_spec to the size of the named type.  */
 static const int width_bytes[] =
 {
@@ -154,18 +150,18 @@ static const int width_bytes[] =
   sizeof (short int),
   sizeof (int),
   sizeof (long int),
-  sizeof (ulonglong_t),
+  sizeof (unsigned_long_long_int),
   sizeof (float),
   sizeof (double),
-  sizeof (LONG_DOUBLE)
+  sizeof (long double)
 };
 
-/* Ensure that for each member of `enum size_spec' there is an
+/* Ensure that for each member of 'enum size_spec' there is an
    initializer in the width_bytes array.  */
-VERIFY (sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS);
+verify (ARRAY_CARDINALITY (width_bytes) == N_SIZE_SPECS);
 
 /* Names for some non-printing characters.  */
-static const char *const charname[33] =
+static char const charname[33][4] =
 {
   "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
   "bs", "ht", "nl", "vt", "ff", "cr", "so", "si",
@@ -185,7 +181,10 @@ static int address_base;
 /* Width of a normal address.  */
 static int address_pad_len;
 
+/* Minimum length when detecting --strings.  */
 static size_t string_min;
+
+/* True when in --strings mode.  */
 static bool flag_dump_strings;
 
 /* True if we should recognize the older non-option arguments
@@ -193,7 +192,7 @@ static bool flag_dump_strings;
    offset and pseudo-start address.  */
 static bool traditional;
 
-/* True if an old-style `pseudo-address' was specified.  */
+/* True if an old-style 'pseudo-address' was specified.  */
 static bool flag_pseudo_start;
 
 /* The difference between the old-style pseudo starting address and
@@ -254,10 +253,10 @@ static FILE *in_stream;
 /* If true, at least one of the files we read was standard input.  */
 static bool have_read_stdin;
 
-#define MAX_INTEGRAL_TYPE_SIZE sizeof (ulonglong_t)
+/* Map the size in bytes to a type identifier.  */
 static enum size_spec integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1];
 
-#define MAX_FP_TYPE_SIZE sizeof (LONG_DOUBLE)
+#define MAX_FP_TYPE_SIZE sizeof (long double)
 static enum size_spec fp_type_size[MAX_FP_TYPE_SIZE + 1];
 
 static char const short_options[] = "A:aBbcDdeFfHhIij:LlN:OoS:st:vw::Xx";
@@ -289,8 +288,7 @@ void
 usage (int status)
 {
   if (status != EXIT_SUCCESS)
-    fprintf (stderr, _("Try `%s --help' for more information.\n"),
-            program_name);
+    emit_try_help ();
   else
     {
       printf (_("\
@@ -298,7 +296,7 @@ Usage: %s [OPTION]... [FILE]...\n\
   or:  %s [-abcdfilosx]... [FILE] [[+]OFFSET[.][b]]\n\
   or:  %s --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]]\n\
 "),
-             program_name, program_name, program_name);
+              program_name, program_name, program_name);
       fputs (_("\n\
 Write an unambiguous representation, octal bytes by default,\n\
 of FILE to standard output.  With more than one FILE argument,\n\
@@ -307,26 +305,40 @@ With no FILE, or when FILE is -, read standard input.\n\
 \n\
 "), stdout);
       fputs (_("\
-All arguments to long options are mandatory for short options.\n\
+If first and second call formats both apply, the second format is assumed\n\
+if the last operand begins with + or (if there are 2 operands) a digit.\n\
+An OFFSET operand means -j OFFSET.  LABEL is the pseudo-address\n\
+at first byte printed, incremented when dump is progressing.\n\
+For OFFSET and LABEL, a 0x or 0X prefix indicates hexadecimal;\n\
+suffixes may be . for octal and b for multiply by 512.\n\
+\n\
 "), stdout);
       fputs (_("\
-  -A, --address-radix=RADIX   decide how file offsets are printed\n\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+      fputs (_("\
+  -A, --address-radix=RADIX   output format for file offsets.  RADIX is one\n\
+                                of [doxn], for Decimal, Octal, Hex or None\n\
   -j, --skip-bytes=BYTES      skip BYTES input bytes first\n\
 "), stdout);
       fputs (_("\
   -N, --read-bytes=BYTES      limit dump to BYTES input bytes\n\
-  -S, --strings[=BYTES]       output strings of at least BYTES graphic chars\n\
+  -S BYTES, --strings[=BYTES]  output strings of at least BYTES graphic chars.\
+\n\
+                                3 is implied when BYTES is not specified\n\
   -t, --format=TYPE           select output format or formats\n\
   -v, --output-duplicates     do not use * to mark line suppression\n\
-  -w, --width[=BYTES]         output BYTES bytes per output line\n\
-      --traditional           accept arguments in traditional form\n\
+  -w[BYTES], --width[=BYTES]  output BYTES bytes per output line.\n\
+                                32 is implied when BYTES is not specified\n\
+      --traditional           accept arguments in third form above\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
       fputs (_("\
 \n\
+\n\
 Traditional format specifications may be intermixed; they accumulate:\n\
-  -a   same as -t a,  select named characters\n\
+  -a   same as -t a,  select named characters, ignoring high-order bit\n\
   -b   same as -t o1, select octal bytes\n\
   -c   same as -t c,  select ASCII characters or backslash escapes\n\
   -d   same as -t u2, select unsigned decimal 2-byte units\n\
@@ -341,16 +353,9 @@ Traditional format specifications may be intermixed; they accumulate:\n\
 "), stdout);
       fputs (_("\
 \n\
-If first and second call formats both apply, the second format is assumed\n\
-if the last operand begins with + or (if there are 2 operands) a digit.\n\
-An OFFSET operand means -j OFFSET.  LABEL is the pseudo-address\n\
-at first byte printed, incremented when dump is progressing.\n\
-For OFFSET and LABEL, a 0x or 0X prefix indicates hexadecimal;\n\
-suffixes may be . for octal and b for multiply by 512.\n\
 \n\
 TYPE is made up of one or more of these specifications:\n\
-\n\
-  a          named character\n\
+  a          named character, ignoring high-order bit\n\
   c          ASCII character or backslash escape\n\
 "), stdout);
       fputs (_("\
@@ -362,121 +367,75 @@ TYPE is made up of one or more of these specifications:\n\
 "), stdout);
       fputs (_("\
 \n\
-SIZE is a number.  For TYPE in doux, SIZE may also be C for\n\
+SIZE is a number.  For TYPE in [doux], SIZE may also be C for\n\
 sizeof(char), S for sizeof(short), I for sizeof(int) or L for\n\
 sizeof(long).  If TYPE is f, SIZE may also be F for sizeof(float), D\n\
 for sizeof(double) or L for sizeof(long double).\n\
 "), stdout);
       fputs (_("\
 \n\
-RADIX is d for decimal, o for octal, x for hexadecimal or n for none.\n\
-BYTES is hexadecimal with 0x or 0X prefix, it is multiplied by 512\n\
-with b suffix, by 1024 with k and by 1048576 with m.  Adding a z suffix to\n\
-any type adds a display of printable characters to the end of each line\n\
-of output.  \
+Adding a z suffix to any type displays printable characters at the end of\n\
+each output line.\n\
 "), stdout);
       fputs (_("\
---string without a number implies 3.  --width without a number\n\
-implies 32.  By default, od uses -A o -t d2 -w 16.\n\
+\n\
+\n\
+BYTES is hex with 0x or 0X prefix, and may have a multiplier suffix:\n\
+  b    512\n\
+  KB   1000\n\
+  K    1024\n\
+  MB   1000*1000\n\
+  M    1024*1024\n\
+and so on for G, T, P, E, Z, Y.\n\
 "), stdout);
-      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+      emit_ancillary_info ();
     }
   exit (status);
 }
 
 /* Define the print functions.  */
 
-static void
-print_s_char (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  signed char const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
+#define PRINT_FIELDS(N, T, FMT_STRING, ACTION)                          \
+static void                                                             \
+N (size_t fields, size_t blank, void const *block,                      \
+   char const *FMT_STRING, int width, int pad)                          \
+{                                                                       \
+  T const *p = block;                                                   \
+  size_t i;                                                             \
+  int pad_remaining = pad;                                              \
+  for (i = fields; blank < i; i--)                                      \
+    {                                                                   \
+      int next_pad = pad * (i - 1) / fields;                            \
+      int adjusted_width = pad_remaining - next_pad + width;            \
+      T x = *p++;                                                       \
+      ACTION;                                                           \
+      pad_remaining = next_pad;                                         \
+    }                                                                   \
 }
 
-static void
-print_char (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  unsigned char const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
+#define PRINT_TYPE(N, T)                                                \
+  PRINT_FIELDS (N, T, fmt_string, xprintf (fmt_string, adjusted_width, x))
 
-static void
-print_s_short (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  short int const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
+#define PRINT_FLOATTYPE(N, T, FTOASTR, BUFSIZE)                         \
+  PRINT_FIELDS (N, T, fmt_string ATTRIBUTE_UNUSED,                      \
+                char buf[BUFSIZE];                                      \
+                FTOASTR (buf, sizeof buf, 0, 0, x);                     \
+                xprintf ("%*s", adjusted_width, buf))
 
-static void
-print_short (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  unsigned short int const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
+PRINT_TYPE (print_s_char, signed char)
+PRINT_TYPE (print_char, unsigned char)
+PRINT_TYPE (print_s_short, short int)
+PRINT_TYPE (print_short, unsigned short int)
+PRINT_TYPE (print_int, unsigned int)
+PRINT_TYPE (print_long, unsigned long int)
+PRINT_TYPE (print_long_long, unsigned_long_long_int)
 
-static void
-print_int (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  unsigned int const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
-
-static void
-print_long (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  unsigned long int const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
+PRINT_FLOATTYPE (print_float, float, ftoastr, FLT_BUFSIZE_BOUND)
+PRINT_FLOATTYPE (print_double, double, dtoastr, DBL_BUFSIZE_BOUND)
+PRINT_FLOATTYPE (print_long_double, long double, ldtoastr, LDBL_BUFSIZE_BOUND)
 
-static void
-print_long_long (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  ulonglong_t const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
-
-static void
-print_float (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  float const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
-
-static void
-print_double (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  double const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
-
-#ifdef HAVE_LONG_DOUBLE
-static void
-print_long_double (size_t n_bytes, void const *block, char const *fmt_string)
-{
-  long double const *p = block;
-  size_t i;
-  for (i = n_bytes / sizeof *p; i != 0; i--)
-    printf (fmt_string, *p++);
-}
-#endif
+#undef PRINT_TYPE
+#undef PRINT_FLOATTYPE
 
 static void
 dump_hexl_mode_trailer (size_t n_bytes, const char *block)
@@ -486,90 +445,99 @@ dump_hexl_mode_trailer (size_t n_bytes, const char *block)
   for (i = n_bytes; i > 0; i--)
     {
       unsigned char c = *block++;
-      unsigned char c2 = (ISPRINT(c) ? c : '.');
+      unsigned char c2 = (isprint (c) ? c : '.');
       putchar (c2);
     }
   putchar ('<');
 }
 
 static void
-print_named_ascii (size_t n_bytes, void const *block,
-                  const char *unused_fmt_string ATTRIBUTE_UNUSED)
+print_named_ascii (size_t fields, size_t blank, void const *block,
+                   const char *unused_fmt_string ATTRIBUTE_UNUSED,
+                   int width, int pad)
 {
   unsigned char const *p = block;
   size_t i;
-  for (i = n_bytes; i > 0; i--)
+  int pad_remaining = pad;
+  for (i = fields; blank < i; i--)
     {
+      int next_pad = pad * (i - 1) / fields;
       int masked_c = *p++ & 0x7f;
       const char *s;
-      char buf[5];
+      char buf[2];
 
       if (masked_c == 127)
-       s = "del";
+        s = "del";
       else if (masked_c <= 040)
-       s = charname[masked_c];
+        s = charname[masked_c];
       else
-       {
-         sprintf (buf, "  %c", masked_c);
-         s = buf;
-       }
-
-      printf (" %3s", s);
+        {
+          buf[0] = masked_c;
+          buf[1] = 0;
+          s = buf;
+        }
+
+      xprintf ("%*s", pad_remaining - next_pad + width, s);
+      pad_remaining = next_pad;
     }
 }
 
 static void
-print_ascii (size_t n_bytes, void const *block,
-            const char *unused_fmt_string ATTRIBUTE_UNUSED)
+print_ascii (size_t fields, size_t blank, void const *block,
+             const char *unused_fmt_string ATTRIBUTE_UNUSED, int width,
+             int pad)
 {
   unsigned char const *p = block;
   size_t i;
-  for (i = n_bytes; i > 0; i--)
+  int pad_remaining = pad;
+  for (i = fields; blank < i; i--)
     {
+      int next_pad = pad * (i - 1) / fields;
       unsigned char c = *p++;
       const char *s;
-      char buf[5];
+      char buf[4];
 
       switch (c)
-       {
-       case '\0':
-         s = " \\0";
-         break;
-
-       case '\a':
-         s = " \\a";
-         break;
-
-       case '\b':
-         s = " \\b";
-         break;
-
-       case '\f':
-         s = " \\f";
-         break;
-
-       case '\n':
-         s = " \\n";
-         break;
-
-       case '\r':
-         s = " \\r";
-         break;
-
-       case '\t':
-         s = " \\t";
-         break;
-
-       case '\v':
-         s = " \\v";
-         break;
-
-       default:
-         sprintf (buf, (ISPRINT (c) ? "  %c" : "%03o"), c);
-         s = buf;
-       }
-
-      printf (" %3s", s);
+        {
+        case '\0':
+          s = "\\0";
+          break;
+
+        case '\a':
+          s = "\\a";
+          break;
+
+        case '\b':
+          s = "\\b";
+          break;
+
+        case '\f':
+          s = "\\f";
+          break;
+
+        case '\n':
+          s = "\\n";
+          break;
+
+        case '\r':
+          s = "\\r";
+          break;
+
+        case '\t':
+          s = "\\t";
+          break;
+
+        case '\v':
+          s = "\\v";
+          break;
+
+        default:
+          sprintf (buf, (isprint (c) ? "%c" : "%03o"), c);
+          s = buf;
+        }
+
+      xprintf ("%*s", pad_remaining - next_pad + width, s);
+      pad_remaining = next_pad;
     }
 }
 
@@ -591,7 +559,7 @@ simple_strtoul (const char *s, const char **p, unsigned long int *val)
     {
       int c = *s++ - '0';
       if (sum > (ULONG_MAX - c) / 10)
-       return false;
+        return false;
       sum = sum * 10 + c;
     }
   *p = s;
@@ -609,25 +577,27 @@ simple_strtoul (const char *s, const char **p, unsigned long int *val)
        fmt = SIGNED_DECIMAL;
        size = INT or LONG; (whichever integral_type_size[4] resolves to)
        print_function = print_int; (assuming size == INT)
-       fmt_string = "%011d%c";
+       field_width = 11;
+       fmt_string = "%*d";
       }
+   pad_width is determined later, but is at least as large as the
+   number of fields printed per row.
    S_ORIG is solely for reporting errors.  It should be the full format
    string argument.
    */
 
 static bool
 decode_one_format (const char *s_orig, const char *s, const char **next,
-                  struct tspec *tspec)
+                   struct tspec *tspec)
 {
   enum size_spec size_spec;
   unsigned long int size;
   enum output_format fmt;
-  const char *pre_fmt_string;
-  void (*print_function) (size_t, void const *, char const *);
+  void (*print_function) (size_t, size_t, void const *, char const *,
+                          int, int);
   const char *p;
   char c;
   int field_width;
-  int precision;
 
   assert (tspec != NULL);
 
@@ -640,51 +610,52 @@ decode_one_format (const char *s_orig, const char *s, const char **next,
       c = *s;
       ++s;
       switch (*s)
-       {
-       case 'C':
-         ++s;
-         size = sizeof (char);
-         break;
-
-       case 'S':
-         ++s;
-         size = sizeof (short int);
-         break;
-
-       case 'I':
-         ++s;
-         size = sizeof (int);
-         break;
-
-       case 'L':
-         ++s;
-         size = sizeof (long int);
-         break;
-
-       default:
-         if (! simple_strtoul (s, &p, &size))
-           {
-             /* The integer at P in S would overflow an unsigned long int.
-                A digit string that long is sufficiently odd looking
-                that the following diagnostic is sufficient.  */
-             error (0, 0, _("invalid type string %s"), quote (s_orig));
-             return false;
-           }
-         if (p == s)
-           size = sizeof (int);
-         else
-           {
-             if (MAX_INTEGRAL_TYPE_SIZE < size
-                 || integral_type_size[size] == NO_SIZE)
-               {
-                 error (0, 0, _("invalid type string %s;\n\
-this system doesn't provide a %lu-byte integral type"), quote (s_orig), size);
-                 return false;
-               }
-             s = p;
-           }
-         break;
-       }
+        {
+        case 'C':
+          ++s;
+          size = sizeof (char);
+          break;
+
+        case 'S':
+          ++s;
+          size = sizeof (short int);
+          break;
+
+        case 'I':
+          ++s;
+          size = sizeof (int);
+          break;
+
+        case 'L':
+          ++s;
+          size = sizeof (long int);
+          break;
+
+        default:
+          if (! simple_strtoul (s, &p, &size))
+            {
+              /* The integer at P in S would overflow an unsigned long int.
+                 A digit string that long is sufficiently odd looking
+                 that the following diagnostic is sufficient.  */
+              error (0, 0, _("invalid type string %s"), quote (s_orig));
+              return false;
+            }
+          if (p == s)
+            size = sizeof (int);
+          else
+            {
+              if (MAX_INTEGRAL_TYPE_SIZE < size
+                  || integral_type_size[size] == NO_SIZE)
+                {
+                  error (0, 0, _("invalid type string %s;\nthis system"
+                                 " doesn't provide a %lu-byte integral type"),
+                         quote (s_orig), size);
+                  return false;
+                }
+              s = p;
+            }
+          break;
+        }
 
 #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format)     \
   ((Spec) == LONG_LONG ? (Max_format)                                  \
@@ -694,149 +665,149 @@ this system doesn't provide a %lu-byte integral type"), quote (s_orig), size);
       size_spec = integral_type_size[size];
 
       switch (c)
-       {
-       case 'd':
-         fmt = SIGNED_DECIMAL;
-         sprintf (tspec->fmt_string, " %%%d%s",
-                  (field_width = bytes_to_signed_dec_digits[size]),
-                  ISPEC_TO_FORMAT (size_spec, "d", "ld", PRIdMAX));
-         break;
-
-       case 'o':
-         fmt = OCTAL;
-         sprintf (tspec->fmt_string, " %%0%d%s",
-                  (field_width = bytes_to_oct_digits[size]),
-                  ISPEC_TO_FORMAT (size_spec, "o", "lo", PRIoMAX));
-         break;
-
-       case 'u':
-         fmt = UNSIGNED_DECIMAL;
-         sprintf (tspec->fmt_string, " %%%d%s",
-                  (field_width = bytes_to_unsigned_dec_digits[size]),
-                  ISPEC_TO_FORMAT (size_spec, "u", "lu", PRIuMAX));
-         break;
-
-       case 'x':
-         fmt = HEXADECIMAL;
-         sprintf (tspec->fmt_string, " %%0%d%s",
-                  (field_width = bytes_to_hex_digits[size]),
-                  ISPEC_TO_FORMAT (size_spec, "x", "lx", PRIxMAX));
-         break;
-
-       default:
-         abort ();
-       }
+        {
+        case 'd':
+          fmt = SIGNED_DECIMAL;
+          field_width = bytes_to_signed_dec_digits[size];
+          sprintf (tspec->fmt_string, "%%*%s",
+                   ISPEC_TO_FORMAT (size_spec, "d", "ld", PRIdMAX));
+          break;
+
+        case 'o':
+          fmt = OCTAL;
+          sprintf (tspec->fmt_string, "%%*.%d%s",
+                   (field_width = bytes_to_oct_digits[size]),
+                   ISPEC_TO_FORMAT (size_spec, "o", "lo", PRIoMAX));
+          break;
+
+        case 'u':
+          fmt = UNSIGNED_DECIMAL;
+          field_width = bytes_to_unsigned_dec_digits[size];
+          sprintf (tspec->fmt_string, "%%*%s",
+                   ISPEC_TO_FORMAT (size_spec, "u", "lu", PRIuMAX));
+          break;
+
+        case 'x':
+          fmt = HEXADECIMAL;
+          sprintf (tspec->fmt_string, "%%*.%d%s",
+                   (field_width = bytes_to_hex_digits[size]),
+                   ISPEC_TO_FORMAT (size_spec, "x", "lx", PRIxMAX));
+          break;
+
+        default:
+          abort ();
+        }
 
       assert (strlen (tspec->fmt_string) < FMT_BYTES_ALLOCATED);
 
       switch (size_spec)
-       {
-       case CHAR:
-         print_function = (fmt == SIGNED_DECIMAL
-                           ? print_s_char
-                           : print_char);
-         break;
-
-       case SHORT:
-         print_function = (fmt == SIGNED_DECIMAL
-                           ? print_s_short
-                           : print_short);
-         break;
-
-       case INT:
-         print_function = print_int;
-         break;
-
-       case LONG:
-         print_function = print_long;
-         break;
-
-       case LONG_LONG:
-         print_function = print_long_long;
-         break;
-
-       default:
-         abort ();
-       }
+        {
+        case CHAR:
+          print_function = (fmt == SIGNED_DECIMAL
+                            ? print_s_char
+                            : print_char);
+          break;
+
+        case SHORT:
+          print_function = (fmt == SIGNED_DECIMAL
+                            ? print_s_short
+                            : print_short);
+          break;
+
+        case INT:
+          print_function = print_int;
+          break;
+
+        case LONG:
+          print_function = print_long;
+          break;
+
+        case LONG_LONG:
+          print_function = print_long_long;
+          break;
+
+        default:
+          abort ();
+        }
       break;
 
     case 'f':
       fmt = FLOATING_POINT;
       ++s;
       switch (*s)
-       {
-       case 'F':
-         ++s;
-         size = sizeof (float);
-         break;
-
-       case 'D':
-         ++s;
-         size = sizeof (double);
-         break;
-
-       case 'L':
-         ++s;
-         size = sizeof (LONG_DOUBLE);
-         break;
-
-       default:
-         if (! simple_strtoul (s, &p, &size))
-           {
-             /* The integer at P in S would overflow an unsigned long int.
-                A digit string that long is sufficiently odd looking
-                that the following diagnostic is sufficient.  */
-             error (0, 0, _("invalid type string %s"), quote (s_orig));
-             return false;
-           }
-         if (p == s)
-           size = sizeof (double);
-         else
-           {
-             if (size > MAX_FP_TYPE_SIZE
-                 || fp_type_size[size] == NO_SIZE)
-               {
-                 error (0, 0, _("invalid type string %s;\n\
-this system doesn't provide a %lu-byte floating point type"),
-                        quote (s_orig), size);
-                 return false;
-               }
-             s = p;
-           }
-         break;
-       }
+        {
+        case 'F':
+          ++s;
+          size = sizeof (float);
+          break;
+
+        case 'D':
+          ++s;
+          size = sizeof (double);
+          break;
+
+        case 'L':
+          ++s;
+          size = sizeof (long double);
+          break;
+
+        default:
+          if (! simple_strtoul (s, &p, &size))
+            {
+              /* The integer at P in S would overflow an unsigned long int.
+                 A digit string that long is sufficiently odd looking
+                 that the following diagnostic is sufficient.  */
+              error (0, 0, _("invalid type string %s"), quote (s_orig));
+              return false;
+            }
+          if (p == s)
+            size = sizeof (double);
+          else
+            {
+              if (size > MAX_FP_TYPE_SIZE
+                  || fp_type_size[size] == NO_SIZE)
+                {
+                  error (0, 0,
+                         _("invalid type string %s;\n"
+                           "this system doesn't provide a %lu-byte"
+                           " floating point type"),
+                         quote (s_orig), size);
+                  return false;
+                }
+              s = p;
+            }
+          break;
+        }
       size_spec = fp_type_size[size];
 
-      switch (size_spec)
-       {
-       case FLOAT_SINGLE:
-         print_function = print_float;
-         /* Don't use %#e; not all systems support it.  */
-         pre_fmt_string = " %%%d.%de";
-         precision = FLT_DIG;
-         break;
-
-       case FLOAT_DOUBLE:
-         print_function = print_double;
-         pre_fmt_string = " %%%d.%de";
-         precision = DBL_DIG;
-         break;
-
-#ifdef HAVE_LONG_DOUBLE
-       case FLOAT_LONG_DOUBLE:
-         print_function = print_long_double;
-         pre_fmt_string = " %%%d.%dLe";
-         precision = LDBL_DIG;
-         break;
-#endif
-
-       default:
-         abort ();
-       }
-
-      field_width = precision + 8;
-      sprintf (tspec->fmt_string, pre_fmt_string, field_width, precision);
-      break;
+      {
+        struct lconv const *locale = localeconv ();
+        size_t decimal_point_len =
+          (locale->decimal_point[0] ? strlen (locale->decimal_point) : 1);
+
+        switch (size_spec)
+          {
+          case FLOAT_SINGLE:
+            print_function = print_float;
+            field_width = FLT_STRLEN_BOUND_L (decimal_point_len);
+            break;
+
+          case FLOAT_DOUBLE:
+            print_function = print_double;
+            field_width = DBL_STRLEN_BOUND_L (decimal_point_len);
+            break;
+
+          case FLOAT_LONG_DOUBLE:
+            print_function = print_long_double;
+            field_width = LDBL_STRLEN_BOUND_L (decimal_point_len);
+            break;
+
+          default:
+            abort ();
+          }
+
+        break;
+      }
 
     case 'a':
       ++s;
@@ -855,8 +826,8 @@ this system doesn't provide a %lu-byte floating point type"),
       break;
 
     default:
-      error (0, 0, _("invalid character `%c' in type string %s"),
-            *s, quote (s_orig));
+      error (0, 0, _("invalid character '%c' in type string %s"),
+             *s, quote (s_orig));
       return false;
     }
 
@@ -891,30 +862,31 @@ open_next_file (void)
     {
       input_filename = *file_list;
       if (input_filename == NULL)
-       return ok;
+        return ok;
       ++file_list;
 
       if (STREQ (input_filename, "-"))
-       {
-         input_filename = _("standard input");
-         in_stream = stdin;
-         have_read_stdin = true;
-       }
+        {
+          input_filename = _("standard input");
+          in_stream = stdin;
+          have_read_stdin = true;
+          if (O_BINARY && ! isatty (STDIN_FILENO))
+            xfreopen (NULL, "rb", stdin);
+        }
       else
-       {
-         in_stream = fopen (input_filename, "r");
-         if (in_stream == NULL)
-           {
-             error (0, errno, "%s", input_filename);
-             ok = false;
-           }
-       }
+        {
+          in_stream = fopen (input_filename, (O_BINARY ? "rb" : "r"));
+          if (in_stream == NULL)
+            {
+              error (0, errno, "%s", input_filename);
+              ok = false;
+            }
+        }
     }
   while (in_stream == NULL);
 
-  if (limit_bytes_to_format & !flag_dump_strings)
-    SETVBUF (in_stream, NULL, _IONBF, 0);
-  SET_BINARY (fileno (in_stream));
+  if (limit_bytes_to_format && !flag_dump_strings)
+    setvbuf (in_stream, NULL, _IONBF, 0);
 
   return ok;
 }
@@ -934,17 +906,17 @@ check_and_close (int in_errno)
   if (in_stream != NULL)
     {
       if (ferror (in_stream))
-       {
-         error (0, in_errno, _("%s: read error"), input_filename);
-         if (! STREQ (file_list[-1], "-"))
-           fclose (in_stream);
-         ok = false;
-       }
+        {
+          error (0, in_errno, _("%s: read error"), input_filename);
+          if (! STREQ (file_list[-1], "-"))
+            fclose (in_stream);
+          ok = false;
+        }
       else if (! STREQ (file_list[-1], "-") && fclose (in_stream) != 0)
-       {
-         error (0, errno, "%s", input_filename);
-         ok = false;
-       }
+        {
+          error (0, errno, "%s", input_filename);
+          ok = false;
+        }
 
       in_stream = NULL;
     }
@@ -973,10 +945,10 @@ decode_format_string (const char *s)
       const char *next;
 
       if (n_specs_allocated <= n_specs)
-       spec = x2nrealloc (spec, &n_specs_allocated, sizeof *spec);
+        spec = X2NREALLOC (spec, &n_specs_allocated);
 
       if (! decode_one_format (s_orig, s, &next, &spec[n_specs]))
-       return false;
+        return false;
 
       assert (s != next);
       s = next;
@@ -1007,71 +979,71 @@ skip (uintmax_t n_skip)
       struct stat file_stats;
 
       /* First try seeking.  For large offsets, this extra work is
-        worthwhile.  If the offset is below some threshold it may be
-        more efficient to move the pointer by reading.  There are two
-        issues when trying to seek:
-          - the file must be seekable.
-          - before seeking to the specified position, make sure
-            that the new position is in the current file.
-            Try to do that by getting file's size using fstat.
-            But that will work only for regular files.  */
+         worthwhile.  If the offset is below some threshold it may be
+         more efficient to move the pointer by reading.  There are two
+         issues when trying to seek:
+           - the file must be seekable.
+           - before seeking to the specified position, make sure
+             that the new position is in the current file.
+             Try to do that by getting file's size using fstat.
+             But that will work only for regular files.  */
 
       if (fstat (fileno (in_stream), &file_stats) == 0)
-       {
-         /* The st_size field is valid only for regular files
-            (and for symbolic links, which cannot occur here).
-            If the number of bytes left to skip is at least
-            as large as the size of the current file, we can
-            decrement n_skip and go on to the next file.  */
-
-         if (S_ISREG (file_stats.st_mode) && 0 <= file_stats.st_size)
-           {
-             if ((uintmax_t) file_stats.st_size <= n_skip)
-               n_skip -= file_stats.st_size;
-             else
-               {
-                 if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
-                   {
-                     in_errno = errno;
-                     ok = false;
-                   }
-                 n_skip = 0;
-               }
-           }
-
-         /* If it's not a regular file with nonnegative size,
-            position the file pointer by reading.  */
-
-         else
-           {
-             char buf[BUFSIZ];
-             size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
-
-             while (0 < n_skip)
-               {
-                 if (n_skip < n_bytes_to_read)
-                   n_bytes_to_read = n_skip;
-                 n_bytes_read = fread (buf, 1, n_bytes_to_read, in_stream);
-                 n_skip -= n_bytes_read;
-                 if (n_bytes_read != n_bytes_to_read)
-                   {
-                     in_errno = errno;
-                     ok = false;
-                     n_skip = 0;
-                     break;
-                   }
-               }
-           }
-
-         if (n_skip == 0)
-           break;
-       }
+        {
+          /* The st_size field is valid for regular files.
+             If the number of bytes left to skip is larger than
+             the size of the current file, we can decrement n_skip
+             and go on to the next file.  Skip this optimization also
+             when st_size is 0, because some kernels report that
+             nonempty files in /proc have st_size == 0.  */
+          if (S_ISREG (file_stats.st_mode) && 0 < file_stats.st_size)
+            {
+              if ((uintmax_t) file_stats.st_size < n_skip)
+                n_skip -= file_stats.st_size;
+              else
+                {
+                  if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
+                    {
+                      in_errno = errno;
+                      ok = false;
+                    }
+                  n_skip = 0;
+                }
+            }
+
+          /* If it's not a regular file with nonnegative size,
+             position the file pointer by reading.  */
+
+          else
+            {
+              char buf[BUFSIZ];
+              size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
+
+              while (0 < n_skip)
+                {
+                  if (n_skip < n_bytes_to_read)
+                    n_bytes_to_read = n_skip;
+                  n_bytes_read = fread (buf, 1, n_bytes_to_read, in_stream);
+                  n_skip -= n_bytes_read;
+                  if (n_bytes_read != n_bytes_to_read)
+                    {
+                      in_errno = errno;
+                      ok = false;
+                      n_skip = 0;
+                      break;
+                    }
+                }
+            }
+
+          if (n_skip == 0)
+            break;
+        }
 
       else   /* cannot fstat() file */
-       {
-         error (0, errno, "%s", input_filename);
-         ok = false;
-       }
+        {
+          error (0, errno, "%s", input_filename);
+          ok = false;
+        }
 
       ok &= check_and_close (in_errno);
 
@@ -1085,7 +1057,8 @@ skip (uintmax_t n_skip)
 }
 
 static void
-format_address_none (uintmax_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
+format_address_none (uintmax_t address ATTRIBUTE_UNUSED,
+                     char c ATTRIBUTE_UNUSED)
 {
 }
 
@@ -1106,19 +1079,19 @@ format_address_std (uintmax_t address, char c)
     {
     case 8:
       do
-       *--p = '0' + (address & 7);
+        *--p = '0' + (address & 7);
       while ((address >>= 3) != 0);
       break;
 
     case 10:
       do
-       *--p = '0' + (address % 10);
+        *--p = '0' + (address % 10);
       while ((address /= 10) != 0);
       break;
 
     case 16:
       do
-       *--p = "0123456789abcdef"[address & 15];
+        *--p = "0123456789abcdef"[address & 15];
       while ((address >>= 4) != 0);
       break;
     }
@@ -1153,32 +1126,31 @@ format_address_label (uintmax_t address, char c)
    for a sequence of identical input blocks is the output for the first
    block followed by an asterisk alone on a line.  It is valid to compare
    the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
-   That condition may be false only for the last input block -- and then
-   only when it has not been padded to length BYTES_PER_BLOCK.  */
+   That condition may be false only for the last input block.  */
 
 static void
 write_block (uintmax_t current_offset, size_t n_bytes,
-            const char *prev_block, const char *curr_block)
+             const char *prev_block, const char *curr_block)
 {
   static bool first = true;
   static bool prev_pair_equal = false;
 
-#define EQUAL_BLOCKS(b1, b2) (memcmp ((b1), (b2), bytes_per_block) == 0)
+#define EQUAL_BLOCKS(b1, b2) (memcmp (b1, b2, bytes_per_block) == 0)
 
   if (abbreviate_duplicate_blocks
       && !first && n_bytes == bytes_per_block
       && EQUAL_BLOCKS (prev_block, curr_block))
     {
       if (prev_pair_equal)
-       {
-         /* The two preceding blocks were equal, and the current
-            block is the same as the last one, so print nothing.  */
-       }
+        {
+          /* The two preceding blocks were equal, and the current
+             block is the same as the last one, so print nothing.  */
+        }
       else
-       {
-         printf ("*\n");
-         prev_pair_equal = true;
-       }
+        {
+          printf ("*\n");
+          prev_pair_equal = true;
+        }
     }
   else
     {
@@ -1186,23 +1158,28 @@ write_block (uintmax_t current_offset, size_t n_bytes,
 
       prev_pair_equal = false;
       for (i = 0; i < n_specs; i++)
-       {
-         if (i == 0)
-           format_address (current_offset, '\0');
-         else
-           printf ("%*s", address_pad_len, "");
-         (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
-         if (spec[i].hexl_mode_trailer)
-           {
-             /* space-pad out to full line width, then dump the trailer */
-             int datum_width = width_bytes[spec[i].size];
-             int blank_fields = (bytes_per_block - n_bytes) / datum_width;
-             int field_width = spec[i].field_width + 1;
-             printf ("%*s", blank_fields * field_width, "");
-             dump_hexl_mode_trailer (n_bytes, curr_block);
-           }
-         putchar ('\n');
-       }
+        {
+          int datum_width = width_bytes[spec[i].size];
+          int fields_per_block = bytes_per_block / datum_width;
+          int blank_fields = (bytes_per_block - n_bytes) / datum_width;
+          if (i == 0)
+            format_address (current_offset, '\0');
+          else
+            printf ("%*s", address_pad_len, "");
+          (*spec[i].print_function) (fields_per_block, blank_fields,
+                                     curr_block, spec[i].fmt_string,
+                                     spec[i].field_width, spec[i].pad_width);
+          if (spec[i].hexl_mode_trailer)
+            {
+              /* space-pad out to full line width, then dump the trailer */
+              int field_width = spec[i].field_width;
+              int pad_width = (spec[i].pad_width * blank_fields
+                               / fields_per_block);
+              printf ("%*s", blank_fields * field_width + pad_width, "");
+              dump_hexl_mode_trailer (n_bytes, curr_block);
+            }
+          putchar ('\n');
+        }
     }
   first = false;
 }
@@ -1230,7 +1207,7 @@ read_char (int *c)
       *c = fgetc (in_stream);
 
       if (*c != EOF)
-       break;
+        break;
 
       ok &= check_and_close (errno);
 
@@ -1277,7 +1254,7 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
       *n_bytes_in_buffer += n_read;
 
       if (n_read == n_needed)
-       break;
+        break;
 
       ok &= check_and_close (errno);
 
@@ -1290,7 +1267,7 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
 /* Return the least common multiple of the sizes associated
    with the format specs.  */
 
-static int
+static int _GL_ATTRIBUTE_PURE
 get_lcm (void)
 {
   size_t i;
@@ -1316,17 +1293,17 @@ parse_old_offset (const char *s, uintmax_t *offset)
   if (s[0] == '+')
     ++s;
 
-  /* Determine the radix we'll use to interpret S.  If there is a `.',
-     it's decimal, otherwise, if the string begins with `0X'or `0x',
+  /* Determine the radix we'll use to interpret S.  If there is a '.',
+     it's decimal, otherwise, if the string begins with '0X'or '0x',
      it's hexadecimal, else octal.  */
   if (strchr (s, '.') != NULL)
     radix = 10;
   else
     {
       if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
-       radix = 16;
+        radix = 16;
       else
-       radix = 8;
+        radix = 8;
     }
 
   return xstrtoumax (s, NULL, radix, offset, "Bb") == LONGINT_OK;
@@ -1361,38 +1338,38 @@ dump (void)
   if (limit_bytes_to_format)
     {
       while (1)
-       {
-         size_t n_needed;
-         if (current_offset >= end_offset)
-           {
-             n_bytes_read = 0;
-             break;
-           }
-         n_needed = MIN (end_offset - current_offset,
-                         (uintmax_t) bytes_per_block);
-         ok &= read_block (n_needed, block[idx], &n_bytes_read);
-         if (n_bytes_read < bytes_per_block)
-           break;
-         assert (n_bytes_read == bytes_per_block);
-         write_block (current_offset, n_bytes_read,
-                      block[!idx], block[idx]);
-         current_offset += n_bytes_read;
-         idx = !idx;
-       }
+        {
+          size_t n_needed;
+          if (current_offset >= end_offset)
+            {
+              n_bytes_read = 0;
+              break;
+            }
+          n_needed = MIN (end_offset - current_offset,
+                          (uintmax_t) bytes_per_block);
+          ok &= read_block (n_needed, block[idx], &n_bytes_read);
+          if (n_bytes_read < bytes_per_block)
+            break;
+          assert (n_bytes_read == bytes_per_block);
+          write_block (current_offset, n_bytes_read,
+                       block[!idx], block[idx]);
+          current_offset += n_bytes_read;
+          idx = !idx;
+        }
     }
   else
     {
       while (1)
-       {
-         ok &= read_block (bytes_per_block, block[idx], &n_bytes_read);
-         if (n_bytes_read < bytes_per_block)
-           break;
-         assert (n_bytes_read == bytes_per_block);
-         write_block (current_offset, n_bytes_read,
-                      block[!idx], block[idx]);
-         current_offset += n_bytes_read;
-         idx = !idx;
-       }
+        {
+          ok &= read_block (bytes_per_block, block[idx], &n_bytes_read);
+          if (n_bytes_read < bytes_per_block)
+            break;
+          assert (n_bytes_read == bytes_per_block);
+          write_block (current_offset, n_bytes_read,
+                       block[!idx], block[idx]);
+          current_offset += n_bytes_read;
+          idx = !idx;
+        }
     }
 
   if (n_bytes_read > 0)
@@ -1402,13 +1379,12 @@ dump (void)
 
       l_c_m = get_lcm ();
 
-      /* Make bytes_to_write the smallest multiple of l_c_m that
-        is at least as large as n_bytes_read.  */
+      /* Ensure zero-byte padding up to the smallest multiple of l_c_m that
+         is at least as large as n_bytes_read.  */
       bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
 
       memset (block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
-      write_block (current_offset, bytes_to_write,
-                  block[!idx], block[idx]);
+      write_block (current_offset, n_bytes_read, block[!idx], block[idx]);
       current_offset += n_bytes_read;
     }
 
@@ -1423,7 +1399,7 @@ dump (void)
 }
 
 /* STRINGS mode.  Find each "string constant" in the input.
-   A string constant is a run of at least `string_min' ASCII
+   A string constant is a run of at least 'string_min' ASCII
    graphic (or formatting) characters terminated by a null.
    Based on a function written by Richard Stallman for a
    traditional version of od.  Return true if successful.  */
@@ -1441,91 +1417,91 @@ dump_strings (void)
       size_t i;
       int c;
 
-      /* See if the next `string_min' chars are all printing chars.  */
+      /* See if the next 'string_min' chars are all printing chars.  */
     tryline:
 
       if (limit_bytes_to_format
-         && (end_offset < string_min || end_offset - string_min <= address))
-       break;
+          && (end_offset < string_min || end_offset - string_min <= address))
+        break;
 
       for (i = 0; i < string_min; i++)
-       {
-         ok &= read_char (&c);
-         address++;
-         if (c < 0)
-           {
-             free (buf);
-             return ok;
-           }
-         if (!ISPRINT (c))
-           /* Found a non-printing.  Try again starting with next char.  */
-           goto tryline;
-         buf[i] = c;
-       }
-
-      /* We found a run of `string_min' printable characters.
-        Now see if it is terminated with a null byte.  */
+        {
+          ok &= read_char (&c);
+          address++;
+          if (c < 0)
+            {
+              free (buf);
+              return ok;
+            }
+          if (! isprint (c))
+            /* Found a non-printing.  Try again starting with next char.  */
+            goto tryline;
+          buf[i] = c;
+        }
+
+      /* We found a run of 'string_min' printable characters.
+         Now see if it is terminated with a null byte.  */
       while (!limit_bytes_to_format || address < end_offset)
-       {
-         if (i == bufsize)
-           {
-             buf = X2REALLOC (buf, &bufsize);
-           }
-         ok &= read_char (&c);
-         address++;
-         if (c < 0)
-           {
-             free (buf);
-             return ok;
-           }
-         if (c == '\0')
-           break;              /* It is; print this string.  */
-         if (!ISPRINT (c))
-           goto tryline;       /* It isn't; give up on this string.  */
-         buf[i++] = c;         /* String continues; store it all.  */
-       }
+        {
+          if (i == bufsize)
+            {
+              buf = X2REALLOC (buf, &bufsize);
+            }
+          ok &= read_char (&c);
+          address++;
+          if (c < 0)
+            {
+              free (buf);
+              return ok;
+            }
+          if (c == '\0')
+            break;             /* It is; print this string.  */
+          if (! isprint (c))
+            goto tryline;      /* It isn't; give up on this string.  */
+          buf[i++] = c;                /* String continues; store it all.  */
+        }
 
       /* If we get here, the string is all printable and null-terminated,
-        so print it.  It is all in `buf' and `i' is its length.  */
+         so print it.  It is all in 'buf' and 'i' is its length.  */
       buf[i] = 0;
       format_address (address - i - 1, ' ');
 
       for (i = 0; (c = buf[i]); i++)
-       {
-         switch (c)
-           {
-           case '\a':
-             fputs ("\\a", stdout);
-             break;
-
-           case '\b':
-             fputs ("\\b", stdout);
-             break;
-
-           case '\f':
-             fputs ("\\f", stdout);
-             break;
-
-           case '\n':
-             fputs ("\\n", stdout);
-             break;
-
-           case '\r':
-             fputs ("\\r", stdout);
-             break;
-
-           case '\t':
-             fputs ("\\t", stdout);
-             break;
-
-           case '\v':
-             fputs ("\\v", stdout);
-             break;
-
-           default:
-             putc (c, stdout);
-           }
-       }
+        {
+          switch (c)
+            {
+            case '\a':
+              fputs ("\\a", stdout);
+              break;
+
+            case '\b':
+              fputs ("\\b", stdout);
+              break;
+
+            case '\f':
+              fputs ("\\f", stdout);
+              break;
+
+            case '\n':
+              fputs ("\\n", stdout);
+              break;
+
+            case '\r':
+              fputs ("\\r", stdout);
+              break;
+
+            case '\t':
+              fputs ("\\t", stdout);
+              break;
+
+            case '\v':
+              fputs ("\\v", stdout);
+              break;
+
+            default:
+              putc (c, stdout);
+            }
+        }
       putchar ('\n');
     }
 
@@ -1541,21 +1517,22 @@ dump_strings (void)
 int
 main (int argc, char **argv)
 {
-  int c;
   int n_files;
   size_t i;
   int l_c_m;
-  size_t desired_width IF_LINT (= 0);
+  size_t desired_width IF_LINT ( = 0);
   bool modern = false;
   bool width_specified = false;
   bool ok = true;
+  size_t width_per_block = 0;
+  static char const multipliers[] = "bEGKkMmPTYZ0";
 
-  /* The old-style `pseudo starting address' to be printed in parentheses
+  /* The old-style 'pseudo starting address' to be printed in parentheses
      after any true address.  */
-  uintmax_t pseudo_start IF_LINT (= 0);
+  uintmax_t pseudo_start IF_LINT ( = 0);
 
   initialize_main (&argc, &argv);
-  program_name = argv[0];
+  set_program_name (argv[0]);
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, LOCALEDIR);
   textdomain (PACKAGE);
@@ -1569,20 +1546,20 @@ main (int argc, char **argv)
   integral_type_size[sizeof (short int)] = SHORT;
   integral_type_size[sizeof (int)] = INT;
   integral_type_size[sizeof (long int)] = LONG;
-#if HAVE_UNSIGNED_LONG_LONG
-  /* If `long int' and `long long int' have the same size, it's fine
-     to overwrite the entry for `long' with this one.  */
-  integral_type_size[sizeof (ulonglong_t)] = LONG_LONG;
+#if HAVE_UNSIGNED_LONG_LONG_INT
+  /* If 'long int' and 'long long int' have the same size, it's fine
+     to overwrite the entry for 'long' with this one.  */
+  integral_type_size[sizeof (unsigned_long_long_int)] = LONG_LONG;
 #endif
 
   for (i = 0; i <= MAX_FP_TYPE_SIZE; i++)
     fp_type_size[i] = NO_SIZE;
 
   fp_type_size[sizeof (float)] = FLOAT_SINGLE;
-  /* The array entry for `double' is filled in after that for LONG_DOUBLE
-     so that if `long double' is the same type or if long double isn't
-     supported FLOAT_LONG_DOUBLE will never be used.  */
-  fp_type_size[sizeof (LONG_DOUBLE)] = FLOAT_LONG_DOUBLE;
+  /* The array entry for 'double' is filled in after that for 'long double'
+     so that if they are the same size, we avoid any overhead of
+     long double computation in libc.  */
+  fp_type_size[sizeof (long double)] = FLOAT_LONG_DOUBLE;
   fp_type_size[sizeof (double)] = FLOAT_DOUBLE;
 
   n_specs = 0;
@@ -1594,157 +1571,161 @@ main (int argc, char **argv)
   address_pad_len = 7;
   flag_dump_strings = false;
 
-  while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
-        != -1)
+  while (true)
     {
       uintmax_t tmp;
       enum strtol_error s_err;
+      int oi = -1;
+      int c = getopt_long (argc, argv, short_options, long_options, &oi);
+      if (c == -1)
+        break;
 
       switch (c)
-       {
-       case 'A':
-         modern = true;
-         switch (optarg[0])
-           {
-           case 'd':
-             format_address = format_address_std;
-             address_base = 10;
-             address_pad_len = 7;
-             break;
-           case 'o':
-             format_address = format_address_std;
-             address_base = 8;
-             address_pad_len = 7;
-             break;
-           case 'x':
-             format_address = format_address_std;
-             address_base = 16;
-             address_pad_len = 6;
-             break;
-           case 'n':
-             format_address = format_address_none;
-             address_pad_len = 0;
-             break;
-           default:
-             error (EXIT_FAILURE, 0,
-                    _("invalid output address radix `%c'; \
-it must be one character from [doxn]"),
-                    optarg[0]);
-             break;
-           }
-         break;
-
-       case 'j':
-         modern = true;
-         s_err = xstrtoumax (optarg, NULL, 0, &n_bytes_to_skip, "bkm");
-         if (s_err != LONGINT_OK)
-           STRTOL_FATAL_ERROR (optarg, _("skip argument"), s_err);
-         break;
-
-       case 'N':
-         modern = true;
-         limit_bytes_to_format = true;
-
-         s_err = xstrtoumax (optarg, NULL, 0, &max_bytes_to_format, "bkm");
-         if (s_err != LONGINT_OK)
-           STRTOL_FATAL_ERROR (optarg, _("limit argument"), s_err);
-         break;
-
-       case 'S':
-         modern = true;
-         if (optarg == NULL)
-           string_min = 3;
-         else
-           {
-             s_err = xstrtoumax (optarg, NULL, 0, &tmp, "bkm");
-             if (s_err != LONGINT_OK)
-               STRTOL_FATAL_ERROR (optarg, _("minimum string length"), s_err);
-
-             /* The minimum string length may be no larger than SIZE_MAX,
-                since we may allocate a buffer of this size.  */
-             if (SIZE_MAX < tmp)
-               error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
-
-             string_min = tmp;
-           }
-         flag_dump_strings = true;
-         break;
-
-       case 't':
-         modern = true;
-         ok &= decode_format_string (optarg);
-         break;
-
-       case 'v':
-         modern = true;
-         abbreviate_duplicate_blocks = false;
-         break;
-
-       case TRADITIONAL_OPTION:
-         traditional = true;
-         break;
-
-         /* The next several cases map the traditional format
-            specification options to the corresponding modern format
-            specs.  GNU od accepts any combination of old- and
-            new-style options.  Format specification options accumulate.
-            The obsolescent and undocumented formats are compatible
-            with FreeBSD 4.10 od.  */
+        {
+        case 'A':
+          modern = true;
+          switch (optarg[0])
+            {
+            case 'd':
+              format_address = format_address_std;
+              address_base = 10;
+              address_pad_len = 7;
+              break;
+            case 'o':
+              format_address = format_address_std;
+              address_base = 8;
+              address_pad_len = 7;
+              break;
+            case 'x':
+              format_address = format_address_std;
+              address_base = 16;
+              address_pad_len = 6;
+              break;
+            case 'n':
+              format_address = format_address_none;
+              address_pad_len = 0;
+              break;
+            default:
+              error (EXIT_FAILURE, 0,
+                     _("invalid output address radix '%c';\
+ it must be one character from [doxn]"),
+                     optarg[0]);
+              break;
+            }
+          break;
+
+        case 'j':
+          modern = true;
+          s_err = xstrtoumax (optarg, NULL, 0, &n_bytes_to_skip, multipliers);
+          if (s_err != LONGINT_OK)
+            xstrtol_fatal (s_err, oi, c, long_options, optarg);
+          break;
+
+        case 'N':
+          modern = true;
+          limit_bytes_to_format = true;
+
+          s_err = xstrtoumax (optarg, NULL, 0, &max_bytes_to_format,
+                              multipliers);
+          if (s_err != LONGINT_OK)
+            xstrtol_fatal (s_err, oi, c, long_options, optarg);
+          break;
+
+        case 'S':
+          modern = true;
+          if (optarg == NULL)
+            string_min = 3;
+          else
+            {
+              s_err = xstrtoumax (optarg, NULL, 0, &tmp, multipliers);
+              if (s_err != LONGINT_OK)
+                xstrtol_fatal (s_err, oi, c, long_options, optarg);
+
+              /* The minimum string length may be no larger than SIZE_MAX,
+                 since we may allocate a buffer of this size.  */
+              if (SIZE_MAX < tmp)
+                error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
+
+              string_min = tmp;
+            }
+          flag_dump_strings = true;
+          break;
+
+        case 't':
+          modern = true;
+          ok &= decode_format_string (optarg);
+          break;
+
+        case 'v':
+          modern = true;
+          abbreviate_duplicate_blocks = false;
+          break;
+
+        case TRADITIONAL_OPTION:
+          traditional = true;
+          break;
+
+          /* The next several cases map the traditional format
+             specification options to the corresponding modern format
+             specs.  GNU od accepts any combination of old- and
+             new-style options.  Format specification options accumulate.
+             The obsolescent and undocumented formats are compatible
+             with FreeBSD 4.10 od.  */
 
 #define CASE_OLD_ARG(old_char,new_string)              \
-       case old_char:                                  \
-         ok &= decode_format_string (new_string);      \
-         break
-
-         CASE_OLD_ARG ('a', "a");
-         CASE_OLD_ARG ('b', "o1");
-         CASE_OLD_ARG ('c', "c");
-         CASE_OLD_ARG ('D', "u4"); /* obsolescent and undocumented */
-         CASE_OLD_ARG ('d', "u2");
-       case 'F': /* obsolescent and undocumented alias */
-         CASE_OLD_ARG ('e', "fD"); /* obsolescent and undocumented */
-         CASE_OLD_ARG ('f', "fF");
-       case 'X': /* obsolescent and undocumented alias */
-         CASE_OLD_ARG ('H', "x4"); /* obsolescent and undocumented */
-         CASE_OLD_ARG ('i', "dI");
-       case 'I': case 'L': /* obsolescent and undocumented aliases */
-         CASE_OLD_ARG ('l', "dL");
-         CASE_OLD_ARG ('O', "o4"); /* obsolesent and undocumented */
-       case 'B': /* obsolescent and undocumented alias */
-         CASE_OLD_ARG ('o', "o2");
-         CASE_OLD_ARG ('s', "d2");
-       case 'h': /* obsolescent and undocumented alias */
-         CASE_OLD_ARG ('x', "x2");
+        case old_char:                                 \
+          ok &= decode_format_string (new_string);     \
+          break
+
+          CASE_OLD_ARG ('a', "a");
+          CASE_OLD_ARG ('b', "o1");
+          CASE_OLD_ARG ('c', "c");
+          CASE_OLD_ARG ('D', "u4"); /* obsolescent and undocumented */
+          CASE_OLD_ARG ('d', "u2");
+        case 'F': /* obsolescent and undocumented alias */
+          CASE_OLD_ARG ('e', "fD"); /* obsolescent and undocumented */
+          CASE_OLD_ARG ('f', "fF");
+        case 'X': /* obsolescent and undocumented alias */
+          CASE_OLD_ARG ('H', "x4"); /* obsolescent and undocumented */
+          CASE_OLD_ARG ('i', "dI");
+        case 'I': case 'L': /* obsolescent and undocumented aliases */
+          CASE_OLD_ARG ('l', "dL");
+          CASE_OLD_ARG ('O', "o4"); /* obsolesent and undocumented */
+        case 'B': /* obsolescent and undocumented alias */
+          CASE_OLD_ARG ('o', "o2");
+          CASE_OLD_ARG ('s', "d2");
+        case 'h': /* obsolescent and undocumented alias */
+          CASE_OLD_ARG ('x', "x2");
 
 #undef CASE_OLD_ARG
 
-       case 'w':
-         modern = true;
-         width_specified = true;
-         if (optarg == NULL)
-           {
-             desired_width = 32;
-           }
-         else
-           {
-             uintmax_t w_tmp;
-             s_err = xstrtoumax (optarg, NULL, 10, &w_tmp, "");
-             if (s_err != LONGINT_OK)
-               STRTOL_FATAL_ERROR (optarg, _("width specification"), s_err);
-             if (SIZE_MAX < w_tmp)
-               error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
-             desired_width = w_tmp;
-           }
-         break;
-
-       case_GETOPT_HELP_CHAR;
-
-       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
-       default:
-         usage (EXIT_FAILURE);
-         break;
-       }
+        case 'w':
+          modern = true;
+          width_specified = true;
+          if (optarg == NULL)
+            {
+              desired_width = 32;
+            }
+          else
+            {
+              uintmax_t w_tmp;
+              s_err = xstrtoumax (optarg, NULL, 10, &w_tmp, "");
+              if (s_err != LONGINT_OK)
+                xstrtol_fatal (s_err, oi, c, long_options, optarg);
+              if (SIZE_MAX < w_tmp)
+                error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
+              desired_width = w_tmp;
+            }
+          break;
+
+        case_GETOPT_HELP_CHAR;
+
+        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+        default:
+          usage (EXIT_FAILURE);
+          break;
+        }
     }
 
   if (!ok)
@@ -1752,103 +1733,103 @@ it must be one character from [doxn]"),
 
   if (flag_dump_strings && n_specs > 0)
     error (EXIT_FAILURE, 0,
-          _("no type may be specified when dumping strings"));
+           _("no type may be specified when dumping strings"));
 
   n_files = argc - optind;
 
   /* If the --traditional option is used, there may be from
      0 to 3 remaining command line arguments;  handle each case
      separately.
-       od [file] [[+]offset[.][b] [[+]label[.][b]]]
+        od [file] [[+]offset[.][b] [[+]label[.][b]]]
      The offset and label have the same syntax.
 
      If --traditional is not given, and if no modern options are
      given, and if the offset begins with + or (if there are two
      operands) a digit, accept only this form, as per POSIX:
-       od [file] [[+]offset[.][b]]
+        od [file] [[+]offset[.][b]]
   */
 
-  if (!modern | traditional)
+  if (!modern || traditional)
     {
       uintmax_t o1;
       uintmax_t o2;
 
       switch (n_files)
-       {
-       case 1:
-         if ((traditional || argv[optind][0] == '+')
-             && parse_old_offset (argv[optind], &o1))
-           {
-             n_bytes_to_skip = o1;
-             --n_files;
-             ++argv;
-           }
-         break;
-
-       case 2:
-         if ((traditional || argv[optind + 1][0] == '+'
-              || ISDIGIT (argv[optind + 1][0]))
-             && parse_old_offset (argv[optind + 1], &o2))
-           {
-             if (traditional && parse_old_offset (argv[optind], &o1))
-               {
-                 n_bytes_to_skip = o1;
-                 flag_pseudo_start = true;
-                 pseudo_start = o2;
-                 argv += 2;
-                 n_files -= 2;
-               }
-             else
-               {
-                 n_bytes_to_skip = o2;
-                 --n_files;
-                 argv[optind + 1] = argv[optind];
-                 ++argv;
-               }
-           }
-         break;
-
-       case 3:
-         if (traditional
-             && parse_old_offset (argv[optind + 1], &o1)
-             && parse_old_offset (argv[optind + 2], &o2))
-           {
-             n_bytes_to_skip = o1;
-             flag_pseudo_start = true;
-             pseudo_start = o2;
-             argv[optind + 2] = argv[optind];
-             argv += 2;
-             n_files -= 2;
-           }
-         break;
-       }
+        {
+        case 1:
+          if ((traditional || argv[optind][0] == '+')
+              && parse_old_offset (argv[optind], &o1))
+            {
+              n_bytes_to_skip = o1;
+              --n_files;
+              ++argv;
+            }
+          break;
+
+        case 2:
+          if ((traditional || argv[optind + 1][0] == '+'
+               || ISDIGIT (argv[optind + 1][0]))
+              && parse_old_offset (argv[optind + 1], &o2))
+            {
+              if (traditional && parse_old_offset (argv[optind], &o1))
+                {
+                  n_bytes_to_skip = o1;
+                  flag_pseudo_start = true;
+                  pseudo_start = o2;
+                  argv += 2;
+                  n_files -= 2;
+                }
+              else
+                {
+                  n_bytes_to_skip = o2;
+                  --n_files;
+                  argv[optind + 1] = argv[optind];
+                  ++argv;
+                }
+            }
+          break;
+
+        case 3:
+          if (traditional
+              && parse_old_offset (argv[optind + 1], &o1)
+              && parse_old_offset (argv[optind + 2], &o2))
+            {
+              n_bytes_to_skip = o1;
+              flag_pseudo_start = true;
+              pseudo_start = o2;
+              argv[optind + 2] = argv[optind];
+              argv += 2;
+              n_files -= 2;
+            }
+          break;
+        }
 
       if (traditional && 1 < n_files)
-       {
-         error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
-         error (0, 0, "%s\n",
-                _("Compatibility mode supports at most one file."));
-         usage (EXIT_FAILURE);
-       }
+        {
+          error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
+          error (0, 0, "%s",
+                 _("compatibility mode supports at most one file"));
+          usage (EXIT_FAILURE);
+        }
     }
 
   if (flag_pseudo_start)
     {
       if (format_address == format_address_none)
-       {
-         address_base = 8;
-         address_pad_len = 7;
-         format_address = format_address_paren;
-       }
+        {
+          address_base = 8;
+          address_pad_len = 7;
+          format_address = format_address_paren;
+        }
       else
-       format_address = format_address_label;
+        format_address = format_address_label;
     }
 
   if (limit_bytes_to_format)
     {
       end_offset = n_bytes_to_skip + max_bytes_to_format;
       if (end_offset < n_bytes_to_skip)
-       error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large"));
+        error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large"));
     }
 
   if (n_specs == 0)
@@ -1857,15 +1838,15 @@ it must be one character from [doxn]"),
   if (n_files > 0)
     {
       /* Set the global pointer FILE_LIST so that it
-        references the first file-argument on the command-line.  */
+         references the first file-argument on the command-line.  */
 
       file_list = (char const *const *) &argv[optind];
     }
   else
     {
       /* No files were listed on the command line.
-        Set the global pointer FILE_LIST so that it
-        references the null-terminated list of one name: "-".  */
+         Set the global pointer FILE_LIST so that it
+         references the null-terminated list of one name: "-".  */
 
       file_list = default_file_list;
     }
@@ -1888,33 +1869,53 @@ it must be one character from [doxn]"),
   if (width_specified)
     {
       if (desired_width != 0 && desired_width % l_c_m == 0)
-       bytes_per_block = desired_width;
+        bytes_per_block = desired_width;
       else
-       {
-         error (0, 0, _("warning: invalid width %lu; using %d instead"),
-                (unsigned long int) desired_width, l_c_m);
-         bytes_per_block = l_c_m;
-       }
+        {
+          error (0, 0, _("warning: invalid width %lu; using %d instead"),
+                 (unsigned long int) desired_width, l_c_m);
+          bytes_per_block = l_c_m;
+        }
     }
   else
     {
       if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
-       bytes_per_block = l_c_m * (DEFAULT_BYTES_PER_BLOCK / l_c_m);
+        bytes_per_block = l_c_m * (DEFAULT_BYTES_PER_BLOCK / l_c_m);
       else
-       bytes_per_block = l_c_m;
+        bytes_per_block = l_c_m;
+    }
+
+  /* Compute padding necessary to align output block.  */
+  for (i = 0; i < n_specs; i++)
+    {
+      int fields_per_block = bytes_per_block / width_bytes[spec[i].size];
+      int block_width = (spec[i].field_width + 1) * fields_per_block;
+      if (width_per_block < block_width)
+        width_per_block = block_width;
+    }
+  for (i = 0; i < n_specs; i++)
+    {
+      int fields_per_block = bytes_per_block / width_bytes[spec[i].size];
+      int block_width = spec[i].field_width * fields_per_block;
+      spec[i].pad_width = width_per_block - block_width;
     }
 
 #ifdef DEBUG
+  printf ("lcm=%d, width_per_block=%zu\n", l_c_m, width_per_block);
   for (i = 0; i < n_specs; i++)
     {
-      printf (_("%d: fmt=\"%s\" width=%d\n"),
-             i, spec[i].fmt_string, width_bytes[spec[i].size]);
+      int fields_per_block = bytes_per_block / width_bytes[spec[i].size];
+      assert (bytes_per_block % width_bytes[spec[i].size] == 0);
+      assert (1 <= spec[i].pad_width / fields_per_block);
+      printf ("%d: fmt=\"%s\" in_width=%d out_width=%d pad=%d\n",
+              i, spec[i].fmt_string, width_bytes[spec[i].size],
+              spec[i].field_width, spec[i].pad_width);
     }
 #endif
 
   ok &= (flag_dump_strings ? dump_strings () : dump ());
 
-cleanup:;
+cleanup:
 
   if (have_read_stdin && fclose (stdin) == EOF)
     error (EXIT_FAILURE, errno, _("standard input"));