(fseeko): Define a stub if ! HAVE_FSEEKO.
[platform/upstream/coreutils.git] / src / od.c
index a7397dc..72e33ca 100644 (file)
--- a/src/od.c
+++ b/src/od.c
@@ -1,5 +1,5 @@
 /* od -- dump files in octal and other formats
-   Copyright (C) 1992 Free Software Foundation, Inc.
+   Copyright (C) 92, 95, 96, 1997 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
    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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /* Written by Jim Meyering.  */
 
-/* AIX requires this to be the first thing in the file.  */
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else /* not HAVE_ALLOCA_H */
-#ifdef _AIX
- #pragma alloca
-#else /* not _AIX */
-char *alloca ();
-#endif /* not _AIX */
-#endif /* not HAVE_ALLOCA_H */
-#endif /* not __GNUC__ */
+#include <config.h>
 
 #include <stdio.h>
 #include <assert.h>
 #include <getopt.h>
 #include <sys/types.h>
 #include "system.h"
+#include "xstrtoul.h"
+#include "error.h"
 
 #if defined(__GNUC__) || defined(STDC_HEADERS)
-#include <float.h>
+# include <float.h>
 #endif
 
-#ifdef __STDC__
+#ifdef HAVE_LONG_DOUBLE
 typedef long double LONG_DOUBLE;
 #else
 typedef double LONG_DOUBLE;
 #endif
 
 #if HAVE_LIMITS_H
-#include <limits.h>
+# include <limits.h>
 #endif
 #ifndef SCHAR_MAX
-#define SCHAR_MAX 127
+# define SCHAR_MAX 127
 #endif
 #ifndef SCHAR_MIN
-#define SCHAR_MIN (-128)
+# define SCHAR_MIN (-128)
 #endif
 #ifndef SHRT_MAX
-#define SHRT_MAX 32767
+# define SHRT_MAX 32767
 #endif
 #ifndef SHRT_MIN
-#define SHRT_MIN (-32768)
+# define SHRT_MIN (-32768)
 #endif
 #ifndef ULONG_MAX
-#define ULONG_MAX ((unsigned long) ~(unsigned long) 0)
+# define ULONG_MAX ((unsigned long) ~(unsigned long) 0)
+#endif
+#ifndef LONG_MAX
+# define LONG_MAX ((long int) (ULONG_MAX >> 1))
+#endif
+
+#if HAVE_VALUES_H
+# include <values.h>
 #endif
 
-#define STREQ(a,b) (strcmp((a), (b)) == 0)
+#ifndef BITSPERBYTE
+# define BITSPERBYTE 8
+#endif
 
 #ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
 #ifndef MIN
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+# define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#if defined (_WIN32) && defined (_O_BINARY)
+# define WINDOWS_SETFILEMODE_BINARY(IN_STREAM, FILENAME)               \
+    do                                                                 \
+      { /* Turn off DOS text file modes, "rb" doesn't work on stdin.  */\
+       if (_setmode (_fileno ((IN_STREAM)), _O_BINARY) == -1)          \
+          {                                                            \
+           error (0, errno, "%s", (FILENAME));                         \
+           err = 1;                                                    \
+           continue;                                                   \
+         }                                                             \
+      }                                                                        \
+    while (0)
+#else
+# define WINDOWS_SETFILEMODE_BINARY(IN_STREAM, FILENAME) /* empty */
 #endif
 
 /* The default number of input bytes per output line.  */
@@ -82,22 +96,23 @@ typedef double LONG_DOUBLE;
 
 /* The number of decimal digits of precision in a float.  */
 #ifndef FLT_DIG
-#define FLT_DIG 7
+# define FLT_DIG 7
 #endif
 
 /* The number of decimal digits of precision in a double.  */
 #ifndef DBL_DIG
-#define DBL_DIG 15
+# define DBL_DIG 15
 #endif
 
 /* The number of decimal digits of precision in a long double.  */
 #ifndef LDBL_DIG
-#define LDBL_DIG DBL_DIG
+# define LDBL_DIG DBL_DIG
 #endif
 
-char *xmalloc ();
-char *xrealloc ();
-void error ();
+#if !HAVE_FSEEKO
+# undef fseeko
+# define fseeko(Stream, Offset, Whence) (-1)
+#endif
 
 enum size_spec
   {
@@ -106,9 +121,9 @@ enum size_spec
     SHORT,
     INT,
     LONG,
-    FP_SINGLE,
-    FP_DOUBLE,
-    FP_LONG_DOUBLE
+    FLOAT_SINGLE,
+    FLOAT_DOUBLE,
+    FLOAT_LONG_DOUBLE
   };
 
 enum output_format
@@ -122,12 +137,6 @@ enum output_format
     CHARACTER
   };
 
-enum strtoul_error
-  {
-    UINT_OK, UINT_INVALID, UINT_INVALID_SUFFIX_CHAR, UINT_OVERFLOW
-  };
-typedef enum strtoul_error strtoul_error;
-
 /* Each output format specification (from POSIX `-t spec' or from
    old-style options) is represented by one of these structures.  */
 struct tspec
@@ -136,8 +145,13 @@ struct tspec
     enum size_spec size;
     void (*print_function) ();
     char *fmt_string;
+    int hexl_mode_trailer;
+    int field_width;
   };
 
+/* 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.
@@ -200,7 +214,7 @@ static unsigned long int flag_dump_strings;
 /* Non-zero if we should recognize the pre-POSIX non-option arguments
    that specified at most one file and optional arguments specifying
    offset and pseudo-start address.  */
-static int flag_compatibility;
+static int traditional;
 
 /* Non-zero if an old-style `pseudo-address' was specified.  */
 static long int flag_pseudo_start;
@@ -211,20 +225,20 @@ static long int pseudo_offset;
 
 /* Function to format an address and optionally an additional parenthesized
    pseudo-address; it returns the formatted string.  */
-static const char *(*format_address) (/* long unsigned int */);
+static const char *(*format_address) __P ((long unsigned int));
 
 /* The number of input bytes to skip before formatting and writing.  */
-static unsigned long int n_bytes_to_skip = 0;
+static off_t n_bytes_to_skip = 0;
 
-/* When non-zero, MAX_BYTES_TO_FORMAT is the maximum number of bytes
+/* When nonzero, MAX_BYTES_TO_FORMAT is the maximum number of bytes
    to be read and formatted.  Otherwise all input is formatted.  */
 static int limit_bytes_to_format = 0;
 
 /* The maximum number of bytes that will be formatted.  This
-   value is used only when LIMIT_BYTES_TO_FORMAT is non-zero.  */
-static unsigned long int max_bytes_to_format;
+   value is used only when LIMIT_BYTES_TO_FORMAT is nonzero.  */
+static off_t max_bytes_to_format;
 
