rpm2cpio: handle bz2 too; code shrink
[oweals/busybox.git] / coreutils / od_bloaty.c
index 803407224c7818665a7b276ed0c057d3a59fd927..00efec51c64779aaa7adda5b3483ff8461a519a1 100644 (file)
@@ -17,7 +17,7 @@
 
 /* Written by Jim Meyering.  */
 
-/* Busyboxed by Denis Vlasenko
+/* Busyboxed by Denys Vlasenko
 
 Based on od.c from coreutils-5.2.1
 Top bloat sources:
@@ -50,7 +50,6 @@ diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
 */
 
 #include "libbb.h"
-#include <getopt.h>
 
 #define assert(a) ((void)0)
 
@@ -129,20 +128,20 @@ struct tspec {
    10  unsigned decimal
    8   unsigned hexadecimal  */
 
-static const uint8_t bytes_to_oct_digits[] =
+static const uint8_t bytes_to_oct_digits[] ALIGN1 =
 {0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
 
-static const uint8_t bytes_to_signed_dec_digits[] =
+static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
 {1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
 
-static const uint8_t bytes_to_unsigned_dec_digits[] =
+static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
 {0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
 
-static const uint8_t bytes_to_hex_digits[] =
+static const uint8_t bytes_to_hex_digits[] ALIGN1 =
 {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
 
 /* Convert enum size_spec to the size of the named type.  */
-static const signed char width_bytes[] = {
+static const signed char width_bytes[] ALIGN1 = {
        -1,
        sizeof(char),
        sizeof(short),
@@ -155,9 +154,8 @@ static const signed char width_bytes[] = {
 };
 /* Ensure that for each member of 'enum size_spec' there is an
    initializer in the width_bytes array.  */
-struct dummy {
-       int assert_width_bytes_matches_size_spec_decl
-               [ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
+struct ERR_width_bytes_has_bad_size {
+       char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
 };
 
 static smallint flag_dump_strings;
@@ -182,37 +180,23 @@ static void (*format_address)(off_t, char);
 /* The difference between the old-style pseudo starting address and
    the number of bytes to skip.  */
 static off_t pseudo_offset;
-/* The number of input bytes to skip before formatting and writing.  */
-static off_t n_bytes_to_skip;
 /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
    input is formatted.  */
-/* The maximum number of bytes that will be formatted.  */
-static off_t max_bytes_to_format;
-/* The offset of the first byte after the last byte to be formatted.  */
-static off_t end_offset;
 
 /* The number of input bytes formatted per output line.  It must be
    a multiple of the least common multiple of the sizes associated with
    the specified output types.  It should be as large as possible, but
    no larger than 16 -- unless specified with the -w option.  */
-static size_t bytes_per_block;
-
-/* Human-readable representation of *file_list (for error messages).
-   It differs from *file_list only when *file_list is "-".  */
-static char const *input_filename;
+static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */
 
 /* A NULL-terminated list of the file-arguments from the command line.  */
-static char const *const *file_list;
-
-/* Initializer for file_list if no file-arguments
-   were specified on the command line.  */
-static char const *const default_file_list[] = { "-", NULL };
+static const char *const *file_list;
 
 /* The input stream associated with the current file.  */
 static FILE *in_stream;
 
 #define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
-static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = {
+static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
        [sizeof(char)] = CHAR,
 #if USHRT_MAX != UCHAR_MAX
        [sizeof(short)] = SHORT,
@@ -229,11 +213,11 @@ static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = {
 };
 
 #define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
-static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = {
+static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
        /* gcc seems to allow repeated indexes. Last one stays */
        [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
        [sizeof(double)] = FLOAT_DOUBLE,
-       [sizeof(float)] = FLOAT_SINGLE,
+       [sizeof(float)] = FLOAT_SINGLE
 };
 
 
@@ -375,15 +359,15 @@ print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
 }
 
 /* print_[named]_ascii are optimized for speed.
- * Remember, someday you may want to pump gigabytes thru this thing.
+ * Remember, someday you may want to pump gigabytes through this thing.
  * Saving a dozen of .text bytes here is counter-productive */
 
 static void
 print_named_ascii(size_t n_bytes, const char *block,
-               const char *unused_fmt_string ATTRIBUTE_UNUSED)
+               const char *unused_fmt_string UNUSED_PARAM)
 {
        /* Names for some non-printing characters.  */
-       static const char charname[33][3] = {
+       static const char charname[33][3] ALIGN1 = {
                "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
                " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
                "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
@@ -420,7 +404,7 @@ print_named_ascii(size_t n_bytes, const char *block,
 
 static void
 print_ascii(size_t n_bytes, const char *block,
-               const char *unused_fmt_string ATTRIBUTE_UNUSED)
+               const char *unused_fmt_string UNUSED_PARAM)
 {
        // buf[N] pos:  01234 56789
        char buf[12] = "   x\0 0xx\0";
@@ -482,14 +466,10 @@ static void
 open_next_file(void)
 {
        while (1) {
-               input_filename = *file_list;
-               if (!input_filename)
+               if (!*file_list)
                        return;
-               file_list++;
-               in_stream = fopen_or_warn_stdin(input_filename);
+               in_stream = fopen_or_warn_stdin(*file_list++);
                if (in_stream) {
-                       if (in_stream == stdin)
-                               input_filename = bb_msg_standard_input;
                        break;
                }
                ioerror = 1;
@@ -511,7 +491,10 @@ check_and_close(void)
 {
        if (in_stream) {
                if (ferror(in_stream))  {
-                       bb_error_msg("%s: read error", input_filename);
+                       bb_error_msg("%s: read error", (in_stream == stdin)
+                                       ? bb_msg_standard_input
+                                       : file_list[-1]
+                       );
                        ioerror = 1;
                }
                fclose_if_not_stdin(in_stream);
@@ -525,10 +508,10 @@ check_and_close(void)
 }
 
 /* If S points to a single valid modern od format string, put
-   a description of that format in *TSPEC, make *NEXT point at the
-   character following the just-decoded format (if *NEXT is non-NULL),
-   and return zero.  For example, if S were "d4afL"
-   *NEXT would be set to "afL" and *TSPEC would be
+   a description of that format in *TSPEC, return pointer to
+   character following the just-decoded format.
+   For example, if S were "d4afL", we will return a rtp to "afL"
+   and *TSPEC would be
        {
                fmt = SIGNED_DECIMAL;
                size = INT or LONG; (whichever integral_type_size[4] resolves to)
@@ -538,9 +521,8 @@ check_and_close(void)
    S_ORIG is solely for reporting errors.  It should be the full format
    string argument. */
 
-static void
-decode_one_format(const char *s_orig, const char *s, const char **next,
-                                          struct tspec *tspec)
+static const char *
+decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
 {
        enum size_spec size_spec;
        unsigned size;
@@ -553,14 +535,13 @@ decode_one_format(const char *s_orig, const char *s, const char **next,
        unsigned field_width = 0;
        int pos;
 
-       assert(tspec != NULL);
 
        switch (*s) {
        case 'd':
        case 'o':
        case 'u':
        case 'x': {
-               static const char CSIL[] = "CSIL";
+               static const char CSIL[] ALIGN1 = "CSIL";
 
                c = *s++;
                p = strchr(CSIL, *s);
@@ -579,13 +560,14 @@ decode_one_format(const char *s_orig, const char *s, const char **next,
                                s = end;
                        }
                } else {
-                       static const uint8_t CSIL_sizeof[] = {
+                       static const uint8_t CSIL_sizeof[4] = {
                                sizeof(char),
                                sizeof(short),
                                sizeof(int),
                                sizeof(long),
                        };
                        size = CSIL_sizeof[p - CSIL];
+                       s++; /* skip C/S/I/L */
                }
 
 #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
@@ -596,7 +578,7 @@ decode_one_format(const char *s_orig, const char *s, const char **next,
                size_spec = integral_type_size[size];
 
                {
-                       static const char doux[] = "doux";
+                       static const char doux[] ALIGN1 = "doux";
                        static const char doux_fmt_letter[][4] = {
                                "lld", "llo", "llu", "llx"
                        };
@@ -653,7 +635,7 @@ decode_one_format(const char *s_orig, const char *s, const char **next,
        }
 
        case 'f': {
-               static const char FDL[] = "FDL";
+               static const char FDL[] ALIGN1 = "FDL";
 
                fmt = FLOATING_POINT;
                ++s;
@@ -733,13 +715,12 @@ decode_one_format(const char *s_orig, const char *s, const char **next,
        if (tspec->hexl_mode_trailer)
                s++;
 
-       if (next != NULL)
-               *next = s;
+       return s;
 }
 
 /* Decode the modern od format string S.  Append the decoded
    representation to the global array SPEC, reallocating SPEC if
-   necessary.  Return zero if S is valid, nonzero otherwise.  */
+   necessary.  */
 
 static void
 decode_format_string(const char *s)
@@ -750,13 +731,13 @@ decode_format_string(const char *s)
                struct tspec tspec;
                const char *next;
 
-               decode_one_format(s_orig, s, &next, &tspec);
+               next = decode_one_format(s_orig, s, &tspec);
 
                assert(s != next);
                s = next;
+               spec = xrealloc_vector(spec, 4, n_specs);
+               memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
                n_specs++;
-               spec = xrealloc(spec, n_specs * sizeof(*spec));
-               memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
        }
 }
 
@@ -792,21 +773,22 @@ skip(off_t n_skip)
                           as large as the size of the current file, we can
                           decrement n_skip and go on to the next file.  */
                if (fstat(fileno(in_stream), &file_stats) == 0
-                && S_ISREG(file_stats.st_mode) && file_stats.st_size >= 0
+                && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
                ) {
                        if (file_stats.st_size < n_skip) {
                                n_skip -= file_stats.st_size;
-                               /* take check&close / open_next route */
+                               /* take "check & close / open_next" route */
                        } else {
                                if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
                                        ioerror = 1;
                                return;
                        }
                } else {
-                       /* If it's not a regular file with nonnegative size,
+                       /* If it's not a regular file with positive size,
                           position the file pointer by reading.  */
-                       char buf[BUFSIZ];
-                       size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
+                       char buf[1024];
+                       size_t n_bytes_to_read = 1024;
+                       size_t n_bytes_read;
 
                        while (n_skip > 0) {
                                if (n_skip < n_bytes_to_read)
@@ -832,11 +814,11 @@ skip(off_t n_skip)
 typedef void FN_format_address(off_t address, char c);
 
 static void
-format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
+format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
 {
 }
 
-static char address_fmt[] = "%0n"OFF_FMT"xc";
+static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
 /* Corresponds to 'x' above */
 #define address_base_char address_fmt[sizeof(address_fmt)-3]
 /* Corresponds to 'n' above */
@@ -850,7 +832,7 @@ format_address_std(off_t address, char c)
        printf(address_fmt, address);
 }
 
-#if ENABLE_GETOPT_LONG
+#if ENABLE_LONG_OPTS
 /* only used with --traditional */
 static void
 format_address_paren(off_t address, char c)
@@ -971,7 +953,7 @@ get_lcm(void)
        return l_c_m;
 }
 
-#if ENABLE_GETOPT_LONG
+#if ENABLE_LONG_OPTS
 /* If S is a valid traditional offset specification with an optional
    leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
 
@@ -981,7 +963,7 @@ parse_old_offset(const char *s, off_t *offset)
        static const struct suffix_mult Bb[] = {
                { "B", 1024 },
                { "b", 512 },
-               { NULL, 0 }
+               { }
        };
        char *p;
        int radix;
@@ -1015,23 +997,18 @@ parse_old_offset(const char *s, off_t *offset)
    spec, extend the input block with zero bytes until its length is a
    multiple of all format spec sizes.  Write the final block.  Finally,
    write on a line by itself the offset of the byte after the last byte
-   read.  Accumulate return values from calls to read_block and
-   check_and_close, and if any was nonzero, return nonzero.
-   Otherwise, return zero.  */
+   read.  */
 
 static void
-dump(void)
+dump(off_t current_offset, off_t end_offset)
 {
        char *block[2];
-       off_t current_offset;
        int idx;
        size_t n_bytes_read;
 
        block[0] = xmalloc(2*bytes_per_block);
        block[1] = block[0] + bytes_per_block;
 
-       current_offset = n_bytes_to_skip;
-
        idx = 0;
        if (limit_bytes_to_format) {
                while (1) {
@@ -1096,8 +1073,7 @@ dump(void)
    and INPUT_FILENAME so they correspond to the next file in the list.
    Then try to read a byte from the newly opened file.  Repeat if
    necessary until EOF is reached for the last file in FILE_LIST, then
-   set *C to EOF and return.  Subsequent calls do likewise.  The return
-   value is nonzero if any errors occured, zero otherwise.  */
+   set *C to EOF and return.  Subsequent calls do likewise.  */
 
 static void
 read_char(int *c)
@@ -1130,15 +1106,13 @@ read_char(int *c)
    A string constant is a run of at least 'string_min' ASCII
    graphic (or formatting) characters terminated by a null.
    Based on a function written by Richard Stallman for a
-   traditional version of od.  Return nonzero if an error
-   occurs.  Otherwise, return zero.  */
+   traditional version of od.  */
 
 static void
-dump_strings(void)
+dump_strings(off_t address, off_t end_offset)
 {
        size_t bufsize = MAX(100, string_min);
        char *buf = xmalloc(bufsize);
-       off_t address = n_bytes_to_skip;
 
        while (1) {
                size_t i;
@@ -1170,7 +1144,7 @@ dump_strings(void)
                if (i < string_min)             /* Too short! */
                        goto tryline;
 
-               /* If we get here, the string is all printable and null-terminated,
+               /* If we get here, the string is all printable and NUL-terminated,
                 * so print it.  It is all in 'buf' and 'i' is its length.  */
                buf[i] = 0;
                format_address(address - i - 1, ' ');
@@ -1184,7 +1158,7 @@ dump_strings(void)
                        case '\r': fputs("\\r", stdout); break;
                        case '\t': fputs("\\t", stdout); break;
                        case '\v': fputs("\\v", stdout); break;
-                       default: putc(c, stdout);
+                       default: putchar(c);
                        }
                }
                putchar('\n');
@@ -1197,20 +1171,15 @@ dump_strings(void)
        check_and_close();
 }
 
-int od_main(int argc, char **argv);
+int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int od_main(int argc, char **argv)
 {
        static const struct suffix_mult bkm[] = {
                { "b", 512 },
                { "k", 1024 },
                { "m", 1024*1024 },
-               { NULL, 0 }
+               { }
        };
-       unsigned opt;
-       int l_c_m;
-       /* The old-style 'pseudo starting address' to be printed in parentheses
-          after any true address.  */
-       off_t pseudo_start = 0; // only for gcc
        enum {
                OPT_A = 1 << 0,
                OPT_N = 1 << 1,
@@ -1230,10 +1199,10 @@ int od_main(int argc, char **argv)
                OPT_s = 1 << 15,
                OPT_S = 1 << 16,
                OPT_w = 1 << 17,
-               OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
+               OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
        };
-#if ENABLE_GETOPT_LONG
-       static const char od_longopts[] =
+#if ENABLE_LONG_OPTS
+       static const char od_longopts[] ALIGN1 =
                "skip-bytes\0"        Required_argument "j"
                "address-radix\0"     Required_argument "A"
                "read-bytes\0"        Required_argument "N"
@@ -1245,8 +1214,18 @@ int od_main(int argc, char **argv)
                ;
 #endif
        char *str_A, *str_N, *str_j, *str_S;
-       char *str_w = NULL;
        llist_t *lst_t = NULL;
+       unsigned opt;
+       int l_c_m;
+       /* The old-style 'pseudo starting address' to be printed in parentheses
+          after any true address.  */
+       off_t pseudo_start = pseudo_start; // for gcc
+       /* The number of input bytes to skip before formatting and writing.  */
+       off_t n_bytes_to_skip = 0;
+       /* The offset of the first byte after the last byte to be formatted.  */
+       off_t end_offset = 0;
+       /* The maximum number of bytes that will be formatted.  */
+       off_t max_bytes_to_format = 0;
 
        spec = NULL;
        format_address = format_address_std;
@@ -1255,24 +1234,24 @@ int od_main(int argc, char **argv)
        /* flag_dump_strings = 0; - already is */
 
        /* Parse command line */
-       opt_complementary = "t::"; // list
-#if ENABLE_GETOPT_LONG
+       opt_complementary = "w+:t::"; /* -w N, -t is a list */
+#if ENABLE_LONG_OPTS
        applet_long_options = od_longopts;
 #endif
-       opt = getopt32(argc, argv, "A:N:abcdfhij:lot:vxsS:"
+       opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
                "w::", // -w with optional param
                // -S was -s and also had optional parameter
                // but in coreutils 6.3 it was renamed and now has
                // _mandatory_ parameter
-               &str_A, &str_N, &str_j, &lst_t, &str_S, &str_w);
+               &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
        argc -= optind;
        argv += optind;
        if (opt & OPT_A) {
-               static const char doxn[] = "doxn";
-               static const char doxn_address_base_char[] = {
+               static const char doxn[] ALIGN1 = "doxn";
+               static const char doxn_address_base_char[] ALIGN1 = {
                        'u', 'o', 'x', /* '?' fourth one is not important */
                };
-               static const uint8_t doxn_address_pad_len_char[] = {
+               static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
                        '7', '7', '6', /* '?' */
                };
                char *p;
@@ -1302,8 +1281,7 @@ int od_main(int argc, char **argv)
        if (opt & OPT_o) decode_format_string("o2");
        //if (opt & OPT_t)...
        while (lst_t) {
-               decode_format_string(lst_t->data);
-               lst_t = lst_t->link;
+               decode_format_string(llist_pop(&lst_t));
        }
        if (opt & OPT_v) verbose = 1;
        if (opt & OPT_x) decode_format_string("x2");
@@ -1328,7 +1306,7 @@ int od_main(int argc, char **argv)
         * FIXME: POSIX 1003.1-2001 with XSI requires support for the
         * traditional syntax even if --traditional is not given.  */
 
-#if ENABLE_GETOPT_LONG
+#if ENABLE_LONG_OPTS
        if (opt & OPT_traditional) {
                off_t o1, o2;
 
@@ -1400,7 +1378,7 @@ int od_main(int argc, char **argv)
        /* If no files were listed on the command line,
           set the global pointer FILE_LIST so that it
           references the null-terminated list of one name: "-".  */
-       file_list = default_file_list;
+       file_list = bb_argv_dash;
        if (argc > 0) {
                /* Set the global pointer FILE_LIST so that it
                   references the first file-argument on the command-line.  */
@@ -1412,7 +1390,7 @@ int od_main(int argc, char **argv)
        /* skip over any unwanted header bytes */
        skip(n_bytes_to_skip);
        if (!in_stream)
-               return 1;
+               return EXIT_FAILURE;
 
        pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
 
@@ -1420,12 +1398,9 @@ int od_main(int argc, char **argv)
        l_c_m = get_lcm();
 
        if (opt & OPT_w) { /* -w: width */
-               bytes_per_block = 32;
-               if (str_w)
-                       bytes_per_block = xatou(str_w);
                if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
-                       bb_error_msg("warning: invalid width %zu; using %d instead",
-                                       bytes_per_block, l_c_m);
+                       bb_error_msg("warning: invalid width %u; using %d instead",
+                                       (unsigned)bytes_per_block, l_c_m);
                        bytes_per_block = l_c_m;
                }
        } else {
@@ -1442,9 +1417,9 @@ int od_main(int argc, char **argv)
 #endif
 
        if (flag_dump_strings)
-               dump_strings();
+               dump_strings(n_bytes_to_skip, end_offset);
        else
-               dump();
+               dump(n_bytes_to_skip, end_offset);
 
        if (fclose(stdin) == EOF)
                bb_perror_msg_and_die(bb_msg_standard_input);