Remove unnecessary casts of alloca, since now it's guaranteed to be (void *).
[platform/upstream/coreutils.git] / src / od.c
index 2b8ba2f..d362e66 100644 (file)
--- a/src/od.c
+++ b/src/od.c
@@ -1,5 +1,5 @@
 /* od -- dump files in octal and other formats
-   Copyright (C) 92, 1995-2000 Free Software Foundation, Inc.
+   Copyright (C) 92, 1995-2003 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
 #include <getopt.h>
 #include <sys/types.h>
 #include "system.h"
-#include "closeout.h"
 #include "error.h"
+#include "posixver.h"
 #include "xstrtol.h"
 
 /* The official name of this program (e.g., no `g' prefix).  */
 #define PROGRAM_NAME "od"
 
-#define AUTHORS "Jim Meyering"
+#define WRITTEN_BY _("Written by Jim Meyering.")
 
 #if defined(__GNUC__) || defined(STDC_HEADERS)
 # include <float.h>
@@ -43,10 +43,6 @@ typedef long double LONG_DOUBLE;
 typedef double LONG_DOUBLE;
 #endif
 
-#if HAVE_VALUES_H
-# include <values.h>
-#endif
-
 /* The default number of input bytes per output line.  */
 #define DEFAULT_BYTES_PER_BLOCK 16
 
@@ -65,6 +61,14 @@ typedef double LONG_DOUBLE;
 # define LDBL_DIG DBL_DIG
 #endif
 
+#if HAVE_UNSIGNED_LONG_LONG
+typedef unsigned long long ulonglong_t;
+#else
+/* 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;
+#endif
+
 enum size_spec
   {
     NO_SIZE,
@@ -76,7 +80,8 @@ enum size_spec
     /* FIXME: add INTMAX support, too */
     FLOAT_SINGLE,
     FLOAT_DOUBLE,
-    FLOAT_LONG_DOUBLE
+    FLOAT_LONG_DOUBLE,
+    N_SIZE_SPECS
   };
 
 enum output_format
@@ -90,13 +95,13 @@ enum output_format
     CHARACTER
   };
 
-/* Each output format specification (from POSIX `-t spec' or from
+/* 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) ();
+    void (*print_function) (size_t, const char *, const char *);
     char *fmt_string;
     int hexl_mode_trailer;
     int field_width;
@@ -135,11 +140,20 @@ static const int width_bytes[] =
   sizeof (short int),
   sizeof (int),
   sizeof (long int),
+  sizeof (ulonglong_t),
   sizeof (float),
   sizeof (double),
   sizeof (LONG_DOUBLE)
 };
 
+/* Ensure that for each member of `enum size_spec' there is an
+   initializer in the width_bytes array.  */
+struct dummy
+{
+  int assert_width_bytes_matches_size_spec_decl
+    [sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1];
+};
+
 /* Names for some non-printing characters.  */
 static const char *const charname[33] =
 {
@@ -150,46 +164,48 @@ static const char *const charname[33] =
   "sp"
 };
 
-/* A printf control string for printing a file offset.  */
-static const char *output_address_fmt_string;
+/* Address base (8, 10 or 16).  */
+static int address_base;
 
-/* FIXME: make this the number of octal digits in an unsigned long.  */
-#define MAX_ADDRESS_LENGTH 13
+/* The number of octal digits required to represent the largest
+   address value.  */
+#define MAX_ADDRESS_LENGTH \
+  ((sizeof (uintmax_t) * CHAR_BIT + CHAR_BIT - 1) / 3)
 
-/* Space for a normal address, a space, a pseudo address, parentheses
-   around the pseudo address, and a trailing zero byte. */
-static char address_fmt_buffer[2 * MAX_ADDRESS_LENGTH + 4];
-static char address_pad[MAX_ADDRESS_LENGTH + 1];
+/* Width of a normal address.  */
+static int address_pad_len;
 
-static unsigned long int string_min;
-static unsigned long int flag_dump_strings;
+static size_t string_min;
+static int flag_dump_strings;
 
-/* Non-zero if we should recognize the pre-POSIX non-option arguments
+/* Non-zero if we should recognize the older non-option arguments
    that specified at most one file and optional arguments specifying
    offset and pseudo-start address.  */
 static int traditional;
 
 /* Non-zero if an old-style `pseudo-address' was specified.  */
-static long int flag_pseudo_start;
+static int flag_pseudo_start;
 
 /* The difference between the old-style pseudo starting address and
    the number of bytes to skip.  */
-static long int pseudo_offset;
+static uintmax_t pseudo_offset;
 
-/* Function to format an address and optionally an additional parenthesized
-   pseudo-address; it returns the formatted string.  */
-static const char *(*format_address) PARAMS ((long unsigned int));
+/* Function that accepts an address and an optional following char,
+   and prints the address and char to stdout.  */
+static void (*format_address) (uintmax_t, char);
 
 /* The number of input bytes to skip before formatting and writing.  */
-static off_t n_bytes_to_skip = 0;
+static uintmax_t n_bytes_to_skip = 0;
 
-/* When nonzero, MAX_BYTES_TO_FORMAT is the maximum number of bytes
-   to be read and formatted.  Otherwise all input is formatted.  */
+/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and 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 nonzero.  */
-static off_t max_bytes_to_format;
+/* The maximum number of bytes that will be formatted.  */
+static uintmax_t max_bytes_to_format;
+
+/* The offset of the first byte after the last byte to be formatted.  */
+static uintmax_t end_offset;
 
 /* When nonzero and two or more consecutive blocks are equal, format
    only the first block and output an asterisk alone on the following
@@ -200,56 +216,58 @@ static int abbreviate_duplicate_blocks = 1;
 static struct tspec *spec;
 
 /* The number of format specs.  */
-static unsigned int n_specs;
+static size_t n_specs;
 
 /* The allocated length of SPEC.  */
