busybox/coreutils/od_bloaty.c
<<
>>
Prefs
   1/* od -- dump files in octal and other formats
   2   Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
   3
   4   This program is free software; you can redistribute it and/or modify
   5   it under the terms of the GNU General Public License as published by
   6   the Free Software Foundation; either version 2, or (at your option)
   7   any later version.
   8
   9   This program is distributed in the hope that it will be useful,
  10   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12   GNU General Public License for more details.
  13
  14   You should have received a copy of the GNU General Public License
  15   along with this program; if not, write to the Free Software Foundation,
  16   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17 */
  18/* Written by Jim Meyering.  */
  19/* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */
  20
  21
  22/* #include "libbb.h" - done in od.c */
  23#include "common_bufsiz.h"
  24#define assert(a) ((void)0)
  25
  26
  27//usage:#if ENABLE_DESKTOP
  28//usage:#define od_trivial_usage
  29//usage:       "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..."
  30// We don't support:
  31// ... [FILE] [[+]OFFSET[.][b]]
  32// Support is buggy for:
  33// od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]]
  34
  35//usage:#define od_full_usage "\n\n"
  36//usage:       "Print FILEs (or stdin) unambiguously, as octal bytes by default"
  37//usage:#endif
  38
  39enum {
  40        OPT_A = 1 << 0,
  41        OPT_N = 1 << 1,
  42        OPT_a = 1 << 2,
  43        OPT_b = 1 << 3,
  44        OPT_c = 1 << 4,
  45        OPT_d = 1 << 5,
  46        OPT_f = 1 << 6,
  47        OPT_h = 1 << 7,
  48        OPT_i = 1 << 8,
  49        OPT_j = 1 << 9,
  50        OPT_l = 1 << 10,
  51        OPT_o = 1 << 11,
  52        OPT_t = 1 << 12,
  53        /* When zero and two or more consecutive blocks are equal, format
  54           only the first block and output an asterisk alone on the following
  55           line to indicate that identical blocks have been elided: */
  56        OPT_v = 1 << 13,
  57        OPT_x = 1 << 14,
  58        OPT_s = 1 << 15,
  59        OPT_S = 1 << 16,
  60        OPT_w = 1 << 17,
  61        OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
  62};
  63
  64#define OD_GETOPT32() getopt32long(argv, \
  65        "A:N:abcdfhij:lot:*vxsS:w:+:", od_longopts, \
  66        /* -w with optional param */ \
  67        /* -S was -s and also had optional parameter */ \
  68        /* but in coreutils 6.3 it was renamed and now has */ \
  69        /* _mandatory_ parameter */ \
  70        &str_A, &str_N, &str_j, &lst_t, &str_S, &G.bytes_per_block)
  71
  72
  73/* Check for 0x7f is a coreutils 6.3 addition */
  74#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f)
  75
  76typedef long double longdouble_t;
  77typedef unsigned long long ulonglong_t;
  78typedef long long llong;
  79
  80#if ENABLE_LFS
  81# define xstrtooff_sfx xstrtoull_sfx
  82#else
  83# define xstrtooff_sfx xstrtoul_sfx
  84#endif
  85
  86/* The default number of input bytes per output line.  */
  87#define DEFAULT_BYTES_PER_BLOCK 16
  88
  89/* The number of decimal digits of precision in a float.  */
  90#ifndef FLT_DIG
  91# define FLT_DIG 7
  92#endif
  93
  94/* The number of decimal digits of precision in a double.  */
  95#ifndef DBL_DIG
  96# define DBL_DIG 15
  97#endif
  98
  99/* The number of decimal digits of precision in a long double.  */
 100#ifndef LDBL_DIG
 101# define LDBL_DIG DBL_DIG
 102#endif
 103
 104enum size_spec {
 105        NO_SIZE,
 106        CHAR,
 107        SHORT,
 108        INT,
 109        LONG,
 110        LONG_LONG,
 111        FLOAT_SINGLE,
 112        FLOAT_DOUBLE,
 113        FLOAT_LONG_DOUBLE,
 114        N_SIZE_SPECS
 115};
 116
 117enum output_format {
 118        SIGNED_DECIMAL,
 119        UNSIGNED_DECIMAL,
 120        OCTAL,
 121        HEXADECIMAL,
 122        FLOATING_POINT,
 123        NAMED_CHARACTER,
 124        CHARACTER
 125};
 126
 127/* Each output format specification (from '-t spec' or from
 128   old-style options) is represented by one of these structures.  */
 129struct tspec {
 130        enum output_format fmt;
 131        enum size_spec size;
 132        void (*print_function) (size_t, const char *, const char *);
 133        char *fmt_string;
 134        int hexl_mode_trailer;
 135        int field_width;
 136};
 137
 138/* Convert the number of 8-bit bytes of a binary representation to
 139   the number of characters (digits + sign if the type is signed)
 140   required to represent the same quantity in the specified base/type.
 141   For example, a 32-bit (4-byte) quantity may require a field width
 142   as wide as the following for these types:
 143   11   unsigned octal
 144   11   signed decimal
 145   10   unsigned decimal
 146   8    unsigned hexadecimal  */
 147
 148static const uint8_t bytes_to_oct_digits[] ALIGN1 =
 149{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
 150
 151static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
 152{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
 153
 154static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
 155{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
 156
 157static const uint8_t bytes_to_hex_digits[] ALIGN1 =
 158{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
 159
 160/* Convert enum size_spec to the size of the named type.  */
 161static const signed char width_bytes[] ALIGN1 = {
 162        -1,
 163        sizeof(char),
 164        sizeof(short),
 165        sizeof(int),
 166        sizeof(long),
 167        sizeof(ulonglong_t),
 168        sizeof(float),
 169        sizeof(double),
 170        sizeof(longdouble_t)
 171};
 172/* Ensure that for each member of 'enum size_spec' there is an
 173   initializer in the width_bytes array.  */
 174struct ERR_width_bytes_has_bad_size {
 175        char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
 176};
 177
 178struct globals {
 179        smallint exit_code;
 180
 181        unsigned string_min;
 182
 183        /* An array of specs describing how to format each input block.  */
 184        unsigned n_specs;
 185        struct tspec *spec;
 186
 187        /* Function that accepts an address and an optional following char,
 188           and prints the address and char to stdout.  */
 189        void (*format_address)(off_t, char);
 190
 191        /* The difference between the old-style pseudo starting address and
 192           the number of bytes to skip.  */
 193#if ENABLE_LONG_OPTS
 194        off_t pseudo_offset;
 195# define G_pseudo_offset G.pseudo_offset
 196#endif
 197        /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
 198           input is formatted.  */
 199
 200        /* The number of input bytes formatted per output line.  It must be
 201           a multiple of the least common multiple of the sizes associated with
 202           the specified output types.  It should be as large as possible, but
 203           no larger than 16 -- unless specified with the -w option.  */
 204        unsigned bytes_per_block; /* have to use unsigned, not size_t */
 205
 206        /* A NULL-terminated list of the file-arguments from the command line.  */
 207        const char *const *file_list;
 208
 209        /* The input stream associated with the current file.  */
 210        FILE *in_stream;
 211
 212        bool not_first;
 213        bool prev_pair_equal;
 214
 215        char address_fmt[sizeof("%0n"OFF_FMT"xc")];
 216} FIX_ALIASING;
 217/* Corresponds to 'x' above */
 218#define address_base_char G.address_fmt[sizeof(G.address_fmt)-3]
 219/* Corresponds to 'n' above */
 220#define address_pad_len_char G.address_fmt[2]
 221
 222#if !ENABLE_LONG_OPTS
 223enum { G_pseudo_offset = 0 };
 224#endif
 225#define G (*(struct globals*)bb_common_bufsiz1)
 226#define INIT_G() do { \
 227        setup_common_bufsiz(); \
 228        BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
 229        G.bytes_per_block = 32; \
 230        strcpy(G.address_fmt, "%0n"OFF_FMT"xc"); \
 231} while (0)
 232
 233
 234#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
 235static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
 236        [sizeof(char)] = CHAR,
 237#if USHRT_MAX != UCHAR_MAX
 238        [sizeof(short)] = SHORT,
 239#endif
 240#if UINT_MAX != USHRT_MAX
 241        [sizeof(int)] = INT,
 242#endif
 243#if ULONG_MAX != UINT_MAX
 244        [sizeof(long)] = LONG,
 245#endif
 246#if ULLONG_MAX != ULONG_MAX
 247        [sizeof(ulonglong_t)] = LONG_LONG,
 248#endif
 249};
 250
 251#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
 252static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
 253        /* gcc seems to allow repeated indexes. Last one wins */
 254        [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
 255        [sizeof(double)] = FLOAT_DOUBLE,
 256        [sizeof(float)] = FLOAT_SINGLE
 257};
 258
 259
 260static unsigned
 261gcd(unsigned u, unsigned v)
 262{
 263        unsigned t;
 264        while (v != 0) {
 265                t = u % v;
 266                u = v;
 267                v = t;
 268        }
 269        return u;
 270}
 271
 272/* Compute the least common multiple of U and V.  */
 273static unsigned
 274lcm(unsigned u, unsigned v) {
 275        unsigned t = gcd(u, v);
 276        if (t == 0)
 277                return 0;
 278        return u * v / t;
 279}
 280
 281static void
 282print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
 283{
 284        while (n_bytes--) {
 285                int tmp = *(signed char *) block;
 286                printf(fmt_string, tmp);
 287                block += sizeof(unsigned char);
 288        }
 289}
 290
 291static void
 292print_char(size_t n_bytes, const char *block, const char *fmt_string)
 293{
 294        while (n_bytes--) {
 295                unsigned tmp = *(unsigned char *) block;
 296                printf(fmt_string, tmp);
 297                block += sizeof(unsigned char);
 298        }
 299}
 300
 301static void
 302print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
 303{
 304        n_bytes /= sizeof(signed short);
 305        while (n_bytes--) {
 306                int tmp = *(signed short *) block;
 307                printf(fmt_string, tmp);
 308                block += sizeof(unsigned short);
 309        }
 310}
 311
 312static void
 313print_short(size_t n_bytes, const char *block, const char *fmt_string)
 314{
 315        n_bytes /= sizeof(unsigned short);
 316        while (n_bytes--) {
 317                unsigned tmp = *(unsigned short *) block;
 318                printf(fmt_string, tmp);
 319                block += sizeof(unsigned short);
 320        }
 321}
 322
 323static void
 324print_int(size_t n_bytes, const char *block, const char *fmt_string)
 325{
 326        n_bytes /= sizeof(unsigned);
 327        while (n_bytes--) {
 328                unsigned tmp = *(unsigned *) block;
 329                printf(fmt_string, tmp);
 330                block += sizeof(unsigned);
 331        }
 332}
 333
 334#if UINT_MAX == ULONG_MAX
 335# define print_long print_int
 336#else
 337static void
 338print_long(size_t n_bytes, const char *block, const char *fmt_string)
 339{
 340        n_bytes /= sizeof(unsigned long);
 341        while (n_bytes--) {
 342                unsigned long tmp = *(unsigned long *) block;
 343                printf(fmt_string, tmp);
 344                block += sizeof(unsigned long);
 345        }
 346}
 347#endif
 348
 349#if ULONG_MAX == ULLONG_MAX
 350# define print_long_long print_long
 351#else
 352static void
 353print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
 354{
 355        n_bytes /= sizeof(ulonglong_t);
 356        while (n_bytes--) {
 357                ulonglong_t tmp = *(ulonglong_t *) block;
 358                printf(fmt_string, tmp);
 359                block += sizeof(ulonglong_t);
 360        }
 361}
 362#endif
 363
 364static void
 365print_float(size_t n_bytes, const char *block, const char *fmt_string)
 366{
 367        n_bytes /= sizeof(float);
 368        while (n_bytes--) {
 369                float tmp = *(float *) block;
 370                printf(fmt_string, tmp);
 371                block += sizeof(float);
 372        }
 373}
 374
 375static void
 376print_double(size_t n_bytes, const char *block, const char *fmt_string)
 377{
 378        n_bytes /= sizeof(double);
 379        while (n_bytes--) {
 380                double tmp = *(double *) block;
 381                printf(fmt_string, tmp);
 382                block += sizeof(double);
 383        }
 384}
 385
 386static void
 387print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
 388{
 389        n_bytes /= sizeof(longdouble_t);
 390        while (n_bytes--) {
 391                longdouble_t tmp = *(longdouble_t *) block;
 392                printf(fmt_string, tmp);
 393                block += sizeof(longdouble_t);
 394        }
 395}
 396
 397/* print_[named]_ascii are optimized for speed.
 398 * Remember, someday you may want to pump gigabytes through this thing.
 399 * Saving a dozen of .text bytes here is counter-productive */
 400
 401static void
 402print_named_ascii(size_t n_bytes, const char *block,
 403                const char *unused_fmt_string UNUSED_PARAM)
 404{
 405        /* Names for some non-printing characters.  */
 406        static const char charname[33][3] ALIGN1 = {
 407                "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
 408                " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
 409                "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
 410                "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
 411                " sp"
 412        };
 413        // buf[N] pos:  01234 56789
 414        char buf[12] = "   x\0 xxx\0";
 415        // [12] because we take three 32bit stack slots anyway, and
 416        // gcc is too dumb to initialize with constant stores,
 417        // it copies initializer from rodata. Oh well.
 418        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65410
 419
 420        while (n_bytes--) {
 421                unsigned masked_c = *(unsigned char *) block++;
 422
 423                masked_c &= 0x7f;
 424                if (masked_c == 0x7f) {
 425                        fputs_stdout(" del");
 426                        continue;
 427                }
 428                if (masked_c > ' ') {
 429                        buf[3] = masked_c;
 430                        fputs_stdout(buf);
 431                        continue;
 432                }
 433                /* Why? Because printf(" %3.3s") is much slower... */
 434                buf[6] = charname[masked_c][0];
 435                buf[7] = charname[masked_c][1];
 436                buf[8] = charname[masked_c][2];
 437                fputs_stdout(buf+5);
 438        }
 439}
 440
 441static void
 442print_ascii(size_t n_bytes, const char *block,
 443                const char *unused_fmt_string UNUSED_PARAM)
 444{
 445        // buf[N] pos:  01234 56789
 446        char buf[12] = "   x\0 xxx\0";
 447
 448        while (n_bytes--) {
 449                const char *s;
 450                unsigned c = *(unsigned char *) block++;
 451
 452                if (ISPRINT(c)) {
 453                        buf[3] = c;
 454                        fputs_stdout(buf);
 455                        continue;
 456                }
 457                switch (c) {
 458                case '\0':
 459                        s = "  \\0";
 460                        break;
 461                case '\007':
 462                        s = "  \\a";
 463                        break;
 464                case '\b':
 465                        s = "  \\b";
 466                        break;
 467                case '\f':
 468                        s = "  \\f";
 469                        break;
 470                case '\n':
 471                        s = "  \\n";
 472                        break;
 473                case '\r':
 474                        s = "  \\r";
 475                        break;
 476                case '\t':
 477                        s = "  \\t";
 478                        break;
 479                case '\v':
 480                        s = "  \\v";
 481                        break;
 482                default:
 483                        buf[6] = (c >> 6 & 3) + '0';
 484                        buf[7] = (c >> 3 & 7) + '0';
 485                        buf[8] = (c & 7) + '0';
 486                        s = buf + 5;
 487                }
 488                fputs_stdout(s);
 489        }
 490}
 491
 492/* Given a list of one or more input filenames FILE_LIST, set the global
 493   file pointer IN_STREAM and the global string INPUT_FILENAME to the
 494   first one that can be successfully opened. Modify FILE_LIST to
 495   reference the next filename in the list.  A file name of "-" is
 496   interpreted as standard input.  If any file open fails, give an error
 497   message and return nonzero.  */
 498
 499static void
 500open_next_file(void)
 501{
 502        while (1) {
 503                if (!*G.file_list)
 504                        return;
 505                G.in_stream = fopen_or_warn_stdin(*G.file_list++);
 506                if (G.in_stream) {
 507                        break;
 508                }
 509                G.exit_code = 1;
 510        }
 511
 512        if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
 513                setbuf(G.in_stream, NULL);
 514}
 515
 516/* Test whether there have been errors on in_stream, and close it if
 517   it is not standard input.  Return nonzero if there has been an error
 518   on in_stream or stdout; return zero otherwise.  This function will
 519   report more than one error only if both a read and a write error
 520   have occurred.  IN_ERRNO, if nonzero, is the error number
 521   corresponding to the most recent action for IN_STREAM.  */
 522
 523static void
 524check_and_close(void)
 525{
 526        if (G.in_stream) {
 527                if (ferror(G.in_stream))        {
 528                        bb_error_msg("%s: read error", (G.in_stream == stdin)
 529                                        ? bb_msg_standard_input
 530                                        : G.file_list[-1]
 531                        );
 532                        G.exit_code = 1;
 533                }
 534                fclose_if_not_stdin(G.in_stream);
 535                G.in_stream = NULL;
 536        }
 537
 538        if (ferror(stdout)) {
 539                bb_simple_error_msg_and_die(bb_msg_write_error);
 540        }
 541}
 542
 543/* If S points to a single valid modern od format string, put
 544   a description of that format in *TSPEC, return pointer to
 545   character following the just-decoded format.
 546   For example, if S were "d4afL", we will return a rtp to "afL"
 547   and *TSPEC would be
 548        {
 549                fmt = SIGNED_DECIMAL;
 550                size = INT or LONG; (whichever integral_type_size[4] resolves to)
 551                print_function = print_int; (assuming size == INT)
 552                fmt_string = "%011d%c";
 553        }
 554   S_ORIG is solely for reporting errors.  It should be the full format
 555   string argument. */
 556
 557static NOINLINE const char *
 558decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
 559{
 560        enum size_spec size_spec;
 561        unsigned size;
 562        enum output_format fmt;
 563        const char *p;
 564        char *end;
 565        char *fmt_string = NULL;
 566        void (*print_function) (size_t, const char *, const char *);
 567        unsigned c;
 568        unsigned field_width = 0;
 569        int pos;
 570
 571        switch (*s) {
 572        case 'd':
 573        case 'o':
 574        case 'u':
 575        case 'x': {
 576                static const char CSIL[] ALIGN1 = "CSIL";
 577
 578                c = *s++;
 579                p = strchr(CSIL, *s);
 580                /* if *s == NUL, p != NULL! Testcase: "od -tx" */
 581                if (!p || *p == '\0') {
 582                        size = sizeof(int);
 583                        if (isdigit(s[0])) {
 584                                size = bb_strtou(s, &end, 0);
 585                                if (errno == ERANGE
 586                                 || MAX_INTEGRAL_TYPE_SIZE < size
 587                                 || integral_type_size[size] == NO_SIZE
 588                                ) {
 589                                        bb_error_msg_and_die("invalid type string '%s'; "
 590                                                "%u-byte %s type is not supported",
 591                                                s_orig, size, "integral");
 592                                }
 593                                s = end;
 594                        }
 595                } else {
 596                        static const uint8_t CSIL_sizeof[4] = {
 597                                sizeof(char),
 598                                sizeof(short),
 599                                sizeof(int),
 600                                sizeof(long),
 601                        };
 602                        size = CSIL_sizeof[p - CSIL];
 603                        s++; /* skip C/S/I/L */
 604                }
 605
 606#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
 607        ((Spec) == LONG_LONG ? (Max_format) \
 608        : ((Spec) == LONG ? (Long_format) : (Min_format)))
 609
 610#define FMT_BYTES_ALLOCATED 9
 611                size_spec = integral_type_size[size];
 612
 613                {
 614                        static const char doux[] ALIGN1 = "doux";
 615                        static const char doux_fmt_letter[][4] = {
 616                                "lld", "llo", "llu", "llx"
 617                        };
 618                        static const enum output_format doux_fmt[] = {
 619                                SIGNED_DECIMAL,
 620                                OCTAL,
 621                                UNSIGNED_DECIMAL,
 622                                HEXADECIMAL,
 623                        };
 624                        static const uint8_t *const doux_bytes_to_XXX[] = {
 625                                bytes_to_signed_dec_digits,
 626                                bytes_to_oct_digits,
 627                                bytes_to_unsigned_dec_digits,
 628                                bytes_to_hex_digits,
 629                        };
 630                        static const char doux_fmtstring[][sizeof(" %%0%u%s")] ALIGN1 = {
 631                                " %%%u%s",
 632                                " %%0%u%s",
 633                                " %%%u%s",
 634                                " %%0%u%s",
 635                        };
 636
 637                        pos = strchr(doux, c) - doux;
 638                        fmt = doux_fmt[pos];
 639                        field_width = doux_bytes_to_XXX[pos][size];
 640                        p = doux_fmt_letter[pos] + 2;
 641                        if (size_spec == LONG) p--;
 642                        if (size_spec == LONG_LONG) p -= 2;
 643                        fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
 644                }
 645
 646                switch (size_spec) {
 647                case CHAR:
 648                        print_function = (fmt == SIGNED_DECIMAL
 649                                    ? print_s_char
 650                                    : print_char);
 651                        break;
 652                case SHORT:
 653                        print_function = (fmt == SIGNED_DECIMAL
 654                                    ? print_s_short
 655                                    : print_short);
 656                        break;
 657                case INT:
 658                        print_function = print_int;
 659                        break;
 660                case LONG:
 661                        print_function = print_long;
 662                        break;
 663                default: /* case LONG_LONG: */
 664                        print_function = print_long_long;
 665                        break;
 666                }
 667                break;
 668        }
 669
 670        case 'f': {
 671                static const char FDL[] ALIGN1 = "FDL";
 672
 673                fmt = FLOATING_POINT;
 674                ++s;
 675                p = strchr(FDL, *s);
 676                if (!p || *p == '\0') {
 677                        size = sizeof(double);
 678                        if (isdigit(s[0])) {
 679                                size = bb_strtou(s, &end, 0);
 680                                if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
 681                                 || fp_type_size[size] == NO_SIZE
 682                                ) {
 683                                        bb_error_msg_and_die("invalid type string '%s'; "
 684                                                "%u-byte %s type is not supported",
 685                                                s_orig, size, "floating point");
 686                                }
 687                                s = end;
 688                        }
 689                } else {
 690                        static const uint8_t FDL_sizeof[] = {
 691                                sizeof(float),
 692                                sizeof(double),
 693                                sizeof(longdouble_t),
 694                        };
 695
 696                        size = FDL_sizeof[p - FDL];
 697                        s++; /* skip F/D/L */
 698                }
 699
 700                size_spec = fp_type_size[size];
 701
 702                switch (size_spec) {
 703                case FLOAT_SINGLE:
 704                        print_function = print_float;
 705                        field_width = FLT_DIG + 8;
 706                        /* Don't use %#e; not all systems support it.  */
 707                        fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
 708                        break;
 709                case FLOAT_DOUBLE:
 710                        print_function = print_double;
 711                        field_width = DBL_DIG + 8;
 712                        fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
 713                        break;
 714                default: /* case FLOAT_LONG_DOUBLE: */
 715                        print_function = print_long_double;
 716                        field_width = LDBL_DIG + 8;
 717                        fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
 718                        break;
 719                }
 720                break;
 721        }
 722
 723        case 'a':
 724                ++s;
 725                fmt = NAMED_CHARACTER;
 726                size_spec = CHAR;
 727                print_function = print_named_ascii;
 728                field_width = 3;
 729                break;
 730        case 'c':
 731                ++s;
 732                fmt = CHARACTER;
 733                size_spec = CHAR;
 734                print_function = print_ascii;
 735                field_width = 3;
 736                break;
 737        default:
 738                bb_error_msg_and_die("invalid character '%c' "
 739                                "in type string '%s'", *s, s_orig);
 740        }
 741
 742        tspec->size = size_spec;
 743        tspec->fmt = fmt;
 744        tspec->print_function = print_function;
 745        tspec->fmt_string = fmt_string;
 746
 747        tspec->field_width = field_width;
 748        tspec->hexl_mode_trailer = (*s == 'z');
 749        if (tspec->hexl_mode_trailer)
 750                s++;
 751
 752        return s;
 753}
 754
 755/* Decode the modern od format string S.  Append the decoded
 756   representation to the global array SPEC, reallocating SPEC if
 757   necessary.  */
 758
 759static void
 760decode_format_string(const char *s)
 761{
 762        const char *s_orig = s;
 763
 764        while (*s != '\0') {
 765                struct tspec tspec;
 766                const char *next;
 767
 768                next = decode_one_format(s_orig, s, &tspec);
 769
 770                assert(s != next);
 771                s = next;
 772                G.spec = xrealloc_vector(G.spec, 4, G.n_specs);
 773                memcpy(&G.spec[G.n_specs], &tspec, sizeof(G.spec[0]));
 774                G.n_specs++;
 775        }
 776}
 777
 778/* Given a list of one or more input filenames FILE_LIST, set the global
 779   file pointer IN_STREAM to position N_SKIP in the concatenation of
 780   those files.  If any file operation fails or if there are fewer than
 781   N_SKIP bytes in the combined input, give an error message and return
 782   nonzero.  When possible, use seek rather than read operations to
 783   advance IN_STREAM.  */
 784
 785static void
 786skip(off_t n_skip)
 787{
 788        if (n_skip == 0)
 789                return;
 790
 791        while (G.in_stream) { /* !EOF */
 792                struct stat file_stats;
 793
 794                /* First try seeking.  For large offsets, this extra work is
 795                   worthwhile.  If the offset is below some threshold it may be
 796                   more efficient to move the pointer by reading.  There are two
 797                   issues when trying to seek:
 798                        - the file must be seekable.
 799                        - before seeking to the specified position, make sure
 800                          that the new position is in the current file.
 801                          Try to do that by getting file's size using fstat.
 802                          But that will work only for regular files.  */
 803
 804                        /* The st_size field is valid only for regular files
 805                           (and for symbolic links, which cannot occur here).
 806                           If the number of bytes left to skip is at least
 807                           as large as the size of the current file, we can
 808                           decrement n_skip and go on to the next file.  */
 809                if (fstat(fileno(G.in_stream), &file_stats) == 0
 810                 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
 811                ) {
 812                        if (file_stats.st_size < n_skip) {
 813                                n_skip -= file_stats.st_size;
 814                                /* take "check & close / open_next" route */
 815                        } else {
 816                                if (fseeko(G.in_stream, n_skip, SEEK_CUR) != 0)
 817                                        G.exit_code = 1;
 818                                return;
 819                        }
 820                } else {
 821                        /* If it's not a regular file with positive size,
 822                           position the file pointer by reading.  */
 823                        char buf[1024];
 824                        size_t n_bytes_to_read = 1024;
 825                        size_t n_bytes_read;
 826
 827                        while (n_skip > 0) {
 828                                if (n_skip < n_bytes_to_read)
 829                                        n_bytes_to_read = n_skip;
 830                                n_bytes_read = fread(buf, 1, n_bytes_to_read, G.in_stream);
 831                                n_skip -= n_bytes_read;
 832                                if (n_bytes_read != n_bytes_to_read)
 833                                        break; /* EOF on this file or error */
 834                        }
 835                }
 836                if (n_skip == 0)
 837                        return;
 838
 839                check_and_close();
 840                open_next_file();
 841        }
 842
 843        if (n_skip)
 844                bb_simple_error_msg_and_die("can't skip past end of combined input");
 845}
 846
 847
 848typedef void FN_format_address(off_t address, char c);
 849
 850static void
 851format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
 852{
 853}
 854
 855static void
 856format_address_std(off_t address, char c)
 857{
 858        /* Corresponds to 'c' */
 859        G.address_fmt[sizeof(G.address_fmt)-2] = c;
 860        printf(G.address_fmt, address);
 861}
 862
 863#if ENABLE_LONG_OPTS
 864/* only used with --traditional */
 865static void
 866format_address_paren(off_t address, char c)
 867{
 868        putchar('(');
 869        format_address_std(address, ')');
 870        if (c) putchar(c);
 871}
 872
 873static void
 874format_address_label(off_t address, char c)
 875{
 876        format_address_std(address, ' ');
 877        format_address_paren(address + G_pseudo_offset, c);
 878}
 879#endif
 880
 881static void
 882dump_hexl_mode_trailer(size_t n_bytes, const char *block)
 883{
 884        fputs_stdout("  >");
 885        while (n_bytes--) {
 886                unsigned c = *(unsigned char *) block++;
 887                c = (ISPRINT(c) ? c : '.');
 888                putchar(c);
 889        }
 890        putchar('<');
 891}
 892
 893/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
 894   of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
 895   CURR_BLOCK in the concatenation of input files, and it is printed
 896   (optionally) only before the output line associated with the first
 897   format spec.  When duplicate blocks are being abbreviated, the output
 898   for a sequence of identical input blocks is the output for the first
 899   block followed by an asterisk alone on a line.  It is valid to compare
 900   the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
 901   That condition may be false only for the last input block -- and then
 902   only when it has not been padded to length BYTES_PER_BLOCK.  */
 903
 904static void
 905write_block(off_t current_offset, size_t n_bytes,
 906                const char *prev_block, const char *curr_block)
 907{
 908        unsigned i;
 909
 910        if (!(option_mask32 & OPT_v)
 911         && G.not_first
 912         && n_bytes == G.bytes_per_block
 913         && memcmp(prev_block, curr_block, G.bytes_per_block) == 0
 914        ) {
 915                if (G.prev_pair_equal) {
 916                        /* The two preceding blocks were equal, and the current
 917                           block is the same as the last one, so print nothing.  */
 918                } else {
 919                        puts("*");
 920                        G.prev_pair_equal = 1;
 921                }
 922        } else {
 923                G.not_first = 1;
 924                G.prev_pair_equal = 0;
 925                for (i = 0; i < G.n_specs; i++) {
 926                        if (i == 0)
 927                                G.format_address(current_offset, '\0');
 928                        else
 929                                printf("%*s", address_pad_len_char - '0', "");
 930                        (*G.spec[i].print_function) (n_bytes, curr_block, G.spec[i].fmt_string);
 931                        if (G.spec[i].hexl_mode_trailer) {
 932                                /* space-pad out to full line width, then dump the trailer */
 933                                unsigned datum_width = width_bytes[G.spec[i].size];
 934                                unsigned blank_fields = (G.bytes_per_block - n_bytes) / datum_width;
 935                                unsigned field_width = G.spec[i].field_width + 1;
 936                                printf("%*s", blank_fields * field_width, "");
 937                                dump_hexl_mode_trailer(n_bytes, curr_block);
 938                        }
 939                        putchar('\n');
 940                }
 941        }
 942}
 943
 944static void
 945read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
 946{
 947        assert(0 < n && n <= G.bytes_per_block);
 948
 949        *n_bytes_in_buffer = 0;
 950
 951        if (n == 0)
 952                return;
 953
 954        while (G.in_stream != NULL) { /* EOF.  */
 955                size_t n_needed;
 956                size_t n_read;
 957
 958                n_needed = n - *n_bytes_in_buffer;
 959                n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, G.in_stream);
 960                *n_bytes_in_buffer += n_read;
 961                if (n_read == n_needed)
 962                        break;
 963                /* error check is done in check_and_close */
 964                check_and_close();
 965                open_next_file();
 966        }
 967}
 968
 969/* Return the least common multiple of the sizes associated
 970   with the format specs.  */
 971
 972static int
 973get_lcm(void)
 974{
 975        size_t i;
 976        int l_c_m = 1;
 977
 978        for (i = 0; i < G.n_specs; i++)
 979                l_c_m = lcm(l_c_m, width_bytes[(int) G.spec[i].size]);
 980        return l_c_m;
 981}
 982
 983/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
 984   formatted block to standard output, and repeat until the specified
 985   maximum number of bytes has been read or until all input has been
 986   processed.  If the last block read is smaller than BYTES_PER_BLOCK
 987   and its size is not a multiple of the size associated with a format
 988   spec, extend the input block with zero bytes until its length is a
 989   multiple of all format spec sizes.  Write the final block.  Finally,
 990   write on a line by itself the offset of the byte after the last byte
 991   read.  */
 992
 993static void
 994dump(off_t current_offset, off_t end_offset)
 995{
 996        char *block[2];
 997        int idx;
 998        size_t n_bytes_read;
 999
1000        block[0] = xmalloc(2 * G.bytes_per_block);
1001        block[1] = block[0] + G.bytes_per_block;
1002
1003        idx = 0;
1004        if (option_mask32 & OPT_N) {
1005                while (1) {
1006                        size_t n_needed;
1007                        if (current_offset >= end_offset) {
1008                                n_bytes_read = 0;
1009                                break;
1010                        }
1011                        n_needed = MIN(end_offset - current_offset, (off_t) G.bytes_per_block);
1012                        read_block(n_needed, block[idx], &n_bytes_read);
1013                        if (n_bytes_read < G.bytes_per_block)
1014                                break;
1015                        assert(n_bytes_read == G.bytes_per_block);
1016                        write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1017                        current_offset += n_bytes_read;
1018                        idx ^= 1;
1019                }
1020        } else {
1021                while (1) {
1022                        read_block(G.bytes_per_block, block[idx], &n_bytes_read);
1023                        if (n_bytes_read < G.bytes_per_block)
1024                                break;
1025                        assert(n_bytes_read == G.bytes_per_block);
1026                        write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1027                        current_offset += n_bytes_read;
1028                        idx ^= 1;
1029                }
1030        }
1031
1032        if (n_bytes_read > 0) {
1033                int l_c_m;
1034                size_t bytes_to_write;
1035
1036                l_c_m = get_lcm();
1037
1038                /* Make bytes_to_write the smallest multiple of l_c_m that
1039                   is at least as large as n_bytes_read.  */
1040                bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1041
1042                memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1043                write_block(current_offset, bytes_to_write,
1044                                block[idx ^ 1], block[idx]);
1045                current_offset += n_bytes_read;
1046        }
1047
1048        G.format_address(current_offset, '\n');
1049
1050        if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1051                check_and_close();
1052
1053        free(block[0]);
1054}
1055
1056/* Read N bytes into BLOCK from the concatenation of the input files
1057   named in the global array FILE_LIST.  On the first call to this
1058   function, the global variable IN_STREAM is expected to be an open
1059   stream associated with the input file INPUT_FILENAME.  If all N
1060   bytes cannot be read from IN_STREAM, close IN_STREAM and update
1061   the global variables IN_STREAM and INPUT_FILENAME.  Then try to
1062   read the remaining bytes from the newly opened file.  Repeat if
1063   necessary until EOF is reached for the last file in FILE_LIST.
1064   On subsequent calls, don't modify BLOCK and return zero.  Set
1065   *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
1066   it will be detected through ferror when the stream is about to be
1067   closed.  If there is an error, give a message but continue reading
1068   as usual and return nonzero.  Otherwise return zero.  */
1069
1070/* STRINGS mode.  Find each "string constant" in the input.
1071   A string constant is a run of at least 'string_min' ASCII
1072   graphic (or formatting) characters terminated by a null.
1073   Based on a function written by Richard Stallman for a
1074   traditional version of od.  */
1075
1076static void
1077dump_strings(off_t address, off_t end_offset)
1078{
1079        unsigned bufsize = MAX(100, G.string_min);
1080        unsigned char *buf = xmalloc(bufsize);
1081
1082        while (1) {
1083                size_t i;
1084                int c;
1085
1086                /* See if the next 'G.string_min' chars are all printing chars.  */
1087 tryline:
1088                if ((option_mask32 & OPT_N) && (end_offset - G.string_min <= address))
1089                        break;
1090                i = 0;
1091                while (!(option_mask32 & OPT_N) || address < end_offset) {
1092                        if (i == bufsize) {
1093                                bufsize += bufsize/8;
1094                                buf = xrealloc(buf, bufsize);
1095                        }
1096
1097                        while (G.in_stream) { /* !EOF */
1098                                c = fgetc(G.in_stream);
1099                                if (c != EOF)
1100                                        goto got_char;
1101                                check_and_close();
1102                                open_next_file();
1103                        }
1104                        /* EOF */
1105                        goto ret;
1106 got_char:
1107                        address++;
1108                        if (!c)
1109                                break;
1110                        if (!ISPRINT(c))
1111                                goto tryline;   /* It isn't; give up on this string.  */
1112                        buf[i++] = c;           /* String continues; store it all.  */
1113                }
1114
1115                if (i < G.string_min)           /* Too short! */
1116                        goto tryline;
1117
1118                /* If we get here, the string is all printable and NUL-terminated */
1119                buf[i] = 0;
1120                G.format_address(address - i - 1, ' ');
1121
1122                for (i = 0; (c = buf[i]); i++) {
1123                        switch (c) {
1124                        case '\007': fputs_stdout("\\a"); break;
1125                        case '\b': fputs_stdout("\\b"); break;
1126                        case '\f': fputs_stdout("\\f"); break;
1127                        case '\n': fputs_stdout("\\n"); break;
1128                        case '\r': fputs_stdout("\\r"); break;
1129                        case '\t': fputs_stdout("\\t"); break;
1130                        case '\v': fputs_stdout("\\v"); break;
1131                        default: putchar(c);
1132                        }
1133                }
1134                putchar('\n');
1135        }
1136
1137        /* We reach this point only if we search through
1138           (max_bytes_to_format - G.string_min) bytes before reaching EOF.  */
1139        check_and_close();
1140 ret:
1141        free(buf);
1142}
1143
1144#if ENABLE_LONG_OPTS
1145/* If S is a valid traditional offset specification with an optional
1146   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
1147
1148static int
1149parse_old_offset(const char *s, off_t *offset)
1150{
1151        static const struct suffix_mult Bb[] ALIGN_SUFFIX = {
1152                { "B", 1024 },
1153                { "b", 512 },
1154                { "", 0 }
1155        };
1156        char *p;
1157        int radix;
1158
1159        /* Skip over any leading '+'. */
1160        if (s[0] == '+') ++s;
1161        if (!isdigit(s[0])) return 0; /* not a number */
1162
1163        /* Determine the radix we'll use to interpret S.  If there is a '.',
1164         * it's decimal, otherwise, if the string begins with '0X'or '0x',
1165         * it's hexadecimal, else octal.  */
1166        p = strchr(s, '.');
1167        radix = 8;
1168        if (p) {
1169                p[0] = '\0'; /* cheating */
1170                radix = 10;
1171        } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1172                radix = 16;
1173
1174        *offset = xstrtooff_sfx(s, radix, Bb);
1175        if (p) p[0] = '.';
1176
1177        return (*offset >= 0);
1178}
1179#endif
1180
1181int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1182int od_main(int argc UNUSED_PARAM, char **argv)
1183{
1184#if ENABLE_LONG_OPTS
1185        static const char od_longopts[] ALIGN1 =
1186                "skip-bytes\0"        Required_argument "j"
1187                "address-radix\0"     Required_argument "A"
1188                "read-bytes\0"        Required_argument "N"
1189                "format\0"            Required_argument "t"
1190                "output-duplicates\0" No_argument       "v"
1191                /* Yes, it's true: -S NUM, but --strings[=NUM]!
1192                 * that is, NUM is mandatory for -S but optional for --strings!
1193                 */
1194                "strings\0"           Optional_argument "S"
1195                "width\0"             Optional_argument "w"
1196                "traditional\0"       No_argument       "\xff"
1197                ;
1198#endif
1199        const char *str_A, *str_N, *str_j, *str_S = "3";
1200        llist_t *lst_t = NULL;
1201        unsigned opt;
1202        int l_c_m;
1203        /* The number of input bytes to skip before formatting and writing.  */
1204        off_t n_bytes_to_skip = 0;
1205        /* The offset of the first byte after the last byte to be formatted.  */
1206        off_t end_offset = 0;
1207        /* The maximum number of bytes that will be formatted.  */
1208        off_t max_bytes_to_format = 0;
1209
1210        INIT_G();
1211
1212        /*G.spec = NULL; - already is */
1213        G.format_address = format_address_std;
1214        address_base_char = 'o';
1215        address_pad_len_char = '7';
1216
1217        /* Parse command line */
1218        opt = OD_GETOPT32();
1219        argv += optind;
1220        if (opt & OPT_A) {
1221                static const char doxn[] ALIGN1 = "doxn";
1222                static const char doxn_address_base_char[] ALIGN1 = {
1223                        'u', 'o', 'x', /* '?' fourth one is not important */
1224                };
1225                static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
1226                        '7', '7', '6', /* '?' */
1227                };
1228                char *p;
1229                int pos;
1230                p = strchr(doxn, str_A[0]);
1231                if (!p)
1232                        bb_error_msg_and_die("bad output address radix "
1233                                "'%c' (must be [doxn])", str_A[0]);
1234                pos = p - doxn;
1235                if (pos == 3) G.format_address = format_address_none;
1236                address_base_char = doxn_address_base_char[pos];
1237                address_pad_len_char = doxn_address_pad_len_char[pos];
1238        }
1239        if (opt & OPT_N) {
1240                max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm_suffixes);
1241        }
1242        if (opt & OPT_a) decode_format_string("a");
1243        if (opt & OPT_b) decode_format_string("oC");
1244        if (opt & OPT_c) decode_format_string("c");
1245        if (opt & OPT_d) decode_format_string("u2");
1246        if (opt & OPT_f) decode_format_string("fF");
1247        if (opt & OPT_h) decode_format_string("x2");
1248        if (opt & OPT_i) decode_format_string("d2");
1249        if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm_suffixes);
1250        if (opt & OPT_l) decode_format_string("d4");
1251        if (opt & OPT_o) decode_format_string("o2");
1252        while (lst_t) {
1253                decode_format_string(llist_pop(&lst_t));
1254        }
1255        if (opt & OPT_x) decode_format_string("x2");
1256        if (opt & OPT_s) decode_format_string("d2");
1257        if (opt & OPT_S) {
1258                G.string_min = xstrtou_sfx(str_S, 0, bkm_suffixes);
1259        }
1260
1261        // Bloat:
1262        //if ((option_mask32 & OPT_S) && G.n_specs > 0)
1263        //      bb_error_msg_and_die("no type may be specified when dumping strings");
1264
1265        /* If the --traditional option is used, there may be from
1266         * 0 to 3 remaining command line arguments;  handle each case
1267         * separately.
1268         * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]]
1269         * The offset and pseudo_start have the same syntax.
1270         *
1271         * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1272         * traditional syntax even if --traditional is not given.  */
1273
1274#if ENABLE_LONG_OPTS
1275        if (opt & OPT_traditional) {
1276                if (argv[0]) {
1277                        off_t pseudo_start = -1;
1278                        off_t o1, o2;
1279
1280                        if (!argv[1]) { /* one arg */
1281                                if (parse_old_offset(argv[0], &o1)) {
1282                                        /* od --traditional OFFSET */
1283                                        n_bytes_to_skip = o1;
1284                                        argv++;
1285                                }
1286                                /* od --traditional FILE */
1287                        } else if (!argv[2]) { /* two args */
1288                                if (parse_old_offset(argv[0], &o1)
1289                                 && parse_old_offset(argv[1], &o2)
1290                                ) {
1291                                        /* od --traditional OFFSET LABEL */
1292                                        n_bytes_to_skip = o1;
1293                                        pseudo_start = o2;
1294                                        argv += 2;
1295                                } else if (parse_old_offset(argv[1], &o2)) {
1296                                        /* od --traditional FILE OFFSET */
1297                                        n_bytes_to_skip = o2;
1298                                        argv[1] = NULL;
1299                                } else {
1300                                        bb_error_msg_and_die("invalid second argument '%s'", argv[1]);
1301                                }
1302                        } else if (!argv[3]) { /* three args */
1303                                if (parse_old_offset(argv[1], &o1)
1304                                 && parse_old_offset(argv[2], &o2)
1305                                ) {
1306                                        /* od --traditional FILE OFFSET LABEL */
1307                                        n_bytes_to_skip = o1;
1308                                        pseudo_start = o2;
1309                                        argv[1] = NULL;
1310                                } else {
1311                                        bb_simple_error_msg_and_die("the last two arguments must be offsets");
1312                                }
1313                        } else { /* >3 args */
1314                                bb_simple_error_msg_and_die("too many arguments");
1315                        }
1316
1317                        if (pseudo_start >= 0) {
1318                                if (G.format_address == format_address_none) {
1319                                        address_base_char = 'o';
1320                                        address_pad_len_char = '7';
1321                                        G.format_address = format_address_paren;
1322                                } else {
1323                                        G.format_address = format_address_label;
1324                                }
1325                                G_pseudo_offset = pseudo_start - n_bytes_to_skip;
1326                        }
1327                }
1328                /* else: od --traditional (without args) */
1329        }
1330#endif
1331
1332        if (option_mask32 & OPT_N) {
1333                end_offset = n_bytes_to_skip + max_bytes_to_format;
1334                if (end_offset < n_bytes_to_skip)
1335                        bb_simple_error_msg_and_die("SKIP + SIZE is too large");
1336        }
1337
1338        if (G.n_specs == 0) {
1339                decode_format_string("o2");
1340                /*G.n_specs = 1; - done by decode_format_string */
1341        }
1342
1343        /* If no files were listed on the command line,
1344           set the global pointer FILE_LIST so that it
1345           references the null-terminated list of one name: "-".  */
1346        G.file_list = bb_argv_dash;
1347        if (argv[0]) {
1348                /* Set the global pointer FILE_LIST so that it
1349                   references the first file-argument on the command-line.  */
1350                G.file_list = (char const *const *) argv;
1351        }
1352
1353        /* Open the first input file */
1354        open_next_file();
1355        /* Skip over any unwanted header bytes */
1356        skip(n_bytes_to_skip);
1357        if (!G.in_stream)
1358                return EXIT_FAILURE;
1359
1360        /* Compute output block length */
1361        l_c_m = get_lcm();
1362
1363        if (opt & OPT_w) { /* -w: width */
1364                if (!G.bytes_per_block || G.bytes_per_block % l_c_m != 0) {
1365                        bb_error_msg("warning: invalid width %u; using %d instead",
1366                                        (unsigned)G.bytes_per_block, l_c_m);
1367                        G.bytes_per_block = l_c_m;
1368                }
1369        } else {
1370                G.bytes_per_block = l_c_m;
1371                if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
1372                        G.bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
1373        }
1374
1375#ifdef DEBUG
1376        {
1377                int i;
1378                for (i = 0; i < G.n_specs; i++) {
1379                        printf("%d: fmt='%s' width=%d\n",
1380                                i, G.spec[i].fmt_string,
1381                                width_bytes[G.spec[i].size]);
1382                }
1383        }
1384#endif
1385
1386        if (option_mask32 & OPT_S)
1387                dump_strings(n_bytes_to_skip, end_offset);
1388        else
1389                dump(n_bytes_to_skip, end_offset);
1390
1391        if (fclose(stdin))
1392                bb_simple_perror_msg_and_die(bb_msg_standard_input);
1393
1394        return G.exit_code;
1395}
1396