17 |
|
|
18 |
/* Written by Jim Meyering. */ |
/* Written by Jim Meyering. */ |
19 |
|
|
20 |
/* Busyboxed by Denis Vlasenko |
/* Busyboxed by Denys Vlasenko |
21 |
|
|
22 |
Based on od.c from coreutils-5.2.1 |
Based on od.c from coreutils-5.2.1 |
23 |
Top bloat sources: |
Top bloat sources: |
49 |
|
|
50 |
*/ |
*/ |
51 |
|
|
52 |
|
#include "libbb.h" |
|
#include "busybox.h" |
|
|
#include <getopt.h> |
|
53 |
|
|
54 |
#define assert(a) ((void)0) |
#define assert(a) ((void)0) |
55 |
|
|
128 |
10 unsigned decimal |
10 unsigned decimal |
129 |
8 unsigned hexadecimal */ |
8 unsigned hexadecimal */ |
130 |
|
|
131 |
static const uint8_t bytes_to_oct_digits[] = |
static const uint8_t bytes_to_oct_digits[] ALIGN1 = |
132 |
{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43}; |
{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43}; |
133 |
|
|
134 |
static const uint8_t bytes_to_signed_dec_digits[] = |
static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 = |
135 |
{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40}; |
{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40}; |
136 |
|
|
137 |
static const uint8_t bytes_to_unsigned_dec_digits[] = |
static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 = |
138 |
{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39}; |
{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39}; |
139 |
|
|
140 |
static const uint8_t bytes_to_hex_digits[] = |
static const uint8_t bytes_to_hex_digits[] ALIGN1 = |
141 |
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}; |
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}; |
142 |
|
|
143 |
/* Convert enum size_spec to the size of the named type. */ |
/* Convert enum size_spec to the size of the named type. */ |
144 |
static const signed char width_bytes[] = { |
static const signed char width_bytes[] ALIGN1 = { |
145 |
-1, |
-1, |
146 |
sizeof(char), |
sizeof(char), |
147 |
sizeof(short), |
sizeof(short), |
152 |
sizeof(double), |
sizeof(double), |
153 |
sizeof(longdouble_t) |
sizeof(longdouble_t) |
154 |
}; |
}; |
|
|
|
155 |
/* Ensure that for each member of 'enum size_spec' there is an |
/* Ensure that for each member of 'enum size_spec' there is an |
156 |
initializer in the width_bytes array. */ |
initializer in the width_bytes array. */ |
157 |
struct dummy { |
struct ERR_width_bytes_has_bad_size { |
158 |
int assert_width_bytes_matches_size_spec_decl |
char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; |
|
[sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1]; |
|
159 |
}; |
}; |
160 |
|
|
161 |
|
static smallint flag_dump_strings; |
162 |
|
/* Non-zero if an old-style 'pseudo-address' was specified. */ |
163 |
|
static smallint flag_pseudo_start; |
164 |
|
static smallint limit_bytes_to_format; |
165 |
|
/* When zero and two or more consecutive blocks are equal, format |
166 |
|
only the first block and output an asterisk alone on the following |
167 |
|
line to indicate that identical blocks have been elided. */ |
168 |
|
static smallint verbose; |
169 |
|
static smallint ioerror; |
170 |
|
|
171 |
static size_t string_min; |
static size_t string_min; |
|
static int flag_dump_strings; |
|
172 |
|
|
173 |
/* Non-zero if an old-style 'pseudo-address' was specified. */ |
/* An array of specs describing how to format each input block. */ |
174 |
static int flag_pseudo_start; |
static size_t n_specs; |
175 |
|
static struct tspec *spec; |
176 |
|
|
177 |
|
/* Function that accepts an address and an optional following char, |
178 |
|
and prints the address and char to stdout. */ |
179 |
|
static void (*format_address)(off_t, char); |
180 |
/* The difference between the old-style pseudo starting address and |
/* The difference between the old-style pseudo starting address and |
181 |
the number of bytes to skip. */ |
the number of bytes to skip. */ |
182 |
static off_t pseudo_offset; |
static off_t pseudo_offset; |
|
|
|
|
/* Function that accepts an address and an optional following char, |
|
|
and prints the address and char to stdout. */ |
|
|
static void (*format_address) (off_t, char); |
|
|
|
|
|
/* The number of input bytes to skip before formatting and writing. */ |
|
|
static off_t n_bytes_to_skip; // = 0; |
|
|
|
|
183 |
/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all |
/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all |
184 |
input is formatted. */ |
input is formatted. */ |
|
static int limit_bytes_to_format; // = 0; |
|
|
|
|
|
/* 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; |
|
|
|
|
|
/* When nonzero and two or more consecutive blocks are equal, format |
|
|
only the first block and output an asterisk alone on the following |
|
|
line to indicate that identical blocks have been elided. */ |
|
|
static int abbreviate_duplicate_blocks = 1; |
|
|
|
|
|
/* An array of specs describing how to format each input block. */ |
|
|
static size_t n_specs; |
|
|
static struct tspec *spec; |
|
185 |
|
|
186 |
/* The number of input bytes formatted per output line. It must be |
/* The number of input bytes formatted per output line. It must be |
187 |
a multiple of the least common multiple of the sizes associated with |
a multiple of the least common multiple of the sizes associated with |
188 |
the specified output types. It should be as large as possible, but |
the specified output types. It should be as large as possible, but |
189 |
no larger than 16 -- unless specified with the -w option. */ |
no larger than 16 -- unless specified with the -w option. */ |
190 |
static size_t bytes_per_block; |
static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */ |
|
|
|
|
/* Human-readable representation of *file_list (for error messages). |
|
|
It differs from *file_list only when *file_list is "-". */ |
|
|
static char const *input_filename; |
|
191 |
|
|
192 |
/* A NULL-terminated list of the file-arguments from the command line. */ |
/* A NULL-terminated list of the file-arguments from the command line. */ |
193 |
static char const *const *file_list; |
static const char *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 }; |
|
194 |
|
|
195 |
/* The input stream associated with the current file. */ |
/* The input stream associated with the current file. */ |
196 |
static FILE *in_stream; |
static FILE *in_stream; |
197 |
|
|
|
static int ioerror; |
|
|
|
|
198 |
#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) |
#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) |
199 |
static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = { |
static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = { |
200 |
[sizeof(char)] = CHAR, |
[sizeof(char)] = CHAR, |
201 |
#if USHRT_MAX != UCHAR_MAX |
#if USHRT_MAX != UCHAR_MAX |
202 |
[sizeof(short)] = SHORT, |
[sizeof(short)] = SHORT, |
213 |
}; |
}; |
214 |
|
|
215 |
#define MAX_FP_TYPE_SIZE sizeof(longdouble_t) |
#define MAX_FP_TYPE_SIZE sizeof(longdouble_t) |
216 |
static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = { |
static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { |
217 |
/* gcc seems to allow repeated indexes. Last one stays */ |
/* gcc seems to allow repeated indexes. Last one stays */ |
218 |
[sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, |
[sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, |
219 |
[sizeof(double)] = FLOAT_DOUBLE, |
[sizeof(double)] = FLOAT_DOUBLE, |
220 |
[sizeof(float)] = FLOAT_SINGLE, |
[sizeof(float)] = FLOAT_SINGLE |
221 |
}; |
}; |
222 |
|
|
223 |
|
|
359 |
} |
} |
360 |
|
|
361 |
/* print_[named]_ascii are optimized for speed. |
/* print_[named]_ascii are optimized for speed. |
362 |
* Remember, someday you may want to pump gigabytes thru this thing. |
* Remember, someday you may want to pump gigabytes through this thing. |
363 |
* Saving a dozen of .text bytes here is counter-productive */ |
* Saving a dozen of .text bytes here is counter-productive */ |
364 |
|
|
365 |
static void |
static void |
366 |
print_named_ascii(size_t n_bytes, const char *block, |
print_named_ascii(size_t n_bytes, const char *block, |
367 |
const char *unused_fmt_string ATTRIBUTE_UNUSED) |
const char *unused_fmt_string UNUSED_PARAM) |
368 |
{ |
{ |
369 |
/* Names for some non-printing characters. */ |
/* Names for some non-printing characters. */ |
370 |
static const char charname[33][3] = { |
static const char charname[33][3] ALIGN1 = { |
371 |
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", |
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", |
372 |
" bs", " ht", " nl", " vt", " ff", " cr", " so", " si", |
" bs", " ht", " nl", " vt", " ff", " cr", " so", " si", |
373 |
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", |
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", |
404 |
|
|
405 |
static void |
static void |
406 |
print_ascii(size_t n_bytes, const char *block, |
print_ascii(size_t n_bytes, const char *block, |
407 |
const char *unused_fmt_string ATTRIBUTE_UNUSED) |
const char *unused_fmt_string UNUSED_PARAM) |
408 |
{ |
{ |
409 |
// buf[N] pos: 01234 56789 |
// buf[N] pos: 01234 56789 |
410 |
char buf[12] = " x\0 0xx\0"; |
char buf[12] = " x\0 0xx\0"; |
466 |
open_next_file(void) |
open_next_file(void) |
467 |
{ |
{ |
468 |
while (1) { |
while (1) { |
469 |
input_filename = *file_list; |
if (!*file_list) |
|
if (!input_filename) |
|
470 |
return; |
return; |
471 |
file_list++; |
in_stream = fopen_or_warn_stdin(*file_list++); |
|
in_stream = fopen_or_warn_stdin(input_filename); |
|
472 |
if (in_stream) { |
if (in_stream) { |
|
if (in_stream == stdin) |
|
|
input_filename = bb_msg_standard_input; |
|
473 |
break; |
break; |
474 |
} |
} |
475 |
ioerror = 1; |
ioerror = 1; |
491 |
{ |
{ |
492 |
if (in_stream) { |
if (in_stream) { |
493 |
if (ferror(in_stream)) { |
if (ferror(in_stream)) { |
494 |
bb_error_msg("%s: read error", input_filename); |
bb_error_msg("%s: read error", (in_stream == stdin) |
495 |
|
? bb_msg_standard_input |
496 |
|
: file_list[-1] |
497 |
|
); |
498 |
ioerror = 1; |
ioerror = 1; |
499 |
} |
} |
500 |
fclose_if_not_stdin(in_stream); |
fclose_if_not_stdin(in_stream); |
508 |
} |
} |
509 |
|
|
510 |
/* If S points to a single valid modern od format string, put |
/* If S points to a single valid modern od format string, put |
511 |
a description of that format in *TSPEC, make *NEXT point at the |
a description of that format in *TSPEC, return pointer to |
512 |
character following the just-decoded format (if *NEXT is non-NULL), |
character following the just-decoded format. |
513 |
and return zero. For example, if S were "d4afL" |
For example, if S were "d4afL", we will return a rtp to "afL" |
514 |
*NEXT would be set to "afL" and *TSPEC would be |
and *TSPEC would be |
515 |
{ |
{ |
516 |
fmt = SIGNED_DECIMAL; |
fmt = SIGNED_DECIMAL; |
517 |
size = INT or LONG; (whichever integral_type_size[4] resolves to) |
size = INT or LONG; (whichever integral_type_size[4] resolves to) |
518 |
print_function = print_int; (assuming size == INT) |
print_function = print_int; (assuming size == INT) |
519 |
fmt_string = "%011d%c"; |
fmt_string = "%011d%c"; |
520 |
} |
} |
521 |
S_ORIG is solely for reporting errors. It should be the full format |
S_ORIG is solely for reporting errors. It should be the full format |
522 |
string argument. */ |
string argument. */ |
523 |
|
|
524 |
static void |
static const char * |
525 |
decode_one_format(const char *s_orig, const char *s, const char **next, |
decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) |
|
struct tspec *tspec) |
|
526 |
{ |
{ |
527 |
enum size_spec size_spec; |
enum size_spec size_spec; |
528 |
unsigned size; |
unsigned size; |
535 |
unsigned field_width = 0; |
unsigned field_width = 0; |
536 |
int pos; |
int pos; |
537 |
|
|
|
assert(tspec != NULL); |
|
538 |
|
|
539 |
switch (*s) { |
switch (*s) { |
540 |
case 'd': |
case 'd': |
541 |
case 'o': |
case 'o': |
542 |
case 'u': |
case 'u': |
543 |
case 'x': { |
case 'x': { |
544 |
static const char CSIL[] = "CSIL"; |
static const char CSIL[] ALIGN1 = "CSIL"; |
545 |
|
|
546 |
c = *s++; |
c = *s++; |
547 |
p = strchr(CSIL, *s); |
p = strchr(CSIL, *s); |
560 |
s = end; |
s = end; |
561 |
} |
} |
562 |
} else { |
} else { |
563 |
static const uint8_t CSIL_sizeof[] = { |
static const uint8_t CSIL_sizeof[4] = { |
564 |
sizeof(char), |
sizeof(char), |
565 |
sizeof(short), |
sizeof(short), |
566 |
sizeof(int), |
sizeof(int), |
567 |
sizeof(long), |
sizeof(long), |
568 |
}; |
}; |
569 |
size = CSIL_sizeof[p - CSIL]; |
size = CSIL_sizeof[p - CSIL]; |
570 |
|
s++; /* skip C/S/I/L */ |
571 |
} |
} |
572 |
|
|
573 |
#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \ |
#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \ |
578 |
size_spec = integral_type_size[size]; |
size_spec = integral_type_size[size]; |
579 |
|
|
580 |
{ |
{ |
581 |
static const char doux[] = "doux"; |
static const char doux[] ALIGN1 = "doux"; |
582 |
static const char doux_fmt_letter[][4] = { |
static const char doux_fmt_letter[][4] = { |
583 |
"lld", "llo", "llu", "llx" |
"lld", "llo", "llu", "llx" |
584 |
}; |
}; |
635 |
} |
} |
636 |
|
|
637 |
case 'f': { |
case 'f': { |
638 |
static const char FDL[] = "FDL"; |
static const char FDL[] ALIGN1 = "FDL"; |
639 |
|
|
640 |
fmt = FLOATING_POINT; |
fmt = FLOATING_POINT; |
641 |
++s; |
++s; |
715 |
if (tspec->hexl_mode_trailer) |
if (tspec->hexl_mode_trailer) |
716 |
s++; |
s++; |
717 |
|
|
718 |
if (next != NULL) |
return s; |
|
*next = s; |
|
719 |
} |
} |
720 |
|
|
721 |
/* Decode the modern od format string S. Append the decoded |
/* Decode the modern od format string S. Append the decoded |
722 |
representation to the global array SPEC, reallocating SPEC if |
representation to the global array SPEC, reallocating SPEC if |
723 |
necessary. Return zero if S is valid, nonzero otherwise. */ |
necessary. */ |
724 |
|
|
725 |
static void |
static void |
726 |
decode_format_string(const char *s) |
decode_format_string(const char *s) |
731 |
struct tspec tspec; |
struct tspec tspec; |
732 |
const char *next; |
const char *next; |
733 |
|
|
734 |
decode_one_format(s_orig, s, &next, &tspec); |
next = decode_one_format(s_orig, s, &tspec); |
735 |
|
|
736 |
assert(s != next); |
assert(s != next); |
737 |
s = next; |
s = next; |
738 |
|
spec = xrealloc_vector(spec, 4, n_specs); |
739 |
|
memcpy(&spec[n_specs], &tspec, sizeof(spec[0])); |
740 |
n_specs++; |
n_specs++; |
|
spec = xrealloc(spec, n_specs * sizeof(*spec)); |
|
|
memcpy(&spec[n_specs-1], &tspec, sizeof *spec); |
|
741 |
} |
} |
742 |
} |
} |
743 |
|
|
773 |
as large as the size of the current file, we can |
as large as the size of the current file, we can |
774 |
decrement n_skip and go on to the next file. */ |
decrement n_skip and go on to the next file. */ |
775 |
if (fstat(fileno(in_stream), &file_stats) == 0 |
if (fstat(fileno(in_stream), &file_stats) == 0 |
776 |
&& S_ISREG(file_stats.st_mode) && file_stats.st_size >= 0 |
&& S_ISREG(file_stats.st_mode) && file_stats.st_size > 0 |
777 |
) { |
) { |
778 |
if (file_stats.st_size < n_skip) { |
if (file_stats.st_size < n_skip) { |
779 |
n_skip -= file_stats.st_size; |
n_skip -= file_stats.st_size; |
780 |
/* take check&close / open_next route */ |
/* take "check & close / open_next" route */ |
781 |
} else { |
} else { |
782 |
if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) |
if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) |
783 |
ioerror = 1; |
ioerror = 1; |
784 |
return; |
return; |
785 |
} |
} |
786 |
} else { |
} else { |
787 |
/* If it's not a regular file with nonnegative size, |
/* If it's not a regular file with positive size, |
788 |
position the file pointer by reading. */ |
position the file pointer by reading. */ |
789 |
char buf[BUFSIZ]; |
char buf[1024]; |
790 |
size_t n_bytes_read, n_bytes_to_read = BUFSIZ; |
size_t n_bytes_to_read = 1024; |
791 |
|
size_t n_bytes_read; |
792 |
|
|
793 |
while (n_skip > 0) { |
while (n_skip > 0) { |
794 |
if (n_skip < n_bytes_to_read) |
if (n_skip < n_bytes_to_read) |
796 |
n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream); |
n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream); |
797 |
n_skip -= n_bytes_read; |
n_skip -= n_bytes_read; |
798 |
if (n_bytes_read != n_bytes_to_read) |
if (n_bytes_read != n_bytes_to_read) |
799 |
break; /* EOF on this file or error */ |
break; /* EOF on this file or error */ |
800 |
} |
} |
801 |
} |
} |
802 |
if (n_skip == 0) |
if (n_skip == 0) |
814 |
typedef void FN_format_address(off_t address, char c); |
typedef void FN_format_address(off_t address, char c); |
815 |
|
|
816 |
static void |
static void |
817 |
format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED) |
format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM) |
818 |
{ |
{ |
819 |
} |
} |
820 |
|
|
821 |
static char address_fmt[] = "%0n"OFF_FMT"xc"; |
static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc"; |
822 |
/* Corresponds to 'x' above */ |
/* Corresponds to 'x' above */ |
823 |
#define address_base_char address_fmt[sizeof(address_fmt)-3] |
#define address_base_char address_fmt[sizeof(address_fmt)-3] |
824 |
/* Corresponds to 'n' above */ |
/* Corresponds to 'n' above */ |
881 |
static char prev_pair_equal = 0; |
static char prev_pair_equal = 0; |
882 |
size_t i; |
size_t i; |
883 |
|
|
884 |
if (abbreviate_duplicate_blocks |
if (!verbose && !first |
|
&& !first |
|
885 |
&& n_bytes == bytes_per_block |
&& n_bytes == bytes_per_block |
886 |
&& memcmp(prev_block, curr_block, bytes_per_block) == 0 |
&& memcmp(prev_block, curr_block, bytes_per_block) == 0 |
887 |
) { |
) { |
963 |
static const struct suffix_mult Bb[] = { |
static const struct suffix_mult Bb[] = { |
964 |
{ "B", 1024 }, |
{ "B", 1024 }, |
965 |
{ "b", 512 }, |
{ "b", 512 }, |
966 |
{ NULL, 0 } |
{ } |
967 |
}; |
}; |
968 |
char *p; |
char *p; |
969 |
int radix; |
int radix; |
997 |
spec, extend the input block with zero bytes until its length is a |
spec, extend the input block with zero bytes until its length is a |
998 |
multiple of all format spec sizes. Write the final block. Finally, |
multiple of all format spec sizes. Write the final block. Finally, |
999 |
write on a line by itself the offset of the byte after the last byte |
write on a line by itself the offset of the byte after the last byte |
1000 |
read. Accumulate return values from calls to read_block and |
read. */ |
|
check_and_close, and if any was nonzero, return nonzero. |
|
|
Otherwise, return zero. */ |
|
1001 |
|
|
1002 |
static void |
static void |
1003 |
dump(void) |
dump(off_t current_offset, off_t end_offset) |
1004 |
{ |
{ |
1005 |
char *block[2]; |
char *block[2]; |
|
off_t current_offset; |
|
1006 |
int idx; |
int idx; |
1007 |
size_t n_bytes_read; |
size_t n_bytes_read; |
1008 |
|
|
1009 |
block[0] = xmalloc(2*bytes_per_block); |
block[0] = xmalloc(2*bytes_per_block); |
1010 |
block[1] = block[0] + bytes_per_block; |
block[1] = block[0] + bytes_per_block; |
1011 |
|
|
|
current_offset = n_bytes_to_skip; |
|
|
|
|
1012 |
idx = 0; |
idx = 0; |
1013 |
if (limit_bytes_to_format) { |
if (limit_bytes_to_format) { |
1014 |
while (1) { |
while (1) { |
1073 |
and INPUT_FILENAME so they correspond to the next file in the list. |
and INPUT_FILENAME so they correspond to the next file in the list. |
1074 |
Then try to read a byte from the newly opened file. Repeat if |
Then try to read a byte from the newly opened file. Repeat if |
1075 |
necessary until EOF is reached for the last file in FILE_LIST, then |
necessary until EOF is reached for the last file in FILE_LIST, then |
1076 |
set *C to EOF and return. Subsequent calls do likewise. The return |
set *C to EOF and return. Subsequent calls do likewise. */ |
|
value is nonzero if any errors occured, zero otherwise. */ |
|
1077 |
|
|
1078 |
static void |
static void |
1079 |
read_char(int *c) |
read_char(int *c) |
1106 |
A string constant is a run of at least 'string_min' ASCII |
A string constant is a run of at least 'string_min' ASCII |
1107 |
graphic (or formatting) characters terminated by a null. |
graphic (or formatting) characters terminated by a null. |
1108 |
Based on a function written by Richard Stallman for a |
Based on a function written by Richard Stallman for a |
1109 |
traditional version of od. Return nonzero if an error |
traditional version of od. */ |
|
occurs. Otherwise, return zero. */ |
|
1110 |
|
|
1111 |
static void |
static void |
1112 |
dump_strings(void) |
dump_strings(off_t address, off_t end_offset) |
1113 |
{ |
{ |
1114 |
size_t bufsize = MAX(100, string_min); |
size_t bufsize = MAX(100, string_min); |
1115 |
char *buf = xmalloc(bufsize); |
char *buf = xmalloc(bufsize); |
|
off_t address = n_bytes_to_skip; |
|
1116 |
|
|
1117 |
while (1) { |
while (1) { |
1118 |
size_t i; |
size_t i; |
1144 |
if (i < string_min) /* Too short! */ |
if (i < string_min) /* Too short! */ |
1145 |
goto tryline; |
goto tryline; |
1146 |
|
|
1147 |
/* If we get here, the string is all printable and null-terminated, |
/* If we get here, the string is all printable and NUL-terminated, |
1148 |
* so print it. It is all in 'buf' and 'i' is its length. */ |
* so print it. It is all in 'buf' and 'i' is its length. */ |
1149 |
buf[i] = 0; |
buf[i] = 0; |
1150 |
format_address(address - i - 1, ' '); |
format_address(address - i - 1, ' '); |
1158 |
case '\r': fputs("\\r", stdout); break; |
case '\r': fputs("\\r", stdout); break; |
1159 |
case '\t': fputs("\\t", stdout); break; |
case '\t': fputs("\\t", stdout); break; |
1160 |
case '\v': fputs("\\v", stdout); break; |
case '\v': fputs("\\v", stdout); break; |
1161 |
default: putc(c, stdout); |
default: putchar(c); |
1162 |
} |
} |
1163 |
} |
} |
1164 |
putchar('\n'); |
putchar('\n'); |
1171 |
check_and_close(); |
check_and_close(); |
1172 |
} |
} |
1173 |
|
|
1174 |
int |
int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1175 |
od_main(int argc, char **argv) |
int od_main(int argc, char **argv) |
1176 |
{ |
{ |
1177 |
static const struct suffix_mult bkm[] = { |
static const struct suffix_mult bkm[] = { |
1178 |
{ "b", 512 }, |
{ "b", 512 }, |
1179 |
{ "k", 1024 }, |
{ "k", 1024 }, |
1180 |
{ "m", 1024*1024 }, |
{ "m", 1024*1024 }, |
1181 |
{ NULL, 0 } |
{ } |
1182 |
}; |
}; |
|
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 |
|
1183 |
enum { |
enum { |
1184 |
OPT_A = 1 << 0, |
OPT_A = 1 << 0, |
1185 |
OPT_N = 1 << 1, |
OPT_N = 1 << 1, |
1202 |
OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG, |
OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG, |
1203 |
}; |
}; |
1204 |
#if ENABLE_GETOPT_LONG |
#if ENABLE_GETOPT_LONG |
1205 |
static const struct option long_options[] = { |
static const char od_longopts[] ALIGN1 = |
1206 |
{ "skip-bytes", required_argument, NULL, 'j' }, |
"skip-bytes\0" Required_argument "j" |
1207 |
{ "address-radix", required_argument, NULL, 'A' }, |
"address-radix\0" Required_argument "A" |
1208 |
{ "read-bytes", required_argument, NULL, 'N' }, |
"read-bytes\0" Required_argument "N" |
1209 |
{ "format", required_argument, NULL, 't' }, |
"format\0" Required_argument "t" |
1210 |
{ "output-duplicates", no_argument, NULL, 'v' }, |
"output-duplicates\0" No_argument "v" |
1211 |
{ "strings", optional_argument, NULL, 'S' }, |
"strings\0" Optional_argument "S" |
1212 |
{ "width", optional_argument, NULL, 'w' }, |
"width\0" Optional_argument "w" |
1213 |
{ "traditional", no_argument, NULL, 0xff }, |
"traditional\0" No_argument "\xff" |
1214 |
{ NULL, 0, NULL, 0 } |
; |
|
}; |
|
1215 |
#endif |
#endif |
1216 |
char *str_A, *str_N, *str_j, *str_S; |
char *str_A, *str_N, *str_j, *str_S; |
|
char *str_w = NULL; |
|
1217 |
llist_t *lst_t = NULL; |
llist_t *lst_t = NULL; |
1218 |
|
unsigned opt; |
1219 |
|
int l_c_m; |
1220 |
|
/* The old-style 'pseudo starting address' to be printed in parentheses |
1221 |
|
after any true address. */ |
1222 |
|
off_t pseudo_start = pseudo_start; // for gcc |
1223 |
|
/* The number of input bytes to skip before formatting and writing. */ |
1224 |
|
off_t n_bytes_to_skip = 0; |
1225 |
|
/* The offset of the first byte after the last byte to be formatted. */ |
1226 |
|
off_t end_offset = 0; |
1227 |
|
/* The maximum number of bytes that will be formatted. */ |
1228 |
|
off_t max_bytes_to_format = 0; |
1229 |
|
|
1230 |
spec = NULL; |
spec = NULL; |
1231 |
format_address = format_address_std; |
format_address = format_address_std; |
1232 |
address_base_char = 'o'; |
address_base_char = 'o'; |
1233 |
address_pad_len_char = '7'; |
address_pad_len_char = '7'; |
1234 |
flag_dump_strings = 0; |
/* flag_dump_strings = 0; - already is */ |
1235 |
|
|
1236 |
/* Parse command line */ |
/* Parse command line */ |
1237 |
opt_complementary = "t::"; // list |
opt_complementary = "w+:t::"; /* -w N, -t is a list */ |
1238 |
#if ENABLE_GETOPT_LONG |
#if ENABLE_GETOPT_LONG |
1239 |
applet_long_options = long_options; |
applet_long_options = od_longopts; |
1240 |
#endif |
#endif |
1241 |
opt = getopt32(argc, argv, "A:N:abcdfhij:lot:vxsS:" |
opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:" |
1242 |
"w::", // -w with optional param |
"w::", // -w with optional param |
1243 |
// -S was -s and also had optional parameter |
// -S was -s and also had optional parameter |
1244 |
// but in coreutils 6.3 it was renamed and now has |
// but in coreutils 6.3 it was renamed and now has |
1245 |
// _mandatory_ parameter |
// _mandatory_ parameter |
1246 |
&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); |
1247 |
argc -= optind; |
argc -= optind; |
1248 |
argv += optind; |
argv += optind; |
1249 |
if (opt & OPT_A) { |
if (opt & OPT_A) { |
1250 |
static const char doxn[] = "doxn"; |
static const char doxn[] ALIGN1 = "doxn"; |
1251 |
static const char doxn_address_base_char[] = { |
static const char doxn_address_base_char[] ALIGN1 = { |
1252 |
'u', 'o', 'x', /* '?' fourth one is not important */ |
'u', 'o', 'x', /* '?' fourth one is not important */ |
1253 |
}; |
}; |
1254 |
static const uint8_t doxn_address_pad_len_char[] = { |
static const uint8_t doxn_address_pad_len_char[] ALIGN1 = { |
1255 |
'7', '7', '6', /* '?' */ |
'7', '7', '6', /* '?' */ |
1256 |
}; |
}; |
1257 |
char *p; |
char *p; |
1280 |
if (opt & OPT_l) decode_format_string("d4"); |
if (opt & OPT_l) decode_format_string("d4"); |
1281 |
if (opt & OPT_o) decode_format_string("o2"); |
if (opt & OPT_o) decode_format_string("o2"); |
1282 |
//if (opt & OPT_t)... |
//if (opt & OPT_t)... |
|
lst_t = rev_llist(lst_t); |
|
1283 |
while (lst_t) { |
while (lst_t) { |
1284 |
decode_format_string(lst_t->data); |
decode_format_string(llist_pop(&lst_t)); |
|
lst_t = lst_t->link; |
|
1285 |
} |
} |
1286 |
if (opt & OPT_v) abbreviate_duplicate_blocks = 0; |
if (opt & OPT_v) verbose = 1; |
1287 |
if (opt & OPT_x) decode_format_string("x2"); |
if (opt & OPT_x) decode_format_string("x2"); |
1288 |
if (opt & OPT_s) decode_format_string("d2"); |
if (opt & OPT_s) decode_format_string("d2"); |
1289 |
if (opt & OPT_S) { |
if (opt & OPT_S) { |
1300 |
/* If the --traditional option is used, there may be from |
/* If the --traditional option is used, there may be from |
1301 |
* 0 to 3 remaining command line arguments; handle each case |
* 0 to 3 remaining command line arguments; handle each case |
1302 |
* separately. |
* separately. |
1303 |
* od [file] [[+]offset[.][b] [[+]label[.][b]]] |
* od [file] [[+]offset[.][b] [[+]label[.][b]]] |
1304 |
* The offset and pseudo_start have the same syntax. |
* The offset and pseudo_start have the same syntax. |
1305 |
* |
* |
1306 |
* FIXME: POSIX 1003.1-2001 with XSI requires support for the |
* FIXME: POSIX 1003.1-2001 with XSI requires support for the |
1378 |
/* If no files were listed on the command line, |
/* If no files were listed on the command line, |
1379 |
set the global pointer FILE_LIST so that it |
set the global pointer FILE_LIST so that it |
1380 |
references the null-terminated list of one name: "-". */ |
references the null-terminated list of one name: "-". */ |
1381 |
file_list = default_file_list; |
file_list = bb_argv_dash; |
1382 |
if (argc > 0) { |
if (argc > 0) { |
1383 |
/* Set the global pointer FILE_LIST so that it |
/* Set the global pointer FILE_LIST so that it |
1384 |
references the first file-argument on the command-line. */ |
references the first file-argument on the command-line. */ |
1390 |
/* skip over any unwanted header bytes */ |
/* skip over any unwanted header bytes */ |
1391 |
skip(n_bytes_to_skip); |
skip(n_bytes_to_skip); |
1392 |
if (!in_stream) |
if (!in_stream) |
1393 |
return 1; |
return EXIT_FAILURE; |
1394 |
|
|
1395 |
pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); |
pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); |
1396 |
|
|
1398 |
l_c_m = get_lcm(); |
l_c_m = get_lcm(); |
1399 |
|
|
1400 |
if (opt & OPT_w) { /* -w: width */ |
if (opt & OPT_w) { /* -w: width */ |
|
bytes_per_block = 32; |
|
|
if (str_w) |
|
|
bytes_per_block = xatou(str_w); |
|
1401 |
if (!bytes_per_block || bytes_per_block % l_c_m != 0) { |
if (!bytes_per_block || bytes_per_block % l_c_m != 0) { |
1402 |
bb_error_msg("warning: invalid width %zu; using %d instead", |
bb_error_msg("warning: invalid width %u; using %d instead", |
1403 |
bytes_per_block, l_c_m); |
(unsigned)bytes_per_block, l_c_m); |
1404 |
bytes_per_block = l_c_m; |
bytes_per_block = l_c_m; |
1405 |
} |
} |
1406 |
} else { |
} else { |
1417 |
#endif |
#endif |
1418 |
|
|
1419 |
if (flag_dump_strings) |
if (flag_dump_strings) |
1420 |
dump_strings(); |
dump_strings(n_bytes_to_skip, end_offset); |
1421 |
else |
else |
1422 |
dump(); |
dump(n_bytes_to_skip, end_offset); |
1423 |
|
|
1424 |
if (fclose(stdin) == EOF) |
if (fclose(stdin) == EOF) |
1425 |
bb_perror_msg_and_die(bb_msg_standard_input); |
bb_perror_msg_and_die(bb_msg_standard_input); |
1426 |
|
|
1427 |
return (ioerror != 0); /* err != 0 - return 1 (failure) */ |
return ioerror; |
1428 |
} |
} |