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