-/* When non-zero and two or more consecutive blocks are equal, format
+/* When nonzero and two or more consecutive blocks are equal, format
    only the first block and output an asterisk alone on the following
    line to indicate that identical blocks have been elided.  */
 static int abbreviate_duplicate_blocks = 1;
@@ -256,7 +270,7 @@ static char const *const *file_list;
 /* The input stream associated with the current file.  */
 static FILE *in_stream;
 
-/* If non-zero, at least one of the files we read was standard input.  */
+/* If nonzero, at least one of the files we read was standard input.  */
 static int have_read_stdin;
 
 #define LONGEST_INTEGRAL_TYPE long int
@@ -267,6 +281,12 @@ static enum size_spec integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1];
 #define MAX_FP_TYPE_SIZE sizeof(LONG_DOUBLE)
 static enum size_spec fp_type_size[MAX_FP_TYPE_SIZE + 1];
 
+/* If nonzero, display usage information and exit.  */
+static int show_help;
+
+/* If nonzero, print the version on standard output then exit.  */
+static int show_version;
+
 static struct option const long_options[] =
 {
   /* POSIX options.  */
@@ -277,34 +297,93 @@ static struct option const long_options[] =
   {"output-duplicates", no_argument, NULL, 'v'},
 
   /* non-POSIX options.  */
-  {"compatible", no_argument, NULL, 'C'},
   {"strings", optional_argument, NULL, 's'},
+  {"traditional", no_argument, NULL, 'B'},
   {"width", optional_argument, NULL, 'w'},
+  {"help", no_argument, &show_help, 1},
+  {"version", no_argument, &show_version, 1},
   {NULL, 0, NULL, 0}
 };
 
-/* The name this program was run with.  */
-char *program_name;
-
 static void