-static unsigned int n_specs_allocated;
+static size_t n_specs_allocated;
 
 /* The number of input bytes formatted per output line.  It must be
    a multiple of the least common multiple of the sizes associated with
    the specified output types.  It should be as large as possible, but
    no larger than 16 -- unless specified with the -w option.  */
-static unsigned int bytes_per_block;
+static size_t bytes_per_block;
 
 /* Human-readable representation of *file_list (for error messages).
    It differs from *file_list only when *file_list is "-".  */
 static char const *input_filename;
 
-/* A NULL-terminated list of the file-arguments from the command line.
-   If no file-arguments were specified, this variable is initialized
-   to { "-", NULL }.  */
+/* A NULL-terminated list of the file-arguments from the command line.  */
 static char const *const *file_list;
 
+/* Initializer for file_list if no file-arguments
+   were specified on the command line.  */
+static char const *const default_file_list[] = {"-", NULL};
+
 /* The input stream associated with the current file.  */
 static FILE *in_stream;
 
 /* If nonzero, at least one of the files we read was standard input.  */
 static int have_read_stdin;
 
-#ifdef HAVE_UNSIGNED_LONG_LONG
-# define LONGEST_INTEGRAL_TYPE unsigned long long
-#else
-# define LONGEST_INTEGRAL_TYPE long int
-#endif
-
-#define MAX_INTEGRAL_TYPE_SIZE sizeof(LONGEST_INTEGRAL_TYPE)
+#define MAX_INTEGRAL_TYPE_SIZE sizeof (ulonglong_t)
 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];
 
+#define COMMON_SHORT_OPTIONS "A:N:abcdfhij:lot:vx"
+
+/* For long options that have no equivalent short option, use a
+   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
+enum
+{
+  TRADITIONAL_OPTION = CHAR_MAX + 1
+};
+
 static struct option const long_options[] =
 {
-  /* POSIX options.  */
   {"skip-bytes", required_argument, NULL, 'j'},
   {"address-radix", required_argument, NULL, 'A'},
   {"read-bytes", required_argument, NULL, 'N'},
   {"format", required_argument, NULL, 't'},
   {"output-duplicates", no_argument, NULL, 'v'},
-
-  /* non-POSIX options.  */
   {"strings", optional_argument, NULL, 's'},
-  {"traditional", no_argument, NULL, 'B'},
+  {"traditional", no_argument, NULL, TRADITIONAL_OPTION},
   {"width", optional_argument, NULL, 'w'},
 
   {GETOPT_HELP_OPTION_DECL},
@@ -270,63 +288,85 @@ 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\
+      fputs (_("\n\
+Write an unambiguous representation, octal bytes by default,\n\
+of FILE to standard output.  With more than one FILE argument,\n\
+concatenate them in the listed order to form the input.\n\
+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\
+"), stdout);
+      fputs (_("\
   -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\
+  -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\
   -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\
+      --traditional           accept arguments in traditional form\n\
+"), stdout);
+      fputs (HELP_OPTION_DESCRIPTION, stdout);
+      fputs (VERSION_OPTION_DESCRIPTION, stdout);
+      fputs (_("\
 \n\
-Pre-POSIX format specifications may be intermixed, they accumulate:\n\
+Traditional 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\
+"), stdout);
+      fputs (_("\
   -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 (_("\
+"), stdout);
+      fputs (_("\
 \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\
+hexadecimal, 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\
   c          ASCII character or backslash escape\n\
+"), stdout);
+      fputs (_("\
   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\
+"), stdout);
+      fputs (_("\
 \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.  -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 <bug-textutils@gnu.org>."));
+of output.  \
+"), 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\
+"), stdout);
+      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     }
   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
@@ -359,10 +399,9 @@ lcm (unsigned int u, unsigned int v)
 }
 
 static void
