source: branches/3.2/mindi-busybox/coreutils/od_bloaty.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 36.2 KB
Line 
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}
Note: See TracBrowser for help on using the repository browser.