-usage ()
+usage (int status)
 {
-  fprintf (stderr, "\
-Usage: %s [-abcdfhiloxv] [-s[bytes]] [-w[bytes]] [-A radix] [-j bytes]\n\
-       [-N bytes] [-t type] [--skip-bytes=bytes] [--address-radix=radix]\n\
-       [--read-bytes=bytes] [--format=type] [--output-duplicates]\n\
-       [--strings[=bytes]] [--width[=bytes]] [file...]\n",
-          program_name);
-  exit (1);
+  if (status != 0)
+    fprintf (stderr, _("Try `%s --help' for more information.\n"),
+            program_name);
+  else
+    {
+      printf (_("\
+Usage: %s [OPTION]... [FILE]...\n\
+  or:  %s --traditional [FILE] [[+]OFFSET [[+]LABEL]]\n\
+"),
+             program_name, program_name);
+      printf (_("\
+Write an unambiguous representation, octal bytes by default, of FILE\n\
+to standard output.  With no FILE, or when FILE is -, read standard input.\n\
+\n\
+  -A, --address-radix=RADIX   decide how file offsets are printed\n\
+  -j, --skip-bytes=BYTES      skip BYTES input bytes first on each file\n\
+  -N, --read-bytes=BYTES      limit dump to BYTES input bytes per file\n\
+  -s, --strings[=BYTES]       output strings of at least BYTES graphic chars\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 pre-POSIX form\n\
+      --help                  display this help and exit\n\
+      --version               output version information and exit\n\
+\n\
+Pre-POSIX format specifications may be intermixed, they accumulate:\n\
+  -a   same as -t a,  select named characters\n\
+  -b   same as -t oC, select octal bytes\n\
+  -c   same as -t c,  select ASCII characters or backslash escapes\n\
+  -d   same as -t u2, select unsigned decimal shorts\n\
+  -f   same as -t fF, select floats\n\
+  -h   same as -t x2, select hexadecimal shorts\n\
+  -i   same as -t d2, select decimal shorts\n\
+  -l   same as -t d4, select decimal longs\n\
+  -o   same as -t o2, select octal shorts\n\
+  -x   same as -t x2, select hexadecimal shorts\n\
+"));
+      printf (_("\
+\n\
+For older syntax (second call format), OFFSET means -j OFFSET.  LABEL\n\
+is the pseudo-address at first byte printed, incremented when dump is\n\
+progressing.  For OFFSET and LABEL, a 0x or 0X prefix indicates\n\
+hexadecimal, suffixes maybe . for octal and b multiply by 512.\n\
+\n\
+TYPE is made up of one or more of these specifications:\n\
+\n\
+  a          named character\n\
+  c          ASCII character or backslash escape\n\
+  d[SIZE]    signed decimal, SIZE bytes per integer\n\
+  f[SIZE]    floating point, SIZE bytes per integer\n\
+  o[SIZE]    octal, SIZE bytes per integer\n\
+  u[SIZE]    unsigned decimal, SIZE bytes per integer\n\
+  x[SIZE]    hexadecimal, SIZE bytes per integer\n\
+\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\
+\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.  -s without a number implies 3.  -w without a number implies 32.\n\
+By default, od uses -A o -t d2 -w 16.\n\
+"));
+      puts (_("\nReport bugs to <textutils-bugs@gnu.org>."));
+    }
+  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
 
 /* Compute the greatest common denominator of U and V
    using Euclid's algorithm.  */
 
 static unsigned int
-gcd (u, v)
-     unsigned int u;
-     unsigned int v;
+gcd (unsigned int u, unsigned int v)
 {
   unsigned int t;
   while (v != 0)
@@ -319,9 +398,7 @@ gcd (u, v)
 /* Compute the least common multiple of U and V.  */
 
 static unsigned int
-lcm (u, v)
-     unsigned int u;
-     unsigned int v;
+lcm (unsigned int u, unsigned int v)
 {
   unsigned int t = gcd (u, v);
   if (t == 0)
@@ -329,101 +406,9 @@ lcm (u, v)
   return u * v / t;
 }
 
-static strtoul_error
-my_strtoul (s, base, val, allow_bkm_suffix)
-     const char *s;
-     int base;
-     long unsigned int *val;
-     int allow_bkm_suffix;
-{
-  char *p;
-  unsigned long int tmp;
-
-  assert (0 <= base && base <= 36);
-
-  tmp = strtoul (s, &p, base);
-  if (errno != 0)
-    return UINT_OVERFLOW;
-  if (p == s)
-    return UINT_INVALID;
-  if (!allow_bkm_suffix)
-    {
-      if (*p == '\0')
-       {
-         *val = tmp;
-         return UINT_OK;
-       }
-      else
-       return UINT_INVALID_SUFFIX_CHAR;
-    }
-
-  switch (*p)
-    {
-    case '\0':
-      break;
-
-#define BKM_SCALE(x,scale_factor,error_return)         \
-      do                                               \
-       {                                               \
-         if (x > (double) ULONG_MAX / scale_factor)    \
-           return error_return;                        \
-         x *= scale_factor;                            \
-       }                                               \
-      while (0)
-
-    case 'b':
-      BKM_SCALE (tmp, 512, UINT_OVERFLOW);
-      break;
-
-    case 'k':
-      BKM_SCALE (tmp, 1024, UINT_OVERFLOW);
-      break;
-
-    case 'm':
-      BKM_SCALE (tmp, 1024 * 1024, UINT_OVERFLOW);
-      break;
-
-    default:
-      return UINT_INVALID_SUFFIX_CHAR;
-      break;
-    }
-
-  *val = tmp;
-  return UINT_OK;
-}
-
-static void
-uint_fatal_error (str, argument_type_string, err)
-     const char *str;
-     const char *argument_type_string;
-     strtoul_error err;
-{
-  switch (err)
-    {
-    case UINT_OK:
-      abort ();
-
-    case UINT_INVALID:
-      error (2, 0, "invalid %s `%s'", argument_type_string, str);
-      break;
-
-    case UINT_INVALID_SUFFIX_CHAR:
-      error (2, 0, "invalid character following %s `%s'",
-            argument_type_string, str);
-      break;
-
-    case UINT_OVERFLOW:
-      error (2, 0, "%s `%s' larger than maximum unsigned long",
-            argument_type_string, str);
-      break;
-    }
-}
-
 static void
-print_s_char (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_s_char (long unsigned int n_bytes, const char *block,
+             const char *fmt_string)
 {
   int i;
   for (i = n_bytes; i > 0; i--)
@@ -432,31 +417,27 @@ print_s_char (n_bytes, block, fmt_string)
       if (tmp > SCHAR_MAX)
        tmp -= SCHAR_MAX - SCHAR_MIN + 1;
       assert (tmp <= SCHAR_MAX);
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (unsigned char);
     }
 }
 
 static void
-print_char (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_char (long unsigned int n_bytes, const char *block,
+           const char *fmt_string)
 {
   int i;
   for (i = n_bytes; i > 0; i--)
     {
       unsigned int tmp = *(const unsigned char *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (unsigned char);
     }
 }
 
 static void
-print_s_short (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_s_short (long unsigned int n_bytes, const char *block,
+              const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (unsigned short); i > 0; i--)
@@ -465,97 +446,86 @@ print_s_short (n_bytes, block, fmt_string)
       if (tmp > SHRT_MAX)
        tmp -= SHRT_MAX - SHRT_MIN + 1;
       assert (tmp <= SHRT_MAX);
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (unsigned short);
     }
 }
+
 static void
-print_short (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_short (long unsigned int n_bytes, const char *block,
+            const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (unsigned short); i > 0; i--)
     {
       unsigned int tmp = *(const unsigned short *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (unsigned short);
     }
 }
 
 static void
-print_int (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_int (long unsigned int n_bytes, const char *block,
+          const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (unsigned int); i > 0; i--)
     {
       unsigned int tmp = *(const unsigned int *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (unsigned int);
     }
 }
 
 static void
-print_long (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_long (long unsigned int n_bytes, const char *block,
+           const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (unsigned long); i > 0; i--)
     {
       unsigned long tmp = *(const unsigned long *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (unsigned long);
     }
 }
 
 static void
-print_float (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_float (long unsigned int n_bytes, const char *block,
+            const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (float); i > 0; i--)
     {
       float tmp = *(const float *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (float);
     }
 }
 
 static void
-print_double (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_double (long unsigned int n_bytes, const char *block,
+             const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (double); i > 0; i--)
     {
       double tmp = *(const double *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (double);
     }
 }
 
-#ifdef __STDC__
+#ifdef HAVE_LONG_DOUBLE
 static void
-print_long_double (n_bytes, block, fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *fmt_string;
+print_long_double (long unsigned int n_bytes, const char *block,
+                  const char *fmt_string)
 {
   int i;
   for (i = n_bytes / sizeof (LONG_DOUBLE); i > 0; i--)
     {
       LONG_DOUBLE tmp = *(const LONG_DOUBLE *) block;
-      printf (fmt_string, tmp, (i == 1 ? '\n' : ' '));
+      printf (fmt_string, tmp);
       block += sizeof (LONG_DOUBLE);
     }
 }
@@ -563,10 +533,23 @@ print_long_double (n_bytes, block, fmt_string)
 #endif
 
 static void
-print_named_ascii (n_bytes, block, unused_fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *unused_fmt_string;
+dump_hexl_mode_trailer (long unsigned int n_bytes, const char *block)
+{
+  int i;
+  fputs ("  >", stdout);
+  for (i = n_bytes; i > 0; i--)
+    {
+      unsigned int c = *(const unsigned char *) block;
+      unsigned int c2 = (ISPRINT(c) ? c : '.');
+      putchar (c2);
+      block += sizeof (unsigned char);
+    }
+  putchar ('<');
+}
+
+static void
+print_named_ascii (long unsigned int n_bytes, const char *block,
+                  const char *unused_fmt_string)
 {
   int i;
   for (i = n_bytes; i > 0; i--)
@@ -586,16 +569,14 @@ print_named_ascii (n_bytes, block, unused_fmt_string)
          s = buf;
        }
 
-      printf ("%3s%c", s, (i == 1 ? '\n' : ' '));
+      printf (" %3s", s);
       block += sizeof (unsigned char);
     }
 }
 
 static void
-print_ascii (n_bytes, block, unused_fmt_string)
-     long unsigned int n_bytes;
-     const char *block;
-     const char *unused_fmt_string;
+print_ascii (long unsigned int n_bytes, const char *block,
+            const char *unused_fmt_string)
 {
   int i;
   for (i = n_bytes; i > 0; i--)
@@ -643,7 +624,7 @@ print_ascii (n_bytes, block, unused_fmt_string)
          s = (const char *) buf;
        }
 
-      printf ("%3s%c", s, (i == 1 ? '\n' : ' '));
+      printf (" %3s", s);
       block += sizeof (unsigned char);
     }
 }
@@ -652,15 +633,12 @@ print_ascii (n_bytes, block, unused_fmt_string)
    unsigned long integer value.  If S points to a non-digit set *P to S,
    *VAL to 0, and return 0.  Otherwise, accumulate the integer value of
    the string of digits.  If the string of digits represents a value
-   larger than ULONG_MAX, don't modify *VAL or *P and return non-zero.
+   larger than ULONG_MAX, don't modify *VAL or *P and return nonzero.
    Otherwise, advance *P to the first non-digit after S, set *VAL to
    the result of the conversion and return zero.  */
 
 static int
-simple_strtoul (s, p, val)
-     const char *s;
-     const char **p;
-     long unsigned int *val;
+simple_strtoul (const char *s, const char **p, long unsigned int *val)
 {
   unsigned long int sum;
 
@@ -677,25 +655,25 @@ simple_strtoul (s, p, val)
   return 0;
 }
 
-/* If S points to a single valid POSIX-style od format string, put a
-   description of that format in *TSPEC, make *NEXT point at the character
-   following the just-decoded format (if *NEXT is non-NULL), and return
-   zero.  If S is not valid, don't modify *NEXT or *TSPEC and return
-   non-zero.  For example, if S were "d4afL" *NEXT would be set to "afL"
-   and *TSPEC would be
+/* If S points to a single valid POSIX-style od format string, put
+   a description of that format in *TSPEC, make *NEXT point at the
+   character following the just-decoded format (if *NEXT is non-NULL),
+   and return zero.  If S is not valid, don't modify *NEXT or *TSPEC,
+   give a diagnostic, and return nonzero.  For example, if S were
+   "d4afL" *NEXT would be set to "afL" and *TSPEC would be
      {
        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";
       }
+   S_ORIG is solely for reporting errors.  It should be the full format
+   string argument.
    */
 
 static int
-decode_one_format (s, next, tspec)
-     const char *s;
-     const char **next;
-     struct tspec *tspec;
+decode_one_format (const char *s_orig, const char *s, const char **next,
+                  struct tspec *tspec)
 {
   enum size_spec size_spec;
   unsigned long int size;
@@ -705,6 +683,7 @@ decode_one_format (s, next, tspec)
   void (*print_function) ();
   const char *p;
   unsigned int c;
+  unsigned int field_width = 0;
 
   assert (tspec != NULL);
 
@@ -740,14 +719,24 @@ decode_one_format (s, next, tspec)
 
        default:
          if (simple_strtoul (s, &p, &size) != 0)
-           return 1;
+           {
+             /* The integer at P in S would overflow an unsigned long.
+                A digit string that long is sufficiently odd looking
+                that the following diagnostic is sufficient.  */
+             error (0, 0, _("invalid type string `%s'"), s_orig);
+             return 1;
+           }
          if (p == s)
            size = sizeof (int);
          else
            {
              if (size > MAX_INTEGRAL_TYPE_SIZE
                  || integral_type_size[size] == NO_SIZE)
-               return 1;
+               {
+                 error (0, 0, _("invalid type string `%s';\n\
+this system doesn't provide a %lu-byte integral type"), s_orig, size);
+                 return 1;
+               }
              s = p;
            }
          break;
@@ -762,29 +751,29 @@ decode_one_format (s, next, tspec)
        {
        case 'd':
          fmt = SIGNED_DECIMAL;
-         sprintf (fmt_string, "%%%u%sd%%c",
-                  bytes_to_signed_dec_digits[size],
+         sprintf (fmt_string, " %%%u%sd",
+                  (field_width = bytes_to_signed_dec_digits[size]),
                   (size_spec == LONG ? "l" : ""));
          break;
 
        case 'o':
          fmt = OCTAL;
-         sprintf (fmt_string, "%%0%u%so%%c",
-                  bytes_to_oct_digits[size],
+         sprintf (fmt_string, " %%0%u%so",
+                  (field_width = bytes_to_oct_digits[size]),
                   (size_spec == LONG ? "l" : ""));
          break;
 
        case 'u':
          fmt = UNSIGNED_DECIMAL;
-         sprintf (fmt_string, "%%%u%su%%c",
-                  bytes_to_unsigned_dec_digits[size],
+         sprintf (fmt_string, " %%%u%su",
+                  (field_width = bytes_to_unsigned_dec_digits[size]),
                   (size_spec == LONG ? "l" : ""));
          break;
 
        case 'x':
          fmt = HEXADECIMAL;
-         sprintf (fmt_string, "%%0%u%sx%%c",
-                  bytes_to_hex_digits[size],
+         sprintf (fmt_string, " %%0%u%sx",
+                  (field_width = bytes_to_hex_digits[size]),
                   (size_spec == LONG ? "l" : ""));
          break;
 
@@ -805,7 +794,7 @@ decode_one_format (s, next, tspec)
        case SHORT:
          print_function = (fmt == SIGNED_DECIMAL
                            ? print_s_short
-                           : print_short);;
+                           : print_short);
          break;
 
        case INT:
@@ -843,14 +832,24 @@ decode_one_format (s, next, tspec)
 
        default:
          if (simple_strtoul (s, &p, &size) != 0)
-           return 1;
+           {
+             /* The integer at P in S would overflow an unsigned long.
+                A digit string that long is sufficiently odd looking
+                that the following diagnostic is sufficient.  */
+             error (0, 0, _("invalid type string `%s'"), s_orig);
+             return 1;
+           }
          if (p == s)
            size = sizeof (double);
          else
            {
              if (size > MAX_FP_TYPE_SIZE
                  || fp_type_size[size] == NO_SIZE)
-               return 1;
+               {
+                 error (0, 0, _("invalid type string `%s';\n\
+this system doesn't provide a %lu-byte floating point type"), s_orig, size);
+                 return 1;
+               }
              s = p;
            }
          break;
@@ -859,30 +858,30 @@ decode_one_format (s, next, tspec)
 
       switch (size_spec)
        {
-       case FP_SINGLE:
+       case FLOAT_SINGLE:
          print_function = print_float;
          /* Don't use %#e; not all systems support it.  */
-         pre_fmt_string = "%%%d.%de%%c";
+         pre_fmt_string = " %%%d.%de";
          fmt_string = xmalloc (strlen (pre_fmt_string));
          sprintf (fmt_string, pre_fmt_string,
-                  FLT_DIG + 8, FLT_DIG);
+                  (field_width = FLT_DIG + 8), FLT_DIG);
          break;
 
-       case FP_DOUBLE:
+       case FLOAT_DOUBLE:
          print_function = print_double;
-         pre_fmt_string = "%%%d.%de%%c";
+         pre_fmt_string = " %%%d.%de";
          fmt_string = xmalloc (strlen (pre_fmt_string));
          sprintf (fmt_string, pre_fmt_string,
-                  DBL_DIG + 8, DBL_DIG);
+                  (field_width = DBL_DIG + 8), DBL_DIG);
          break;
 
-#ifdef __STDC__
-       case FP_LONG_DOUBLE:
+#ifdef HAVE_LONG_DOUBLE
+       case FLOAT_LONG_DOUBLE:
          print_function = print_long_double;
-         pre_fmt_string = "%%%d.%dle%%c";
+         pre_fmt_string = " %%%d.%dLe";
          fmt_string = xmalloc (strlen (pre_fmt_string));
          sprintf (fmt_string, pre_fmt_string,
-                  LDBL_DIG + 8, LDBL_DIG);
+                  (field_width = LDBL_DIG + 8), LDBL_DIG);
          break;
 #endif
 
@@ -897,6 +896,7 @@ decode_one_format (s, next, tspec)
       size_spec = CHAR;
       fmt_string = NULL;
       print_function = print_named_ascii;
+      field_width = 3;
       break;
 
     case 'c':
@@ -905,9 +905,12 @@ decode_one_format (s, next, tspec)
       size_spec = CHAR;
       fmt_string = NULL;
       print_function = print_ascii;
+      field_width = 3;
       break;
 
     default:
+      error (0, 0, _("invalid character `%c' in type string `%s'"),
+            *s, s_orig);
       return 1;
     }
 
@@ -916,6 +919,11 @@ decode_one_format (s, next, tspec)
   tspec->print_function = print_function;
   tspec->fmt_string = fmt_string;
 
+  tspec->field_width = field_width;
+  tspec->hexl_mode_trailer = (*s == 'z');
+  if (tspec->hexl_mode_trailer)
+    s++;
+
   if (next != NULL)
     *next = s;
 
@@ -924,12 +932,12 @@ decode_one_format (s, next, tspec)
 
 /* Decode the POSIX-style od format string S.  Append the decoded
    representation to the global array SPEC, reallocating SPEC if
-   necessary.  Return zero if S is valid, non-zero otherwise.  */
+   necessary.  Return zero if S is valid, nonzero otherwise.  */
 
 static int
-decode_format_string (s)
-     const char *s;
+decode_format_string (const char *s)
 {
+  const char *s_orig = s;
   assert (s != NULL);
 
   while (*s != '\0')
@@ -937,7 +945,7 @@ decode_format_string (s)
       struct tspec tspec;
       const char *next;
 
-      if (decode_one_format (s, &next, &tspec))
+      if (decode_one_format (s_orig, s, &next, &tspec))
        return 1;
 
       assert (s != next);
@@ -946,11 +954,13 @@ decode_format_string (s)
       if (n_specs >= n_specs_allocated)
        {
          n_specs_allocated = 1 + (3 * n_specs_allocated) / 2;
-         spec = (struct tspec *) xrealloc (spec, (n_specs_allocated
-                                                  * sizeof (struct tspec)));
+         spec = (struct tspec *) xrealloc ((char *) spec,
+                                           (n_specs_allocated
+                                            * sizeof (struct tspec)));
        }
 
-      bcopy ((char *) &tspec, (char *) &spec[n_specs], sizeof (struct tspec));
+      memcpy ((char *) &spec[n_specs], (char *) &tspec,
+             sizeof (struct tspec));
       ++n_specs;
     }
 
@@ -960,13 +970,13 @@ decode_format_string (s)
 /* Given a list of one or more input filenames FILE_LIST, set the global
    file pointer IN_STREAM to position N_SKIP in the concatenation of
    those files.  If any file operation fails or if there are fewer than
-   N_SKIP bytes in the combined input, give an error message and exit.
-   When possible, use seek- rather than read operations to advance
-   IN_STREAM.  A file name of "-" is interpreted as standard input.  */
+   N_SKIP bytes in the combined input, give an error message and return
+   nonzero.  When possible, use seek- rather than read operations to
+   advance IN_STREAM.  A file name of "-" is interpreted as standard
+   input.  */
 
 static int
-skip (n_skip)
-     long unsigned int n_skip;
+skip (off_t n_skip)
 {
   int err;
 
@@ -978,7 +988,7 @@ skip (n_skip)
 
       if (STREQ (*file_list, "-"))
        {
-         input_filename = "standard input";
+         input_filename = _("standard input");
          in_stream = stdin;
          have_read_stdin = 1;
        }
@@ -993,6 +1003,7 @@ skip (n_skip)
              continue;
            }
        }
+      WINDOWS_SETFILEMODE_BINARY (in_stream, input_filename);
 
       if (n_skip == 0)
        break;
@@ -1004,7 +1015,7 @@ skip (n_skip)
           - 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 stat().
+            Try to do that by getting file's size using fstat().
             But that will work only for regular files and dirs.  */
 
       if (fstat (fileno (in_stream), &file_stats))
@@ -1024,7 +1035,7 @@ skip (n_skip)
          if (n_skip >= file_stats.st_size)
            {
              n_skip -= file_stats.st_size;
-             if (in_stream != stdin && fclose (in_stream))
+             if (in_stream != stdin && fclose (in_stream) == EOF)
                {
                  error (0, errno, "%s", input_filename);
                  err = 1;
@@ -1033,7 +1044,10 @@ skip (n_skip)
            }
          else
            {
-             if (fseek (in_stream, n_skip, SEEK_SET) == 0)
+             /* Try fseeko if available, fseek otherwise.  */
+             if (fseeko (in_stream, n_skip, SEEK_SET) == 0
+                 || (n_skip <= LONG_MAX
+                     && fseek (in_stream, (long) n_skip, SEEK_SET) == 0))
                {
                  n_skip = 0;
                  break;
@@ -1041,7 +1055,8 @@ skip (n_skip)
            }
        }
 
-      /* fseek didn't work or wasn't attempted; do it the slow way.  */
+      /* Seek didn't work or wasn't attempted;  position the file pointer
+        by reading.  */
 
       for (j = n_skip / BUFSIZ; j >= 0; j--)
        {
@@ -1061,21 +1076,19 @@ skip (n_skip)
     }
 
   if (n_skip != 0)
-    error (2, 0, "cannot skip past end of combined input");
+    error (EXIT_FAILURE, 0, _("cannot skip past end of combined input"));
 
   return err;
 }
 
 static const char *
-format_address_none (address)
-     long unsigned int address;
+format_address_none (long unsigned int address)
 {
   return "";
 }
 
 static const char *
-format_address_std (address)
-     long unsigned int address;
+format_address_std (long unsigned int address)
 {
   const char *address_string;
 
@@ -1085,8 +1098,7 @@ format_address_std (address)
 }
 
 static const char *
-format_address_label (address)
-     long unsigned int address;
+format_address_label (long unsigned int address)
 {
   const char *address_string;
   assert (output_address_fmt_string != NULL);
@@ -1109,16 +1121,13 @@ format_address_label (address)
    only when it has not been padded to length BYTES_PER_BLOCK.  */
 
 static void
-write_block (current_offset, n_bytes, prev_block, curr_block)
-     long unsigned int current_offset;
-     long unsigned int n_bytes;
-     const char *prev_block;
-     const char *curr_block;
+write_block (long unsigned int current_offset, long unsigned int n_bytes,
+            const char *prev_block, const char *curr_block)
 {
   static int first = 1;
   static int prev_pair_equal = 0;
 
-#define EQUAL_BLOCKS(b1, b2) (bcmp ((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
@@ -1137,28 +1146,40 @@ write_block (current_offset, n_bytes, prev_block, curr_block)
     }
   else
     {
-      int i;
+      unsigned int i;
 
       prev_pair_equal = 0;
       for (i = 0; i < n_specs; i++)
        {
-         printf ("%s ", (i == 0
-                         ? format_address (current_offset)
-                         : address_pad));
+         const char *addr_or_pad = (i == 0
+                                    ? format_address (current_offset)
+                                    : address_pad);
+
+         fputs (addr_or_pad, stdout);
          (*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');
        }
     }
   first = 0;
 }
 
 /* Test whether there have been errors on in_stream, and close it if
-   it is not standard input.  Return non-zero if there has been an error
+   it is not standard input.  Return nonzero if there has been an error
    on in_stream or stdout; return zero otherwise.  This function will
    report more than one error only if both a read and a write error
    have occurred.  */
 
 static int
-check_and_close ()
+check_and_close (void)
 {
   int err;
 
@@ -1178,7 +1199,7 @@ check_and_close ()
 
   if (ferror (stdout))
     {
-      error (0, errno, "standard output");
+      error (0, errno, _("standard output"));
       err = 1;
     }
 
@@ -1194,12 +1215,11 @@ check_and_close ()
    the list.  Then try to read a byte from the newly opened file.
    Repeat if necessary until *FILE_LIST is NULL.  When EOF is reached
    for the last file in FILE_LIST, set *C to EOF and return.  Subsequent
-   calls do likewise.  The return value is non-zero if any errors
+   calls do likewise.  The return value is nonzero if any errors
    occured, zero otherwise.  */
 
 static int
-read_char (c)
-     int *c;
+read_char (int *c)
 {
   int err;
 
@@ -1227,7 +1247,7 @@ read_char (c)
 
          if (STREQ (*file_list, "-"))
            {
-             input_filename = "standard input";
+             input_filename = _("standard input");
              in_stream = stdin;
              have_read_stdin = 1;
            }
@@ -1241,6 +1261,7 @@ read_char (c)
                  err = 1;
                }
            }
+         WINDOWS_SETFILEMODE_BINARY (in_stream, input_filename);
        }
       while (in_stream == NULL);
     }
@@ -1257,14 +1278,11 @@ read_char (c)
    Repeat if necessary until *FILE_LIST is NULL.  Set *N_BYTES_IN_BUFFER
    to the number of bytes read.  If an error occurs, it will be detected
    through ferror when the stream is about to be closed.  If there is an
-   error, give a message but continue reading as usual and return non-zero.
+   error, give a message but continue reading as usual and return nonzero.
    Otherwise return zero.  */
 
 static int
-read_block (n, block, n_bytes_in_buffer)
-     size_t n;
-     char *block;
-     size_t *n_bytes_in_buffer;
+read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
 {
   int err;
 
@@ -1302,7 +1320,7 @@ read_block (n, block, n_bytes_in_buffer)
 
          if (STREQ (*file_list, "-"))
            {
-             input_filename = "standard input";
+             input_filename = _("standard input");
              in_stream = stdin;
              have_read_stdin = 1;
            }
@@ -1316,6 +1334,7 @@ read_block (n, block, n_bytes_in_buffer)
                  err = 1;
                }
            }
+         WINDOWS_SETFILEMODE_BINARY (in_stream, input_filename);
        }
       while (in_stream == NULL);
     }
@@ -1325,9 +1344,9 @@ read_block (n, block, n_bytes_in_buffer)
    with the format specs.  */
 
 static int
-get_lcm ()
+get_lcm (void)
 {
-  int i;
+  unsigned int i;
   int l_c_m = 1;
 
   for (i = 0; i < n_specs; i++)
@@ -1338,12 +1357,13 @@ get_lcm ()
 /* If S is a valid pre-POSIX offset specification with an optional leading '+'
    return the offset it denotes.  Otherwise, return -1.  */
 
-long int
+off_t
 parse_old_offset (const char *s)
 {
   int radix;
-  char *suffix;
-  long offset;
+  off_t offset;
+  enum strtol_error s_err;
+  long unsigned int tmp;
 
   if (*s == '\0')
     return -1;
@@ -1353,43 +1373,26 @@ parse_old_offset (const char *s)
     ++s;
 
   /* Determine the radix we'll use to interpret S.  If there is a `.',
-     it's decimal, otherwise, if the string begins with `0x', it's
-     hexadecimal, else octal.  */
-  if (index (s, '.') != NULL)
+     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 (strlen (s) >= 2 && s[0] == '0' && s[1] == 'x')
+      if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
        radix = 16;
       else
        radix = 8;
     }
-  offset = strtoul (s, &suffix, radix);
-  if (suffix == s || errno != 0)
-    return -1;
-  if (*suffix == '.')
-    ++suffix;
-  switch (*suffix)
-    {
-    case 'b':
-      BKM_SCALE (offset, 512, -1);
-      ++suffix;
-      break;
-
-    case 'B':
-      BKM_SCALE (offset, 1024, -1);
-      ++suffix;
-      break;
 
-    default:
-      /* empty */
-      break;
+  s_err = xstrtoul (s, NULL, radix, &tmp, "Bb");
+  if (s_err != LONGINT_OK)
+    {
+      STRTOL_FAIL_WARN (s, _("old-style offset"), s_err);
+      return -1;
     }
-
-  if (*suffix != '\0')
-    return -1;
-  else
-    return offset;
+  offset = tmp;
+  return offset;
 }
 
 /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
@@ -1401,18 +1404,22 @@ parse_old_offset (const char *s)
    multiple of all format spec sizes.  Write the final block.  Finally,
    write on a line by itself the offset of the byte after the last byte
    read.  Accumulate return values from calls to read_block and
-   check_and_close, and if any was non-zero, return non-zero.
+   check_and_close, and if any was nonzero, return nonzero.
    Otherwise, return zero.  */
 
 static int
-dump ()
+dump (void)
 {
   char *block[2];
-  unsigned long int current_offset;
+  off_t current_offset;
+  off_t end_offset;
   int idx;
   int err;
   size_t n_bytes_read;
-  size_t end_offset;
+
+#ifdef lint  /* Suppress `used before initialized' warning.  */
+  end_offset = 0;
+#endif
 
   block[0] = (char *) alloca (bytes_per_block);
   block[1] = (char *) alloca (bytes_per_block);
@@ -1425,11 +1432,16 @@ dump ()
     {
       end_offset = n_bytes_to_skip + max_bytes_to_format;
 
-      n_bytes_read = 0;
-      while (current_offset < end_offset)
+      while (1)
        {
          size_t n_needed;
-         n_needed = MIN (end_offset - current_offset, bytes_per_block);
+         if (current_offset >= end_offset)
+           {
+             n_bytes_read = 0;
+             break;
+           }
+         n_needed = MIN (end_offset - current_offset,
+                         (off_t) bytes_per_block);
          err |= read_block (n_needed, block[idx], &n_bytes_read);
          if (n_bytes_read < bytes_per_block)
            break;
@@ -1466,7 +1478,7 @@ dump ()
         is at least as large as n_bytes_read.  */
       bytes_to_write = l_c_m * (int) ((n_bytes_read + l_c_m - 1) / l_c_m);
 
-      bzero (block[idx] + n_bytes_read, bytes_to_write - n_bytes_read);
+      memset (block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
       write_block (current_offset, bytes_to_write,
                   block[!idx], block[idx]);
       current_offset += n_bytes_read;
@@ -1485,28 +1497,29 @@ dump ()
    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
-   pre-POSIX version of od.  Return non-zero if an error
+   pre-POSIX version of od.  Return nonzero if an error
    occurs.  Otherwise, return zero.  */
 
 static int
-dump_strings ()
+dump_strings (void)
 {
-  int bufsize = MAX (100, string_min);
+  size_t bufsize = MAX (100, string_min);
   char *buf = xmalloc (bufsize);
-  unsigned long address = n_bytes_to_skip;
+  off_t address = n_bytes_to_skip;
   int err;
 
   err = 0;
   while (1)
     {
-      int i;
+      unsigned int i;
       int c;
 
       /* See if the next `string_min' chars are all printing chars.  */
     tryline:
 
       if (limit_bytes_to_format
-         && address >= (n_bytes_to_skip + max_bytes_to_format - string_min))
+         && address >= (n_bytes_to_skip + max_bytes_to_format -
+                        (off_t) string_min))
        break;
 
       for (i = 0; i < string_min; i++)
@@ -1604,24 +1617,31 @@ dump_strings ()
 }
 
 int
-main (argc, argv)
-     int argc;
-     char **argv;
+main (int argc, char **argv)
 {
   int c;
   int n_files;
-  int i;
+  unsigned int i;
   unsigned int l_c_m;
   unsigned int address_pad_len;
   unsigned long int desired_width;
   int width_specified = 0;
+  int n_failed_decodes = 0;
   int err;
 
   /* The old-style `pseudo starting address' to be printed in parentheses
      after any true address.  */
   long int pseudo_start;
 
+#ifdef lint  /* Suppress `used before initialized' warning.  */
+  pseudo_start = 0;
+#endif
+
   program_name = argv[0];
+  setlocale (LC_ALL, "");
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
   err = 0;
 
   for (i = 0; i <= MAX_INTEGRAL_TYPE_SIZE; i++)
@@ -1635,12 +1655,12 @@ main (argc, argv)
   for (i = 0; i <= MAX_FP_TYPE_SIZE; i++)
     fp_type_size[i] = NO_SIZE;
 
-  fp_type_size[sizeof (float)] = FP_SINGLE;
+  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 FP_LONG_DOUBLE will never be used.  */
-  fp_type_size[sizeof (LONG_DOUBLE)] = FP_LONG_DOUBLE;
-  fp_type_size[sizeof (double)] = FP_DOUBLE;
+     supported FLOAT_LONG_DOUBLE will never be used.  */
+  fp_type_size[sizeof (LONG_DOUBLE)] = FLOAT_LONG_DOUBLE;
+  fp_type_size[sizeof (double)] = FLOAT_DOUBLE;
 
   n_specs = 0;
   n_specs_allocated = 5;
@@ -1651,14 +1671,17 @@ main (argc, argv)
   address_pad_len = 7;
   flag_dump_strings = 0;
 
-  while ((c = getopt_long (argc, argv, "abcCdfhilos::xw::A:j:N:t:v",
-                          long_options, (int *) 0))
-        != EOF)
+  while ((c = getopt_long (argc, argv, "abcdfhilos::xw::A:j:N:t:v",
+                          long_options, NULL)) != -1)
     {
-      strtoul_error s_err;
+      unsigned long int tmp;
+      enum strtol_error s_err;
 
       switch (c)
        {
+       case 0:
+         break;
+
        case 'A':
          switch (optarg[0])
            {
@@ -1683,25 +1706,35 @@ main (argc, argv)
              address_pad_len = 0;
              break;
            default:
-             error (2, 0,
-                    "invalid output address radix `%c'; it must be one character from [doxn]",
+             error (EXIT_FAILURE, 0,
+                    _("invalid output address radix `%c'; \
+it must be one character from [doxn]"),
                     optarg[0]);
              break;
            }
          break;
 
        case 'j':
-         s_err = my_strtoul (optarg, 0, &n_bytes_to_skip, 1);
-         if (s_err != UINT_OK)
-           uint_fatal_error (optarg, "skip argument", s_err);
+         s_err = xstrtoul (optarg, NULL, 0, &tmp, "bkm");
+         n_bytes_to_skip = tmp;
+         if (s_err != LONGINT_OK)
+           STRTOL_FATAL_ERROR (optarg, _("skip argument"), s_err);
          break;
 
        case 'N':
          limit_bytes_to_format = 1;
 
-         s_err = my_strtoul (optarg, 0, &max_bytes_to_format, 1);
-         if (s_err != UINT_OK)
-           uint_fatal_error (optarg, "limit argument", s_err);
+         /* FIXME: if off_t is long long and that's an 8-byte type,
+            use xstrtouq here.  */
+         s_err = xstrtoul (optarg, NULL, 0, &tmp, "bkm");
+         max_bytes_to_format = tmp;
+         if (s_err != LONGINT_OK)
+           STRTOL_FATAL_ERROR (optarg, _("limit argument"), s_err);
+
+         if (tmp > LONG_MAX)
+           error (EXIT_FAILURE, 0,
+                  _("specified number of bytes `%s' is larger than \
+the maximum\nrepresentable value of type `long'"), optarg);
          break;
 
        case 's':
@@ -1709,24 +1742,24 @@ main (argc, argv)
            string_min = 3;
          else
            {
-             s_err = my_strtoul (optarg, 0, &string_min, 1);
-             if (s_err != UINT_OK)
-               uint_fatal_error (optarg, "minimum string length", s_err);
+             s_err = xstrtoul (optarg, NULL, 0, &string_min, "bkm");
+             if (s_err != LONGINT_OK)
+               STRTOL_FATAL_ERROR (optarg, _("minimum string length"), s_err);
            }
          ++flag_dump_strings;
          break;
 
        case 't':
          if (decode_format_string (optarg))
-           error (2, 0, "invalid type string `%s'", optarg);
+           ++n_failed_decodes;
          break;
 
        case 'v':
          abbreviate_duplicate_blocks = 0;
          break;
 
-       case 'C':
-         flag_compatibility = 1;
+       case 'B':
+         traditional = 1;
          break;
 
          /* The next several cases map the old, pre-POSIX format
@@ -1737,9 +1770,8 @@ main (argc, argv)
 #define CASE_OLD_ARG(old_char,new_string)              \
        case old_char:                                  \
          {                                             \
-           int tmp;                                    \
-           tmp = decode_format_string (new_string);    \
-           assert (tmp == 0);                          \
+           if (decode_format_string (new_string))      \
+             ++n_failed_decodes;                       \
          }                                             \
          break
 
@@ -1764,33 +1796,45 @@ main (argc, argv)
            }
          else
            {
-             s_err = my_strtoul (optarg, 10, &desired_width, 0);
-             if (s_err != UINT_OK)
-               error (2, 0, "invalid width specification `%s'", optarg);
+             s_err = xstrtoul (optarg, NULL, 10, &desired_width, "");
+             if (s_err != LONGINT_OK)
+               STRTOL_FATAL_ERROR (optarg, _("width specification"), s_err);
            }
          break;
 
        default:
-         usage ();
+         usage (1);
          break;
        }
     }
 
+  if (n_failed_decodes > 0)
+    exit (EXIT_FAILURE);
+
+  if (show_version)
+    {
+      printf ("od (%s) %s\n", GNU_PACKAGE, VERSION);
+      exit (EXIT_SUCCESS);
+    }
+
+  if (show_help)
+    usage (0);
+
   if (flag_dump_strings && n_specs > 0)
-    error (2, 0, "no type may be specified when dumping strings");
+    error (EXIT_FAILURE, 0,
+          _("no type may be specified when dumping strings"));
 
   n_files = argc - optind;
 
-  /* If the --compatible option was used, there may be from 0 to 3
-     remaining command line arguments:
-       [file] [offset [pseudo_start]]
-     The offset and pseudo_start have the same syntax
-     FIXME: elaborate */
+  /* If the --backward-compatible option is used, there may be from
+     0 to 3 remaining command line arguments;  handle each case
+     separately.
+       od [file] [[+]offset[.][b] [[+]label[.][b]]]
+     The offset and pseudo_start have the same syntax.  */
 
-  if (flag_compatibility)
+  if (traditional)
     {
-      long int offset;
-      int usage_error = 0;
+      off_t offset;
 
       if (n_files == 1)
        {
@@ -1803,7 +1847,7 @@ main (argc, argv)
        }
       else if (n_files == 2)
        {
-         long int o1, o2;
+         off_t o1, o2;
          if ((o1 = parse_old_offset (argv[optind])) >= 0
              && (o2 = parse_old_offset (argv[optind + 1])) >= 0)
            {
@@ -1822,16 +1866,15 @@ main (argc, argv)
            }
          else
            {
-             usage_error = 1;
              error (0, 0,
-                    "invalid second operand in compatibility mode `%s'",
+                    _("invalid second operand in compatibility mode `%s'"),
                     argv[optind + 1]);
-             usage ();
+             usage (1);
            }
        }
       else if (n_files == 3)
        {
-         long int o1, o2;
+         off_t o1, o2;
          if ((o1 = parse_old_offset (argv[optind + 1])) >= 0
              && (o2 = parse_old_offset (argv[optind + 2])) >= 0)
            {
@@ -1845,15 +1888,15 @@ main (argc, argv)
          else
            {
              error (0, 0,
-             "in compatibility mode the last 2 arguments must be offsets");
-             usage ();
+           _("in compatibility mode, the last two arguments must be offsets"));
+             usage (1);
            }
        }
-      else
+      else if (n_files > 3)
        {
          error (0, 0,
-            "in compatibility mode there may be no more than 3 arguments");
-         usage ();
+                _("compatibility mode supports at most three arguments"));
+         usage (1);
        }
 
       if (flag_pseudo_start)
@@ -1883,9 +1926,13 @@ main (argc, argv)
 
   if (n_specs == 0)
     {
-      int d_err = decode_one_format ("o2", NULL, &(spec[0]));
+      if (decode_one_format ("o2", "o2", NULL, &(spec[0])))
+       {
+         /* This happens on Cray systems that don't have a 2-byte
+            integral type.  */
+         exit (EXIT_FAILURE);
+       }
 
-      assert (d_err == 0);
       n_specs = 1;
     }
 
@@ -1902,6 +1949,8 @@ main (argc, argv)
     }
 
   err |= skip (n_bytes_to_skip);
+  if (in_stream == NULL)
+    goto cleanup;
 
   pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
 
@@ -1914,7 +1963,7 @@ main (argc, argv)
        bytes_per_block = desired_width;
       else
        {
-         error (0, 0, "warning: invalid width %d; using %d instead",
+         error (0, 0, _("warning: invalid width %lu; using %d instead"),
                 desired_width, l_c_m);
          bytes_per_block = l_c_m;
        }
@@ -1930,18 +1979,20 @@ main (argc, argv)
 #ifdef DEBUG
   for (i = 0; i < n_specs; i++)
     {
-      printf ("%d: fmt=\"%s\" width=%d\n",
+      printf (_("%d: fmt=\"%s\" width=%d\n"),
              i, spec[i].fmt_string, width_bytes[spec[i].size]);
     }
 #endif
 
   err |= (flag_dump_strings ? dump_strings () : dump ());
 
+cleanup:;
+
   if (have_read_stdin && fclose (stdin) == EOF)
-    error (2, errno, "standard input");
+    error (EXIT_FAILURE, errno, _("standard input"));
 
   if (fclose (stdout) == EOF)
-    error (2, errno, "write error");
+    error (EXIT_FAILURE, errno, _("write error"));
 
-  exit (err);
+  exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }