Remove unnecessary casts of alloca, since now it's guaranteed to be (void *).
[platform/upstream/coreutils.git] / src / od.c
index a2798e6..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-2001 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) PARAMS ((size_t, const char *, const char *));
+    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] =
 {
@@ -164,7 +178,7 @@ static int address_pad_len;
 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;
@@ -178,7 +192,7 @@ static uintmax_t pseudo_offset;
 
 /* Function that accepts an address and an optional following char,
    and prints the address and char to stdout.  */
-static void (*format_address) PARAMS ((uintmax_t, char));
+static void (*format_address) (uintmax_t, char);
 
 /* The number of input bytes to skip before formatting and writing.  */
 static uintmax_t n_bytes_to_skip = 0;
@@ -230,32 +244,30 @@ static FILE *in_stream;
 /* If nonzero, at least one of the files we read was standard input.  */
 static int have_read_stdin;
 
-#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
-
 #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},
@@ -282,7 +294,11 @@ 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\
-Mandatory arguments to long options are mandatory for short options too.\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\n\
 "), stdout);
@@ -292,13 +308,13 @@ Mandatory arguments to long options are mandatory for short options too.\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\
+      --traditional           accept arguments in traditional form\n\
 "), stdout);
+      fputs (HELP_OPTION_DESCRIPTION, stdout);
+      fputs (VERSION_OPTION_DESCRIPTION, stdout);
       fputs (_("\
-      --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\
+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\
@@ -317,7 +333,7 @@ Pre-POSIX format specifications may be intermixed, they accumulate:\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\
@@ -330,24 +346,27 @@ TYPE is made up of one or more of these specifications:\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\
-"), stdout);
-      fputs (_("\
 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\
-"), stdout);
-      fputs (_("\
 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\
+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);
-      puts (_("\nReport bugs to <bug-textutils@gnu.org>."));
+      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     }
   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
@@ -631,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,
@@ -656,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) PARAMS ((size_t, const char *, const char *));
+  void (*print_function) (size_t, const char *, const char *);
   const char *p;
   unsigned int c;
   unsigned int field_width = 0;
@@ -718,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);
 
@@ -727,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:
@@ -960,10 +982,11 @@ open_next_file (void)
    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.  */
+   have occurred.  IN_ERRNO, if nonzero, is the error number
+   corresponding to the most recent action for IN_STREAM.  */
 
 static int
-check_and_close (void)
+check_and_close (int in_errno)
 {
   int err = 0;
 
@@ -971,7 +994,7 @@ check_and_close (void)
     {
       if (ferror (in_stream))
        {
-         error (0, errno, "%s", input_filename);
+         error (0, in_errno, _("%s: read error"), input_filename);
          if (in_stream != stdin)
            fclose (in_stream);
          err = 1;
@@ -987,14 +1010,14 @@ check_and_close (void)
 
   if (ferror (stdout))
     {
-      error (0, errno, _("standard output"));
+      error (0, 0, _("write error"));
       err = 1;
     }
 
   return err;
 }
 
-/* Decode the POSIX-style od format string S.  Append the decoded
+/* 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.  */
 
@@ -1018,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,
@@ -1042,6 +1063,7 @@ static int
 skip (uintmax_t n_skip)
 {
   int err = 0;
+  int in_errno = 0;
 
   if (n_skip == 0)
     return 0;
@@ -1070,13 +1092,13 @@ skip (uintmax_t n_skip)
 
          if (S_ISREG (file_stats.st_mode) && 0 <= file_stats.st_size)
            {
-             if (file_stats.st_size <= n_skip)
+             if ((uintmax_t) file_stats.st_size <= n_skip)
                n_skip -= file_stats.st_size;
              else
                {
-                 if (lseek (fileno (in_stream), n_skip, SEEK_CUR) < 0)
+                 if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
                    {
-                     error (0, errno, "%s", input_filename);
+                     in_errno = errno;
                      err = 1;
                    }
                  n_skip = 0;
@@ -1098,7 +1120,12 @@ skip (uintmax_t 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)
-                   break;
+                   {
+                     in_errno = errno;
+                     err = 1;
+                     n_skip = 0;
+                     break;
+                   }
                }
            }
 
@@ -1112,7 +1139,7 @@ skip (uintmax_t n_skip)
          err = 1;
        }
 
-      err |= check_and_close ();
+      err |= check_and_close (in_errno);
 
       err |= open_next_file ();
     }
@@ -1270,7 +1297,7 @@ read_char (int *c)
       if (*c != EOF)
        break;
 
-      err |= check_and_close ();
+      err |= check_and_close (errno);
 
       err |= open_next_file ();
     }
@@ -1317,7 +1344,7 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
       if (n_read == n_needed)
        break;
 
-      err |= check_and_close ();
+      err |= check_and_close (errno);
 
       err |= open_next_file ();
     }
@@ -1339,8 +1366,8 @@ get_lcm (void)
   return l_c_m;
 }
 
-/* If S is a valid pre-POSIX offset specification with an optional leading '+'
-   return nonzero and set *OFFSET to the offset it denotes.  */
+/* If S is a valid traditional offset specification with an optional
+   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
 
 static int
 parse_old_offset (const char *s, uintmax_t *offset)
@@ -1398,8 +1425,8 @@ dump (void)
   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;
 
@@ -1462,7 +1489,7 @@ dump (void)
   format_address (current_offset, '\n');
 
   if (limit_bytes_to_format && current_offset >= end_offset)
-    err |= check_and_close ();
+    err |= check_and_close (0);
 
   return err;
 }
@@ -1471,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
@@ -1582,7 +1609,7 @@ dump_strings (void)
 
   free (buf);
 
-  err |= check_and_close ();
+  err |= check_and_close (0);
   return err;
 }
 
@@ -1597,11 +1624,15 @@ main (int argc, char **argv)
   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.  */
   uintmax_t pseudo_start IF_LINT (= 0);
 
+  initialize_main (&argc, &argv);
   program_name = argv[0];
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, LOCALEDIR);
@@ -1619,6 +1650,8 @@ main (int argc, char **argv)
   integral_type_size[sizeof (int)] = INT;
   integral_type_size[sizeof (long int)] = 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
 
@@ -1634,15 +1667,15 @@ 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));
 
   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)
     {
       uintmax_t tmp;
       enum strtol_error s_err;
@@ -1725,12 +1758,12 @@ it must be one character from [doxn]"),
          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 @@ it must be one character from [doxn]"),
          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':
@@ -1775,10 +1815,10 @@ it must be one character from [doxn]"),
 
        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;
        }
     }
@@ -1792,11 +1832,14 @@ it must be one character from [doxn]"),
 
   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)
     {
@@ -1835,7 +1878,7 @@ it must be one character from [doxn]"),
              error (0, 0,
                     _("invalid second operand in compatibility mode `%s'"),
                     argv[optind + 1]);
-             usage (1);
+             usage (EXIT_FAILURE);
            }
        }
       else if (n_files == 3)
@@ -1855,14 +1898,14 @@ it must be one character from [doxn]"),
            {
              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)
@@ -1882,7 +1925,7 @@ it must be one character from [doxn]"),
     {
       end_offset = n_bytes_to_skip + max_bytes_to_format;
       if (end_offset < n_bytes_to_skip)
-       error (EXIT_FAILURE, 0, "skip-bytes + read-bytes is too large");
+       error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large"));
     }
 
   if (n_specs == 0)