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() getopt32(argv, \
  65        "A:N:abcdfhij:lot:*vxsS:w:+:", \
  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} FIX_ALIASING;
 215#if !ENABLE_LONG_OPTS
 216enum { G_pseudo_offset = 0 };
 217#endif
 218#define G (*(struct globals*)bb_common_bufsiz1)
 219#define INIT_G() do { \
 220        setup_common_bufsiz(); \
 221        BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
 222        G.bytes_per_block = 32; \
 223} while (0)
 224
 225
 226#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
 227static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
 228        [sizeof(char)] = CHAR,
 229#if USHRT_MAX != UCHAR_MAX
 230        [sizeof(short)] = SHORT,
 231#endif
 232#if UINT_MAX != USHRT_MAX
 233        [sizeof(int)] = INT,
 234#endif
 235#if ULONG_MAX != UINT_MAX
 236        [sizeof(long)] = LONG,
 237#endif
 238#if ULLONG_MAX != ULONG_MAX
 239        [sizeof(ulonglong_t)] = LONG_LONG,
 240#endif
 241};
 242
 243#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
 244static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
 245        /* gcc seems to allow repeated indexes. Last one wins */
 246        [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
 247        [sizeof(double)] = FLOAT_DOUBLE,
 248        [sizeof(float)] = FLOAT_SINGLE
 249};
 250
 251
 252static unsigned
 253gcd(unsigned u, unsigned v)
 254{
 255        unsigned t;
 256        while (v != 0) {
 257                t = u % v;
 258                u = v;
 259                v = t;
 260        }
 261        return u;
 262}
 263
 264/* Compute the least common multiple of U and V.  */
 265static unsigned
 266lcm(unsigned u, unsigned v) {
 267        unsigned t = gcd(u, v);
 268        if (t == 0)
 269                return 0;
 270        return u * v / t;
 271}
 272
 273static void
 274print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
 275{
 276        while (n_bytes--) {
 277                int tmp = *(signed char *) block;
 278                printf(fmt_string, tmp);
 279                block += sizeof(unsigned char);
 280        }
 281}
 282
 283static void
 284print_char(size_t n_bytes, const char *block, const char *fmt_string)
 285{
 286        while (n_bytes--) {
 287                unsigned tmp = *(unsigned char *) block;
 288                printf(fmt_string, tmp);
 289                block += sizeof(unsigned char);
 290        }
 291}
 292
 293static void
 294print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
 295{
 296        n_bytes /= sizeof(signed short);
 297        while (n_bytes--) {
 298                int tmp = *(signed short *) block;
 299                printf(fmt_string, tmp);
 300                block += sizeof(unsigned short);
 301        }
 302}
 303
 304static void
 305print_short(size_t n_bytes, const char *block, const char *fmt_string)
 306{
 307        n_bytes /= sizeof(unsigned short);
 308        while (n_bytes--) {
 309                unsigned tmp = *(unsigned short *) block;
 310                printf(fmt_string, tmp);
 311                block += sizeof(unsigned short);
 312        }
 313}
 314
 315static void
 316print_int(size_t n_bytes, const char *block, const char *fmt_string)
 317{
 318        n_bytes /= sizeof(unsigned);
 319        while (n_bytes--) {
 320                unsigned tmp = *(unsigned *) block;
 321                printf(fmt_string, tmp);
 322                block += sizeof(unsigned);
 323        }
 324}
 325
 326#if UINT_MAX == ULONG_MAX
 327# define print_long print_int
 328#else
 329static void
 330print_long(size_t n_bytes, const char *block, const char *fmt_string)
 331{
 332        n_bytes /= sizeof(unsigned long);
 333        while (n_bytes--) {
 334                unsigned long tmp = *(unsigned long *) block;
 335                printf(fmt_string, tmp);
 336                block += sizeof(unsigned long);
 337        }
 338}
 339#endif
 340
 341#if ULONG_MAX == ULLONG_MAX
 342# define print_long_long print_long
 343#else
 344static void
 345print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
 346{
 347        n_bytes /= sizeof(ulonglong_t);
 348        while (n_bytes--) {
 349                ulonglong_t tmp = *(ulonglong_t *) block;
 350                printf(fmt_string, tmp);
 351                block += sizeof(ulonglong_t);
 352        }
 353}
 354#endif
 355
 356static void
 357print_float(size_t n_bytes, const char *block, const char *fmt_string)
 358{
 359        n_bytes /= sizeof(float);
 360        while (n_bytes--) {
 361                float tmp = *(float *) block;
 362                printf(fmt_string, tmp);
 363                block += sizeof(float);
 364        }
 365}
 366
 367static void
 368print_double(size_t n_bytes, const char *block, const char *fmt_string)
 369{
 370        n_bytes /= sizeof(double);
 371        while (n_bytes--) {
 372                double tmp = *(double *) block;
 373                printf(fmt_string, tmp);
 374                block += sizeof(double);
 375        }
 376}
 377
 378static void
 379print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
 380{
 381        n_bytes /= sizeof(longdouble_t);
 382        while (n_bytes--) {
 383                longdouble_t tmp = *(longdouble_t *) block;
 384                printf(fmt_string, tmp);
 385                block += sizeof(longdouble_t);
 386        }
 387}
 388
 389/* print_[named]_ascii are optimized for speed.
 390 * Remember, someday you may want to pump gigabytes through this thing.
 391 * Saving a dozen of .text bytes here is counter-productive */
 392
 393static void
 394print_named_ascii(size_t n_bytes, const char *block,
 395                const char *unused_fmt_string UNUSED_PARAM)
 396{
 397        /* Names for some non-printing characters.  */
 398        static const char charname[33][3] ALIGN1 = {
 399                "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
 400                " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
 401                "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
 402                "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
 403                " sp"
 404        };
 405        // buf[N] pos:  01234 56789
 406        char buf[12] = "   x\0 xxx\0";
 407        // [12] because we take three 32bit stack slots anyway, and
 408        // gcc is too dumb to initialize with constant stores,
 409        // it copies initializer from rodata. Oh well.
 410        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65410
 411
 412        while (n_bytes--) {
 413                unsigned masked_c = *(unsigned char *) block++;
 414
 415                masked_c &= 0x7f;
 416                if (masked_c == 0x7f) {
 417                        fputs(" del", stdout);
 418                        continue;
 419                }
 420                if (masked_c > ' ') {
 421                        buf[3] = masked_c;
 422                        fputs(buf, stdout);
 423                        continue;
 424                }
 425                /* Why? Because printf(" %3.3s") is much slower... */
 426                buf[6] = charname[masked_c][0];
 427                buf[7] = charname[masked_c][1];
 428                buf[8] = charname[masked_c][2];
 429                fputs(buf+5, stdout);
 430        }
 431}
 432
 433static void
 434print_ascii(size_t n_bytes, const char *block,
 435                const char *unused_fmt_string UNUSED_PARAM)
 436{
 437        // buf[N] pos:  01234 56789
 438        char buf[12] = "   x\0 xxx\0";
 439
 440        while (n_bytes--) {
 441                const char *s;
 442                unsigned c = *(unsigned char *) block++;
 443
 444                if (ISPRINT(c)) {
 445                        buf[3] = c;
 446                        fputs(buf, stdout);
 447                        continue;
 448                }
 449                switch (c) {
 450                case '\0':
 451                        s = "  \\0";
 452                        break;
 453                case '\007':
 454                        s = "  \\a";
 455                        break;
 456                case '\b':
 457                        s = "  \\b";
 458                        break;
 459                case '\f':
 460                        s = "  \\f";
 461                        break;
 462                case '\n':
 463                        s = "  \\n";
 464                        break;
 465                case '\r':
 466                        s = "  \\r";
 467                        break;
 468                case '\t':
 469                        s = "  \\t";
 470                        break;
 471                case '\v':
 472                        s = "  \\v";
 473                        break;
 474                default:
 475                        buf[6] = (c >> 6 & 3) + '0';
 476                        buf[7] = (c >> 3 & 7) + '0';
 477                        buf[8] = (c & 7) + '0';
 478                        s = buf + 5;
 479                }
 480                fputs(s, stdout);
 481        }
 482}
 483
 484/* Given a list of one or more input filenames FILE_LIST, set the global
 485   file pointer IN_STREAM and the global string INPUT_FILENAME to the
 486   first one that can be successfully opened. Modify FILE_LIST to
 487   reference the next filename in the list.  A file name of "-" is
 488   interpreted as standard input.  If any file open fails, give an error
 489   message and return nonzero.  */
 490
 491static void
 492open_next_file(void)
 493{
 494        while (1) {
 495                if (!*G.file_list)
 496                        return;
 497                G.in_stream = fopen_or_warn_stdin(*G.file_list++);
 498                if (G.in_stream) {
 499                        break;
 500                }
 501                G.exit_code = 1;
 502        }
 503
 504        if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
 505                setbuf(G.in_stream, NULL);
 506}
 507
 508/* Test whether there have been errors on in_stream, and close it if
 509   it is not standard input.  Return nonzero if there has been an error
 510   on in_stream or stdout; return zero otherwise.  This function will
 511   report more than one error only if both a read and a write error
 512   have occurred.  IN_ERRNO, if nonzero, is the error number
 513   corresponding to the most recent action for IN_STREAM.  */
 514
 515static void
 516check_and_close(void)
 517{
 518        if (G.in_stream) {
 519                if (ferror(G.in_stream))        {
 520                        bb_error_msg("%s: read error", (G.in_stream == stdin)
 521                                        ? bb_msg_standard_input
 522                                        : G.file_list[-1]
 523                        );
 524                        G.exit_code = 1;
 525                }
 526                fclose_if_not_stdin(G.in_stream);
 527                G.in_stream = NULL;
 528        }
 529
 530        if (ferror(stdout)) {
 531                bb_error_msg_and_die(bb_msg_write_error);
 532        }
 533}
 534
 535/* If S points to a single valid modern od format string, put
 536   a description of that format in *TSPEC, return pointer to
 537   character following the just-decoded format.
 538   For example, if S were "d4afL", we will return a rtp to "afL"
 539   and *TSPEC would be
 540        {
 541                fmt = SIGNED_DECIMAL;
 542                size = INT or LONG; (whichever integral_type_size[4] resolves to)
 543                print_function = print_int; (assuming size == INT)
 544                fmt_string = "%011d%c";
 545        }
 546   S_ORIG is solely for reporting errors.  It should be the full format
 547   string argument. */
 548
 549static NOINLINE const char *
 550decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
 551{
 552        enum size_spec size_spec;
 553        unsigned size;
 554        enum output_format fmt;
 555        const char *p;
 556        char *end;
 557        char *fmt_string = NULL;
 558        void (*print_function) (size_t, const char *, const char *);
 559        unsigned c;
 560        unsigned field_width = 0;
 561        int pos;
 562
 563        switch (*s) {
 564        case 'd':
 565        case 'o':
 566        case 'u':
 567        case 'x': {
 568                static const char CSIL[] ALIGN1 = "CSIL";
 569
 570                c = *s++;
 571                p = strchr(CSIL, *s);
 572                /* if *s == NUL, p != NULL! Testcase: "od -tx" */
 573                if (!p || *p == '\0') {
 574                        size = sizeof(int);
 575                        if (isdigit(s[0])) {
 576                                size = bb_strtou(s, &end, 0);
 577                                if (errno == ERANGE
 578                                 || MAX_INTEGRAL_TYPE_SIZE < size
 579                                 || integral_type_size[size] == NO_SIZE
 580                                ) {
 581                                        bb_error_msg_and_die("invalid type string '%s'; "
 582                                                "%u-byte %s type is not supported",
 583                                                s_orig, size, "integral");
 584                                }
 585                                s = end;
 586                        }
 587                } else {
 588                        static const uint8_t CSIL_sizeof[4] = {
 589                                sizeof(char),
 590                                sizeof(short),
 591                                sizeof(int),
 592                                sizeof(long),
 593                        };
 594                        size = CSIL_sizeof[p - CSIL];
 595                        s++; /* skip C/S/I/L */
 596                }
 597
 598#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
 599        ((Spec) == LONG_LONG ? (Max_format) \
 600        : ((Spec) == LONG ? (Long_format) : (Min_format)))
 601
 602#define FMT_BYTES_ALLOCATED 9
 603                size_spec = integral_type_size[size];
 604
 605                {
 606                        static const char doux[] ALIGN1 = "doux";
 607                        static const char doux_fmt_letter[][4] = {
 608                                "lld", "llo", "llu", "llx"
 609                        };
 610                        static const enum output_format doux_fmt[] = {
 611                                SIGNED_DECIMAL,
 612                                OCTAL,
 613                                UNSIGNED_DECIMAL,
 614                                HEXADECIMAL,
 615                        };
 616                        static const uint8_t *const doux_bytes_to_XXX[] = {
 617                                bytes_to_signed_dec_digits,
 618                                bytes_to_oct_digits,
 619                                bytes_to_unsigned_dec_digits,
 620                                bytes_to_hex_digits,
 621                        };
 622                        static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
 623                                " %%%u%s",
 624                                " %%0%u%s",
 625                                " %%%u%s",
 626                                " %%0%u%s",
 627                        };
 628
 629                        pos = strchr(doux, c) - doux;
 630                        fmt = doux_fmt[pos];
 631                        field_width = doux_bytes_to_XXX[pos][size];
 632                        p = doux_fmt_letter[pos] + 2;
 633                        if (size_spec == LONG) p--;
 634                        if (size_spec == LONG_LONG) p -= 2;
 635                        fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
 636                }
 637
 638                switch (size_spec) {
 639                case CHAR:
 640                        print_function = (fmt == SIGNED_DECIMAL
 641                                    ? print_s_char
 642                                    : print_char);
 643                        break;
 644                case SHORT:
 645                        print_function = (fmt == SIGNED_DECIMAL
 646                                    ? print_s_short
 647                                    : print_short);
 648                        break;
 649                case INT:
 650                        print_function = print_int;
 651                        break;
 652                case LONG:
 653                        print_function = print_long;
 654                        break;
 655                default: /* case LONG_LONG: */
 656                        print_function = print_long_long;
 657                        break;
 658                }
 659                break;
 660        }
 661
 662        case 'f': {
 663                static const char FDL[] ALIGN1 = "FDL";
 664
 665                fmt = FLOATING_POINT;
 666                ++s;
 667                p = strchr(FDL, *s);
 668                if (!p) {
 669                        size = sizeof(double);
 670                        if (isdigit(s[0])) {
 671                                size = bb_strtou(s, &end, 0);
 672                                if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
 673                                 || fp_type_size[size] == NO_SIZE
 674                                ) {
 675                                        bb_error_msg_and_die("invalid type string '%s'; "
 676                                                "%u-byte %s type is not supported",
 677                                                s_orig, size, "floating point");
 678                                }
 679                                s = end;
 680                        }
 681                } else {
 682                        static const uint8_t FDL_sizeof[] = {
 683                                sizeof(float),
 684                                sizeof(double),
 685                                sizeof(longdouble_t),
 686                        };
 687
 688                        size = FDL_sizeof[p - FDL];
 689                }
 690
 691                size_spec = fp_type_size[size];
 692
 693                switch (size_spec) {
 694                case FLOAT_SINGLE:
 695                        print_function = print_float;
 696                        field_width = FLT_DIG + 8;
 697                        /* Don't use %#e; not all systems support it.  */
 698                        fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
 699                        break;
 700                case FLOAT_DOUBLE:
 701                        print_function = print_double;
 702                        field_width = DBL_DIG + 8;
 703                        fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
 704                        break;
 705                default: /* case FLOAT_LONG_DOUBLE: */
 706                        print_function = print_long_double;
 707                        field_width = LDBL_DIG + 8;
 708                        fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
 709                        break;
 710                }
 711                break;
 712        }
 713
 714        case 'a':
 715                ++s;
 716                fmt = NAMED_CHARACTER;
 717                size_spec = CHAR;
 718                print_function = print_named_ascii;
 719                field_width = 3;
 720                break;
 721        case 'c':
 722                ++s;
 723                fmt = CHARACTER;
 724                size_spec = CHAR;
 725                print_function = print_ascii;
 726                field_width = 3;
 727                break;
 728        default:
 729                bb_error_msg_and_die("invalid character '%c' "
 730                                "in type string '%s'", *s, s_orig);
 731        }
 732
 733        tspec->size = size_spec;
 734        tspec->fmt = fmt;
 735        tspec->print_function = print_function;
 736        tspec->fmt_string = fmt_string;
 737
 738        tspec->field_width = field_width;
 739        tspec->hexl_mode_trailer = (*s == 'z');
 740        if (tspec->hexl_mode_trailer)
 741                s++;
 742
 743        return s;
 744}
 745
 746/* Decode the modern od format string S.  Append the decoded
 747   representation to the global array SPEC, reallocating SPEC if
 748   necessary.  */
 749
 750static void
 751decode_format_string(const char *s)
 752{
 753        const char *s_orig = s;
 754
 755        while (*s != '\0') {
 756                struct tspec tspec;
 757                const char *next;
 758
 759                next = decode_one_format(s_orig, s, &tspec);
 760
 761                assert(s != next);
 762                s = next;
 763                G.spec = xrealloc_vector(G.spec, 4, G.n_specs);
 764                memcpy(&G.spec[G.n_specs], &tspec, sizeof(G.spec[0]));
 765                G.n_specs++;
 766        }
 767}
 768
 769/* Given a list of one or more input filenames FILE_LIST, set the global
 770   file pointer IN_STREAM to position N_SKIP in the concatenation of
 771   those files.  If any file operation fails or if there are fewer than
 772   N_SKIP bytes in the combined input, give an error message and return
 773   nonzero.  When possible, use seek rather than read operations to
 774   advance IN_STREAM.  */
 775
 776static void
 777skip(off_t n_skip)
 778{
 779        if (n_skip == 0)
 780                return;
 781
 782        while (G.in_stream) { /* !EOF */
 783                struct stat file_stats;
 784
 785                /* First try seeking.  For large offsets, this extra work is
 786                   worthwhile.  If the offset is below some threshold it may be
 787                   more efficient to move the pointer by reading.  There are two
 788                   issues when trying to seek:
 789                        - the file must be seekable.
 790                        - before seeking to the specified position, make sure
 791                          that the new position is in the current file.
 792                          Try to do that by getting file's size using fstat.
 793                          But that will work only for regular files.  */
 794
 795                        /* The st_size field is valid only for regular files
 796                           (and for symbolic links, which cannot occur here).
 797                           If the number of bytes left to skip is at least
 798                           as large as the size of the current file, we can
 799                           decrement n_skip and go on to the next file.  */
 800                if (fstat(fileno(G.in_stream), &file_stats) == 0
 801                 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
 802                ) {
 803                        if (file_stats.st_size < n_skip) {
 804                                n_skip -= file_stats.st_size;
 805                                /* take "check & close / open_next" route */
 806                        } else {
 807                                if (fseeko(G.in_stream, n_skip, SEEK_CUR) != 0)
 808                                        G.exit_code = 1;
 809                                return;
 810                        }
 811                } else {
 812                        /* If it's not a regular file with positive size,
 813                           position the file pointer by reading.  */
 814                        char buf[1024];
 815                        size_t n_bytes_to_read = 1024;
 816                        size_t n_bytes_read;
 817
 818                        while (n_skip > 0) {
 819                                if (n_skip < n_bytes_to_read)
 820                                        n_bytes_to_read = n_skip;
 821                                n_bytes_read = fread(buf, 1, n_bytes_to_read, G.in_stream);
 822                                n_skip -= n_bytes_read;
 823                                if (n_bytes_read != n_bytes_to_read)
 824                                        break; /* EOF on this file or error */
 825                        }
 826                }
 827                if (n_skip == 0)
 828                        return;
 829
 830                check_and_close();
 831                open_next_file();
 832        }
 833
 834        if (n_skip)
 835                bb_error_msg_and_die("can't skip past end of combined input");
 836}
 837
 838
 839typedef void FN_format_address(off_t address, char c);
 840
 841static void
 842format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
 843{
 844}
 845
 846static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
 847/* Corresponds to 'x' above */
 848#define address_base_char address_fmt[sizeof(address_fmt)-3]
 849/* Corresponds to 'n' above */
 850#define address_pad_len_char address_fmt[2]
 851
 852static void
 853format_address_std(off_t address, char c)
 854{
 855        /* Corresponds to 'c' */
 856        address_fmt[sizeof(address_fmt)-2] = c;
 857        printf(address_fmt, address);
 858}
 859
 860#if ENABLE_LONG_OPTS
 861/* only used with --traditional */
 862static void
 863format_address_paren(off_t address, char c)
 864{
 865        putchar('(');
 866        format_address_std(address, ')');
 867        if (c) putchar(c);
 868}
 869
 870static void
 871format_address_label(off_t address, char c)
 872{
 873        format_address_std(address, ' ');
 874        format_address_paren(address + G_pseudo_offset, c);
 875}
 876#endif
 877
 878static void
 879dump_hexl_mode_trailer(size_t n_bytes, const char *block)
 880{
 881        fputs("  >", stdout);
 882        while (n_bytes--) {
 883                unsigned c = *(unsigned char *) block++;
 884                c = (ISPRINT(c) ? c : '.');
 885                putchar(c);
 886        }
 887        putchar('<');
 888}
 889
 890/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
 891   of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
 892   CURR_BLOCK in the concatenation of input files, and it is printed
 893   (optionally) only before the output line associated with the first
 894   format spec.  When duplicate blocks are being abbreviated, the output
 895   for a sequence of identical input blocks is the output for the first
 896   block followed by an asterisk alone on a line.  It is valid to compare
 897   the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
 898   That condition may be false only for the last input block -- and then
 899   only when it has not been padded to length BYTES_PER_BLOCK.  */
 900
 901static void
 902write_block(off_t current_offset, size_t n_bytes,
 903                const char *prev_block, const char *curr_block)
 904{
 905        unsigned i;
 906
 907        if (!(option_mask32 & OPT_v)
 908         && G.not_first
 909         && n_bytes == G.bytes_per_block
 910         && memcmp(prev_block, curr_block, G.bytes_per_block) == 0
 911        ) {
 912                if (G.prev_pair_equal) {
 913                        /* The two preceding blocks were equal, and the current
 914                           block is the same as the last one, so print nothing.  */
 915                } else {
 916                        puts("*");
 917                        G.prev_pair_equal = 1;
 918                }
 919        } else {
 920                G.not_first = 1;
 921                G.prev_pair_equal = 0;
 922                for (i = 0; i < G.n_specs; i++) {
 923                        if (i == 0)
 924                                G.format_address(current_offset, '\0');
 925                        else
 926                                printf("%*s", address_pad_len_char - '0', "");
 927                        (*G.spec[i].print_function) (n_bytes, curr_block, G.spec[i].fmt_string);
 928                        if (G.spec[i].hexl_mode_trailer) {
 929                                /* space-pad out to full line width, then dump the trailer */
 930                                unsigned datum_width = width_bytes[G.spec[i].size];
 931                                unsigned blank_fields = (G.bytes_per_block - n_bytes) / datum_width;
 932                                unsigned field_width = G.spec[i].field_width + 1;
 933                                printf("%*s", blank_fields * field_width, "");
 934                                dump_hexl_mode_trailer(n_bytes, curr_block);
 935                        }
 936                        putchar('\n');
 937                }
 938        }
 939}
 940
 941static void
 942read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
 943{
 944        assert(0 < n && n <= G.bytes_per_block);
 945
 946        *n_bytes_in_buffer = 0;
 947
 948        if (n == 0)
 949                return;
 950
 951        while (G.in_stream != NULL) { /* EOF.  */
 952                size_t n_needed;
 953                size_t n_read;
 954
 955                n_needed = n - *n_bytes_in_buffer;
 956                n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, G.in_stream);
 957                *n_bytes_in_buffer += n_read;
 958                if (n_read == n_needed)
 959                        break;
 960                /* error check is done in check_and_close */
 961                check_and_close();
 962                open_next_file();
 963        }
 964}
 965
 966/* Return the least common multiple of the sizes associated
 967   with the format specs.  */
 968
 969static int
 970get_lcm(void)
 971{
 972        size_t i;
 973        int l_c_m = 1;
 974
 975        for (i = 0; i < G.n_specs; i++)
 976                l_c_m = lcm(l_c_m, width_bytes[(int) G.spec[i].size]);
 977        return l_c_m;
 978}
 979
 980/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
 981   formatted block to standard output, and repeat until the specified
 982   maximum number of bytes has been read or until all input has been
 983   processed.  If the last block read is smaller than BYTES_PER_BLOCK
 984   and its size is not a multiple of the size associated with a format
 985   spec, extend the input block with zero bytes until its length is a
 986   multiple of all format spec sizes.  Write the final block.  Finally,
 987   write on a line by itself the offset of the byte after the last byte
 988   read.  */
 989
 990static void
 991dump(off_t current_offset, off_t end_offset)
 992{
 993        char *block[2];
 994        int idx;
 995        size_t n_bytes_read;
 996
 997        block[0] = xmalloc(2 * G.bytes_per_block);
 998        block[1] = block[0] + G.bytes_per_block;
 999
1000        idx = 0;
1001        if (option_mask32 & OPT_N) {
1002                while (1) {
1003                        size_t n_needed;
1004                        if (current_offset >= end_offset) {
1005                                n_bytes_read = 0;
1006                                break;
1007                        }
1008                        n_needed = MIN(end_offset - current_offset, (off_t) G.bytes_per_block);
1009                        read_block(n_needed, block[idx], &n_bytes_read);
1010                        if (n_bytes_read < G.bytes_per_block)
1011                                break;
1012                        assert(n_bytes_read == G.bytes_per_block);
1013                        write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1014                        current_offset += n_bytes_read;
1015                        idx ^= 1;
1016                }
1017        } else {
1018                while (1) {
1019                        read_block(G.bytes_per_block, block[idx], &n_bytes_read);
1020                        if (n_bytes_read < G.bytes_per_block)
1021                                break;
1022                        assert(n_bytes_read == G.bytes_per_block);
1023                        write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1024                        current_offset += n_bytes_read;
1025                        idx ^= 1;
1026                }
1027        }
1028
1029        if (n_bytes_read > 0) {
1030                int l_c_m;
1031                size_t bytes_to_write;
1032
1033                l_c_m = get_lcm();
1034
1035                /* Make bytes_to_write the smallest multiple of l_c_m that
1036                   is at least as large as n_bytes_read.  */
1037                bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1038
1039                memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1040                write_block(current_offset, bytes_to_write,
1041                                block[idx ^ 1], block[idx]);
1042                current_offset += n_bytes_read;
1043        }
1044
1045        G.format_address(current_offset, '\n');
1046
1047        if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1048                check_and_close();
1049
1050        free(block[0]);
1051}
1052
1053/* Read N bytes into BLOCK from the concatenation of the input files
1054   named in the global array FILE_LIST.  On the first call to this
1055   function, the global variable IN_STREAM is expected to be an open
1056   stream associated with the input file INPUT_FILENAME.  If all N
1057   bytes cannot be read from IN_STREAM, close IN_STREAM and update
1058   the global variables IN_STREAM and INPUT_FILENAME.  Then try to
1059   read the remaining bytes from the newly opened file.  Repeat if
1060   necessary until EOF is reached for the last file in FILE_LIST.
1061   On subsequent calls, don't modify BLOCK and return zero.  Set
1062   *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
1063   it will be detected through ferror when the stream is about to be
1064   closed.  If there is an error, give a message but continue reading
1065   as usual and return nonzero.  Otherwise return zero.  */
1066
1067/* STRINGS mode.  Find each "string constant" in the input.
1068   A string constant is a run of at least 'string_min' ASCII
1069   graphic (or formatting) characters terminated by a null.
1070   Based on a function written by Richard Stallman for a
1071   traditional version of od.  */
1072
1073static void
1074dump_strings(off_t address, off_t end_offset)
1075{
1076        unsigned bufsize = MAX(100, G.string_min);
1077        unsigned char *buf = xmalloc(bufsize);
1078
1079        while (1) {
1080                size_t i;
1081                int c;
1082
1083                /* See if the next 'G.string_min' chars are all printing chars.  */
1084 tryline:
1085                if ((option_mask32 & OPT_N) && (end_offset - G.string_min <= address))
1086                        break;
1087                i = 0;
1088                while (!(option_mask32 & OPT_N) || address < end_offset) {
1089                        if (i == bufsize) {
1090                                bufsize += bufsize/8;
1091                                buf = xrealloc(buf, bufsize);
1092                        }
1093
1094                        while (G.in_stream) { /* !EOF */
1095                                c = fgetc(G.in_stream);
1096                                if (c != EOF)
1097                                        goto got_char;
1098                                check_and_close();
1099                                open_next_file();
1100                        }
1101                        /* EOF */
1102                        goto ret;
1103 got_char:
1104                        address++;
1105                        if (!c)
1106                                break;
1107                        if (!ISPRINT(c))
1108                                goto tryline;   /* It isn't; give up on this string.  */
1109                        buf[i++] = c;           /* String continues; store it all.  */
1110                }
1111
1112                if (i < G.string_min)           /* Too short! */
1113                        goto tryline;
1114
1115                /* If we get here, the string is all printable and NUL-terminated */
1116                buf[i] = 0;
1117                G.format_address(address - i - 1, ' ');
1118
1119                for (i = 0; (c = buf[i]); i++) {
1120                        switch (c) {
1121                        case '\007': fputs("\\a", stdout); break;
1122                        case '\b': fputs("\\b", stdout); break;
1123                        case '\f': fputs("\\f", stdout); break;
1124                        case '\n': fputs("\\n", stdout); break;
1125                        case '\r': fputs("\\r", stdout); break;
1126                        case '\t': fputs("\\t", stdout); break;
1127                        case '\v': fputs("\\v", stdout); break;
1128                        default: putchar(c);
1129                        }
1130                }
1131                putchar('\n');
1132        }
1133
1134        /* We reach this point only if we search through
1135           (max_bytes_to_format - G.string_min) bytes before reaching EOF.  */
1136        check_and_close();
1137 ret:
1138        free(buf);
1139}
1140
1141#if ENABLE_LONG_OPTS
1142/* If S is a valid traditional offset specification with an optional
1143   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
1144
1145static int
1146parse_old_offset(const char *s, off_t *offset)
1147{
1148        static const struct suffix_mult Bb[] = {
1149                { "B", 1024 },
1150                { "b", 512 },
1151                { "", 0 }
1152        };
1153        char *p;
1154        int radix;
1155
1156        /* Skip over any leading '+'. */
1157        if (s[0] == '+') ++s;
1158        if (!isdigit(s[0])) return 0; /* not a number */
1159
1160        /* Determine the radix we'll use to interpret S.  If there is a '.',
1161         * it's decimal, otherwise, if the string begins with '0X'or '0x',
1162         * it's hexadecimal, else octal.  */
1163        p = strchr(s, '.');
1164        radix = 8;
1165        if (p) {
1166                p[0] = '\0'; /* cheating */
1167                radix = 10;
1168        } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1169                radix = 16;
1170
1171        *offset = xstrtooff_sfx(s, radix, Bb);
1172        if (p) p[0] = '.';
1173
1174        return (*offset >= 0);
1175}
1176#endif
1177
1178int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1179int od_main(int argc UNUSED_PARAM, char **argv)
1180{
1181#if ENABLE_LONG_OPTS
1182        static const char od_longopts[] ALIGN1 =
1183                "skip-bytes\0"        Required_argument "j"
1184                "address-radix\0"     Required_argument "A"
1185                "read-bytes\0"        Required_argument "N"
1186                "format\0"            Required_argument "t"
1187                "output-duplicates\0" No_argument       "v"
1188                /* Yes, it's true: -S NUM, but --strings[=NUM]!
1189                 * that is, NUM is mandatory for -S but optional for --strings!
1190                 */
1191                "strings\0"           Optional_argument "S"
1192                "width\0"             Optional_argument "w"
1193                "traditional\0"       No_argument       "\xff"
1194                ;
1195#endif
1196        const char *str_A, *str_N, *str_j, *str_S = "3";
1197        llist_t *lst_t = NULL;
1198        unsigned opt;
1199        int l_c_m;
1200        /* The number of input bytes to skip before formatting and writing.  */
1201        off_t n_bytes_to_skip = 0;
1202        /* The offset of the first byte after the last byte to be formatted.  */
1203        off_t end_offset = 0;
1204        /* The maximum number of bytes that will be formatted.  */
1205        off_t max_bytes_to_format = 0;
1206
1207        INIT_G();
1208
1209        /*G.spec = NULL; - already is */
1210        G.format_address = format_address_std;
1211        address_base_char = 'o';
1212        address_pad_len_char = '7';
1213
1214        /* Parse command line */
1215#if ENABLE_LONG_OPTS
1216        applet_long_options = od_longopts;
1217#endif
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_error_msg_and_die("the last two arguments must be offsets");
1312                                }
1313                        } else { /* >3 args */
1314                                bb_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_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        for (i = 0; i < G.n_specs; i++) {
1377                printf("%d: fmt=\"%s\" width=%d\n",
1378                        i, spec[i].fmt_string, width_bytes[spec[i].size]);
1379        }
1380#endif
1381
1382        if (option_mask32 & OPT_S)
1383                dump_strings(n_bytes_to_skip, end_offset);
1384        else
1385                dump(n_bytes_to_skip, end_offset);
1386
1387        if (fclose(stdin))
1388                bb_perror_msg_and_die(bb_msg_standard_input);
1389
1390        return G.exit_code;
1391}
1392