-print_s_char (long unsigned int n_bytes, const char *block,
-             const char *fmt_string)
+print_s_char (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes; i > 0; i--)
     {
       int tmp = (unsigned) *(const unsigned char *) block;
@@ -375,10 +414,9 @@ print_s_char (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_char (long unsigned int n_bytes, const char *block,
-           const char *fmt_string)
+print_char (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes; i > 0; i--)
     {
       unsigned int tmp = *(const unsigned char *) block;
@@ -388,10 +426,9 @@ print_char (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_s_short (long unsigned int n_bytes, const char *block,
-              const char *fmt_string)
+print_s_short (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (unsigned short); i > 0; i--)
     {
       int tmp = (unsigned) *(const unsigned short *) block;
@@ -404,10 +441,9 @@ print_s_short (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_short (long unsigned int n_bytes, const char *block,
-            const char *fmt_string)
+print_short (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (unsigned short); i > 0; i--)
     {
       unsigned int tmp = *(const unsigned short *) block;
@@ -417,10 +453,9 @@ print_short (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_int (long unsigned int n_bytes, const char *block,
-          const char *fmt_string)
+print_int (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (unsigned int); i > 0; i--)
     {
       unsigned int tmp = *(const unsigned int *) block;
@@ -430,10 +465,9 @@ print_int (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_long (long unsigned int n_bytes, const char *block,
-           const char *fmt_string)
+print_long (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (unsigned long); i > 0; i--)
     {
       unsigned long tmp = *(const unsigned long *) block;
@@ -442,26 +476,22 @@ print_long (long unsigned int n_bytes, const char *block,
     }
 }
 
-#ifdef HAVE_UNSIGNED_LONG_LONG
 static void
-print_long_long (long unsigned int n_bytes, const char *block,
-                const char *fmt_string)
+print_long_long (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
-  for (i = n_bytes / sizeof (unsigned long long); i > 0; i--)
+  size_t i;
+  for (i = n_bytes / sizeof (ulonglong_t); i > 0; i--)
     {
-      unsigned long long tmp = *(const unsigned long long *) block;
+      ulonglong_t tmp = *(const ulonglong_t *) block;
       printf (fmt_string, tmp);
-      block += sizeof (unsigned long long);
+      block += sizeof (ulonglong_t);
     }
 }
-#endif
 
 static void
-print_float (long unsigned int n_bytes, const char *block,
-            const char *fmt_string)
+print_float (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (float); i > 0; i--)
     {
       float tmp = *(const float *) block;
@@ -471,10 +501,9 @@ print_float (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_double (long unsigned int n_bytes, const char *block,
-             const char *fmt_string)
+print_double (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (double); i > 0; i--)
     {
       double tmp = *(const double *) block;
@@ -485,10 +514,9 @@ print_double (long unsigned int n_bytes, const char *block,
 
 #ifdef HAVE_LONG_DOUBLE
 static void
-print_long_double (long unsigned int n_bytes, const char *block,
-                  const char *fmt_string)
+print_long_double (size_t n_bytes, const char *block, const char *fmt_string)
 {
-  int i;
+  size_t i;
   for (i = n_bytes / sizeof (LONG_DOUBLE); i > 0; i--)
     {
       LONG_DOUBLE tmp = *(const LONG_DOUBLE *) block;
@@ -500,9 +528,9 @@ print_long_double (long unsigned int n_bytes, const char *block,
 #endif
 
 static void
-dump_hexl_mode_trailer (long unsigned int n_bytes, const char *block)
+dump_hexl_mode_trailer (size_t n_bytes, const char *block)
 {
-  int i;
+  size_t i;
   fputs ("  >", stdout);
   for (i = n_bytes; i > 0; i--)
     {
@@ -515,10 +543,10 @@ dump_hexl_mode_trailer (long unsigned int n_bytes, const char *block)
 }
 
 static void
-print_named_ascii (long unsigned int n_bytes, const char *block,
-                  const char *unused_fmt_string)
+print_named_ascii (size_t n_bytes, const char *block,
+                  const char *unused_fmt_string ATTRIBUTE_UNUSED)
 {
-  int i;
+  size_t i;
   for (i = n_bytes; i > 0; i--)
     {
       unsigned int c = *(const unsigned char *) block;
@@ -542,10 +570,10 @@ print_named_ascii (long unsigned int n_bytes, const char *block,
 }
 
 static void
-print_ascii (long unsigned int n_bytes, const char *block,
-            const char *unused_fmt_string)
+print_ascii (size_t n_bytes, const char *block,
+            const char *unused_fmt_string ATTRIBUTE_UNUSED)
 {
-  int i;
+  size_t i;
   for (i = n_bytes; i > 0; i--)
     {
       unsigned int c = *(const unsigned char *) block;
@@ -622,7 +650,7 @@ simple_strtoul (const char *s, const char **p, long unsigned int *val)
   return 0;
 }
 
-/* If S points to a single valid POSIX-style od format string, put
+/* If S points to a single valid modern 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,
@@ -647,7 +675,7 @@ decode_one_format (const char *s_orig, const char *s, const char **next,
   enum output_format fmt;
   const char *pre_fmt_string;
   char *fmt_string;
-  void (*print_function) ();
+  void (*print_function) (size_t, const char *, const char *);
   const char *p;
   unsigned int c;
   unsigned int field_width = 0;
@@ -697,7 +725,7 @@ decode_one_format (const char *s_orig, const char *s, const char **next,
            size = sizeof (int);
          else
            {
-             if (size > MAX_INTEGRAL_TYPE_SIZE
+             if (MAX_INTEGRAL_TYPE_SIZE < size
                  || integral_type_size[size] == NO_SIZE)
                {
                  error (0, 0, _("invalid type string `%s';\n\
@@ -709,6 +737,11 @@ this system doesn't provide a %lu-byte integral type"), s_orig, size);
          break;
        }
 
+#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format)     \
+  ((Spec) == LONG_LONG ? (Max_format)                                  \
+   : ((Spec) == LONG ? (Long_format)                                   \
+      : (Min_format)))                                                 \
+
 #define FMT_BYTES_ALLOCATED 9
       fmt_string = xmalloc (FMT_BYTES_ALLOCATED);
 
@@ -718,32 +751,30 @@ this system doesn't provide a %lu-byte integral type"), s_orig, size);
        {
        case 'd':
          fmt = SIGNED_DECIMAL;
-         sprintf (fmt_string, " %%%u%sd",
+         sprintf (fmt_string, " %%%u%s",
                   (field_width = bytes_to_signed_dec_digits[size]),
-                  (size_spec == LONG ? "l"
-                   : (size_spec == LONG_LONG ? "ll"
-                      : "")));
+                  ISPEC_TO_FORMAT (size_spec, "d", "ld", PRIdMAX));
          break;
 
        case 'o':
          fmt = OCTAL;
-         sprintf (fmt_string, " %%0%u%so",
+         sprintf (fmt_string, " %%0%u%s",
                   (field_width = bytes_to_oct_digits[size]),
-                  (size_spec == LONG ? "l" : ""));
+                  ISPEC_TO_FORMAT (size_spec, "o", "lo", PRIoMAX));
          break;
 
        case 'u':
          fmt = UNSIGNED_DECIMAL;
-         sprintf (fmt_string, " %%%u%su",
+         sprintf (fmt_string, " %%%u%s",
                   (field_width = bytes_to_unsigned_dec_digits[size]),
-                  (size_spec == LONG ? "l" : ""));
+                  ISPEC_TO_FORMAT (size_spec, "u", "lu", PRIuMAX));
          break;
 
        case 'x':
          fmt = HEXADECIMAL;
-         sprintf (fmt_string, " %%0%u%sx",
+         sprintf (fmt_string, " %%0%u%s",
                   (field_width = bytes_to_hex_digits[size]),
-                  (size_spec == LONG ? "l" : ""));
+                  ISPEC_TO_FORMAT (size_spec, "x", "lx", PRIxMAX));
          break;
 
        default:
@@ -903,7 +934,90 @@ this system doesn't provide a %lu-byte floating point type"), s_orig, size);
   return 0;
 }
 
-/* Decode the POSIX-style od format string S.  Append the decoded
+/* Given a list of one or more input filenames FILE_LIST, set the global
+   file pointer IN_STREAM and the global string INPUT_FILENAME to the
+   first one that can be successfully opened. Modify FILE_LIST to
+   reference the next filename in the list.  A file name of "-" is
+   interpreted as standard input.  If any file open fails, give an error
+   message and return nonzero.  */
+
+static int
+open_next_file (void)
+{
+  int err = 0;
+
+  do
+    {
+      input_filename = *file_list;
+      if (input_filename == NULL)
+       return err;
+      ++file_list;
+
+      if (STREQ (input_filename, "-"))
+       {
+         input_filename = _("standard input");
+         in_stream = stdin;
+         have_read_stdin = 1;
+       }
+      else
+       {
+         in_stream = fopen (input_filename, "r");
+         if (in_stream == NULL)
+           {
+             error (0, errno, "%s", input_filename);
+             err = 1;
+           }
+       }
+    }
+  while (in_stream == NULL);
+
+  if (limit_bytes_to_format && !flag_dump_strings)
+    SETVBUF (in_stream, NULL, _IONBF, 0);
+  SET_BINARY (fileno (in_stream));
+
+  return err;
+}
+
+/* Test whether there have been errors on in_stream, and close it if
+   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.  IN_ERRNO, if nonzero, is the error number
+   corresponding to the most recent action for IN_STREAM.  */
+
+static int
+check_and_close (int in_errno)
+{
+  int err = 0;
+
+  if (in_stream != NULL)
+    {
+      if (ferror (in_stream))
+       {
+         error (0, in_errno, _("%s: read error"), input_filename);
+         if (in_stream != stdin)
+           fclose (in_stream);
+         err = 1;
+       }
+      else if (in_stream != stdin && fclose (in_stream) == EOF)
+       {
+         error (0, errno, "%s", input_filename);
+         err = 1;
+       }
+
+      in_stream = NULL;
+    }
+
+  if (ferror (stdout))
+    {
+      error (0, 0, _("write error"));
+      err = 1;
+    }
+
+  return err;
+}
+
+/* Decode the modern od format string S.  Append the decoded
    representation to the global array SPEC, reallocating SPEC if
    necessary.  Return zero if S is valid, nonzero otherwise.  */
 
@@ -927,9 +1041,7 @@ decode_format_string (const char *s)
       if (n_specs >= n_specs_allocated)
        {
          n_specs_allocated = 1 + (3 * n_specs_allocated) / 2;
-         spec = (struct tspec *) xrealloc ((char *) spec,
-                                           (n_specs_allocated
-                                            * sizeof (struct tspec)));
+         spec = xrealloc (spec, (n_specs_allocated * sizeof (struct tspec)));
        }
 
       memcpy ((char *) &spec[n_specs], (char *) &tspec,
@@ -944,42 +1056,21 @@ decode_format_string (const char *s)
    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 return
-   nonzero.  When possible, use seek- rather than read operations to
-   advance IN_STREAM.  A file name of "-" is interpreted as standard
-   input.  */
+   nonzero.  When possible, use seek rather than read operations to
+   advance IN_STREAM.  */
 
 static int
-skip (off_t n_skip)
+skip (uintmax_t n_skip)
 {
-  int err;
+  int err = 0;
+  int in_errno = 0;
 
-  err = 0;
-  for ( /* empty */ ; *file_list != NULL; ++file_list)
+  if (n_skip == 0)
+    return 0;
+
+  while (in_stream != NULL)    /* EOF.  */
     {
       struct stat file_stats;
-      int j;
-
-      if (STREQ (*file_list, "-"))
-       {
-         input_filename = _("standard input");
-         in_stream = stdin;
-         have_read_stdin = 1;
-       }
-      else
-       {
-         input_filename = *file_list;
-         in_stream = fopen (input_filename, "r");
-         if (in_stream == NULL)
-           {
-             error (0, errno, "%s", input_filename);
-             err = 1;
-             continue;
-           }
-       }
-      SET_BINARY (fileno (in_stream));
-
-      if (n_skip == 0)
-       break;
 
       /* First try seeking.  For large offsets, this extra work is
         worthwhile.  If the offset is below some threshold it may be
@@ -991,58 +1082,66 @@ skip (off_t n_skip)
             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))
+      if (fstat (fileno (in_stream), &file_stats) == 0)
        {
-         error (0, errno, "%s", input_filename);
-         err = 1;
-         continue;
-       }
+         /* 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.  */
 
-      /* 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))
-       {
-         if (n_skip >= file_stats.st_size)
+         if (S_ISREG (file_stats.st_mode) && 0 <= file_stats.st_size)
            {
-             n_skip -= file_stats.st_size;
-             if (in_stream != stdin && fclose (in_stream) == EOF)
+             if ((uintmax_t) file_stats.st_size <= n_skip)
+               n_skip -= file_stats.st_size;
+             else
                {
-                 error (0, errno, "%s", input_filename);
-                 err = 1;
+                 if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
+                   {
+                     in_errno = errno;
+                     err = 1;
+                   }
+                 n_skip = 0;
                }
-             continue;
            }
+
+         /* If it's not a regular file with nonnegative size,
+            position the file pointer by reading.  */
+
          else
            {
-             if (0 <= lseek (fileno (in_stream), n_skip, SEEK_CUR))
+             char buf[BUFSIZ];
+             size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
+
+             while (0 < n_skip)
                {
-                 n_skip = 0;
-                 break;
+                 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;
+                     err = 1;
+                     n_skip = 0;
+                     break;
+                   }
                }
            }
-       }
 
-      /* Seek didn't work or wasn't attempted;  position the file pointer
-        by reading.  */
+         if (n_skip == 0)
+           break;
+       }
 
-      for (j = n_skip / BUFSIZ; j >= 0; j--)
+      else   /* cannot fstat() file */
        {
-         char buf[BUFSIZ];
-         size_t n_bytes_to_read = (j > 0
-                                   ? BUFSIZ
-                                   : n_skip % BUFSIZ);
-         size_t n_bytes_read;
-         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)
-           break;
+         error (0, errno, "%s", input_filename);
+         err = 1;
        }
 
-      if (n_skip == 0)
-       break;
+      err |= check_and_close (in_errno);
+
+      err |= open_next_file ();
     }
 
   if (n_skip != 0)
@@ -1051,32 +1150,64 @@ skip (off_t n_skip)
   return err;
 }
 
-static const char *
-format_address_none (long unsigned int address)
+static void
+format_address_none (uintmax_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
 {
-  return "";
 }
 
-static const char *
-format_address_std (long unsigned int address)
+static void
+format_address_std (uintmax_t address, char c)
 {
-  const char *address_string;
+  char buf[MAX_ADDRESS_LENGTH + 2];
+  char *p = buf + sizeof buf;
+  char const *pbound;
+
+  *--p = '\0';
+  *--p = c;
+  pbound = p - address_pad_len;
+
+  /* Use a special case of the code for each base.  This is measurably
+     faster than generic code.  */
+  switch (address_base)
+    {
+    case 8:
+      do
+       *--p = '0' + (address & 7);
+      while ((address >>= 3) != 0);
+      break;
 
-  sprintf (address_fmt_buffer, output_address_fmt_string, address);
-  address_string = address_fmt_buffer;
-  return address_string;
+    case 10:
+      do
+       *--p = '0' + (address % 10);
+      while ((address /= 10) != 0);
+      break;
+
+    case 16:
+      do
+       *--p = "0123456789abcdef"[address & 15];
+      while ((address >>= 4) != 0);
+      break;
+    }
+
+  while (pbound < p)
+    *--p = '0';
+
+  fputs (p, stdout);
 }
 
-static const char *
-format_address_label (long unsigned int address)
+static void
+format_address_paren (uintmax_t address, char c)
 {
-  const char *address_string;
-  assert (output_address_fmt_string != NULL);
+  putchar ('(');
+  format_address_std (address, ')');
+  putchar (c);
+}
 
-  sprintf (address_fmt_buffer, output_address_fmt_string,
-          address, address + pseudo_offset);
-  address_string = address_fmt_buffer;
-  return address_string;
+static void
+format_address_label (uintmax_t address, char c)
+{
+  format_address_std (address, ' ');
+  format_address_paren (address + pseudo_offset, c);
 }
 
 /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
@@ -1091,7 +1222,7 @@ format_address_label (long unsigned int address)
    only when it has not been padded to length BYTES_PER_BLOCK.  */
 
 static void
-write_block (long unsigned int current_offset, long unsigned int n_bytes,
+write_block (uintmax_t current_offset, size_t n_bytes,
             const char *prev_block, const char *curr_block)
 {
   static int first = 1;
@@ -1116,16 +1247,15 @@ write_block (long unsigned int current_offset, long unsigned int n_bytes,
     }
   else
     {
-      unsigned int i;
+      size_t i;
 
       prev_pair_equal = 0;
       for (i = 0; i < n_specs; i++)
        {
-         const char *addr_or_pad = (i == 0
-                                    ? format_address (current_offset)
-                                    : address_pad);
-
-         fputs (addr_or_pad, stdout);
+         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)
            {
@@ -1142,132 +1272,66 @@ write_block (long unsigned int current_offset, long unsigned int n_bytes,
   first = 0;
 }
 
-/* Test whether there have been errors on in_stream, and close it if
-   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 (void)
-{
-  int err;
-
-  err = 0;
-  if (ferror (in_stream))
-    {
-      error (0, errno, "%s", input_filename);
-      if (in_stream != stdin)
-       fclose (in_stream);
-      err = 1;
-    }
-  else if (in_stream != stdin && fclose (in_stream) == EOF)
-    {
-      error (0, errno, "%s", input_filename);
-      err = 1;
-    }
-
-  if (ferror (stdout))
-    {
-      error (0, errno, _("standard output"));
-      err = 1;
-    }
-
-  return err;
-}
-
 /* Read a single byte into *C from the concatenation of the input files
    named in the global array FILE_LIST.  On the first call to this
    function, the global variable IN_STREAM is expected to be an open
-   stream associated with the input file *FILE_LIST.  If IN_STREAM is
-   at end-of-file, close it and update the global variables IN_STREAM,
-   FILE_LIST, and INPUT_FILENAME so they correspond to the next file in
-   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 nonzero if any errors
-   occured, zero otherwise.  */
+   stream associated with the input file INPUT_FILENAME.  If IN_STREAM
+   is at end-of-file, close it and update the global variables IN_STREAM
+   and INPUT_FILENAME so they correspond to the next file in the list.
+   Then try to read a byte from the newly opened file.  Repeat if
+   necessary until EOF is reached for the last file in FILE_LIST, then
+   set *C to EOF and return.  Subsequent calls do likewise.  The return
+   value is nonzero if any errors occured, zero otherwise.  */
 
 static int
 read_char (int *c)
 {
-  int err;
+  int err = 0;
 
-  if (*file_list == NULL)
-    {
-      *c = EOF;
-      return 0;
-    }
+  *c = EOF;
 
-  err = 0;
-  while (1)
+  while (in_stream != NULL)    /* EOF.  */
     {
       *c = fgetc (in_stream);
 
       if (*c != EOF)
-       return err;
+       break;
 
-      err |= check_and_close ();
+      err |= check_and_close (errno);
 
-      do
-       {
-         ++file_list;
-         if (*file_list == NULL)
-           return err;
-
-         if (STREQ (*file_list, "-"))
-           {
-             input_filename = _("standard input");
-             in_stream = stdin;
-             have_read_stdin = 1;
-           }
-         else
-           {
-             input_filename = *file_list;
-             in_stream = fopen (input_filename, "r");
-             if (in_stream == NULL)
-               {
-                 error (0, errno, "%s", input_filename);
-                 err = 1;
-               }
-           }
-         SET_BINARY (fileno (in_stream));
-       }
-      while (in_stream == NULL);
+      err |= open_next_file ();
     }
+
+  return err;
 }
 
 /* Read N bytes into BLOCK from the concatenation of the input files
    named in the global array FILE_LIST.  On the first call to this
    function, the global variable IN_STREAM is expected to be an open
-   stream associated with the input file *FILE_LIST.  On subsequent
-   calls, if *FILE_LIST is NULL, don't modify BLOCK and return zero.
-   If all N bytes cannot be read from IN_STREAM, close IN_STREAM and
-   update the global variables IN_STREAM, FILE_LIST, and INPUT_FILENAME.
-   Then try to read the remaining bytes from the newly opened file.
-   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 nonzero.
-   Otherwise return zero.  */
+   stream associated with the input file INPUT_FILENAME.  If all N
+   bytes cannot be read from IN_STREAM, close IN_STREAM and update
+   the global variables IN_STREAM and INPUT_FILENAME.  Then try to
+   read the remaining bytes from the newly opened file.  Repeat if
+   necessary until EOF is reached for the last file in FILE_LIST.
+   On subsequent calls, don't modify BLOCK and return zero.  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 nonzero.  Otherwise return zero.  */
 
 static int
 read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
 {
-  int err;
+  int err = 0;
 
-  assert (n > 0 && n <= bytes_per_block);
+  assert (0 < n && n <= bytes_per_block);
 
   *n_bytes_in_buffer = 0;
 
   if (n == 0)
     return 0;
 
-  if (*file_list == NULL)
-    return 0;                  /* EOF.  */
-
-  err = 0;
-  while (1)
+  while (in_stream != NULL)    /* EOF.  */
     {
       size_t n_needed;
       size_t n_read;
@@ -1278,36 +1342,14 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
       *n_bytes_in_buffer += n_read;
 
       if (n_read == n_needed)
-       return err;
+       break;
 
-      err |= check_and_close ();
+      err |= check_and_close (errno);
 
-      do
-       {
-         ++file_list;
-         if (*file_list == NULL)
-           return err;
-
-         if (STREQ (*file_list, "-"))
-           {
-             input_filename = _("standard input");
-             in_stream = stdin;
-             have_read_stdin = 1;
-           }
-         else
-           {
-             input_filename = *file_list;
-             in_stream = fopen (input_filename, "r");
-             if (in_stream == NULL)
-               {
-                 error (0, errno, "%s", input_filename);
-                 err = 1;
-               }
-           }
-         SET_BINARY (fileno (in_stream));
-       }
-      while (in_stream == NULL);
+      err |= open_next_file ();
     }
+
+  return err;
 }
 
 /* Return the least common multiple of the sizes associated
@@ -1316,7 +1358,7 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
 static int
 get_lcm (void)
 {
-  unsigned int i;
+  size_t i;
   int l_c_m = 1;
 
   for (i = 0; i < n_specs; i++)
@@ -1324,19 +1366,17 @@ get_lcm (void)
   return l_c_m;
 }
 
-/* If S is a valid pre-POSIX offset specification with an optional leading '+'
-   return the offset it denotes.  Otherwise, return -1.  */
+/* If S is a valid traditional offset specification with an optional
+   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
 
-off_t
-parse_old_offset (const char *s)
+static int
+parse_old_offset (const char *s, uintmax_t *offset)
 {
   int radix;
-  off_t offset;
   enum strtol_error s_err;
-  long unsigned int tmp;
 
   if (*s == '\0')
-    return -1;
+    return 0;
 
   /* Skip over any leading '+'. */
   if (s[0] == '+')
@@ -1355,14 +1395,13 @@ parse_old_offset (const char *s)
        radix = 8;
     }
 
-  s_err = xstrtoul (s, NULL, radix, &tmp, "Bb");
+  s_err = xstrtoumax (s, NULL, radix, offset, "Bb");
   if (s_err != LONGINT_OK)
     {
       STRTOL_FAIL_WARN (s, _("old-style offset"), s_err);
-      return -1;
+      return 0;
     }
-  offset = tmp;
-  return offset;
+  return 1;
 }
 
 /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
@@ -1381,14 +1420,13 @@ static int
 dump (void)
 {
   char *block[2];
-  off_t current_offset;
-  off_t end_offset IF_LINT (= 0);
+  uintmax_t current_offset;
   int idx;
   int err;
   size_t n_bytes_read;
 
-  block[0] = (char *) alloca (bytes_per_block);
-  block[1] = (char *) alloca (bytes_per_block);
+  block[0] = alloca (bytes_per_block);
+  block[1] = alloca (bytes_per_block);
 
   current_offset = n_bytes_to_skip;
 
@@ -1396,8 +1434,6 @@ dump (void)
   err = 0;
   if (limit_bytes_to_format)
     {
-      end_offset = n_bytes_to_skip + max_bytes_to_format;
-
       while (1)
        {
          size_t n_needed;
@@ -1407,7 +1443,7 @@ dump (void)
              break;
            }
          n_needed = MIN (end_offset - current_offset,
-                         (off_t) bytes_per_block);
+                         (uintmax_t) bytes_per_block);
          err |= read_block (n_needed, block[idx], &n_bytes_read);
          if (n_bytes_read < bytes_per_block)
            break;
@@ -1442,7 +1478,7 @@ dump (void)
 
       /* Make bytes_to_write the smallest multiple of l_c_m that
         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);
+      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,
@@ -1450,11 +1486,10 @@ dump (void)
       current_offset += n_bytes_read;
     }
 
-  if (output_address_fmt_string != NULL)
-    printf ("%s\n", format_address (current_offset));
+  format_address (current_offset, '\n');
 
-  if (limit_bytes_to_format && current_offset > end_offset)
-    err |= check_and_close ();
+  if (limit_bytes_to_format && current_offset >= end_offset)
+    err |= check_and_close (0);
 
   return err;
 }
@@ -1463,7 +1498,7 @@ dump (void)
    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 nonzero if an error
+   traditional version of od.  Return nonzero if an error
    occurs.  Otherwise, return zero.  */
 
 static int
@@ -1471,21 +1506,20 @@ dump_strings (void)
 {
   size_t bufsize = MAX (100, string_min);
   char *buf = xmalloc (bufsize);
-  off_t address = n_bytes_to_skip;
+  uintmax_t address = n_bytes_to_skip;
   int err;
 
   err = 0;
   while (1)
     {
-      unsigned int i;
+      size_t 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 -
-                        (off_t) string_min))
+         && (end_offset < string_min || end_offset - string_min <= address))
        break;
 
       for (i = 0; i < string_min; i++)
@@ -1505,8 +1539,7 @@ dump_strings (void)
 
       /* 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 < n_bytes_to_skip + max_bytes_to_format)
+      while (!limit_bytes_to_format || address < end_offset)
        {
          if (i == bufsize)
            {
@@ -1530,10 +1563,8 @@ dump_strings (void)
       /* 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.  */
       buf[i] = 0;
-      if (output_address_fmt_string != NULL)
-       {
-         printf ("%s ", format_address (address - i - 1));
-       }
+      format_address (address - i - 1, ' ');
+
       for (i = 0; (c = buf[i]); i++)
        {
          switch (c)
@@ -1574,11 +1605,11 @@ dump_strings (void)
     }
 
   /* We reach this point only if we search through
-     (max_bytes_to_format - string_min) bytes before reachine EOF.  */
+     (max_bytes_to_format - string_min) bytes before reaching EOF.  */
 
   free (buf);
 
-  err |= check_and_close ();
+  err |= check_and_close (0);
   return err;
 }
 
@@ -1587,18 +1618,21 @@ main (int argc, char **argv)
 {
   int c;
   int n_files;
-  unsigned int i;
-  unsigned int l_c_m;
-  unsigned int address_pad_len;
-  unsigned long int desired_width;
+  size_t i;
+  int l_c_m;
+  size_t desired_width IF_LINT (= 0);
   int width_specified = 0;
   int n_failed_decodes = 0;
   int err;
+  char const *short_options = (posix2_version () < 200112
+                              ? COMMON_SHORT_OPTIONS "s::w::"
+                              : COMMON_SHORT_OPTIONS "s:w:");
 
   /* The old-style `pseudo starting address' to be printed in parentheses
      after any true address.  */
-  long int pseudo_start IF_LINT (= 0);
+  uintmax_t pseudo_start IF_LINT (= 0);
 
+  initialize_main (&argc, &argv);
   program_name = argv[0];
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, LOCALEDIR);
@@ -1615,8 +1649,10 @@ 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;
-#ifdef HAVE_UNSIGNED_LONG_LONG
-  integral_type_size[sizeof (long long)] = LONG_LONG;
+#if HAVE_UNSIGNED_LONG_LONG
+  /* If `long' and `long long' have the same size, it's fine
+     to overwrite the entry for `long' with this one.  */
+  integral_type_size[sizeof (ulonglong_t)] = LONG_LONG;
 #endif
 
   for (i = 0; i <= MAX_FP_TYPE_SIZE; i++)
@@ -1631,17 +1667,17 @@ main (int argc, char **argv)
 
   n_specs = 0;
   n_specs_allocated = 5;
-  spec = (struct tspec *) xmalloc (n_specs_allocated * sizeof (struct tspec));
+  spec = xmalloc (n_specs_allocated * sizeof (struct tspec));
 
-  output_address_fmt_string = "%07o";
   format_address = format_address_std;
+  address_base = 8;
   address_pad_len = 7;
   flag_dump_strings = 0;
 
-  while ((c = getopt_long (argc, argv, "abcdfhilos::xw::A:j:N:t:v",
-                          long_options, NULL)) != -1)
+  while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
+        != -1)
     {
-      unsigned long int tmp;
+      uintmax_t tmp;
       enum strtol_error s_err;
 
       switch (c)
@@ -1653,22 +1689,21 @@ main (int argc, char **argv)
          switch (optarg[0])
            {
            case 'd':
-             output_address_fmt_string = "%07d";
              format_address = format_address_std;
+             address_base = 10;
              address_pad_len = 7;
              break;
            case 'o':
-             output_address_fmt_string = "%07o";
              format_address = format_address_std;
+             address_base = 8;
              address_pad_len = 7;
              break;
            case 'x':
-             output_address_fmt_string = "%06x";
              format_address = format_address_std;
+             address_base = 16;
              address_pad_len = 6;
              break;
            case 'n':
-             output_address_fmt_string = NULL;
              format_address = format_address_none;
              address_pad_len = 0;
              break;
@@ -1682,8 +1717,7 @@ it must be one character from [doxn]"),
          break;
 
        case 'j':
-         s_err = xstrtoul (optarg, NULL, 0, &tmp, "bkm");
-         n_bytes_to_skip = tmp;
+         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;
@@ -1691,17 +1725,9 @@ it must be one character from [doxn]"),
        case 'N':
          limit_bytes_to_format = 1;
 
-         /* 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;
+         s_err = xstrtoumax (optarg, NULL, 0, &max_bytes_to_format, "bkm");
          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,11 +1735,18 @@ the maximum\nrepresentable value of type `long'"), optarg);
            string_min = 3;
          else
            {
-             s_err = xstrtoul (optarg, NULL, 0, &string_min, "bkm");
+             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;
+         flag_dump_strings = 1;
          break;
 
        case 't':
@@ -1725,12 +1758,12 @@ the maximum\nrepresentable value of type `long'"), optarg);
          abbreviate_duplicate_blocks = 0;
          break;
 
-       case 'B':
+       case TRADITIONAL_OPTION:
          traditional = 1;
          break;
 
-         /* The next several cases map the old, pre-POSIX format
-            specification options to the corresponding POSIX format
+         /* 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.  */
 
@@ -1753,6 +1786,13 @@ the maximum\nrepresentable value of type `long'"), optarg);
          CASE_OLD_ARG ('o', "o2");
          CASE_OLD_ARG ('x', "x2");
 
+         /* FIXME: POSIX 1003.1-2001 with XSI requires this:
+
+            CASE_OLD_ARG ('s', "d2");
+
+            for the traditional syntax, but this conflicts with case
+            's' above. */
+
 #undef CASE_OLD_ARG
 
        case 'w':
@@ -1763,18 +1803,22 @@ the maximum\nrepresentable value of type `long'"), optarg);
            }
          else
            {
-             s_err = xstrtoul (optarg, NULL, 10, &desired_width, "");
+             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);
+       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
 
        default:
-         usage (1);
+         usage (EXIT_FAILURE);
          break;
        }
     }
@@ -1788,19 +1832,22 @@ the maximum\nrepresentable value of type `long'"), optarg);
 
   n_files = argc - optind;
 
-  /* If the --backward-compatible option is used, there may be from
+  /* 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]]]
-     The offset and pseudo_start have the same syntax.  */
+     The offset and pseudo_start have the same syntax.
+
+     FIXME: POSIX 1003.1-2001 with XSI requires support for the
+     traditional syntax even if --traditional is not given.  */
 
   if (traditional)
     {
-      off_t offset;
+      uintmax_t offset;
 
       if (n_files == 1)
        {
-         if ((offset = parse_old_offset (argv[optind])) >= 0)
+         if (parse_old_offset (argv[optind], &offset))
            {
              n_bytes_to_skip = offset;
              --n_files;
@@ -1809,9 +1856,9 @@ the maximum\nrepresentable value of type `long'"), optarg);
        }
       else if (n_files == 2)
        {
-         off_t o1, o2;
-         if ((o1 = parse_old_offset (argv[optind])) >= 0
-             && (o2 = parse_old_offset (argv[optind + 1])) >= 0)
+         uintmax_t o1, o2;
+         if (parse_old_offset (argv[optind], &o1)
+             && parse_old_offset (argv[optind + 1], &o2))
            {
              n_bytes_to_skip = o1;
              flag_pseudo_start = 1;
@@ -1819,7 +1866,7 @@ the maximum\nrepresentable value of type `long'"), optarg);
              argv += 2;
              n_files -= 2;
            }
-         else if ((o2 = parse_old_offset (argv[optind + 1])) >= 0)
+         else if (parse_old_offset (argv[optind + 1], &o2))
            {
              n_bytes_to_skip = o2;
              --n_files;
@@ -1831,14 +1878,14 @@ the maximum\nrepresentable value of type `long'"), optarg);
              error (0, 0,
                     _("invalid second operand in compatibility mode `%s'"),
                     argv[optind + 1]);
-             usage (1);
+             usage (EXIT_FAILURE);
            }
        }
       else if (n_files == 3)
        {
-         off_t o1, o2;
-         if ((o1 = parse_old_offset (argv[optind + 1])) >= 0
-             && (o2 = parse_old_offset (argv[optind + 2])) >= 0)
+         uintmax_t o1, o2;
+         if (parse_old_offset (argv[optind + 1], &o1)
+             && parse_old_offset (argv[optind + 2], &o2))
            {
              n_bytes_to_skip = o1;
              flag_pseudo_start = 1;
@@ -1851,40 +1898,35 @@ the maximum\nrepresentable value of type `long'"), optarg);
            {
              error (0, 0,
            _("in compatibility mode, the last two arguments must be offsets"));
-             usage (1);
+             usage (EXIT_FAILURE);
            }
        }
       else if (n_files > 3)
        {
          error (0, 0,
                 _("compatibility mode supports at most three arguments"));
-         usage (1);
+         usage (EXIT_FAILURE);
        }
 
       if (flag_pseudo_start)
        {
-         static char buf[10];
-
-         if (output_address_fmt_string == NULL)
+         if (format_address == format_address_none)
            {
-             output_address_fmt_string = "(%07o)";
-             format_address = format_address_std;
+             address_base = 8;
+             address_pad_len = 7;
+             format_address = format_address_paren;
            }
          else
-           {
-             sprintf (buf, "%s (%s)",
-                      output_address_fmt_string,
-                      output_address_fmt_string);
-             output_address_fmt_string = buf;
-             format_address = format_address_label;
-           }
+           format_address = format_address_label;
        }
     }
 
-  assert (address_pad_len <= MAX_ADDRESS_LENGTH);
-  for (i = 0; i < address_pad_len; i++)
-    address_pad[i] = ' ';
-  address_pad[address_pad_len] = '\0';
+  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"));
+    }
 
   if (n_specs == 0)
     {
@@ -1899,17 +1941,27 @@ the maximum\nrepresentable value of type `long'"), optarg);
     }
 
   if (n_files > 0)
-    file_list = (char const *const *) &argv[optind];
+    {
+      /* Set the global pointer FILE_LIST so that it
+        references the first file-argument on the command-line.  */
+
+      file_list = (char const *const *) &argv[optind];
+    }
   else
     {
-      /* If no files were listed on the command line, set up the
-        global array FILE_LIST so that it contains the null-terminated
-        list of one name: "-".  */
-      static char const *const default_file_list[] = {"-", NULL};
+      /* 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: "-".  */
 
       file_list = default_file_list;
     }
 
+  /* open the first input file */
+  err |= open_next_file ();
+  if (in_stream == NULL)
+    goto cleanup;
+
+  /* skip over any unwanted header bytes */
   err |= skip (n_bytes_to_skip);
   if (in_stream == NULL)
     goto cleanup;
@@ -1926,14 +1978,14 @@ the maximum\nrepresentable value of type `long'"), optarg);
       else
        {
          error (0, 0, _("warning: invalid width %lu; using %d instead"),
-                desired_width, l_c_m);
+                (unsigned long) 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 * (int) (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;
     }