od: provide full-blown od from coreutils if CONFIG_DESKTOP
authorDenis Vlasenko <vda.linux@googlemail.com>
Tue, 10 Oct 2006 23:26:05 +0000 (23:26 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Tue, 10 Oct 2006 23:26:05 +0000 (23:26 -0000)
archival/libunarchive/init_handle.c
coreutils/od.c
coreutils/od_bloaty.c [new file with mode: 0644]
include/libbb.h
libbb/xatol.c

index beda1b462aa0399b586a4d37646ac42beee3428e..06e8d1fab0ad3519aa403d22cf040b64e4afc195 100644 (file)
@@ -3,8 +3,6 @@
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
-//#include <unistd.h>
-//#include <string.h>
 #include "libbb.h"
 #include "unarchive.h"
 
index 60a5174d9b66772c990fac71b16ab27833f0b40d..9a2d4c3432ed80dbb486932a064300b1c215141c 100644 (file)
  * Original copyright notice is retained at the end of this file.
  */
 
-// TODO: -t. busybox's own build script needs it
 
-#include <ctype.h>
-#include <string.h>
+#if ENABLE_DESKTOP
+/* This one provides -t (busybox's own build script needs it) */
+#include "od_bloaty.c"
+#else
+
 #include <getopt.h>
-#include <stdlib.h>
 #include "busybox.h"
 #include "dump.h"
 
@@ -191,6 +192,7 @@ int od_main(int argc, char **argv)
 
        return bb_dump_dump(argv);
 }
+#endif /* ENABLE_DESKTOP */
 
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
new file mode 100644 (file)
index 0000000..a88ca25
--- /dev/null
@@ -0,0 +1,1594 @@
+/* od -- dump files in octal and other formats
+   Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Written by Jim Meyering.  */
+
+/* Busyboxed by Denis Vlasenko
+** Based on od.c from coreutils-5.2.1
+** Top bloat sources:
+** get_lcm               111
+** print_ascii           145
+** format_address_std    159
+** check_and_close       160
+** open_next_file        170
+** write_block           368
+** decode_format_string 1169
+** od_main              2623
+** TODO: get rid of getopt()
+*/
+
+#include "busybox.h"
+#include <getopt.h>
+
+#define assert(a) ((void)0)
+#define ISPRINT(c) ((c)>=' ')
+
+typedef long double longdouble_t;
+typedef unsigned long long ulonglong_t;
+#define xstrtoumax_sfx xstrtoul_sfx
+#define xstrtoumax_range_sfx xstrtoul_range_sfx
+
+/* The default number of input bytes per output line.  */
+#define DEFAULT_BYTES_PER_BLOCK 16
+
+/* The number of decimal digits of precision in a float.  */
+#ifndef FLT_DIG
+# define FLT_DIG 7
+#endif
+
+/* The number of decimal digits of precision in a double.  */
+#ifndef DBL_DIG
+# define DBL_DIG 15
+#endif
+
+/* The number of decimal digits of precision in a long double.  */
+#ifndef LDBL_DIG
+# define LDBL_DIG DBL_DIG
+#endif
+
+enum size_spec {
+       NO_SIZE,
+       CHAR,
+       SHORT,
+       INT,
+       LONG,
+       LONG_LONG,
+       /* FIXME: add INTMAX support, too */
+       FLOAT_SINGLE,
+       FLOAT_DOUBLE,
+       FLOAT_LONG_DOUBLE,
+       N_SIZE_SPECS
+};
+
+enum output_format {
+       SIGNED_DECIMAL,
+       UNSIGNED_DECIMAL,
+       OCTAL,
+       HEXADECIMAL,
+       FLOATING_POINT,
+       NAMED_CHARACTER,
+       CHARACTER
+};
+
+/* Each output format specification (from '-t spec' or from
+   old-style options) is represented by one of these structures.  */
+struct tspec {
+       enum output_format fmt;
+       enum size_spec size;
+       void (*print_function) (size_t, const char *, const char *);
+       char *fmt_string;
+       int hexl_mode_trailer;
+       int field_width;
+};
+
+/* 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.
+   For example, a 32-bit (4-byte) quantity may require a field width
+   as wide as the following for these types:
+   11  unsigned octal
+   11  signed decimal
+   10  unsigned decimal
+   8   unsigned hexadecimal  */
+
+static const unsigned char bytes_to_oct_digits[] =
+{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
+
+static const unsigned char bytes_to_signed_dec_digits[] =
+{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
+
+static const unsigned char bytes_to_unsigned_dec_digits[] =
+{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
+
+static const unsigned char bytes_to_hex_digits[] =
+{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
+
+/* Convert enum size_spec to the size of the named type.  */
+static const signed char width_bytes[] = {
+       -1,
+       sizeof(char),
+       sizeof(short),
+       sizeof(int),
+       sizeof(long),
+       sizeof(ulonglong_t),
+       sizeof(float),
+       sizeof(double),
+       sizeof(longdouble_t)
+};
+
+/* 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 charname[33][4] = {
+       "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+       "bs", "ht", "nl", "vt", "ff", "cr", "so", "si",
+       "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+       "can", "em", "sub", "esc", "fs", "gs", "rs", "us",
+       "sp"
+};
+
+/* Address base (8, 10 or 16).  */
+static int address_base;
+
+/* 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)
+
+/* Width of a normal address.  */
+static int address_pad_len;
+
+static size_t string_min;
+static int flag_dump_strings;
+
+/* 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 int flag_pseudo_start;
+
+/* The difference between the old-style pseudo starting address and
+   the number of bytes to skip.  */
+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) (uintmax_t, char);
+
+/* The number of input bytes to skip before formatting and writing.  */
+static uintmax_t n_bytes_to_skip; // = 0;
+
+/* 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.  */
+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
+   line to indicate that identical blocks have been elided.  */
+static int abbreviate_duplicate_blocks = 1;
+
+/* An array of specs describing how to format each input block.  */
+static struct tspec *spec;
+
+/* The number of format specs.  */
+static size_t n_specs;
+
+/* 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 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.  */
+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;
+
+#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
+static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1];
+
+#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
+static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1];
+
+
+/* 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 unsigned
+gcd(unsigned u, unsigned v)
+{
+       unsigned t;
+       while (v != 0) {
+               t = u % v;
+               u = v;
+               v = t;
+       }
+       return u;
+}
+
+/* Compute the least common multiple of U and V.  */
+static unsigned
+lcm(unsigned u, unsigned v) {
+       unsigned t = gcd(u, v);
+       if (t == 0)
+               return 0;
+       return u * v / t;
+}
+
+static void
+print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes; i > 0; i--) {
+               int tmp = (unsigned) *(unsigned char *) block;
+               if (tmp > SCHAR_MAX)
+                       tmp -= SCHAR_MAX - SCHAR_MIN + 1;
+               assert(tmp <= SCHAR_MAX);
+               printf(fmt_string, tmp);
+               block += sizeof(unsigned char);
+       }
+}
+
+static void
+print_char(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes; i > 0; i--) {
+               unsigned tmp = *(unsigned char *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(unsigned char);
+       }
+}
+
+static void
+print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(unsigned short); i > 0; i--) {
+               int tmp = (unsigned) *(unsigned short *) block;
+               if (tmp > SHRT_MAX)
+                       tmp -= SHRT_MAX - SHRT_MIN + 1;
+               assert(tmp <= SHRT_MAX);
+               printf(fmt_string, tmp);
+               block += sizeof(unsigned short);
+       }
+}
+
+static void
+print_short(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(unsigned short); i > 0; i--) {
+               unsigned tmp = *(unsigned short *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(unsigned short);
+       }
+}
+
+static void
+print_int(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(unsigned); i > 0; i--) {
+               unsigned tmp = *(unsigned *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(unsigned);
+       }
+}
+
+static void
+print_long(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(unsigned long); i > 0; i--) {
+               unsigned long tmp = *(unsigned long *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(unsigned long);
+       }
+}
+
+static void
+print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(ulonglong_t); i > 0; i--) {
+               ulonglong_t tmp = *(ulonglong_t *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(ulonglong_t);
+       }
+}
+
+static void
+print_float(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(float); i > 0; i--) {
+               float tmp = *(float *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(float);
+       }
+}
+
+static void
+print_double(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(double); i > 0; i--) {
+               double tmp = *(double *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(double);
+       }
+}
+
+static void
+print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
+{
+       size_t i;
+       for (i = n_bytes / sizeof(longdouble_t); i > 0; i--) {
+               longdouble_t tmp = *(longdouble_t *) block;
+               printf(fmt_string, tmp);
+               block += sizeof(longdouble_t);
+       }
+}
+
+static void
+dump_hexl_mode_trailer(size_t n_bytes, const char *block)
+{
+       size_t i;
+       fputs("  >", stdout);
+       for (i = n_bytes; i > 0; i--) {
+               unsigned c = *(unsigned char *) block;
+               unsigned c2 = (ISPRINT(c) ? c : '.');
+               putchar(c2);
+               block += sizeof(unsigned char);
+       }
+       putchar('<');
+}
+
+static void
+print_named_ascii(size_t n_bytes, const char *block,
+               const char *unused_fmt_string ATTRIBUTE_UNUSED)
+{
+       size_t i;
+       for (i = n_bytes; i > 0; i--) {
+               unsigned c = *(unsigned char *) block;
+               unsigned masked_c = (0x7f & c);
+               const char *s;
+               char buf[5];
+
+               if (masked_c == 127)
+                       s = "del";
+               else if (masked_c <= 040)
+                       s = charname[masked_c];
+               else {
+                       sprintf(buf, "  %c", masked_c);
+                       s = buf;
+               }
+
+               printf(" %3s", s);
+               block += sizeof(unsigned char);
+       }
+}
+
+static void
+print_ascii(size_t n_bytes, const char *block,
+               const char *unused_fmt_string ATTRIBUTE_UNUSED)
+{
+       size_t i;
+       for (i = n_bytes; i > 0; i--) {
+               unsigned c = *(unsigned char *) block;
+               const char *s;
+               char buf[5];
+
+               switch (c) {
+               case '\0':
+                       s = " \\0";
+                       break;
+               case '\007':
+                       s = " \\a";
+                       break;
+               case '\b':
+                       s = " \\b";
+                       break;
+               case '\f':
+                       s = " \\f";
+                       break;
+               case '\n':
+                       s = " \\n";
+                       break;
+               case '\r':
+                       s = " \\r";
+                       break;
+               case '\t':
+                       s = " \\t";
+                       break;
+               case '\v':
+                       s = " \\v";
+                       break;
+               default:
+                       sprintf(buf, (ISPRINT(c) ? "  %c" : "%03o"), c);
+                       s = (const char *) buf;
+               }
+
+               printf(" %3s", s);
+               block += sizeof(unsigned char);
+       }
+}
+
+/* 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,
+   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(const char *s_orig, const char *s, const char **next,
+                                          struct tspec *tspec)
+{
+       enum size_spec size_spec;
+       unsigned long size;
+       enum output_format fmt;
+       const char *pre_fmt_string;
+       char *end;
+       char *fmt_string;
+       void (*print_function) (size_t, const char *, const char *);
+       unsigned c;
+       unsigned field_width = 0;
+
+       assert(tspec != NULL);
+
+       switch (*s) {
+       case 'd':
+       case 'o':
+       case 'u':
+       case 'x':
+               c = *s;
+               ++s;
+               switch (*s) {
+               case 'C':
+                       ++s;
+                       size = sizeof(char);
+                       break;
+               case 'S':
+                       ++s;
+                       size = sizeof(short);
+                       break;
+               case 'I':
+                       ++s;
+                       size = sizeof(int);
+                       break;
+               case 'L':
+                       ++s;
+                       size = sizeof(long);
+                       break;
+               default:
+                       size = sizeof(int);
+                       if (isdigit(s[0])) {
+                               size = strtoul(s, &end, 0);
+                               if (end[0] || MAX_INTEGRAL_TYPE_SIZE < size
+                                || integral_type_size[size] == NO_SIZE
+                               ) {
+                                       bb_error_msg("invalid type string '%s'; "
+                                               "%lu-byte integral type is not spported", s_orig, size);
+                                       return 1;
+                               }
+                               s = end;
+                       }
+                       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);
+
+               size_spec = integral_type_size[size];
+
+               switch (c) {
+               case 'd':
+                       fmt = SIGNED_DECIMAL;
+                       sprintf(fmt_string, " %%%u%s",
+                          (field_width = bytes_to_signed_dec_digits[size]),
+                          ISPEC_TO_FORMAT(size_spec, "d", "ld", PRIdMAX));
+                       break;
+               case 'o':
+                       fmt = OCTAL;
+                       sprintf(fmt_string, " %%0%u%s",
+                          (field_width = bytes_to_oct_digits[size]),
+                          ISPEC_TO_FORMAT(size_spec, "o", "lo", PRIoMAX));
+                       break;
+               case 'u':
+                       fmt = UNSIGNED_DECIMAL;
+                       sprintf(fmt_string, " %%%u%s",
+                          (field_width = bytes_to_unsigned_dec_digits[size]),
+                          ISPEC_TO_FORMAT(size_spec, "u", "lu", PRIuMAX));
+                       break;
+               case 'x':
+                       fmt = HEXADECIMAL;
+                       sprintf(fmt_string, " %%0%u%s",
+                          (field_width = bytes_to_hex_digits[size]),
+                          ISPEC_TO_FORMAT(size_spec, "x", "lx", PRIxMAX));
+                       break;
+               default:
+                       abort();
+               }
+
+               assert(strlen(fmt_string) < FMT_BYTES_ALLOCATED);
+
+               switch (size_spec) {
+               case CHAR:
+                       print_function = (fmt == SIGNED_DECIMAL
+                                   ? print_s_char
+                                   : print_char);
+                       break;
+               case SHORT:
+                       print_function = (fmt == SIGNED_DECIMAL
+                                   ? print_s_short
+                                   : print_short);
+                       break;
+               case INT:
+                       print_function = print_int;
+                       break;
+               case LONG:
+                       print_function = print_long;
+                       break;
+               case LONG_LONG:
+                       print_function = print_long_long;
+                       break;
+               default:
+                       abort();
+               }
+               break;
+
+       case 'f':
+               fmt = FLOATING_POINT;
+               ++s;
+               switch (*s) {
+               case 'F':
+                       ++s;
+                       size = sizeof(float);
+                       break;
+               case 'D':
+                       ++s;
+                       size = sizeof(double);
+                       break;
+               case 'L':
+                       ++s;
+                       size = sizeof(longdouble_t);
+                       break;
+               default:
+                       size = sizeof(double);
+                       if (isdigit(s[0])) {
+                               size = strtoul(s, &end, 0);
+                               if (end[0] || size > MAX_FP_TYPE_SIZE
+                                || fp_type_size[size] == NO_SIZE
+                               ) {
+                                       bb_error_msg("invalid type string '%s'; "
+                                               "%lu-byte floating point type is not supported", s_orig, size);
+                                       return 1;
+                               }
+                               s = end;
+                       }
+                       break;
+               }
+               size_spec = fp_type_size[size];
+
+               switch (size_spec) {
+               case FLOAT_SINGLE:
+                       print_function = print_float;
+                       /* Don't use %#e; not all systems support it.  */
+                       pre_fmt_string = " %%%d.%de";
+                       fmt_string = xmalloc(strlen(pre_fmt_string));
+                       sprintf(fmt_string, pre_fmt_string,
+                               (field_width = FLT_DIG + 8), FLT_DIG);
+                       break;
+               case FLOAT_DOUBLE:
+                       print_function = print_double;
+                       pre_fmt_string = " %%%d.%de";
+                       fmt_string = xmalloc(strlen(pre_fmt_string));
+                       sprintf(fmt_string, pre_fmt_string,
+                               (field_width = DBL_DIG + 8), DBL_DIG);
+                       break;
+               case FLOAT_LONG_DOUBLE:
+                       print_function = print_long_double;
+                       pre_fmt_string = " %%%d.%dLe";
+                       fmt_string = xmalloc(strlen(pre_fmt_string));
+                       sprintf(fmt_string, pre_fmt_string,
+                               (field_width = LDBL_DIG + 8), LDBL_DIG);
+                       break;
+               default:
+                       abort();
+               }
+               break;
+
+       case 'a':
+               ++s;
+               fmt = NAMED_CHARACTER;
+               size_spec = CHAR;
+               fmt_string = NULL;
+               print_function = print_named_ascii;
+               field_width = 3;
+               break;
+       case 'c':
+               ++s;
+               fmt = CHARACTER;
+               size_spec = CHAR;
+               fmt_string = NULL;
+               print_function = print_ascii;
+               field_width = 3;
+               break;
+       default:
+               bb_error_msg("invalid character '%c' in type string '%s'", *s, s_orig);
+               return 1;
+       }
+
+       tspec->size = size_spec;
+       tspec->fmt = fmt;
+       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;
+
+       return 0;
+}
+
+/* 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 (input_filename[0] == '-' && !input_filename[1]) {
+                               input_filename = "standard input";
+                               in_stream = stdin;
+                               have_read_stdin = 1;
+               } else {
+                       in_stream = fopen(input_filename, "r");
+                       if (in_stream == NULL) {
+                               bb_perror_msg("%s", input_filename);
+                               err = 1;
+                       }
+               }
+       } while (in_stream == NULL);
+
+       if (limit_bytes_to_format && !flag_dump_strings)
+               setbuf(in_stream, NULL);
+
+       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))  {
+                       bb_error_msg("%s: read error", input_filename);
+                       if (in_stream != stdin)
+                               fclose(in_stream);
+                       err = 1;
+               } else if (in_stream != stdin && fclose(in_stream) == EOF) {
+                       bb_perror_msg("%s", input_filename);
+                       err = 1;
+               }
+               in_stream = NULL;
+       }
+
+       if (ferror(stdout)) {
+               bb_error_msg("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.  */
+
+static int
+decode_format_string(const char *s)
+{
+       const char *s_orig = s;
+       assert(s != NULL);
+
+       while (*s != '\0') {
+               struct tspec tspec;
+               const char *next;
+
+               if (decode_one_format(s_orig, s, &next, &tspec))
+                       return 1;
+
+               assert(s != next);
+               s = next;
+               n_specs++;
+               spec = xrealloc(spec, n_specs * sizeof(*spec));
+               memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
+       }
+
+       return 0;
+}
+
+/* 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 return
+   nonzero.  When possible, use seek rather than read operations to
+   advance IN_STREAM.  */
+
+static int
+skip(uintmax_t n_skip)
+{
+       int err = 0;
+       int in_errno = 0;
+
+       if (n_skip == 0)
+               return 0;
+
+       while (in_stream != NULL) { /* EOF */
+               struct stat file_stats;
+
+               /* First try seeking.  For large offsets, this extra work is
+                  worthwhile.  If the offset is below some threshold it may be
+                  more efficient to move the pointer by reading.  There are two
+                  issues when trying to seek:
+                       - the file must be seekable.
+                       - before seeking to the specified position, make sure
+                         that the new position is in the current file.
+                         Try to do that by getting file's size using fstat.
+                         But that will work only for regular files.  */
+
+               if (fstat(fileno(in_stream), &file_stats) == 0) {
+                       /* The st_size field is valid only for regular files
+                          (and for symbolic links, which cannot occur here).
+                          If the number of bytes left to skip is at least
+                          as large as the size of the current file, we can
+                          decrement n_skip and go on to the next file.  */
+
+                       if (S_ISREG(file_stats.st_mode) && 0 <= file_stats.st_size) {
+                               if ((uintmax_t) file_stats.st_size <= n_skip)
+                                       n_skip -= file_stats.st_size;
+                               else {
+                                       if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) {
+                                               in_errno = errno;
+                                               err = 1;
+                                       }
+                                       n_skip = 0;
+                               }
+                       } else {
+
+                               /* If it's not a regular file with nonnegative size,
+                                  position the file pointer by reading.  */
+                               char buf[BUFSIZ];
+                               size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
+
+                               while (0 < n_skip) {
+                                       if (n_skip < n_bytes_to_read)
+                                               n_bytes_to_read = n_skip;
+                                       n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
+                                       n_skip -= n_bytes_read;
+                                       if (n_bytes_read != n_bytes_to_read) {
+                                               in_errno = errno;
+                                               err = 1;
+                                               n_skip = 0;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (n_skip == 0)
+                               break;
+               } else {  /* cannot fstat() file */
+                       bb_perror_msg("%s", input_filename);
+                       err = 1;
+               }
+
+               err |= check_and_close(in_errno);
+               err |= open_next_file();
+       }
+
+       if (n_skip != 0)
+               bb_error_msg_and_die("cannot skip past end of combined input");
+       return err;
+}
+
+static void
+format_address_none(uintmax_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
+{
+}
+
+static void
+format_address_std(uintmax_t address, char c)
+{
+       char buf[MAX_ADDRESS_LENGTH + 2];
+       char *p = buf + sizeof(buf)-1;
+       char const *pbound;
+
+       *p = '\0';
+       *--p = c;
+       pbound = p - address_pad_len;
+
+       do {
+               *--p = "0123456789abcdef"[address % address_base];
+               address /= address_base;
+       } while (address);
+
+       while (pbound < p)
+               *--p = '0';
+       fputs(p, stdout);
+}
+
+static void
+format_address_paren(uintmax_t address, char c)
+{
+       putchar('(');
+       format_address_std(address, ')');
+       putchar(c);
+}
+
+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
+   of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
+   CURR_BLOCK in the concatenation of input files, and it is printed
+   (optionally) only before the output line associated with the first
+   format spec.  When duplicate blocks are being abbreviated, the output
+   for a sequence of identical input blocks is the output for the first
+   block followed by an asterisk alone on a line.  It is valid to compare
+   the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
+   That condition may be false only for the last input block -- and then
+   only when it has not been padded to length BYTES_PER_BLOCK.  */
+
+static void
+write_block(uintmax_t current_offset, size_t n_bytes,
+               const char *prev_block, const char *curr_block)
+{
+       static int first = 1;
+       static int prev_pair_equal = 0;
+
+       if (abbreviate_duplicate_blocks
+        && !first
+        && n_bytes == bytes_per_block
+        && memcmp(prev_block, curr_block, bytes_per_block) == 0
+       ) {
+               if (prev_pair_equal) {
+                       /* The two preceding blocks were equal, and the current
+                          block is the same as the last one, so print nothing.  */
+               } else {
+                       puts("*");
+                       prev_pair_equal = 1;
+               }
+       } else {
+               size_t i;
+
+               prev_pair_equal = 0;
+               for (i = 0; i < n_specs; i++) {
+                       if (i == 0)
+                               format_address(current_offset, '\0');
+                       else
+                               printf("%*s", address_pad_len, "");
+                       (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
+                       if (spec[i].hexl_mode_trailer) {
+                               /* space-pad out to full line width, then dump the trailer */
+                               int datum_width = width_bytes[spec[i].size];
+                               int blank_fields = (bytes_per_block - n_bytes) / datum_width;
+                               int field_width = spec[i].field_width + 1;
+                               printf("%*s", blank_fields * field_width, "");
+                               dump_hexl_mode_trailer(n_bytes, curr_block);
+                       }
+                       putchar('\n');
+               }
+       }
+       first = 0;
+}
+
+/* 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 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 = 0;
+
+       *c = EOF;
+
+       while (in_stream != NULL) { /* EOF.  */
+               *c = fgetc(in_stream);
+               if (*c != EOF)
+                       break;
+               err |= check_and_close(errno);
+               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 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 = 0;
+
+       assert(0 < n && n <= bytes_per_block);
+
+       *n_bytes_in_buffer = 0;
+
+       if (n == 0)
+               return 0;
+
+       while (in_stream != NULL) { /* EOF.  */
+               size_t n_needed;
+               size_t n_read;
+
+               n_needed = n - *n_bytes_in_buffer;
+               n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
+               *n_bytes_in_buffer += n_read;
+               if (n_read == n_needed)
+                       break;
+               err |= check_and_close(errno);
+               err |= open_next_file();
+       }
+
+       return err;
+}
+
+/* Return the least common multiple of the sizes associated
+   with the format specs.  */
+
+static int
+get_lcm(void)
+{
+       size_t i;
+       int l_c_m = 1;
+
+       for (i = 0; i < n_specs; i++)
+               l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
+       return l_c_m;
+}
+
+/* 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)
+{
+       static const struct suffix_mult Bb[] = {
+               { "B", 1024 },
+               { "b", 512 },
+               { NULL, 0 }
+       };
+
+       int radix;
+
+       if (*s == '\0')
+               return 0;
+
+       /* Skip over any leading '+'. */
+       if (s[0] == '+')
+               ++s;
+
+       /* Determine the radix we'll use to interpret S.  If there is a '.',
+                it's decimal, otherwise, if the string begins with '0X'or '0x',
+                it's hexadecimal, else octal.  */
+       if (strchr(s, '.') != NULL)
+               radix = 10;
+       else {
+               if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+                       radix = 16;
+               else
+                       radix = 8;
+       }
+
+       *offset = xstrtoumax_sfx(s, radix, Bb);
+       return 1;
+}
+
+/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
+   formatted block to standard output, and repeat until the specified
+   maximum number of bytes has been read or until all input has been
+   processed.  If the last block read is smaller than BYTES_PER_BLOCK
+   and its size is not a multiple of the size associated with a format
+   spec, extend the input block with zero bytes until its length is a
+   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 nonzero, return nonzero.
+   Otherwise, return zero.  */
+
+static int
+dump(void)
+{
+       char *block[2];
+       uintmax_t current_offset;
+       int idx;
+       int err;
+       size_t n_bytes_read;
+
+       block[0] = xmalloc(2*bytes_per_block);
+       block[1] = block[0] + bytes_per_block;
+
+       current_offset = n_bytes_to_skip;
+
+       idx = 0;
+       err = 0;
+       if (limit_bytes_to_format) {
+               while (1) {
+                       size_t n_needed;
+                       if (current_offset >= end_offset) {
+                               n_bytes_read = 0;
+                               break;
+                       }
+                       n_needed = MIN(end_offset - current_offset,
+                               (uintmax_t) bytes_per_block);
+                       err |= read_block(n_needed, block[idx], &n_bytes_read);
+                       if (n_bytes_read < bytes_per_block)
+                               break;
+                       assert(n_bytes_read == bytes_per_block);
+                       write_block(current_offset, n_bytes_read,
+                              block[!idx], block[idx]);
+                       current_offset += n_bytes_read;
+                       idx = !idx;
+               }
+       } else {
+               while (1) {
+                       err |= read_block(bytes_per_block, block[idx], &n_bytes_read);
+                       if (n_bytes_read < bytes_per_block)
+                               break;
+                       assert(n_bytes_read == bytes_per_block);
+                       write_block(current_offset, n_bytes_read,
+                              block[!idx], block[idx]);
+                       current_offset += n_bytes_read;
+                       idx = !idx;
+               }
+       }
+
+       if (n_bytes_read > 0) {
+               int l_c_m;
+               size_t bytes_to_write;
+
+               l_c_m = get_lcm();
+
+               /* Make bytes_to_write the smallest multiple of l_c_m that
+                        is at least as large as n_bytes_read.  */
+               bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
+
+               memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
+               write_block(current_offset, bytes_to_write,
+                                  block[!idx], block[idx]);
+               current_offset += n_bytes_read;
+       }
+
+       format_address(current_offset, '\n');
+
+       if (limit_bytes_to_format && current_offset >= end_offset)
+               err |= check_and_close(0);
+
+       free(block[0]);
+
+       return err;
+}
+
+/* STRINGS mode.  Find each "string constant" in the input.
+   A string constant is a run of at least 'string_min' ASCII
+   graphic (or formatting) characters terminated by a null.
+   Based on a function written by Richard Stallman for a
+   traditional version of od.  Return nonzero if an error
+   occurs.  Otherwise, return zero.  */
+
+static int
+dump_strings(void)
+{
+       size_t bufsize = MAX(100, string_min);
+       char *buf = xmalloc(bufsize);
+       uintmax_t address = n_bytes_to_skip;
+       int err;
+
+       err = 0;
+       while (1) {
+               size_t i;
+               int c;
+
+               /* See if the next 'string_min' chars are all printing chars.  */
+       tryline:
+
+               if (limit_bytes_to_format
+                && (end_offset < string_min || end_offset - string_min <= address)
+               ) {
+                       break;
+               }
+
+               for (i = 0; i < string_min; i++) {
+                       err |= read_char(&c);
+                       address++;
+                       if (c < 0) {
+                               free(buf);
+                               return err;
+                       }
+                       if (!ISPRINT(c))
+                               /* Found a non-printing.  Try again starting with next char.  */
+                               goto tryline;
+                       buf[i] = c;
+               }
+
+               /* We found a run of 'string_min' printable characters.
+                        Now see if it is terminated with a null byte.  */
+               while (!limit_bytes_to_format || address < end_offset) {
+                       if (i == bufsize) {
+                               bufsize *= 2;
+                               buf = xrealloc(buf, bufsize * sizeof(*buf));
+                       }
+                       err |= read_char(&c);
+                       address++;
+                       if (c < 0) {
+                               free(buf);
+                               return err;
+                       }
+                       if (c == '\0')
+                               break;          /* It is; print this string.  */
+                       if (!ISPRINT(c))
+                               goto tryline;   /* It isn't; give up on this string.  */
+                       buf[i++] = c;           /* String continues; store it all.  */
+               }
+
+               /* If we get here, the string is all printable and null-terminated,
+                        so print it.  It is all in 'buf' and 'i' is its length.  */
+               buf[i] = 0;
+               format_address(address - i - 1, ' ');
+
+               for (i = 0; (c = buf[i]); i++) {
+                       switch (c) {
+                       case '\007':
+                               fputs("\\a", stdout);
+                               break;
+
+                       case '\b':
+                               fputs("\\b", stdout);
+                               break;
+
+                       case '\f':
+                               fputs("\\f", stdout);
+                               break;
+
+                       case '\n':
+                               fputs("\\n", stdout);
+                               break;
+
+                       case '\r':
+                               fputs("\\r", stdout);
+                               break;
+
+                       case '\t':
+                               fputs("\\t", stdout);
+                               break;
+
+                       case '\v':
+                               fputs("\\v", stdout);
+                               break;
+
+                       default:
+                               putc(c, stdout);
+                       }
+               }
+               putchar('\n');
+       }
+
+       /* We reach this point only if we search through
+          (max_bytes_to_format - string_min) bytes before reaching EOF.  */
+
+       free(buf);
+
+       err |= check_and_close(0);
+       return err;
+}
+
+int
+od_main(int argc, char **argv)
+{
+       static const struct suffix_mult bkm[] = {
+               { "b", 512 },
+               { "k", 1024 },
+               { "m", 1024*1024 },
+               { NULL, 0 }
+       };
+       static const struct option long_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' },
+               { "strings", optional_argument, NULL, 's' },
+               { "traditional", no_argument, NULL, TRADITIONAL_OPTION },
+               { "width", optional_argument, NULL, 'w' },
+               { NULL, 0, NULL, 0 }
+       };
+
+       int c;
+       int n_files;
+       int l_c_m;
+       size_t desired_width = 0; // only for gcc
+       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.  */
+       uintmax_t pseudo_start = 0; // only for gcc
+
+       err = 0;
+
+       integral_type_size[sizeof(char)] = CHAR;
+       integral_type_size[sizeof(short)] = SHORT;
+       integral_type_size[sizeof(int)] = INT;
+       integral_type_size[sizeof(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;
+
+       fp_type_size[sizeof(float)] = FLOAT_SINGLE;
+       /* The array entry for 'double' is filled in after that for longdouble_t
+          so that if 'long double' is the same type or if long double isn't
+          supported FLOAT_LONG_DOUBLE will never be used.  */
+       fp_type_size[sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE;
+       fp_type_size[sizeof(double)] = FLOAT_DOUBLE;
+
+       spec = NULL;
+
+       format_address = format_address_std;
+       address_base = 8;
+       address_pad_len = 7;
+       flag_dump_strings = 0;
+
+       while ((c = getopt_long(argc, argv, "A:N:abcdfhij:lot:vxs:w:", long_options, NULL)) != -1)      {
+               switch (c) {
+               case 0:
+                       break;
+               case 'A':
+                       switch (optarg[0]) {
+                       case 'd':
+                               format_address = format_address_std;
+                               address_base = 10;
+                               address_pad_len = 7;
+                               break;
+                       case 'o':
+                               format_address = format_address_std;
+                               address_base = 8;
+                               address_pad_len = 7;
+                               break;
+                       case 'x':
+                               format_address = format_address_std;
+                               address_base = 16;
+                               address_pad_len = 6;
+                               break;
+                       case 'n':
+                               format_address = format_address_none;
+                               address_pad_len = 0;
+                               break;
+                       default:
+                               bb_error_msg_and_die("bad output address radix '%c' "
+                                       "(must be [doxn])", optarg[0]);
+                               break;
+                       }
+                       break;
+
+               case 'j':
+                       n_bytes_to_skip = xstrtoumax_sfx(optarg, 0, bkm);
+                       break;
+               case 'N':
+                       limit_bytes_to_format = 1;
+
+                       max_bytes_to_format = xstrtoumax_sfx(optarg, 0, bkm);
+                       break;
+               case 's':
+                       if (optarg == NULL)
+                               string_min = 3;
+                       else {
+                               string_min = xstrtoumax_range_sfx(optarg, 0, 0, SIZE_MAX, bkm);
+                       }
+                       flag_dump_strings = 1;
+                       break;
+               case 't':
+                       if (decode_format_string(optarg))
+                               ++n_failed_decodes;
+                       break;
+               case 'v':
+                       abbreviate_duplicate_blocks = 0;
+                       break;
+               case TRADITIONAL_OPTION:
+                       traditional = 1;
+                       break;
+
+                       /* The next several cases map the traditional format
+                          specification options to the corresponding modern format
+                          specs.  GNU od accepts any combination of old- and
+                          new-style options.  Format specification options accumulate.  */
+
+#define CASE_OLD_ARG(old_char,new_string) \
+               case old_char: \
+                       if (decode_format_string(new_string)) \
+                               ++n_failed_decodes; \
+                       break
+
+               CASE_OLD_ARG('a', "a");
+               CASE_OLD_ARG('b', "oC");
+               CASE_OLD_ARG('c', "c");
+               CASE_OLD_ARG('d', "u2");
+               CASE_OLD_ARG('f', "fF");
+               CASE_OLD_ARG('h', "x2");
+               CASE_OLD_ARG('i', "d2");
+               CASE_OLD_ARG('l', "d4");
+               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':
+                       width_specified = 1;
+                       desired_width = 32;
+                       if (optarg)
+                               desired_width = xatoul_range(optarg, 0, SIZE_MAX);
+                       break;
+               default:
+                       bb_show_usage();
+                       break;
+               }
+       }
+
+       if (n_failed_decodes > 0)
+               exit(EXIT_FAILURE);
+
+       if (flag_dump_strings && n_specs > 0)
+               bb_error_msg_and_die("no type may be specified when dumping strings");
+
+       n_files = argc - optind;
+
+       /* If the --traditional option is used, there may be from
+                0 to 3 remaining command line arguments;  handle each case
+                separately.
+                               od [file] [[+]offset[.][b] [[+]label[.][b]]]
+                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) {
+               uintmax_t offset;
+
+               if (n_files == 1) {
+                       if (parse_old_offset(argv[optind], &offset)) {
+                               n_bytes_to_skip = offset;
+                               --n_files;
+                               ++argv;
+                       }
+               } else if (n_files == 2) {
+                       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;
+                               pseudo_start = o2;
+                               argv += 2;
+                               n_files -= 2;
+                       } else if (parse_old_offset(argv[optind + 1], &o2)) {
+                               n_bytes_to_skip = o2;
+                               --n_files;
+                               argv[optind + 1] = argv[optind];
+                               ++argv;
+                       } else {
+                               bb_error_msg_and_die("invalid second operand "
+                                       "in compatibility mode '%s'", argv[optind + 1]);
+                       }
+               } else if (n_files == 3) {
+                       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;
+                               pseudo_start = o2;
+                               argv[optind + 2] = argv[optind];
+                               argv += 2;
+                               n_files -= 2;
+                       } else {
+                               bb_error_msg_and_die("in compatibility mode "
+                                       "the last two arguments must be offsets");
+                       }
+               } else if (n_files > 3) {
+                       bb_error_msg_and_die("compatibility mode supports "
+                               "at most three arguments");
+               }
+
+               if (flag_pseudo_start) {
+                       if (format_address == format_address_none) {
+                               address_base = 8;
+                               address_pad_len = 7;
+                               format_address = format_address_paren;
+                       } else
+                               format_address = format_address_label;
+               }
+       }
+
+       if (limit_bytes_to_format) {
+               end_offset = n_bytes_to_skip + max_bytes_to_format;
+               if (end_offset < n_bytes_to_skip)
+                       bb_error_msg_and_die("skip-bytes + read-bytes is too large");
+       }
+
+       if (n_specs == 0) {
+               if (decode_format_string("o2")) {
+                       /* This happens on Cray systems that don't have a 2-byte
+                          integral type.  */
+                       exit(EXIT_FAILURE);
+               }
+               n_specs = 1;
+       }
+
+       if (n_files > 0) {
+               /* 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 {
+               /* 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;
+
+       pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
+
+       /* Compute output block length.  */
+       l_c_m = get_lcm();
+
+       if (width_specified) {
+               if (desired_width != 0 && desired_width % l_c_m == 0)
+                       bytes_per_block = desired_width;
+               else {
+                       bb_error_msg("warning: invalid width %lu; using %d instead",
+                                       (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 * (DEFAULT_BYTES_PER_BLOCK / l_c_m);
+               else
+                       bytes_per_block = l_c_m;
+       }
+
+#ifdef DEBUG
+       for (i = 0; i < n_specs; i++) {
+               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)
+               bb_perror_msg_and_die("standard input");
+
+       exit(err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
index e4e67aa5a99b1dcf8291ea13f89c3603a10ee41d..1d26b0367f3461942c4499acb5bd8a9fa8c9769e 100644 (file)
@@ -326,6 +326,8 @@ unsigned long xstrtoul_range_sfx(const char *numstr, int base,
 unsigned long xstrtoul_range(const char *numstr, int base,
                unsigned long lower,
                unsigned long upper);
+unsigned long xstrtoul_sfx(const char *numstr, int base,
+               const struct suffix_mult *suffixes);
 unsigned long xstrtoul(const char *numstr, int base);
 unsigned long xatoul_range_sfx(const char *numstr,
                unsigned long lower,
index 1b71e9ca8707f28523914b44ab3113544a8bcd70..1b281b1e9495e878f1c9627bd64303773c9ee74f 100644 (file)
@@ -98,6 +98,12 @@ unsigned long xstrtoul_range(const char *numstr, int base,
        return xstrtoul_range_sfx(numstr, base, lower, upper, NULL);
 }
 
+unsigned long xstrtoul_sfx(const char *numstr, int base,
+               const struct suffix_mult *suffixes)
+{
+       return xstrtoul_range_sfx(numstr, base, 0, ULONG_MAX, suffixes);
+}
+
 unsigned long xstrtoul(const char *numstr, int base)
 {
        return xstrtoul_range_sfx(numstr, base, 0, ULONG_MAX, NULL);